今天尝试自动动手写usb设备驱动程序。本想自己写一个U盘的驱动程序,实现简单的挂载。但块设备读写还不是弄得很透彻,只能照着教程实现简单的usb鼠标驱动程序。
usb hid设备驱动程序,在系统里是自带的。需要去掉 CONFIG_USB_HIDX选项,但是CONFIG_HID_GENERIC需要选中,否则,注册usb驱动之后,不能识别HID设备,就跳转不到probe成员中。
首先,需要申明usb_device_id,系统就是根据这个ID号来匹配设备,进而调用驱动的。
static struct usb_device_id usb_mouse_pen_id_table [] = {
{USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE)},
//{USB_DEVICE(0x046d,0xc534)},
{}
};
这里可以用鼠标的接口描述符来声明,实际测试,如果你知道设备的PID,VID,用PID,VID的方式声明也是可以的,这样驱动就只适合特定的设备了。用lsusb查看,得到设备的PID,VID号为0x046d,0xc534。但我的usb receiver是连接鼠标和键盘的。这会导致两次进入.probe函数,需要分别处理键盘和鼠标设备。这里,简单起见,我就只识别了鼠标。
usb driver的声明
static struct usb_driver usb_pen_driver = {
.name = "usb_pen_driver",
.probe = usbpen_probe,
.disconnect = usbpen_disconnect,
.id_table = usb_mouse_pen_id_table,
};
init函数中,只需要usb_register(&usb_pen_driver);就可以了。
在probe中,申请,注册一个input_device
/*申请设置一个inputdevice*/
up_dev = input_allocate_device();
/*setting*/
set_bit(EV_SYN, up_dev->evbit);
set_bit(EV_ABS, up_dev->evbit);
set_bit(EV_KEY, up_dev->evbit);
set_bit(BTN_TOUCH, up_dev->keybit);
set_bit(ABS_X, up_dev->absbit);
set_bit(ABS_Y, up_dev->absbit);
set_bit(ABS_PRESSURE, up_dev->absbit);
set_bit(BTN_TOUCH, up_dev->keybit);
input_set_abs_params(up_dev, ABS_X, 0, 800, 0, 0);
input_set_abs_params(up_dev, ABS_Y, 0, 480, 0, 0);
input_set_abs_params(up_dev, ABS_PRESSURE, 0, 1, 0 , 0);
input_register_device(up_dev);
分配,设置,最后提交urb
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
len = endpoint->wMaxPacketSize;
usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);
up_urb = usb_alloc_urb(0,GFP_KERNEL);
usb_fill_int_urb(up_urb,dev,pipe,usb_buf,len,usb_pen_irq,NULL,endpoint->bInterval);
up_urb->transfer_dma = usb_buf_phys;
up_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
//up_urb->hcpriv = "just_as_a_flag";
usb_submit_urb(up_urb,GFP_KERNEL);
中断函数中,需要解析usb鼠标传过来的数据。第二个byte表示各个按键的标志位。第三个byte和第四个byte是x轴和y轴的相对位移。这里,鼠标的反应是很灵敏的,可以在这里除以一个参数。这里,我的设计是,监测到位移后,x,y的坐标值做相应改变。鼠标左键按下后,提交绝对位置。
static void usb_pen_irq(struct urb *urb)
{
//USB_PRINT("%s, urb[%x], up_urb[%x]\n",__FUNCTION__,(unsigned long)urb,(unsigned long)up_urb);
signed char *ms_data = usb_buf;
if(ms_data[1]&0x01)
{
// 左键按下
USB_PRINT("pressed x[%d], y[%d]\n",ms_data[1] ,ms_data[2]);
input_report_abs(up_dev, ABS_X, up_location.x);
input_report_abs(up_dev, ABS_Y, up_location.y);
input_report_abs(up_dev, ABS_PRESSURE, 1);
input_report_key(up_dev, BTN_TOUCH, 1);
input_sync(up_dev);
}else
{
// 左键松开
USB_PRINT("rel\n");
input_report_abs(up_dev, ABS_PRESSURE, 0);
input_report_key(up_dev, BTN_TOUCH, 0);
input_sync(up_dev);
}
if ((ms_data[3]!=0)||(ms_data[4]!=0))
{
if (ms_data[3]>0)
{
if (ms_data[3]+up_location.x<800)
{
up_location.x += ms_data[3];
}
else
{
up_location.x = 800;
}
}else
{
if (ms_data[3]+up_location.x < 0)
{
up_location.x = 0;
} else
{
up_location.x += ms_data[3];
}
}
if (ms_data[4]>0)
{
if (ms_data[4]+up_location.y<480)
{
up_location.y += ms_data[4]/4;
}
else
{
up_location.y = 480;
}
}else
{
if (ms_data[4]+up_location.y < 0)
{
up_location.y = 0;
} else
{
up_location.y += ms_data[4]/4;
}
}
//USB_PRINT("move x[%d], y[%d]\n",up_location.x ,up_location.y);
//USB_PRINT("move d0[%d], d1[%d],d2[%d],d3[%d],d4[%d],d5[%d]\n",ms_data[0],ms_data[1],ms_data[2],ms_data[3],ms_data[4],ms_data[5]);
}
usb_submit_urb(urb, GFP_KERNEL);
return;
}
编译,insmod之后,插上鼠标,就能在/dev/input下看到新增加的event*设备。
按照tslib的配置流程,设置TSLIB_TSDEVICE为新增加的event,运行ts_test就可以用鼠标当画笔。不过ts_calibrate校准的过程,需要根据触摸屏大小,计算几个校准点的位置,然后根据打印出来的数据来校准。以后碰到usb设备的驱动程序,也有章可循