USB gadget 驱动 printer.c 分析
1. modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 modprobe后面也可以加模块参数
2. prn_example从stdout获取数据然后通过USB发送出去,下面让他将文件中的内容发送出去:
# cat data_file | prn_example -write_data
3.pdev = device_create(usb_gadget_class, NULL, devt, NULL, "g_printer%d", dev->minor); /*这个应该是在gadget类下创建一个device: struct device结构*/
4. 在调用bind后,经过重重函数调用,现在设备终于饱满了,既有配置了,也有接口了,接口里也有相应的端点了。各层的关系也都联系起来了。
5.核心结构体
/*将配置分组到gadget中*/
struct usb_composite_driver {
const char *name;
const struct usb_device_descriptor *dev;
struct usb_gadget_strings **strings;
enum usb_device_speed max_speed;
unsigned needs_serial:; /*用于分配整个设备共享的资源,例如字符串ID,并使用@usb_add_config()添加其配置。
返回负的errno值可能会失败; 它应该在成功初始化时返回零。*/
int (*bind)(struct usb_composite_dev *cdev);
/*与@bind相反*/
int (*unbind)(struct usb_composite_dev *); /*可选的驱动断开方法*/
void (*disconnect)(struct usb_composite_dev *); /* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
/*hidg_driver:指向全局composite_driver_template*/
struct usb_gadget_driver gadget_driver;
};
/*代表一个复合的USB gadget*/
struct usb_composite_dev {
/*只读,作为此gadget的USB外设USB控制器的抽象*/
struct usb_gadget *gadget;
/*用于控制响应; 缓冲区是预先分配的*/
struct usb_request *req;
/*用于OS描述符响应,缓冲区是预先分配的*/
struct usb_request *os_desc_req;
/*当前active的配置*/
struct usb_configuration *config; /* OS String is a custom (yet popular) extension to the USB standard. */
u8 qw_sign[OS_STRING_QW_SIGN_LEN];
u8 b_vendor_code;
struct usb_configuration *os_desc_config;
unsigned int use_os_string:; /* private: */
/* internals */
unsigned int suspended:;
struct usb_device_descriptor desc;
struct list_head configs; /*usb_add_config_only()中将add的config添加到这个链表中*/
struct list_head gstrings;
struct usb_composite_driver *driver;
u8 next_string_id;
char *def_manufacturer; /* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations; /* the composite driver won't complete the control transfer's
* data/status stages till delayed_status is zero.
*/
int delayed_status; /* protects deactivations and delayed_status counts*/
spinlock_t lock; /* public: */
unsigned int setup_pending:; /*true when setup request is queued but not completed*/
unsigned int os_desc_pending:;
};
/*代表usb从设备的驱动程序 */
struct usb_gadget_driver {
/*描述此gadget功能的字符串*/
char *function; /*String describing the gadget's function*/
/*此驱动能处理的最大速率*/
enum usb_device_speed max_speed;
/*驱动的bind的回调*/
int (*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
/*驱动程序从gadget解除绑定时调用,通常来自rmmod(报告断开连接后),在允许睡眠的环境中调用*/
void (*unbind)(struct usb_gadget *);
/** @setup:由未被硬件级驱动程序处理的ep0控制请求调用。 大多数调用必须由gadget驱动程序处理,
包括描述符和配置管理。 设置数据的16位成员按USB字节顺序排列。 在in_interrupt被调用; 这可能不
会睡觉。 驱动程序将对ep0的响应排队,或将负数返回到停止。*/
int (*setup)(struct usb_gadget *,const struct usb_ctrlrequest *);
/*@disconnect:在主机断开连接后停止所有传输后调用。 可能在中断上下文中调用; 这可能不能睡眠。
某些设备无法检测到断开连接,因此除非作为控制器关闭的一部分,否则可能无法调用此函数。*/
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *);
void (*reset)(struct usb_gadget *); /* FIXME support safe rmmod */ /*===>目前内核不支持安全的rmmod!!!*/
struct device_driver driver; /*应该绑定此驱动程序的UDC名称。 如果udc_name为NULL,则此驱动程序将绑定到任何可用的UDC。*/
char *udc_name;
struct list_head pending; /*挂载在等待udc的链表上*/
unsigned match_existing_only:; /*决定在没有available的UDC时,释放将绑定请求的driver挂载到pending链表上
see usb_gadget_probe_driver()*/
};
/*代表一个usb从设备*/
struct usb_gadget {
/*sysfs_notify()使用的工作队列*/
struct work_struct work;
struct usb_udc *udc;
/*用于访问特定于硬件的操作的函数指针,对于gadget driver来说是只读的*/
const struct usb_gadget_ops *ops; /*指向全局的:renesas_usb3_gadget_ops*/
struct usb_ep *ep0; /*端点0,控制端点*/
struct list_head ep_list; /* of usb_ep, List of other endpoints supported by the device.*/
enum usb_device_speed speed;
enum usb_device_speed max_speed;
enum usb_device_state state;
const char *name;
struct device dev;
unsigned out_epnum; /*分别为输入输出端点数*/
unsigned in_epnum;
unsigned mA;
struct usb_otg_caps *otg_caps; unsigned sg_supported:;
unsigned is_otg:; /*标识是不是otg设备*/
unsigned is_a_peripheral:;
unsigned b_hnp_enable:;
unsigned a_hnp_support:;
unsigned a_alt_hnp_support:;
unsigned hnp_polling_support:;
unsigned host_request_flag:;
unsigned quirk_ep_out_aligned_size:;
unsigned quirk_altset_not_supp:;
unsigned quirk_stall_not_supp:;
unsigned quirk_zlp_not_supp:;
unsigned quirk_avoids_skb_reserve:;
unsigned is_selfpowered:; /*表明此gadget是否为self-powered*/
unsigned deactivated:;
unsigned connected:;
unsigned lpm_capable:;
};
我们发现这些bind就是一个目的,初始化struct usb_composite_dev结构,使其逐渐丰满。因为这个结构代表一个USB设备。
经过合适的初始化后设备才能正确的工作。经过重重初始化,三层总算整合在了一起了。这三层最终形成了一个饱满的
struct usb_composite_dev结构。这个结构包含USB设备运行各种信息。包括:配置,接口,端点等。
6.作为host称为USB主机控制器驱动,作为gadget时称为USB设备控制器驱动(UDC)
7.USB设备控制器驱动,需要关心四个核心的数据结构,这些数据结构包括描述一个USB设备控制器的usb_gadget、UDC操作usb_gadget_ops、
描述一个端点的usb_ep以及描述端点操作的usb_ep_ops结构体。UDC驱动围绕这些数据结构及其成员函数展开
8.打印机的设备位置为:# ls -l /devices/virtual/usb_printer_gadget/
9.由于普通USB设备只有一个上游端口,因此它们只有一个这样的驱动程序。 控制器驱动程序可以支持任意数量的不同gadget驱动程序,
但一次只能使用其中一个。
10.host端驱动与USB主机控制器驱动的交互通过 struct urb, 而作为gadget时,驱动与USB设备控制器之间的交互使用 struct usb_request来 ########
描述一次IO请求。
11.内核文档翻译:
https://01.org/linuxgraphics/gfx-docs/drm/driver-api/usb/gadget.html?highlight=usb_composite_probe#c.usb_composite_probe
gadget驱动的生命周期
gadget驱动程序向硬件发出端点I/O请求,而无需了解硬件的许多细节,但驱动程序设置/配置代码需要处理一些差异。 像这样使用API:
1.为特定设备端USB控制器硬件注册驱动程序,例如PCI PDA(USB 2.0)上的net2280,Linux PDA中的sa11x0或pxa25x,依此类推。 此时,
设备逻辑上处于USB ch9初始状态(已连接attached),没有电源且无法使用(因为它还不支持枚举)。 任何主机都不应该看到该设备,
因为即使VBUS电源可用,它也不会激活主机用来检测设备的数据线上拉。
2.注册实现某些更高级别设备功能的gadget驱动程序。 然后将bind()绑定到usb_gadget,它会在检测到VBUS后的某个时间激活数据线上拉。
3.硬件驱动程序现在可以开始枚举。 它处理的步骤是接受USB电源和set_address请求。 其他步骤由gadget驱动程序处理。 如果在主机开
始枚举之前卸载gadget驱动程序模块,则跳过步骤7之前的步骤。
4.gadget驱动程序的setup()调用返回usb描述符,既基于总线接口硬件提供的内容,也基于正在实现的功能。 这可能涉及备用设置或配置,
除非硬件阻止此类操作。 对于OTG设备,每个配置描述符包括OTG描述符。
5.当USB主机发出set_configuration调用时,gadget驱动程序处理枚举的最后一步。 它使能该配置中使用的所有端点,使所有接口都处于
默认设置。 这涉及使用硬件端点列表,根据端点描述符启用每个端点。 它还可能涉及使用usb_gadget_vbus_draw()来从VBUS中获取更多的
电流(可选Rcar3没有实现),如该配置所允许的那样。 对于OTG设备,设置配置还可能涉及通过用户界面报告HNP功能。
6.执行实际工作并执行数据传输,可能涉及更改接口设置或切换到新配置,直到设备与主机断开连接disconnect()。 将任意数量的传输请求
排队到每个端点。 在断开连接之前,它可能会被suspend和resume几次。 断开连接时,驱动程序将返回步骤3(上方)。
7.卸载gadget驱动程序模块时,驱动程序的unbind()将会被回调。 这样可以控制器驱动程序被卸载。
驱动程序通常会被安排,以便只加载gadget驱动程序模块(或将其静态链接到Linux内核)允许外围设备被枚举,但某些驱动程序将推迟枚举,
直到某些更高级别的组件(如用户模式守护程序)使能它。 请注意,在此最低级别,没有关于如何实现ep0配置逻辑的策略,除了它应遵守
USB规范。 这些问题属于gadget驱动程序领域,包括了解某些USB控制器强加的实施限制,或者了解复合设备可能恰好通过集成可重用组件来
构建。
请注意,OTG设备的上述生命周期可能略有不同。 除了在每个配置中提供额外的OTG描述符之外,只有与HNP相关的差异对于驱动程序代码特
别可见。 它们涉及SET_CONFIGURATION请求期间的报告要求,以及在某些挂起回调期间调用HNP的选项。 此外,SRP稍微改变了
usb_gadget_wakeup的语义。
struct usb_request 描述一次IO请求。
12.Gadget 功能驱动层, Gadget功能驱动层是USB Gadget软件结构的最上层。主要是实现USB设备的功能,
这一层通常与linux内核的其他子系统有密切的联系。
13.mod_gadget.c:这个是2.0的UDC驱动,renesas_usb3.c: 应该3.0的usb gadget的UDC驱动。
14.UDC层还需要提供USB设备的中断处理程序,中断处理尤其重要。因为所有的USB传输都是由主机发起,而有没有USB传输完全由USB中断判
定,所以USB中断处理程序是整个软件架构的核心。
15.在用户程序中,select()和poll()本质上是一样的, 不同只是引入的方式不同,前者是在BSD UNIX中引入的,后者是在System V中引入的。
用的比较广泛的是select
16.DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
/*==========================上面等效下面======================================*/
static struct usb_function_driver printerusb_func = {
.name = "printer",
.mod = THIS_MODULE,
.alloc_inst = gprinter_alloc_inst,
.alloc_func = gprinter_alloc,
};
MODULE_ALIAS("usbfunc:printer"); /*用它来加载驱动模块*/ static int __init printermod_init(void)
{
return usb_function_register(&printerusb_func);
}
static void __exit printermod_exit(void)
{
usb_function_unregister(&printerusb_func);
}
module_init(printermod_init);
module_exit(printermod_exit);
module_usb_composite_driver(printer_driver);
/*-------module_usb_composite_driver(printer_driver);等效如下----------*/
static int __init printer_driver_init(void)
{
return usb_composite_probe(&printer_driver);
}
module_init(printer_driver_init);
static void __exit printer_driver_exit(void)
{
usb_composite_unregister(&printer_driver);
}
module_exit(printer_driver_exit); /*----------------------------------------------------------------------*/
17.module_param_named(maximum_line_test, max_test, int, 0); 此定义下,该参数的外部可见参数名为:maximum_line_test,内部全局变量名为:max_test */
18.usb2.0的gadget的UDC注册过程:
usbhs_probe //[usb/renesas_usbhs/common.c]
usbhs_mod_probe //[usb/renesas_usbhs/mod.c]
usbhs_mod_gadget_probe //[usb/renesas_usbhs/mod_gadget.c]
usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) //[usb/gadget/udc/core.c] 将新gadget添加到udc类驱动程序列表中
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
udc->dev.class = udc_class;
udc->dev.groups = usb_udc_attr_groups;
list_add_tail(&udc->list, &udc_list); 将此udc放到全局链表udc_list中
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
udc->vbus = true;
check_pending_gadget_drivers(udc) //[usb/gadget/udc/core.c] 匹配一个usb_gadget_driver
list_for_each_entry(driver, &gadget_driver_pending_list, pending) //在gadget_driver_pending_list查找pending的usb_gadget_driver
if (!driver->udc_name || strcmp(driver->udc_name, dev_name(&udc->dev)) == ) //usb_udc和usb_gadget_driver通过名字进行匹配
ret = udc_bind_to_driver(udc, driver); =====>UDC也是以一个usb_gadget的形式注册的。
19.f_printer.c中function注册过程:
DECLARE_USB_FUNCTION_INIT
int __init printermod_init(void)
usb_function_register(&printerusb_func) //这个注册仅仅是将usb_function_driver放到全局链表func_list中
list_add_tail(&newf->list, &func_list); //[usb/gadget/functions.c]
20.printer.c中:
printer_driver_init
usb_composite_probe(struct usb_composite_driver *driver); //传参为全局变量&printer_driver
driver->gadget_driver = composite_driver_template;
由usb_composite_driver构造出一个usb_gadget_driver
usb_gadget_probe_driver(struct usb_gadget_driver *driver) //传参为构造出来的usb_gadget_driver
它里面去匹配已经注册的UDC,若匹配不到就挂载在gadget_driver_pending_list上。
list_for_each_entry(udc, &udc_list, list) //在udc_list上查找已经注册的usb_udc
list_add_tail(&driver->pending, &gadget_driver_pending_list); //若匹配不到就将usb_gadget_driver放到链表gadget_driver_pending_list中。
udc_bind_to_driver(udc, driver); //若找到就将此usb_udc绑定到usb_gadget_driver中
udc->driver = driver;
driver->bind(udc->gadget, driver); //调用usb_gadget_driver的bind(),也就是composite_bind,是模板composite_driver_template中的,
参数是usb_gadget和usb_gadget_driver
struct usb_composite_dev *cdev = kzalloc(sizeof *cdev, GFP_KERNEL); //分配一个usb_composite_dev结构
cdev->gadget = usb_gadget;
composite_dev_prepare(struct usb_composite_driver *composite, struct usb_composite_dev *cdev)
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); //预先分配ep0使用的req
cdev->req->complete = composite_setup_complete; //指定ep0的完成回调函数
usb_ep_autoconfig_reset(gadget); //执行一次端点的rest
composite->bind(cdev); //调用usb_composite_driver的bind(),也就是printer_bind,其调用流程如① ###########
update_unchanged_dev_desc(usb_device_descriptor *new, usb_device_descriptor *old) //更新设备描述符
usb_gadget_udc_start(udc); //使能usb设备控制器
udc->gadget->ops->udc_start(udc->gadget, udc->driver) //也就是调用usbhsg_gadget_ops.usbhsg_gadget_start,做一些设备控制器的初始化
usbhsg_try_start
usbhsg_update_pullup
usb_udc_connect_control(udc);
usb_gadget_connect
gadget->ops->pullup(gadget, ); //也就是调用usbhsg_gadget_ops.pullup,使能Dp上拉
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); //上报App printer.c中的usb_composite_driver.bind的调用流程:
printer_bind //① ###########
usb_get_function_instance("printer");
try_get_usb_function_instance(name);
list_for_each_entry(fd, &func_list, list) 在全局链表func_list中通过name查找之前使用
fi = fd->alloc_inst(); 找到对应的usb_function_driver并调用其alloc_inst()
fi->fd = fd; return fi; 然后fi->fd指向找到的usb_function_driver结构体。
usb_string_ids_tab(cdev, strings); //[usb/gadget/functions.c] 为每一个字符串都分配一个唯一的id,然后将这些id赋值给
usb_device_descriptor中的iManufacturer,iProduct,iSerialNumber。
gadget_is_otg //判断是不是otg,如果是,还要为其分配otg描述符 usb_otg_descriptor_alloc
usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
usb_add_config_only(cdev, config); //将此config添加到usb_composite_dev cdev->configs中
printer_do_config //执行这个回调
usb_ep_autoconfig_reset
usb_gadget_set_selfpowered
f_printer = usb_get_function(fi_printer);
f = fi->fd->alloc_func(fi); //调用usb_function_instance的usb_function_driver的alloc_func,应该就
是gprinter_alloc,获取到一个usb_function
gprinter_alloc中初始化usb_function的各个成员函数(bind,unbind,setup,set_alt),做一些链表的初始化
f->fi = fi; return f; //usb_function的usb_function_instance指向此fi
status = usb_add_function(c, f_printer);
list_add_tail(&function->list, &config->functions); //将此usb_function挂接在usb_configuration的functions链表上
function->bind(config, function) //调用function的bind进行绑定,也即是printer_func_bind
usb_interface_id //分配一个没有使用的接口id
in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); //根据端点描述符自动分配IN/OUT端点
out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
usb_assign_descriptors //对usb_function中的各个描述符赋值
for (i = ; i < dev->q_len; i++) //为in_ep/out_ep分配usb_request,并将其插入队列中
req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
list_add(&req->list, &dev->tx_reqs);
req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
list_add(&req->list, &dev->rx_reqs);
device_create(usb_gadget_class, NULL, devt, NULL, "g_printer%d", dev->minor); //创建设备类,为创建设备文件做准备
cdev_init(&dev->printer_cdev, &printer_io_operations); //在bind()中指定对用户空间的接口printer_io_operations
ret = cdev_add(&dev->printer_cdev, devt, );
usb_ep_autoconfig_reset(cdev->gadget); //执行一次reset ep的操作
usb_composite_overwrite_options //用来以实际的值覆盖USB_GADGET_COMPOSITE_OPTIONS中的模块参数声明。
USB gadget 驱动 printer.c 分析的更多相关文章
- Linux下 USB设备驱动分析(原创)
之前做过STM32的usb HID复合设备,闲来看看linux下USB设备驱动是怎么一回事, 参考资料基于韦东山JZ2440开发板,以下,有错误欢迎指出. 1.准备知识 1.1USB相关概念: USB ...
- USB键盘驱动分析
简介 本文介绍USB驱动程序编写的流程,分析一个键盘的USB程序,基于linux-2.6.39 USB驱动概要 分层 主机层面的USB驱动的整体架构可以分成4层,自顶到下依次是 1.USB设备驱动:本 ...
- Linux USB ECM Gadget 驱动介绍
1 USB ECM介绍 USB ECM,属于USB-IF定义的CDC(Communication Device Class)下的一个子类:Ethernet Networking Control Mo ...
- Linux usb子系统(一) _写一个usb鼠标驱动
USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...
- debian下使用dynamic printk分析usb转串口驱动执行流程
看了一篇文章<debug by printing>,文中提到了多种通过printk来调试驱动的方法,其中最有用的就是"Dynamic debugging". “Dyna ...
- Linux gadget驱动分析1------驱动加载过程
为了解决一个问题,简单看了一遍linux gadget驱动的加载流程.做一下记录. 使用的内核为linux 2.6.35 硬件为芯唐NUC950. gadget是在UDC驱动上面的一层,如果要编写ga ...
- Android USB驱动源码分析(-)
Android USB驱动中,上层应用协议里最重要的一个文件是android/kernel/drivers/usb/gadget/android.c.这个文件实现USB的上层应用协议. 首先包含了一些 ...
- [置顶] 自娱自乐6之Linux gadget驱动5(自编gadget驱动,包涵与之通讯的主机usb驱动,已调试通过)
这个代码调试,你首先要保证你的udc驱动没用问题,这个有些矛盾,应为我本来要用gadget驱动来调试udc驱动,结果反过来了. 这是在zero基础改的,大概的改动 1. 去掉loop. 2. sink ...
- Linux 串口、usb转串口驱动分析(2-2) 【转】
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...
随机推荐
- eclipse 与 tomcat 的那些路径
我们用mvn创建了一个web工程,同时希望在eclipse里调试开发.mvn有mvn的路径要求,eclispe有eclipse的默认路径,怎么整合二者? 首先介绍一下eclipse的默认路径. 重点在 ...
- English trip -- VC(情景课)1 C What's your name?
Grammar focus 语法点 What's your name? What's his name? What her name? My name is Angela. His name is K ...
- LeetCode--035--搜索插入位置
问题描述: 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会被按顺序插入的位置. 你可以假设数组中无重复元素. 方法1:for 循环 class S ...
- Ubuntu 18.04 LTS 安装wine 、exe程序安装和卸载
什么是wine?Wine(是“Wine Is Not an Emulator”的缩写)是一个兼容层,能够在几个POSIX兼容的操作系统上运行Windows应用程序,如Linux.MaOS.BSD.代替 ...
- HDU1506(真心不错的DP)
Largest Rectangle in a Histogram Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 ...
- 牛客网——B-栈和排序
链接:https://www.nowcoder.com/acm/contest/58/B来源:牛客网 题目描述 给你一个1->n的排列和一个栈,入栈顺序给定 你要在不打乱入栈顺序的情况下,对数组 ...
- Pl/sql Dev登录数据库查询报ORA-03114
pl/sql dev能正常登录数据库,但是登录后执行查询报 ORA-03114异常. 1.建议安装PL/SQL Dev的32位版,64位版不太好用,且需要64位对应的OCI.DLL. 64位简直不要太 ...
- length,lengthb,substr,substrb,instr小用
--字符串的字符长度 select length('wm.dfw.士农工商.sda.人马ss.dfw.4.sdf.332.sf.qq.sd') from dual; --字符串的字节长度 select ...
- json添加数据
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- sgu 183. Painting the balls 动态规划 难度:3
183. Painting the balls time limit per test: 0.25 sec.memory limit per test: 4096 KB input: standard ...