Linux_USB驱动程序设计学习

来源:互联网 发布:语音速记软件 编辑:程序博客网 时间:2024/05/16 02:35

Linux_USB软件系统架构构成:



usb主控制驱动(USB Host Controller Driver):为USB主控制器提供驱动

usb核心(USB Core):为usb主控制器和usb设备驱动连接起桥梁作用

usb设备驱动(USB Client Driver):为连接到usb主机的设备提供驱动


USB设备组成结构:



usb设备包括配置(configuration)、 接口( interface) 和端点(endpoint), 一个USB设备驱动程序对应一个USB接口 , 而非整个USB设备。
usb设备可能由一个或者多个配置构成,一个配置可能为多个接口构成,一个接口可能由一个或者多个端点构成。

usb的一个接口就是usb设备的一个基本功能。如果一个usb设备拥有多个功能,那就需要多个接口构成。


主机与usb设备通信方式:

主机首先要发送请求给设备,设备才能给出相应的响应。

usb设备驱动(发起请求的源,发起usb请求)->usb核心->usb主控制器驱动(使用usb总线把指令、信息发给usb设备) 

usb设备响应指令(返回信息)usb总线->usb主控制器->usb核心->usb设备驱动


usb请求信息使用urb结构体描述:

USB请求块( USB request block-URB)是USB设备驱动中用来与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体, 是USB主机与设备通信的“电波”。

urb使用步奏:

1. USB 设备驱动程序创建并初始化一个访问特定端点的 urb,并提交给USB core;
2. USB core提交该urb到 USB主控制器驱动程序;
3. USB 主控制器驱动程序根据该urb描述的信息,来访问 USB设备;
4. 当设备访问结束后,USB 主控制器驱动程序通知USB 设备驱动程序。

创建urb:

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
参数:
iso_packets: urb所包含的等时数据包的个数,创建非批量urb填写0。
mem_flags:内 存分配标识(如GFP_KERNEL), 参考kmalloc。


初始化urb:

由端点类型分为四种不同的端点:
对于中断urb,使用 usb_fill_int_urb函数来初始化
对于批量urb,使用 usb_fill_bulk_urb函数来初始化
对于控制urb,使用 usb_fill_control_urb函数来初始化
对于等时urb, 只能手动地初始化urb。
static inline void usb_fill_int_urb(struct urb *urb, //待初始化的urbstruct usb_device *dev, //urb所要访问的设备unsigned int pipe, //要访问的端点所对应的管道,void *transfer_buffer, //保存传输数据的bufferint buffer_length, //buffer长度usb_complete_t complete_fn, //urb完成时调用的函数void *context, //赋值到urb->context的数据int interval) //urb被调度的时间间隔

提交urb:

在完成urb的创建和初始化后, USB驱动需要将urb提交给USB核心.
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
参数:
urb: 要提交urb的指针
mem_flags: 内存分配标识(如GFP_KERNEL), 参考kmallocURB被提交到 USB核心后, USB核心指定usb主控制器驱动程序来处理该urb, 处理完之后, urb完成函数将被调用。

在Linux内核中,使用 struct usb_driver结构描述一个USB驱动。

struct usb_driver {const char *name; /*驱动程序名 *//* 当 USB核心发现了 该驱动能够处理的 USB接口时, 调用该函数 */int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);/* 当相应的 USB接口被移除时, 调用该函数 */void (*disconnect) (struct usb_interface *intf);/* USB驱动能够处理的设备列表 */const struct usb_device_id *id_table;}

struct usb_device_id *id_table:保存这个驱动所支持的设备的ID,由制造商ID和编号ID构成,查看usb设备的两个ID号使用lsusb命令查看,前一个为制造商ID后一个为编号ID。当有usb加载时,驱动首先会检查是否有usb设备的设备ID与usb驱动的id_table里面的设备ID相同,如果有则调用此驱动的probe函数。

当设备拔出的时候就会调用disconnect函数。

HID协议:HID(Human Interface Device), 属于人机交互类的设备,如USB鼠标, USB键盘, USB游戏操纵杆等。 该类设备必须遵循HID设计规范。

USB下载线驱动设计步奏:

1、实现struct usb_driver结构体:

struct usb_driver dnw_driver = {.name= "dnw",   /* 驱动名 */.probe= dnw_probe, /* 捕获函数 */.disconnect= dnw_disconnect, /* 卸载函数 */.id_table= dnw_id_table, /* 设备列表 */};
2、使用lsusb命令查看usb设备号,并实现id_table参数:

static struct usb_device_id dnw_id_table [] = {{ USB_DEVICE(0x5345, 0x1234) },{ }};
3、实现probe函数:

3.1、(应用程序需要读写操作)初始化字符设备,usb总线的字符设备用struct usb_class_driver结构体和usb_register_dev函数创建,与一般字符设备创建函数cdev实现效果一样:

static struct usb_class_driver dnw_class = {.name ="secbulk%d",/*创建的设备名,打开设备时的名字*/.fops =&dnw_ops,/*设备的操作函数集名字*/.minor_base =100,/*需要创建的设备的次设备号,从100开始*/};

static struct file_operations dnw_ops ={.owner =THIS_MODULE,.write =dnw_write,.open =dnw_open,.release =dnw_release,};

