浅析usb转serial串口设备在linux内核中枚举创建及生成tty设备的全过程
1.usb_register和usb_register_driver用来注册一个interface接口驱动for_devices = 0;
2.usb_register_device_driver用来注册一个usb设备驱动,for_devices = 1;用来解析设备描述符,
进而生成配置描述符下的功能接口,尝试匹配usb_register_driver注册的接口驱动来驱动该usb设备的功能接口.[luther.gliethttp]
在整个kernel中,只有usb_init即[subsys_initcall(usb_init)]一处调用了usb_register_device_driver函数,
usb_register_device_driver(&usb_generic_driver, THIS_MODULE);所以所有通过hub_thread检测到插入的usb设备也都将调用到
generic_probe设备枚举函数,我们这里需要提到一些usb通信方面的知识,以便我们能够透彻理解kernel中usb代码,
一个插入到HUB上的usb设备使用4种描述符来描述自己,
(1) 设备描述符
(2) 配置描述符
(3) 接口描述符
(4) 端点描述符
一个usb设备只能有1个设备描述符,1个设备描述符可以有多个配置描述符,然后每个配置描述符下面又可以有多个接口描述符用来
具体描述一个设备在该配置下的一个或多个独立功能,每个接口下面又由端点描述符来具体声明该功能接口在usb物理通信中使用哪几个
端点管道来执行usb物理信道的实际收发工作[luther.gliethttp].
所以一个usb设备的具体功能是由接口描述符来描述的,因此我们开发的usb driver也就几乎99.9%都在使用usb_register函数来实现一个
接口对应的驱动,进而驱动usb设备上该接口对应的具体功能,比如UMS.[luther.gliethttp]
当kernel使用generic_probe()函数完成插入到HUB上的usb设备的合法检验之后,将调用设置配置描述符操作usb_set_configuration,
生成该配置描述符下面若干个接口描述符对应的dev设备,
usb_set_configuration
==>device_add(&intf->dev);
// 这样该接口dev将扫描usb_bus_type总线上的所有drivers驱动,kernel尝试为该接口dev找到驱动它的driver.[luther.gliethttp]
如下几个函数中都会调用到usb_set_configuration
usb_authorize_device // 以sysfs中attr性质存在,这样用户空间的程序就可以通过attr属性文件来强制控制接口驱动的关联.
usb_deauthorize_device
driver_set_config_work
proc_setconfig
set_bConfigurationValue
generic_disconnect
generic_probe
usb设备的检测工作是通过内核线程hub_thread完成的.
usb_hub_init==>khubd_task = kthread_run(hub_thread, NULL, "khubd"); // 创建内核线程hub_thread,监控hub上usb设备的插拔情况
hub_thread
==>hub_events
==>hub_port_connect_change==>udev =usb_alloc_dev // 添加usb设备
==>hub_port_connect_change==>usb_new_device(udev)==>device_add(&udev->dev); // 将检测到的usb设备添加到usb_bus_type总线上,
// 该dev的type值为usb_device_type,最后函数执行device_add==>bus_add_device实现具体添加操作[luther.gliethttp]
// bus_add_device将调用上面usb_register_device_driver(&usb_generic_driver, THIS_MODULE);注册的唯一一个设备描述符解析驱动
// usb_generic_driver==>generic_probe来完成接口设备生成和相应的接口设备驱动关联动作[luther.gliethttp].
usb_add_hcd==>usb_alloc_dev // 添加HCD
usb_alloc_dev
==>dev->dev.bus = &usb_bus_type;设置dev为usb总线上的设备
==>dev->dev.type = &usb_device_type;设置该dev为usb设备而非接口
driver_register或者device_register
调用driver_attach或者bus_attach_device==>device_attach
来为设备尝试匹配驱动或者为驱动尝试添加设备,不论是哪一种情况,都将
执行到:driver_probe_device函数.
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev)) // 1.设备已经完成了注册到bus总线工作
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv)) // 2.执行bus提供的match操作usb_device_match
goto done;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
ret = really_probe(dev, drv); // bus的match通过检验,这里做进一步的probe检验,
// 如果bus提供了probe,那么执行bus->probe(dev);
// 否则执行driver提供的probe函数drv->probe(dev);
done:
return ret;
}
usb_register(&mct_u232_driver);
==>usb_register_driver
new_driver->drvwrap.for_devices = 0; // 仅仅用来驱动interface接口,所以上面hub_port_connect_change由usb_alloc_dev生成的usb设备不会调用该usb接口驱动
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface; // 当检测到usb设备插入后,将调用usb_probe_interface进行细致处理
// 提供为device_driver提供probe处理函数,因为bus总线usb_bus_type不提供probe操作[luther.gliethttp]
==>driver_register(&new_driver->drvwrap.driver); // 将驱动添加到usb bus总线管理的driver驱动链表上
usb_device_match==>is_usb_device
static inline int is_usb_device(const struct device *dev)
{
return dev->type == &usb_device_type;
}
开看看驱动hub_thread==>usb_alloc_dev创建的插入到HUB上的usb设备的probe函数generic_probe.[luther.gliethttp]
subsys_initcall(usb_init);
==>usb_init
==>usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
};
==>generic_probe
==>usb_set_configuration // 生成该设置配置描述下的所有接口描述符所描述的接口dev对象
==>ret = device_add(&intf->dev);
int usb_set_configuration(struct usb_device *dev, int configuration)
{
int i, ret;
struct usb_host_config *cp = NULL;
struct usb_interface **new_interfaces = NULL;
int n, nintf;
if (dev->authorized == 0 || configuration == -1)
configuration = 0;
else {
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue ==
configuration) {
cp = &dev->config[i];
break;
}
}
}
if ((!cp && configuration != 0))
return -EINVAL;
/* The USB spec says configuration 0 means unconfigured.
* But if a device includes a configuration numbered 0,
* we will accept it as a correctly configured state.
* Use -1 if you really want to unconfigure the device.
*/
if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n");
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
if (cp) {
nintf = cp->desc.bNumInterfaces;
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
GFP_KERNEL);
if (!new_interfaces) {
dev_err(&dev->dev, "Out of memory\n");
return -ENOMEM;
}
for (; n < nintf; ++n) {
new_interfaces[n] = kzalloc(
sizeof(struct usb_interface),
GFP_KERNEL);
if (!new_interfaces[n]) {
dev_err(&dev->dev, "Out of memory\n");
ret = -ENOMEM;
free_interfaces:
while (--n >= 0)
kfree(new_interfaces[n]);
kfree(new_interfaces);
return ret;
}
}
i = dev->bus_mA - cp->desc.bMaxPower * 2;
if (i < 0)
dev_warn(&dev->dev, "new config #%d exceeds power "
"limit by %dmA\n",
configuration, -i);
}
/* Wake up the device so we can send it the Set-Config request */
ret = usb_autoresume_device(dev);
if (ret)
goto free_interfaces;
/* if it's already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers.
*/
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device(dev, 1); /* Skip ep0 */
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (ret < 0) {
/* All the old state is gone, so what else can we do?
* The device is probably useless now anyway.
*/
cp = NULL;
}
dev->actconfig = cp;
if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
usb_autosuspend_device(dev);
goto free_interfaces;
}
usb_set_device_state(dev, USB_STATE_CONFIGURED);
/* Initialize the new interface structures and the
* hc/hcd/usbcore interface/endpoint state.
*/
for (i = 0; i < nintf; ++i) {
struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt;
cp->interface[i] = intf = new_interfaces[i];
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
intf->intf_assoc = find_iad(dev, cp, i);
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0);
/* No altsetting 0? We'll assume the first altsetting.
* We could use a GetInterface call, but if a device is
* so non-compliant that it doesn't have altsetting 0
* then I wouldn't trust its reply anyway.
*/
if (!alt)
alt = &intf->altsetting[0];
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf);
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type; // 位于usb_bus_type总线
intf->dev.type = &usb_if_device_type; // 为接口设备,这样在usb_device_match中将去和接口驱动去匹配[luther.gliethttp]
intf->dev.dma_mask = dev->dev.dma_mask;
device_initialize(&intf->dev);
mark_quiesced(intf);
sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
}
kfree(new_interfaces);
if (cp->string == NULL)
cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
/* Now that all the interfaces are set up, register them
* to trigger binding of drivers to interfaces. probe()
* routines may install different altsettings and may
* claim() any interfaces not yet bound. Many class drivers
* need that: CDC, audio, video, etc.
*/
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf