Linux_USB驱动程序设计学习

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, //待初始化的urb
struct usb_device *dev, //urb所要访问的设备
unsigned int pipe, //要访问的端点所对应的管道,
void *transfer_buffer, //保存传输数据的buffer
int 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 512
char *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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值