浅析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 () 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(ð_driver);
drivers/usb/gadget/ether.c
static int __init init (void)
{
return usb_gadget_register_driver (ð_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);
|