USB驱动程序设计之三USB鼠标驱动程序设计

USB驱动程序简介

USB设备驱动程序(层次划分)
在这里插入图片描述
USB设备驱动程序位于不同的内核子系统和USB主控制器之间,USB核心为USB驱动程序提供一个用于访问和控制USB硬件的软件接口,使得USB设备驱动程序不必考虑USB硬件控制器。

USB设备层次:
在这里插入图片描述
从上图可以看出,USB设备逻辑层次可以分为:设备、配置、接口、端点

划重点:

  • USB设备驱动程序对应的是接口层次,也就是说每一个接口都要对应一个USB设备驱动程序

USB驱动程序描述

在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;
	}; 

USB设备列表

一种设备:
Linux内核提供了宏USB_DEVICE来定义一种USB设备的
USB_DEVICE(vend,prod)

参数

  • vend:USB Vendor ID
  • prod:USB Product ID

例(USB OV511摄像头):
USB_DEVICE(VEND_OMNIVISION,PROD_OV511)
#define VEND_OMNIVISION 0x05A9
#define PROD_OV511 0x0511

struct usb_device_id
在这里插入图片描述

一类设备:

Linux内核提供了宏USB_INTERFACE_INFO来协助定义一类USB设备
USB_INTERFACE_INFO(cl,sc,pr)

参数:

  • cl:bInterfaceClass value
  • sc:bInterfaceSubClass value
  • pr:bInterfaceProtocol value

例(USB鼠标)

	USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE)

注册驱动

Linux内核提供了函数usb_register来注册USB驱动程序。

	static inline int usb_register(struct usb_driver *driver)

Linux USB 描述符

设备描述

linux内核使用 struct usb_device 来描述一个USB 设备

	struct usb_device
	{
		int devnum;  //USB设备号
		char devpath[16];  //设备ID字符串
		enum usb_device_state state;  //设备状态:未连接,已配置
		enum usb_device_speed speed;  //高速;全速;低速
	
		struct usb_device_descriptor descriptor; //USB设备描述符
		struct usb_host_config *config;
		
		char *product; //产品号
		char *manufacturer;  //厂商名
		char *serial;  //设备串号
	};

设备描述

上面结构体中包含的结构体:usb_device_descriptor 来对应USB描述符中的 设备描述符

	struct usb_device_descriptor
	{
		__u8 bLength;
		__u8 bDescriptorTypr;
		__le16 bcdUSB;
		__u8 bDeviceClass;
		__u8 bDeviceSubClass;
		__u8 bDeviceProtocol;
		__u8 bMaxPacketSize0;
		__le16 idVendor;
		__le16 idProduct;
		__le16 bcdDevice;
		__u8 iManufacturer;
		__u8 iProduct;
		__u8 iSerialNumber;
		__u8 bNumConfigurations;
	} __attribute__((packed));  //按字节对齐

严格按照USB协议的USB设备描述符保持一致;

配置描述:

上面结构体 struct usb_device 中包含的结构体:usb_device_host_config描述USB配置

	struct usb_host_config
	{
		struct usb_config_descriptor desc;  //配置描述符
		char *string;  //配置的字符串(如果存在)
		//接口链表
		struct usb_interface *interface[USB_MAXINTERFACES];
	};

上述结构体中的 struct usb_config_descriptor
在这里插入图片描述

接口描述

linux内核使用 struct usb_interface 来描述USB接口。

	struct usb_interface
	{
		struct usb_host_interface *altsetting;  //接口设置数组
		struct usb_host_interface *cur_altsetting; //当前设置
		unsigned num_altsetting;  //设置数
	};

配置 VS 设置

  • 一个配置包含一个或多个接口
  • 一个接口包含一个或多个设置

举例
在这里插入图片描述

接口设置

linux内核使用 struct usb_host_interface 来描述USB接口设置

	struct usb_host_interface
	{
		struct usb_interface_descriptor desc;//接口描述符
		struct usb_host_endpoint *endpoint;  //接口包含的端点
	};

接口描述符

linux内核使用 struct usb_interface_descriptor 来对应USB描述符中的接口描述符

在这里插入图片描述

端点描述

linux内核使用 struct usb_host_endpoint 来描述USB端点

	struct usb_host_endpoint
	{
		struct usb_endpoint_descriptor desc;  //端点描述符
		struct list_head  usb_list;  //urb
		
		.........
		.........
	};

在这里插入图片描述

USB URB

URB定义

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

URB处理流程

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

创建URB

创建URB函数为:

	struct *usb_alloc_urb(int iso_packets,gtp_t mem_flags) 

参数

  • iso_packets:urb所包含的等时数据包个数
  • mem_flags:内存分配标识(如GFP_KERNEL),参考kmalloc

