Wednesday, April 22, 2009

Multi-Touch Demo on T91 with Intel fantastic Moblin

因為老闆的要求,所以要在新的moblin上面porting multi-touch的幾種Gesture。
由於是demo的緣故所以用ioctl pollling的方式,沒有實做新的input event並且用送event的方式實踐。下面是這個實做的diagram。

首先談談panel的構造,我們使用的是傳統的四線式單點touch panel,使用自己做的硬體電路模擬實際的多點情形。Controller裡FW送上來的data是長這樣:

g_MouseReport[0] = 1;
g_MouseReport[1] = (g_uZ1 & 0xFF);
g_MouseReport[2] = (g_uZ1 >> 8) & 0x0F;
g_MouseReport[3] = 0;

g_MouseReport[4] = (g_uX1 & 0xFF);
g_MouseReport[5] = (g_uX1 >> 8) & 0x0F;
g_MouseReport[6] = (g_uY1 & 0xFF);
g_MouseReport[7] = (g_uY1 >> 8) & 0x0F;

g_MouseReport[8] = 1;
g_MouseReport[9] = (g_uZ2 & 0xFF);
g_MouseReport[10] = (g_uZ2 >> 8) & 0x0F;
g_MouseReport[11] = 1;
g_MouseReport[12] = (g_uX2 & 0xFF);
g_MouseReport[13] = (g_uX2 >> 8) & 0x0F | 0x10;
g_MouseReport[14] = (g_uY2 & 0xFF);
g_MouseReport[15] = (g_uY2 >> 8) & 0x0F | 0x10;

會收到兩個report為一筆資料,一個report data有八個bytes,兩組report data可以合成一組七個bytes的資料,以利上面的演算法做計算。g_MouseReport陣列的單位是byte,第零個和第八個代表的是資料的結束或者是開始,如果是1代表還有資料在傳送,若是零則代表一筆gesture資料的結束。每筆report data的第四個byte也就是3和11所代表的意義是兩筆data的前後順序,如果是0代表第一筆,1代表第二筆,因此可以由這個值來區分前後。其他的bytes分別封裝了X1,Y1,Z1,X2,Y2,Z2。Z代表壓力的大小,這些座標值的精準度為12 bits,知道這個就可以看懂,上面的code為何要這麼寫。

洋洋灑灑的寫了一堆,重點是我們要如何拿到這些data?首先,要trace USB hub,在drivers/usb/core/hub.c裡面,看了以後你就知道,東西是如何從下面的UHCI, EHCI傳送資料上來,之後在hub_port_connect_change會幫一個新的device做註冊,使用usb_new_device這個function。再接著,你會看到一個device進來後會先mapping到一支generic_driver,這是每個usb device的必經之路,這支driver,會對usb device做descriptor的詢問動作,然後得到device的基本資料,再拿這些資訊去對所有usb bus上面的interface driver做mapping。這就是一個usb device進來以後是如何能在系統裡正常運作的過程。

很可愛的是,我們這邊的Device宣告為hid device,但,傳送上來的資料卻和report descriptor裡面宣告的資料是不符合的,我們自己傳送兩筆8 bytes的report descriptor資料,裡面也宣告為x軸或者是y軸,但是都是需要上面所提到的算法,做重新的組合,因此,我們的HID deviced不能經過HID protocol,不然parse出來的資料會亂七八糟。就是為了要解決這個問題,而且時間又不太充裕的情況下,我想了兩個方法,第一個是去看makefile,想辦法在hid driver被註冊前,先註冊我們的Driver,同時也用Vendor ID and Device ID做比對的項目,這樣,我們的Driver就會提早被device認到,也就不會走到HID protocol這個死胡同裡。但是這樣的缺點是要build in,而且在開發的過程極其麻煩,每次改code要確定能不能work就要整個kernel重build,然後再重開機,實在是太耗時間了。第二個方法,我在usb bus的match函式裡頭,加入了判斷的式子:


