diff options
Diffstat (limited to 'drivers/hid/wacom_sys.c')
-rw-r--r-- | drivers/hid/wacom_sys.c | 271 |
1 files changed, 200 insertions, 71 deletions
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index f0db7ec..8593047 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -13,23 +13,26 @@ #include "wacom_wac.h" #include "wacom.h" -#include <linux/hid.h> #define WAC_MSG_RETRIES 5 +#define WAC_CMD_WL_LED_CONTROL 0x03 #define WAC_CMD_LED_CONTROL 0x20 #define WAC_CMD_ICON_START 0x21 #define WAC_CMD_ICON_XFER 0x23 #define WAC_CMD_ICON_BT_XFER 0x26 #define WAC_CMD_RETRIES 10 -static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id, - void *buf, size_t size, unsigned int retries) +#define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP) +#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP) + +static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf, + size_t size, unsigned int retries) { int retval; do { - retval = hid_hw_raw_request(hdev, id, buf, size, type, + retval = hid_hw_raw_request(hdev, buf[0], buf, size, type, HID_REQ_GET_REPORT); } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); @@ -106,12 +109,35 @@ static void wacom_feature_mapping(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_features *features = &wacom->wacom_wac.features; + struct hid_data *hid_data = &wacom->wacom_wac.hid_data; + u8 *data; + int ret; switch (usage->hid) { case HID_DG_CONTACTMAX: /* leave touch_max as is if predefined */ - if (!features->touch_max) - features->touch_max = field->value[0]; + if (!features->touch_max) { + /* read manually */ + data = kzalloc(2, GFP_KERNEL); + if (!data) + break; + data[0] = field->report->id; + ret = wacom_get_report(hdev, HID_FEATURE_REPORT, + data, 2, 0); + if (ret == 2) + features->touch_max = data[1]; + kfree(data); + } + break; + case HID_DG_INPUTMODE: + /* Ignore if value index is out of bounds. */ + if (usage->usage_index >= field->report_count) { + dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n"); + break; + } + + hid_data->inputmode = field->report->id; + hid_data->inputmode_index = usage->usage_index; break; } } @@ -199,6 +225,9 @@ static void wacom_usage_mapping(struct hid_device *hdev, features->pressure_max = field->logical_maximum; break; } + + if (features->type == HID_GENERIC) + wacom_wac_usage_mapping(hdev, field, usage); } static void wacom_parse_hid(struct hid_device *hdev, @@ -237,6 +266,25 @@ static void wacom_parse_hid(struct hid_device *hdev, } } +static int wacom_hid_set_device_mode(struct hid_device *hdev) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct hid_data *hid_data = &wacom->wacom_wac.hid_data; + struct hid_report *r; + struct hid_report_enum *re; + + if (hid_data->inputmode < 0) + return 0; + + re = &(hdev->report_enum[HID_FEATURE_REPORT]); + r = re->report_id_hash[hid_data->inputmode]; + if (r) { + r->field[0]->value[hid_data->inputmode_index] = 2; + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + } + return 0; +} + static int wacom_set_device_mode(struct hid_device *hdev, int report_id, int length, int mode) { @@ -255,7 +303,7 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id, length, 1); if (error >= 0) error = wacom_get_report(hdev, HID_FEATURE_REPORT, - report_id, rep_data, length, 1); + rep_data, length, 1); } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES); kfree(rep_data); @@ -329,6 +377,9 @@ static int wacom_query_tablet_data(struct hid_device *hdev, if (hdev->bus == BUS_BLUETOOTH) return wacom_bt_query_tablet_data(hdev, 1, features); + if (features->type == HID_GENERIC) + return wacom_hid_set_device_mode(hdev); + if (features->device_type == BTN_TOOL_FINGER) { if (features->type > TABLETPC) { /* MT Tablet PC touch */ @@ -487,8 +538,14 @@ static int wacom_led_control(struct wacom *wacom) { unsigned char *buf; int retval; + unsigned char report_id = WAC_CMD_LED_CONTROL; + int buf_size = 9; - buf = kzalloc(9, GFP_KERNEL); + if (wacom->wacom_wac.pid) { /* wireless connected */ + report_id = WAC_CMD_WL_LED_CONTROL; + buf_size = 13; + } + buf = kzalloc(buf_size, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -502,9 +559,16 @@ static int wacom_led_control(struct wacom *wacom) int ring_led = wacom->led.select[0] & 0x03; int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03; int crop_lum = 0; - - buf[0] = WAC_CMD_LED_CONTROL; - buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led); + unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led); + + buf[0] = report_id; + if (wacom->wacom_wac.pid) { + wacom_get_report(wacom->hdev, HID_FEATURE_REPORT, + buf, buf_size, WAC_CMD_RETRIES); + buf[0] = report_id; + buf[4] = led_bits; + } else + buf[1] = led_bits; } else { int led = wacom->led.select[0] | 0x4; @@ -513,14 +577,14 @@ static int wacom_led_control(struct wacom *wacom) wacom->wacom_wac.features.type == WACOM_24HD) led |= (wacom->led.select[1] << 4) | 0x40; - buf[0] = WAC_CMD_LED_CONTROL; + buf[0] = report_id; buf[1] = led; buf[2] = wacom->led.llv; buf[3] = wacom->led.hlv; buf[4] = wacom->led.img_lum; } - retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 9, + retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, buf_size, WAC_CMD_RETRIES); kfree(buf); @@ -602,9 +666,10 @@ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ { \ struct hid_device *hdev = container_of(dev, struct hid_device, dev);\ struct wacom *wacom = hid_get_drvdata(hdev); \ - return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \ + return scnprintf(buf, PAGE_SIZE, "%d\n", \ + wacom->led.select[SET_ID]); \ } \ -static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \ +static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM, \ wacom_led##SET_ID##_select_show, \ wacom_led##SET_ID##_select_store) @@ -641,8 +706,15 @@ static ssize_t wacom_##name##_luminance_store(struct device *dev, \ return wacom_luminance_store(wacom, &wacom->led.field, \ buf, count); \ } \ -static DEVICE_ATTR(name##_luminance, S_IWUSR, \ - NULL, wacom_##name##_luminance_store) +static ssize_t wacom_##name##_luminance_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct wacom *wacom = dev_get_drvdata(dev); \ + return scnprintf(buf, PAGE_SIZE, "%d\n", wacom->led.field); \ +} \ +static DEVICE_ATTR(name##_luminance, DEV_ATTR_RW_PERM, \ + wacom_##name##_luminance_show, \ + wacom_##name##_luminance_store) DEVICE_LUMINANCE_ATTR(status0, llv); DEVICE_LUMINANCE_ATTR(status1, hlv); @@ -683,7 +755,7 @@ static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \ { \ return wacom_button_image_store(dev, BUTTON_ID, buf, count); \ } \ -static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \ +static DEVICE_ATTR(button##BUTTON_ID##_rawimg, DEV_ATTR_WO_PERM, \ NULL, wacom_btnimg##BUTTON_ID##_store) DEVICE_BTNIMG_ATTR(0); @@ -989,7 +1061,7 @@ static ssize_t wacom_store_speed(struct device *dev, return count; } -static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, +static DEVICE_ATTR(speed, DEV_ATTR_RW_PERM, wacom_show_speed, wacom_store_speed); static struct input_dev *wacom_allocate_input(struct wacom *wacom) @@ -1010,47 +1082,82 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom) input_dev->uniq = hdev->uniq; input_dev->id.bustype = hdev->bus; input_dev->id.vendor = hdev->vendor; - input_dev->id.product = hdev->product; + input_dev->id.product = wacom_wac->pid ? wacom_wac->pid : hdev->product; input_dev->id.version = hdev->version; input_set_drvdata(input_dev, wacom); return input_dev; } -static void wacom_unregister_inputs(struct wacom *wacom) +static void wacom_free_inputs(struct wacom *wacom) { - if (wacom->wacom_wac.input) - input_unregister_device(wacom->wacom_wac.input); - if (wacom->wacom_wac.pad_input) - input_unregister_device(wacom->wacom_wac.pad_input); - wacom->wacom_wac.input = NULL; - wacom->wacom_wac.pad_input = NULL; + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + + if (wacom_wac->input) + input_free_device(wacom_wac->input); + if (wacom_wac->pad_input) + input_free_device(wacom_wac->pad_input); + wacom_wac->input = NULL; + wacom_wac->pad_input = NULL; } -static int wacom_register_inputs(struct wacom *wacom) +static int wacom_allocate_inputs(struct wacom *wacom) { struct input_dev *input_dev, *pad_input_dev; struct wacom_wac *wacom_wac = &(wacom->wacom_wac); - int error; input_dev = wacom_allocate_input(wacom); pad_input_dev = wacom_allocate_input(wacom); if (!input_dev || !pad_input_dev) { - error = -ENOMEM; - goto fail1; + wacom_free_inputs(wacom); + return -ENOMEM; } wacom_wac->input = input_dev; wacom_wac->pad_input = pad_input_dev; wacom_wac->pad_input->name = wacom_wac->pad_name; + return 0; +} + +static void wacom_clean_inputs(struct wacom *wacom) +{ + if (wacom->wacom_wac.input) { + if (wacom->wacom_wac.input_registered) + input_unregister_device(wacom->wacom_wac.input); + else + input_free_device(wacom->wacom_wac.input); + } + if (wacom->wacom_wac.pad_input) { + if (wacom->wacom_wac.input_registered) + input_unregister_device(wacom->wacom_wac.pad_input); + else + input_free_device(wacom->wacom_wac.pad_input); + } + wacom->wacom_wac.input = NULL; + wacom->wacom_wac.pad_input = NULL; + wacom_destroy_leds(wacom); +} + +static int wacom_register_inputs(struct wacom *wacom) +{ + struct input_dev *input_dev, *pad_input_dev; + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + int error; + + input_dev = wacom_wac->input; + pad_input_dev = wacom_wac->pad_input; + + if (!input_dev || !pad_input_dev) + return -EINVAL; + error = wacom_setup_input_capabilities(input_dev, wacom_wac); if (error) - goto fail2; + return error; error = input_register_device(input_dev); if (error) - goto fail2; + return error; error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); if (error) { @@ -1061,22 +1168,23 @@ static int wacom_register_inputs(struct wacom *wacom) } else { error = input_register_device(pad_input_dev); if (error) - goto fail3; + goto fail_register_pad_input; + + error = wacom_initialize_leds(wacom); + if (error) + goto fail_leds; } + wacom_wac->input_registered = true; + return 0; -fail3: +fail_leds: + input_unregister_device(pad_input_dev); + pad_input_dev = NULL; +fail_register_pad_input: input_unregister_device(input_dev); - input_dev = NULL; -fail2: wacom_wac->input = NULL; - wacom_wac->pad_input = NULL; -fail1: - if (input_dev) - input_free_device(input_dev); - if (pad_input_dev) - input_free_device(pad_input_dev); return error; } @@ -1101,13 +1209,13 @@ static void wacom_wireless_work(struct work_struct *work) hdev1 = usb_get_intfdata(usbdev->config->interface[1]); wacom1 = hid_get_drvdata(hdev1); wacom_wac1 = &(wacom1->wacom_wac); - wacom_unregister_inputs(wacom1); + wacom_clean_inputs(wacom1); /* Touch interface */ hdev2 = usb_get_intfdata(usbdev->config->interface[2]); wacom2 = hid_get_drvdata(hdev2); wacom_wac2 = &(wacom2->wacom_wac); - wacom_unregister_inputs(wacom2); + wacom_clean_inputs(wacom2); if (wacom_wac->pid == 0) { hid_info(wacom->hdev, "wireless tablet disconnected\n"); @@ -1140,7 +1248,9 @@ static void wacom_wireless_work(struct work_struct *work) wacom_wac1->features.name); wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max; wacom_wac1->shared->type = wacom_wac1->features.type; - error = wacom_register_inputs(wacom1); + wacom_wac1->pid = wacom_wac->pid; + error = wacom_allocate_inputs(wacom1) || + wacom_register_inputs(wacom1); if (error) goto fail; @@ -1160,7 +1270,9 @@ static void wacom_wireless_work(struct work_struct *work) "%s (WL) Pad",wacom_wac2->features.name); snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX, "%s (WL) Pad", wacom_wac2->features.name); - error = wacom_register_inputs(wacom2); + wacom_wac2->pid = wacom_wac->pid; + error = wacom_allocate_inputs(wacom2) || + wacom_register_inputs(wacom2); if (error) goto fail; @@ -1177,8 +1289,8 @@ static void wacom_wireless_work(struct work_struct *work) return; fail: - wacom_unregister_inputs(wacom1); - wacom_unregister_inputs(wacom2); + wacom_clean_inputs(wacom1); + wacom_clean_inputs(wacom2); return; } @@ -1241,10 +1353,13 @@ static int wacom_probe(struct hid_device *hdev, struct wacom_wac *wacom_wac; struct wacom_features *features; int error; + unsigned int connect_mask = HID_CONNECT_HIDRAW; if (!id->driver_data) return -EINVAL; + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); if (!wacom) return -ENOMEM; @@ -1256,7 +1371,7 @@ static int wacom_probe(struct hid_device *hdev, error = hid_parse(hdev); if (error) { hid_err(hdev, "parse failed\n"); - goto fail1; + goto fail_parse; } wacom_wac = &wacom->wacom_wac; @@ -1265,12 +1380,12 @@ static int wacom_probe(struct hid_device *hdev, features->pktlen = wacom_compute_pktlen(hdev); if (features->pktlen > WACOM_PKGLEN_MAX) { error = -EINVAL; - goto fail1; + goto fail_pktlen; } if (features->check_for_hid_type && features->hid_type != hdev->type) { error = -ENODEV; - goto fail1; + goto fail_type; } wacom->usbdev = dev; @@ -1278,6 +1393,12 @@ static int wacom_probe(struct hid_device *hdev, mutex_init(&wacom->lock); INIT_WORK(&wacom->work, wacom_wireless_work); + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { + error = wacom_allocate_inputs(wacom); + if (error) + goto fail_allocate_inputs; + } + /* set the default size in case we do not get them from hid */ wacom_set_default_phy(features); @@ -1339,24 +1460,20 @@ static int wacom_probe(struct hid_device *hdev, error = wacom_add_shared_data(hdev); if (error) - goto fail1; + goto fail_shared_data; } - error = wacom_initialize_leds(wacom); - if (error) - goto fail2; - if (!(features->quirks & WACOM_QUIRK_MONITOR) && (features->quirks & WACOM_QUIRK_BATTERY)) { error = wacom_initialize_battery(wacom); if (error) - goto fail3; + goto fail_battery; } if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { error = wacom_register_inputs(wacom); if (error) - goto fail4; + goto fail_register_inputs; } if (hdev->bus == BUS_BLUETOOTH) { @@ -1367,16 +1484,19 @@ static int wacom_probe(struct hid_device *hdev, error); } - /* Note that if query fails it is not a hard failure */ - wacom_query_tablet_data(hdev, features); + if (features->type == HID_GENERIC) + connect_mask |= HID_CONNECT_DRIVER; /* Regular HID work starts now */ - error = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + error = hid_hw_start(hdev, connect_mask); if (error) { hid_err(hdev, "hw start failed\n"); - goto fail5; + goto fail_hw_start; } + /* Note that if query fails it is not a hard failure */ + wacom_query_tablet_data(hdev, features); + if (features->quirks & WACOM_QUIRK_MONITOR) error = hid_hw_open(hdev); @@ -1387,13 +1507,21 @@ static int wacom_probe(struct hid_device *hdev, return 0; - fail5: if (hdev->bus == BUS_BLUETOOTH) +fail_hw_start: + if (hdev->bus == BUS_BLUETOOTH) device_remove_file(&hdev->dev, &dev_attr_speed); - wacom_unregister_inputs(wacom); - fail4: wacom_destroy_battery(wacom); - fail3: wacom_destroy_leds(wacom); - fail2: wacom_remove_shared_data(wacom_wac); - fail1: kfree(wacom); +fail_register_inputs: + wacom_clean_inputs(wacom); + wacom_destroy_battery(wacom); +fail_battery: + wacom_remove_shared_data(wacom_wac); +fail_shared_data: + wacom_clean_inputs(wacom); +fail_allocate_inputs: +fail_type: +fail_pktlen: +fail_parse: + kfree(wacom); hid_set_drvdata(hdev, NULL); return error; } @@ -1405,11 +1533,10 @@ static void wacom_remove(struct hid_device *hdev) hid_hw_stop(hdev); cancel_work_sync(&wacom->work); - wacom_unregister_inputs(wacom); + wacom_clean_inputs(wacom); if (hdev->bus == BUS_BLUETOOTH) device_remove_file(&hdev->dev, &dev_attr_speed); wacom_destroy_battery(wacom); - wacom_destroy_leds(wacom); wacom_remove_shared_data(&wacom->wacom_wac); hid_set_drvdata(hdev, NULL); @@ -1444,6 +1571,8 @@ static struct hid_driver wacom_driver = { .id_table = wacom_ids, .probe = wacom_probe, .remove = wacom_remove, + .event = wacom_wac_event, + .report = wacom_wac_report, #ifdef CONFIG_PM .resume = wacom_resume, .reset_resume = wacom_reset_resume, |