usb驱动分为通过usbfs操作设备的用户空间驱动,内核空间的内核驱动。两者不能同时进行,否则容易引发对共享资源访问的问题,死锁!使用了内核驱动,就不能在usbfs里驱动该设备。libusb中须要先detach内核驱动后,才能claim interface,否则claim会返回的vice busy的错误。如果你不dettach,也不claim interface,也能使用libusb对设备进行访问,但是,容易导致内核usbfs瘫痪,这是不允许的。
如果不能dettach内核驱动,那么你不能通过usbfs访问设备,也就是不能使用libusb。若确实需要usbfs才能完成的操作,如控制传输,中断传输等,可以在内核驱动里给ioctl添加对应的功能,即可通过内核驱动完成usb设备的所有原始通信。
使用libusb读写ft232的eeprom,不允许把内核驱动dettach的情况下,经常导致usbfs瘫痪,所有usb都处于disk sleep状态。这里我之用得到control传输,经过一番思考,我在内核驱动里面的ioctl里添加了USBDEVFS_CONTROL选项,通过内核驱动完成usb control msg,测试了好几天,没发现任何问题。好,基本完成任务。
以下是我的操作:
在设备驱动文件ftdi_sio.c的ioctl函数里面添加:
static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) { struct ftdi_private *priv = usb_get_serial_port_data(port); int ret, mask; dbg("%s cmd 0x%04x", __FUNCTION__, cmd); /* Based on code from acm.c and others */ switch (cmd) { /* To support usb_control_msg to ttyUSB */ case USBDEVFS_CONTROL: dev_printk(KERN_DEBUG, &port->serial->dev->dev, "%s: CONTROL\n", __FUNCTION__); return tty_control(port, (void __user *)arg); break; case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ 。。。。。。
static int tty_control(struct usb_serial_port *port, void __user *arg) { struct usb_device *dev = port->serial->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 * HZ + 999) / 1000; if (ctrl.bRequestType & 0x80) { if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.wLength)) { free_page((unsigned long)tbuf); return -EINVAL; } dev_printk(KERN_DEBUG, &dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); if ((i > 0) && ctrl.wLength) { if (copy_to_user(ctrl.data, tbuf, ctrl.wLength)) { 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; } } dev_printk(KERN_DEBUG, &dev->dev, "control write: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); } free_page((unsigned long)tbuf); if (i<0) { 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; }
好了,编译加载后,设备文件就支持usb_control_msg了。以下是用户程序的usb_control_msg
#define IOCTL_USB_CONTROL _IOWR('U', 0, struct usb_ctrltransfer) struct usb_ctrltransfer { /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ u_int8_t bRequestType; u_int8_t bRequest; u_int16_t wValue; u_int16_t wIndex; u_int16_t wLength; u_int32_t timeout; /* in milliseconds */ /* pointer to data */ void *data; }; static int usb_control_msg(int fd, int requesttype, int request, int value, int index, char *bytes, int size, int timeout) { struct usb_ctrltransfer ctrl; int ret; ctrl.bRequestType = requesttype; ctrl.bRequest = request; ctrl.wValue = value; ctrl.wIndex = index; ctrl.wLength = size; ctrl.data = bytes; ctrl.timeout = timeout; ret = ioctl(fd, IOCTL_USB_CONTROL, &ctrl); if (ret < 0) printf("usb_control_msg: %s\n", strerror(errno)); return ret; }
其实,上面的usb_ctrltransfer和usbdevice_fs.h 里的struct usbdevfs_ctrltransfer是一样的,只是为了方便查阅,才定义了另外一个在源文件里。
linux-2.6.8.1/include/linux/usbdevice_fs.h
/* usbdevfs ioctl codes */ struct usbdevfs_ctrltransfer { __u8 bRequestType; __u8 bRequest; __u16 wValue; __u16 wIndex; __u16 wLength; __u32 timeout; /* in milliseconds */ void __user *data; }; #define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer)