USB 驱动

 

linux驱动由浅入深系列:usb子系统

https://blog.csdn.net/RadianceBlau/article/details/78275434

android上的usb口是支持OTG(on the go)的,USB OTG既可以作为Host又可以作为Device,我们本文来看一下android手机作为Host连接鼠标的情况。
OTG是如何做到既可以做Host又可以作为Device的呢
标准usb接头中有四根线:vbus,gnd,dp(d+),dm(d-),android手机上的usb为miniUSB接口增加了一根id线,用来区分Host、Device。
usb是如何检测设备插入的呢
设备插拔检测都是由hub来进行的,即使不外接hub在USB host controler中也集成了一个roothub。hub上的dp、dm线都有一个15k的下拉电阻拉到低电平,设备端的dp或dm线上有1.5k的上拉电阻,设备插入时就会改变dp、dm线上的电平。
当把一个USB设备插入到一个usb hub的某个端口时,集中器就会检测到设备的接入,从而在下一次受到主机通过中断交互查询时就会向其报告。集中器的端口在没有设备接入时都处于关闭状态,插入设备之后也不会自动打开,必须由主机通过控制交互发出命令予以打开。所以,在得到集中器的报告之后,主机的USB驱动程序就会为新插入的设备调度若干个控制交互,并向集中器发出打开这个端口的命令,这样新插入的设备就会出现在USB总线上了,并为该设备分配唯一的地址。
鼠标插入android手机后代码执行过程分析
1,usb鼠标属于hid设备,linux启动过程中会注册hid总线bus_register(&hid_bus_type)

 
  1. drivers/hid/hid-core.c

  2. static int __init hid_init(void)

  3. {

  4. int ret;

  5. ret = bus_register(&hid_bus_type);

  6. ret = hidraw_init();

  7. return 0;

  8. }

