/* * devio.c -- User space communication with USB devices. * * Copyright (C) 1999-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * * It allows user space programs/"drivers" to communicate directly * with USB devices without intervening kernel driver. * * Revision history * 22.12.1999 0.1 Initial release (split from proc_usb.c) * 04.01.2000 0.2 Turned into its own filesystem * 30.09.2005 0.3 Fix user-triggerable oops in async URB delivery * (CAN-2005-3055)
*/
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;
/* * In DMA-unavailable cases, hcd_buffer_alloc_pages allocates * normal pages and assigns DMA_MAPPING_ERROR to dma_handle. Check * whether we are in such cases, and then use remap_pfn_range (or * dma_mmap_coherent) to map normal (or DMA) pages into the user * space, respectively.
*/ if (dma_handle == DMA_MAPPING_ERROR) { if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(usbm->mem) >> PAGE_SHIFT,
size, vma->vm_page_prot) < 0) {
dec_usb_memory_use_count(usbm, &usbm->vma_use_count); return -EAGAIN;
}
} else { if (dma_mmap_coherent(hcd->self.sysdev, vma, mem, dma_handle,
size)) {
dec_usb_memory_use_count(usbm, &usbm->vma_use_count); return -EAGAIN;
}
}
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 * including the first one without AS_CONTINUATION. If such an * URB is encountered then a new transfer has already started so * the endpoint doesn't need to be disabled; otherwise it does.
*/
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);
}
/* * interface claims are made only at the request of user level code, * which can also release them (explicitly or by closing files). * they're also undone when devices disconnect.
*/
#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)) return 0; /* 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);
}
/* * check for the special corner case 'get_device_id' in the printer * class specification, which we always want to allow as it is used * to query things like ink level, etc.
*/ if (requesttype == 0xa1 && request == 0) {
alt_setting = usb_find_alt_setting(ps->dev->actconfig,
index >> 8, index & 0xff); if (alt_setting
&& alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER) return 0;
}
index &= 0xff; switch (requesttype & USB_RECIP_MASK) { case USB_RECIP_ENDPOINT: if ((index & ~USB_DIR_IN) == 0) return 0;
ret = findintfep(ps->dev, index); if (ret < 0) { /* * Some not fully compliant Win apps seem to get * index wrong and have the endpoint number here * rather than the endpoint address (with the * correct direction). Win does let this through, * so we'll not reject it here but leave it to * the device to not break KVM. But we warn.
*/
ret = findintfep(ps->dev, index ^ 0x80); if (ret >= 0)
dev_info(&ps->dev->dev, "%s: process %i (%s) requesting ep %02x but needs %02x\n",
__func__, task_pid_nr(current),
current->comm, index, index ^ 0x80);
} if (ret >= 0)
ret = checkintf(ps, ret); break;
case USB_RECIP_INTERFACE:
ret = checkintf(ps, index); break;
} return ret;
}
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;
/* * Much like usb_start_wait_urb, but returns status separately from * actual_length and uses a killable wait.
*/ staticint usbfs_start_wait_urb(struct urb *urb, int timeout, unsignedint *actlen)
{
DECLARE_COMPLETION_ONSTACK(ctx); unsignedlong expire; int rc;
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);
if (bulk->ep & USB_DIR_IN)
pipe = usb_rcvbulkpipe(dev, bulk->ep & 0x7f); else
pipe = usb_sndbulkpipe(dev, bulk->ep & 0x7f);
ep = usb_pipe_endpoint(dev, pipe); if (!ep || !usb_endpoint_maxp(&ep->desc)) return -EINVAL;
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb)); if (ret) return ret;
/* * len1 can be almost arbitrarily large. Don't WARN if it's * too big, just fail the request.
*/
ret = -ENOMEM;
tbuf = kmalloc(len1, GFP_KERNEL | __GFP_NOWARN); if (!tbuf) goto done;
urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) goto done;
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;
return 0;
}
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 * privilege to do such things and any of the interfaces are * currently claimed.
*/ 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. * It could interfere with other drivers' operations, and if * an interface is claimed by usbfs it could easily deadlock.
*/ 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, * so avoid usb_set_configuration()'s kick to sysfs
*/ if (status == 0) { if (actconfig && actconfig->desc.bConfigurationValue == u)
status = usb_reset_configuration(ps->dev); else
status = usb_set_configuration(ps->dev, u);
}
if ((unsignedint)uurb->buffer_length >= USBFS_XFER_MAX) return -EINVAL; if (uurb->buffer_length > 0 && !uurb->buffer) return -EINVAL; if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
(uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
ifnum = findintfep(ps->dev, uurb->endpoint); if (ifnum < 0) return ifnum;
ret = checkintf(ps, ifnum); if (ret) return ret;
}
ep = ep_to_host_endpoint(ps->dev, uurb->endpoint); if (!ep) return -ENOENT;
is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0;
u = 0; switch (uurb->type) { case USBDEVFS_URB_TYPE_CONTROL: if (!usb_endpoint_xfer_control(&ep->desc)) return -EINVAL; /* min 8 byte setup packet */ if (uurb->buffer_length < 8) return -EINVAL;
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); if (!dr) return -ENOMEM; if (copy_from_user(dr, uurb->buffer, 8)) {
ret = -EFAULT; goto error;
} if (uurb->buffer_length < (le16_to_cpu(dr->wLength) + 8)) {
ret = -EINVAL; goto error;
}
ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
le16_to_cpu(dr->wIndex)); if (ret) goto error;
uurb->buffer_length = le16_to_cpu(dr->wLength);
uurb->buffer += 8; if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
is_in = true;
uurb->endpoint |= USB_DIR_IN;
} else {
is_in = false;
uurb->endpoint &= ~USB_DIR_IN;
} if (is_in)
allow_short = true;
snoop(&ps->dev->dev, "control urb: bRequestType=%02x " "bRequest=%02x wValue=%04x " "wIndex=%04x wLength=%04x\n",
dr->bRequestType, dr->bRequest,
__le16_to_cpu(dr->wValue),
__le16_to_cpu(dr->wIndex),
__le16_to_cpu(dr->wLength));
u = sizeof(struct usb_ctrlrequest); break;
case USBDEVFS_URB_TYPE_BULK: if (!is_in)
allow_zero = true; else
allow_short = true; switch (usb_endpoint_type(&ep->desc)) { case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_ISOC: return -EINVAL; case USB_ENDPOINT_XFER_INT: /* allow single-shot interrupt transfers */
uurb->type = USBDEVFS_URB_TYPE_INTERRUPT; goto interrupt_urb;
}
num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
num_sgs = 0; if (ep->streams)
stream_id = uurb->stream_id; break;
case USBDEVFS_URB_TYPE_INTERRUPT: if (!usb_endpoint_xfer_int(&ep->desc)) return -EINVAL;
interrupt_urb: if (!is_in)
allow_zero = true; else
allow_short = true; break;
case USBDEVFS_URB_TYPE_ISO: /* arbitrary limit */ if (uurb->number_of_packets < 1 ||
uurb->number_of_packets > 128) return -EINVAL; if (!usb_endpoint_xfer_isoc(&ep->desc)) return -EINVAL;
number_of_packets = uurb->number_of_packets;
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
number_of_packets;
isopkt = memdup_user(iso_frame_desc, isofrmlen); if (IS_ERR(isopkt)) {
ret = PTR_ERR(isopkt);
isopkt = NULL; goto error;
} for (totlen = u = 0; u < number_of_packets; u++) { /* * arbitrary limit need for USB 3.1 Gen2 * sizemax: 96 DPs at SSP, 96 * 1024 = 98304
*/ if (isopkt[u].length > 98304) {
ret = -EINVAL; goto error;
}
totlen += isopkt[u].length;
}
u *= sizeof(struct usb_iso_packet_descriptor);
uurb->buffer_length = totlen; break;
default: return -EINVAL;
}
if (uurb->buffer_length > 0 &&
!access_ok(uurb->buffer, uurb->buffer_length)) {
ret = -EFAULT; goto error;
}
as = alloc_async(number_of_packets); if (!as) {
ret = -ENOMEM; goto error;
}
as->usbm = find_memory_area(ps, uurb); if (IS_ERR(as->usbm)) {
ret = PTR_ERR(as->usbm);
as->usbm = NULL; goto error;
}
/* do not use SG buffers when memory mapped segments * are in use
*/ if (as->usbm)
num_sgs = 0;
u += sizeof(struct async) + sizeof(struct urb) +
(as->usbm ? 0 : uurb->buffer_length) +
num_sgs * sizeof(struct scatterlist);
ret = usbfs_increase_memory_usage(u); if (ret) goto error;
as->mem_usage = u;
if (num_sgs) {
as->urb->sg = kmalloc_array(num_sgs, sizeof(struct scatterlist),
GFP_KERNEL | __GFP_NOWARN); if (!as->urb->sg) {
ret = -ENOMEM; goto error;
}
as->urb->num_sgs = num_sgs;
sg_init_table(as->urb->sg, as->urb->num_sgs);
totlen = uurb->buffer_length; for (i = 0; i < as->urb->num_sgs; i++) {
u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen;
buf = kmalloc(u, GFP_KERNEL); if (!buf) {
ret = -ENOMEM; goto error;
}
sg_set_buf(&as->urb->sg[i], buf, u);
if (!is_in) { if (copy_from_user(buf, uurb->buffer, u)) {
ret = -EFAULT; goto error;
}
uurb->buffer += u;
}
totlen -= u;
}
} elseif (uurb->buffer_length > 0) { if (as->usbm) { unsignedlong uurb_start = (unsignedlong)uurb->buffer;
as->urb->transfer_buffer = as->usbm->mem +
(uurb_start - as->usbm->vm_start);
} else {
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
GFP_KERNEL | __GFP_NOWARN); if (!as->urb->transfer_buffer) {
ret = -ENOMEM; goto error;
} if (!is_in) { if (copy_from_user(as->urb->transfer_buffer,
uurb->buffer,
uurb->buffer_length)) {
ret = -EFAULT; goto error;
}
} elseif (uurb->type == USBDEVFS_URB_TYPE_ISO) { /* * Isochronous input data may end up being * discontiguous if some of the packets are * short. Clear the buffer so that the gaps * don't leak kernel data to userspace.
*/
memset(as->urb->transfer_buffer, 0,
uurb->buffer_length);
}
}
}
as->urb->dev = ps->dev;
as->urb->pipe = (uurb->type << 30) |
__create_pipe(ps->dev, uurb->endpoint & 0xf) |
(uurb->endpoint & USB_DIR_IN);
/* This tedious sequence is necessary because the URB_* flags * are internal to the kernel and subject to change, whereas * the USBDEVFS_URB_* flags are a user API and must not be changed.
*/
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 * shifted to the 0x10 position so that the value will be * between 0 and 31.
*/
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 * the endpoint. Otherwise mark it as a continuation URB.
*/ 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 * disabled because of an earlier error.
*/ 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;
}
}
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; return 0;
}
/* 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, you cannot regain them. You may however reissue * this ioctl to shrink the allowed interfaces mask.
*/
ps->interface_allowed_mask &= data;
ps->privileges_dropped = true;
return 0;
}
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;
staticint proc_wait_for_resume(struct usb_dev_state *ps)
{ int ret;
usb_unlock_device(ps->dev);
ret = wait_event_interruptible(ps->wait_for_resume,
READ_ONCE(ps->not_yet_resumed) == 0);
usb_lock_device(ps->dev);
if (ret != 0) return -EINTR; return proc_forbid_suspend(ps);
}
/* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from * changing. But there's no mechanism to ensure that...
*/ staticlong usbdev_do_ioctl(struct file *file, unsignedint cmd, void __user *p)
{ struct usb_dev_state *ps = file->private_data; struct inode *inode = file_inode(file); struct usb_device *dev = ps->dev; int ret = -ENOTTY;
if (!(file->f_mode & FMODE_WRITE)) return -EPERM;
usb_lock_device(dev);
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet)
¤
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.