一、 驱动加载部分
static int __init usbtouch
_init(void)
{
return
usb_register(&usbtouch_driver);//注册触摸屏驱动
}
其中usbtouch
_driver的定义为:
static struct usb_driver usbtouch _driver =
{
.name = "usbtouchscreen",
.probe = usbtouch_probe,
.disconnect = usbtouch_disconnect,
.id_table = usbtouch_devices,
};
如果注册成功的话,将会调用usbtouch_probe。那么什么时候才算注册成功呢?和其它驱动注册过程一样,只有在其对应的“总线”上发现匹配的“设备”才会调用probe。当设备的接口类、接口子类、接口协议匹配驱动时驱动才会调用probe方法。
static struct usb_device_id usbtouch_devices[] =
{
#ifdef
CONFIG_TOUCHSCREEN_USB_EGALAX
// ignore the HID capable devices, handled by usbhid
*/
{USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info =
DEVTYPE_IGNORE},
{USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info =
DEVTYPE_IGNORE},
// normal device IDs */
{USB_DEVICE(0x3823, 0x0001), .driver_info =
DEVTYPE_EGALAX},
{USB_DEVICE(0x3823, 0x0002), .driver_info =
DEVTYPE_EGALAX},
{USB_DEVICE(0x0123, 0x0001), .driver_info =
DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0001), .driver_info =
DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0002), .driver_info =
DEVTYPE_EGALAX},
{USB_DEVICE(0x1234, 0x0001), .driver_info =
DEVTYPE_EGALAX},
{USB_DEVICE(0x1234, 0x0002), .driver_info =
DEVTYPE_EGALAX},
#endif
}
// usbtouch
driver支持的devices列表,
USB_DEVICE(0x1234, 0x0002), .driver_info =
DEVTYPE_EGALAX
USB 设备的VID =
0x1234, PID
= 0x0002,
driver_info
的值是DEVTYPE_EGALAX,根据其值在usbtouch_dev_info中查找driver
的相关设置。
static struct usbtouch_device_info usbtouch_dev_info[] =
{
#ifdef
CONFIG_TOUCHSCREEN_USB_EGALAX
[DEVTYPE_EGALAX] = {
.min_xc
= 0x0,
.max_xc
= 0x07ff,
.min_yc
= 0x0,
.max_yc
= 0x07ff,
.rept_size = 16,
.process_pkt
= usbtouch_process_multi,
.get_pkt_len
= egalax_get_pkt_len,
.read_data
= egalax_read_data,
},
#endif
}
二、probe部分
static int usbtouch_probe(struct usb_interface *
intf, const struct usb_device_id * id)
{
struct usbtouch_usb
*usbtouch;
struct
input_dev *input_dev;
struct
usb_host_interface *interface;
struct
usb_endpoint_descriptor *endpoint;
struct
usb_device *udev =
interface_to_usbdev(intf);
struct
usbtouch_device_info *type;
int
err = -ENOMEM;
// usb
设备有一个configuration
的概念,表示配置,一个设备可以有多个配置,但只能同时激活一个,如:一些设备可以下载固件,或可以设置不同的全局模式,cur_altsetting
就是表示的当前的这个setting,或者说设置。可以查看原码中usb_interface
结构定义的说明部分。从说明中可以看到一个接口可以有多种setting*/
if (id->driver_info ==
DEVTYPE_IGNORE)
return
-ENODEV;
interface
= intf->cur_altsetting;
//端点0描述符,此处的0表示中断端点
endpoint =
&interface->endpoint[0].desc;//端点0描述符,此处的0表示中断端点
usbtouch =
kzalloc(sizeof(struct usbtouch_usb),
GFP_KERNEL);
input_dev
= input_allocate_device();
if
(!usbtouch || !input_dev)
goto
out_free;
//为usbtouch分配内存空间,申请input
设备*/
type =
&usbtouch_dev_info[id->driver_info];
usbtouch->type
= type;
if
(!type->process_pkt)
type->process_pkt = usbtouch_process_pkt;
usbtouch->data = usb_buffer_alloc(udev,
type->rept_size,GFP_KERNEL,
&usbtouch->data_dma);
if
(!usbtouch->data)
goto out_free;
if
(type->get_pkt_len) {
usbtouch->buffer
= kmalloc(type->rept_size,
GFP_KERNEL);
if
(!usbtouch->buffer)
goto
out_free_buffers;
}
//申请用于urb用于数据传输的内存,注意:这里将返回“usbtouch->data”
//usbtouch->data:记录了用于普通传输用的内存指针
//usbtouch->buffer:记录了用于存储读取到的数据的内存指针
usbtouch->irq =
usb_alloc_urb(0,
GFP_KERNEL);
if
(!usbtouch->irq) {
dbg("%s
- usb_alloc_urb failed: usbtouch->irq",
__func__);
goto
out_free_buffers;
}
usbtouch->usbdev =
udev;
usbtouch->input =
input_dev;
//struct
usb_device 中有一个成员struct
usb_device_descriptor,而struct
usb_device_descriptor 中的成员__u16
bcdDevice,表示的是制造商指定的产品的版本号,制造商id
和产品id 来标志一个设备.bcdDevice
一共16 位,是以bcd码的方式保存的信息,也就是说,每4
位代表一个十进制的数,比如0011 0110 1001
0111 就代表的3697.业内为每家公司编一个号,这样便于管理,比如三星的编号就是0x0839,那么三星的产品中就会在其设备描述符中idVendor
的烙上0x0839.而三星自己的每种产品也会有个编号,和Digimax
410 对应的编号就是0x000a,而bcdDevice_lo
和bcdDevice_hi
共同组成一个具体设备的编号(device
release number),bcd
就意味着这个编号是二进制的格式.*/
if
(udev->manufacturer)
strlcpy(usbtouch->name,
udev->manufacturer,
sizeof(usbtouch->name));
if
(udev->product) {
if
(udev->manufacturer)
strlcat(usbtouch->name,
" ",
sizeof(usbtouch->name));
strlcat(usbtouch->name,
udev->product,
sizeof(usbtouch->name));
}
if
(!strlen(usbtouch->name))
snprintf(usbtouch->name,
sizeof(usbtouch->name),
"USB
Touchscreen x:x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
usb_make_path(udev,
usbtouch->phys,
sizeof(usbtouch->phys));
strlcat(usbtouch->phys,
"/input0",
sizeof(usbtouch->phys));
input_dev->name
= usbtouch->name;
input_dev->phys
= usbtouch->phys;
usb_to_input_id(udev,
&input_dev->id);
input_dev->dev.parent
=
&intf->dev;
input_set_drvdata(input_dev,
usbtouch);
//设置打开和关闭设备*/
input_dev->open = usbtouch_open;
input_dev->close
= usbtouch_close;
input_dev->evbit[0]
= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)]
= BIT_MASK(BTN_TOUCH);
// 设置支持的输入子系统事件:botton,key,press*/
input_set_abs_params(input_dev,
ABS_X, type->min_xc, type->max_xc, 0,
0);
input_set_abs_params(input_dev,
ABS_Y, type->min_yc, type->max_yc, 0,
0);
if
(type->max_press)
input_set_abs_params(input_dev,
ABS_PRESSURE,
type->min_press,type->max_press, 0,
0);
//构建好一个urb,在open方法中会实现向usb
core递交urb,
usbtouch_irq 回调函数。这里是两个DMA
相关的flag,一个是URB_NO_SETUP_DMA_MAP,而另一个是URB_NO_TRANSFER_DMA_MAP.注意这两个是不一样的,前一个是专门为控制传输准备的,因为只有控制传输需要有这么一个setup
阶段需要准备一个setup
packet。transfer_buffer
是给各种传输方式中真正用来数据传输的,而setup_packet
仅仅是在控制传输中发送setup
的包,控制传输除了setup 阶段之外,也会有数据传输阶段,这一阶段要传输数据还是得靠transfer_buffer,而如果使用dma
方式,那么就是使用transfer_dma.因为这里使用了mouse->irq->transfer_flags
|=
URB_NO_TRANSFER_DMA_MAP,所以应该给urb的transfer_dma赋值。所以用了:usbtouch->irq->transfer_dma
=
usbtouch->data_dma;*/
usb_fill_int_urb(usbtouch->irq,
usbtouch->udev,
usb_rcvintpipe(usbtouch->udev,
endpoint->bEndpointAddress),
usbtouch->data,
type->rept_size,
usbtouch_irq,
usbtouch,
endpoint->bInterval);
usbtouch->irq->dev
= usbtouch->udev;
usbtouch->irq->transfer_dma
= usbtouch->data_dma;
usbtouch->irq->transfer_flags
|= URB_NO_TRANSFER_DMA_MAP;
// device
specific init */
if
(type->init) {
err
= type->init(usbtouch);
if
(err) {
dbg("%s
- type->init() failed, err: %d", __func__,
err);
goto
out_free_buffers;
}
}
//向input系统注册input设备
err = input_register_device(usbtouch->input);
if (err) {
dbg("%s - input_register_device failed, err: %d", __func__,
err);
goto out_free_buffers;
}
usb_set_intfdata(intf,
usbtouch);
return 0;
三、 open部分
当应用层打开触摸屏设备时,usbtouch_open将被调用
static int usbtouch_open(struct input_dev
*input)
{
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
usbtouch->irq->dev =
usbtouch->udev;
//向usb
core递交了在probe中构建好的中断urb,注意:此处是成功递交给usb
core以后就返回,而不是等到从设备取得数据。
if (usb_submit_urb(usbtouch->irq,
GFP_KERNEL))
return -EIO;
return 0;
}
四、 urb回调函数处理部分
当出现传输错误或获取到触摸屏数据后,urb回调函数将被执行
static void usbtouch_irq(struct urb
*urb)
{
struct usbtouch_usb *usbtouch =
urb->context;
int retval;
switch (urb->status) {
case 0:
break;
case -ETIME:
dbg("%s - urb timed out - was the device
unplugged?",
__func__);
return;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
dbg("%s - urb shutting down with status:
%d",
__func__, urb->status);
return;
default:
dbg("%s - nonzero urb status received:
%d",
__func__, urb->status);
goto exit;
}
//向input系统报告坐标和按键事件
usbtouch->type->process_pkt(usbtouch,
usbtouch->data,
urb->actual_length);
exit:
retval = usb_submit_urb(urb,
GFP_ATOMIC); //重新发送URB
if (retval)
err("%s - usb_submit_urb failed with result:
%d",
__func__, retval);
}
static void usbtouch_process_pkt(struct usbtouch_usb
*usbtouch, unsigned char *pkt, int len)
{
struct usbtouch_device_info *type =
usbtouch->type;
if (!type->read_data(usbtouch, pkt))
return;
input_report_key(usbtouch->input,
BTN_TOUCH, usbtouch->touch);
if (swap_xy) {
input_report_abs(usbtouch->input,
ABS_X, usbtouch->y);
input_report_abs(usbtouch->input,
ABS_Y, usbtouch->x);
} else {
input_report_abs(usbtouch->input,
ABS_X, usbtouch->x);
input_report_abs(usbtouch->input,
ABS_Y, usbtouch->y);
}
if (type->max_press)
input_report_abs(usbtouch->input,
ABS_PRESSURE, usbtouch->press);
//最后需要向事件接受者发送一个完整的报告。这是input系统的要求。
input_sync(usbtouch->input);
}