2,usb鼠标插入后的会调用到hub_port_connect()

 
  1. drivers\usb\core\hub.c

  2. static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,

  3. u16 portchange)

  4. {

  5. int status, i;

  6. unsigned unit_load;

  7. struct usb_device *hdev = hub->hdev;

  8. struct usb_hcd *hcd = bus_to_hcd(hdev->bus);

  9. struct usb_port *port_dev = hub->ports[port1 - 1];

  10. struct usb_device *udev = port_dev->child;

  11. static int unreliable_port = -1;

  12. printk("radia hub_port_connect\n");//添加log

  13.  
  14. status = 0;

  15. for (i = 0; i < SET_CONFIG_TRIES; i++) {

  16. /* reallocate for each attempt, since references

  17. * to the previous one can escape in various ways

  18. */

  19. udev = usb_alloc_dev(hdev, hdev->bus, port1);创建一个usb_device设备

  20. if (!udev) {

  21. dev_err(&port_dev->dev,

  22. "couldn't allocate usb_device\n");

  23. goto done;

  24. }

  25. ........

  26. /* Run it through the hoops (find a driver, etc) */

  27. if (!status) {

  28. status = usb_new_device(udev);调用usb_new_device-->usb_enumerate_device枚举设备

  29. if (status) {

  30. mutex_lock(&usb_port_peer_mutex);

  31. spin_lock_irq(&device_state_lock);

  32. port_dev->child = NULL;

  33. spin_unlock_irq(&device_state_lock);

  34. mutex_unlock(&usb_port_peer_mutex);

  35. } else {

  36. if (hcd->usb_phy && !hdev->parent)

  37. usb_phy_notify_connect(hcd->usb_phy,

  38. udev->speed);

  39. }

  40. }

  41. }

其中会调用usb_alloc_dev创建一个usb_device设备,然后调用usb_new_device-->usb_enumerate_device-->usb_get_configuration-->usb_parse_configuration-->usb_parse_interface(会分配struct usb_interface_cache)-->usb_parse_endpoint分析设备各描述符,最后usb_new_device会调用device_add 这里把这个usb_device注册到usb总线下。
3,当注册这个usb_device的时候usb总线会调用generic driver驱动的probe函数

 
  1. drivers\usb\core\generic.c

  2. static int generic_probe(struct usb_device *udev)

  3. {

  4. int err, c;

  5. printk("radia generic_probe\n");//添加log

  6. if (udev->authorized == 0)

  7. dev_err(&udev->dev, "Device is not authorized for usage\n");

  8. else {

  9. c = usb_choose_configuration(udev);//选择合适的configuration

  10. if (c >= 0) {

  11. err = usb_set_configuration(udev, c);/配置合适的configuration

  12. if (err && err != -ENODEV) {

  13. dev_err(&udev->dev, "can't set config #%d, error %d\n",

  14. c, err);

  15. /* This need not be fatal. The user can try to

  16. * set other configurations. */

  17. }

  18. }

  19. }

  20. /* USB device state == configured ... usable */

  21. usb_notify_add_device(udev);

  22. return 0;

  23. }

这个函数主要为这个usb_device选择一个合适的配置configuration,并且设置这个configuration

4,usb_set_configuration这个函数为这个配置的每个接口初始化为usb_if_device_type类型的设备。

usb_interface设备是挂载usb_device下面的,并且这个usb_device可能有多个usb_interface功能设备。

 
  1. drivers\usb\core\message.c

  2. int usb_set_configuration(struct usb_device *dev, int configuration)

  3. {

  4. if (dev->authorized == 0 || configuration == -1)

  5. configuration = 0;

  6. else {

  7. for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {

  8. if (dev->config[i].desc.bConfigurationValue ==

  9. configuration) {

  10. cp = &dev->config[i];

  11. break;

  12. }

  13. }

  14. }

  15. if (cp) {

  16. nintf = cp->desc.bNumInterfaces;

  17. new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),

  18. GFP_NOIO);

  19. if (!new_interfaces) {

  20. dev_err(&dev->dev, "Out of memory\n");

  21. return -ENOMEM;

  22. }

  23. for (; n < nintf; ++n) {

  24. new_interfaces[n] = kzalloc(

  25. sizeof(struct usb_interface),

  26. GFP_NOIO);

  27. if (!new_interfaces[n]) {

  28. dev_err(&dev->dev, "Out of memory\n");

  29. ret = -ENOMEM;

  30. free_interfaces:

  31. while (--n >= 0)

  32. kfree(new_interfaces[n]);

  33. kfree(new_interfaces);

  34. return ret;

  35. }

  36. }

  37. i = dev->bus_mA - usb_get_max_power(dev, cp);

  38. if (i < 0)

  39. dev_warn(&dev->dev, "new config #%d exceeds power "

  40. "limit by %dmA\n",

  41. configuration, -i);

  42. }

  43. /*

  44. * Initialize the new interface structures and the

  45. * hc/hcd/usbcore interface/endpoint state.

  46. */

  47. for (i = 0; i < nintf; ++i) {

  48. struct usb_interface_cache *intfc;

  49. struct usb_interface *intf;

  50. struct usb_host_interface *alt;

  51.  
  52. cp->interface[i] = intf = new_interfaces[i];

  53. intfc = cp->intf_cache[i];

  54. intf->altsetting = intfc->altsetting;

  55. intf->num_altsetting = intfc->num_altsetting;

  56. kref_get(&intfc->ref);

  57.  
  58. alt = usb_altnum_to_altsetting(intf, 0);

  59.  
  60. /* No altsetting 0? We'll assume the first altsetting.

  61. * We could use a GetInterface call, but if a device is

  62. * so non-compliant that it doesn't have altsetting 0

  63. * then I wouldn't trust its reply anyway.

  64. */

  65. if (!alt)

  66. alt = &intf->altsetting[0];

  67.  
  68. intf->intf_assoc =

  69. find_iad(dev, cp, alt->desc.bInterfaceNumber);

  70. intf->cur_altsetting = alt;

  71. usb_enable_interface(dev, intf, true);

  72. intf->dev.parent = &dev->dev;

  73. intf->dev.driver = NULL;

  74. intf->dev.bus = &usb_bus_type;

  75. intf->dev.type = &usb_if_device_type;

  76. intf->dev.groups = usb_interface_groups;

  77. intf->dev.dma_mask = dev->dev.dma_mask;

  78. INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);

  79. intf->minor = -1;

  80. device_initialize(&intf->dev);

  81. pm_runtime_no_callbacks(&intf->dev);

  82. dev_set_name(&intf->dev, "%d-%s:%d.%d",

  83. dev->bus->busnum, dev->devpath,

  84. configuration, alt->desc.bInterfaceNumber);

  85. usb_get_dev(dev);

  86. }

  87. kfree(new_interfaces);

  88.  
  89. ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),

  90. USB_REQ_SET_CONFIGURATION, 0, configuration, 0,

  91. NULL, 0, USB_CTRL_SET_TIMEOUT);

  92. /* Now that all the interfaces are set up, register them

  93. * to trigger binding of drivers to interfaces. probe()

  94. * routines may install different altsettings and may

  95. * claim() any interfaces not yet bound. Many class drivers

  96. * need that: CDC, audio, video, etc.

  97. */

  98. for (i = 0; i < nintf; ++i) {

  99. struct usb_interface *intf = cp->interface[i];

  100. dev_dbg(&dev->dev,

  101. "adding %s (config #%d, interface %d)\n",

  102. dev_name(&intf->dev), configuration,

  103. intf->cur_altsetting->desc.bInterfaceNumber);

  104. device_enable_async_suspend(&intf->dev);

  105. ret = device_add(&intf->dev);

  106. if (ret != 0) {

  107. dev_err(&dev->dev, "device_add(%s) --> %d\n",

  108. dev_name(&intf->dev), ret);

  109. continue;

  110. }

  111. create_intf_ep_devs(intf);

  112. }

  113. usb_autosuspend_device(dev);

  114. return 0;

  115. }

