JZ2440开发板学习------中级(二十五:上)

2019-07-12 14:46发布

自己写驱动程序之USB     通过查看硬件手册,发现USB相关的硬件配置很难哦,而且USB是个大学问,现在的我只能写写比较简单的咯。看了一下内核中usbmouse.c,发现这个写的还算简单。先看看四个例子的效果: 例一: /*********************************************************************************************************************************** usbmouse_as_key.c
***********************************************************************************************************************************/ /*
 * drivershidusbhidusbmouse.c
 */
#include
#include
#include
#include
#include
#include

static struct usb_device_id usbmouse_as_key_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
//{USB_DEVICE(0x1234,0x5678)},
{ } /* Terminating entry */
};

static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
printk("found usbmouse! ");
return 0;
}

static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
printk("disconnect usbmouse! ");
}

/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "usbmouse_as_key_",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usbmouse_as_key_id_table,
};

static int usbmouse_as_key_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}


static void usbmouse_as_key_exit(void)
{
usb_deregister(&usbmouse_as_key_driver);
}


module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);


MODULE_LICENSE("GPL");


Makefile: KERN_DIR = /home/dianyuxiao/linux-3.4.2


all:
make -C $(KERN_DIR) M=`pwd` modules 


clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order


obj-m += usbmouse_as_key.o


测试: insmod usbmouse_as_key.ko
在开发板上接入、拔出USB鼠标





例二: /*********************************************************************************************************************************** usbmouse_as_key.c
***********************************************************************************************************************************/ /*
 * drivershidusbhidusbmouse.c
 */


#include
#include
#include
#include
#include
#include

static struct usb_device_id usbmouse_as_key_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
//{USB_DEVICE(0x1234,0x5678)},
{ } /* Terminating entry */
};


static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);

printk("found usbmouse! ");

printk("bcdUSB = %x ", dev->descriptor.bcdUSB);
printk("VID    = 0x%x ", dev->descriptor.idVendor);
printk("PID    = 0x%x ", dev->descriptor.idProduct);

return 0;
}

static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
printk("disconnect usbmouse! ");
}

/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "usbmouse_as_key_",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usbmouse_as_key_id_table,
};

static int usbmouse_as_key_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}

static void usbmouse_as_key_exit(void)
{
usb_deregister(&usbmouse_as_key_driver);
}

module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);

MODULE_LICENSE("GPL");

测试: insmod usbmouse_as_key.ko
在开发板上接入、拔出USB鼠标






例三: /*********************************************************************************************************************************** usbmouse_as_key.c
***********************************************************************************************************************************/ /*
 * drivershidusbhidusbmouse.c
 */

#include
#include
#include
#include
#include
#include

static struct input_dev *uk_dev;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;

static struct usb_device_id usbmouse_as_key_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
//{USB_DEVICE(0x1234,0x5678)},
{ } /* Terminating entry */
};

static void usbmouse_as_key_irq(struct urb *urb)
{
int i;
static int cnt = 0;
printk("data cnt %d: ", ++cnt);
for (i = 0; i < len; i++)
{
printk("%02x ", usb_buf[i]);
}
printk(" ");


/* 重新提交urb */
usb_submit_urb(uk_urb, GFP_KERNEL);
}

static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;

interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;


/* a. 分配一个input_dev */
uk_dev = input_allocate_device();

/* b. 设置 */
/* b.1 能产生哪类事件 */
set_bit(EV_KEY, uk_dev->evbit);
set_bit(EV_REP, uk_dev->evbit);

/* b.2 能产生哪些事件 */
set_bit(KEY_L, uk_dev->keybit);
set_bit(KEY_S, uk_dev->keybit);
set_bit(KEY_ENTER, uk_dev->keybit);

/* c. 注册 */
input_register_device(uk_dev);

/* d. 硬件相关操作 */
/* 数据传输3要素: 源,目的,长度 */
/* 源: USB设备的某个端点 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

/* 长度: */
len = endpoint->wMaxPacketSize;

/* 目的: */
usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);//  usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);

/* 使用"3要素" */
/* 分配usb request block */
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
/* 使用"3要素设置urb" */
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
uk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

/* 使用URB */
usb_submit_urb(uk_urb, GFP_KERNEL); return 0;
}


static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);

//printk("disconnect usbmouse! ");
usb_kill_urb(uk_urb);
usb_free_urb(uk_urb);

usb_buffer_free(dev, len, usb_buf, usb_buf_phys); //  usb_buf = usb_free_coherent(dev, len,usb_buf,usb_buf_phys);

