diff --git a/README b/README index 82de8c2..7e32dcd 100644 --- a/README +++ b/README @@ -1,17 +1,21 @@ Hanvon tablet driver ==================== -Linux kernel driver for some Hanvon tablet usb models. Following functionality -is covered. pen coordinates, touch/float/click detection, pressure, x and y -tilt, pen button. On Artmaster I four simple tablet buttons (note that the first +Driver for Linux kernels which supports complete functionality of the tablet: +pen coordinates, touch/float/click detection, pressure, x and y tilt, pen +button. On Artmaster I four simple tablet buttons (note that the first one works only together with pen activity), and the slider button. Supported hardware ================== -Artmaster I: AM0605, AM0806, AM1107, AM1209 -Rollick: RL0604 +AM3M, AM0806, AM0605, AM1107, AM1209 +RL0604, RL0504 +GP0806, GP0605, GP0504 +NXS1315 + +AM - Artmaster I, RL - Rollick, GP - GraphicPal, NXS - Nilox Installation @@ -25,14 +29,10 @@ insmod ./hanvon.ko If everything goes right the tablet should start working immediately. -Revision history -================ +Diagnostics +=========== -0.0.1 - initial release - 0.2 - corrected pressure detection, working slider button - 0.3 - remaining buttons also working, added x and y tilting - 0.3b - patch for AM1209 from Markus Zucker applied - 0.3c - patch for AM1107 from Daniel Koch applied - 0.3d - support for right side buttons of AM1107 and AM1209 - 0.4 - code cleanup, RL0604 patch from Daniel Clemmer - 0.5 - code cleanup, AM0605 support +After insmod, check with dmesg, if the module was loaded properly. +"USB Hanvon tablet driver" should appear in the listing. + +lsmod should also contain hanvon in its listing: lsmod | grep hanvon diff --git a/hanvon.c b/hanvon.c index cc081d9..a33df90 100644 --- a/hanvon.c +++ b/hanvon.c @@ -3,7 +3,9 @@ #include #include #include +#include +#define DRIVER_VERSION "0.5" #define DRIVER_AUTHOR "Ondra Havel " #define DRIVER_DESC "USB Hanvon tablet driver" #define DRIVER_LICENSE "GPL" @@ -13,19 +15,24 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE(DRIVER_LICENSE); #define USB_VENDOR_ID_HANVON 0x0b57 -#define USB_AM_PACKET_LEN 10 - -#define USB_PRODUCT_ID_AM0605 0x8503 +#define USB_PRODUCT_ID_AM3M 0x8528 #define USB_PRODUCT_ID_AM0806 0x8502 +#define USB_PRODUCT_ID_AM0605 0x8503 #define USB_PRODUCT_ID_AM1107 0x8505 #define USB_PRODUCT_ID_AM1209 0x8501 #define USB_PRODUCT_ID_RL0604 0x851f -#define USB_PRODUCT_ID_GP0605A 0x803a +#define USB_PRODUCT_ID_RL0504 0x851d +#define USB_PRODUCT_ID_GP0806 0x8039 +#define USB_PRODUCT_ID_GP0806B 0x8511 +#define USB_PRODUCT_ID_GP0605 0x8512 +#define USB_PRODUCT_ID_GP0605A 0x803a +#define USB_PRODUCT_ID_GP0504 0x8037 +#define USB_PRODUCT_ID_NXS1513 0x8030 -/* reported on all AMs */ -static int lbuttons[] = {BTN_0, BTN_1, BTN_2, BTN_3}; -/* reported on AM1107+ */ -static int rbuttons[] = {BTN_4, BTN_5, BTN_6, BTN_7}; +#define USB_AM_PACKET_LEN 10 + +static int lbuttons[]={BTN_0,BTN_1,BTN_2,BTN_3}; /* reported on all AMs */ +static int rbuttons[]={BTN_4,BTN_5,BTN_6,BTN_7}; /* reported on AM1107+ */ #define AM_WHEEL_THRESHOLD 4 @@ -45,20 +52,18 @@ struct hanvon { char phys[32]; }; -static void report_buttons(struct hanvon *hanvon, - int buttons[], unsigned char dta) +static void report_buttons(struct hanvon *hanvon, int buttons[],unsigned char dta) { struct input_dev *dev = hanvon->dev; - if ((dta & 0xf0) == 0xa0) { + if((dta & 0xf0) == 0xa0) { input_report_key(dev, buttons[1], dta & 0x02); input_report_key(dev, buttons[2], dta & 0x04); input_report_key(dev, buttons[3], dta & 0x08); } else { - if (dta <= 0x3f) { /* slider area active */ + if(dta <= 0x3f) { /* slider area active */ int diff = dta - hanvon->old_wheel_pos; - /* detect new/continue old move */ - if (abs(diff) < AM_WHEEL_THRESHOLD) + if(abs(diff) < AM_WHEEL_THRESHOLD) input_report_rel(dev, REL_WHEEL, diff); hanvon->old_wheel_pos = dta; @@ -71,64 +76,69 @@ static void hanvon_irq(struct urb *urb) struct hanvon *hanvon = urb->context; unsigned char *data = hanvon->data; struct input_dev *dev = hanvon->dev; - int ret; + int retval; switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - return; - default: - goto exit; + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + printk("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + printk("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; } - switch (data[0]) { - case 0x01: /* button press */ - if (data[1] == 0x55) /* left side */ - report_buttons(hanvon, lbuttons, data[2]); + switch(data[0]) { + case 0x01: /* button press */ + if(data[1]==0x55) /* left side */ + report_buttons(hanvon,lbuttons,data[2]); - if (data[3] == 0xaa) /* right side (am1107, am1209) */ - report_buttons(hanvon, rbuttons, data[4]); + if(data[3]==0xaa) /* right side (am1107, am1209) */ + report_buttons(hanvon,rbuttons,data[4]); + break; + + case 0x02: /* position change */ + if((data[1] & 0xf0) != 0) { + input_report_abs(dev, ABS_X, get_unaligned_be16(&data[2])); + input_report_abs(dev, ABS_Y, get_unaligned_be16(&data[4])); + input_report_abs(dev, ABS_TILT_X, data[7] & 0x3f); + input_report_abs(dev, ABS_TILT_Y, data[8]); + input_report_abs(dev, ABS_PRESSURE, get_unaligned_be16(&data[6])>>6); + } + + input_report_key(dev, BTN_LEFT, data[1] & 0x1); + input_report_key(dev, BTN_RIGHT, data[1] & 0x2); /* stylus button pressed (right click) */ + input_report_key(dev, lbuttons[0], data[1] & 0x20); break; - case 0x02: /* position change */ - if ((data[1] & 0xf0) != 0) { - input_report_abs(dev, ABS_X, - be16_to_cpup((__be16 *)&data[2])); - input_report_abs(dev, ABS_Y, - be16_to_cpup((__be16 *)&data[4])); - input_report_abs(dev, ABS_TILT_X, data[7] & 0x3f); - input_report_abs(dev, ABS_TILT_Y, data[8]); - input_report_abs(dev, ABS_PRESSURE, - be16_to_cpup((__be16 *)&data[6])>>6); - } - - input_report_key(dev, BTN_LEFT, data[1] & 0x1); - /* stylus button pressed (right click) */ - input_report_key(dev, BTN_RIGHT, data[1] & 0x2); - input_report_key(dev, lbuttons[0], data[1] & 0x20); - break; } input_sync(dev); exit: - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - err("%s - usb_submit_urb failed with result %d", - __func__, ret); + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + printk("%s - usb_submit_urb failed with result %d", __func__, retval); } static struct usb_device_id hanvon_ids[] = { + { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_AM3M) }, { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_AM1209) }, { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_AM1107) }, { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_AM0806) }, { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_AM0605) }, { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_RL0604) }, + { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_RL0504) }, + { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_GP0806) }, + { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_GP0806B) }, + { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_GP0605) }, { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_GP0605A) }, + { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_GP0504) }, + { USB_DEVICE(USB_VENDOR_ID_HANVON, USB_PRODUCT_ID_NXS1513) }, { } }; @@ -153,8 +163,7 @@ static void hanvon_close(struct input_dev *dev) usb_kill_urb(hanvon->irq); } -static int hanvon_probe(struct usb_interface *intf, - const struct usb_device_id *id) +static int hanvon_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); struct usb_endpoint_descriptor *endpoint; @@ -167,8 +176,7 @@ static int hanvon_probe(struct usb_interface *intf, if (!hanvon || !input_dev) goto fail1; - hanvon->data = usb_alloc_coherent(dev, - USB_AM_PACKET_LEN, GFP_KERNEL, &hanvon->data_dma); + hanvon->data = (unsigned char *)usb_alloc_coherent(dev, USB_AM_PACKET_LEN, GFP_KERNEL, &hanvon->data_dma); if (!hanvon->data) goto fail1; @@ -192,26 +200,19 @@ static int hanvon_probe(struct usb_interface *intf, input_dev->open = hanvon_open; input_dev->close = hanvon_close; - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_REL, input_dev->evbit); - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOUCH, input_dev->keybit); - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - for (i = 0; i < sizeof(lbuttons) / sizeof(lbuttons[0]); i++) + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_REL); + input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH); + input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); + for(i=0;ikeybit); - for (i = 0; i < sizeof(rbuttons) / sizeof(rbuttons[0]); i++) + for(i=0;ikeybit); input_set_abs_params(input_dev, ABS_X, 0, AM_MAX_ABS_X, 4, 0); input_set_abs_params(input_dev, ABS_Y, 0, AM_MAX_ABS_Y, 4, 0); - input_set_abs_params(input_dev, ABS_TILT_X, - 0, AM_MAX_TILT_X, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_Y, - 0, AM_MAX_TILT_Y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, AM_MAX_PRESSURE, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_X, 0, AM_MAX_TILT_X, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, 0, AM_MAX_TILT_Y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, AM_MAX_PRESSURE, 0, 0); input_set_capability(input_dev, EV_REL, REL_WHEEL); endpoint = &intf->cur_altsetting->endpoint[0].desc; @@ -223,17 +224,15 @@ static int hanvon_probe(struct usb_interface *intf, hanvon->irq->transfer_dma = hanvon->data_dma; hanvon->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - usb_set_intfdata(intf, hanvon); - error = input_register_device(hanvon->dev); if (error) goto fail3; + usb_set_intfdata(intf, hanvon); return 0; fail3: usb_free_urb(hanvon->irq); -fail2: usb_free_coherent(dev, USB_AM_PACKET_LEN, - hanvon->data, hanvon->data_dma); +fail2: usb_free_coherent(dev, USB_AM_PACKET_LEN, hanvon->data, hanvon->data_dma); fail1: input_free_device(input_dev); kfree(hanvon); return error; @@ -243,12 +242,14 @@ static void hanvon_disconnect(struct usb_interface *intf) { struct hanvon *hanvon = usb_get_intfdata(intf); - input_unregister_device(hanvon->dev); - usb_free_urb(hanvon->irq); - usb_free_coherent(interface_to_usbdev(intf), - USB_AM_PACKET_LEN, hanvon->data, hanvon->data_dma); - kfree(hanvon); usb_set_intfdata(intf, NULL); + if (hanvon) { + usb_kill_urb(hanvon->irq); + input_unregister_device(hanvon->dev); + usb_free_urb(hanvon->irq); + usb_free_coherent(interface_to_usbdev(intf), USB_AM_PACKET_LEN, hanvon->data, hanvon->data_dma); + kfree(hanvon); + } } static struct usb_driver hanvon_driver = { @@ -258,4 +259,22 @@ static struct usb_driver hanvon_driver = { .id_table = hanvon_ids, }; -module_usb_driver(hanvon_driver); +static int __init hanvon_init(void) +{ + int rv; + + if((rv = usb_register(&hanvon_driver)) != 0) + return rv; + + printk(DRIVER_DESC " " DRIVER_VERSION "\n"); + + return 0; +} + +static void __exit hanvon_exit(void) +{ + usb_deregister(&hanvon_driver); +} + +module_init(hanvon_init); +module_exit(hanvon_exit);