📄 devio.c
字号:
/* check if we are called from a real node or usbfs */ if (imajor(inode) == USB_DEVICE_MAJOR) dev = usbdev_lookup_minor(iminor(inode)); if (!dev) dev = inode->u.generic_ip; if (!dev) { kfree(ps); goto out; } usb_get_dev(dev); ret = 0; ps->dev = dev; ps->file = file; spin_lock_init(&ps->lock); INIT_LIST_HEAD(&ps->async_pending); INIT_LIST_HEAD(&ps->async_completed); init_waitqueue_head(&ps->wait); ps->discsignr = 0; ps->disc_pid = current->pid; ps->disc_uid = current->uid; ps->disc_euid = current->euid; ps->disccontext = NULL; ps->ifclaimed = 0; wmb(); list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; out: unlock_kernel(); out_nolock: return ret;}static int usbdev_release(struct inode *inode, struct file *file){ struct dev_state *ps = (struct dev_state *)file->private_data; struct usb_device *dev = ps->dev; unsigned int ifnum; usb_lock_device(dev); list_del_init(&ps->list); for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); ifnum++) { if (test_bit(ifnum, &ps->ifclaimed)) releaseintf(ps, ifnum); } destroy_all_async(ps); usb_unlock_device(dev); usb_put_dev(dev); ps->dev = NULL; kfree(ps); return 0;}static int proc_control(struct dev_state *ps, void __user *arg){ struct usb_device *dev = ps->dev; struct usbdevfs_ctrltransfer ctrl; unsigned int tmo; unsigned char *tbuf; int i, j, ret; if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; if ((ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex))) return ret; if (ctrl.wLength > PAGE_SIZE) return -EINVAL; if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; tmo = ctrl.timeout; if (ctrl.bRequestType & 0x80) { if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.wLength)) { free_page((unsigned long)tbuf); return -EINVAL; } snoop(&dev->dev, "control read: bRequest=%02x " "bRrequestType=%02x wValue=%04x " "wIndex=%04x wLength=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, ctrl.wLength); usb_unlock_device(dev); i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); if ((i > 0) && ctrl.wLength) { if (usbfs_snoop) { dev_info(&dev->dev, "control read: data "); for (j = 0; j < i; ++j) printk("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } if (copy_to_user(ctrl.data, tbuf, i)) { free_page((unsigned long)tbuf); return -EFAULT; } } } else { if (ctrl.wLength) { if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) { free_page((unsigned long)tbuf); return -EFAULT; } } snoop(&dev->dev, "control write: bRequest=%02x " "bRrequestType=%02x wValue=%04x " "wIndex=%04x wLength=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, ctrl.wLength); if (usbfs_snoop) { dev_info(&dev->dev, "control write: data: "); for (j = 0; j < ctrl.wLength; ++j) printk("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); } free_page((unsigned long)tbuf); 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); } return i;}static int proc_bulk(struct dev_state *ps, void __user *arg){ struct usb_device *dev = ps->dev; struct usbdevfs_bulktransfer bulk; unsigned int tmo, len1, pipe; int len2; unsigned char *tbuf; int i, j, ret; if (copy_from_user(&bulk, arg, sizeof(bulk))) return -EFAULT; if ((ret = findintfep(ps->dev, bulk.ep)) < 0) return ret; if ((ret = checkintf(ps, ret))) return ret; if (bulk.ep & USB_DIR_IN) pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f); else pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f); if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) return -EINVAL; len1 = bulk.len; if (len1 > MAX_USBFS_BUFFER_SIZE) return -EINVAL; if (!(tbuf = kmalloc(len1, GFP_KERNEL))) return -ENOMEM; tmo = bulk.timeout; if (bulk.ep & 0x80) { if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { kfree(tbuf); return -EINVAL; } snoop(&dev->dev, "bulk read: len=0x%02x timeout=%04d\n", bulk.len, bulk.timeout); usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); if (!i && len2) { if (usbfs_snoop) { dev_info(&dev->dev, "bulk read: data "); for (j = 0; j < len2; ++j) printk("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); return -EFAULT; } } } else { if (len1) { if (copy_from_user(tbuf, bulk.data, len1)) { kfree(tbuf); return -EFAULT; } } snoop(&dev->dev, "bulk write: len=0x%02x timeout=%04d\n", bulk.len, bulk.timeout); if (usbfs_snoop) { dev_info(&dev->dev, "bulk write: data: "); for (j = 0; j < len1; ++j) printk("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); } kfree(tbuf); if (i < 0) return i; return len2;}static int proc_resetep(struct dev_state *ps, void __user *arg){ unsigned int ep; int ret; if (get_user(ep, (unsigned int __user *)arg)) return -EFAULT; if ((ret = findintfep(ps->dev, ep)) < 0) return ret; if ((ret = checkintf(ps, ret))) return ret; usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0); return 0;}static int proc_clearhalt(struct dev_state *ps, void __user *arg){ unsigned int ep; int pipe; int ret; if (get_user(ep, (unsigned int __user *)arg)) return -EFAULT; if ((ret = findintfep(ps->dev, ep)) < 0) return ret; if ((ret = checkintf(ps, ret))) return ret; if (ep & USB_DIR_IN) pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f); else pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f); return usb_clear_halt(ps->dev, pipe);} static int proc_getdriver(struct dev_state *ps, void __user *arg){ struct usbdevfs_getdriver gd; struct usb_interface *intf; int ret; if (copy_from_user(&gd, arg, sizeof(gd))) return -EFAULT; down_read(&usb_bus_type.subsys.rwsem); intf = usb_ifnum_to_if(ps->dev, gd.interface); if (!intf || !intf->dev.driver) ret = -ENODATA; else { strncpy(gd.driver, intf->dev.driver->name, sizeof(gd.driver)); ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0); } up_read(&usb_bus_type.subsys.rwsem); return ret;}static int proc_connectinfo(struct dev_state *ps, void __user *arg){ struct usbdevfs_connectinfo ci; ci.devnum = ps->dev->devnum; ci.slow = ps->dev->speed == USB_SPEED_LOW; if (copy_to_user(arg, &ci, sizeof(ci))) return -EFAULT; return 0;}static int proc_resetdevice(struct dev_state *ps){ return usb_reset_device(ps->dev);}static int proc_setintf(struct dev_state *ps, void __user *arg){ struct usbdevfs_setinterface setintf; int ret; if (copy_from_user(&setintf, arg, sizeof(setintf))) return -EFAULT; if ((ret = checkintf(ps, setintf.interface))) return ret; return usb_set_interface(ps->dev, setintf.interface, setintf.altsetting);}static int proc_setconfig(struct dev_state *ps, void __user *arg){ unsigned int u; int status = 0; struct usb_host_config *actconfig; if (get_user(u, (unsigned 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); } return status;}static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct usbdevfs_iso_packet_desc __user *iso_frame_desc, void __user *arg){ struct usbdevfs_iso_packet_desc *isopkt = NULL; struct usb_host_endpoint *ep; struct async *as; struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; int ret, interval = 0, ifnum = -1; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK| URB_NO_FSBR|URB_ZERO_PACKET)) return -EINVAL; if (!uurb->buffer) return -EINVAL; if (uurb->signr != 0 && (uurb->signr < SIGRTMIN || uurb->signr > SIGRTMAX)) return -EINVAL; if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { if ((ifnum = findintfep(ps->dev, uurb->endpoint)) < 0) return ifnum; if ((ret = checkintf(ps, ifnum))) return ret; } if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) ep = ps->dev->ep_in [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; else ep = ps->dev->ep_out [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; if (!ep) return -ENOENT; switch(uurb->type) { case USBDEVFS_URB_TYPE_CONTROL: if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_CONTROL) return -EINVAL; /* min 8 byte setup packet, max arbitrary */ if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE) return -EINVAL; if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) return -ENOMEM; if (copy_from_user(dr, uurb->buffer, 8)) { kfree(dr); return -EFAULT; } if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) { kfree(dr); return -EINVAL; } if ((ret = check_ctrlrecip(ps, dr->bRequestType, le16_to_cpup(&dr->wIndex)))) { kfree(dr); return ret; } uurb->endpoint = (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK); uurb->number_of_packets = 0; uurb->buffer_length = le16_to_cpup(&dr->wLength); uurb->buffer += 8; if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) { kfree(dr); return -EFAULT; } snoop(&ps->dev->dev, "control urb\n"); break; case USBDEVFS_URB_TYPE_BULK: switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_ISOC: return -EINVAL; /* allow single-shot interrupt transfers, at bogus rates */ } uurb->number_of_packets = 0; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; snoop(&ps->dev->dev, "bulk urb\n"); break; case USBDEVFS_URB_TYPE_ISO: /* arbitrary limit */ if (uurb->number_of_packets < 1 || uurb->number_of_packets > 128) return -EINVAL; if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) return -EINVAL; interval = 1 << min (15, ep->desc.bInterval - 1); isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets; if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) return -ENOMEM; if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { kfree(isopkt); return -EFAULT; } for (totlen = u = 0; u < uurb->number_of_packets; u++) { if (isopkt[u].length > 1023) { kfree(isopkt); return -EINVAL; } totlen += isopkt[u].length; } if (totlen > 32768) { kfree(isopkt); return -EINVAL; } uurb->buffer_length = totlen; snoop(&ps->dev->dev, "iso urb\n"); break; case USBDEVFS_URB_TYPE_INTERRUPT: uurb->number_of_packets = 0; if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) return -EINVAL; if (ps->dev->speed == USB_SPEED_HIGH) interval = 1 << min (15, ep->desc.bInterval - 1); else interval = ep->desc.bInterval; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; snoop(&ps->dev->dev, "interrupt urb\n"); break; default: return -EINVAL; } if (!(as = alloc_async(uurb->number_of_packets))) { kfree(isopkt); kfree(dr); return -ENOMEM; } if (!(as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL))) { kfree(isopkt); kfree(dr); free_async(as); return -ENOMEM; } as->urb->dev = ps->dev; as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | (uurb->endpoint & USB_DIR_IN); as->urb->transfer_flags = uurb->flags; as->urb->transfer_buffer_length = uurb->buffer_length; as->urb->setup_packet = (unsigned char*)dr; as->urb->start_frame = uurb->start_frame; as->urb->number_of_packets = uurb->number_of_packets; as->urb->interval = interval; as->urb->context = as; as->urb->complete = async_completed; for (totlen = u = 0; u < uurb->number_of_packets; u++) { as->urb->iso_frame_desc[u].offset = totlen; as->urb->iso_frame_desc[u].length = isopkt[u].length; totlen += isopkt[u].length; } kfree(isopkt); as->ps = ps; as->userurb = arg; if (uurb->endpoint & USB_DIR_IN) as->userbuffer = uurb->buffer; else as->userbuffer = NULL; as->signr = uurb->signr; as->ifnum = ifnum; as->pid = current->pid; as->uid = current->uid; as->euid = current->euid; if (!(uurb->endpoint & USB_DIR_IN)) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { free_async(as); return -EFAULT; } } snoop(&as->urb->dev->dev, "submit urb\n"); snoop_urb(as->urb, as->userurb); async_newpending(as); if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) { dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret); async_removepending(as); free_async(as); return ret; } return 0;}static int proc_submiturb(struct dev_state *ps, void __user *arg){ struct usbdevfs_urb uurb; if (copy_from_user(&uurb, arg, sizeof(uurb))) return -EFAULT; return proc_do_submiturb(ps, &uurb, (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), arg);}static int proc_unlinkurb(struct dev_state *ps, void __user *arg){ struct async *as; as = async_getpending(ps, arg); if (!as) return -EINVAL; usb_kill_urb(as->urb); return 0;}static int processcompl(struct async *as, void __user * __user *arg){ struct urb *urb = as->urb; struct usbdevfs_urb __user *userurb = as->userurb; void __user *addr = as->userurb; unsigned int i; if (as->userbuffer) if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length)) return -EFAULT; if (put_user(urb->status, &userurb->status)) return -EFAULT; if (put_user(urb->actual_length, &userurb->actual_length))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -