staticbool usbfs_snoop;
module_param(usbfs_snoop, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
staticunsigned usbfs_snoop_max = 65536;
module_param(usbfs_snoop_max, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usbfs_snoop_max, "maximum number of bytes to print while snooping");
#define snoop(dev, format, arg...) \ do { \ if (usbfs_snoop) \
dev_info(dev, format, ## arg); \
} while (0)
enum snoop_when {
SUBMIT, COMPLETE
};
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
/* Limit on the total amount of memory we can allocate for transfers */ static u32 usbfs_memory_mb = 16;
module_param(usbfs_memory_mb, uint, 0644);
MODULE_PARM_DESC(usbfs_memory_mb, "maximum MB allowed for usbfs buffers (0 = no limit)");
/* Hard limit, necessary to avoid arithmetic overflow */ #define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
static DEFINE_SPINLOCK(usbfs_memory_usage_lock); static u64 usbfs_memory_usage; /* Total memory currently allocated */
/* Check whether it's okay to allocate more memory for a transfer */ staticint usbfs_increase_memory_usage(u64 amount)
{
u64 lim, total_mem; unsignedlong flags; int ret;
lim = READ_ONCE(usbfs_memory_mb);
lim <<= 20;
ret = 0;
spin_lock_irqsave(&usbfs_memory_usage_lock, flags);
total_mem = usbfs_memory_usage + amount; if (lim > 0 && total_mem > lim)
ret = -ENOMEM; else
usbfs_memory_usage = total_mem;
spin_unlock_irqrestore(&usbfs_memory_usage_lock, flags);
return ret;
}
/* Memory for a transfer is being deallocated */ staticvoid usbfs_decrease_memory_usage(u64 amount)
{ unsignedlong flags;
pos = sizeof(struct usb_device_descriptor); for (i = 0; nbytes && i < dev->descriptor.bNumConfigurations; i++) { struct usb_config_descriptor *config =
(struct usb_config_descriptor *)dev->rawdescriptors[i]; unsignedint length = le16_to_cpu(config->wTotalLength);
if (*ppos < pos + length) {
/* The descriptor may claim to be longer than it
* really is. Here is the actual allocated length. */ unsigned alloclen =
le16_to_cpu(dev->config[i].desc.wTotalLength);
len = length - (*ppos - pos); if (len > nbytes)
len = nbytes;
/* Simply don't write (skip over) unallocated parts */ if (alloclen > (*ppos - pos)) {
alloclen -= (*ppos - pos); if (copy_to_user(buf,
dev->rawdescriptors[i] + (*ppos - pos),
min(len, alloclen))) {
ret = -EFAULT; goto err;
}
}
as = kzalloc(sizeof(struct async), GFP_KERNEL); if (!as) return NULL;
as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL); if (!as->urb) {
kfree(as); return NULL;
} return as;
}
staticvoid free_async(struct async *as)
{ int i;
put_pid(as->pid); if (as->cred)
put_cred(as->cred); for (i = 0; i < as->urb->num_sgs; i++) { if (sg_page(&as->urb->sg[i]))
kfree(sg_virt(&as->urb->sg[i]));
}
kfree(as->urb->sg); if (as->usbm == NULL)
kfree(as->urb->transfer_buffer); else
dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count);
/* Mark all the pending URBs that match bulk_addr, up to but not *includingthefirstonewithoutAS_CONTINUATION.Ifsuchan *URBisencounteredthenanewtransferhasalreadystartedso *theendpointdoesn'tneedtobedisabled;otherwiseitdoes.
*/
list_for_each_entry(as, &ps->async_pending, asynclist) { if (as->bulk_addr == bulk_addr) { if (as->bulk_status != AS_CONTINUATION) goto rescan;
as->bulk_status = AS_UNLINK;
as->bulk_addr = 0;
}
}
ps->disabled_bulk_eps |= (1 << bulk_addr);
/* Now carefully unlink all the marked pending URBs */
rescan:
list_for_each_entry_reverse(as, &ps->async_pending, asynclist) { if (as->bulk_status == AS_UNLINK) {
as->bulk_status = 0; /* Only once */
urb = as->urb;
usb_get_urb(urb);
spin_unlock(&ps->lock); /* Allow completions */
usb_unlink_urb(urb);
usb_put_urb(urb);
spin_lock(&ps->lock); goto rescan;
}
}
}
spin_lock_irqsave(&ps->lock, flags); while (!list_empty(list)) {
as = list_last_entry(list, struct async, asynclist);
list_del_init(&as->asynclist);
urb = as->urb;
usb_get_urb(urb);
/* drop the spinlock so the completion handler can run */
spin_unlock_irqrestore(&ps->lock, flags);
usb_kill_urb(urb);
usb_put_urb(urb);
spin_lock_irqsave(&ps->lock, flags);
}
spin_unlock_irqrestore(&ps->lock, flags);
}
#ifdef CONFIG_PM /* The following routines apply to the entire device, not interfaces */ void usbfs_notify_suspend(struct usb_device *udev)
{ /* We don't need to handle this */
}
staticint checkintf(struct usb_dev_state *ps, unsignedint ifnum)
{ if (ps->dev->state != USB_STATE_CONFIGURED) return -EHOSTUNREACH; if (ifnum >= 8*sizeof(ps->ifclaimed)) return -EINVAL; if (test_bit(ifnum, &ps->ifclaimed)) return0; /* if not yet claimed, claim it for the driver */
dev_warn(&ps->dev->dev, "usbfs: process %d (%s) did not claim " "interface %u before use\n", task_pid_nr(current),
current->comm, ifnum); return claimintf(ps, ifnum);
}
if (get_user(num_streams, &streams->num_streams) ||
get_user(num_eps, &streams->num_eps)) return -EFAULT;
if (num_eps < 1 || num_eps > USB_MAXENDPOINTS) return -EINVAL;
/* The XHCI controller allows max 2 ^ 16 streams */ if (num_streams_ret && (num_streams < 2 || num_streams > 65536)) return -EINVAL;
eps = kmalloc_array(num_eps, sizeof(*eps), GFP_KERNEL); if (!eps) return -ENOMEM;
for (i = 0; i < num_eps; i++) { if (get_user(ep, &streams->eps[i])) {
ret = -EFAULT; goto error;
}
eps[i] = ep_to_host_endpoint(ps->dev, ep); if (!eps[i]) {
ret = -EINVAL; goto error;
}
/* usb_alloc/free_streams operate on an usb_interface */
ifnum = findintfep(ps->dev, ep); if (ifnum < 0) {
ret = ifnum; goto error;
}
if (i == 0) {
ret = checkintf(ps, ifnum); if (ret < 0) goto error;
intf = usb_ifnum_to_if(ps->dev, ifnum);
} else { /* Verify all eps belong to the same interface */ if (ifnum != intf->altsetting->desc.bInterfaceNumber) {
ret = -EINVAL; goto error;
}
}
}
/* Can't race with resume; the device is already active */
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
usb_unlock_device(dev);
snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current),
current->comm); return ret;
usb_unlock_device(dev);
i = usbfs_start_wait_urb(urb, tmo, &actlen);
/* Linger a bit, prior to the next control message. */ if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
msleep(200);
usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, tbuf, actlen); if (!i && actlen) { if (copy_to_user(ctrl->data, tbuf, actlen)) {
ret = -EFAULT; goto done;
}
}
} else { if (wLength) { if (copy_from_user(tbuf, ctrl->data, wLength)) {
ret = -EFAULT; goto done;
}
}
pipe = usb_sndctrlpipe(dev, 0);
usb_fill_control_urb(urb, dev, pipe, (unsignedchar *) dr, tbuf,
wLength, NULL, NULL);
snoop_urb(dev, NULL, pipe, wLength, tmo, SUBMIT, tbuf, wLength);
usb_unlock_device(dev);
i = usbfs_start_wait_urb(urb, tmo, &actlen);
/* Linger a bit, prior to the next control message. */ if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
msleep(200);
usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, actlen, i, COMPLETE, NULL, 0);
} if (i < 0 && i != -EPIPE) {
dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL " "failed cmd %s rqt %u rq %u len %u ret %d\n",
current->comm, ctrl->bRequestType, ctrl->bRequest,
ctrl->wLength, i);
}
ret = (i < 0 ? i : actlen);
while (udev && udev->portnum != 0) { if (++ci.num_ports <= ARRAY_SIZE(ci.ports))
ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports] =
udev->portnum;
udev = udev->parent;
}
if (ci.num_ports < ARRAY_SIZE(ci.ports))
memmove(&ci.ports[0],
&ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports],
ci.num_ports);
if (copy_to_user(arg, &ci, min(sizeof(ci), size))) return -EFAULT;
return0;
}
staticint proc_resetdevice(struct usb_dev_state *ps)
{ struct usb_host_config *actconfig = ps->dev->actconfig; struct usb_interface *interface; int i, number;
/* Don't allow a device reset if the process has dropped the *privilegetodosuchthingsandanyoftheinterfacesare *currentlyclaimed.
*/ if (ps->privileges_dropped && actconfig) { for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
interface = actconfig->interface[i];
number = interface->cur_altsetting->desc.bInterfaceNumber; if (usb_interface_claimed(interface) &&
!test_bit(number, &ps->ifclaimed)) {
dev_warn(&ps->dev->dev, "usbfs: interface %d claimed by %s while '%s' resets device\n",
number, interface->dev.driver->name, current->comm); return -EACCES;
}
}
}
staticint proc_setconfig(struct usb_dev_state *ps, void __user *arg)
{ int u; int status = 0; struct usb_host_config *actconfig;
if (get_user(u, (int __user *)arg)) return -EFAULT;
actconfig = ps->dev->actconfig;
/* Don't touch the device if any interfaces are claimed. *Itcouldinterferewithotherdrivers'operations,andif *aninterfaceisclaimedbyusbfsitcouldeasilydeadlock.
*/ if (actconfig) { int i;
for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) { if (usb_interface_claimed(actconfig->interface[i])) {
dev_warn(&ps->dev->dev, "usbfs: interface %d claimed by %s " "while '%s' sets config #%d\n",
actconfig->interface[i]
->cur_altsetting
->desc.bInterfaceNumber,
actconfig->interface[i]
->dev.driver->name,
current->comm, u);
status = -EBUSY; break;
}
}
}
/* SET_CONFIGURATION is often abused as a "cheap" driver reset, *soavoidusb_set_configuration()'skicktosysfs
*/ if (status == 0) { if (actconfig && actconfig->desc.bConfigurationValue == u)
status = usb_reset_configuration(ps->dev); else
status = usb_set_configuration(ps->dev, u);
}
/* This tedious sequence is necessary because the URB_* flags *areinternaltothekernelandsubjecttochange,whereas *theUSBDEVFS_URB_*flagsareauserAPIandmustnotbechanged.
*/
u = (is_in ? URB_DIR_IN : URB_DIR_OUT); if (uurb->flags & USBDEVFS_URB_ISO_ASAP)
u |= URB_ISO_ASAP; if (allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
u |= URB_SHORT_NOT_OK; if (allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET)
u |= URB_ZERO_PACKET; if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT)
u |= URB_NO_INTERRUPT;
as->urb->transfer_flags = u;
if (!allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_SHORT_NOT_OK.\n"); if (!allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET)
dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_ZERO_PACKET.\n");
if (usb_endpoint_xfer_bulk(&ep->desc)) {
spin_lock_irq(&ps->lock);
/* Not exactly the endpoint address; the direction bit is *shiftedtothe0x10positionsothatthevaluewillbe *between0and31.
*/
as->bulk_addr = usb_endpoint_num(&ep->desc) |
((ep->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
>> 3);
/* If this bulk URB is the start of a new transfer, re-enable *theendpoint.OtherwisemarkitasacontinuationURB.
*/ if (uurb->flags & USBDEVFS_URB_BULK_CONTINUATION)
as->bulk_status = AS_CONTINUATION; else
ps->disabled_bulk_eps &= ~(1 << as->bulk_addr);
/* Don't accept continuation URBs if the endpoint is *disabledbecauseofanearliererror.
*/ if (ps->disabled_bulk_eps & (1 << as->bulk_addr))
ret = -EREMOTEIO; else
ret = usb_submit_urb(as->urb, GFP_ATOMIC);
spin_unlock_irq(&ps->lock);
} else {
ret = usb_submit_urb(as->urb, GFP_KERNEL);
}
compute_isochronous_actual_length(urb); if (as->userbuffer && urb->actual_length) { if (copy_urb_data_to_user(as->userbuffer, urb)) goto err_out;
} if (put_user(as->status, &userurb->status)) goto err_out; if (put_user(urb->actual_length, &userurb->actual_length)) goto err_out; if (put_user(urb->error_count, &userurb->error_count)) goto err_out;
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) { for (i = 0; i < urb->number_of_packets; i++) { if (put_user(urb->iso_frame_desc[i].actual_length,
&userurb->iso_frame_desc[i].actual_length)) goto err_out; if (put_user(urb->iso_frame_desc[i].status,
&userurb->iso_frame_desc[i].status)) goto err_out;
}
}
if (put_user(addr, (void __user * __user *)arg)) return -EFAULT; return0;
compute_isochronous_actual_length(urb); if (as->userbuffer && urb->actual_length) { if (copy_urb_data_to_user(as->userbuffer, urb)) return -EFAULT;
} if (put_user(as->status, &userurb->status)) return -EFAULT; if (put_user(urb->actual_length, &userurb->actual_length)) return -EFAULT; if (put_user(urb->error_count, &userurb->error_count)) return -EFAULT;
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) { for (i = 0; i < urb->number_of_packets; i++) { if (put_user(urb->iso_frame_desc[i].actual_length,
&userurb->iso_frame_desc[i].actual_length)) return -EFAULT; if (put_user(urb->iso_frame_desc[i].status,
&userurb->iso_frame_desc[i].status)) return -EFAULT;
}
}
if (put_user(ptr_to_compat(addr), (u32 __user *)arg)) return -EFAULT; return0;
}
/* disconnect kernel driver from interface */ case USBDEVFS_DISCONNECT: if (intf->dev.driver) {
driver = to_usb_driver(intf->dev.driver);
dev_dbg(&intf->dev, "disconnect by usbfs\n");
usb_driver_release_interface(driver, intf);
} else
retval = -ENODATA; break;
/* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: if (!intf->dev.driver)
retval = device_attach(&intf->dev); else
retval = -EBUSY; break;
/* talk directly to the interface's driver */ default: if (intf->dev.driver)
driver = to_usb_driver(intf->dev.driver); if (driver == NULL || driver->unlocked_ioctl == NULL) {
retval = -ENOTTY;
} else {
retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf); if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
}
}
if (copy_from_user(&data, arg, sizeof(data))) return -EFAULT;
/* This is a one way operation. Once privileges are *dropped,youcannotregainthem.Youmayhoweverreissue *thisioctltoshrinktheallowedinterfacesmask.
*/
ps->interface_allowed_mask &= data;
ps->privileges_dropped = true;
return0;
}
staticint proc_forbid_suspend(struct usb_dev_state *ps)
{ int ret = 0;
if (ps->suspend_allowed) {
ret = usb_autoresume_device(ps->dev); if (ret == 0)
ps->suspend_allowed = false; elseif (ret != -ENODEV)
ret = -EIO;
} return ret;
}
staticint proc_allow_suspend(struct usb_dev_state *ps)
{ if (!connected(ps)) return -ENODEV;
/* Reap operations are allowed even after disconnection */ switch (cmd) { case USBDEVFS_REAPURB:
snoop(&dev->dev, "%s: REAPURB\n", __func__);
ret = proc_reapurb(ps, p); goto done;
case USBDEVFS_REAPURBNDELAY:
snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
ret = proc_reapurbnonblock(ps, p); goto done;
#ifdef CONFIG_COMPAT case USBDEVFS_REAPURB32:
snoop(&dev->dev, "%s: REAPURB32\n", __func__);
ret = proc_reapurb_compat(ps, p); goto done;
case USBDEVFS_REAPURBNDELAY32:
snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
ret = proc_reapurbnonblock_compat(ps, p); goto done; #endif
}
if (!connected(ps)) {
usb_unlock_device(dev); return -ENODEV;
}
switch (cmd) { case USBDEVFS_CONTROL:
snoop(&dev->dev, "%s: CONTROL\n", __func__);
ret = proc_control(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
case USBDEVFS_BULK:
snoop(&dev->dev, "%s: BULK\n", __func__);
ret = proc_bulk(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
case USBDEVFS_RESETEP:
snoop(&dev->dev, "%s: RESETEP\n", __func__);
ret = proc_resetep(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
case USBDEVFS_RESET:
snoop(&dev->dev, "%s: RESET\n", __func__);
ret = proc_resetdevice(ps); break;
case USBDEVFS_CLEAR_HALT:
snoop(&dev->dev, "%s: CLEAR_HALT\n", __func__);
ret = proc_clearhalt(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
case USBDEVFS_GETDRIVER:
snoop(&dev->dev, "%s: GETDRIVER\n", __func__);
ret = proc_getdriver(ps, p); break;
case USBDEVFS_CONNECTINFO:
snoop(&dev->dev, "%s: CONNECTINFO\n", __func__);
ret = proc_connectinfo(ps, p); break;
case USBDEVFS_SETINTERFACE:
snoop(&dev->dev, "%s: SETINTERFACE\n", __func__);
ret = proc_setintf(ps, p); break;
case USBDEVFS_SETCONFIGURATION:
snoop(&dev->dev, "%s: SETCONFIGURATION\n", __func__);
ret = proc_setconfig(ps, p); break;
case USBDEVFS_SUBMITURB:
snoop(&dev->dev, "%s: SUBMITURB\n", __func__);
ret = proc_submiturb(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
#ifdef CONFIG_COMPAT case USBDEVFS_CONTROL32:
snoop(&dev->dev, "%s: CONTROL32\n", __func__);
ret = proc_control_compat(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
case USBDEVFS_BULK32:
snoop(&dev->dev, "%s: BULK32\n", __func__);
ret = proc_bulk_compat(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
case USBDEVFS_DISCSIGNAL32:
snoop(&dev->dev, "%s: DISCSIGNAL32\n", __func__);
ret = proc_disconnectsignal_compat(ps, p); break;
case USBDEVFS_SUBMITURB32:
snoop(&dev->dev, "%s: SUBMITURB32\n", __func__);
ret = proc_submiturb_compat(ps, p); if (ret >= 0)
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode)); break;
case USBDEVFS_IOCTL32:
snoop(&dev->dev, "%s: IOCTL32\n", __func__);
ret = proc_ioctl_compat(ps, ptr_to_compat(p)); break; #endif
case USBDEVFS_DISCARDURB:
snoop(&dev->dev, "%s: DISCARDURB %px\n", __func__, p);
ret = proc_unlinkurb(ps, p); break;
case USBDEVFS_DISCSIGNAL:
snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
ret = proc_disconnectsignal(ps, p); break;
case USBDEVFS_CLAIMINTERFACE:
snoop(&dev->dev, "%s: CLAIMINTERFACE\n", __func__);
ret = proc_claiminterface(ps, p); break;
case USBDEVFS_RELEASEINTERFACE:
snoop(&dev->dev, "%s: RELEASEINTERFACE\n", __func__);
ret = proc_releaseinterface(ps, p); break;
case USBDEVFS_IOCTL:
snoop(&dev->dev, "%s: IOCTL\n", __func__);
ret = proc_ioctl_default(ps, p); break;
case USBDEVFS_CLAIM_PORT:
snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__);
ret = proc_claim_port(ps, p); break;
case USBDEVFS_RELEASE_PORT:
snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
ret = proc_release_port(ps, p); break; case USBDEVFS_GET_CAPABILITIES:
ret = proc_get_capabilities(ps, p); break; case USBDEVFS_DISCONNECT_CLAIM:
ret = proc_disconnect_claim(ps, p); break; case USBDEVFS_ALLOC_STREAMS:
ret = proc_alloc_streams(ps, p); break; case USBDEVFS_FREE_STREAMS:
ret = proc_free_streams(ps, p); break; case USBDEVFS_DROP_PRIVILEGES:
ret = proc_drop_privileges(ps, p); break; case USBDEVFS_GET_SPEED:
ret = ps->dev->speed; break; case USBDEVFS_FORBID_SUSPEND:
ret = proc_forbid_suspend(ps); break; case USBDEVFS_ALLOW_SUSPEND:
ret = proc_allow_suspend(ps); break; case USBDEVFS_WAIT_FOR_RESUME:
ret = proc_wait_for_resume(ps); break;
}
/* Handle variable-length commands */ switch (cmd & ~IOCSIZE_MASK) { case USBDEVFS_CONNINFO_EX(0):
ret = proc_conninfo_ex(ps, p, _IOC_SIZE(cmd)); break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.