5,最后在注册这些interface时,注册函数会调用usb总线的match函数匹配usb_interface和usb_driver

但是usb_bus_type总线只有match函数,没有probe函数需要调用usb_driver的probe函数usb_probe_interface(drivers/usb/core/driver.c),这个函数会真正的调用具体usb_driver的probe函数。root hub或者hub也都是usb_device设备,usb_device下面包含usb_interface功能(function)

 
  1. drivers/usb/core/driver.c

  2. static int usb_probe_interface(struct device *dev)

  3. {

  4. struct usb_driver *driver = to_usb_driver(dev->driver);

  5. struct usb_interface *intf = to_usb_interface(dev);

  6. struct usb_device *udev = interface_to_usbdev(intf);

  7. const struct usb_device_id *id;

  8. int error = -ENODEV;

  9. int lpm_disable_error;

  10. printk("radia usb_probe_interface\n");//添加log

  11. /* Carry out a deferred switch to altsetting 0 */

  12. if (intf->needs_altsetting0) {

  13. error = usb_set_interface(udev, intf->altsetting[0].

  14. desc.bInterfaceNumber, 0);

  15. if (error < 0)

  16. goto err;

  17. intf->needs_altsetting0 = 0;

  18. }

  19. error = driver->probe(intf, id);//调用具体设备的probe

  20. usb_autosuspend_device(udev);

  21. return error;

  22. }

6,usb鼠标在android代码中没有使用linux中常用的mousedev.c驱动,而是使用了hid-generic驱动,hid-generic.c的代码很简洁

 
  1. kernel\drivers\hid\hid-generic.c

  2. #include <linux/module.h>

  3. #include <linux/slab.h>

  4. #include <linux/kernel.h>

  5. #include <asm/unaligned.h>

  6. #include <asm/byteorder.h>

  7. #include <linux/hid.h>

  8.  
  9. static const struct hid_device_id hid_table[] = {

  10. { HID_DEVICE(HID_BUS_ANY, HID_GROUP_GENERIC, HID_ANY_ID, HID_ANY_ID) },

  11. { }

  12. };

  13. MODULE_DEVICE_TABLE(hid, hid_table);

  14. static struct hid_driver hid_generic = {

  15. .name = "hid-generic",

  16. .id_table = hid_table,

  17. };

  18. module_hid_driver(hid_generic);

  19. MODULE_AUTHOR("Henrik Rydberg");

  20. MODULE_DESCRIPTION("HID generic driver");

  21. MODULE_LICENSE("GPL");

  22. 其中module_hid_driver定义在

  23. kernel\include\linux\hid.h

  24. #define module_hid_driver(__hid_driver) \

  25. module_driver(__hid_driver, hid_register_driver, \

  26. hid_unregister_driver)