初始化URB

  • 对于中断urb,使用 usb_fill_int_urb 函数来初始化:
	static inline void usb_fill_int_urb(struct urb *urb,
							struct usb_device *dev,
							unsigned int pipe,
							void *transfer_buffer,
							int buffer_length,
							usb_complete_t complete_fn,
							void *context,
							int interval)

参数:

  • urb:要初始化的urb指针;

  • dev:urb所要访问的设备;

  • pipe:要访问的端点所对应的管道,使用usb_sndintpipe()或usb_rcvintpipe()创建

  • transfer_buffer:要传输的数据缓冲区

  • buffer_length:transfer_buffer所指缓冲区长度

  • complete_fn:当完成urb所请求的操作时,要调用的回调函数;

  • context:complet_fn 函数所需的上下文,通常取值为dev;

  • interval:urb被调度的时间间隔

  • 对于批量urb,使用usb_fill_bulk_urb 函数来初始化

  • 对于控制urb,使用usb_fill_control_urb 函数来初始化

  • 等时urb没有像中断、控制和批量urb那样的初始化函数,我们只能手动地初始化urb

提交URB

在完成urb的创建和初始化后,urb便可以通过usb_submit_urb函数来提交后USB核心

	int usb_submit_urb(struct urb *urb,gfp_t mem_flags)

参数:

  • urb:执行urb的指针
  • mem_flags:内存分配标识,它用于告知USB核心如何分配内存缓冲区

处理URB

URB被提交后,USB核心指定主控制器驱动程序来处理该urb,在3种情况下,urb会被认为为处理完成

  1. urb 被成功发送给设备,并且设备返回正确的确认。如果 urb->status 为0,意味着对于一个输出urb,数据被成功发送;对于一个输入的urb,请求的数据被成功收到。
  2. 如果发送数据到设备或从设备接受数据时出现错误,urb->status将记录该错误值。
  3. urb 被“”取消“”,这发生在驱动通过 usb_unlink_urb()或usb_kill_urb()函数取消urb,或urb虽已提交,而USB设备被拔出的情况下。

当urb处理完成后,urb完成函数将被调用。

USB鼠标驱动程序分析设计

代码部分 mouse.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/slab.h>

#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
#define DRIVER_LICENSE "GPL"

MODULE_AUTHOR("silence");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);

struct usb_mouse
{
        char name[128];
        char phys[64];
        struct usb_device *usbdev;
        struct input_dev *dev;
        struct urb *irq;

        signed char *data;
        dma_addr_t data_dma;
};

static void usb_mouse_irq(struct urb *urb)
{
        struct usb_mouse *mouse = urb->context;
        signed char *data = mouse->data;
        struct input_dev *dev = mouse->dev;
        int status;

        //检测urb传输是否成功
        switch(urb->status)
        {
        case 0:         //success
                break;

        case -ECONNRESET:     //unlink
        case -ENOENT:
        case -ESHUTDOWN:
                return;

        //-EPIPE:should clear the halt
        default:         //error
                goto resubmit;
        }

        //报告按键状态
        input_report_key(dev,BTN_LEFT,data[0] & 0x01);
        input_report_key(dev,BTN_RIGHT,data[0] & 0x02);
        input_report_key(dev,BTN_MIDDLE,data[0] & 0x04);
        input_report_key(dev,BTN_SIDE,data[0] & 0x08);
        input_report_key(dev,BTN_EXTRA,data[0] & 0x10);

        input_report_rel(dev,REL_X,data[1]);
        input_report_rel(dev,REL_Y,data[2]);
        input_report_rel(dev,REL_WHEEL,data[3]);
		input_sync(dev);
		
resubmit:
        //提交下次传输
        status = usb_submit_urb(urb,GFP_ATOMIC);
        if(status)
                err("can't resubmit intr,%s-%s/input0,status %d",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath,status);
}

static int usb_mouse_open(struct input_dev *dev)
{
        struct usb_mouse *mouse = input_get_drvdata(dev);

        mouse->irq->dev = mouse->usbdev;
        if(usb_submit_urb(mouse->irq,GFP_KERNEL))
                return -EIO;

        return 0;
}

static void usb_mouse_close(struct input_dev *dev)
{
        struct usb_mouse *mouse = input_get_drvdata(dev);

        usb_kill_urb(mouse->irq);
}

