Chinaunix首页 | 论坛 | 博客
  • 博客访问: 680518
  • 博文数量: 404
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1237
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-03 10:45
文章分类

全部博文(404)

文章存档

2017年(1)

2016年(27)

2015年(39)

2014年(55)

2013年(66)

2012年(216)

分类: 嵌入式

2014-04-20 20:58:54

原文地址:USB 3G卡热插拔那些事2 作者:linuxDOS

上一节我们已经讲到usb_new_device来初始化设备配置,然后让设备工作,我们知道这之前,首先hub检测到端口电流变化,然后分配usb设备地址,申请设备,获取设备一些描述性信息(这些信息有助于以后总线match和probe函数来check是否支持这个设备和找到相应的驱动)。这里我们首先展开usb_new_device这个函数看看它到底做了什么工作。

/**
 * usb_new_device - perform initial device setup (usbcore-internal)
 * @udev: newly addressed device (in ADDRESS state)
 *
 * This is called with devices which have been enumerated, but not yet
 * configured.  The device descriptor is available, but not descriptors
 * for any device configuration.  The caller must have locked either
 * the parent hub (if udev is a normal device) or else the
 * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
 * udev has already been installed, but udev is not yet visible through
 * sysfs or other filesystem code.
 *
 * Returns 0 for success (device is configured and listed, with its
 * interfaces, in sysfs); else a negative errno value.
 *
 * This call is synchronous, and may not be used in an interrupt context.
 *
 * Only the hub driver or root-hub registrar should ever call this.
 */
int usb_new_device(struct usb_device *udev)
{
int err;
int c;

err = usb_get_configuration(udev);
if (err < 0) {
dev_err(&udev->dev, "can't read configurations, error %d\n",
err);
goto fail;
}

/* read the standard strings and cache them if present */
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
udev->manufacturer = usb_cache_string(udev,
udev->descriptor.iManufacturer);
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);

/* Tell the world! */
dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
"SerialNumber=%d\n",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
udev->descriptor.iSerialNumber);
show_string(udev, "Product", udev->product);
show_string(udev, "Manufacturer", udev->manufacturer);
show_string(udev, "SerialNumber", udev->serial);

#ifdef CONFIG_USB_OTG
/*
* OTG-aware devices on OTG-capable root hubs may be able to use SRP,
* to wake us after we've powered off VBUS; and HNP, switching roles
* "host" to "peripheral".  The OTG descriptor helps figure this out.
*/
if (!udev->bus->is_b_host
&& udev->config
&& udev->parent == udev->bus->root_hub) {
struct usb_otg_descriptor *desc = 0;
struct usb_bus *bus = udev->bus;

/* descriptor may appear anywhere in config */
if (__usb_get_extra_descriptor (udev->rawdescriptors[0],
le16_to_cpu(udev->config[0].desc.wTotalLength),
USB_DT_OTG, (void **) &desc) == 0) {
if (desc->bmAttributes & USB_OTG_HNP) {
unsigned port1 = udev->portnum;
struct usb_device *root = udev->parent;
dev_info(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
? "" : "non-");

/* enable HNP before suspend, it's simpler */
if (port1 == bus->otg_port)
bus->b_hnp_enable = 1;
err = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
bus->b_hnp_enable
? USB_DEVICE_B_HNP_ENABLE
: USB_DEVICE_A_ALT_HNP_SUPPORT,
0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (err < 0) {
/* OTG MESSAGE: report errors here,
* customize to match your product.
*/
dev_info(&udev->dev,
"can't set HNP mode; %d\n",
err);
bus->b_hnp_enable = 0;
}
}
}
}

if (!is_targeted(udev)) {

/* Maybe it can talk to us, though we can't talk to it.
* (Includes HNP test device.)
*/
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
static int __usb_suspend_device(struct usb_device *,
int port1);
err = __usb_suspend_device(udev, udev->bus->otg_port);
if (err < 0)
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
}
err = -ENODEV;
goto fail;
}
#endif

/* put device-specific files into sysfs */
err = device_add (&udev->dev);
if (err) {
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
}
usb_create_sysfs_dev_files (udev);

usb_lock_device(udev);

/* choose and set the configuration. that registers the interfaces
* with the driver core, and lets usb device drivers bind to them.
*/
c = choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
if (err) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
/* This need not be fatal.  The user can try to
* set other configurations. */
}
}

/* USB device state == configured ... usable */
usb_notify_add_device(udev);

usb_unlock_device(udev);

return 0;

fail:
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
return err;
}
这里我们看到粗体部分为关键函数,新设备默认为host控制器总线下从设备。首先从外设获取配置信息
 usb_get_configuration(),然后是device_add()这里完成了设备的注册,当然包括设备总线的注册,以及在设备模型中的注册,而真正热插拔事件处理函数是uevent(在lld3中是hotplug函数,不过看2.6以后的内核全部为uevent),这个函数指针初始化是在总线bus_type初始化的时候初始化的,在usb.c文件中代码如下:
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.suspend = usb_generic_suspend,
.resume = usb_generic_resume,
};
我们知道在一个usb设备check的过程中,当设备被注册进usb core后,就是设备的总线的一系列的函数的工作(包括match、probe、uevent)。uevent它的作用就是把内核的一些环境变量传递到用户空间,以便通知用户空间做出相应的事件。
这里我们简单说一下match函数:
int usb_device_match(struct device *dev, struct device_driver *drv)
{
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;

/* check for generic driver, which we don't match any device with */
if (drv == &usb_generic_driver)
return 0;

intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);

id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;

id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
return 0;
}
这里我们只需要关注usb_match_id()这个函数即可,让我们可以真正明白一个热插拔设备识别和检测的机制是如何运作的。这里还要说明一下首先总线检验总线是否支持这个设备,如果支持那么才去找相应的驱动是否支持它,如果支持就和他bind在一起,那么这个设备就可以工作了。我们可以通过/proc来查看usb设备的信息:
#cat /proc/bus/usb/devices
当然我们知道3G卡插入usb口,首先识别出来的是U盘,即加载的是storage驱动,那么我们需要通过usb_modemswitch工具来把它转换成modem模式 (就是usb 串行工作模式  / *usb 串行设备 */ ),它会把总线初始化为usb_serial_bus,并加载usb serial驱动(usb/serial目录下option.c) 而这个通知事件就是通过上边提过的uevent这个函数来完成。如果直接是usb serial设备那么它会直接识别为usb serial总线,并加载相应的驱动。例如有些U盘还附带bluetooth的功能,bluetooth就是串行设备。那么下一节我们将分析usb serial相关驱动。


阅读(551) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~