浅析libusb控制接口***

Android Usb源码分析 android上usb设备接入,拔出, 读写操作过程分析 a. usb设备插入,如何组成usbdevice. 存放在UsbManager.devicesList. usb设备插入后host端如何获取该设备信息呢? 分析: 当usb设备接入时, 会有硬件产生中断. 收到中断后host端会由总线驱动程序根据usb协议, 对端口0地址发送一个请求包给usb设备. 设备收到请求后会将自己的设备描述符信... 阅读详情

 

 

浅析libusb控制接口和mountd守护进程处理uevent切换usb设备的实现

1. UMS mode (USB Mass-Storage mode) [ums]
2. 从usb_gadget_register_driver的实现来看,insmod xxx.ko,然后重新插拔一下usb cable,那么pc再次枚举到的设备就是insmod xxx.ko对应的设备了[luther.gliethttp]
3. init进程没有对change event事件进行处理,mountd守护进程的detect_thread线程会等待该uevent事件到来,然后卸载前一个ko,加载欲成为设备的相应ko驱动[luther.gliethttp].
4. 其实uevent已经在udc_uevent中将ret数值打入了uevent strings中了MODE=;NET_ENUM=;TESTMODE=;直接解析就可以了,所以就不需要read_usb_switch了[luther.gliethttp].
5. libusb库链接程序时,可以使用 -static -lusb选项,将libusb静态编译到程序中,这样其他pc就不用单独安装libusb了[luther.gliethttp].
====================================================================
usb_init=>我的是strncpy(usb_path, "/dev/bus/usb", sizeof(usb_path) - 1);"/dev/bus/usb"或者"/proc/bus/usb"
usb_find_busses=>然后将usb目录下的所有文件目录路径名bus->dirname添加到struct usb_bus *usb_busses = NULL;
链表上,比如:/dev/bus/usb下的001和002目录等.
usb_find_devices=>根据文件目录路径名遍历usb_busses文件夹下的所有文件对应的char节点,如果合法将dev添加bus->devices设备链表上.
同时将char节点的文件路径名作为访问文件节点的路径名存储起来.
//比如打开host控制器的2号hub下插入的第1个设备
//usb_path="/dev/bus/usb"
//dev->bus->dirname="002"
//dev->filename="001"
    /* Get the device number */
    ret = ioctl(fd, IOCTL_USB_CONNECTINFO, &connectinfo);
    ret = read(fd, (void *)device_desc, DEVICE_DESC_LENGTH);
    usb_parse_descriptor(device_desc, "bbWbbbbWWWbbbb", &dev->descriptor);
