现从msg_bind()函数(drivers/usb/gadget/legacy/mass_storage.c)开始讲起。

U盘的gadget驱动比较复杂,因为它包含几部分,包括gadget驱动、U盘相关的处理(SCSI命令的处理、lun概念等)以及虚拟文件系统的读写操作(因为我们虚拟出来的U盘是用文件系统来保存的)等。

//不失一般性,删除一些错误处理代码
static int msg_bind(struct usb_composite_dev *cdev)
{static const struct fsg_operations ops = {.thread_exits = msg_thread_exits,};struct fsg_opts *opts;struct fsg_config config;int status;fi_msg = usb_get_function_instance("mass_storage");fsg_config_from_params(&config, &mod_data, fsg_num_buffers);opts = fsg_opts_from_func_inst(fi_msg);opts->no_configfs = true;status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);fsg_common_set_ops(opts->common, &ops);status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);fsg_common_set_sysfs(opts->common, true);status = fsg_common_create_luns(opts->common, &config);fsg_common_set_inquiry_string(opts->common, config.vendor_name,config.product_name);status = usb_string_ids_tab(cdev, strings_dev);msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {struct usb_descriptor_header *usb_desc;usb_desc = usb_otg_descriptor_alloc(cdev->gadget);if (!usb_desc)goto fail_string_ids;usb_otg_descriptor_init(cdev->gadget, usb_desc);otg_desc[0] = usb_desc;otg_desc[1] = NULL;}status = usb_add_config(cdev, &msg_config_driver, msg_do_config);usb_composite_overwrite_options(cdev, &coverwrite);return 0;
}

内核有个特点,就是驱动分层,gadget也不例外,它有一系列的function驱动(源码在drivers/usb/gadget/function目录下),譬如f_mass_storage.c(usb_f_mass_storage.ko)就是实现U盘的功能,而f_hid.c(usb_f_hid.ko)则是实现HID键鼠功能,为什么这么做呢?因为一个USB设备不一定只有一种功能,譬如"U盘+网卡"、“摄像头+串口”等等这种复合设备,其中drivers/usb/gadget/legacy/multi.c就是一个复合设备的例子。只是我们现在分析的mass_storage.c刚好只有一种功能(U盘),你觉得没必要分层而已,毕竟驱动分层后变复杂了很多。你看 fi_msg = usb_get_function_instance("mass_storage"),不跟进去根本不知道它做了什么,除非你比较熟悉gadget的api接口。它其实是会得到由drivers/usb/gadget/function/f_mass_storage.c这个驱动(usb_f_mass_storage.ko)所注册的“usb功能实例”(struct usb_function_instance *), 而在f_mass_storage.c这边是预先通过DECLARE_USB_FUNCTION_INIT宏(在include/linux/usb/composite.h中定义)进行注册的:

// f_mass_storage.c
DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");

每调用DECLARE_USB_FUNCTION_INIT宏注册一个function实例,就会add进一个全局链表变量的尾部(list_add_tail),所以当在其他驱动中调用usb_get_function_instance("mass_storage"),就可以通过“mass_storage”名称在全局链表中找到对应的“usb功能实例”(struct usb_function_instance *)。

好了,知道这一层关系后,调用usb_get_function_instance("mass_storage")其实就是调用f_mass_storage.c的fsg_alloc_inst()函数:

//去掉错误处理代码
static struct usb_function_instance *fsg_alloc_inst(void)
{struct fsg_opts *opts;struct fsg_lun_config config;int rc;opts = kzalloc(sizeof(*opts), GFP_KERNEL);
...mutex_init(&opts->lock);opts->func_inst.free_func_inst = fsg_free_inst;opts->common = fsg_common_setup(opts->common);
...rc = fsg_common_set_num_buffers(opts->common,CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
...memset(&config, 0, sizeof(config));config.removable = true;rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",(const char **)&opts->func_inst.group.cg_item.ci_name);
...opts->lun0.lun = opts->common->luns[0];opts->lun0.lun_id = 0;config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);opts->default_groups[0] = &opts->lun0.group;opts->func_inst.group.default_groups = opts->default_groups;config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);return &opts->func_inst;
}

