diff --git a/airtouch.c b/airtouch.c index 848e69a..4924b09 100644 --- a/airtouch.c +++ b/airtouch.c @@ -98,14 +98,17 @@ static struct usb_device_id airtouch_table [] = { }; MODULE_DEVICE_TABLE (usb, airtouch_table); +#define MAX_FINGERS 12 /* Structure to hold all of our device specific stuff */ struct airtouch { char phys[64]; struct usb_device *udev; /* usb device */ struct urb *urb; /* usb request block */ + struct urb *button_urb; /* usb request block for button */ signed char *data; /* transferred data */ - struct input_dev *input; /* input dev */ + signed char *button_data; /* transferred data */ + struct input_dev *input[MAX_FINGERS]; /* input dev */ unsigned char open; /* non-zero if opened */ unsigned char valid; /* are the sensors valid ? */ unsigned char size_detect_done; @@ -142,15 +145,167 @@ static inline int airtouch_is_wellspring2(struct airtouch *dev) (productId == WELLSPRING2_JIS_PRODUCT_ID); } +static int airtouch_wellspring_init(struct usb_device *udev) +{ + char data[8]; + int size; + + size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_GET_DESCRIPTOR, + USB_DIR_IN, 0x0303, 0x0409, data, 4, + 100); + + printk("Got %d\n", size); + + size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_GET_INTERFACE, USB_DIR_OUT | + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 0, + NULL, 0, 100); + + memset(data, 0, sizeof(data)); + data[0] = 0x01; + data[1] = 0x05; + size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_CONFIGURATION, USB_DIR_OUT | + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x0300, + 0, data, 8, 100); + + printk("Got %d\n", size); + + return 0; +} + +static int airtouch_open(struct input_dev *input) +{ + struct airtouch *dev = input_get_drvdata(input); + if (dev->open == 0) + if (usb_submit_urb(dev->urb, GFP_ATOMIC)) + return -EIO; + dev->open++; + return 0; +} + +static void airtouch_close(struct input_dev *input) +{ + struct airtouch *dev = input_get_drvdata(input); + + dev->open--; + + if (dev->open == 0) + usb_kill_urb(dev->urb); +} + +/* When a URB has completed, this function is run */ +static void airtouch_button_complete(struct urb *urb) +{ + int retval; + struct airtouch *dev = urb->context; + + switch (urb->status) { + case 0: + /* success */ + break; + case -EOVERFLOW: + if(!dev->overflowwarn) { + printk(KERN_WARNING "airtouch: Overflow of data with length" + " %d, actual length is %d\n", dev->datalen, dev->urb->actual_length); + dev->overflowwarn = 1; + } + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* This urb is terminated, clean up */ + debug_msg("airtouch: urb shutting down with status: %d\n", urb->status); + return; + default: + debug_msg("airtouch: nonzero urb status received: %d\n", urb->status); + goto exit; + } + + input_report_key(dev->input[0], BTN_LEFT, dev->button_data[1]); +exit: + retval = usb_submit_urb(dev->button_urb, GFP_ATOMIC); + if (retval) { + err("airtouch: usb_submit_urb failed with result %d", retval); + } +} + +static void airtouch_complete(struct urb* urb) +{ + int retval; + struct airtouch *dev = urb->context; + int numfingers, x, y; + int i; + + switch (urb->status) { + case 0: + /* success */ + break; + case -EOVERFLOW: + if(!dev->overflowwarn) { + printk(KERN_WARNING "airtouch: Overflow of data with length" + " %d, actual length is %d\n", dev->datalen, dev->urb->actual_length); + dev->overflowwarn = 1; + } + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* This urb is terminated, clean up */ + debug_msg("airtouch: urb shutting down with status: %d\n", urb->status); + return; + default: + debug_msg("airtouch: nonzero urb status received: %d\n", urb->status); + goto exit; + } + + /* Check that the data length is valid */ + if (dev->urb->actual_length < 26 || + (dev->urb->actual_length - 26) % 28 != 0) + goto exit; + + /* FIXME: Grab x, y coordinates of each finger, etc etc */ + + numfingers =( dev->urb->actual_length-26)/28; + + if (numfingers == 0) + goto exit; + + for (i=0; idata[26+28*i+3] << 8) + dev->data[26+28*i+2]; + y = (dev->data[26+28*i+5] << 8) + dev->data[26+28*i+4]; + input_report_key(dev->input[i], BTN_TOUCH, 1); + input_report_abs(dev->input[i], ABS_X, x); + input_report_abs(dev->input[i], ABS_Y, y); + input_report_abs(dev->input[i], ABS_PRESSURE, 20); + } + + for (i=numfingers; iinput[i], BTN_TOUCH, 0); + input_report_abs(dev->input[i], ABS_PRESSURE, 0); + } + + input_report_key(dev->input[0], BTN_TOOL_FINGER, numfingers == 1); + input_report_key(dev->input[0], BTN_TOOL_DOUBLETAP, numfingers == 2); + input_report_key(dev->input[0], BTN_TOOL_TRIPLETAP, numfingers > 2); + + exit: /* When something goes wrong */ + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) { + err("airtouch: usb_submit_urb failed with result %d", retval); + } +} + /* If a product in the ID table is driverless, this runs. */ static int airtouch_probe(struct usb_interface *iface, const struct usb_device_id *id) { struct airtouch *dev; - struct input_dev *input_dev; struct usb_device *udev = interface_to_usbdev(iface); struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - int int_in_endpointAddr = 0; + int int_in_endpointAddr = 1; + int int_button_endpointAddr = 2; int i, error = -ENOMEM; /* set up the endpoint information */ @@ -171,27 +326,104 @@ static int airtouch_probe(struct usb_interface *iface, const struct usb_device_i /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(struct airtouch), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!dev || !input_dev) { - err("Out of memory"); - goto err_free_devs; + if (!dev) + goto err_free_devs; + + for (i=0; iinput[i]= input_allocate_device(); + if (!dev->input[i]) + goto err_free_devs; } dev->udev = udev; - dev->input = input_dev; dev->overflowwarn = 0; + dev->datalen = 362; if (airtouch_is_wellspring(dev)) { debug_msg("airtouch: found a wellspring trackpad.\n"); } else if(airtouch_is_wellspring2(dev)) { debug_msg("airtouch: found a wellspring2 trackpad.\n"); } + /* FIXME: Initialize our device here */ - /* FIXME: Register with input here */ + if (airtouch_wellspring_init(udev)) + goto err_free_devs; + + dev->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb) + goto err_free_devs; + + dev->button_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb) + goto err_free_urb; + + dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL, + &dev->urb->transfer_dma); + + if (!dev->data) + goto err_free_button_urb; + + dev->button_data = usb_buffer_alloc(dev->udev, 2, GFP_KERNEL, + &dev->button_urb->transfer_dma); + + if (!dev->button_data) + goto err_free_button_urb; + + usb_fill_int_urb(dev->urb, udev, usb_rcvintpipe(udev, int_in_endpointAddr), + dev->data, dev->datalen, airtouch_complete, dev, 1); + + usb_fill_int_urb(dev->button_urb, udev, + usb_rcvintpipe(udev, int_button_endpointAddr), + dev->button_data, 2, airtouch_button_complete, dev, 1); + usb_set_intfdata(iface, dev); + + usb_make_path(udev, dev->phys, sizeof(dev->phys)); + + for (i=0; iinput[i]->name = "airtouch"; + dev->input[i]->phys = dev->phys; + dev->input[i]->dev.parent = &iface->dev; + usb_to_input_id(dev->udev, &dev->input[i]->id); + input_set_drvdata(dev->input[i], dev); + + dev->input[i]->open = airtouch_open; + dev->input[i]->close = airtouch_close; + + set_bit(EV_ABS, dev->input[i]->evbit); + input_set_abs_params(dev->input[i], ABS_X, -5000, 5000, 20, 0); + input_set_abs_params(dev->input[i], ABS_Y, -200, 4200, 20, 0); + input_set_abs_params(dev->input[i], ABS_PRESSURE, 0, 100, 0, 0); + set_bit(EV_KEY, dev->input[i]->evbit); + set_bit(BTN_TOUCH, dev->input[i]->keybit); + set_bit(BTN_LEFT, dev->input[i]->keybit); + set_bit(BTN_TOOL_FINGER, dev->input[i]->keybit); + set_bit(BTN_TOOL_DOUBLETAP, dev->input[i]->keybit); + set_bit(BTN_TOOL_TRIPLETAP, dev->input[i]->keybit); + error = input_register_device(dev->input[i]); + if (error) + goto err_free_button_urb; + } + + return 0; + + /* FIXME: Register with input here */ +err_free_button_urb: + usb_kill_urb(dev->button_urb); + usb_buffer_free(dev->udev, 2, + dev->button_data, dev->button_urb->transfer_dma); + usb_free_urb(dev->button_urb); +err_free_urb: + usb_kill_urb(dev->urb); + usb_buffer_free(dev->udev, dev->datalen, + dev->data, dev->urb->transfer_dma); + usb_free_urb(dev->urb); err_free_devs: + for (i=0; iinput[i]) + input_free_device(dev->input[i]); + usb_set_intfdata(iface, NULL); kfree(dev); - input_free_device(input_dev); return error; } @@ -203,6 +435,7 @@ static int airtouch_suspend(struct usb_interface *iface, pm_message_t message) debug_msg("airtouch: airtouchd_suspend called\n"); usb_kill_urb(dev->urb); + usb_kill_urb(dev->button_urb); dev->valid = 0; return 0; @@ -217,67 +450,28 @@ static int airtouch_resume(struct usb_interface *iface) debug_msg("airtouch: airtouchd_resume called\n"); if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC)) return -EIO; + if (dev->open && usb_submit_urb(dev->button_urb, GFP_ATOMIC)) + return -EIO; return 0; } -/* When a URB has completed, this function is run */ -static void airtouch_complete(struct urb* urb) -{ - int retval; - struct airtouch *dev = urb->context; - - debug_msg("airtouch: airtouchd_complete called\n"); - switch (urb->status) { - case 0: - /* success */ - break; - case -EOVERFLOW: - if(!dev->overflowwarn) { - printk(KERN_WARNING "airtouch: Overflow of data with length" - " %d, actual length is %d\n", dev->datalen, dev->urb->actual_length); - dev->overflowwarn = 1; - } - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* This urb is terminated, clean up */ - debug_msg("airtouch: urb shutting down with status: %d\n", urb->status); - return; - default: - debug_msg("airtouch: nonzero urb status received: %d\n", urb->status); - goto exit; - } - - /* drop incomplete datasets */ - if (dev->urb->actual_length != dev->datalen) { - debug_msg("airtouch: incomplete data package " - "(first byte: %d, length: %d).\n", dev->data[0], dev->urb->actual_length); - goto exit; - } - - /* FIXME: Grab x, y coordinates of each finger, etc etc */ - - exit: /* When something goes wrong */ - retval = usb_submit_urb(dev->urb, GFP_ATOMIC); - if (retval) { - err("airtouch: usb_submit_urb failed with result %d", retval); - } -} - - static void airtouch_disconnect(struct usb_interface *iface) { struct airtouch *dev = usb_get_intfdata(iface); + int i; usb_set_intfdata(iface, NULL); if (dev) { usb_kill_urb(dev->urb); - input_unregister_device(dev->input); + usb_kill_urb(dev->button_urb); + for (i=0; iinput[i]); usb_buffer_free(dev->udev, dev->datalen, dev->data, dev->urb->transfer_dma); usb_free_urb(dev->urb); + usb_free_urb(dev->button_urb); kfree(dev); } printk(KERN_INFO "input: airtouch disconnected\n");