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

本文主要是介绍浅析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]
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 *= (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 (= 1; i < argc; i++) {
        const char* arg = argv[i];
        
        if (strcmp(arg, "-f") == 0) {
            if (< 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 (== 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 (= 0;< 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);

这篇关于浅析libusb控制接口和mountd守护进程处理uevent切换usb设备的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/876748

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P