可以看到我的pc主机usb-host2控制器挂接了4个usb设备,usb-host1控制器挂接了1个设备
luther@gliethttp:~$ tree /dev/bus/usb/
/dev/bus/usb/
|-- 001
| `-- 001
|-- 002
| |-- 001
| |-- 002
| |-- 003
| `-- 004
`-- devices -> .usbfs/devices
luther@gliethttp:~$ ll /dev/bus/usb/002/
total 0
crw-rw-r-- 1 root root 189, 128 2008-10-16 17:30 001
crw-rw-r-- 1 root root 189, 129 2008-10-16 17:30 002
crw-rw-r-- 1 root root 189, 130 2008-10-16 17:30 003
可以看到major都是189,因为是bus2,所以从128开始,一个bus现在最多支持128个usb设备.
//根据pid和vid查找设备是否已经存在
struct usb_device *find_device(int vendor, int product) {
    struct usb_bus *bus;
    struct usb_device *right_dev;
    right_dev = NULL;
    
    for (bus = usb_get_busses(); bus; bus = bus->next) {
     struct usb_device *dev;
    
     for (dev = bus->devices; dev; dev = dev->next) {
     if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) {
         right_dev = dev;
                DevicesN++;
     }
        }
    }
       
    return right_dev;
}
然后调用usb_open打开find_device()返回的usb_device设备,
usb_dev_handle *usb_open(struct usb_device *dev)
{
  usb_dev_handle *udev;

  udev = malloc(sizeof(*udev));
  if (!udev)
    return NULL;

  udev->fd = -1;
  udev->device = dev;
  udev->bus = dev->bus;
  udev->config = udev->interface = udev->altsetting = -1;

  if (usb_os_open(udev) < 0) {
    free(udev);
    return NULL;
  }

  return udev;
}
int usb_os_open(usb_dev_handle *dev)
{
  dev->fd = device_open(dev->device);

  return 0;
}
static int device_open(struct usb_device *dev)
{
  char filename[PATH_MAX + 1];
  int fd;

  snprintf(filename, sizeof(filename) - 1, "%s/%s/%s",
    usb_path, dev->bus->dirname, dev->filename);
//比如打开host控制器的2号hub下插入的第1个设备
//usb_path="/dev/bus/usb"
//dev->bus->dirname="002"
//dev->filename="001"
  fd = open(filename, O_RDWR);
  if (fd < 0) {
    fd = open(filename, O_RDONLY);
    if (fd < 0)
      USB_ERROR_STR(-errno, "failed to open %s: %s",
    filename, strerror(errno));
  }

  return fd;
}
====================================================================
链接程序时,可以使用 -static -lusb选项,将libusb静态编译到程序中,这样其他pc就不用单独安装libusb了[luther.gliethttp].
以下代码摘自libusb-0.1.12
#define USB_MAXDRIVERNAME 255

struct usb_getdriver {
    unsigned int interface;
    char driver[USB_MAXDRIVERNAME + 1];
};
#define IOCTL_USB_CONTROL    _IOWR('U', 0, struct usb_ctrltransfer)
#define IOCTL_USB_BULK        _IOWR('U', 2, struct usb_bulktransfer)
#define IOCTL_USB_RESETEP    _IOR('U', 3, unsigned int)
#define IOCTL_USB_SETINTF    _IOR('U', 4, struct usb_setinterface)
#define IOCTL_USB_SETCONFIG    _IOR('U', 5, unsigned int)
#define IOCTL_USB_GETDRIVER    _IOW('U', 8, struct usb_getdriver)
#define IOCTL_USB_SUBMITURB    _IOR('U', 10, struct usb_urb)
#define IOCTL_USB_DISCARDURB    _IO('U', 11)
#define IOCTL_USB_REAPURB    _IOW('U', 12, void *)
#define IOCTL_USB_REAPURBNDELAY    _IOW('U', 13, void *)
#define IOCTL_USB_CLAIMINTF    _IOR('U', 15, unsigned int)
#define IOCTL_USB_RELEASEINTF    _IOR('U', 16, unsigned int)
#define IOCTL_USB_CONNECTINFO    _IOW('U', 17, struct usb_connectinfo)
#define IOCTL_USB_IOCTL _IOWR('U', 18, struct usb_ioctl)
#define IOCTL_USB_HUB_PORTINFO    _IOR('U', 19, struct usb_hub_portinfo)
#define IOCTL_USB_RESET        _IO('U', 20)
#define IOCTL_USB_CLEAR_HALT    _IOR('U', 21, unsigned int)
#define IOCTL_USB_DISCONNECT    _IO('U', 22)
#define IOCTL_USB_CONNECT    _IO('U', 23)
int usb_reset(usb_dev_handle *dev)
{
  int ret;

  ret = ioctl(dev->fd, IOCTL_USB_RESET, NULL);
  if (ret)
     USB_ERROR_STR(-errno, "could not reset: %s", strerror(errno));

  return 0;
}

int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name,
    unsigned int namelen)
{
  struct usb_getdriver getdrv;
  int ret;

  getdrv.interface = interface;
  ret = ioctl(dev->fd, IOCTL_USB_GETDRIVER, &getdrv);
  if (ret)
    USB_ERROR_STR(-errno, "could not get bound driver: %s", strerror(errno));

  strncpy(name, getdrv.driver, namelen - 1);
  name[namelen - 1] = 0;

  return 0;
}

int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface)
{
  struct usb_ioctl command;
  int ret;

  command.ifno = interface;
  command.ioctl_code = IOCTL_USB_DISCONNECT;
  command.data = NULL;

  ret = ioctl(dev->fd, IOCTL_USB_IOCTL, &command);
  if (ret)
    USB_ERROR_STR(-errno, "could not detach kernel driver from interface %d: %s",
        interface, strerror(errno));

  return 0;
}
====================================================================
devh = usb_open(dev);
ret = usb_get_driver_np(devh, 0, buf, sizeof(buf));
ret = usb_detach_kernel_driver_np(devh, 0);//断开设备
ret = usb_claim_interface(devh, 0);//改变一个usb设备的接口,一个接口就是一个独立的功能
ret = usb_set_altinterface(devh, 0);//切换的实际动作并不在这里执行,而是由处理uevent事件的mountd完成[luther.gliethttp].
ret = usb_bulk_write(devh, endpoint, message, length, 0);
ret = usb_release_interface(devh, 0);
ret = usb_close(devh);

int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size,
    int timeout)
{
  /* Ensure the endpoint address is correct */
  return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size,
        timeout);
}
=>usb_urb_transfer
=>ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb);//提交写操作

usb_detach_kernel_driver_np
=>ioctl(dev->fd, IOCTL_USB_IOCTL, &command);
=>kernel中调用usb_driver_release_interface
=>来将dev和driver拆开,同时device_is_registered如果有匹配的driver了=>device_release_driver释放和dev匹配上的driver彼此链表.

int usb_claim_interface(usb_dev_handle *dev, int interface)
{
  int ret;

  ret = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &interface);
  if (ret < 0) {
    if (errno == EBUSY && usb_debug > 0)
      fprintf(stderr, "Check that you have permissions to write to %s/%s and, if you don't, that you set up hotplug (http://linux-hotplug.sourceforge.net/) correctly.\n", dev->bus->dirname, dev->device->filename);

    USB_ERROR_STR(-errno, "could not claim interface %d: %s", interface,
    strerror(errno));
  }

  dev->interface = interface;

  return 0;
}
usb_claim_interface
=>ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &interface);
=>case USBDEVFS_CLAIMINTERFACE:
=>kernel中调用proc_claiminterface来重新设定usb设备的接口,接口信息有usb设备描述符和接口描述符中指定

int usb_set_altinterface(usb_dev_handle *dev, int alternate)
{
  int ret;
  struct usb_setinterface setintf;

  if (dev->interface < 0)
    USB_ERROR(-EINVAL);

  setintf.interface = dev->interface;
  setintf.altsetting = alternate;

  ret = ioctl(dev->fd, IOCTL_USB_SETINTF, &setintf);
  if (ret < 0)
    USB_ERROR_STR(-errno, "could not set alt intf %d/%d: %s",
    dev->interface, alternate, strerror(errno));

  dev->altsetting = alternate;

  return 0;
}
usb_set_altinterface
=>ioctl(dev->fd, IOCTL_USB_SETINTF, &setintf);
=>case USBDEVFS_SETINTERFACE:
=>kernel中调用proc_setintf改变kernel中对该usb设备的接口序号和描述符,同时u
  sb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                 USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
                 alternate, interface, NULL, 0, 5000);
  下发数据到usb设备,让设备改变相应接口对应的驱动程序,这样设备当断开usb总线或者hub发送reset复位总线时,usb
  设备就会发送指定接口对应的接口描述符下的endpoint端点信息供kernel使用.
====================================================================
include/linux/usbdevice_fs.h
#define USBDEVFS_MAXDRIVERNAME 255

struct usbdevfs_getdriver {
    unsigned int interface;
    char driver[USBDEVFS_MAXDRIVERNAME + 1];
};
#define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer)
#define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer)
#define USBDEVFS_RESETEP _IOR('U', 3, unsigned int)
#define USBDEVFS_SETINTERFACE _IOR('U', 4, struct usbdevfs_setinterface)
#define USBDEVFS_SETCONFIGURATION _IOR('U', 5, unsigned int)
#define USBDEVFS_GETDRIVER _IOW('U', 8, struct usbdevfs_getdriver)
#define USBDEVFS_SUBMITURB _IOR('U', 10, struct usbdevfs_urb)
#define USBDEVFS_SUBMITURB32 _IOR('U', 10, struct usbdevfs_urb32)
#define USBDEVFS_DISCARDURB _IO('U', 11)
#define USBDEVFS_REAPURB _IOW('U', 12, void *)
#define USBDEVFS_REAPURB32 _IOW('U', 12, __u32)
#define USBDEVFS_REAPURBNDELAY _IOW('U', 13, void *)
#define USBDEVFS_REAPURBNDELAY32 _IOW('U', 13, __u32)
#define USBDEVFS_DISCSIGNAL _IOR('U', 14, struct usbdevfs_disconnectsignal)
#define USBDEVFS_CLAIMINTERFACE _IOR('U', 15, unsigned int)
#define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int)
#define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo)
#define USBDEVFS_IOCTL _IOWR('U', 18, struct usbdevfs_ioctl)
#define USBDEVFS_IOCTL32 _IOWR('U', 18, struct usbdevfs_ioctl32)
#define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo)
#define USBDEVFS_RESET _IO('U', 20)
#define USBDEVFS_CLEAR_HALT _IOR('U', 21, unsigned int)
#define USBDEVFS_DISCONNECT _IO('U', 22)
#define USBDEVFS_CONNECT _IO('U', 23)
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;
    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);
    }
    return ret;
}

static int usbdev_ioctl(struct inode *inode, struct file *file,
            unsigned int cmd, unsigned long arg)
{
    struct dev_state *ps = file->private_data;
    struct usb_device *dev = ps->dev;
    void __user *p = (void __user *)arg;
    int ret = -ENOTTY;

    if (!(file->f_mode & FMODE_WRITE))
        return -EPERM;
    usb_lock_device(dev);
    if (!connected(ps)) {
        usb_unlock_device(dev);
        return -ENODEV;
    }

    switch (cmd) {
    case USBDEVFS_CONTROL:
    ...
}
const struct file_operations usbdev_file_operations = {
    .owner =     THIS_MODULE,
    .llseek =    usbdev_lseek,
    .read =        usbdev_read,
    .poll =        usbdev_poll,
    .ioctl =    usbdev_ioctl,
    .open =        usbdev_open,
    .release =    usbdev_release,
};
int __init usb_devio_init(void)
{
    ...
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
#define USB_MAJOR            180
#define USB_DEVICE_MAJOR    189
#define USB_MAXBUS            64
#define USB_DEVICE_MAX     USB_MAXBUS * 128
    retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
                    "usb_device");
    cdev_init(&usb_device_cdev, &usbdev_file_operations);
    retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);//添加字符节点到cdev_map
    ...
}
drivers/usb/core/usb.c
static int __init usb_init(void)
{
    ...
    retval = usb_devio_init();
    ...
}
subsys_initcall(usb_init);
====================================================================
drivers/usb/gadget/file_storage.c
static int __init fsg_init(void)
{
    int        rc;
    struct fsg_dev    *fsg;

    if ((rc = fsg_alloc()) != 0)
        return rc;
    fsg = the_fsg;
    if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0)
        kref_put(&fsg->ref, fsg_release);
    return rc;
}
module_init(fsg_init);
static struct usb_gadget_driver    fsg_driver = {
    ...
    .bind        = fsg_bind,
    ...
};
fsg_bind
=>fsg->thread_task = kthread_create(fsg_main_thread, fsg, "file-storage-gadget");
=>fsg_main_thread
=>do_scsi_command
在
static int do_scsi_command(struct fsg_dev *fsg)
{
    ...
#if defined(CONFIG_USB_MODE_SWITCH)
    case SC_USB_MODESWITCH:
        
        if(fsg->cmnd[11]==0x35)//switch to usbnet
        {
            printk(" SC_USB_MODESWITCH: usbnet mode: %d\n", fsg->cmnd[11]);    
            udc_sysfs_data.mode=USB_SWITCH_CMD;    
            udc_kobject_uevent(&udc_sysfs_data,KOBJ_CHANGE,1);
//init进程没有对change event事件进行处理,mountd守护进程的detect_thread线程会等待该uevent事件到来,然后
//卸载前一个ko,加载欲成为设备的相应ko驱动[luther.gliethttp].
        }
    ...
}
在使用libusb的应用程序中:
char net_mode[]="55534243123456780000000000000cd6000000000000000000003500000000";//scsi命令.
将net_mode字符串转为hex之后调用usb_bulk_write(devh, endpoint, message, length, 0);将转换后的net_mode下发下去.
static ssize_t switch_mode_show(struct device *dev,
                 struct device_attribute *attr,
                 char *buf)
{
    return sprintf(buf, "%d\n", udc_sysfs_data.mode);
}
static ssize_t switch_testmode_store(struct device *dev,
                 struct device_attribute *attr,
                 const char *buf, size_t count)
{
    
    if(buf[0]=='1')    {//input "echo 1 > testmode " in the console will switch to usbnet mode
        udc_sysfs_data.testmode=1;//testmode has 2 value, value 0 is no-usbnet mode,value 1 usbnet mode.
        udc_sysfs_data.mode=5;//mode=5,inform usbd to switch to usbnet mode;
        kobject_uevent(&udc_dev->kobj, KOBJ_CHANGE);
    }
    return count;
}
static ssize_t switch_net_show(struct device *dev,
                 struct device_attribute *attr,
                 char *buf)
{
    return sprintf(buf, "%d\n", udc_sysfs_data.net_enum);
}
static ssize_t switch_testmode_show(struct device *dev,
                 struct device_attribute *attr,
                 char *buf)
{
    return sprintf(buf, "%d\n", udc_sysfs_data.testmode);
}
DEVICE_ATTR(mode, S_IRUGO, switch_mode_show, NULL);
DEVICE_ATTR(net_enum, S_IRUGO, switch_net_show, NULL);
DEVICE_ATTR(testmode, 644, switch_testmode_show, switch_testmode_store);
static int udc_uevent(struct device *dev, struct kobj_uevent_env *env)
{
    if (add_uevent_var(env, "MODE=%d", udc_sysfs_data.mode))//传递出去,给mountd守护daemon程序
            return -ENOMEM;
    if (add_uevent_var(env, "NET_ENUM=%d", udc_sysfs_data.net_enum))
            return -ENOMEM;
    if (add_uevent_var(env, "TESTMODE=%d", udc_sysfs_data.testmode))
            return -ENOMEM;
    return 0;
}
udc_class_init=>udc_class->dev_uevent = udc_uevent;
void udc_kobject_uevent(struct udc_sysfs_data *data, 
            enum kobject_action action,bool uevent)
{
    udc_sysfs_data.mode = data->mode;
    udc_sysfs_data.net_enum = data->net_enum;
    if(data->net_enum!=0)
    udc_sysfs_data.testmode=1;
    else
    udc_sysfs_data.testmode=0;
    if(uevent)
    kobject_uevent(&udc_dev->kobj, action);
}


arch/arm/mach-pxa/devices.c
struct platform_device pxa27x_device_udc = {
    .name        = "pxa27x-udc",
    .id        = -1,
    .resource    = pxa2xx_udc_resources,
    .num_resources    = ARRAY_SIZE(pxa2xx_udc_resources),
    .dev        = {
        .platform_data    = &pxa_udc_info,
        .dma_mask    = &udc_dma_mask,
#ifdef CONFIG_PXA3xx
        .coherent_dma_mask = 0xffffffff,
#endif
    }
};
和
drivers/usb/gadget/pxa27x_udc.c
static struct platform_driver udc_driver = {
    .driver    = {
        .name    = "pxa27x-udc",
    },
    .probe        = pxa27x_udc_probe,
    .remove        = __exit_p(pxa27x_udc_remove),
#ifdef CONFIG_PM
    /* with accessory detection module, usb client driver does not need to handle PM */
    //.suspend     = pxa27x_udc_suspend,
    //.resume     = pxa27x_udc_resume
#endif
};
配对.
arch/arm/mach-pxa/luther.c
static void __init luther_init(void)
{
    ...
    #if defined(CONFIG_USB_PXA27X_UDC) || defined(CONFIG_USB_PXA27X_UDC_MODULE)
    if (device_is_enabled(device_udc)) {
        pxa_set_udc_info(&luther_udc_info);
    }
    #endif
    ...
}
#if defined(CONFIG_USB_PXA27X_UDC) || defined(CONFIG_USB_PXA27X_UDC_MODULE)

int luther_udc_is_miniA(void)
{
    return 0;
}

static struct pxa2xx_udc_mach_info luther_udc_info = {
    .udc_is_miniA = luther_udc_is_miniA,
};
#endif


drivers/usb/gadget/pxa27x_udc.c
=>usb_gadget_register_driver
=>comp_register_driver
=>gadget_info_init(dev, gadget, driver);
=>dev->first_gadget = dev->active_gadget = info;
//从usb_gadget_register_driver的实现来看,insmod xxx.ko,然后重新插拔一下usb cable,那么pc再次枚举到的设备就是
//insmod xxx.ko对应的设备了[luther.gliethttp]
1.fsg_init [drivers/usb/gadget/file_storage.c]
=>usb_gadget_register_driver(&fsg_driver);
2.init [drivers/usb/gadget/ether.c]
=>usb_gadget_register_driver(&eth_driver);
drivers/usb/gadget/ether.c
static int __init init (void)
{
    return usb_gadget_register_driver (&eth_driver);
}
module_init (init);

drivers/usb/gadget/file_storage.c
static int __init fsg_init(void)
{
    int        rc;
    struct fsg_dev    *fsg;

    if ((rc = fsg_alloc()) != 0)
        return rc;
    fsg = the_fsg;
    if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0)
        kref_put(&fsg->ref, fsg_release);
    return rc;
}
module_init(fsg_init);
static struct usb_gadget_driver        fsg_driver = {
    ...
    .bind        = fsg_bind,
    ...
};

fsg_setup
=>standard_setup_req
=>将处理USB_REQ_GET_DESCRIPTOR等端点0的描述符请求处理
static int standard_setup_req(struct fsg_dev *fsg,
        const struct usb_ctrlrequest *ctrl)
{
    ...
        switch (ctrl->bRequest) {

    case USB_REQ_GET_DESCRIPTOR:
        if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
                USB_RECIP_DEVICE))
            break;
        switch (w_value >> 8) {

        case USB_DT_DEVICE:
            VDBG(fsg, "get device descriptor\n");
            value = sizeof device_desc;
            memcpy(req->buf, &device_desc, value);//拷贝设备描述符
            break;
        case USB_DT_DEVICE_QUALIFIER:
            VDBG(fsg, "get device qualifier\n");
            if (!gadget_is_dualspeed(fsg->gadget))
                break;
            value = sizeof dev_qualifier;
            memcpy(req->buf, &dev_qualifier, value);
            break;

        case USB_DT_OTHER_SPEED_CONFIG:
            VDBG(fsg, "get other-speed config descriptor\n");
            if (!gadget_is_dualspeed(fsg->gadget))
                break;
            goto get_config;
        case USB_DT_CONFIG:
            VDBG(fsg, "get configuration descriptor\n");
get_config:
            value = populate_config_buf(fsg->gadget,
                    req->buf,
                    w_value >> 8,
                    w_value & 0xff);
            break;

        case USB_DT_STRING:
            VDBG(fsg, "get string descriptor\n");

            /* wIndex == language code */
            value = usb_gadget_get_string(&stringtab,
                    w_value & 0xff, req->buf);
            break;
        }
        break;
    ...
}

#define CDC_VENDOR_NUM    0x0525        /* NetChip */
#define CDC_PRODUCT_NUM    0xa4a1        /* Linux-USB Ethernet Gadget */
#define DRIVER_VENDOR_ID    0x0525    // NetChip
#define DRIVER_PRODUCT_ID    0xa4a5    // Linux-USB File-backed Storage Gadget
drivers/usb/gadget/file_storage.c
static struct usb_device_descriptor
device_desc = {
    .bLength =        sizeof device_desc,
    .bDescriptorType =    USB_DT_DEVICE,

    .bcdUSB =        __constant_cpu_to_le16(0x0200),
    .bDeviceClass =        USB_CLASS_PER_INTERFACE,

    /* The next three values can be overridden by module parameters */
    .idVendor =        __constant_cpu_to_le16(DRIVER_VENDOR_ID),
    .idProduct =        __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
    .bcdDevice =        __constant_cpu_to_le16(0xffff),

    .iManufacturer =    STRING_MANUFACTURER,
    .iProduct =        STRING_PRODUCT,
    .iSerialNumber =    STRING_SERIAL,
    .bNumConfigurations =    1,
};
drivers/usb/gadget/ether.c
static struct usb_device_descriptor
device_desc = {
    .bLength =        sizeof device_desc,
    .bDescriptorType =    USB_DT_DEVICE,

    .bcdUSB =        __constant_cpu_to_le16 (0x0200),

    .bDeviceClass =        USB_CLASS_COMM,
#ifndef CONFIG_USB_COMPOSITE
    .bDeviceSubClass =    0,
#else
    .bDeviceSubClass =     USB_CDC_SUBCLASS_ETHERNET,
#endif
    .bDeviceProtocol =    0,

    .idVendor =        __constant_cpu_to_le16 (CDC_VENDOR_NUM),
    .idProduct =        __constant_cpu_to_le16 (CDC_PRODUCT_NUM),
    .iManufacturer =    STRING_MANUFACTURER,
    .iProduct =        STRING_PRODUCT,
    .bNumConfigurations =    1,
};
====================================================================
/system/bin/mountd就是mountd守护daemon程序.
system/bin/mountd/USBDetect.c|78| sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
system/bin/mountd/AutoMount.c|638| fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
system/bin/mountd/mountd.c
int main(int argc, char* argv[])
{
    const char* configPath = "/system/etc/mountd.conf";
    int i; 

    for (i = 1; i < argc; i++) {
        const char* arg = argv[i];
        
        if (strcmp(arg, "-f") == 0) {
            if (i < argc - 1)
                configPath = argv[++i];
        }
    }
        
    ReadConfigFile(configPath);
    StartAutoMounter();
    start_detect_server();
    return RunServer();
}

start_detect_server
=>pthread_create(&detect_pid, NULL, detect_thread, NULL);
=>detect_thread
=>sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
static void* detect_thread(void* arg)
{
    ...
    is_test_mode();
    ...
    if (0 == strncmp(((char *)usb_msghdr), USB_SWITCH_MSG, strlen(USB_SWITCH_MSG))) {
        ums2net_or_serial();//收到改变的uevent
    }
    ...
}

static int is_test_mode(void)
{
    usb_premode = parse_feature_conf();
    switch (usb_premode) {
        case TEST_MODE:
            for (i = 0;i < TRY_NUM;i++) {
//尝试最大次数TRY_NUM
//在测试期间,所以默认加载g_ether.ko模块[luther.gliethttp]
                if ((status = system("insmod /system/lib/modules/g_ether.ko") < 0)) {
                    LOG_ERROR("USB_DETECT: insmod g_ether.ko error!\n");
                    continue;
                } else {
                    usb_drv_state = NET_DRV;
                    break;
                }
            }
        break;
        case UMS_MODE:
            break;
        case NET_MODE:
            break;
        case SERIAL_MODE:
            break;
        default:
            break;
    }

    return usb_premode;
}
#define FEATURE_CONF_DATA "/data/etc/feature.conf"
#define FEATURE_CONF_SYSTEM "/system/etc/feature.conf"
static int parse_feature_conf(void)
{
    ...
    fp = fopen(FEATURE_CONF_DATA, "r");
    buf = (char*) malloc(1024);
    memset(buf, 0, 1024);
    fread(buf, 1, 1020, fp);
    
    if (strstr(buf, "tc_mod")) {
        ret = TEST_MODE;
    } else if (strstr(buf, "ums_mod")) {
        ret = UMS_MODE;
    } else if (strstr(buf, "net_mod")) {
        ret = NET_MODE;
    } else if (strstr(buf, "serial_mod")) {
        ret = SERIAL_MODE;
    } else {
        ret = -1;
    }
    ...
}

static int ums2net_or_serial(void)
{
    ...
    ret = read_usb_switch();//其实uevent已经在udc_uevent中将ret数值打入了uevent strings中了
    //MODE=;NET_ENUM=;TESTMODE=;直接解析就可以了,所以就可以不用调用read_usb_switch了[luther.gliethttp].
    ...
    if (UMS_DRV == usb_drv_state) {
     ...
        system("/telephony/bin/busybox rmmod g_file_storage.ko");//因为改变之前是ums.ko驱动,所以先rmmod卸载掉它
        ...
    }
    ...
    if (UMS2NET == ret) {
        ...
        system("insmod /system/lib/modules/g_ether.ko");//欲切换到ethernet功能,所以insmod插入它的驱动.
        ...
    }
    ...
    if (UMS2SERIAL == ret) {
        ...
        system("/telephony/bin/busybox insmod /system/lib/modules/g_serial.ko use_acm=1");//欲切换到serial串口功能.
        ...
    }
    ...
}
#define USB_SWITCH_MODE "/sys/class/udc/pxa27x_udc/mode"
static int read_usb_switch(void)
{
    int usb_switch_mode;
    int fd = 0;

    fd = open(USB_SWITCH_MODE, O_RDONLY);//调用switch_mode_show读取数据
//调用驱动中的注册的属性文件,对应的方法
//DEVICE_ATTR(mode, S_IRUGO, switch_mode_show, NULL);
//DEVICE_ATTR(net_enum, S_IRUGO, switch_net_show, NULL);
//DEVICE_ATTR(testmode, 644, switch_testmode_show, switch_testmode_store);
    if (fd < 0) {
        LOG_ERROR("USB_DETECT: cannot open sys file %s!\n", USB_SWITCH_MODE);
        return -1;
    }

    read(fd, ((void *)&usb_switch_mode), sizeof(int)); //read sys interface
    usb_switch_mode -= 0x30; //ascii to int
    LOG_MOUNT("USB_DETECT: switched mode is '%d'\n", usb_switch_mode);
    close(fd);

    return usb_switch_mode;
}

StartAutoMounter
=>pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
=>AutoMountThread
=>CreateUEventSocket
=>fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

 

linux usb 同步传输,从设备到主机的用户模式USB等时传输 我目前正在尝试与用户土地上的USB音频设备接口。目前,我已经完全枚举了该设备,并且已经设置了接口并将备用接口设置为非零带宽备用接口。首先,我不得不说我不能使用libusb之类的东西。我已通过Linux的USB设备文件系统执行此操作。据我所知,我准备开始接收同步数据。但是,我几乎找不到有关如何进行同步传输的信息。据我所知,我需要填充usbdevfs_urb结构,但是我完全不确定如何准确填充该结构。另... 阅读详情

相关推荐

USB系列-LibUSB使用指南(1)-Windows下的报错与踩坑

Windows下的LibUSB开发的大怨种踩坑日记

The Road of Engineer 2万+

CodeForces 721A One-dimensional Japanese Crossword(第三场)

,,,

Shyazhut 2706

别再手动算K值了!ZEMAX中两种锥透镜建模方法详解(标准面 vs. 轴锥面)

本文详细解析了ZEMAX中锥透镜的两种建模方法:标准面与轴锥面。通过对比两者的精度、计算效率和适用场景,帮助工程师在激光整形、光束匀化等应用中做出最优选择,并提供了参数设置和异常排查的实用技巧。

weixin_42548874的博客 316

C++——【USACO 4.4.1】——Shuttle Puzzle

Shuttle Puzzle Traditional The Shuttle Puzzle of size 3 consists of 3 white marbles, 3 black marbles, and a strip of wood with 7 holes. The marbles of the same color are placed in the holes at the

McDonnell_Douglas的博客 9万+

调试GMS应用,报错“此设备未获得play保护机制认证”问题解决

不少同学在调试GMS相关应用时,需登录Google账号,有时会弹出如下通知。Google登录界面也会出现如下提示这个报错的原因是设备未通过Google认证,google服务器未配置荣耀设备的型号白名单导致国内网页有一些指导方法在鸿蒙\荣耀的设备上消除这个提示,但本质的问题没有解决,无法登录Google账号。

胡同老道的博客 2万+

手把手教你用SNAP做全极化SAR图像分类

引言 最近在研究极化SAR图像分类方面的内容,发现用SNAP软件可以很方便的实现图像的分类。因为在入门过程中踩了很多坑,浪费了一些时间,为了不让大家踩坑,所以决定写下这篇教程。 前期准备 软件下载 SNAP是欧空局旗下的一个SAR图像处理软件,可以点击下载地址进行下载。 数据下载 数据下载地址:https://mdacorporation.com/sample_data/RS2_OK76385_...

zz651044657的博客 1万+

寒假翻译三

寒假翻译三

enenen1108的博客 48万+

Open3D(C++) 读取、可视化并保存点云【2025最新版】

博客长期更新,本文最近更新时间为:2025年1月6日。

点云侠的博客 4612

poj 1753 filp game 2015年暑假训练第一题 枚举

题意就是说:翻牌,随便翻到一张牌,其上下左右随他都得翻,看最少用翻几张牌,能够让16张牌全为正或全为负。 这道题有一个大坑,就是每一张牌其实只能翻奇数次,比如翻的顺序为 (3,2)->   (2,2)  ->  (3,2)   = =   (2,2)  是  等价的 故最多翻的个数是16,从0到16一个一个测试,枚举的是翻牌的个数,若全为正或反,则断开枚举,否则一直枚举 最多有C(0,1

sholck222的专栏 229万+

OpenClaw 与十几款国产 “龙虾” 智能体深度对比

详解介绍OpenClaw和各种类似国产“龙虾”智能体工具深度对比

program哲学 2328

【AI面试】 Python 的装饰器

Python装饰器是一种高阶函数,用于在不修改原函数代码的情况下增强其功能。它通过@语法糖接收目标函数作为参数,并返回一个包含额外逻辑(如日志、缓存、权限验证等)的新函数。装饰器的核心价值在于代码复用和解耦,符合开闭原则,典型应用包括性能分析、权限控制等场景。例如,@timer_decorator可自动为函数添加耗时统计功能。本质上,@decorator等价于func = decorator(func),实际调用的是装饰器返回的包装函数。进阶技巧包括使用@functools.wraps保留原函数元信息。装饰

钱多多先森 174

Codeforces Round #168 (Div. 2) B. Convex Shape(BFS || 模拟)

题目链接:https://codeforces.com/problemset/problem/275/B 题意:从任意一个黑格子到任意一个黑格子都可以互达,且方向变化不超过1次(只能走黑格子)。 模拟思路:从一个黑色格到任意一个黑色格看能否通过两种方式到达其他任意一个黑色格子。 #include <bits/stdc++.h> using namespace std; #def...

sugarbliss 33万+

YOLOv11改进策略【损失函数篇】| 替换激活函数为Mish、PReLU、Hardswish、LeakyReLU、ReLU6

在卷积神经网络中,激活函数通过在神经网络中引入非线性,使网络能够学习和逼近复杂函数,从而处理复杂的模式识别与分类问题。

Limiiiing的博客 2754

Flip Game(枚举)

~题目链接~ http://poj.org/problem?id=1753 可参考大神题解(位运算):http://www.cnblogs.com/tanhehe/archive/2013/06/11/3131615.html http://www.cnblogs.com/kuangbin/archive/20...

ddn220120的博客 13万+

YOLOv11改进策略【Neck】| GFPN 超越BiFPN 通过跳层连接和跨尺度连接改进v11颈部网络

本文记录的是利用颈部结构优化YOLOv11的目标检测网络模型。利用改进后的颈部网络,通过跳层连接,避免了在进行反向传播时的梯度消失问题,并且引入跨尺度连接,可以实现不同级别和层次的特征充分融合,获取足够的高层语义信息和低层空间信息交换,从而在大规模变化场景下提高检测性能。GIRAFFEDET: A HEAVY-NECK PARADIGM FOR OBJECT DETECTION-GFPN包含了上述的跳层连接(dense-link和log2nlog _{2} nlog2​n-link)以及跨尺度连接(Quee

Limiiiing的博客 2989

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Not Shading

描述: There is a grid withnnrows andmmcolumns. Some cells are colored black, and the rest of the cells are colored white. In one operation, you can select someblackcell and doexactly oneof the following: color all cells in its row black, or color...

weixin_62767819的博客 24万+

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

libusb 串口 android,浅析libusb控制接口和mountd守护进程处理uevent切换usb设备的实现...

浅析libusb控制接口和mountd守护进程处理uevent切换usb设备的实现1. UMS mode (USB Mass-Storage mode) [ums]2. 从usb_gadget_register_driver的实现来看,insmod xxx.ko,然后重新插拔一下usb cable,那么pc再次枚举到的设备就是insmod xxx.ko对应的设备了[luther.gliethttp...

weixin_29193359的博客 9572

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

POJ 1753 Flip Game BFS

Flip Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 27410   Accepted: 11902 Description Flip game is played on a rectangular 4x4 field with two-sided p

kdwycz的刷题空间 3万+

One-dimensional Japanese Crossword(codeforces 712A)

Input 3 BBW Output 1 2 Input 5 BWBWB Output 3 1 1 1 Input 4 WWWW Output 0 Input 4 BBBB Output 1 4 Input 13 WBBBBWWBWBBBW Output 3 4 1 3 题意:就是将连续出现的B记录下来并输出。 #include <algorithm> #i...

ZCY的博客 185万+

当你已经23~男生女生都该看

女生篇                   在路上主动和你套近乎的陌生人,就不要傻不拉叽的给人电话号码了,你会被短信电话骚扰致死的。        在场上不要标榜自己多能喝,很男人的与人划拳喝酒了,真正在乎你的人是不会看着你喝到醉醺醺的。        告别灰姑娘和白马王子的白日梦吧,你的脚没那么小,穿不进那水晶鞋。        有给男友买进口剃须刀的钱,不如给老爸买双袜子,他穿着你老妈

中国风的专栏 1352

Broken Necklace

Broken Necklace You have a necklace of N red, white, or blue beads (3 1 2 1 2 r b b r b r r b r

jimzhai的专栏 17万+

USACO Section 4.4 Shuttle Puzzle

开始看的时候觉得是宽搜,后来发现状态量太大了。看了一下Sample感觉有规律n = 4WWWW□BBBBWWW□WBBBBWWWBW□BBBWWWBWB□BBWWWB□BWBBWW□BWBWBBW□WBWBWBBWBW□WBWBBWBWBW□WBBWBWBWBW□BWBWBWBWB□WBWBWB□BWWBWB□BWBWWB□BWBWBW□BWBWBWBWB□WBWBWBWBBW□WBWBWBBWBW□WBWBBWBWBW□WBBWBWB□WWBBWB□BWWWBB□BWBWWWBBB□WBWWWBBBBW□

Space_midtree的专栏 156万+

signature=dd0970a9b662473bb13dbfcf9427df37,KKnnoowwlleeddggee iinn SSoouunndd DDeessiiggnn:: TThheeS...

摘要:This article builds on a large industry-driven sound design experiment focusing on the underexplored area of sound signature for silent electric vehicles. On the basis of some retrospective observa...

weixin_39880318的博客 4万+

POJ 1753 Flip Game BFS+位压缩状态

http://acm.pku.edu.cn/JudgeOnline/problem?id=1753Flip GameTime Limit: 1000MSMemory Limit: 65536KTotal Submissions: 2565Accepted: 1016DescriptionFlip game is played on a rec

饕饕不绝的专栏 1万+

AAAAABBBBB

AAAAABBBCCCCDDDAAABBBBCCCCCDDDDDAAAAABBBCCCCDDDAAABBBBCCCCCDDDDDAAAAABBBCCCCDDDAAABBBBCCCCCDDDDDAAAAABBBCCCCDDDAAABBBBCCCCCDDDDDAAAAABBBCCCCDDDAAABBBBCCCCCDDDDD

xiaobao007的专栏 29万+

5.5 USB虚拟化

本节分析qemu中usb 的虚拟化原理

wanthelping的博客 4314
上一篇: C语言高级用法---typeof()关键字***
下一篇: android studio 显示混乱, 本地文件正常
隨意的風
隨意的風 领域专家: C/C++技术领域 领域专家: C/C++技术领域
博客等级 码龄19年 1万+粉丝 551原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值