当前位置:   article > 正文

linux usb gadget驱动详解(五)_libcomposite

libcomposite

        现从fsg_bind()讲起。

  1. //不失一般性,删掉错误处理和configfs相关代码
  2. static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
  3. {
  4. struct fsg_dev *fsg = fsg_from_func(f);
  5. struct fsg_common *common = fsg->common;
  6. struct usb_gadget *gadget = c->cdev->gadget;
  7. int i;
  8. struct usb_ep *ep;
  9. unsigned max_burst;
  10. int ret;
  11. struct fsg_opts *opts;
  12. /* Don't allow to bind if we don't have at least one LUN */
  13. ret = _fsg_common_get_max_lun(common);
  14. opts = fsg_opts_from_func_inst(f->fi);
  15. if (!common->thread_task) {
  16. common->state = FSG_STATE_IDLE;
  17. common->thread_task =
  18. kthread_create(fsg_main_thread, common, "file-storage");
  19. if (IS_ERR(common->thread_task)) {
  20. ...
  21. }
  22. wake_up_process(common->thread_task);
  23. }
  24. fsg->gadget = gadget;
  25. /* New interface */
  26. i = usb_interface_id(c, f);
  27. fsg_intf_desc.bInterfaceNumber = i;
  28. fsg->interface_number = i;
  29. /* Find all the endpoints we will use */
  30. ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
  31. fsg->bulk_in = ep;
  32. ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
  33. fsg->bulk_out = ep;
  34. /* Assume endpoint addresses are the same for both speeds */
  35. fsg_hs_bulk_in_desc.bEndpointAddress =
  36. fsg_fs_bulk_in_desc.bEndpointAddress;
  37. fsg_hs_bulk_out_desc.bEndpointAddress =
  38. fsg_fs_bulk_out_desc.bEndpointAddress;
  39. /* Calculate bMaxBurst, we know packet size is 1024 */
  40. max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
  41. fsg_ss_bulk_in_desc.bEndpointAddress =
  42. fsg_fs_bulk_in_desc.bEndpointAddress;
  43. fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
  44. fsg_ss_bulk_out_desc.bEndpointAddress =
  45. fsg_fs_bulk_out_desc.bEndpointAddress;
  46. fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
  47. ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
  48. fsg_ss_function);
  49. ...
  50. return 0;
  51. }

可以看到该函数主要是通过kthread_create+wake_up_process的组合创建了一个内核线程fsg_main_thread,名称是"file-storage",通过shell的ps可以看到。另外就是利用usb_interface_id()分配一个接口号,填充进接口描述符,以便在设备枚举时返回给usb host,最后利用composite.c框架所创建的gadget对象对U盘的IN/OUT端点初始化:

  1. //storage_common.c
  2. /*
  3. * Three
  4. full-speed endpoint descriptors: bulk-in, bulk-out, and
  5. * interrupt-in.
  6. */
  7. struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
  8. .bLength = USB_DT_ENDPOINT_SIZE,
  9. .bDescriptorType = USB_DT_ENDPOINT,
  10. .bEndpointAddress = USB_DIR_IN,
  11. .bmAttributes = USB_ENDPOINT_XFER_BULK,
  12. /* wMaxPacketSize set by autoconfiguration */
  13. };
  14. struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
  15. .bLength = USB_DT_ENDPOINT_SIZE,
  16. .bDescriptorType = USB_DT_ENDPOINT,
  17. .bEndpointAddress = USB_DIR_OUT,
  18. .bmAttributes = USB_ENDPOINT_XFER_BULK,
  19. /* wMaxPacketSize set by autoconfiguration */
  20. };
  21. /* Find all the endpoints we will use */
  22. ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
  23. fsg->bulk_in = ep;
  24. ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
  25. fsg->bulk_out = ep;

        因为只有端点(fifo)初始化完,未来才可以利用由usb_ep_queue()传输usb数据,而我们的U盘gadget驱动就利用usb_ep_queue()封装而成以下两个函数用于传输U盘数据:

static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh);
static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh);

        当然现在只是初始化,U盘还不能正常工作,毕竟现在连fsg_setup()都没有调用!也就是说还没被usb host枚举到,也没有SetConfiguration()等操作。那究竟什么时候调用fsg_setup()回调??

        事实上,我们无需关心,因为在composite.c(libcomposite.ko)框架已经帮我们处理好细节了,在composite_setup()函数中被处理,该函数处于中断上下文中,不要放入sleep或者切换调度之类的代码。相当于当我们插入我们的U盘到PC上,它就会在composite_setup()回调我们的fsg_setup()。

        fsg_setup()中主要处理了两个Mass Storage Class相关的请求:US_BULK_RESET_REQUEST和US_BULK_GET_MAX_LUN,这些请求都是由usb host(电脑的U盘驱动)下发给U盘的,U盘只有按要求处理即可。

        想要深入理解gadget,还是需要仔细阅读libcomposite.c(libcomposite.ko)的实现,否则我们就只会调调gadget的api,以后我再讲解libcomposite.ko和udc驱动的流程。

        下面主要分析fsg_main_thread();基本上U盘的所有读写操作都是靠它完成,十分重要的一个函数!

  1. static int fsg_main_thread(void *common_)
  2. {
  3. struct fsg_common *common = common_;
  4. /*
  5. * Allow the thread to be killed by a signal, but set the signal mask
  6. * to block everything but INT, TERM, KILL, and USR1.
  7. */
  8. allow_signal(SIGINT);
  9. allow_signal(SIGTERM);
  10. allow_signal(SIGKILL);
  11. allow_signal(SIGUSR1);
  12. /* Allow the thread to be frozen */
  13. set_freezable();
  14. /*
  15. * Arrange for userspace references to be interpreted as kernel
  16. * pointers. That way we can pass a kernel pointer to a routine
  17. * that expects a __user pointer and it will work okay.
  18. */
  19. set_fs(get_ds());
  20. /* The main loop */
  21. while (common->state != FSG_STATE_TERMINATED) {
  22. if (exception_in_progress(common) || signal_pending(current)) {
  23. handle_exception(common);
  24. continue;
  25. }
  26. if (!common->running) {
  27. sleep_thread(common, true);
  28. continue;
  29. }
  30. if (get_next_command(common))
  31. continue;
  32. spin_lock_irq(&common->lock);
  33. if (!exception_in_progress(common))
  34. common->state = FSG_STATE_DATA_PHASE;
  35. spin_unlock_irq(&common->lock);
  36. if (do_scsi_command(common) || finish_reply(common))
  37. continue;
  38. spin_lock_irq(&common->lock);
  39. if (!exception_in_progress(common))
  40. common->state = FSG_STATE_STATUS_PHASE;
  41. spin_unlock_irq(&common->lock);
  42. if (send_status(common))
  43. continue;
  44. spin_lock_irq(&common->lock);
  45. if (!exception_in_progress(common))
  46. common->state = FSG_STATE_IDLE;
  47. spin_unlock_irq(&common->lock);
  48. }
  49. spin_lock_irq(&common->lock);
  50. common->thread_task = NULL;
  51. spin_unlock_irq(&common->lock);
  52. if (!common->ops || !common->ops->thread_exits
  53. || common->ops->thread_exits(common) < 0) {
  54. int i;
  55. down_write(&common->filesem);
  56. for (i = 0; i < ARRAY_SIZE(common->luns); --i) {
  57. struct fsg_lun *curlun = common->luns[i];
  58. if (!curlun || !fsg_lun_is_open(curlun))
  59. continue;
  60. fsg_lun_close(curlun);
  61. curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
  62. }
  63. up_write(&common->filesem);
  64. }
  65. /* Let fsg_unbind() know the thread has exited */
  66. complete_and_exit(&common->thread_notifier, 0);
  67. }

        它先是声明可以被信号kill调该内核线程,以及能冻结,譬如kiill -STOP、kill -CONT之类的。它主要是靠如下几个函数工作:get_next_command(common)

do_scsi_command(common) || finish_reply(common)

和send_status(common)

        Bulk only 的传输协议可阅读《usbmassbulk_10.pdf》文档,下面只是截取其中一部分:

以及阅读SCSI命令文档。本U盘gadget只是实现其中一些常用的SCSI命令子集而已,我们就挑读(READ_10)和写(WRITE_10)这两个操作:

        可以看到主要是do_read和do_write。因为流程比较繁杂,这里只简单描述,有兴趣的朋友可以逐行代码分析研究,do_write()是通过start_out_transfer()从usb host获取到文件数据,然后调用vfs_write()写入文件系统,完成了将文件写入U盘的过程;而do_read()则是先通过vfs_read()从文件系统(加载驱动时指定的文件路径file=filename[,filename...])中读取文件,然后调用start_in_transfer()写入usb host,完成了读取U盘内的文件到PC。

        终于把U盘gadget驱动讲解了一遍,当然只是粗略走读了一下,代码细节上还是需要大家仔细研究,譬如没有深入到composite.c(libcomposite.ko)gadget框架的具体实现,U盘方面也没有细节到每个SCSI命令的讲解,以及没有讲解CBW/CSW的细节处理(有兴趣可以对照《usbmassbulk_10.pdf》阅读代码)等。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/246853
推荐阅读
相关标签
  

闽ICP备14008679号