在fsg_alloc_inst()中我们看到它先malloc了一个struct fsg_opts对象,该对象在fsg(File Storage Gadget)中算是比较重要的:

struct fsg_opts {struct fsg_common *common;struct usb_function_instance func_inst;struct fsg_lun_opts lun0;struct config_group *default_groups[2];bool no_configfs; /* for legacy gadgets *//** Read/write access to configfs attributes is handled by configfs.** This is to protect the data from concurrent access by read/write* and create symlink/remove symlink.*/struct mutex            lock;int                refcnt;
};

因为它里面保存了struct fsg_common * common和struct usb_function_instance func_inst结构,而no_configfs字段则是指明是否使用configfs配置,这个参数在msg_bind()会配置,我们在第二篇文章中有介绍过使用configfs进行配置gadget驱动,很是方便,只是代码讲解我选用了legacy gadgets来分析,就不细说configfs相关的代码了。U盘gadget的主要全局变量都是聚集在struct fsg_common结构体中,f_mass_storage.c大部分函数都是传入struct fsg_common *参数进去,是本驱动最重要的结构体!struct fsg_common * common就是在这个时候分配好空间(fsg_common_setup())的,并且把common用到的自旋锁、完成量(completion)等等初始化一遍,方便后续使用。然后分配buffer,譬如32个16KB的缓冲区,用于U盘数据传输,多个缓冲区能增加数据读写吞吐量。最后调用fsg_common_create_lun()创建逻辑单元(logical units/LUNs),也是为了后续U盘操作做准备。最后return &opts->func_inst返回struct usb_function_instance *。

后面两个函数是关于configfs的,毕竟要支持从configfs配置驱动,肯定要有相应的api支持,后面遇到configs相关的代码我都直接忽略。

ok,回到msg_bind(),我们这时得到了上述由fsg_alloc_inst()负责填充好并返回的struct usb_function_instance *fi_msg(全局变量),另外,我们都知道可以根据struct usb_function_instance *通过container_of()找回struct fsg_opts对象,方便后续继续配置(初始化)。

另外在msg_bind()中我们还出现了一个struct fsg_config config结构体,该结构体主要用于配置U盘gadget支持的功能,譬如我们可以配置该U盘是否removable的,是否CD/ROM,是否只读,是否nofua,有多少个逻辑块(luns),是否支持端点挂起(stall),以及为各个lun指定映射的文件路径,因为一个lun代表一个逻辑分区,既然我的U盘的底层存储是基于文件系统,自然要为每个分区都指定文件路径。这些参数从哪定义?自然是加载驱动时把参数带进去,譬如(modprobe g_mass_storage luns=2 file=/var/sdcard/disk.img,/var/sdcard/disk1.img 等等。为什么我会知道使用file=filename[,filename...],原因是内核有个文档专门描述怎么配置,具体在内核源码目录下Documentation/usb/mass-storage.txt)。struct fsg_config的具体定义如下:

struct fsg_config {unsigned nluns;struct fsg_lun_config luns[FSG_MAX_LUNS];/* Callback functions. */const struct fsg_operations  *ops;/* Gadget's private data. */void          *private_data;const char *vendor_name;      /*  8 characters or less */const char *product_name;        /* 16 characters or less */char         can_stall;unsigned int      fsg_num_buffers;
};