3.2、创建usb设备struct usb_device *udev,并利用probe函数中的struct usb_interface *intf接口信息转换成usb对对应的设备,转换函数:interface_to_usbdev,所需参数为struct usb_interface *intf,再使用usb_get_dev包含interface_to_usbdev函数,返回值为usb设备。

利用struct usb_interface *intf获取端点地址:

3.3.1、创建接口描述:

struct usb_host_interface *interface;

3.3.2、获取当前接口设置:

interface = intf->cur_altsetting;

3.3.3、利用接口设置拿到接口描述符,在接口描述符里面就有端点的数量:

端点描述符初始化:struct usb_endpoint_descriptor *endpoint;

interface->desc.bNumEndpoints接口下端点的数量,利用for循环找出需要的端点,使用usb_endpoint_is_bulk_out函数判断端点是否为bulk_out端点。

static int dnw_probe(struct usb_interface *intf, const struct usb_device_id *id){    /* 接口设置描述 */    struct usb_host_interface *interface;    struct usb_endpoint_descriptor *endpoint;    int i;    interface = intf->cur_altsetting;    for(i=0;i<interface->desc.bNumEndpoints;i++)    {    endpoint = &interface->endpoint[i].desc;    if(usb_endpoint_is_bulk_out(endpoint))    {        bulk_out_endaddr = endpoint->bEndpointAddress;        break;    }        }    usb_register_dev(intf,&dnw_class);        udev = usb_get_dev(interface_to_usbdev(intf));}

4、实现write函数:

4.1、(分配urb、初始化urb、提交urb)可以使用usb_bulk_msg函数代替之前三个步奏

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)函数参数说明

第一个参数:usb设备名;

第二个参数:usb管道;

usb创建管道函数有usb_sndbulkpipe、和usb_rcvbulkpipe两种,分别是创建发送和接收函数

这两个函数的第一个参数为设备名,第二个参数为端点地址

第三个参数:需要发送的数据;

第四个参数:需要发送的数据的数量;

第五个参数:(返回的)实际传输的数据数量

第六个参数:时间间隔

4.2、(下载为大数据传输)usb端点选择为批量型


static ssize_t dnw_write(struct file *file, const char __user *buf, size_t len, loff_t *pos){    size_t to_write;    size_t total_write = 0;    size_t act_len;        while(len>0)    {    to_write = min(len,(size_t)BULKOUT_BUFFER_SIZE);    copy_from_user(bulkout_buffer,buf+total_write,to_write);usb_bulk_msg(udev,usb_sndbulkpipe(udev,bulk_out_endaddr),bulkout_buffer,to_write,&act_len,3*HZ);len -= to_write;total_write += to_write;    }        return total_write;}

示例代码:

#include <linux/module.h>#include <linux/kernel.h>#include <linux/usb.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/slab.h>#define BULKOUT_BUFFER_SIZE 512char *bulkout_buffer;struct usb_device *udev;__u8 bulk_out_endaddr;static struct usb_device_id dnw_id_table [] = {{ USB_DEVICE(0x5345, 0x1234) },{ }};static int dnw_open(struct inode* inode, struct file *file){     bulkout_buffer = kmalloc(BULKOUT_BUFFER_SIZE,GFP_KERNEL);     return 0;}static int dnw_release (struct inode* inode, struct file *file){    kfree(bulkout_buffer);    return 0;}static ssize_t dnw_write(struct file *file, const char __user *buf, size_t len, loff_t *pos){    size_t to_write;    size_t total_write = 0;    size_t act_len;        while(len>0)    {    to_write = min(len,(size_t)BULKOUT_BUFFER_SIZE);    copy_from_user(bulkout_buffer,buf+total_write,to_write);usb_bulk_msg(udev,usb_sndbulkpipe(udev,bulk_out_endaddr),bulkout_buffer,to_write,&act_len,3*HZ);len -= to_write;total_write += to_write;    }        return total_write;}static struct file_operations dnw_ops ={.owner =THIS_MODULE,.write =dnw_write,.open =dnw_open,.release =dnw_release,};static struct usb_class_driver dnw_class = {.name ="secbulk%d",.fops =&dnw_ops,.minor_base =100,};static int dnw_probe(struct usb_interface *intf, const struct usb_device_id *id){    /* 接口设置描述 */    struct usb_host_interface *interface;    struct usb_endpoint_descriptor *endpoint;    int i;    interface = intf->cur_altsetting;    for(i=0;i<interface->desc.bNumEndpoints;i++)    {    endpoint = &interface->endpoint[i].desc;    if(usb_endpoint_is_bulk_out(endpoint))    {        bulk_out_endaddr = endpoint->bEndpointAddress;        break;    }        }    usb_register_dev(intf,&dnw_class);        udev = usb_get_dev(interface_to_usbdev(intf));}static void dnw_disconnect(struct usb_interface *intf){usb_deregister_dev(intf,&dnw_class);}struct usb_driver dnw_driver = {.name= "dnw",   /* 驱动名 */.probe= dnw_probe, /* 捕获函数 */.disconnect= dnw_disconnect, /* 卸载函数 */.id_table= dnw_id_table, /* 设备列表 */};int dnw_init(){    usb_register(&dnw_driver);    return 0;}void dnw_exit(){    usb_deregister(&dnw_driver);}module_init(dnw_init);module_exit(dnw_exit);MODULE_LICENSE("GPL");

0 0
原创粉丝点击