int usb_match_one_id(...) {
...
if(dev=>descriptor.idVendor == 0x0486 && dev->descriptor.idProduct == 0x0180)
intf->desc.bInterfaceClass = 0;
...
}


這判斷式的用意就是要把我們device的HID標籤拿掉,讓他不會進入HID protocol。但,缺點是,USB protocol被我更動了,這樣,open source community那群人應該不會饒了我吧!其實,好像也沒那麼嚴重,我只是要demo而已,所以最後就這樣做下去了。測試看看,果不期然,這樣我的touch panel就不會被HID給抓走。而在我driver還沒掛上的時候自己埋的printk,dmesg顯示沒有被mapping的現象。

現在HID這邊已經解決了,接下來就是要撰寫自己的USB driver。
恩,我就在Driver資料夾底下的input和usb/input裡面晃呀晃,後來看到drivers/usb/input底下找到了一支Vojtech Pavlik大師(你如果常常看code可以發現input底下的code很多都出自於他的手)寫的usbmouse.c驅動,他裡面就是為了支援boot device的,而不用為了要支援mouse和keyboard而大費周章的把HID protocol掛上。所以自己寫了隻code去parse boot device。正好,大師的作品可以派上用場。所以我就把他短短兩百多行的Code trace一下,不久寫出自己的第一隻usb driver。也是我的第一隻driver。

首先看看我的Makefile:
他是長這樣

obj-m += asusmt.o
KDIR=/home/bboy/linux-2.6.28
PWD=$(shell pwd)

KVERSION = $(shell uname -r)

all:
#cp ../linux-source-2.6.21.4-eeepc/drivers/usb/input/asusmt.c ./
make -C $(KDIR) M=$(PWD) clean
make -C $(KDIR) M=$(PWD) modules
#下面先移除asusmt舊的module
sudo rmmod asusmt
#這邊在插入asusmt.ko新編譯好的module
sudo insmod asusmt.ko

clean:
make -C $(KDIR) M=$(PWD) clean

install:
make -C $(KDIR) M=$(PWD) modules_install

妳可以把Makefile寫成這樣KDIR是你放kernel source code的地方,如果Driver安裝在不同的kernel上,則需要根據不同的kernel source重新編譯。

接著我貼上USB driver端所有的程式碼:
trace這段code可以從init開始看,insmod後就會執行init,rmmod的時候會執行exit。device拔除後會執行disconnect,還有重要的是probe,當driver和device mapping成功後,第一個執行的function就是probe,裡面包含了/dev/asus_mt的創建,還有urb的初始化,和urb的submit,都在這裡面。最後就是irq那個urb的callback function。當有data進來的時候就會從irq那隻把urb丟進來。

/******************************************************************************
* 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/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
//上面這四行後來把他們comment掉其實也沒事,不知道那邊會用到,我也是直接從
//Vojtech Pavlik的code copy過來的。
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <asm/uaccess.h>

//這邊就照著他們的標準寫一些基本的information。
#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);

//asus_mt是用在註冊node時所使用的字串
#define ASUS_MT_STRING "asus_mt"
//點進來的時候我是用100個大小的queue去存他
#define MAXQUEUE 100
//兩個8個bytes的data,最後會parse成7個bytes的data往上傳。
#define DATANUM 7
//parse好的資料暫時存放在axies_data array
u32 axies_data[DATANUM];
//存放動態get到的major number。
static int asus_mt_major;
//註冊class時要用到的point。
static struct class *asus_mt_class;
//queue的head和tail,head用來新增data到queue,tail用來拿出data。
static int head = 0, tail = 0;
//ready用來判斷是否queue內已有資料進來。
static int ready = 0;
//當我們實作等待資料的架構時,要先宣告一個wait。
wait_queue_head_t wait;
//這個就是我們存放資料的queue
typedef struct DataQueue {
u32 axies_data[DATANUM];
}DataQueue;

DataQueue queue[MAXQUEUE];
//DECLARE_MUTEX(cosem);
/*
axies_data[0] = button
axies_data[1] = Z1
axies_data[2] = X1
axies_data[3] = Y1
axies_data[4] = Z2
axies_data[5] = X2
axies_data[6] = Y2
*/

