现从fsg_bind()讲起。

//不失一般性,删掉错误处理和configfs相关代码
static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
{struct fsg_dev     *fsg = fsg_from_func(f);struct fsg_common  *common = fsg->common;struct usb_gadget *gadget = c->cdev->gadget;int            i;struct usb_ep     *ep;unsigned        max_burst;int           ret;struct fsg_opts     *opts;/* Don't allow to bind if we don't have at least one LUN */ret = _fsg_common_get_max_lun(common);opts = fsg_opts_from_func_inst(f->fi);if (!common->thread_task) {common->state = FSG_STATE_IDLE;common->thread_task =kthread_create(fsg_main_thread, common, "file-storage");if (IS_ERR(common->thread_task)) {...}wake_up_process(common->thread_task);}fsg->gadget = gadget;/* New interface */i = usb_interface_id(c, f);fsg_intf_desc.bInterfaceNumber = i;fsg->interface_number = i;/* Find all the endpoints we will use */ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);fsg->bulk_in = ep;ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);fsg->bulk_out = ep;/* Assume endpoint addresses are the same for both speeds */fsg_hs_bulk_in_desc.bEndpointAddress =fsg_fs_bulk_in_desc.bEndpointAddress;fsg_hs_bulk_out_desc.bEndpointAddress =fsg_fs_bulk_out_desc.bEndpointAddress;/* Calculate bMaxBurst, we know packet size is 1024 */max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);fsg_ss_bulk_in_desc.bEndpointAddress =fsg_fs_bulk_in_desc.bEndpointAddress;fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;fsg_ss_bulk_out_desc.bEndpointAddress =fsg_fs_bulk_out_desc.bEndpointAddress;fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,fsg_ss_function);
...return 0;
}

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

//storage_common.c/** Three
full-speed endpoint descriptors: bulk-in, bulk-out, and* interrupt-in.*/
struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {.bLength =      USB_DT_ENDPOINT_SIZE,.bDescriptorType =    USB_DT_ENDPOINT,.bEndpointAddress =    USB_DIR_IN,.bmAttributes =     USB_ENDPOINT_XFER_BULK,/* wMaxPacketSize set by autoconfiguration */
};
struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {.bLength =     USB_DT_ENDPOINT_SIZE,.bDescriptorType =    USB_DT_ENDPOINT,.bEndpointAddress =    USB_DIR_OUT,.bmAttributes =        USB_ENDPOINT_XFER_BULK,/* wMaxPacketSize set by autoconfiguration */
};/* Find all the endpoints we will use */ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);fsg->bulk_in = ep;ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);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盘的所有读写操作都是靠它完成,十分重要的一个函数!

static int fsg_main_thread(void *common_)
{struct fsg_common  *common = common_;/** Allow the thread to be killed by a signal, but set the signal mask* to block everything but INT, TERM, KILL, and USR1.*/allow_signal(SIGINT);allow_signal(SIGTERM);allow_signal(SIGKILL);allow_signal(SIGUSR1);/* Allow the thread to be frozen */set_freezable();/** Arrange for userspace references to be interpreted as kernel* pointers.  That way we can pass a kernel pointer to a routine* that expects a __user pointer and it will work okay.*/set_fs(get_ds());/* The main loop */while (common->state != FSG_STATE_TERMINATED) {if (exception_in_progress(common) || signal_pending(current)) {handle_exception(common);continue;}if (!common->running) {sleep_thread(common, true);continue;}if (get_next_command(common))continue;spin_lock_irq(&common->lock);if (!exception_in_progress(common))common->state = FSG_STATE_DATA_PHASE;spin_unlock_irq(&common->lock);if (do_scsi_command(common) || finish_reply(common))continue;spin_lock_irq(&common->lock);if (!exception_in_progress(common))common->state = FSG_STATE_STATUS_PHASE;spin_unlock_irq(&common->lock);if (send_status(common))continue;spin_lock_irq(&common->lock);if (!exception_in_progress(common))common->state = FSG_STATE_IDLE;spin_unlock_irq(&common->lock);}spin_lock_irq(&common->lock);common->thread_task = NULL;spin_unlock_irq(&common->lock);if (!common->ops || !common->ops->thread_exits|| common->ops->thread_exits(common) < 0) {int i;down_write(&common->filesem);for (i = 0; i < ARRAY_SIZE(common->luns); --i) {struct fsg_lun *curlun = common->luns[i];if (!curlun || !fsg_lun_is_open(curlun))continue;fsg_lun_close(curlun);curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;}up_write(&common->filesem);}/* Let fsg_unbind() know the thread has exited */complete_and_exit(&common->thread_notifier, 0);
}