其实就是在msg_bind()->fsg_config_from_params()里解释参数,不深入了。在这里又调用fsg_common_set_num_buffers()一次,之前在f_mass_storage.c的fsg_alloc_inst()也有初始化过,但没有释放之前的,貌似有内存泄漏了。后面通过fsg_common_create_luns()根据用户设置的luns数量重新创建lun。fsg_common_set_inquiry_string()函数有意思,我们可以改变里面的内容,让PC插入我们的U盘时,下发SCSI INQUIRY命令时,返回的字符串。这个可以用Bus Hound或者USBlyzer抓包看到。usb_string_ids_tab(cdev, strings_dev)是利用strings_dev结构生成对应的字符串描述符,用于usb枚举阶段返回给usb host。接着是otg相关,我们跳过。最重要的函数来了,usb_add_config(cdev, &msg_config_driver,  msg_do_config)。因为调用完它之后,U盘就算初始化完毕!我们看下它的定义,它的具体实现我们不分析,有兴趣的朋友可以自行研究:

//composite.c
/*** usb_add_config() - add a configuration to a device.* @cdev: wraps the USB gadget* @config: the configuration, with bConfigurationValue assigned* @bind: the configuration's bind function* Context: single threaded during gadget setup** One of the main tasks of a composite @bind() routine is to* add each of the configurations it supports, using this routine.** This function returns the value of the configuration's @bind(), which* is zero for success else a negative errno value.  Binding configurations* assigns global resources including string IDs, and per-configuration* resources such as interface IDs and endpoints.*/
int usb_add_config(struct usb_composite_dev *cdev,struct usb_configuration *config,int (*bind)(struct usb_configuration *))
{
...
}

功能是“为一个usb设备增加配置”,我们还记得usb的描述符结构吧?usb设备描述符->配置描述符->接口描述符->端点描述符....,其中,usb_add_config()的int (*bind)(struct usb_configuration *)函数指针就是用于“为配置增加接口”,它会在调用usb_add_config()时被回调,对应我们的U盘gadget就是msg_do_config()。我们先来看两个实参的定义:

static struct usb_configuration msg_config_driver = {.label         = "Linux File-Backed Storage",.bConfigurationValue   = 1,.bmAttributes      = USB_CONFIG_ATT_SELFPOWER,
};static int msg_do_config(struct usb_configuration *c)
{struct fsg_opts *opts;int ret;if (gadget_is_otg(c->cdev->gadget)) {c->descriptors = otg_desc;c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;}opts = fsg_opts_from_func_inst(fi_msg);f_msg = usb_get_function(fi_msg);if (IS_ERR(f_msg))return PTR_ERR(f_msg);ret = usb_add_function(c, f_msg);if (ret)goto put_func;return 0;put_func:usb_put_function(f_msg);return ret;
}

msg_config_driver结构主要是指定.bConfigurationValue    = 1,使用第一个配置,因为到时usb host(譬如PC)在枚举本U盘阶段,会下发SetConfiguration() 进行配置,这时U盘事实上要准备好usb传输相关的东西,如端点分配等,因为紧接着PC就会用端点描述符上指定的端点进行通信,你得提前准备好。

上面的代码片段msg_do_config中的fi_msg变量就是在msg_bind()中通过usb_get_function_instance("mass_storage")得到的全局变量。它先是struct usb_function *f_msg = usb_get_function(fi_msg),该函数事实上是调用了f_mass_storage.c注册好的fsg_alloc():

static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
{struct fsg_opts *opts = fsg_opts_from_func_inst(fi);struct fsg_common *common = opts->common;struct fsg_dev *fsg;fsg = kzalloc(sizeof(*fsg), GFP_KERNEL);if (unlikely(!fsg))return ERR_PTR(-ENOMEM);mutex_lock(&opts->lock);opts->refcnt++;mutex_unlock(&opts->lock);fsg->function.name    = FSG_DRIVER_DESC;fsg->function.bind    = fsg_bind;fsg->function.unbind = fsg_unbind;fsg->function.setup    = fsg_setup;fsg->function.set_alt   = fsg_set_alt;fsg->function.disable = fsg_disable;fsg->function.free_func   = fsg_free;fsg->common               = common;return &fsg->function;
}