struct AsTouch_usb {
unsigned char *data;
dma_addr_t data_dma;
struct urb *irq;
struct usb_device *udev;
struct input_dev *input;
char name[128];
char phys[64];
};

//這個struct存放的是要比較的項目,其中flag代表的是要比較的項目,
//flag下面是要比較的實際內容,其中USB_DEVICE_ID_MATCH_DEVICE代表要比較idVendor與
//idProduct,而實際的內容存放在下面。之所以除了vendor id和device id外還要比較
//SUBCLASS_BOOT和MOUSE是因為目前這個device上面有兩種interface,一個是mouse另一個
//是keyboard,mouse是模擬成我們的touch panel,keyboard用不到,但為了防止keyboard
//亂送資料,所以多加幾個mapping的項目,使之只能mapping到mouse。這樣driver就不會接
//收到keyboard送出來的report descriptor。
static struct usb_device_id AsusTsUsb_devices[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
USB_DEVICE_ID_MATCH_INT_PROTOCOL,
.idVendor = 0x0486,
.idProduct = 0x0180,
.bInterfaceSubClass = USB_INTERFACE_SUBCLASS_BOOT,
.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE
},
{ }
};
//insert_queue用來新增資料到queue
static int insert_queue()
{
if ( (head + 1) == tail ) {
printk("DataQueue is full, some data is losed\n");
/* Push tail to 1 step forward, prevent tail from reading new coming data */
//tail++;
return -1;
}else {
head = (head + 1) % MAXQUEUE;
int i = 0;
for( ; i < DATANUM; i++ ){
queue[head].axies_data[i] = axies_data[i];
}
printk("insert queue[%d].axies_data %04x %04x %04x %04x %04x %04x %04x\n", head,
queue[head].axies_data[0],queue[head].axies_data[1],queue[head].axies_data[2],
queue[head].axies_data[3],queue[head].axies_data[4],queue[head].axies_data[5],
queue[head].axies_data[6]);
}
return 0;
}
//用來從queue copy資料到user space
static int delete_queue(unsigned long arg)
{
if(head == tail) {
goto empty;
} else {
tail = (tail + 1) % MAXQUEUE;

printk("delete queue[%d].axies_data %04x %04x %04x %04x %04x %04x %04x\n", tail,
queue[tail].axies_data[0],queue[tail].axies_data[1],queue[tail].axies_data[2],
queue[tail].axies_data[3],queue[tail].axies_data[4],queue[tail].axies_data[5],
queue[tail].axies_data[6]);
if(copy_to_user((unsigned int *)arg, queue[tail].axies_data, sizeof(u32)*7 )) {
printk("error copy_to_user\n");
return -EFAULT;
}
return 0;
}

empty:
printk("New data have not been coming yet..\n");
return -1;
}
//ioctl function可以從user space接收cmd,並且依據command做必要的行為,
//arg是從user space傳下來的引數,可以傳一個整數下來,或者是傳一個指標下來。
static int asus_mt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
ready = 0;
//wait_event_interruptible(wait, ready);
printk("asus_mt...ioctl\n");
//printk("cmd = %d\n", cmd);
//cli();
//if(down_interruptible(&cosem))
// return -ERESTARTSYS;
switch(cmd){

case 0:
printk("delete queue\n");
delete_queue(arg);
break;
case 1:
printk("query if queue has data\n");
if(head == tail)
*(unsigned int*)arg = (unsigned int)0;
else
*(unsigned int*)arg = (unsigned int)1;
break;
//default:
// return -EFAULTr
}
//up(&cosem);
//sti();
printk("reading success asus_mt_ioctl\n");
return 0;
}
//open在這邊沒有實做
static int asus_mt_open(struct inode *inode, struct file *file)
{
/*struct AsTouch_usb *dev;

dev = container_of(inode->i_cdev, struct AsTouch_usb, cdev);

file->private_data = dev;
*/
return 0;
}