它先是声明可以被信号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》阅读代码)等。

linux usb gadget驱动详解(五)相关推荐

  1. linux usb gadget驱动详解(一)

    由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网.PCIE总线和RS232串口等.这里我们主要讨论USB. USB是一个主从通信架构,但只能一主多从.其中us ...

  2. linux usb gadget驱动详解(二)

    在上篇<linux usb gadget驱动详解(一)>中,我们了解到gadget的测试方法,但在最后,我们留下一个问题,就是怎样使用新的方法进行usb gadget驱动测试. 我们发现l ...

  3. linux usb gadget驱动详解(三)

    本文将对linux4.4.19版本usb gadget源码进行简单分析.鉴于前文反复测试U盘设备驱动,现从linux-4.4.19/drivers/usb/gadget/legacy/mass_sto ...

  4. linux usb gadget驱动详解(四)

    现从msg_bind()函数(drivers/usb/gadget/legacy/mass_storage.c)开始讲起. U盘的gadget驱动比较复杂,因为它包含几部分,包括gadget驱动.U盘 ...

  5. Linux usb设备驱动详解

    1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linux内核几乎支持所有的usb设备,包括键盘,鼠标,打印机,modem,扫描仪.Linux的usb驱动分为主机驱动与gadget驱动 ...

  6. linux usb键盘驱动详解

    1.首先我们通过上节的代码中修改,来打印下键盘驱动的数据到底是怎样的 先来回忆下,我们之前写的鼠标驱动的id_table是这样: 所以我们要修改id_table,使这个驱动为键盘的驱动,如下图所示: ...

  7. linux usb ga驱动详解,Linux设备驱动之内存映射

    1. 内存映射 所谓的内存映射就是把物理内存映射到进程的地址空间之内,这些应用程序就可以直接使用输入输出的地址空间,从而提高读写的效率.Linux提供了mmap()函数,用来映射物理内存. 在驱动程序 ...

  8. Linux字符设备驱动详解七(“插件“设备树实现RGB灯驱动)

    文章目录 系列文章目录 前言 正文 Device Tree Overlays:"插件"设备树 传统设备树 "插件"设备树 使用前提 案例说明 设备树:foo.d ...

  9. Linux字符设备驱动详解四(使用自属的xbus驱动总线)

    文章目录 系列文章目录 前言 驱动目录 正文 驱动总线 总线管理 总线注册 设备注册 驱动注册 代码示例 总结 系列文章目录 Linux字符设备驱动详解 Linux字符设备驱动详解二(使用设备驱动模型 ...

最新文章

  1. 卷积神经网络新手指南 2
  2. 计算机网络(10)-----TCP的拥塞控制
  3. 推荐几个电子/嵌入式方向的公众号
  4. 作者:陶克(1988-),男,博士,北京系统工程研究所助理研究员。
  5. java 课后习题 删除奇数元素下标 然后再删除值为奇数的下标
  6. css 定位以及文字超长省略
  7. python selenium--常用函数3
  8. 一种基于频域滤波法消除干扰项与角谱法重构技术的数字全息显微台阶形貌测量实例分析
  9. 2022-2028年中国差旅管理行业市场行情动态及投资潜力研究报告
  10. Ubuntu系统上安装WPS
  11. win11连接共享打印机错误0x00000709
  12. 为什么使用multiarmed bandit algorithms(多臂赌博机算法)--与A/Btest的对比
  13. 被哈佛录取后,他骑行4300公里旅行回家
  14. 外文书籍的中文翻译版本作参考文献,文献引用格式
  15. txt.......
  16. 对标苹果,小米的漫长激情与征程
  17. 一直显示无法连接服务器失败,老显示无法连接服务器失败
  18. OpenMMLab实战营打卡-第3课
  19. 浙江python信息技术教材_人工智能、Python…浙江省三到九年级将使用信息技术新修订教材...
  20. Spine动画显示错乱问题

热门文章

  1. 网络战利器——“网络安全态势感知”
  2. c语言+nasm语言混合编程
  3. 读书笔记:mini-batch学习 ← 斋藤康毅
  4. Warning: error while crawling ~: boost::filesystem::status: Permission denied: ~/.gvfs
  5. 这几种下属会被领导喜欢
  6. Mysql-mof提权
  7. 做好企业上网行为管理作用大-真实开发案例
  8. android 搜索 时间,Android日历评测 寻找最适合你的日历应用
  9. 算法仿真时,将Excel中真实数据导入simulink中的方法
  10. 开源 word 替代_Google相册的10种开源替代品