Linux USB 驱动开发(三)—— 编写USB 驱动程序

    前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否安装了硬件。当然,这些制造商和设备标识需要我们编写进USB 驱动程序中。

  USB 驱动程序依然遵循设备模型 —— 总线、设备、驱动。和I2C 总线设备驱动编写一样,所有的USB驱动程序都必须创建的主要结构体是 struct usb_driver,它们向USB 核心代码描述了USB 驱动程序。但这是个外壳,只是实现设备和总线的挂接,具体的USB 设备是什么样的,如何实现的,比如一个字符设备,我们还需填写相应的文件操作接口 ,下面我们从外到里进行剖析,学习如何搭建这样的一个USB驱动外壳框架:


一、注册USB驱动程序

  Linux的设备驱动,特别是这种hotplug的USB设备驱动,会被编译成模块,然后在需要时挂在到内核。所以USB驱动和注册与正常的模块注册、卸载是一样的,下面是USB驱动的注册与卸载:
static int __init usb_skel_init(void)   
{   
     int result;   
     /* register this driver with the USB subsystem */   
     result = usb_register(&skel_driver);   
     if (result)   
         err("usb_register failed. Error number %d", result);   
  
     return result;   
}   
  
static void __exit usb_skel_exit(void)   
{   
     /* deregister this driver with the USB subsystem */   
     usb_deregister(&skel_driver);   
}   
  
module_init (usb_skel_init);   
module_exit (usb_skel_exit);   
MODULE_LICENSE("GPL");

      USB设备驱动的模块加载函数通用的方法是在I2C设备驱动的模块加载函数中使用usb_register(struct *usb_driver)函数添加usb_driver的工作,而在模块卸载函数中利用usb_deregister(struct *usb_driver)做相反的工作。 对比I2C设备驱动中的 i2c_add_driver(&i2c_driver)i2c_del_driver(&i2c_driver)

    struct usb_driver是USB设备驱动,我们需要实现其成员函数:

static struct usb_driver skel_driver = {   
     .owner = THIS_MODULE,    
     .name = "skeleton",  
     .id_table = skel_table,       
     .probe = skel_probe,     
     .disconnect = skel_disconnect,     
};    
从代码看来,usb_driver需要初始化五个字段:

模块的所有者 THIS_MODULE
模块的名字  skeleton
probe函数   skel_probe
disconnect函数skel_disconnect

id_table

    最重要的当然是probe函数与disconnect函数,这个在后面详细介绍,先谈一下id_table:

    id_table 是struct usb_device_id 类型,包含了一列该驱动程序可以支持的所有不同类型的USB设备。如果没有设置该变量,USB驱动程序中的探测回调该函数将不会被调用。对比I2C中struct i2c_device_id *id_table,一个驱动程序可以对应多个设备,i2c 示例:

static const struct i2c_device_id mpu6050_id[] = {    
    { "mpu6050", 0},    
    {}    
};  

    usb子系统通过设备的production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备,并调用相关的驱动程序作处理。我们可以看看这个id_table到底是什么东西:

static struct usb_device_id skel_table [] = {     
     { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },     
     { }                    /* Terminating entry */     
};     
    
MODULE_DEVICE_TABLE (usb, skel_table);   

   MODULE_DEVICE_TABLE的第一个参数是 设备的类型,如果是USB设备,那自然是usb。后面一个参数是 设备表这个设备表的最后一个元素是空的,用于标识结束。代码定义了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备的vendor ID和product ID,如果它们的值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备的驱动。

   当USB设备接到USB控制器接口时,usb_core就检测该设备的一些信息,例如生产厂商ID和产品的ID,或者是设备所属的class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备
  
 

     我们下面所要做的就是对probe函数与disconnect函数的填充了,但是在对probe函数与disconnect函数填充之前,有必要先学习三个重要的数据结构,这在我们后面probe函数与disconnect函数中有很大的作用:

二、USB驱动程序中重要数据结构

1、usb-skeleton

       usb-skeleton 是一个局部结构体,用于与端点进行通信。下面先看一下Linux内核源码中的一个usb-skeleton(就是usb驱动的骨架咯),其定义的设备结构体就叫做usb-skel:

struct usb_skel {   
     struct usb_device *udev;                 /* the usb device for this device */   
     struct usb_interface  *interface;            /* the interface for this device */   
     struct semaphore limit_sem;         /* limiting the number of writes in progress */   
     unsigned char *bulk_in_buffer;     /* the buffer to receive data */   
     size_t         bulk_in_size;                  /* the size of the receive buffer */   
     __u8          bulk_in_endpointAddr;        /* the address of the bulk in endpoint */   
     __u8          bulk_out_endpointAddr;      /* the address of the bulk out endpoint */   
     struct kref   kref;   
};  

他拥有:

描述usb设备的结构体udev
一个接口interface
用于并发访问控制的semaphore(信号量) limit_sem
用于接收数据的缓冲bulk_in_buffer
用于接收数据的缓冲尺寸bulk_in_size
批量输入端口地址bulk_in_endpointAddr
批量输出端口地址bulk_out_endpointAddr

内核使用的引用计数器


     从开发人员的角度看,每一个usb设备有若干个配置(configuration)组成,每个配置又可以有多个接口(interface)(我理解就是USB设备的一项功能),每个接口又有多个

  • 18
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Linux下,编写USB设备驱动程序的过程可以分为以下几个步骤: 1. 确定驱动程序框架:一般情况下,驱动程序需要使用Linux内核提供的USB子系统进行通信。可以选择使用USB核心驱动框架,如usbcore或者usbfs,这取决于驱动程序的需求和功能。 2. 编写设备识别和初始化函数:USB设备Linux系统中由Vendor ID(厂商ID)和Product ID(产品ID)唯一标识。在驱动程序中,需要编写设备识别函数,通过与系统中的已知设备进行匹配,确定设备的类型和特性。然后,初始化设备的状态和资源。 3. 实现设备的控制和数据传输:USB设备通常具有多个接口和端点,每个端点的功能和方向都不同。驱动程序需要实现设备的控制和数据传输功能,包括读取设备的描述符、配置设备、发送和接收数据等。 4. 处理中断和事件:某些USB设备可能会产生中断或者其他事件,这需要驱动程序对这些事件进行处理。可以注册中断处理程序或事件处理程序,并根据设备的需求进行响应。 5. 编写设备文件操作函数:Linux系统将USB设备作为设备文件进行管理。驱动程序需要编写打开、关闭、读取、写入等操作函数,将应用程序的请求传递给对应的设备。 6. 编译和加载驱动程序:完成驱动程序编写后,需要将其编译成模块或者内核。通过insmod命令将驱动程序模块加载到内核中,然后可以通过udev等工具进行设备的管理和配置。 编写USB设备驱动程序需要对Linux内核有一定的了解,了解USB协议和接口规范,掌握Linux内核编程的基本知识。同时,还需要通过查阅文档和示例代码来学习和理解USB设备驱动程序编写方法和技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值