//release也是沒有實做
static int asus_mt_release(struct inode *inode, struct file *file)
{}
//我們如果要實做檔案的operation就是實做在這個struct裡
static struct file_operations asus_mt_fops = {
.owner = THIS_MODULE,
.ioctl = asus_mt_ioctl,
.open = asus_mt_open,
.release = asus_mt_release,
};
//當我們實做urb時,就要把下面這個irq call back函式填入urb的complete function欄位,
//接著呼叫usb_submit_urb後,若是有report data進來,就會透過這個function把urb丟進來,
//資料就在下面AsTouch->data這個欄位。
static void AsusTsUsb_irq(struct urb *urb)
{
printk("%s: that's ok\n", __FUNCTION__ );
struct AsTouch_usb *AsTouch = urb->context;
int retval;
unsigned char* data = AsTouch->data;
//下面這邊做的就是一些很routine的判斷。
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;
}

/*if( !(data[3] | 0x00) ) {
if(down_interruptible(&cosem))
return -ERESTARTSYS;
}*/
//這邊要把資料還原
if( !(data[3] | 0x00) ) {
//printk("data[3] == 0x00\n");
axies_data[0] = data[0];
/* Initialize Z1 */
axies_data[1] = ( data[2] << 8 ) | data[1];
/* Initialize X1 */
axies_data[2] = ( data[5] << 8 ) | data[4];
/* Initialize Y1 */
axies_data[3] = ( data[7] << 8 ) | data[6];
}
else {
//printk("data[3] == 0x01\n");
/* Initialize Z2*/
axies_data[4] = ( data[2] << 8 ) | data[1];
/* Initialize X2 */
axies_data[5] = ( data[5] << 8 ) & 0x0fff | data[4];
/* Initialize Y2 */
axies_data[6] = ( data[7] << 8 ) & 0x0fff | data[6];

//printk("axies_data = On=%04x z1=%04x x1=%04x y1=%04x z2=%04x x2=%04x y2=%04x \n", axies_data[0],\
axies_data[1],axies_data[2],axies_data[3],\
axies_data[4],axies_data[5],axies_data[6]);


}
if( data[3] | 0x00 ) {
// if(down_interruptible(&cosem))
// return -ERESTARTSYS;
insert_queue();
ready = ready + 1;
//wake_up_interruptible(&wait);
// up(&cosem);
//int rep = 0;
/*
for( ; rep < 7; rep++ ) {
axies_data[rep] &= 0x00000000;
}
*/
}
//printk("%s:urb->actual_length = %d\n", __FUNCTION__, urb->actual_length );
resubmit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
err("%s - usb_submit_urb failed with result: %d",
__FUNCTION__, retval);
}
//open這個function其實也用不到,你看裡面的code就知道,
//原本預設上是等user space來open我們的device才會submit urb,
//但,這邊為了debug方便,在probe的時候就submit urb了。
//所以代表,只要device和driver兩個有mapping起來就會submit urb,
//預設上是不能這樣做,會造成系統資源的浪費,irq一直進來。
//一直處理一些用不到的資料。
static int AsusTsUsb_open(struct input_dev *input)
{
printk("%s: that's ok\n", __FUNCTION__ );
/*
struct AsTouch_usb *AsTouch = input->private;

AsTouch->irq->dev = AsTouch->udev;

if (usb_submit_urb(AsTouch->irq, GFP_ATOMIC))
return -EIO;

return 0;
*/
}

static void AsusTsUsb_close(struct input_dev *input)
{
//struct AsTouch_usb *AsTouch = input->private;

//usb_kill_urb(AsTouch->irq);
}