这个宏实际上是调用 __hid_register_driver()接口注册一个hid_driver,并把它挂接在hid_bus_type总线驱动链表上。
7,当鼠标移动或点击时使用input子系统上报Event的代码在

 
  1. drivers\hid\hid-input.c

  2. static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,

  3. struct hid_usage *usage)

  4. {

  5. struct input_dev *input = hidinput->input;

  6. struct hid_device *device = input_get_drvdata(input);

  7. int max = 0, code;

  8. unsigned long *bit = NULL;

  9. field->hidinput = hidinput;

  10.  
  11. switch (usage->hid & HID_USAGE_PAGE) {

  12. case HID_UP_UNDEFINED:

  13. goto ignore;

  14. case HID_UP_KEYBOARD:

  15. set_bit(EV_REP, input->evbit);

  16.  
  17.  
  18. if ((usage->hid & HID_USAGE) < 256) {

  19. if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;

  20. map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);

  21. } else

  22. map_key(KEY_UNKNOWN);

  23. break;

  24. case HID_UP_BUTTON:

  25. code = ((usage->hid - 1) & HID_USAGE);

  26. switch (field->application) {

  27. case HID_GD_MOUSE:

  28. case HID_GD_POINTER: code += BTN_MOUSE; break;设置鼠标左键的键值

  29. case HID_GD_JOYSTICK:

  30. if (code <= 0xf)

  31. code += BTN_JOYSTICK;

  32. else

  33. code += BTN_TRIGGER_HAPPY - 0x10;

  34. break;

  35. case HID_GD_GAMEPAD:

  36. if (code <= 0xf)

  37. code += BTN_GAMEPAD;

  38. else

  39. code += BTN_TRIGGER_HAPPY - 0x10;

  40. break;

  41. default:

  42. switch (field->physical) {

  43. case HID_GD_MOUSE:

  44. case HID_GD_POINTER: code += BTN_MOUSE; break;

  45. case HID_GD_JOYSTICK: code += BTN_JOYSTICK; break;

  46. case HID_GD_GAMEPAD: code += BTN_GAMEPAD; break;

  47. default: code += BTN_MISC;

  48. }

  49. }

  50. map_key(code);

  51. break;

  52. ............

  53.  
  54. case HID_UP_PID:

  55. switch (usage->hid & HID_USAGE) {

  56. case 0xa4: map_key_clear(BTN_DEAD); break;

  57. default: goto ignore;

  58. }

  59. break;

  60. default:

  61. unknown:

  62. if (field->report_size == 1) {

  63. if (field->report->type == HID_OUTPUT_REPORT) {

  64. map_led(LED_MISC);

  65. break;

  66. }

  67. map_key(BTN_MISC);

  68. break;

  69. }

  70. if (field->flags & HID_MAIN_ITEM_RELATIVE) {

  71. map_rel(REL_MISC);

  72. break;

  73. }

  74. map_abs(ABS_MISC);

  75. break;

  76. }

  77. if (usage->type == EV_ABS) {

  78. int a = field->logical_minimum;

  79. int b = field->logical_maximum;

  80. if ((device->quirks & HID_QUIRK_BADPAD) && (usage->code == ABS_X || usage->code == ABS_Y)) {

  81. a = field->logical_minimum = 0;

  82. b = field->logical_maximum = 255;

  83. }

  84. if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK)

  85. input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);/上报键值

  86. else input_set_abs_params(input, usage->code, a, b, 0, 0);

  87. input_abs_set_res(input, usage->code,

  88. hidinput_calc_abs_res(field, usage->code));

  89. /* use a larger default input buffer for MT devices */

  90. if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)

  91. input_set_events_per_packet(input, 60);

  92. }

  93. return;

  94. }

8,usb鼠标插入android手机后的log如下
[   86.501420] msm_otg 78db000.usb: phy_reset: success
[   86.626156] msm_otg 78db000.usb: USB exited from low power mode
[   86.670053] msm_otg 78db000.usb: phy_reset: success
[   86.776425] msm_hsusb_host msm_hsusb_host: EHCI Host Controller
[   86.787250] msm_hsusb_host msm_hsusb_host: new USB bus registered, assigned bus number 1
[   86.803480] msm_hsusb_host msm_hsusb_host: irq 49, io mem 0x078db000
[   86.824675] msm_hsusb_host msm_hsusb_host: USB 2.0 started, EHCI 1.00
[   86.831268] radia usb_new_device
[   86.836682] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
[   86.842444] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[   86.850085] usb usb1: Product: EHCI Host Controller
[   86.854990] usb usb1: Manufacturer: Linux 3.18.31-g6649b1f-dirty ehci_hcd
[   86.861286] usb usb1: SerialNumber: msm_hsusb_host
[   86.876553] radia generic_probe
[   86.882021] hub 1-0:1.0: USB hub found
[   86.885627] hub 1-0:1.0: 1 port detected
[   87.085676] radia hub_port_connect
[   87.204811] usb 1-1: new low-speed USB device number 2 using msm_hsusb_host
[   87.358301] radia usb_new_device
[   87.372341] usb 1-1: New USB device found, idVendor=10c4, idProduct=8108
[   87.378352] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[   87.385665] usb 1-1: Product: USB OPTICAL MOUSE
[   87.389643] usb 1-1: Manufacturer: YSPRINGTECH
[   87.402281] radia generic_probe
[   87.432953] input: YSPRINGTECH USB OPTICAL MOUSE as /devices/soc/78db000.usb/msm_hsusb_host/usb1/1-1/1-1:1.0/0003:10C4:8108.0001/input/input9
[   87.461748] hid-generic 0003:10C4:8108.0001: input,hidraw0: USB HID v1.11 Mouse [YSPRINGTECH USB OPTICAL MOUSE] on usb-msm_hsusb_host-1/input0
[   87.815343] SELinux: initialized (dev fuse, type fuse), uses mountpoint labeling

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值