input_unregister_device(uk_dev);
input_free_device(uk_dev);
}


/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "usbmouse_as_key_",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usbmouse_as_key_id_table,
};

static int usbmouse_as_key_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}

static void usbmouse_as_key_exit(void)
{
usb_deregister(&usbmouse_as_key_driver);
}


module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);


MODULE_LICENSE("GPL");

测试: 1. insmod usbmouse_as_key.ko
2. ls /dev/event*
3. 接上USB鼠标
4. ls /dev/event*
5. 操作鼠标观察数据








例四: /*********************************************************************************************************************************** usbmouse_as_key.c
***********************************************************************************************************************************/ /*
 * drivershidusbhidusbmouse.c
 */
#include
#include
#include
#include
#include
#include

static struct input_dev *uk_dev;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;

static struct usb_device_id usbmouse_as_key_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
//{USB_DEVICE(0x1234,0x5678)},
{ } /* Terminating entry */
};

static void usbmouse_as_key_irq(struct urb *urb)
{
static unsigned char pre_val;
#if 0
int i;
static int cnt = 0;
printk("data cnt %d: ", ++cnt);
for (i = 0; i < len; i++)
{
printk("%02x ", usb_buf[i]);
}
printk(" ");
#endif
/* USB鼠标数据含义
* data[0]: bit0-左键, 1-按下, 0-松开
*          bit1-右键, 1-按下, 0-松开
*          bit2-中键, 1-按下, 0-松开 
*
     */
if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
{
/* 左键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0);
input_sync(uk_dev);
}


if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
{
/* 右键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0);
input_sync(uk_dev);
}


if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
{
/* 中键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0);
input_sync(uk_dev);
}

pre_val = usb_buf[0];


/* 重新提交urb */
usb_submit_urb(uk_urb, GFP_KERNEL);
}

static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;

interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;


/* a. 分配一个input_dev */
uk_dev = input_allocate_device();

/* b. 设置 */
/* b.1 能产生哪类事件 */
set_bit(EV_KEY, uk_dev->evbit);
set_bit(EV_REP, uk_dev->evbit);

/* b.2 能产生哪些事件 */
set_bit(KEY_L, uk_dev->keybit);
set_bit(KEY_S, uk_dev->keybit);
set_bit(KEY_ENTER, uk_dev->keybit);

/* c. 注册 */
input_register_device(uk_dev);

/* d. 硬件相关操作 */
/* 数据传输3要素: 源,目的,长度 */
/* 源: USB设备的某个端点 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);


/* 长度: */
len = endpoint->wMaxPacketSize;


/* 目的: */
usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);


/* 使用"3要素" */
/* 分配usb request block */
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
/* 使用"3要素设置urb" */
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
uk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;


/* 使用URB */
usb_submit_urb(uk_urb, GFP_KERNEL);

return 0;
}

static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);


//printk("disconnect usbmouse! ");
usb_kill_urb(uk_urb);
usb_free_urb(uk_urb);


usb_free_coherent(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}

/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "usbmouse_as_key_",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usbmouse_as_key_id_table,
};

static int usbmouse_as_key_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}

static void usbmouse_as_key_exit(void)
{
usb_deregister(&usbmouse_as_key_driver);
}


module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);


MODULE_LICENSE("GPL");

测试:

测试4th:
1. insmod usbmouse_as_key.ko
2. ls /dev/event*
3. 接上USB鼠标
4. ls /dev/event*
5. cat /dev/tty1    然后按鼠标键
6. hexdump /dev/event0






上面有问题,现在还没有去查,先放着,之后再去解决!!!
对程序的理解可以看: http://liu1227787871.blog.163.com/blog/static/20536319720124270340843/





************************************************************************************************************************************************************************************************************************************************************************ http://blog.163.com/cl2006ky@126/blog/static/87195173201131245557340/
http://blog.chinaunix.net/uid-12461657-id-2974697.html
http://www.cnblogs.com/hoys/archive/2011/04/01/2002406.html


注释1: 在linux2.6.34和之前的代码中还可以使用usb_buffer_alloc 和 usb_buffer_free 这两个函数,在2.6.35和之后的内核中
usb_buffer_alloc 和 usb_buffer_free这两个函数已不在使用了,可以用usb_alloc_coherent 和 usb_free_coherent代替。把驱动里分配内存与释放内存释放函数修改就好了。
这是rename的patch:

USB: rename usb_buffer_alloc() and usb_buffer_free()