static int AsusTsUsb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
//probe就是重頭戲了,雖然下面都是每個usb interface driver要做的routine工作。
int ret = 0;
struct device *temp_class;
printk("%s: that's ok\n", __FUNCTION__ );
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct AsTouch_usb *astouch;
int pipe, maxp;
//init_waitqueue_head(&wait);
//register_chrdev做的就是向系統申請一個major number並且命名為ASUS_MT_STRING,
//同時把asus_mt_fops送進去,這樣我們的ioctl就可以被user space存取了。
//但記得這邊還不會在/dev底下出現asus_mt,要經過class_create和device_create後
//node才會跑出來。對這邊如果有問題可以參考ldd ch3的那個char driver。
asus_mt_major = register_chrdev(0, ASUS_MT_STRING,
&asus_mt_fops);

if( asus_mt_major < 0 )
{
printk("Unable to get a major for asus multi-touch driver!\n");
return asus_mt_major;
}

asus_mt_class = class_create(THIS_MODULE, ASUS_MT_STRING);

if (IS_ERR(asus_mt_class)) {
printk(KERN_ERR "Error creating Asus Multi-Touch class.\n");
ret = PTR_ERR(asus_mt_class);
goto err_out1;
}

temp_class = device_create(asus_mt_class, NULL,
MKDEV(asus_mt_major, 0),
NULL, ASUS_MT_STRING);

if (IS_ERR(temp_class)) {
printk(KERN_ERR "Error creating Asus Multi-Touch class device.\n");
ret = PTR_ERR(temp_class);
goto err_out2;
}
interface = intf->cur_altsetting;

if (interface->desc.bNumEndpoints != 1)
return -ENODEV;

endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;

pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

astouch = kzalloc(sizeof(struct AsTouch_usb), GFP_KERNEL);
astouch->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &astouch->data_dma);
if (!astouch->data)
goto fail;

astouch->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!astouch->irq)
goto fail;

astouch->udev = dev;

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

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

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

usb_make_path(dev, astouch->phys, sizeof(astouch->phys));
strlcat(astouch->phys, "/input0", sizeof(astouch->phys));
//看到了吧,這邊的AsusTsUsb_irq就是那個要被填到complete function的callback function
usb_fill_int_urb(astouch->irq, dev, pipe, astouch->data,
(maxp > 8 ? 8 : maxp),
AsusTsUsb_irq, astouch, endpoint->bInterval);
astouch->irq->transfer_dma = astouch->data_dma;
astouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_set_intfdata(intf, astouch);
//在這之前就把urb的資料根據傳進來的interface作好,然後接下來把urb submit出去,
//就可以等著接收report data。
astouch->irq->dev = astouch->udev;
if (usb_submit_urb(astouch->irq, GFP_ATOMIC))
return -EIO;

return 0;

err_out2:
class_destroy(asus_mt_class);
err_out1:
unregister_chrdev(asus_mt_major, ASUS_MT_STRING);
return ret;

fail: usb_buffer_free(dev, 8, astouch->data, astouch->data_dma);
kfree(astouch);
return -ENOMEM;

}
//這邊的disconnect裡面做的不外呼把/dev底下的asus_mt節點給摧毀掉,還有把urb給kill掉。
//但何時會執行disconnect?? 原來是在usb device設備被拔除的時候,你可以試試看把usb
//touch panel的線拔除,此時/dev/asus_mt也會跟著不見。也就不能在對/dev/asus_mt做
//read, write了。
static void AsusTsUsb_disconnect(struct usb_interface *intf)
{
struct AsTouch_usb *AsTouch = usb_get_intfdata(intf);
printk("%s\n", __FUNCTION__ );
device_destroy(asus_mt_class, MKDEV(asus_mt_major, 0));
class_destroy(asus_mt_class);
unregister_chrdev(asus_mt_major, ASUS_MT_STRING);
usb_set_intfdata(intf, NULL);
if (AsTouch) {
usb_kill_urb(AsTouch->irq);
//input_unregister_device(AsTouch->input);
usb_free_urb(AsTouch->irq);
usb_buffer_free(interface_to_usbdev(intf), 8,
AsTouch->data, AsTouch->data_dma);
kfree(AsTouch);
}
}

MODULE_DEVICE_TABLE(usb, AsusTsUsb_devices);

