先給出multi-point一張完整的架構圖
完整的source code如下:
/******************************************************************************
* AsusTsUsb.c -- Driver for Multi-Touch USB Touchscreens
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Gavin Guo, mimi0213kimo@gmail.com"
#define DRIVER_DESC "Asus USB Multi-Touch Touch Panel driver"
#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
#define USB_VENDOR_ID_ASUS 0x0486
#define USB_DEVICE_ID_ASUS 0x0185
#define VAIO_RDESC_CONSTANT 0x0001
static int swap_xy;
module_param(swap_xy, bool, 0644);
MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
struct asus_mt_usb {
unsigned char *data;
struct input_dev *input1, *input2;
struct asmt_device_info *type;
struct usb_device *udev;
dma_addr_t data_dma;
struct urb *urb;
char name[128];
char phys0[64];
char phys1[64];
int x1, y1;
int x2, y2;
int touch1, touch2, press;
int p2;
};
struct asmt_device_info {
int min_xc, max_xc;
int min_yc, max_yc;
int min_press, max_press;
int rept_size;
void (*process_pkt) (struct asus_mt_usb *asmt, unsigned char *pkt, int len);
/*
* used to get the packet len. possible return values:
* > 0: packet len
* = 0: skip one byte
* < 0: -return value more bytes needed
*/
int (*get_pkt_len) (unsigned char *pkt, int len);
int (*read_data) (struct asus_mt_usb *asmt, unsigned char *pkt);
int (*init) (struct asus_mt_usb *asmt);
};
static int asus_read_data(struct asus_mt_usb *dev, unsigned char *pkt)
{
/*
if (!(pkt[0] & 0x80) ((pkt[1] pkt[2] pkt[3]) & 0x80))
return 0;
dev->x = ((pkt[0] & 0x1F) << 7) (pkt[2] & 0x7F);
dev->y = ((pkt[1] & 0x1F) << 7) (pkt[3] & 0x7F);
dev->touch = pkt[0] & 0x20;
*/
dev->x1 = ((pkt[4] & 0x0F) << 8) (pkt[3] & 0xFF);
dev->y1 = ((pkt[6] & 0x0F) << 8) (pkt[5] & 0xFF);
dev->touch1 = pkt[1] & 0x03;
dev->p2 = pkt[13] & 0x02;
if(dev->p2) {
dev->x2 = ((pkt[10] & 0x0F) << 8) (pkt[9] & 0xFF);
dev->y2 = ((pkt[12] & 0x0F) << 8) (pkt[11] & 0xFF);
dev->touch2 = pkt[7] & 0x03;
}
return 1;
}
static struct asmt_device_info type = {
.min_xc = 0x0,
.max_xc = 0x0fff,
.min_yc = 0x0,
.max_yc = 0x0fff,
.rept_size = 8,
.read_data = asus_read_data,
};
static void usbtouch_process_pkt(struct asus_mt_usb *asmt,
unsigned char *pkt, int len)
{
struct asmt_device_info *type = asmt->type;
if (!type->read_data(asmt, pkt))
return;
input_report_key(asmt->input1, BTN_TOUCH, asmt->touch1);
input_report_key(asmt->input2, BTN_TOUCH, asmt->touch2);
if (swap_xy) {
input_report_abs(asmt->input1, ABS_X, asmt->y1);
input_report_abs(asmt->input1, ABS_Y, asmt->x1);
input_report_abs(asmt->input2, ABS_X, asmt->y2);
input_report_abs(asmt->input2, ABS_Y, asmt->x2);
} else {
input_report_abs(asmt->input1, ABS_X, asmt->x1);
input_report_abs(asmt->input1, ABS_Y, asmt->y1);
if(asmt->p2) {
input_report_abs(asmt->input2, ABS_X, asmt->x2);
input_report_abs(asmt->input2, ABS_Y, asmt->y2);
}
}
/*
* if (type->max_press)
*
* input_report_abs input_report_abs(asmt->input, ABS_PRESSURE, asmt->press);
*/
input_sync(asmt->input1);
if(asmt->p2)
input_sync(asmt->input2);
}
#define USB_REQ_SET_REPORT 0x09
static int usb_set_report_feature(struct usb_interface *intf, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(interface_to_usbdev(intf),
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
USB_REQ_SET_REPORT,
USB_TYPE_CLASS USB_RECIP_INTERFACE,
(type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf,
size, HZ);
}
static void asus_mt_irq(struct urb *urb)
{
//printk("%s: that's ok\n", __FUNCTION__ );
struct asus_mt_usb *asmt = urb->context;
//struct usbhid_device *usbhid = hid->driver_data;
int retval;
unsigned char* data = asmt->data;
switch (urb->status) {
case 0:
// success
break;
case -ETIME:
// this urb is timing out
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
return;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
return;
default:
goto resubmit;
}
printk("data = %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", data[0],\
data[1],data[2],data[3],\
data[4],data[5],data[6],data[7],data[8],data[9],data[10],data[11],data[12],data[13]);
//printk("%s:urb->actual_length = %d\n", __FUNCTION__, urb->actual_length );
asmt->type->process_pkt(asmt, asmt->data, urb->actual_length);
resubmit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
err("%s - usb_submit_urb failed with result: %d",
__FUNCTION__, retval);
}
static int asus_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
printk("%s\n", __FUNCTION__);
struct usb_host_interface *interface = intf->cur_altsetting;
struct usb_device *dev = interface_to_usbdev(intf);
struct input_dev *input_dev1, *input_dev2;
int n = 0, insize = 14;
struct asus_mt_usb *asmt = kzalloc(sizeof(struct asus_mt_usb), GFP_KERNEL);
asmt->type = &type;
asmt->udev = dev;
if (dev->manufacturer)
strlcpy(asmt->name, dev->manufacturer, sizeof(asmt->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(asmt->name, " ", sizeof(asmt->name));
strlcat(asmt->name, dev->product, sizeof(asmt->name));
}
if (!strlen(asmt->name))
snprintf(asmt->name, sizeof(asmt->name),
"USB Touchscreen %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, asmt->phys0, sizeof(asmt->phys0));
strlcat(asmt->phys0, "/input0", sizeof(asmt->phys0));
usb_make_path(dev, asmt->phys1, sizeof(asmt->phys1));
strlcat(asmt->phys1, "/input1", sizeof(asmt->phys1));
if (!asmt->type->process_pkt) {
printk("process_pkt is null\n");
asmt->type->process_pkt = usbtouch_process_pkt;
}
usb_set_intfdata(intf, asmt);
input_dev1 = input_allocate_device();
input_dev2 = input_allocate_device();
input_dev1->name = asmt->name;
input_dev2->name = asmt->name;
usb_to_input_id(dev, &input_dev1->id);
usb_to_input_id(dev, &input_dev2->id);
asmt->input1 = input_dev1;
asmt->input2 = input_dev2;
if(!asmt !input_dev1 !input_dev2) {
printk("Memory is not enough\n");
goto fail1;
}
input_dev1->dev.parent = &intf->dev;
input_set_drvdata(input_dev1, asmt);
input_dev1->evbit[0] = BIT_MASK(EV_KEY) BIT_MASK(EV_ABS);
input_dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input_dev1, ABS_X, asmt->type->min_xc, asmt->type->max_xc, 0, 0);
input_set_abs_params(input_dev1, ABS_Y, asmt->type->min_yc, asmt->type->max_yc, 0, 0);
input_dev2->dev.parent = &intf->dev;
input_set_drvdata(input_dev2, asmt);
input_dev2->evbit[0] = BIT_MASK(EV_KEY) BIT_MASK(EV_ABS);
input_dev2->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input_dev2, ABS_X, asmt->type->min_xc, asmt->type->max_xc, 0, 0);
input_set_abs_params(input_dev2, ABS_Y, asmt->type->min_yc, asmt->type->max_yc, 0, 0);
asmt->data = usb_buffer_alloc(dev, insize, GFP_KERNEL,
&asmt->data_dma);
if(!asmt->data) {
printk("asmt->data allocating fail");
goto fail;
}
for (n = 0; n < interface->desc.bNumEndpoints; n++) {
struct usb_endpoint_descriptor *endpoint;
int pipe;
int interval;
endpoint = &interface->endpoint[n].desc;
if (!usb_endpoint_xfer_int(endpoint))
continue;
interval = endpoint->bInterval;
if (usb_endpoint_dir_in(endpoint)) {
if (asmt->urb)
continue;
if (!(asmt->urb = usb_alloc_urb(0, GFP_KERNEL)))
goto fail;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
//insize = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
usb_fill_int_urb(asmt->urb, dev, pipe, asmt->data,
insize, asus_mt_irq, asmt, interval);
asmt->urb->transfer_dma = asmt->data_dma;
asmt->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
}
}
char *buf = kmalloc(8, GFP_KERNEL); /* 8 bytes are enough for both products */
buf[0] = 0x07;
buf[1] = 0x02;
if(usb_set_report_feature(intf, 2, 7, buf, 8))
printk("set report true\n");
else
printk("set report false\n");
if (usb_submit_urb(asmt->urb, GFP_ATOMIC)) {
printk("usb submit urb error\n");
return -EIO;
}
input_register_device(asmt->input1);
input_register_device(asmt->input2);
return 0;
fail:
usb_free_urb(asmt->urb);
asmt->urb = NULL;
usb_buffer_free(dev, insize, asmt->data, asmt->data_dma);
fail1:
input_free_device(input_dev1);
input_free_device(input_dev2);
kfree(asmt);
return 1;
}
static void asus_disconnect(struct usb_interface *intf)
{
struct asus_mt_usb *asmt = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
if (asmt) {
input_unregister_device(asmt->input2);
input_unregister_device(asmt->input1);
usb_kill_urb(asmt->urb);
//input_unregister_device(asmt->input);
usb_free_urb(asmt->urb);
usb_buffer_free(interface_to_usbdev(intf), 14,
asmt->data, asmt->data_dma);
kfree(asmt);
}
}
static const struct usb_device_id asus_devices[] = {
{ USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS) },
{ }
};
MODULE_DEVICE_TABLE(usb, asus_devices);
static struct usb_driver asus_driver = {
.name = "asus",
.probe = asus_probe,
.disconnect = asus_disconnect,
.id_table = asus_devices,
};
static int asus_init(void)
{
printk("asus_init\n");
return usb_register(&asus_driver);
}
static void asus_exit(void)
{
printk("asus_exit\n");
usb_deregister(&asus_driver);
}
module_init(asus_init);
module_exit(asus_exit);
先敘述一下流程,點從firmware丟上來,首先經過UHCI再來hub driver。
經過hub後會判斷是否為hid device若是則會經過hid parser,因為我們的device為hid device,所已預設一定會經過hid parser。經由hid parser去parse也就算了,問題是此時的hid parser還沒有成熟到可以parse這個multi-touch hid的digitizer封包。也因為這樣,所以usb kernel裡頭有一個ignore list,可以把device的vendor id & device id放到這個list內。這樣在mapping的時候,就不會map到hid parser driver。
很有趣的是,當map到hid parser,還會在做一次mapping,這次就是mapping到hid bus。hid bus上面也有一個blacklist。你要加到他的blacklist才不會map到他預設的driver。
嗯嗯,等到match到我們的driver,會先進入probe,然後create兩個input device並且掛上call back function。這兩個input device會match到evdev handler。
要支援multi-point,在firmware方面是用set report的方式把multi-point的功能打開,不然預設他只會送出一個點。為了要找怎麼下command給touch panel controller,trace了一下FW的source code發現應該是用set report的方式;所以我便到了kernel的根目錄底下做搜尋grep -r "set" ./drivers/usb/ grep report。
發現了這個.c檔可以做參考./drivers/usb/misc/iowarrior.c。進去裡面看了以後發覺還真的是不錯。
#define GET_TIMEOUT 5
#define USB_REQ_GET_REPORT 0x01
//#if 0
static int usb_get_report(struct usb_device *dev,
struct usb_host_interface *inter, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_REPORT,
USB_DIR_IN USB_TYPE_CLASS
USB_RECIP_INTERFACE, (type << 8) + id,
inter->desc.bInterfaceNumber, buf, size,
GET_TIMEOUT*HZ);
}
//#endif
#define USB_REQ_SET_REPORT 0x09
static int usb_set_report(struct usb_interface *intf, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(interface_to_usbdev(intf),
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
USB_REQ_SET_REPORT,
USB_TYPE_CLASS USB_RECIP_INTERFACE,
(type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf,
size, HZ);
}
我可以用到usb_set_report這個function。後來測了一下type和id有沒有填沒有差,因為FW不會去檢查這兩個欄位。所以code可以像下面這樣子寫:
char *buf = kmalloc(8, GFP_KERNEL);
buf[0] = 7;
buf[1] = 2;
usb_set_report(intf, 0, 0, buf, 8);
其實這支driver沒有甚麼比較難的地方,大概就是這樣。其他的code自己看一看就知道是為什麼了。
只是剛開始要無中生有,需要去trace相關的source code找出我們需要的部分,比較難。
真的要掛上這支driver要在X window起來以前,所以要到/etc/inittab裡頭,把run level改成3。
開機就會先進入command mode,然後再sudo insmod hid-asusmt.ko。如此,就可以趕在X window起來之前把driver上起來。
然後還要記得的是加入xorg.conf到/etc/X11/xorg.conf,如果你不知道xorg.conf裡頭的/dev/input/evdevxx。不知道要設多少,可以去cat /proc/bus/input/devices查詢,也可以順便看看,driver有沒有上好這個devices。若是driver沒有上,這邊就沒有input devices的資訊。
Section "ServerLayout"
Identifier "X.org Configured"
Screen 0 "Screen0" 0 0
InputDevice "Mouse0" "CorePointer"
InputDevice "Keyboard0" "CoreKeyboard"
InputDevice "Test0"
InputDevice "Test1"
EndSection
Section "Files"
ModulePath "/usr/lib/xorg/modules"
FontPath "catalogue:/etc/X11/fontpath.d"
FontPath "built-ins"
EndSection
Section "Module"
Load "extmod"
Load "dbe"
Load "glx"
Load "dri2"
Load "dri"
EndSection
Section "InputDevice"
Identifier "Keyboard0"
Driver "kbd"
EndSection
Section "InputDevice"
Identifier "Mouse0"
Driver "mouse"
Option "Protocol" "auto"
Option "Device" "/dev/input/mice"
Option "ZAxisMapping" "4 5 6 7"
EndSection
Section "InputDevice"
Identifier "Test0"
Driver "evdev"
Option "Device" "/dev/input/event8"
EndSection
Section "InputDevice"
Identifier "Test1"
Driver "evdev"
Option "Device" "/dev/input/event9"
EndSection
Section "Monitor"
Identifier "Monitor0"
VendorName "Monitor Vendor"
ModelName "Monitor Model"
EndSection
Section "Device"
### Available Driver options are:-
### Values: <i>: integer, <f>: float, <bool>: "True"/"False",
### <string>: "String", <freq>: "<f> Hz/kHz/MHz"
### [arg]: arg optional
#Option "NoAccel" # [<bool>]
#Option "SWcursor" # [<bool>]
#Option "ColorKey" # <i>
#Option "CacheLines" # <i>
#Option "Dac6Bit" # [<bool>]
#Option "DRI" # [<bool>]
#Option "NoDDC" # [<bool>]
#Option "ShowCache" # [<bool>]
#Option "XvMCSurfaces" # <i>
#Option "PageFlip" # [<bool>]
Identifier "Card0"
Driver "intel"
VendorName "Intel Corporation"
BoardName "Mobile 945GME Express Integrated Graphics Controller"
BusID "PCI:0:2:0"
EndSection
Section "Screen"
Identifier "Screen0"
Device "Card0"
Monitor "Monitor0"
SubSection "Display"
Viewport 0 0
Depth 1
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 4
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 8
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 15
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 16
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 24
EndSubSection
EndSection
執行/usr/bin/startx進入X window。
若是還沒有安裝MPX得先遵循下面的步驟:
This is readme for MPX enabling on Moblin.
1 Install the latest Moblin image
2 update the RPM packages in the attached .zip file by “rpm –Uvh xxxx.rpm”
3 reboot
4 plug the second USB mouse
5 execute “xinput list”, then you could find the device id of newly plugged second USB mouse. In my system it’s “HID 413c:3010”
6 execute “xinput –create-master “mouse2””
7 execute “xinput –reattach “HID 413c:3010” “mouse2 pointer””
Then you would find 2 pointers on the screen, which means MPX works J
在我們的兩個node裡面第二個node的名稱叫做Test1,所以必須要把xinput -reattach之後的“HID 413c:3010”改成"Test1",這樣就能夠使用兩個點了。如果要移除第二個點則xinput -remove-master "gavin pointer"。即可。
11 comments:
Hello,在網路上搜尋資訊時看到你這篇文章,使我獲益良多,感謝。另外我想請問一下,你文章中的第一個ignore list是指hid-core.c裡的list還是指usb/core/quirks.c呢?
你好,請問AsusTsUsb.c放在任何資料夾下都可以嗎,我在資料夾下放置了makefile和AsusTsUsb.c不過make時,出現"錯誤:在非結構或聯合中請求成員[parent]"以及錯誤:在非結構或聯合中請求成員[kobj]以及
錯誤:隱含宣告函數[input_set_drvdata]
,我實在不知道這是該如何解決,是我的環境還是什麼錯誤嗎?
我的linux/hid.h沒有hid.h這個檔案,我是上網找一個的,不知道這樣有影響嗎?
如果可以可以提供嗎?謝謝
I would like to exchange links with your site www.blogger.com
Is this possible?
I would like to exchange links with your site bboytaiwan.blogspot.com
Is this possible?
The benefits of making use of resistance bands for your personal P90X
work out are many.
Also visit my web page; free weights for sale
Are you seeking to construct that Bowflex Overall body?
Stop by my page simply click the following website page
Nevertheless, I do propose having some kind of bench to complete
this on.
Review my website; http://www.getfitnstrong.com/bowflex-dumbbells/bowflex-selecttech-dumbbells-ultimate-home-exercising/
Bowflex TreadClimber will offer you the right services of you have
an understanding of anything you needs to be accomplishing.
Review my web page adjustable weights
The require for balance through absolutely free pounds physical
exercises is essentially a drawback, considering that the skill of balancing an object
or maybe the entire body requires a certain amount of target which detracts from
one's capacity to concentrate on contraction.
my blog: adjustable dumbbell set
The weights are extra or eliminated instantly.
Also visit my site :: adjustable dumbbell set
In case you should retail store them less than you bed, not a problem.
Review my web site mouse click the following website page
Post a Comment