这个函数超级重要,很明显它的作用是填充一些回调函数,以及返回struct usb_function* f_msg,在我们前面的代码分析就已经知道struct fsg_opts *opts和struct fsg_common *common结构早在fsg_alloc_inst()时就已经分配好空间了,这里只是拿回指针进行填充(初始化)而已。其中fsg_setup和fsg_bind回调十分重要,未来会被composite.c框架回调,用于处理usb host下发usb枚举以及U盘的实际读写操作。后面再单独分析。

msg_do_config最后调用的一个函数是usb_add_function(c, f_msg),这样就把上面填充的fsg_setup和fsg_bind等回调注册到composite.c框架了。其中参数c是在static struct usb_configuration msg_config_driver基础上通过usb_add_config()->usb_add_config_only()进行进一步配置,剩下就是composite.c框架处理的事情了。那fsg_setup()和fsg_bind()在什么情况下会被回调:

/*** usb_add_function() - add a function to a configuration* @config: the configuration* @function: the function being added* Context: single threaded during gadget setup** After initialization, each configuration must have one or more* functions added to it.  Adding a function involves calling its @bind()* method to allocate resources such as interface and string identifiers* and endpoints.** This function returns the value of the function's bind(), which is* zero for success else a negative errno value.*/
int usb_add_function(struct usb_configuration *config,struct usb_function *function)
{
...
.../* REVISIT *require* function->bind? */if (function->bind) {value = function->bind(config, function);...} elsevalue = 0;
...
...
}

我们发现msg_do_config()在调用usb_add_function()后,fsg_bind()就会被回调,这时就越来越接近我们的初衷:“为啥它能让PC识别成“可移动磁盘”,以及它可以像市面上的U盘一样能读写文件”。

好了,下一篇文章我们从fsg_bind()函数(usb/gadget/function/f_mass_storage.c)讲起!

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驱动详解(五)

    现从fsg_bind()讲起. //不失一般性,删掉错误处理和configfs相关代码 static int fsg_bind(struct usb_configuration *c, struct ...

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

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

  6. Linux usb设备驱动详解

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

  7. linux usb键盘驱动详解

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

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

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

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

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

最新文章

  1. 【Nginx】面试官:给我讲讲Nginx如何实现四层负载均衡?
  2. Nature科学报告:根据大脑思维意图来生成对应匹配的图像
  3. linux进不了容器配置目录,linux – 在Docker容器中使用bcrypt的ELF头文件或安装问题...
  4. Nginx —— 用HTTP proxy module配置一个反向代理服务器
  5. 利用WINDOWS活动目录提供LDAP的方案
  6. 多股回测(backtrader+quantstats+akshare)
  7. 安卓学习笔记38:利用OpenGL ES绘制旋转立方体
  8. oracle数据库disable,Disable/Enable Oracle Database Vault
  9. js sdk 一键分享 微信_微信朋友圈分享自己拍的视频,一键开启这个设置,自带文字和音乐...
  10. JSP标签JSTL(3)--迭代操作
  11. Spring源码-context:component-scan的解析过程
  12. SAP 系统License查看申请及导入
  13. linux查看scsi设备id号,scsi_id查询
  14. Junos 操作系统
  15. flash视频的param属性解释
  16. java参数传递内存图解,探讨Java参数传递问题
  17. 《无法打开包括文件:“Eigen\Dense”:No such file or directory》亲测有效的解决方法
  18. 详细解读WordNet计算相似度的几种方法
  19. 农庄规划软件测试,《模拟农场17》游戏评测:现代化农场让你学会如何种田
  20. 为什么选择分布式垂直架构

热门文章

  1. 5G商用价值到底在哪里,可以赋予哪些能力? 1
  2. 读《C语言的科学与艺术》
  3. [linux-007] 国内的一些出版社laltex book模板汇集
  4. 易点易动固定资产管理系统好用吗?
  5. IIS 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。
  6. PikaScript实践记录(2)之移植PikaScript(1)
  7. 一文了解RPA「财务机器人」 自动运行,效率+1+1+1……
  8. MCP,eMMC,eMCP区别和联系
  9. python画旋转六边形_使用python中的元素进行六边形绘制
  10. 独立游戏Godot游戏开发日志