static struct usb_driver AsusTsUsb_driver = {
.name = "AsusTsUsb",
.probe = AsusTsUsb_probe,
.disconnect = AsusTsUsb_disconnect,
.id_table = AsusTsUsb_devices,
};
//一開始driver被insert時就從這邊run起。
static int __init AsusTsUsb_init(void)
{
printk("%s - called", __FUNCTION__);
return usb_register(&AsusTsUsb_driver);
}
//rmmod時就會從這邊run完結束,所以剛剛在disconnect裡的node銷毀,不能放在這邊run,
//要不然如果拔掉usb touch panel則/dev/asus_mt還會存在,若是user space程式對
//他做ioctl問題就大了。
static void __exit AsusTsUsb_exit(void)
{
printk("%s - called", __FUNCTION__);
usb_deregister(&AsusTsUsb_driver);
}

module_init(AsusTsUsb_init);
module_exit(AsusTsUsb_exit);


恩,接著看ap層要怎麼寫,這是從clutter ap擷取出來的一段code。

g_signal_connect (timeline, "new-frame", G_CALLBACK (GestureRecognition), actor);

這裡面的GestureRecognition就是clutter的callback function,他可以設定callback的timer,時間長短。這裏為什麼要放在call back裡面做我們的讀取function呢?如果你在gui的其他地方做自己的while loop去讀取ioctl,會造成gui掛掉不會更新的情形,因為gui本身也是一組while loop,你不能在gui的讀取while loop結束後,或者是還沒結束前就進入你的while loop,這樣都會造成gui沒辦法更新的現象,因為程式都一直在你的wile loop裡邊run。所以要把while loop內的東西,放到timer的call back函式裡頭做。而原本ioctl裡邊用的wait_event_interruptible也不能用,因為,他讀不到資料就會去睡覺,這樣你的gui要等到有資料進來才會更新。沒資料進來,若是切到別的視窗再回來,就會畫面一片空白,因為沒有資料來醒她。

所以這邊ioctl cmd=1就代表詢問有無資料,如果head==tail則代表queue為空,所以return 0;GestureRecognition收到後就會結束,若是head!=tail則回傳1,while loop接著下cmd=0一直讀,直到queue data被讀完為止。所以signal call back的速度可以不用設的太快,因為這邊是low speed device,如果這邊是high speed device,就要設快點,要不然不久queue就滿了,資料就truncate掉。當然queue的大小也會決定資料的準確率,但是需要越完整的資料,就需要更大的queue。

嗯嗯,接下來看GestureRecognition是怎麼寫的:

void GestureRecognition(ClutterTimeline *timeline,
gint frame_num,
gpointer data)
{

int fd = open("/dev/asus_mt", O_RDONLY);
printf("Enter GestureRecognition\n");

CHiddenWindow PacketProcess;

static unsigned int array[7];
if( fd == -1 )
{
printf("read file error\n");
}

int i;
for( i = 0 ; i < 7 ; i++ )
{
array[i] = 0;
}

unsigned int check = 0;
//確定queue有data在開始抓
while((ioctl(fd,1,&check) != -1) && check == 1) {
//開始把data從queue裡一個個讀進array
if (ioctl(fd,0,array) == -1) {
printf("read ioctl array error\n");
}else {
//下面這邊就是去做gesture的判斷,裡面用了一堆統計的方法
//這邊就不列出來了
PacketProcess.Indicate(array);
PacketProcess.MonitorThread();

}
}
close(fd);
}

記得在用open file和ioctl之前要先include下面的.h:
#include <fcntl.h>
#include <sys/ioctl.h>

下面再給一張完整的結構圖:

2 comments:

Tim Wu said...

Hi! 您好:
正在尋找Linux Multi-touch 可實作的相關資料. 看起來你這邊的進展不錯.

想請問一下. 您說的傳統四線 single touch-screen是指? T91 硬體似乎已有內建 multi-touch.

正在考慮要不要買台T91來作實驗.

LevelOne said...

Dear Sir,
這個 code 原創好像不是你,是否可以寫實際創作人?