static int usb_mouse_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
        //设备描述usb_device
        //接口描述usb_interface
        struct usb_device *dev = interface_to_usbdev(intf);

        //接口设置描述
        struct usb_host_interface *interface;

        //端点描述符
        struct usb_endpoint_descriptor *endpoint;

        struct usb_mouse *mouse;
        struct input_dev *input_dev;
        int pipe,maxp;
        int error = -ENOMEM;
		//获取当前接口设置
        interface = intf->cur_altsetting;

        //根据HID规范,鼠标只有一个端点(不包含0号控制端点)
        if(interface->desc.bNumEndpoints != 1)
                return -ENODEV;

        //获取端点0描述符
        endpoint = &interface->endpoint[0].desc;

        //根据HID规范,鼠标唯一的端点应为中断端点
        if(!usb_endpoint_is_int_in(endpoint))
                return -ENODEV;

        //生成中断管道
        pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);

        //返回该端点能够传输的最大包长度,鼠标的返回最大数据包为4个字节
        maxp = usb_maxpacket(dev,pipe,usb_pipeout(pipe));

        //创建input设备
        mouse = kzalloc(sizeof(struct usb_mouse),GFP_KERNEL);
        input_dev = input_allocate_device();
        if(!mouse || !input_dev)
                goto fail1;

        //申请内存空间用于数据传输,data为指向该空间地址
        mouse->data = usb_alloc_coherent(dev,8,GFP_ATOMIC,&mouse->data_dma);
        if(!mouse->data)
                goto fail1;

        //分配URB
        mouse->irq = usb_alloc_urb(0,GFP_KERNEL);
        if(!mouse->irq)
                goto fail2;
		
 mouse->usbdev = dev;
        mouse->dev = input_dev;

        if(dev->manufacturer)
                strlcpy(mouse->name,dev->manufacturer,sizeof(mouse->name));

        if(dev->product)
        {
                if(dev->manufacturer)
                        strlcat(mouse->name," ",sizeof(mouse->name));

                strlcat(mouse->name,dev->product,sizeof(mouse->name));
        }

        if(!strlen(mouse->name))
                snprintf(mouse->name,sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));

        //usb_make_path 用来获取 USB 设备在 Sysfs 中的路径
        usb_make_path(dev,mouse->phys,sizeof(mouse->phys));

        strlcat(mouse->phys,"/input0",sizeof(mouse->phys));

        //字符设备初始化
        input_dev->name = mouse->name;
        input_dev->phys = mouse->phys;
        usb_to_input_id(dev,&input_dev->id);
        input_dev->dev.parent = &intf->dev;

        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
        input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
		input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
        input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
        input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);

        input_set_drvdata(input_dev,mouse);

        input_dev->open = usb_mouse_open;
        input_dev->close = usb_mouse_close;

        //初始化中断URB
        usb_fill_int_urb(mouse->irq,dev,pipe,mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq,mouse,endpoint->bInterval);

        mouse->irq->transfer_dma = mouse->data_dma;
        mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

        error = input_register_device(mouse->dev);
        if(error)
                goto fail3;

        //将mouse指针保存在intf的dev成员中
        usb_set_intfdata(intf,mouse);
        return 0;

fail3:
        usb_free_urb(mouse->irq);
fail2:
        usb_free_coherent(dev,8,mouse->data,mouse->data_dma);
fail1:
        input_free_device(input_dev);
        kfree(mouse);
        return error;
}

	static void usb_mouse_disconnect(struct usb_interface *intf)
{
        struct usb_mouse *mouse = usb_get_intfdata(intf);

        usb_set_intfdata(intf,NULL);
        if(mouse)
        {
                usb_kill_urb(mouse->irq);
                input_unregister_device(mouse->dev);
                usb_free_urb(mouse->irq);
                usb_free_coherent(interface_to_usbdev(intf),8,mouse->data,mouse->data_dma);
                kfree(mouse);
        }
}

static struct usb_device_id usb_mouse_id_table [] =
{
        { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },
        { }  //Terminating entry
};

MODULE_DEVICE_TABLE(usb,usb_mouse_id_table);

static struct usb_driver usb_mouse_driver =
{
        .name = "usbmouse",  //驱动名
        .probe = usb_mouse_probe,  //捕获函数
        .disconnect = usb_mouse_disconnect,  //卸载函数
        .id_table = usb_mouse_id_table,  //设备列表
};
                                     
static int __init usb_mouse_init(void)
{
        //注册鼠标驱动程序
        int retval = usb_register(&usb_mouse_driver);
        if(retval == 0)
                //printk(KERN_INFO KBUILD_MODNAME ":" DRIVER_VERSION ":" DRIVER_DESC "\n");
                printk("register success\n");
        return retval;
}

static void __exit usb_mouse_exit(void)
{
        usb_deregister(&usb_mouse_driver);
}

module_init(usb_mouse_init);
module_exit(usb_mouse_exit);

猜你喜欢

转载自blog.csdn.net/qq_41782149/article/details/89705702