USB 3G上網絡卡講解之二
USB 3G卡熱插拔那些事3——usb serial驅動
上一節我們已經把3G識別出來並且可以工作了,具體3G卡撥號這一塊我們稍後會講到,這裡先提下,首先就是pppd撥號程式,可以網上下載最新版原始碼自己編譯安裝,我是利用evdo撥號(本人是用的SIM5360E,WCDMA/GSM,即聯通2G/3G),當然還有其他方式。這裡是使用者空間如何建立ppp連線,在核心層就是ppp協議以及tty模組,在往下就是wcdma模組驅動(上一節我們已經講過)。下面就說說usb serial驅動.
在之前我們知道uevent事件,但是對於它的呼叫我一直不太明白,然後就程式碼搜了下(當然這裡知道它是在註冊進裝置模型的時候產生的),在core.c drivers/base,它是在dev_uevent裡呼叫:
if (dev->bus && dev->bus->uevent) {
/* have the bus specific function add its stuff */
retval = dev->bus->uevent(dev, envp, num_envp, buffer, buffer_size);
if (retval) {
pr_debug ("%s - uevent() returned %d\n",
__FUNCTION__, retval);
}
}
然後就追了下,dev_uevent在這裡初始化:
static struct kset_uevent_ops device_uevent_ops = {
.filter = dev_uevent_filter,
.name = dev_uevent_name,
.uevent = dev_uevent,
};
而device_uevent_ops剛好就是一個宣告子系統的巨集裡呼叫:
/*
* devices_subsys - structure to be registered with kobject core.
*/
decl_subsys(devices, &ktype_device, &device_uevent_ops);
言歸正傳,首先我們看下usb serial模組的初始化函式:
static int __init usb_serial_init(void)
{
int i;
int result;
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
if (!usb_serial_tty_driver)
return -ENOMEM;
/* Initialize our global data */
for (i = 0; i < SERIAL_TTY_MINORS; i) {
serial_table[i] = NULL;
}
result = bus_register(&usb_serial_bus_type);
if (result) {
err("%s - registering bus driver failed", __FUNCTION__);
goto exit_bus;
}
usb_serial_tty_driver->owner = THIS_MODULE;
usb_serial_tty_driver->driver_name = "usbserial";
usb_serial_tty_driver->devfs_name = "usb/tts/";
usb_serial_tty_driver->name = "ttyUSB";
usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
usb_serial_tty_driver->minor_start = 0;
usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
usb_serial_tty_driver->init_termios = tty_std_termios;
usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(usb_serial_tty_driver, &serial_ops);
result = tty_register_driver(usb_serial_tty_driver);
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
goto exit_reg_driver;
}
/* register the USB driver */
result = usb_register(&usb_serial_driver);
if (result < 0) {
err("%s - usb_register failed", __FUNCTION__);
goto exit_tty;
}
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < 0) {
err("%s - registering generic driver failed", __FUNCTION__);
goto exit_generic;
}
info(DRIVER_DESC);
return result;
exit_generic:
usb_deregister(&usb_serial_driver);
exit_tty:
tty_unregister_driver(usb_serial_tty_driver);
exit_reg_driver:
bus_unregister(&usb_serial_bus_type);
exit_bus:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
}
這裡我們最關注bus_register(&usb_serial_bus_type)
、 tty_register_driver(usb_serial_tty_driver)
、 usb_register(&usb_serial_driver)
這幾個函式,其實我們知道usb serial的驅動也是註冊到usb bus總線上的,下面我們來看具體的函式usb_register(&usb_serial_driver);
這裡我感覺非常有趣,usb_serial_driver
註冊註冊函式是usb_register
,我們是否明白了點什麼,其實usb驅動沒有我們想象的那麼複雜和難懂^^!
那麼我們來看usb_serial_driver
的初始化:
/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
.name = "usbserial",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.no_dynamic_id = 1,
};
這裡我們只需要關注usb_serial_probe
即可。
int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev (interface);
struct usb_serial *serial = NULL;
struct usb_serial_port *port;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
struct usb_serial_driver *type = NULL;
int retval;
int minor;
int buffer_size;
int i;
int num_interrupt_in = 0;
int num_interrupt_out = 0;
int num_bulk_in = 0;
int num_bulk_out = 0;
int num_ports = 0;
int max_endpoints;
type = search_serial_device(interface);
if (!type) {
dbg("none matched");
return -ENODEV;
}
serial = create_serial (dev, interface, type);
if (!serial) {
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
return -ENOMEM;
}
/* if this device type has a probe function, call it */
if (type->probe) {
const struct usb_device_id *id;
if (!try_module_get(type->driver.owner)) {
dev_err(&interface->dev, "module get failed, exiting\n");
kfree (serial);
return -EIO;
}
id = usb_match_id(interface, type->id_table);
retval = type->probe(serial, id);
module_put(type->driver.owner);
if (retval) {
dbg ("sub driver rejected device");
kfree (serial);
return retval;
}
}
/* descriptor matches, let's find the endpoints needed */
/* check out the endpoints */
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i) {
endpoint = &iface_desc->endpoint[i].desc;
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x02)) {
/* we found a bulk in endpoint */
dbg("found bulk in on endpoint %d", i);
bulk_in_endpoint[num_bulk_in] = endpoint;
num_bulk_in;
}
if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
((endpoint->bmAttributes & 3) == 0x02)) {
/* we found a bulk out endpoint */
dbg("found bulk out on endpoint %d", i);
bulk_out_endpoint[num_bulk_out] = endpoint;
num_bulk_out;
}
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x03)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in on endpoint %d", i);
interrupt_in_endpoint[num_interrupt_in] = endpoint;
num_interrupt_in;
}
if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
((endpoint->bmAttributes & 3) == 0x03)) {
/* we found an interrupt out endpoint */
dbg("found interrupt out on endpoint %d", i);
interrupt_out_endpoint[num_interrupt_out] = endpoint;
num_interrupt_out;
}
}
#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
/* BEGIN HORRIBLE HACK FOR PL2303 */
/* this is needed due to the looney way its endpoints are set up */
if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
(le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
(le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) {
if (interface != dev->actconfig->interface[0]) {
/* check out the endpoints of the other interface*/
iface_desc = dev->actconfig->interface[0]->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i) {
endpoint = &iface_desc->endpoint[i].desc;
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x03)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in for Prolific device on separate interface");
interrupt_in_endpoint[num_interrupt_in] = endpoint;
num_interrupt_in;
}
}
}
/* Now make sure the PL-2303 is configured correctly.
* If not, give up now and hope this hack will work
* properly during a later invocation of usb_serial_probe
*/
if (num_bulk_in == 0 || num_bulk_out == 0) {
dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
kfree (serial);
return -ENODEV;
}
}
/* END HORRIBLE HACK FOR PL2303 */
#endif
/* found all that we need */
dev_info(&interface->dev, "%s converter detected\n", type->description);
...
這個函式後面的程式碼不再貼出,我們看最後一行,因為3G卡整個識別過程我也是根據識別的時候的列印資訊來一步一步分析和驗證的來的。最先呼叫的就是這個usb_serial_probe
函式,如果真的是usb serial
則繼續,後面要執行的就是usb_serial_bus_type
:
struct bus_type usb_serial_bus_type = {
.name = "usb-serial",
.match = usb_serial_device_match,
.probe = usb_serial_device_probe,
.remove = usb_serial_device_remove,
};
接下來執行了usb_serial_device_probe()
,跑到這裡那麼3G卡驅動和tty等已經幫到到一起,可以真正工作了。
我們繼續回到usb_serial_probe
函式裡:
type = search_serial_device(interface);
static struct usb_serial_driver *search_serial_device(struct usb_interface *iface)
{
struct list_head *p;
const struct usb_device_id *id;
struct usb_serial_driver *t;
/* Check if the usb id matches a known device */
list_for_each(p, &usb_serial_driver_list) {
t = list_entry(p, struct usb_serial_driver, driver_list);
id = usb_match_id(iface, t->id_table);
if (id != NULL) {
dbg("descriptor matches");
return t;
}
}
return NULL;
}
這裡就是查詢 usb_serial_bus_type
下注冊的驅動是否有支援它的,而我們看option.c裡程式碼就可以明白,option.c裡驅動是註冊到這個匯流排下的。或許我們應該講講在3G卡插入後識別並載入相應驅動後的工作原理。也就是說3G的工作原理。不過我從網上看到一篇挺不錯了,可以給大家拿來分享。