第一,开发环境
硬件:三星s5p4418
linux version:3.4.39
ubuntu:14.04
usb camera:whois
第二,usb camera调试结果分析
1,将usb camera驱动打入内核,插上usb camera,系统启动后会在kmesg中有如下提示:
<6>[ 1.015000] usb 1-1: New USB device found, idVendor=0547, idProduct=1002
<6>[ 1.015000] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
<6>[ 1.015000] usb 1-1: Product: U02_Sensor
<6>[ 1.015000] usb 1-1: Manufacturer: Whois
<6>[ 1.138000] android_usb gadget: android_usb ready
<4>[ 1.143000] ==========usb_whois_init start========
<4>[ 1.148000] ==========whois probe start========
<6>[ 1.155000] whois_usbcam 1-1:1.0: whois USB camera now attached to whoiscam0==
<6>[ 1.165000] usbcore: registered new interface driver whois_usbcam
<4>[ 1.172000] ==========usb_whois_init end========
表示内核启动过程首先usb host初始化完毕,检测到有一个新的usb设备,并通过握手获得usb从设备的verdorID和productID号,产品类型,产品的厂商名。
随后内核启动调用驱动
static int __init usb_whois_init(void)
{
int result;
printk("==========usb_whois_init start========\n");
result = usb_register(&whois_driver);
if (result)
printk("usb_register failed. Error number %d", result);
printk("==========usb_whois_init end========\n");
return result;
}
module_init(usb_whois_init);
初始化驱动的过程就是注册USB驱动的过程,它会把驱动里的支持的厂商设备ID加到内核中,供内核使用。内核如果找到对应的设备,则加载相应的驱动。
这一点和设备-总线-驱动的设备模型有点不一样,不是通过设备名和驱动名称配对,probe的。
从上面kmsg的信息可以判断usb设备和驱动因为verdorID和productID号的一致,被内核匹配,并调用驱动的probe,进行探测,生成相应的设备节点。
此时在/dev下面可以看到节点;
whoiscam0
如果拔掉usb camera节点会消失。
usb 1-1: USB disconnect, device number 2
<6>[ 14.370000] whois_usbcam 1-1:1.0: USB whoiscam0 now disconnected
如果再次插入usb camera可以看到:
usb 1-1: New USB device found, idVendor=0547, idProduct=1002
<6>[ 25.064000] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
<6>[ 25.071000] usb 1-1: Product: U02_Sensor
<6>[ 25.075000] usb 1-1: Manufacturer: Whois
<4>[ 25.081000] ==========whois probe start========
<6>[ 25.088000] whois_usbcam 1-1:1.0: whois USB camera now attached to whoiscam0==
驱动的probe再次被调用。
此时在/dev下面可以看到设备的节点;
whoiscam0
为了能够让大家更清晰的看到usb驱动,我从内核里拷贝一个简单的usbled的驱动附在下面:
/*
* USB LED driver
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
enum led_type {
DELCOM_VISUAL_SIGNAL_INDICATOR,
DREAM_CHEEKY_WEBMAIL_NOTIFIER,
};
<span style="color:#FF0000;">/* table of devices that work with this driver */
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0fc5, 0x1223),
.driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
{ USB_DEVICE(0x1d34, 0x0004),
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
{ USB_DEVICE(0x1d34, 0x000a),
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);</span>
struct usb_led {
struct usb_device *udev;
unsigned char blue;
unsigned char red;
unsigned char green;
enum led_type type;
};
<span style="color:#FF0000;">static int led_probe(struct usb_interface *interface,
const struct usb_device_id *id)</span>
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_led *dev = NULL;
int retval = -ENOMEM;
dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "out of memory\n");
goto error_mem;
}
dev->udev = usb_get_dev(udev);
dev->type = id->driver_info;
usb_set_intfdata(interface, dev);
retval = device_create_file(&interface->dev, &dev_attr_blue);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_red);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_green);
if (retval)
goto error;
if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
unsigned char *enable;
enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
if (!enable) {
dev_err(&interface->dev, "out of memory\n");
retval = -ENOMEM;
goto error;
}
retval = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
0x09,
0x21,
0x200,
0,
enable,
8,
2000);
kfree(enable);
if (retval != 8)
goto error;
}
dev_info(&interface->dev, "USB LED device now attached\n");
return 0;
error:
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
error_mem:
return retval;
}
static void led_disconnect(struct usb_interface *interface)
{
struct usb_led *dev;
dev = usb_get_intfdata(interface);
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
/* first remove the files, then set the pointer to NULL */
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
dev_info(&interface->dev, "USB LED now disconnected\n");
}
static struct usb_driver led_driver = {
.name = "usbled",
<span style="color:#FF0000;"><strong>.probe = led_probe,</strong></span>
.disconnect = led_disconnect,
<span style="color:#FF0000;"><strong>.id_table = id_table,</strong></span>
};
module_usb_driver(led_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
第三,linux下usb驱动简单分析
1,在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。USB设备程序绑定到接口上。
对于这四个层次的简单描述如下:
设备通常具有一个或多个的配置
配置经常具有一个或多个的接口
接口没有或具有一个以上的端点
1) USB设备:对应数据结构struct usb_device
2) 配置:struct usb_host_config (任一时刻,只能有一个配置生效)
3)USB接口:struct usb_interface (USB 核心将其传递给USB设备驱动,并由USB设备驱动负责后续的控制。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要 一个以上的驱动程序。)
4)端点: struct usb_host_endpoint ,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。
2,USB设备的probe和断开当有USB设备插入时,usb host会获取设备的verdorID和productID号,对比加载到内核的usb设备驱动所支持的verdorID和productID号然后找到相应的驱动,并进行probe操作(创立节点等)。如果usb设备被拔出,则执行相应的断开操作:如
.disconnect = led_disconnect,
参考:http://blog.csdn.net/myarrow/article/details/7013198