For more clearance what the functions actually do,

  usb_buffer_alloc() is renamed to usb_alloc_coherent()
  usb_buffer_free()  is renamed to usb_free_coherent()

They should only be used in code which really needs DMA coherency.

[added compatibility macros so we can convert things easier - gregkh]

Signed-off-by: Daniel Mack
Cc: Alan Stern
Cc: Pedro Ribeiro
Signed-off-by: Greg Kroah-Hartman

    说是为了更好的从名字看出这个函数真实做的事情:DMA coherency
    linux提供两种方式,来保证使用dma时,内存和硬件cache的一致性:

1.Coherent DMA mapping
    When using this mapping, the kernel ensures that there will be no cache coherency problems between the memory and the hardware device; this means that every write operation performed by the CPU on a RAM location is immediately visible to the hardware device, and vice versa. This type of mapping is also called "synchronous" or "consistent."

2.Streaming DMA mapping
    When using this mapping, the device driver must take care of cache coherency problems by using the proper synchronization helper functions. This type of mapping is also called "asynchronous" or "non-coherent."

      如果采用第一种方式的话,就是由kernel来保证一致性,驱动程序是不用考虑的,这种方法的缺点是在某些体系结构上,效率很低;如果采用第二种方式的话,那么是有驱动程序来保证一致性的,所以当驱动要使用dma来进行数据传输时,必须首先检测内存和硬件cache的一致性,linux提供了这类方法。
注释2:  1、定义:USB请求块(USB request block,urb)在USB设备驱动程序中用来描述与USB设备通信所用的基本载体和核心数据结构。非常类似于网络设备驱动程序中的sk_buff结构体,是USB主机与设备通信的电波。 2、URB处理流程: (1)USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb,并提交给USB core (2)USR core提交该urb到到USB主控制器驱动程序。 (3)USB主控制器驱动程序根据该urb描述的信息,来访问usb设备 (4)当设备访问结束后,USB主的控制器驱动程序通知USB设备驱动程序。 3、urb相关函数 (1)创建urb的函数: 原型:struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) 参数:iso_packets:urb所包含的等时数据包的个数            mem_flags  :内存分配标志(如GFP_KERNEL) (2)初始化urb(以中断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                      :要访问的设备            pipe                     :要访问的端点所对应的管道,使用usb_andintpipe()或usb_rcvintpipe()创建(管道:驱动程序的数据缓冲区和一个端点的连接,它代表了一种在两者之间移动数据的能力)             transfer_buffer    :要传输的数据的缓冲区。            buffer_length       :transfer_buffer所指缓冲区的长度            complete_fn        :当完成urb所请求的操作时,要调用的回调函数            context                :complet_fn函数所需的上下文,通常取值为dev            interval                 :urb被调度的时间间隔  (3)提交urb:在完成urb的创建和初始化后,urb便可以通过usb_submit_urb函数来提交给USB核心: 原型:int usb_submit_urb(struct urb *urb, gfp_t mem_flags) 参数:urb              :指向urb的指针            mem_flags :内存分配标志,它用于告诉USB核心如何分配内存缓冲区 处理:urb被提交到USB核心后,usb核心指定usb主控制器驱动程序来处理该urb,在3中情况下,urb会被认为处理完成 (1)urb被成功发送给设备,并且设备返回正确的确认。如果urb->status为0,意味着对于一个输出urb,数据被成功发送,对于一个输入urb,数据被成功接收。 (2)如果发送数据或接收数据发生了错误,urb->status会记录错误值 (3)urb被“取消”,这发生在驱动通过usb_unlink_urb()或usb_kill_urb()函数取消urb,或ur虽已提交,而usb设备被拔出的情况下。urb完成,函数将被调用。
总结: 我们来总结一下工作流程:当插入usb鼠标时,会根据驱动程序注册的id.table来遍历驱动并判断驱动与设备是否匹配。一旦匹配就会调用probe函数,所以我们的主要工作放在了probe函数里面。在probe函数中我们首先获得usb设备端口信息,这些信息在设置urb的时候会用到。接着设置能产生哪类事件以及这类事件里的哪些事件。然后就是重点了,我们来设置urb请求块,可以通过设置它来控制驱动程序与设备端点之间的通信。设置并提交之后,USB主控制器就会根据它以一定的间隔去查询usb设备是否发送数据到usb驱动程序缓冲区,一旦发现了usb设备发送数据过来就会向cpu发出中断请求,并执行相应的中断函数。在中断函数里面,我们根据usb设备发送过来的数据,判断发生了什么事情,然后上报相应的事件,最后不要忘记重新提交urb。