Path: blob/master/drivers/input/tablet/wacom_sys.c
15111 views
/*1* drivers/input/tablet/wacom_sys.c2*3* USB Wacom tablet support - system specific code4*/56/*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2 of the License, or10* (at your option) any later version.11*/1213#include "wacom_wac.h"14#include "wacom.h"1516/* defines to get HID report descriptor */17#define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01)18#define HID_DEVICET_REPORT (USB_TYPE_CLASS | 0x02)19#define HID_USAGE_UNDEFINED 0x0020#define HID_USAGE_PAGE 0x0521#define HID_USAGE_PAGE_DIGITIZER 0x0d22#define HID_USAGE_PAGE_DESKTOP 0x0123#define HID_USAGE 0x0924#define HID_USAGE_X 0x3025#define HID_USAGE_Y 0x3126#define HID_USAGE_X_TILT 0x3d27#define HID_USAGE_Y_TILT 0x3e28#define HID_USAGE_FINGER 0x2229#define HID_USAGE_STYLUS 0x2030#define HID_COLLECTION 0xc03132enum {33WCM_UNDEFINED = 0,34WCM_DESKTOP,35WCM_DIGITIZER,36};3738struct hid_descriptor {39struct usb_descriptor_header header;40__le16 bcdHID;41u8 bCountryCode;42u8 bNumDescriptors;43u8 bDescriptorType;44__le16 wDescriptorLength;45} __attribute__ ((packed));4647/* defines to get/set USB message */48#define USB_REQ_GET_REPORT 0x0149#define USB_REQ_SET_REPORT 0x0950#define WAC_HID_FEATURE_REPORT 0x035152static int usb_get_report(struct usb_interface *intf, unsigned char type,53unsigned char id, void *buf, int size)54{55return usb_control_msg(interface_to_usbdev(intf),56usb_rcvctrlpipe(interface_to_usbdev(intf), 0),57USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,58(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,59buf, size, 100);60}6162static int usb_set_report(struct usb_interface *intf, unsigned char type,63unsigned char id, void *buf, int size)64{65return usb_control_msg(interface_to_usbdev(intf),66usb_sndctrlpipe(interface_to_usbdev(intf), 0),67USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,68(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,69buf, size, 1000);70}7172static void wacom_sys_irq(struct urb *urb)73{74struct wacom *wacom = urb->context;75int retval;7677switch (urb->status) {78case 0:79/* success */80break;81case -ECONNRESET:82case -ENOENT:83case -ESHUTDOWN:84/* this urb is terminated, clean up */85dbg("%s - urb shutting down with status: %d", __func__, urb->status);86return;87default:88dbg("%s - nonzero urb status received: %d", __func__, urb->status);89goto exit;90}9192wacom_wac_irq(&wacom->wacom_wac, urb->actual_length);9394exit:95usb_mark_last_busy(wacom->usbdev);96retval = usb_submit_urb(urb, GFP_ATOMIC);97if (retval)98err ("%s - usb_submit_urb failed with result %d",99__func__, retval);100}101102static int wacom_open(struct input_dev *dev)103{104struct wacom *wacom = input_get_drvdata(dev);105int retval = 0;106107if (usb_autopm_get_interface(wacom->intf) < 0)108return -EIO;109110mutex_lock(&wacom->lock);111112if (usb_submit_urb(wacom->irq, GFP_KERNEL)) {113retval = -EIO;114goto out;115}116117wacom->open = true;118wacom->intf->needs_remote_wakeup = 1;119120out:121mutex_unlock(&wacom->lock);122usb_autopm_put_interface(wacom->intf);123return retval;124}125126static void wacom_close(struct input_dev *dev)127{128struct wacom *wacom = input_get_drvdata(dev);129int autopm_error;130131autopm_error = usb_autopm_get_interface(wacom->intf);132133mutex_lock(&wacom->lock);134usb_kill_urb(wacom->irq);135wacom->open = false;136wacom->intf->needs_remote_wakeup = 0;137mutex_unlock(&wacom->lock);138139if (!autopm_error)140usb_autopm_put_interface(wacom->intf);141}142143static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,144struct wacom_features *features)145{146struct usb_device *dev = interface_to_usbdev(intf);147char limit = 0;148/* result has to be defined as int for some devices */149int result = 0;150int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0;151unsigned char *report;152153report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);154if (!report)155return -ENOMEM;156157/* retrive report descriptors */158do {159result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),160USB_REQ_GET_DESCRIPTOR,161USB_RECIP_INTERFACE | USB_DIR_IN,162HID_DEVICET_REPORT << 8,163intf->altsetting[0].desc.bInterfaceNumber, /* interface */164report,165hid_desc->wDescriptorLength,1665000); /* 5 secs */167} while (result < 0 && limit++ < 5);168169/* No need to parse the Descriptor. It isn't an error though */170if (result < 0)171goto out;172173for (i = 0; i < hid_desc->wDescriptorLength; i++) {174175switch (report[i]) {176case HID_USAGE_PAGE:177switch (report[i + 1]) {178case HID_USAGE_PAGE_DIGITIZER:179usage = WCM_DIGITIZER;180i++;181break;182183case HID_USAGE_PAGE_DESKTOP:184usage = WCM_DESKTOP;185i++;186break;187}188break;189190case HID_USAGE:191switch (report[i + 1]) {192case HID_USAGE_X:193if (usage == WCM_DESKTOP) {194if (finger) {195features->device_type = BTN_TOOL_FINGER;196if (features->type == TABLETPC2FG) {197/* need to reset back */198features->pktlen = WACOM_PKGLEN_TPC2FG;199features->device_type = BTN_TOOL_DOUBLETAP;200}201if (features->type == BAMBOO_PT) {202/* need to reset back */203features->pktlen = WACOM_PKGLEN_BBTOUCH;204features->device_type = BTN_TOOL_DOUBLETAP;205features->x_phy =206get_unaligned_le16(&report[i + 5]);207features->x_max =208get_unaligned_le16(&report[i + 8]);209i += 15;210} else {211features->x_max =212get_unaligned_le16(&report[i + 3]);213features->x_phy =214get_unaligned_le16(&report[i + 6]);215features->unit = report[i + 9];216features->unitExpo = report[i + 11];217i += 12;218}219} else if (pen) {220/* penabled only accepts exact bytes of data */221if (features->type == TABLETPC2FG)222features->pktlen = WACOM_PKGLEN_GRAPHIRE;223if (features->type == BAMBOO_PT)224features->pktlen = WACOM_PKGLEN_BBFUN;225features->device_type = BTN_TOOL_PEN;226features->x_max =227get_unaligned_le16(&report[i + 3]);228i += 4;229}230} else if (usage == WCM_DIGITIZER) {231/* max pressure isn't reported232features->pressure_max = (unsigned short)233(report[i+4] << 8 | report[i + 3]);234*/235features->pressure_max = 255;236i += 4;237}238break;239240case HID_USAGE_Y:241if (usage == WCM_DESKTOP) {242if (finger) {243features->device_type = BTN_TOOL_FINGER;244if (features->type == TABLETPC2FG) {245/* need to reset back */246features->pktlen = WACOM_PKGLEN_TPC2FG;247features->device_type = BTN_TOOL_DOUBLETAP;248features->y_max =249get_unaligned_le16(&report[i + 3]);250features->y_phy =251get_unaligned_le16(&report[i + 6]);252i += 7;253} else if (features->type == BAMBOO_PT) {254/* need to reset back */255features->pktlen = WACOM_PKGLEN_BBTOUCH;256features->device_type = BTN_TOOL_DOUBLETAP;257features->y_phy =258get_unaligned_le16(&report[i + 3]);259features->y_max =260get_unaligned_le16(&report[i + 6]);261i += 12;262} else {263features->y_max =264features->x_max;265features->y_phy =266get_unaligned_le16(&report[i + 3]);267i += 4;268}269} else if (pen) {270/* penabled only accepts exact bytes of data */271if (features->type == TABLETPC2FG)272features->pktlen = WACOM_PKGLEN_GRAPHIRE;273if (features->type == BAMBOO_PT)274features->pktlen = WACOM_PKGLEN_BBFUN;275features->device_type = BTN_TOOL_PEN;276features->y_max =277get_unaligned_le16(&report[i + 3]);278i += 4;279}280}281break;282283case HID_USAGE_FINGER:284finger = 1;285i++;286break;287288case HID_USAGE_STYLUS:289pen = 1;290i++;291break;292293case HID_USAGE_UNDEFINED:294if (usage == WCM_DESKTOP && finger) /* capacity */295features->pressure_max =296get_unaligned_le16(&report[i + 3]);297i += 4;298break;299}300break;301302case HID_COLLECTION:303/* reset UsagePage and Finger */304finger = usage = 0;305break;306}307}308309out:310result = 0;311kfree(report);312return result;313}314315static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features)316{317unsigned char *rep_data;318int limit = 0, report_id = 2;319int error = -ENOMEM;320321rep_data = kmalloc(2, GFP_KERNEL);322if (!rep_data)323return error;324325/* ask to report tablet data if it is 2FGT Tablet PC or326* not a Tablet PC */327if (features->type == TABLETPC2FG) {328do {329rep_data[0] = 3;330rep_data[1] = 4;331report_id = 3;332error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,333report_id, rep_data, 2);334if (error >= 0)335error = usb_get_report(intf,336WAC_HID_FEATURE_REPORT, report_id,337rep_data, 3);338} while ((error < 0 || rep_data[1] != 4) && limit++ < 5);339} else if (features->type != TABLETPC) {340do {341rep_data[0] = 2;342rep_data[1] = 2;343error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,344report_id, rep_data, 2);345if (error >= 0)346error = usb_get_report(intf,347WAC_HID_FEATURE_REPORT, report_id,348rep_data, 2);349} while ((error < 0 || rep_data[1] != 2) && limit++ < 5);350}351352kfree(rep_data);353354return error < 0 ? error : 0;355}356357static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,358struct wacom_features *features)359{360int error = 0;361struct usb_host_interface *interface = intf->cur_altsetting;362struct hid_descriptor *hid_desc;363364/* default features */365features->device_type = BTN_TOOL_PEN;366features->x_fuzz = 4;367features->y_fuzz = 4;368features->pressure_fuzz = 0;369features->distance_fuzz = 0;370371/* only Tablet PCs and Bamboo P&T need to retrieve the info */372if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&373(features->type != BAMBOO_PT))374goto out;375376if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {377if (usb_get_extra_descriptor(&interface->endpoint[0],378HID_DEVICET_REPORT, &hid_desc)) {379printk("wacom: can not retrieve extra class descriptor\n");380error = 1;381goto out;382}383}384error = wacom_parse_hid(intf, hid_desc, features);385if (error)386goto out;387388out:389return error;390}391392struct wacom_usbdev_data {393struct list_head list;394struct kref kref;395struct usb_device *dev;396struct wacom_shared shared;397};398399static LIST_HEAD(wacom_udev_list);400static DEFINE_MUTEX(wacom_udev_list_lock);401402static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev)403{404struct wacom_usbdev_data *data;405406list_for_each_entry(data, &wacom_udev_list, list) {407if (data->dev == dev) {408kref_get(&data->kref);409return data;410}411}412413return NULL;414}415416static int wacom_add_shared_data(struct wacom_wac *wacom,417struct usb_device *dev)418{419struct wacom_usbdev_data *data;420int retval = 0;421422mutex_lock(&wacom_udev_list_lock);423424data = wacom_get_usbdev_data(dev);425if (!data) {426data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL);427if (!data) {428retval = -ENOMEM;429goto out;430}431432kref_init(&data->kref);433data->dev = dev;434list_add_tail(&data->list, &wacom_udev_list);435}436437wacom->shared = &data->shared;438439out:440mutex_unlock(&wacom_udev_list_lock);441return retval;442}443444static void wacom_release_shared_data(struct kref *kref)445{446struct wacom_usbdev_data *data =447container_of(kref, struct wacom_usbdev_data, kref);448449mutex_lock(&wacom_udev_list_lock);450list_del(&data->list);451mutex_unlock(&wacom_udev_list_lock);452453kfree(data);454}455456static void wacom_remove_shared_data(struct wacom_wac *wacom)457{458struct wacom_usbdev_data *data;459460if (wacom->shared) {461data = container_of(wacom->shared, struct wacom_usbdev_data, shared);462kref_put(&data->kref, wacom_release_shared_data);463wacom->shared = NULL;464}465}466467static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)468{469struct usb_device *dev = interface_to_usbdev(intf);470struct usb_endpoint_descriptor *endpoint;471struct wacom *wacom;472struct wacom_wac *wacom_wac;473struct wacom_features *features;474struct input_dev *input_dev;475int error;476477if (!id->driver_info)478return -EINVAL;479480wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);481input_dev = input_allocate_device();482if (!wacom || !input_dev) {483error = -ENOMEM;484goto fail1;485}486487wacom_wac = &wacom->wacom_wac;488wacom_wac->features = *((struct wacom_features *)id->driver_info);489features = &wacom_wac->features;490if (features->pktlen > WACOM_PKGLEN_MAX) {491error = -EINVAL;492goto fail1;493}494495wacom_wac->data = usb_alloc_coherent(dev, WACOM_PKGLEN_MAX,496GFP_KERNEL, &wacom->data_dma);497if (!wacom_wac->data) {498error = -ENOMEM;499goto fail1;500}501502wacom->irq = usb_alloc_urb(0, GFP_KERNEL);503if (!wacom->irq) {504error = -ENOMEM;505goto fail2;506}507508wacom->usbdev = dev;509wacom->intf = intf;510mutex_init(&wacom->lock);511usb_make_path(dev, wacom->phys, sizeof(wacom->phys));512strlcat(wacom->phys, "/input0", sizeof(wacom->phys));513514wacom_wac->input = input_dev;515516endpoint = &intf->cur_altsetting->endpoint[0].desc;517518/* Retrieve the physical and logical size for OEM devices */519error = wacom_retrieve_hid_descriptor(intf, features);520if (error)521goto fail3;522523wacom_setup_device_quirks(features);524525strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));526527if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {528/* Append the device type to the name */529strlcat(wacom_wac->name,530features->device_type == BTN_TOOL_PEN ?531" Pen" : " Finger",532sizeof(wacom_wac->name));533534error = wacom_add_shared_data(wacom_wac, dev);535if (error)536goto fail3;537}538539input_dev->name = wacom_wac->name;540input_dev->dev.parent = &intf->dev;541input_dev->open = wacom_open;542input_dev->close = wacom_close;543usb_to_input_id(dev, &input_dev->id);544input_set_drvdata(input_dev, wacom);545546wacom_setup_input_capabilities(input_dev, wacom_wac);547548usb_fill_int_urb(wacom->irq, dev,549usb_rcvintpipe(dev, endpoint->bEndpointAddress),550wacom_wac->data, features->pktlen,551wacom_sys_irq, wacom, endpoint->bInterval);552wacom->irq->transfer_dma = wacom->data_dma;553wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;554555error = input_register_device(input_dev);556if (error)557goto fail4;558559/* Note that if query fails it is not a hard failure */560wacom_query_tablet_data(intf, features);561562usb_set_intfdata(intf, wacom);563return 0;564565fail4: wacom_remove_shared_data(wacom_wac);566fail3: usb_free_urb(wacom->irq);567fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);568fail1: input_free_device(input_dev);569kfree(wacom);570return error;571}572573static void wacom_disconnect(struct usb_interface *intf)574{575struct wacom *wacom = usb_get_intfdata(intf);576577usb_set_intfdata(intf, NULL);578579usb_kill_urb(wacom->irq);580input_unregister_device(wacom->wacom_wac.input);581usb_free_urb(wacom->irq);582usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,583wacom->wacom_wac.data, wacom->data_dma);584wacom_remove_shared_data(&wacom->wacom_wac);585kfree(wacom);586}587588static int wacom_suspend(struct usb_interface *intf, pm_message_t message)589{590struct wacom *wacom = usb_get_intfdata(intf);591592mutex_lock(&wacom->lock);593usb_kill_urb(wacom->irq);594mutex_unlock(&wacom->lock);595596return 0;597}598599static int wacom_resume(struct usb_interface *intf)600{601struct wacom *wacom = usb_get_intfdata(intf);602struct wacom_features *features = &wacom->wacom_wac.features;603int rv;604605mutex_lock(&wacom->lock);606607/* switch to wacom mode first */608wacom_query_tablet_data(intf, features);609610if (wacom->open)611rv = usb_submit_urb(wacom->irq, GFP_NOIO);612else613rv = 0;614615mutex_unlock(&wacom->lock);616617return rv;618}619620static int wacom_reset_resume(struct usb_interface *intf)621{622return wacom_resume(intf);623}624625static struct usb_driver wacom_driver = {626.name = "wacom",627.id_table = wacom_ids,628.probe = wacom_probe,629.disconnect = wacom_disconnect,630.suspend = wacom_suspend,631.resume = wacom_resume,632.reset_resume = wacom_reset_resume,633.supports_autosuspend = 1,634};635636static int __init wacom_init(void)637{638int result;639640result = usb_register(&wacom_driver);641if (result == 0)642printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"643DRIVER_DESC "\n");644return result;645}646647static void __exit wacom_exit(void)648{649usb_deregister(&wacom_driver);650}651652module_init(wacom_init);653module_exit(wacom_exit);654655656