Linux kernel: USB driver编写入门(二)
前面一篇文章Linux kernel: USB driver编写入门(一)介绍了一个最简单的USB驱动的最基本框架,本文将加入probe和disconnect函数,用于响应该设备插入和拔出。
继续在那个目录下$vim usb_test_drv.c,加入如下代码:
static int usb_drv_probe(struct usb_interface *interface, const struct usb_device_id *id)
{struct usb_host_interface *interface_desc;int ret;interface_desc = interface->cur_altsetting;printk(KERN_INFO "USB info %d now probed: (%04x:%04x)\n", interface_desc->desc.bInterfaceNumber, id->idVendor, id->idProduct);printk(KERN_INFO "ID->bNumEndpoints:%02x\n", interface_desc->desc.bNumEndpoints);printk(KERN_INFO "ID->bInterfaceClass:%02x\n", interface_desc->desc.bInterfaceClass);ret = usb_register_dev(interface,&usb_cd);if(ret){printk(KERN_INFO "usb_register_dev erro: %d\n", ret);}else{printk(KERN_INFO "Minor number = %d\n", interface->minor);}return ret;}static void usb_drv_disconnect(struct usb_interface *interface)
{printk(KERN_INFO "Disconneced and Release the MINOR number %d\n", interface->minor);usb_deregister_dev(interface, &usb_cd);
}static struct usb_driver usb_drv_struct={.name = "Actions USB Driver",.probe = usb_drv_probe,.disconnect = usb_drv_disconnect,.id_table = usb_drv_table
};
注意,这里引用了usb_cd,需要在文件前面加上该变量的声明。
static struct usb_class_driver usb_cd;
保存后,在当前目录下make. 如果没有出错,则加载该模块(命令同(一)),加载成功后,运行$lsmod | grep usb 和 $dmesg | tail来验证已经加载。
这时候,插入文(一)中的USB设备。注意,如果在Linux主机运行期间,已经插入过该设备。则需要重新启动Linux主机,因为Linux已有Kernel的驱动已经对该设备进行了一系列处理,会和我们新写的driver相冲突。
dmesg | tail -n 20
[ 241.357953] Register the usb driver with the usb subsystem
[ 241.358000] usbcore: registered new interface driver Actions USB Driver
[ 282.289547] usb 2-4: new high-speed USB device number 4 using xhci_hcd
[ 282.438387] usb 2-4: New USB device found, idVendor=10d6, idProduct=1101, bcdDevice= 1.00
[ 282.438400] usb 2-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 282.438406] usb 2-4: Product: USB CARDREADER
[ 282.438411] usb 2-4: Manufacturer: ACTIONS
[ 282.438415] usb 2-4: SerialNumber: ㉕捤稰眷㕳愳㤷湲
[ 282.441394] USB info 0 now probed: (10d6:1101)
[ 282.441406] ID->bNumEndpoints:02
[ 282.441411] ID->bInterfaceClass:08
[ 282.441416] usb_register_dev erro: -22
[ 282.441423] Actions USB Driver: probe of 2-4:1.0 failed with error -22
[ 282.519352] usb-storage 2-4:1.0: USB Mass Storage device detected
[ 282.519600] scsi host4: usb-storage 2-4:1.0
[ 282.519755] usbcore: registered new interface driver usb-storage
[ 283.533961] scsi 4:0:0:0: Direct-Access ACTIONS USB DISK FOB 2.0 0 PQ: 0 ANSI: 0 CCS
[ 283.534475] sd 4:0:0:0: Attached scsi generic sg1 type 0
[ 283.534848] sd 4:0:0:0: [sdb] Media removed, stopped polling
[ 283.535920] sd 4:0:0:0: [sdb] Attached SCSI removable disk
之所以这次用了tail -n 20是因为消息比较多,很多是Linux kernel自带驱动识别该设备后触发的消息。橙色背景的是probe函数被调用打印的消息。大家自己结合程序去查看。
usb_register_dev erro: -22返回错误信息,usb_register_dev这个函数没有在我们自己写的程序中调用,但是make通过没有报错,说明这个函数必然是在Linux kernel中定义了。从#include的头文件里,我们看到#include <linux/usb.h>。那么几乎可以肯定,usb_register_dev这个函数在这个头文件里声明了。
于是找到这个文件,
$ find ~/stable_rc/linux-5.19.0/ -wholename */linux/usb.h
/home/minipc/stable_rc/linux-5.19.0/include/linux/usb.h
这里选项用-wholename是因为有路径名称,因为linux目录不是直接在~/stable_rc/linux-5.19.0/(kernel源文件根目录)下,所以前面有*。在该文件中查找该函数声明:
$ grep usb_register_dev /home/minipc/stable_rc/linux-5.19.0/include/linux/usb.h
* number from the USB core by calling usb_register_dev().
* This structure is used for the usb_register_dev() and
extern int usb_register_device_driver(struct usb_device_driver *,
extern int usb_register_dev(struct usb_interface *intf,
最后一行即是:int usb_register_dev(struct usb_interface *intf,
之所以要找到函数原型,是为了后面查找定义更加方便,因为usb_register_dev的函数调用有很多。光有函数名称不够,还要有后面的参数定义就会更精准。
查找定义这个函数的文件。为减少搜索量,我们先在Linux kernel 源文件的根目录查找。
$find ~/stable_rc/linux-5.19.0/ -name usb
/home/minipc/stable_rc/linux-5.19.0/usr/include/linux/usb
/home/minipc/stable_rc/linux-5.19.0/tools/usb
/home/minipc/stable_rc/linux-5.19.0/tools/testing/selftests/drivers/usb
/home/minipc/stable_rc/linux-5.19.0/sound/usb
/home/minipc/stable_rc/linux-5.19.0/drivers/usb
/home/minipc/stable_rc/linux-5.19.0/drivers/media/usb
/home/minipc/stable_rc/linux-5.19.0/drivers/media/cec/usb
/home/minipc/stable_rc/linux-5.19.0/drivers/net/usb
/home/minipc/stable_rc/linux-5.19.0/drivers/net/can/usb
/home/minipc/stable_rc/linux-5.19.0/include/dt-bindings/usb
/home/minipc/stable_rc/linux-5.19.0/include/uapi/linux/usb
/home/minipc/stable_rc/linux-5.19.0/include/linux/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/devicetree/bindings/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/output/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/output/_sources/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/output/_sources/driver-api/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/output/.doctrees/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/output/.doctrees/driver-api/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/output/driver-api/usb
/home/minipc/stable_rc/linux-5.19.0/Documentation/driver-api/usb
从名称来看,我们先从 /home/minipc/stable_rc/linux-5.19.0/drivers/usb 文件夹里查找。
$ grep "usb_register_dev(struct" /home/minipc/stable_rc/linux-5.19.0/drivers/usb -r --include=*.c
/home/minipc/stable_rc/linux-5.19.0/drivers/usb/core/file.c:int usb_register_dev(struct usb_interface *intf,
这里为了更精确查找,字段选择"usb_register_dev(struct",其中struct是关键字,那么就排除了函数调用。其中--include=*.c,表示只从.c文件中选取。我们找到了函数定义文件usb/core/file.c.
打开该文件,可以找到该函数的定义:
int usb_register_dev(struct usb_interface *intf,struct usb_class_driver *class_driver)
{int retval;int minor_base = class_driver->minor_base;int minor;char name[20];#ifdef CONFIG_USB_DYNAMIC_MINORS/** We don't care what the device tries to start at, we want to start* at zero to pack the devices into the smallest available space with* no holes in the minor range.*/minor_base = 0;
#endifif (class_driver->fops == NULL)return -EINVAL;if (intf->minor >= 0)return -EADDRINUSE;mutex_lock(&init_usb_class_mutex);retval = init_usb_class();mutex_unlock(&init_usb_class_mutex);if (retval)return retval;dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);down_write(&minor_rwsem);for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {if (usb_minors[minor])continue;usb_minors[minor] = class_driver->fops;intf->minor = minor;break;}if (intf->minor < 0) {up_write(&minor_rwsem);return -EXFULL;}/* create a usb class device for this usb interface */snprintf(name, sizeof(name), class_driver->name, minor - minor_base);intf->usb_dev = device_create(usb_class->class, &intf->dev,MKDEV(USB_MAJOR, minor), class_driver,"%s", kbasename(name));if (IS_ERR(intf->usb_dev)) {usb_minors[minor] = NULL;intf->minor = -1;retval = PTR_ERR(intf->usb_dev);}up_write(&minor_rwsem);return retval;
}
EXPORT_SYMBOL_GPL(usb_register_dev);
从这个函数的定义里可以看到,
if (class_driver->fops == NULL)return -EINVAL;
if (intf->minor >= 0)return -EADDRINUSE;
这个EINVAL在include的头文件里<linux/errno.h>和<linux/usb.h>里都没有,所以要在原文件里查找它的定义
$ grep "^#define\s\+EINVAL" /home/minipc/stable_rc/linux-5.19.0/drivers/usb -r --include=*.h
这里使用了正则表达式,简单解释一下,^表示以这个开头,\s表示你空格,\+表示前面的字符有1个或多个。
结果输出为空,表明不在这个目录下,更换搜索目录为/home/minipc/stable_rc/linux-5.19.0/include/, 因为.h文件大部分都在这个目录下。
$ grep "^#define\s\+EINVAL" /home/minipc/stable_rc/linux-5.19.0/include/ -r --include=*.h
/home/minipc/stable_rc/linux-5.19.0/include/uapi/asm-generic/errno-base.h:#define EINVAL 22 /* Invalid argument */
发现-EINVAL正好是erro: -22。说明程序在下面一步就出错退出了:
if (class_driver->fops == NULL)return -EINVAL;
这里的class_driver->fops就是在usb_drv_probe函数定义中的调用usb_register_dev
ret = usb_register_dev(interface,&usb_cd);
时的输入参数&usb_cd->fops,而在usb_test_drv.c文件中,usb_cd仅仅声明了
static struct usb_class_driver usb_cd;
没有定义。那么它内部的指针fops就缺省为空了。struct usb_class_driver的定义可以在<linux/usb.h>文件中找到。
struct usb_class_driver {char *name;char *(*devnode)(struct device *dev, umode_t *mode);const struct file_operations *fops;int minor_base;
};
下面一篇文章Linux kernel: USB driver编写入门(三),我们将讲到如何对usb_cd变量进行赋值。
Linux kernel: USB driver编写入门(二)相关推荐
- linux下usb驱动编写
linux下usb驱动编写(内核2.4)--2.6与此接口有区别2006-09-15 14:57我们知道了在Linux下如何去使用一些最常见的USB设备.但对于做系统设计的程序员来说,这是远远不够的, ...
- Linux 驱动USB键盘驱动入门demo
1 需要内核配置文件禁用CONFIG_USB_HID,不然下面的驱动不会被探测到. 2 以下模块打印了8个控制按键是否被按下, 另外如果A按键按下,也会有打印. #include <linux/ ...
- Linux I2C设备驱动编写(二)
I2C对外API I2C client的注册 i2c_register_board_info具体实现 i2c_new_device I2C driver 关于I2C设备驱动的小总结 I2C adapt ...
- I2C driver编写指导二:编写i2c client driver指南
原文地址::http://blog.csdn.net/guoshaobei/archive/2010/06/08/5656001.aspx 译者:郭少悲 日期:2010/06/08 源文:linux- ...
- linux pci扫描链表,Linux Kernel ---- PCI Driver 分析
自己笔记使用. Kernel 版本 4.15.0 (ubuntu 18.04,intel skylake) 最近想学习VGA驱动去了解 DDCCP / EDID 等协议,然后顺便了解下驱动是如何工作的 ...
- Linux Kernel - Debug Guide (Linux内核调试指南 )
linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 ...
- 嵌入式Linux设备驱动程序:编写内核设备驱动程序
嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...
- RISC-V Linux kernel debug 环境搭建
目录 一.目的 二.准备工作 ①Build Ninja ②Build riscv-gnu-compiler toolchain and debug gdb ③命令行安装gcc-riscv64-linu ...
- linux驱动编写(usb host驱动入门)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] usb协议是一个复杂的协议,目前涉及到的版本就有usb1.0, usb2.0, usb3.0.大 ...
最新文章
- 安装esp8266库_基于ESP8266,DIY低成本智能远程开关灯小装置
- 自动化监控--zabbix中的show value详解
- mysql计算多少天后的日期_数据库 - mysql 计算某个时间,多少天后,多少个月后时间戳...
- AX2012 referencegroup
- JS Date格式化为yyyy-MM-dd类字符串
- 4015-基于递归的折半查找(C++)
- 目标检测之hough forest---霍夫森林(Hough Forest)目标检测算法
- python公式计算器_Python小白篇(二):Python计算器
- 关于@JsonView的使用心得及一些隐蔽的注意事项
- HTML5 怎么自定义字体
- 电脑时间校准方法,怎么校准电脑时间
- MSP430 G2553 Launchpad实现电容测量
- 相亲交友v6.7.7
- 可能是最全的:虚拟机使用失败解决方案汇总
- 【100%通过率】华为OD机试真题 Python 实现【核酸最快检测效率】【2022.11 Q4 新题】
- d3.js操作svg
- python新冠病毒COVID-19数据分析和数据可视化
- win10:mingw-w64安装教程
- Latex中编译时出现File `xxx.sty‘ not found.错误的解决方法
- 类Loopy是公共的, 应在名为 Loopy.java 的文件中声明