diff options
71 files changed, 4900 insertions, 3568 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-wacom b/Documentation/ABI/testing/sysfs-driver-wacom index 8d55a83d6921..7fc781048b79 100644 --- a/Documentation/ABI/testing/sysfs-driver-wacom +++ b/Documentation/ABI/testing/sysfs-driver-wacom | |||
| @@ -1,3 +1,16 @@ | |||
| 1 | WWhat: /sys/class/hidraw/hidraw*/device/oled*_img | ||
| 2 | Date: June 2012 | ||
| 3 | Contact: linux-bluetooth@vger.kernel.org | ||
| 4 | Description: | ||
| 5 | The /sys/class/hidraw/hidraw*/device/oled*_img files control | ||
| 6 | OLED mocro displays on Intuos4 Wireless tablet. Accepted image | ||
| 7 | has to contain 256 bytes (64x32 px 1 bit colour). The format | ||
| 8 | is the same as PBM image 62x32px without header (64 bits per | ||
| 9 | horizontal line, 32 lines). An example of setting OLED No. 0: | ||
| 10 | dd bs=256 count=1 if=img_file of=[path to oled0_img]/oled0_img | ||
| 11 | The attribute is read only and no local copy of the image is | ||
| 12 | stored. | ||
| 13 | |||
| 1 | What: /sys/class/hidraw/hidraw*/device/speed | 14 | What: /sys/class/hidraw/hidraw*/device/speed |
| 2 | Date: April 2010 | 15 | Date: April 2010 |
| 3 | Kernel Version: 2.6.35 | 16 | Kernel Version: 2.6.35 |
diff --git a/MAINTAINERS b/MAINTAINERS index 9a6c4da3b2ff..9362f54bccb8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -5322,6 +5322,12 @@ L: linux-mtd@lists.infradead.org | |||
| 5322 | S: Maintained | 5322 | S: Maintained |
| 5323 | F: drivers/mtd/devices/phram.c | 5323 | F: drivers/mtd/devices/phram.c |
| 5324 | 5324 | ||
| 5325 | PICOLCD HID DRIVER | ||
| 5326 | M: Bruno Prémont <bonbons@linux-vserver.org> | ||
| 5327 | L: linux-input@vger.kernel.org | ||
| 5328 | S: Maintained | ||
| 5329 | F: drivers/hid/hid-picolcd* | ||
| 5330 | |||
| 5325 | PICOXCELL SUPPORT | 5331 | PICOXCELL SUPPORT |
| 5326 | M: Jamie Iles <jamie@jamieiles.com> | 5332 | M: Jamie Iles <jamie@jamieiles.com> |
| 5327 | L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) | 5333 | L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) |
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fbf49503508d..2af774ad1060 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
| @@ -307,7 +307,6 @@ config HID_LOGITECH | |||
| 307 | config HID_LOGITECH_DJ | 307 | config HID_LOGITECH_DJ |
| 308 | tristate "Logitech Unifying receivers full support" | 308 | tristate "Logitech Unifying receivers full support" |
| 309 | depends on HID_LOGITECH | 309 | depends on HID_LOGITECH |
| 310 | default m | ||
| 311 | ---help--- | 310 | ---help--- |
| 312 | Say Y if you want support for Logitech Unifying receivers and devices. | 311 | Say Y if you want support for Logitech Unifying receivers and devices. |
| 313 | Unifying receivers are capable of pairing up to 6 Logitech compliant | 312 | Unifying receivers are capable of pairing up to 6 Logitech compliant |
| @@ -527,6 +526,14 @@ config HID_PICOLCD_LEDS | |||
| 527 | ---help--- | 526 | ---help--- |
| 528 | Provide access to PicoLCD's GPO pins via leds class. | 527 | Provide access to PicoLCD's GPO pins via leds class. |
| 529 | 528 | ||
| 529 | config HID_PICOLCD_CIR | ||
| 530 | bool "CIR via RC class" if EXPERT | ||
| 531 | default !EXPERT | ||
| 532 | depends on HID_PICOLCD | ||
| 533 | depends on HID_PICOLCD=RC_CORE || RC_CORE=y | ||
| 534 | ---help--- | ||
| 535 | Provide access to PicoLCD's CIR interface via remote control (LIRC). | ||
| 536 | |||
| 530 | config HID_PRIMAX | 537 | config HID_PRIMAX |
| 531 | tristate "Primax non-fully HID-compliant devices" | 538 | tristate "Primax non-fully HID-compliant devices" |
| 532 | depends on USB_HID | 539 | depends on USB_HID |
| @@ -534,6 +541,15 @@ config HID_PRIMAX | |||
| 534 | Support for Primax devices that are not fully compliant with the | 541 | Support for Primax devices that are not fully compliant with the |
| 535 | HID standard. | 542 | HID standard. |
| 536 | 543 | ||
| 544 | config HID_PS3REMOTE | ||
| 545 | tristate "Sony PS3 BD Remote Control" | ||
| 546 | depends on BT_HIDP | ||
| 547 | ---help--- | ||
| 548 | Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech | ||
| 549 | Harmony Adapter for PS3, which connect over Bluetooth. | ||
| 550 | |||
| 551 | Support for the 6-axis controllers is provided by HID_SONY. | ||
| 552 | |||
| 537 | config HID_ROCCAT | 553 | config HID_ROCCAT |
| 538 | tristate "Roccat device support" | 554 | tristate "Roccat device support" |
| 539 | depends on USB_HID | 555 | depends on USB_HID |
| @@ -561,7 +577,9 @@ config HID_SONY | |||
| 561 | tristate "Sony PS3 controller" | 577 | tristate "Sony PS3 controller" |
| 562 | depends on USB_HID | 578 | depends on USB_HID |
| 563 | ---help--- | 579 | ---help--- |
| 564 | Support for Sony PS3 controller. | 580 | Support for Sony PS3 6-axis controllers. |
| 581 | |||
| 582 | Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE. | ||
| 565 | 583 | ||
| 566 | config HID_SPEEDLINK | 584 | config HID_SPEEDLINK |
| 567 | tristate "Speedlink VAD Cezanne mouse support" | 585 | tristate "Speedlink VAD Cezanne mouse support" |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index f975485f88b2..5a3690ff9bf2 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
| @@ -69,7 +69,28 @@ obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o | |||
| 69 | obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o | 69 | obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o |
| 70 | obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o | 70 | obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o |
| 71 | obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o | 71 | obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o |
| 72 | hid-picolcd-y += hid-picolcd_core.o | ||
| 73 | ifdef CONFIG_HID_PICOLCD_FB | ||
| 74 | hid-picolcd-y += hid-picolcd_fb.o | ||
| 75 | endif | ||
| 76 | ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
| 77 | hid-picolcd-y += hid-picolcd_backlight.o | ||
| 78 | endif | ||
| 79 | ifdef CONFIG_HID_PICOLCD_LCD | ||
| 80 | hid-picolcd-y += hid-picolcd_lcd.o | ||
| 81 | endif | ||
| 82 | ifdef CONFIG_HID_PICOLCD_LEDS | ||
| 83 | hid-picolcd-y += hid-picolcd_leds.o | ||
| 84 | endif | ||
| 85 | ifdef CONFIG_HID_PICOLCD_CIR | ||
| 86 | hid-picolcd-y += hid-picolcd_cir.o | ||
| 87 | endif | ||
| 88 | ifdef CONFIG_DEBUG_FS | ||
| 89 | hid-picolcd-y += hid-picolcd_debugfs.o | ||
| 90 | endif | ||
| 91 | |||
| 72 | obj-$(CONFIG_HID_PRIMAX) += hid-primax.o | 92 | obj-$(CONFIG_HID_PRIMAX) += hid-primax.o |
| 93 | obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o | ||
| 73 | obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ | 94 | obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ |
| 74 | hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ | 95 | hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ |
| 75 | hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \ | 96 | hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \ |
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 902d1dfeb1b5..0a239885e67c 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 585344b6d338..06ebdbb6ea02 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> | 8 | * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c index ba64b041b8bf..7968187ddf7b 100644 --- a/drivers/hid/hid-aureal.c +++ b/drivers/hid/hid-aureal.c | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 9 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 10 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 10 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 11 | * Copyright (c) 2006-2007 Jiri Kosina | 11 | * Copyright (c) 2006-2007 Jiri Kosina |
| 12 | * Copyright (c) 2007 Paul Walmsley | ||
| 13 | * Copyright (c) 2008 Jiri Slaby | 12 | * Copyright (c) 2008 Jiri Slaby |
| 14 | */ | 13 | */ |
| 15 | #include <linux/device.h> | 14 | #include <linux/device.h> |
diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c index a1a765a5b08a..a1a5a12c3a6b 100644 --- a/drivers/hid/hid-belkin.c +++ b/drivers/hid/hid-belkin.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c index 888ece68a47c..af034d3d9256 100644 --- a/drivers/hid/hid-cherry.c +++ b/drivers/hid/hid-cherry.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8bcd168fffae..2cd6880b6b17 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
| @@ -126,7 +126,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) | |||
| 126 | 126 | ||
| 127 | if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { | 127 | if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { |
| 128 | hid_err(parser->device, "collection stack overflow\n"); | 128 | hid_err(parser->device, "collection stack overflow\n"); |
| 129 | return -1; | 129 | return -EINVAL; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | if (parser->device->maxcollection == parser->device->collection_size) { | 132 | if (parser->device->maxcollection == parser->device->collection_size) { |
| @@ -134,7 +134,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) | |||
| 134 | parser->device->collection_size * 2, GFP_KERNEL); | 134 | parser->device->collection_size * 2, GFP_KERNEL); |
| 135 | if (collection == NULL) { | 135 | if (collection == NULL) { |
| 136 | hid_err(parser->device, "failed to reallocate collection array\n"); | 136 | hid_err(parser->device, "failed to reallocate collection array\n"); |
| 137 | return -1; | 137 | return -ENOMEM; |
| 138 | } | 138 | } |
| 139 | memcpy(collection, parser->device->collection, | 139 | memcpy(collection, parser->device->collection, |
| 140 | sizeof(struct hid_collection) * | 140 | sizeof(struct hid_collection) * |
| @@ -170,7 +170,7 @@ static int close_collection(struct hid_parser *parser) | |||
| 170 | { | 170 | { |
| 171 | if (!parser->collection_stack_ptr) { | 171 | if (!parser->collection_stack_ptr) { |
| 172 | hid_err(parser->device, "collection stack underflow\n"); | 172 | hid_err(parser->device, "collection stack underflow\n"); |
| 173 | return -1; | 173 | return -EINVAL; |
| 174 | } | 174 | } |
| 175 | parser->collection_stack_ptr--; | 175 | parser->collection_stack_ptr--; |
| 176 | return 0; | 176 | return 0; |
| @@ -374,7 +374,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) | |||
| 374 | 374 | ||
| 375 | case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: | 375 | case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: |
| 376 | parser->global.report_size = item_udata(item); | 376 | parser->global.report_size = item_udata(item); |
| 377 | if (parser->global.report_size > 96) { | 377 | if (parser->global.report_size > 128) { |
| 378 | hid_err(parser->device, "invalid report_size %d\n", | 378 | hid_err(parser->device, "invalid report_size %d\n", |
| 379 | parser->global.report_size); | 379 | parser->global.report_size); |
| 380 | return -1; | 380 | return -1; |
| @@ -757,6 +757,7 @@ int hid_open_report(struct hid_device *device) | |||
| 757 | struct hid_item item; | 757 | struct hid_item item; |
| 758 | unsigned int size; | 758 | unsigned int size; |
| 759 | __u8 *start; | 759 | __u8 *start; |
| 760 | __u8 *buf; | ||
| 760 | __u8 *end; | 761 | __u8 *end; |
| 761 | int ret; | 762 | int ret; |
| 762 | static int (*dispatch_type[])(struct hid_parser *parser, | 763 | static int (*dispatch_type[])(struct hid_parser *parser, |
| @@ -775,12 +776,21 @@ int hid_open_report(struct hid_device *device) | |||
| 775 | return -ENODEV; | 776 | return -ENODEV; |
| 776 | size = device->dev_rsize; | 777 | size = device->dev_rsize; |
| 777 | 778 | ||
| 779 | buf = kmemdup(start, size, GFP_KERNEL); | ||
| 780 | if (buf == NULL) | ||
| 781 | return -ENOMEM; | ||
| 782 | |||
| 778 | if (device->driver->report_fixup) | 783 | if (device->driver->report_fixup) |
| 779 | start = device->driver->report_fixup(device, start, &size); | 784 | start = device->driver->report_fixup(device, buf, &size); |
| 785 | else | ||
| 786 | start = buf; | ||
| 780 | 787 | ||
| 781 | device->rdesc = kmemdup(start, size, GFP_KERNEL); | 788 | start = kmemdup(start, size, GFP_KERNEL); |
| 782 | if (device->rdesc == NULL) | 789 | kfree(buf); |
| 790 | if (start == NULL) | ||
| 783 | return -ENOMEM; | 791 | return -ENOMEM; |
| 792 | |||
| 793 | device->rdesc = start; | ||
| 784 | device->rsize = size; | 794 | device->rsize = size; |
| 785 | 795 | ||
| 786 | parser = vzalloc(sizeof(struct hid_parser)); | 796 | parser = vzalloc(sizeof(struct hid_parser)); |
| @@ -1448,7 +1458,14 @@ void hid_disconnect(struct hid_device *hdev) | |||
| 1448 | } | 1458 | } |
| 1449 | EXPORT_SYMBOL_GPL(hid_disconnect); | 1459 | EXPORT_SYMBOL_GPL(hid_disconnect); |
| 1450 | 1460 | ||
| 1451 | /* a list of devices for which there is a specialized driver on HID bus */ | 1461 | /* |
| 1462 | * A list of devices for which there is a specialized driver on HID bus. | ||
| 1463 | * | ||
| 1464 | * Please note that for multitouch devices (driven by hid-multitouch driver), | ||
| 1465 | * there is a proper autodetection and autoloading in place (based on presence | ||
| 1466 | * of HID_DG_CONTACTID), so those devices don't need to be added to this list, | ||
| 1467 | * as we are doing the right thing in hid_scan_usage(). | ||
| 1468 | */ | ||
| 1452 | static const struct hid_device_id hid_have_special_driver[] = { | 1469 | static const struct hid_device_id hid_have_special_driver[] = { |
| 1453 | { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, | 1470 | { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, |
| 1454 | { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, | 1471 | { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, |
| @@ -1566,6 +1583,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 1566 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, | 1583 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, |
| 1567 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, | 1584 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, |
| 1568 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, | 1585 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, |
| 1586 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) }, | ||
| 1569 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, | 1587 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, |
| 1570 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, | 1588 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, |
| 1571 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, | 1589 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, |
| @@ -1627,6 +1645,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 1627 | { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, | 1645 | { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, |
| 1628 | { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, | 1646 | { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, |
| 1629 | { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, | 1647 | { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, |
| 1648 | #if IS_ENABLED(CONFIG_HID_ROCCAT) | ||
| 1630 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, | 1649 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, |
| 1631 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, | 1650 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, |
| 1632 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, | 1651 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, |
| @@ -1635,10 +1654,12 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 1635 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, | 1654 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, |
| 1636 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, | 1655 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, |
| 1637 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, | 1656 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, |
| 1657 | #endif | ||
| 1638 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, | 1658 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, |
| 1639 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, | 1659 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, |
| 1640 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, | 1660 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, |
| 1641 | { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, | 1661 | { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, |
| 1662 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, | ||
| 1642 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, | 1663 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, |
| 1643 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, | 1664 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, |
| 1644 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, | 1665 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, |
| @@ -1663,6 +1684,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 1663 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, | 1684 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, |
| 1664 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, | 1685 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, |
| 1665 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, | 1686 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, |
| 1687 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, | ||
| 1666 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, | 1688 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, |
| 1667 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, | 1689 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, |
| 1668 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, | 1690 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, |
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 9e43aaca9774..3e159a50dac7 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 01dd9a7daf7a..933fff0fff1f 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c | |||
| @@ -911,15 +911,21 @@ static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) | |||
| 911 | 911 | ||
| 912 | } | 912 | } |
| 913 | 913 | ||
| 914 | |||
| 915 | static int hid_debug_rdesc_show(struct seq_file *f, void *p) | 914 | static int hid_debug_rdesc_show(struct seq_file *f, void *p) |
| 916 | { | 915 | { |
| 917 | struct hid_device *hdev = f->private; | 916 | struct hid_device *hdev = f->private; |
| 917 | const __u8 *rdesc = hdev->rdesc; | ||
| 918 | unsigned rsize = hdev->rsize; | ||
| 918 | int i; | 919 | int i; |
| 919 | 920 | ||
| 921 | if (!rdesc) { | ||
| 922 | rdesc = hdev->dev_rdesc; | ||
| 923 | rsize = hdev->dev_rsize; | ||
| 924 | } | ||
| 925 | |||
| 920 | /* dump HID report descriptor */ | 926 | /* dump HID report descriptor */ |
| 921 | for (i = 0; i < hdev->rsize; i++) | 927 | for (i = 0; i < rsize; i++) |
| 922 | seq_printf(f, "%02x ", hdev->rdesc[i]); | 928 | seq_printf(f, "%02x ", rdesc[i]); |
| 923 | seq_printf(f, "\n\n"); | 929 | seq_printf(f, "\n\n"); |
| 924 | 930 | ||
| 925 | /* dump parsed data and input mappings */ | 931 | /* dump parsed data and input mappings */ |
diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c index ca1163e9d42d..6540af2871a7 100644 --- a/drivers/hid/hid-ezkey.c +++ b/drivers/hid/hid-ezkey.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index e88b951cd10d..4442c30ef531 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | * Copyright (c) 1999 Andreas Gal | 4 | * Copyright (c) 1999 Andreas Gal |
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2007 Paul Walmsley | ||
| 8 | * Copyright (c) 2008 Jiri Slaby | 7 | * Copyright (c) 2008 Jiri Slaby |
| 9 | * Copyright (c) 2006-2008 Jiri Kosina | 8 | * Copyright (c) 2006-2008 Jiri Kosina |
| 10 | */ | 9 | */ |
diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index 4e7542151e22..ff295e60059b 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c | |||
| @@ -100,8 +100,7 @@ static void holtekff_send(struct holtekff_device *holtekff, | |||
| 100 | holtekff->field->value[i] = data[i]; | 100 | holtekff->field->value[i] = data[i]; |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | dbg_hid("sending %02x %02x %02x %02x %02x %02x %02x\n", data[0], | 103 | dbg_hid("sending %*ph\n", 7, data); |
| 104 | data[1], data[2], data[3], data[4], data[5], data[6]); | ||
| 105 | 104 | ||
| 106 | usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT); | 105 | usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT); |
| 107 | } | 106 | } |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 1dcb76ff51e3..ca4d83e6e387 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | */ | 8 | */ |
| 10 | 9 | ||
| 11 | /* | 10 | /* |
| @@ -269,7 +268,11 @@ | |||
| 269 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa | 268 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa |
| 270 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 | 269 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 |
| 271 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 | 270 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 |
| 271 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 | ||
| 272 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 | 272 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 |
| 273 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 | ||
| 274 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 | ||
| 275 | #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 | ||
| 273 | 276 | ||
| 274 | #define USB_VENDOR_ID_ELECOM 0x056e | 277 | #define USB_VENDOR_ID_ELECOM 0x056e |
| 275 | #define USB_DEVICE_ID_ELECOM_BM084 0x0061 | 278 | #define USB_DEVICE_ID_ELECOM_BM084 0x0061 |
| @@ -283,6 +286,9 @@ | |||
| 283 | #define USB_VENDOR_ID_EMS 0x2006 | 286 | #define USB_VENDOR_ID_EMS 0x2006 |
| 284 | #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 | 287 | #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 |
| 285 | 288 | ||
| 289 | #define USB_VENDOR_ID_FLATFROG 0x25b5 | ||
| 290 | #define USB_DEVICE_ID_MULTITOUCH_3200 0x0002 | ||
| 291 | |||
| 286 | #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f | 292 | #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f |
| 287 | #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 | 293 | #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 |
| 288 | 294 | ||
| @@ -296,6 +302,9 @@ | |||
| 296 | #define USB_VENDOR_ID_EZKEY 0x0518 | 302 | #define USB_VENDOR_ID_EZKEY 0x0518 |
| 297 | #define USB_DEVICE_ID_BTC_8193 0x0002 | 303 | #define USB_DEVICE_ID_BTC_8193 0x0002 |
| 298 | 304 | ||
| 305 | #define USB_VENDOR_ID_FREESCALE 0x15A2 | ||
| 306 | #define USB_DEVICE_ID_FREESCALE_MX28 0x004F | ||
| 307 | |||
| 299 | #define USB_VENDOR_ID_FRUCTEL 0x25B6 | 308 | #define USB_VENDOR_ID_FRUCTEL 0x25B6 |
| 300 | #define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002 | 309 | #define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002 |
| 301 | 310 | ||
| @@ -305,6 +314,7 @@ | |||
| 305 | 314 | ||
| 306 | #define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc | 315 | #define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc |
| 307 | #define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003 | 316 | #define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003 |
| 317 | #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100 | ||
| 308 | 318 | ||
| 309 | #define USB_VENDOR_ID_GLAB 0x06c2 | 319 | #define USB_VENDOR_ID_GLAB 0x06c2 |
| 310 | #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 | 320 | #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 |
| @@ -496,6 +506,7 @@ | |||
| 496 | #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 | 506 | #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 |
| 497 | #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 | 507 | #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 |
| 498 | #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f | 508 | #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f |
| 509 | #define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306 | ||
| 499 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a | 510 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a |
| 500 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 | 511 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 |
| 501 | #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 | 512 | #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 |
| @@ -652,7 +663,6 @@ | |||
| 652 | #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 | 663 | #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 |
| 653 | #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 | 664 | #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 |
| 654 | #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 | 665 | #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 |
| 655 | #define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001 | ||
| 656 | 666 | ||
| 657 | #define USB_VENDOR_ID_ROCCAT 0x1e7d | 667 | #define USB_VENDOR_ID_ROCCAT 0x1e7d |
| 658 | #define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 | 668 | #define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 |
| @@ -683,6 +693,7 @@ | |||
| 683 | 693 | ||
| 684 | #define USB_VENDOR_ID_SONY 0x054c | 694 | #define USB_VENDOR_ID_SONY 0x054c |
| 685 | #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b | 695 | #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b |
| 696 | #define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 | ||
| 686 | #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 | 697 | #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 |
| 687 | #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f | 698 | #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f |
| 688 | 699 | ||
| @@ -758,6 +769,7 @@ | |||
| 758 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 | 769 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 |
| 759 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 | 770 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 |
| 760 | #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 | 771 | #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 |
| 772 | #define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 | ||
| 761 | 773 | ||
| 762 | #define USB_VENDOR_ID_UNITEC 0x227d | 774 | #define USB_VENDOR_ID_UNITEC 0x227d |
| 763 | #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 | 775 | #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 |
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 811bfad64609..d917c0d53685 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c | |||
| @@ -1154,6 +1154,7 @@ static void report_features(struct hid_device *hid) | |||
| 1154 | 1154 | ||
| 1155 | int hidinput_connect(struct hid_device *hid, unsigned int force) | 1155 | int hidinput_connect(struct hid_device *hid, unsigned int force) |
| 1156 | { | 1156 | { |
| 1157 | struct hid_driver *drv = hid->driver; | ||
| 1157 | struct hid_report *report; | 1158 | struct hid_report *report; |
| 1158 | struct hid_input *hidinput = NULL; | 1159 | struct hid_input *hidinput = NULL; |
| 1159 | struct input_dev *input_dev; | 1160 | struct input_dev *input_dev; |
| @@ -1228,6 +1229,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) | |||
| 1228 | * UGCI) cram a lot of unrelated inputs into the | 1229 | * UGCI) cram a lot of unrelated inputs into the |
| 1229 | * same interface. */ | 1230 | * same interface. */ |
| 1230 | hidinput->report = report; | 1231 | hidinput->report = report; |
| 1232 | if (drv->input_configured) | ||
| 1233 | drv->input_configured(hid, hidinput); | ||
| 1231 | if (input_register_device(hidinput->input)) | 1234 | if (input_register_device(hidinput->input)) |
| 1232 | goto out_cleanup; | 1235 | goto out_cleanup; |
| 1233 | hidinput = NULL; | 1236 | hidinput = NULL; |
| @@ -1235,8 +1238,12 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) | |||
| 1235 | } | 1238 | } |
| 1236 | } | 1239 | } |
| 1237 | 1240 | ||
| 1238 | if (hidinput && input_register_device(hidinput->input)) | 1241 | if (hidinput) { |
| 1239 | goto out_cleanup; | 1242 | if (drv->input_configured) |
| 1243 | drv->input_configured(hid, hidinput); | ||
| 1244 | if (input_register_device(hidinput->input)) | ||
| 1245 | goto out_cleanup; | ||
| 1246 | } | ||
| 1240 | 1247 | ||
| 1241 | return 0; | 1248 | return 0; |
| 1242 | 1249 | ||
diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c index c4fe9bd095b7..22bc14abdfa3 100644 --- a/drivers/hid/hid-lcpower.c +++ b/drivers/hid/hid-lcpower.c | |||
| @@ -24,7 +24,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 24 | struct hid_field *field, struct hid_usage *usage, | 24 | struct hid_field *field, struct hid_usage *usage, |
| 25 | unsigned long **bit, int *max) | 25 | unsigned long **bit, int *max) |
| 26 | { | 26 | { |
| 27 | if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000) | 27 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) |
| 28 | return 0; | 28 | return 0; |
| 29 | 29 | ||
| 30 | switch (usage->hid & HID_USAGE) { | 30 | switch (usage->hid & HID_USAGE) { |
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index 60c4e1e85913..cea016e94f43 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c | |||
| @@ -56,9 +56,8 @@ static int tpkbd_input_mapping(struct hid_device *hdev, | |||
| 56 | static int tpkbd_features_set(struct hid_device *hdev) | 56 | static int tpkbd_features_set(struct hid_device *hdev) |
| 57 | { | 57 | { |
| 58 | struct hid_report *report; | 58 | struct hid_report *report; |
| 59 | struct tpkbd_data_pointer *data_pointer; | 59 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 60 | 60 | ||
| 61 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 62 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; | 61 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; |
| 63 | 62 | ||
| 64 | report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; | 63 | report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; |
| @@ -77,14 +76,8 @@ static ssize_t pointer_press_to_select_show(struct device *dev, | |||
| 77 | struct device_attribute *attr, | 76 | struct device_attribute *attr, |
| 78 | char *buf) | 77 | char *buf) |
| 79 | { | 78 | { |
| 80 | struct hid_device *hdev; | 79 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 81 | struct tpkbd_data_pointer *data_pointer; | 80 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 82 | |||
| 83 | hdev = container_of(dev, struct hid_device, dev); | ||
| 84 | if (hdev == NULL) | ||
| 85 | return -ENODEV; | ||
| 86 | |||
| 87 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 88 | 81 | ||
| 89 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); | 82 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); |
| 90 | } | 83 | } |
| @@ -94,16 +87,10 @@ static ssize_t pointer_press_to_select_store(struct device *dev, | |||
| 94 | const char *buf, | 87 | const char *buf, |
| 95 | size_t count) | 88 | size_t count) |
| 96 | { | 89 | { |
| 97 | struct hid_device *hdev; | 90 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 98 | struct tpkbd_data_pointer *data_pointer; | 91 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 99 | int value; | 92 | int value; |
| 100 | 93 | ||
| 101 | hdev = container_of(dev, struct hid_device, dev); | ||
| 102 | if (hdev == NULL) | ||
| 103 | return -ENODEV; | ||
| 104 | |||
| 105 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 106 | |||
| 107 | if (kstrtoint(buf, 10, &value)) | 94 | if (kstrtoint(buf, 10, &value)) |
| 108 | return -EINVAL; | 95 | return -EINVAL; |
| 109 | if (value < 0 || value > 1) | 96 | if (value < 0 || value > 1) |
| @@ -119,14 +106,8 @@ static ssize_t pointer_dragging_show(struct device *dev, | |||
| 119 | struct device_attribute *attr, | 106 | struct device_attribute *attr, |
| 120 | char *buf) | 107 | char *buf) |
| 121 | { | 108 | { |
| 122 | struct hid_device *hdev; | 109 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 123 | struct tpkbd_data_pointer *data_pointer; | 110 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 124 | |||
| 125 | hdev = container_of(dev, struct hid_device, dev); | ||
| 126 | if (hdev == NULL) | ||
| 127 | return -ENODEV; | ||
| 128 | |||
| 129 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 130 | 111 | ||
| 131 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); | 112 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); |
| 132 | } | 113 | } |
| @@ -136,16 +117,10 @@ static ssize_t pointer_dragging_store(struct device *dev, | |||
| 136 | const char *buf, | 117 | const char *buf, |
| 137 | size_t count) | 118 | size_t count) |
| 138 | { | 119 | { |
| 139 | struct hid_device *hdev; | 120 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 140 | struct tpkbd_data_pointer *data_pointer; | 121 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 141 | int value; | 122 | int value; |
| 142 | 123 | ||
| 143 | hdev = container_of(dev, struct hid_device, dev); | ||
| 144 | if (hdev == NULL) | ||
| 145 | return -ENODEV; | ||
| 146 | |||
| 147 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 148 | |||
| 149 | if (kstrtoint(buf, 10, &value)) | 124 | if (kstrtoint(buf, 10, &value)) |
| 150 | return -EINVAL; | 125 | return -EINVAL; |
| 151 | if (value < 0 || value > 1) | 126 | if (value < 0 || value > 1) |
| @@ -161,14 +136,8 @@ static ssize_t pointer_release_to_select_show(struct device *dev, | |||
| 161 | struct device_attribute *attr, | 136 | struct device_attribute *attr, |
| 162 | char *buf) | 137 | char *buf) |
| 163 | { | 138 | { |
| 164 | struct hid_device *hdev; | 139 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 165 | struct tpkbd_data_pointer *data_pointer; | 140 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 166 | |||
| 167 | hdev = container_of(dev, struct hid_device, dev); | ||
| 168 | if (hdev == NULL) | ||
| 169 | return -ENODEV; | ||
| 170 | |||
| 171 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 172 | 141 | ||
| 173 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); | 142 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); |
| 174 | } | 143 | } |
| @@ -178,16 +147,10 @@ static ssize_t pointer_release_to_select_store(struct device *dev, | |||
| 178 | const char *buf, | 147 | const char *buf, |
| 179 | size_t count) | 148 | size_t count) |
| 180 | { | 149 | { |
| 181 | struct hid_device *hdev; | 150 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 182 | struct tpkbd_data_pointer *data_pointer; | 151 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 183 | int value; | 152 | int value; |
| 184 | 153 | ||
| 185 | hdev = container_of(dev, struct hid_device, dev); | ||
| 186 | if (hdev == NULL) | ||
| 187 | return -ENODEV; | ||
| 188 | |||
| 189 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 190 | |||
| 191 | if (kstrtoint(buf, 10, &value)) | 154 | if (kstrtoint(buf, 10, &value)) |
| 192 | return -EINVAL; | 155 | return -EINVAL; |
| 193 | if (value < 0 || value > 1) | 156 | if (value < 0 || value > 1) |
| @@ -203,14 +166,8 @@ static ssize_t pointer_select_right_show(struct device *dev, | |||
| 203 | struct device_attribute *attr, | 166 | struct device_attribute *attr, |
| 204 | char *buf) | 167 | char *buf) |
| 205 | { | 168 | { |
| 206 | struct hid_device *hdev; | 169 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 207 | struct tpkbd_data_pointer *data_pointer; | 170 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 208 | |||
| 209 | hdev = container_of(dev, struct hid_device, dev); | ||
| 210 | if (hdev == NULL) | ||
| 211 | return -ENODEV; | ||
| 212 | |||
| 213 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 214 | 171 | ||
| 215 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); | 172 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); |
| 216 | } | 173 | } |
| @@ -220,16 +177,10 @@ static ssize_t pointer_select_right_store(struct device *dev, | |||
| 220 | const char *buf, | 177 | const char *buf, |
| 221 | size_t count) | 178 | size_t count) |
| 222 | { | 179 | { |
| 223 | struct hid_device *hdev; | 180 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 224 | struct tpkbd_data_pointer *data_pointer; | 181 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 225 | int value; | 182 | int value; |
| 226 | 183 | ||
| 227 | hdev = container_of(dev, struct hid_device, dev); | ||
| 228 | if (hdev == NULL) | ||
| 229 | return -ENODEV; | ||
| 230 | |||
| 231 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 232 | |||
| 233 | if (kstrtoint(buf, 10, &value)) | 184 | if (kstrtoint(buf, 10, &value)) |
| 234 | return -EINVAL; | 185 | return -EINVAL; |
| 235 | if (value < 0 || value > 1) | 186 | if (value < 0 || value > 1) |
| @@ -245,14 +196,8 @@ static ssize_t pointer_sensitivity_show(struct device *dev, | |||
| 245 | struct device_attribute *attr, | 196 | struct device_attribute *attr, |
| 246 | char *buf) | 197 | char *buf) |
| 247 | { | 198 | { |
| 248 | struct hid_device *hdev; | 199 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 249 | struct tpkbd_data_pointer *data_pointer; | 200 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 250 | |||
| 251 | hdev = container_of(dev, struct hid_device, dev); | ||
| 252 | if (hdev == NULL) | ||
| 253 | return -ENODEV; | ||
| 254 | |||
| 255 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 256 | 201 | ||
| 257 | return snprintf(buf, PAGE_SIZE, "%u\n", | 202 | return snprintf(buf, PAGE_SIZE, "%u\n", |
| 258 | data_pointer->sensitivity); | 203 | data_pointer->sensitivity); |
| @@ -263,16 +208,10 @@ static ssize_t pointer_sensitivity_store(struct device *dev, | |||
| 263 | const char *buf, | 208 | const char *buf, |
| 264 | size_t count) | 209 | size_t count) |
| 265 | { | 210 | { |
| 266 | struct hid_device *hdev; | 211 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 267 | struct tpkbd_data_pointer *data_pointer; | 212 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 268 | int value; | 213 | int value; |
| 269 | 214 | ||
| 270 | hdev = container_of(dev, struct hid_device, dev); | ||
| 271 | if (hdev == NULL) | ||
| 272 | return -ENODEV; | ||
| 273 | |||
| 274 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 275 | |||
| 276 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | 215 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) |
| 277 | return -EINVAL; | 216 | return -EINVAL; |
| 278 | 217 | ||
| @@ -286,14 +225,10 @@ static ssize_t pointer_press_speed_show(struct device *dev, | |||
| 286 | struct device_attribute *attr, | 225 | struct device_attribute *attr, |
| 287 | char *buf) | 226 | char *buf) |
| 288 | { | 227 | { |
| 289 | struct hid_device *hdev; | 228 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 290 | struct tpkbd_data_pointer *data_pointer; | 229 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 291 | 230 | ||
| 292 | hdev = container_of(dev, struct hid_device, dev); | 231 | data_pointer = hid_get_drvdata(hdev); |
| 293 | if (hdev == NULL) | ||
| 294 | return -ENODEV; | ||
| 295 | |||
| 296 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 297 | 232 | ||
| 298 | return snprintf(buf, PAGE_SIZE, "%u\n", | 233 | return snprintf(buf, PAGE_SIZE, "%u\n", |
| 299 | data_pointer->press_speed); | 234 | data_pointer->press_speed); |
| @@ -304,16 +239,10 @@ static ssize_t pointer_press_speed_store(struct device *dev, | |||
| 304 | const char *buf, | 239 | const char *buf, |
| 305 | size_t count) | 240 | size_t count) |
| 306 | { | 241 | { |
| 307 | struct hid_device *hdev; | 242 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 308 | struct tpkbd_data_pointer *data_pointer; | 243 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 309 | int value; | 244 | int value; |
| 310 | 245 | ||
| 311 | hdev = container_of(dev, struct hid_device, dev); | ||
| 312 | if (hdev == NULL) | ||
| 313 | return -ENODEV; | ||
| 314 | |||
| 315 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 316 | |||
| 317 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | 246 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) |
| 318 | return -EINVAL; | 247 | return -EINVAL; |
| 319 | 248 | ||
| @@ -370,15 +299,11 @@ static const struct attribute_group tpkbd_attr_group_pointer = { | |||
| 370 | static enum led_brightness tpkbd_led_brightness_get( | 299 | static enum led_brightness tpkbd_led_brightness_get( |
| 371 | struct led_classdev *led_cdev) | 300 | struct led_classdev *led_cdev) |
| 372 | { | 301 | { |
| 373 | struct device *dev; | 302 | struct device *dev = led_cdev->dev->parent; |
| 374 | struct hid_device *hdev; | 303 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 375 | struct tpkbd_data_pointer *data_pointer; | 304 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 376 | int led_nr = 0; | 305 | int led_nr = 0; |
| 377 | 306 | ||
| 378 | dev = led_cdev->dev->parent; | ||
| 379 | hdev = container_of(dev, struct hid_device, dev); | ||
| 380 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 381 | |||
| 382 | if (led_cdev == &data_pointer->led_micmute) | 307 | if (led_cdev == &data_pointer->led_micmute) |
| 383 | led_nr = 1; | 308 | led_nr = 1; |
| 384 | 309 | ||
| @@ -390,16 +315,12 @@ static enum led_brightness tpkbd_led_brightness_get( | |||
| 390 | static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, | 315 | static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, |
| 391 | enum led_brightness value) | 316 | enum led_brightness value) |
| 392 | { | 317 | { |
| 393 | struct device *dev; | 318 | struct device *dev = led_cdev->dev->parent; |
| 394 | struct hid_device *hdev; | 319 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 320 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
| 395 | struct hid_report *report; | 321 | struct hid_report *report; |
| 396 | struct tpkbd_data_pointer *data_pointer; | ||
| 397 | int led_nr = 0; | 322 | int led_nr = 0; |
| 398 | 323 | ||
| 399 | dev = led_cdev->dev->parent; | ||
| 400 | hdev = container_of(dev, struct hid_device, dev); | ||
| 401 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 402 | |||
| 403 | if (led_cdev == &data_pointer->led_micmute) | 324 | if (led_cdev == &data_pointer->led_micmute) |
| 404 | led_nr = 1; | 325 | led_nr = 1; |
| 405 | 326 | ||
| @@ -508,13 +429,11 @@ err_free: | |||
| 508 | 429 | ||
| 509 | static void tpkbd_remove_tp(struct hid_device *hdev) | 430 | static void tpkbd_remove_tp(struct hid_device *hdev) |
| 510 | { | 431 | { |
| 511 | struct tpkbd_data_pointer *data_pointer; | 432 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); |
| 512 | 433 | ||
| 513 | sysfs_remove_group(&hdev->dev.kobj, | 434 | sysfs_remove_group(&hdev->dev.kobj, |
| 514 | &tpkbd_attr_group_pointer); | 435 | &tpkbd_attr_group_pointer); |
| 515 | 436 | ||
| 516 | data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); | ||
| 517 | |||
| 518 | led_classdev_unregister(&data_pointer->led_micmute); | 437 | led_classdev_unregister(&data_pointer->led_micmute); |
| 519 | led_classdev_unregister(&data_pointer->led_mute); | 438 | led_classdev_unregister(&data_pointer->led_mute); |
| 520 | 439 | ||
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index fc37ed6b108c..a2f8e88b9fa2 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | * Copyright (c) 2010 Hendrik Iben | 9 | * Copyright (c) 2010 Hendrik Iben |
| 11 | */ | 10 | */ |
| @@ -109,7 +108,7 @@ static __u8 dfp_rdesc_fixed[] = { | |||
| 109 | static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 108 | static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
| 110 | unsigned int *rsize) | 109 | unsigned int *rsize) |
| 111 | { | 110 | { |
| 112 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); | 111 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); |
| 113 | 112 | ||
| 114 | if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 && | 113 | if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 && |
| 115 | rdesc[84] == 0x8c && rdesc[85] == 0x02) { | 114 | rdesc[84] == 0x8c && rdesc[85] == 0x02) { |
| @@ -278,7 +277,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 278 | 0, 0, 0, 0, 0,183,184,185,186,187, | 277 | 0, 0, 0, 0, 0,183,184,185,186,187, |
| 279 | 188,189,190,191,192,193,194, 0, 0, 0 | 278 | 188,189,190,191,192,193,194, 0, 0, 0 |
| 280 | }; | 279 | }; |
| 281 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); | 280 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); |
| 282 | unsigned int hid = usage->hid; | 281 | unsigned int hid = usage->hid; |
| 283 | 282 | ||
| 284 | if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && | 283 | if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && |
| @@ -319,7 +318,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
| 319 | struct hid_field *field, struct hid_usage *usage, | 318 | struct hid_field *field, struct hid_usage *usage, |
| 320 | unsigned long **bit, int *max) | 319 | unsigned long **bit, int *max) |
| 321 | { | 320 | { |
| 322 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); | 321 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); |
| 323 | 322 | ||
| 324 | if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && | 323 | if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && |
| 325 | (field->flags & HID_MAIN_ITEM_RELATIVE)) | 324 | (field->flags & HID_MAIN_ITEM_RELATIVE)) |
| @@ -335,13 +334,16 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
| 335 | static int lg_event(struct hid_device *hdev, struct hid_field *field, | 334 | static int lg_event(struct hid_device *hdev, struct hid_field *field, |
| 336 | struct hid_usage *usage, __s32 value) | 335 | struct hid_usage *usage, __s32 value) |
| 337 | { | 336 | { |
| 338 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); | 337 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); |
| 339 | 338 | ||
| 340 | if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { | 339 | if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { |
| 341 | input_event(field->hidinput->input, usage->type, usage->code, | 340 | input_event(field->hidinput->input, usage->type, usage->code, |
| 342 | -value); | 341 | -value); |
| 343 | return 1; | 342 | return 1; |
| 344 | } | 343 | } |
| 344 | if (drv_data->quirks & LG_FF4) { | ||
| 345 | return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data); | ||
| 346 | } | ||
| 345 | 347 | ||
| 346 | return 0; | 348 | return 0; |
| 347 | } | 349 | } |
| @@ -358,7 +360,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 358 | return -ENOMEM; | 360 | return -ENOMEM; |
| 359 | } | 361 | } |
| 360 | drv_data->quirks = id->driver_data; | 362 | drv_data->quirks = id->driver_data; |
| 361 | 363 | ||
| 362 | hid_set_drvdata(hdev, (void *)drv_data); | 364 | hid_set_drvdata(hdev, (void *)drv_data); |
| 363 | 365 | ||
| 364 | if (drv_data->quirks & LG_NOGET) | 366 | if (drv_data->quirks & LG_NOGET) |
| @@ -380,7 +382,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 380 | } | 382 | } |
| 381 | 383 | ||
| 382 | /* Setup wireless link with Logitech Wii wheel */ | 384 | /* Setup wireless link with Logitech Wii wheel */ |
| 383 | if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { | 385 | if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { |
| 384 | unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | 386 | unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| 385 | 387 | ||
| 386 | ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); | 388 | ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); |
| @@ -416,7 +418,7 @@ err_free: | |||
| 416 | 418 | ||
| 417 | static void lg_remove(struct hid_device *hdev) | 419 | static void lg_remove(struct hid_device *hdev) |
| 418 | { | 420 | { |
| 419 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); | 421 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); |
| 420 | if (drv_data->quirks & LG_FF4) | 422 | if (drv_data->quirks & LG_FF4) |
| 421 | lg4ff_deinit(hdev); | 423 | lg4ff_deinit(hdev); |
| 422 | 424 | ||
| @@ -476,7 +478,7 @@ static const struct hid_device_id lg_devices[] = { | |||
| 476 | .driver_data = LG_NOGET | LG_FF4 }, | 478 | .driver_data = LG_NOGET | LG_FF4 }, |
| 477 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), | 479 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), |
| 478 | .driver_data = LG_FF4 }, | 480 | .driver_data = LG_FF4 }, |
| 479 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), | 481 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG), |
| 480 | .driver_data = LG_FF }, | 482 | .driver_data = LG_FF }, |
| 481 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), | 483 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), |
| 482 | .driver_data = LG_FF2 }, | 484 | .driver_data = LG_FF2 }, |
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h index d64cf8d2751e..142ce3f5f055 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h | |||
| @@ -25,9 +25,13 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; } | |||
| 25 | #endif | 25 | #endif |
| 26 | 26 | ||
| 27 | #ifdef CONFIG_LOGIWHEELS_FF | 27 | #ifdef CONFIG_LOGIWHEELS_FF |
| 28 | int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, | ||
| 29 | struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data); | ||
| 28 | int lg4ff_init(struct hid_device *hdev); | 30 | int lg4ff_init(struct hid_device *hdev); |
| 29 | int lg4ff_deinit(struct hid_device *hdev); | 31 | int lg4ff_deinit(struct hid_device *hdev); |
| 30 | #else | 32 | #else |
| 33 | static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, | ||
| 34 | struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; } | ||
| 31 | static inline int lg4ff_init(struct hid_device *hdev) { return -1; } | 35 | static inline int lg4ff_init(struct hid_device *hdev) { return -1; } |
| 32 | static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } | 36 | static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } |
| 33 | #endif | 37 | #endif |
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index f3390ee6105c..d7947c701f30 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c | |||
| @@ -43,6 +43,11 @@ | |||
| 43 | #define G27_REV_MAJ 0x12 | 43 | #define G27_REV_MAJ 0x12 |
| 44 | #define G27_REV_MIN 0x38 | 44 | #define G27_REV_MIN 0x38 |
| 45 | 45 | ||
| 46 | #define DFP_X_MIN 0 | ||
| 47 | #define DFP_X_MAX 16383 | ||
| 48 | #define DFP_PEDAL_MIN 0 | ||
| 49 | #define DFP_PEDAL_MAX 255 | ||
| 50 | |||
| 46 | #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) | 51 | #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) |
| 47 | 52 | ||
| 48 | static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); | 53 | static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); |
| @@ -53,6 +58,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at | |||
| 53 | static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); | 58 | static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); |
| 54 | 59 | ||
| 55 | struct lg4ff_device_entry { | 60 | struct lg4ff_device_entry { |
| 61 | __u32 product_id; | ||
| 56 | __u16 range; | 62 | __u16 range; |
| 57 | __u16 min_range; | 63 | __u16 min_range; |
| 58 | __u16 max_range; | 64 | __u16 max_range; |
| @@ -129,26 +135,77 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = { | |||
| 129 | {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ | 135 | {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ |
| 130 | }; | 136 | }; |
| 131 | 137 | ||
| 138 | /* Recalculates X axis value accordingly to currently selected range */ | ||
| 139 | static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range) | ||
| 140 | { | ||
| 141 | __u16 max_range; | ||
| 142 | __s32 new_value; | ||
| 143 | |||
| 144 | if (range == 900) | ||
| 145 | return value; | ||
| 146 | else if (range == 200) | ||
| 147 | return value; | ||
| 148 | else if (range < 200) | ||
| 149 | max_range = 200; | ||
| 150 | else | ||
| 151 | max_range = 900; | ||
| 152 | |||
| 153 | new_value = 8192 + mult_frac(value - 8192, max_range, range); | ||
| 154 | if (new_value < 0) | ||
| 155 | return 0; | ||
| 156 | else if (new_value > 16383) | ||
| 157 | return 16383; | ||
| 158 | else | ||
| 159 | return new_value; | ||
| 160 | } | ||
| 161 | |||
| 162 | int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, | ||
| 163 | struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) | ||
| 164 | { | ||
| 165 | struct lg4ff_device_entry *entry = drv_data->device_props; | ||
| 166 | __s32 new_value = 0; | ||
| 167 | |||
| 168 | if (!entry) { | ||
| 169 | hid_err(hid, "Device properties not found"); | ||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | switch (entry->product_id) { | ||
| 174 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | ||
| 175 | switch (usage->code) { | ||
| 176 | case ABS_X: | ||
| 177 | new_value = lg4ff_adjust_dfp_x_axis(value, entry->range); | ||
| 178 | input_event(field->hidinput->input, usage->type, usage->code, new_value); | ||
| 179 | return 1; | ||
| 180 | default: | ||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | default: | ||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 132 | static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect) | 188 | static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect) |
| 133 | { | 189 | { |
| 134 | struct hid_device *hid = input_get_drvdata(dev); | 190 | struct hid_device *hid = input_get_drvdata(dev); |
| 135 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 191 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 136 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 192 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 193 | __s32 *value = report->field[0]->value; | ||
| 137 | int x; | 194 | int x; |
| 138 | 195 | ||
| 139 | #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff | 196 | #define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0) |
| 140 | 197 | ||
| 141 | switch (effect->type) { | 198 | switch (effect->type) { |
| 142 | case FF_CONSTANT: | 199 | case FF_CONSTANT: |
| 143 | x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ | 200 | x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ |
| 144 | CLAMP(x); | 201 | CLAMP(x); |
| 145 | report->field[0]->value[0] = 0x11; /* Slot 1 */ | 202 | value[0] = 0x11; /* Slot 1 */ |
| 146 | report->field[0]->value[1] = 0x08; | 203 | value[1] = 0x08; |
| 147 | report->field[0]->value[2] = x; | 204 | value[2] = x; |
| 148 | report->field[0]->value[3] = 0x80; | 205 | value[3] = 0x80; |
| 149 | report->field[0]->value[4] = 0x00; | 206 | value[4] = 0x00; |
| 150 | report->field[0]->value[5] = 0x00; | 207 | value[5] = 0x00; |
| 151 | report->field[0]->value[6] = 0x00; | 208 | value[6] = 0x00; |
| 152 | 209 | ||
| 153 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 210 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 154 | break; | 211 | break; |
| @@ -163,14 +220,15 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud | |||
| 163 | struct hid_device *hid = input_get_drvdata(dev); | 220 | struct hid_device *hid = input_get_drvdata(dev); |
| 164 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 221 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 165 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 222 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 223 | __s32 *value = report->field[0]->value; | ||
| 166 | 224 | ||
| 167 | report->field[0]->value[0] = 0xfe; | 225 | value[0] = 0xfe; |
| 168 | report->field[0]->value[1] = 0x0d; | 226 | value[1] = 0x0d; |
| 169 | report->field[0]->value[2] = magnitude >> 13; | 227 | value[2] = magnitude >> 13; |
| 170 | report->field[0]->value[3] = magnitude >> 13; | 228 | value[3] = magnitude >> 13; |
| 171 | report->field[0]->value[4] = magnitude >> 8; | 229 | value[4] = magnitude >> 8; |
| 172 | report->field[0]->value[5] = 0x00; | 230 | value[5] = 0x00; |
| 173 | report->field[0]->value[6] = 0x00; | 231 | value[6] = 0x00; |
| 174 | 232 | ||
| 175 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 233 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 176 | } | 234 | } |
| @@ -181,16 +239,16 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude) | |||
| 181 | struct hid_device *hid = input_get_drvdata(dev); | 239 | struct hid_device *hid = input_get_drvdata(dev); |
| 182 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 240 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 183 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 241 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 242 | __s32 *value = report->field[0]->value; | ||
| 184 | magnitude = magnitude * 90 / 65535; | 243 | magnitude = magnitude * 90 / 65535; |
| 185 | |||
| 186 | 244 | ||
| 187 | report->field[0]->value[0] = 0xfe; | 245 | value[0] = 0xfe; |
| 188 | report->field[0]->value[1] = 0x03; | 246 | value[1] = 0x03; |
| 189 | report->field[0]->value[2] = magnitude >> 14; | 247 | value[2] = magnitude >> 14; |
| 190 | report->field[0]->value[3] = magnitude >> 14; | 248 | value[3] = magnitude >> 14; |
| 191 | report->field[0]->value[4] = magnitude; | 249 | value[4] = magnitude; |
| 192 | report->field[0]->value[5] = 0x00; | 250 | value[5] = 0x00; |
| 193 | report->field[0]->value[6] = 0x00; | 251 | value[6] = 0x00; |
| 194 | 252 | ||
| 195 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 253 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 196 | } | 254 | } |
| @@ -200,15 +258,17 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range) | |||
| 200 | { | 258 | { |
| 201 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 259 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 202 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 260 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 261 | __s32 *value = report->field[0]->value; | ||
| 262 | |||
| 203 | dbg_hid("G25/G27/DFGT: setting range to %u\n", range); | 263 | dbg_hid("G25/G27/DFGT: setting range to %u\n", range); |
| 204 | 264 | ||
| 205 | report->field[0]->value[0] = 0xf8; | 265 | value[0] = 0xf8; |
| 206 | report->field[0]->value[1] = 0x81; | 266 | value[1] = 0x81; |
| 207 | report->field[0]->value[2] = range & 0x00ff; | 267 | value[2] = range & 0x00ff; |
| 208 | report->field[0]->value[3] = (range & 0xff00) >> 8; | 268 | value[3] = (range & 0xff00) >> 8; |
| 209 | report->field[0]->value[4] = 0x00; | 269 | value[4] = 0x00; |
| 210 | report->field[0]->value[5] = 0x00; | 270 | value[5] = 0x00; |
| 211 | report->field[0]->value[6] = 0x00; | 271 | value[6] = 0x00; |
| 212 | 272 | ||
| 213 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 273 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 214 | } | 274 | } |
| @@ -219,16 +279,18 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) | |||
| 219 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 279 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 220 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 280 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 221 | int start_left, start_right, full_range; | 281 | int start_left, start_right, full_range; |
| 282 | __s32 *value = report->field[0]->value; | ||
| 283 | |||
| 222 | dbg_hid("Driving Force Pro: setting range to %u\n", range); | 284 | dbg_hid("Driving Force Pro: setting range to %u\n", range); |
| 223 | 285 | ||
| 224 | /* Prepare "coarse" limit command */ | 286 | /* Prepare "coarse" limit command */ |
| 225 | report->field[0]->value[0] = 0xf8; | 287 | value[0] = 0xf8; |
| 226 | report->field[0]->value[1] = 0x00; /* Set later */ | 288 | value[1] = 0x00; /* Set later */ |
| 227 | report->field[0]->value[2] = 0x00; | 289 | value[2] = 0x00; |
| 228 | report->field[0]->value[3] = 0x00; | 290 | value[3] = 0x00; |
| 229 | report->field[0]->value[4] = 0x00; | 291 | value[4] = 0x00; |
| 230 | report->field[0]->value[5] = 0x00; | 292 | value[5] = 0x00; |
| 231 | report->field[0]->value[6] = 0x00; | 293 | value[6] = 0x00; |
| 232 | 294 | ||
| 233 | if (range > 200) { | 295 | if (range > 200) { |
| 234 | report->field[0]->value[1] = 0x03; | 296 | report->field[0]->value[1] = 0x03; |
| @@ -240,13 +302,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) | |||
| 240 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 302 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 241 | 303 | ||
| 242 | /* Prepare "fine" limit command */ | 304 | /* Prepare "fine" limit command */ |
| 243 | report->field[0]->value[0] = 0x81; | 305 | value[0] = 0x81; |
| 244 | report->field[0]->value[1] = 0x0b; | 306 | value[1] = 0x0b; |
| 245 | report->field[0]->value[2] = 0x00; | 307 | value[2] = 0x00; |
| 246 | report->field[0]->value[3] = 0x00; | 308 | value[3] = 0x00; |
| 247 | report->field[0]->value[4] = 0x00; | 309 | value[4] = 0x00; |
| 248 | report->field[0]->value[5] = 0x00; | 310 | value[5] = 0x00; |
| 249 | report->field[0]->value[6] = 0x00; | 311 | value[6] = 0x00; |
| 250 | 312 | ||
| 251 | if (range == 200 || range == 900) { /* Do not apply any fine limit */ | 313 | if (range == 200 || range == 900) { /* Do not apply any fine limit */ |
| 252 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 314 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| @@ -257,11 +319,11 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) | |||
| 257 | start_left = (((full_range - range + 1) * 2047) / full_range); | 319 | start_left = (((full_range - range + 1) * 2047) / full_range); |
| 258 | start_right = 0xfff - start_left; | 320 | start_right = 0xfff - start_left; |
| 259 | 321 | ||
| 260 | report->field[0]->value[2] = start_left >> 4; | 322 | value[2] = start_left >> 4; |
| 261 | report->field[0]->value[3] = start_right >> 4; | 323 | value[3] = start_right >> 4; |
| 262 | report->field[0]->value[4] = 0xff; | 324 | value[4] = 0xff; |
| 263 | report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); | 325 | value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); |
| 264 | report->field[0]->value[6] = 0xff; | 326 | value[6] = 0xff; |
| 265 | 327 | ||
| 266 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 328 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 267 | } | 329 | } |
| @@ -344,14 +406,15 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) | |||
| 344 | { | 406 | { |
| 345 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | 407 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
| 346 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | 408 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); |
| 347 | 409 | __s32 *value = report->field[0]->value; | |
| 348 | report->field[0]->value[0] = 0xf8; | 410 | |
| 349 | report->field[0]->value[1] = 0x12; | 411 | value[0] = 0xf8; |
| 350 | report->field[0]->value[2] = leds; | 412 | value[1] = 0x12; |
| 351 | report->field[0]->value[3] = 0x00; | 413 | value[2] = leds; |
| 352 | report->field[0]->value[4] = 0x00; | 414 | value[3] = 0x00; |
| 353 | report->field[0]->value[5] = 0x00; | 415 | value[4] = 0x00; |
| 354 | report->field[0]->value[6] = 0x00; | 416 | value[5] = 0x00; |
| 417 | value[6] = 0x00; | ||
| 355 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 418 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
| 356 | } | 419 | } |
| 357 | 420 | ||
| @@ -360,7 +423,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev, | |||
| 360 | { | 423 | { |
| 361 | struct device *dev = led_cdev->dev->parent; | 424 | struct device *dev = led_cdev->dev->parent; |
| 362 | struct hid_device *hid = container_of(dev, struct hid_device, dev); | 425 | struct hid_device *hid = container_of(dev, struct hid_device, dev); |
| 363 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); | 426 | struct lg_drv_data *drv_data = hid_get_drvdata(hid); |
| 364 | struct lg4ff_device_entry *entry; | 427 | struct lg4ff_device_entry *entry; |
| 365 | int i, state = 0; | 428 | int i, state = 0; |
| 366 | 429 | ||
| @@ -395,7 +458,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde | |||
| 395 | { | 458 | { |
| 396 | struct device *dev = led_cdev->dev->parent; | 459 | struct device *dev = led_cdev->dev->parent; |
| 397 | struct hid_device *hid = container_of(dev, struct hid_device, dev); | 460 | struct hid_device *hid = container_of(dev, struct hid_device, dev); |
| 398 | struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); | 461 | struct lg_drv_data *drv_data = hid_get_drvdata(hid); |
| 399 | struct lg4ff_device_entry *entry; | 462 | struct lg4ff_device_entry *entry; |
| 400 | int i, value = 0; | 463 | int i, value = 0; |
| 401 | 464 | ||
| @@ -501,7 +564,7 @@ int lg4ff_init(struct hid_device *hid) | |||
| 501 | /* Check if autocentering is available and | 564 | /* Check if autocentering is available and |
| 502 | * set the centering force to zero by default */ | 565 | * set the centering force to zero by default */ |
| 503 | if (test_bit(FF_AUTOCENTER, dev->ffbit)) { | 566 | if (test_bit(FF_AUTOCENTER, dev->ffbit)) { |
| 504 | if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ | 567 | if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ |
| 505 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; | 568 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; |
| 506 | else | 569 | else |
| 507 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; | 570 | dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; |
| @@ -524,6 +587,7 @@ int lg4ff_init(struct hid_device *hid) | |||
| 524 | } | 587 | } |
| 525 | drv_data->device_props = entry; | 588 | drv_data->device_props = entry; |
| 526 | 589 | ||
| 590 | entry->product_id = lg4ff_devices[i].product_id; | ||
| 527 | entry->min_range = lg4ff_devices[i].min_range; | 591 | entry->min_range = lg4ff_devices[i].min_range; |
| 528 | entry->max_range = lg4ff_devices[i].max_range; | 592 | entry->max_range = lg4ff_devices[i].max_range; |
| 529 | entry->set_range = lg4ff_devices[i].set_range; | 593 | entry->set_range = lg4ff_devices[i].set_range; |
| @@ -534,6 +598,18 @@ int lg4ff_init(struct hid_device *hid) | |||
| 534 | return error; | 598 | return error; |
| 535 | dbg_hid("sysfs interface created\n"); | 599 | dbg_hid("sysfs interface created\n"); |
| 536 | 600 | ||
| 601 | /* Set default axes parameters */ | ||
| 602 | switch (lg4ff_devices[i].product_id) { | ||
| 603 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | ||
| 604 | dbg_hid("Setting axes parameters for Driving Force Pro\n"); | ||
| 605 | input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0); | ||
| 606 | input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); | ||
| 607 | input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); | ||
| 608 | break; | ||
| 609 | default: | ||
| 610 | break; | ||
| 611 | } | ||
| 612 | |||
| 537 | /* Set the maximum range to start with */ | 613 | /* Set the maximum range to start with */ |
| 538 | entry->range = entry->max_range; | 614 | entry->range = entry->max_range; |
| 539 | if (entry->set_range != NULL) | 615 | if (entry->set_range != NULL) |
| @@ -594,6 +670,8 @@ out: | |||
| 594 | return 0; | 670 | return 0; |
| 595 | } | 671 | } |
| 596 | 672 | ||
| 673 | |||
| 674 | |||
| 597 | int lg4ff_deinit(struct hid_device *hid) | 675 | int lg4ff_deinit(struct hid_device *hid) |
| 598 | { | 676 | { |
| 599 | struct lg4ff_device_entry *entry; | 677 | struct lg4ff_device_entry *entry; |
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 73647266daad..25ddf3e3aec6 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c | |||
| @@ -392,7 +392,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd | |||
| 392 | 392 | ||
| 393 | __set_bit(EV_ABS, input->evbit); | 393 | __set_bit(EV_ABS, input->evbit); |
| 394 | 394 | ||
| 395 | error = input_mt_init_slots(input, 16); | 395 | error = input_mt_init_slots(input, 16, 0); |
| 396 | if (error) | 396 | if (error) |
| 397 | return error; | 397 | return error; |
| 398 | input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, | 398 | input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, |
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index e5c699b6c6f3..3acdcfcc17df 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c index dedf757781ae..cd3643e06fa6 100644 --- a/drivers/hid/hid-monterey.c +++ b/drivers/hid/hid-monterey.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 59c8b5c1d2de..3eb02b94fc87 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c | |||
| @@ -51,12 +51,12 @@ MODULE_LICENSE("GPL"); | |||
| 51 | #define MT_QUIRK_VALID_IS_INRANGE (1 << 5) | 51 | #define MT_QUIRK_VALID_IS_INRANGE (1 << 5) |
| 52 | #define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) | 52 | #define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) |
| 53 | #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) | 53 | #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) |
| 54 | #define MT_QUIRK_NO_AREA (1 << 9) | ||
| 54 | 55 | ||
| 55 | struct mt_slot { | 56 | struct mt_slot { |
| 56 | __s32 x, y, p, w, h; | 57 | __s32 x, y, p, w, h; |
| 57 | __s32 contactid; /* the device ContactID assigned to this slot */ | 58 | __s32 contactid; /* the device ContactID assigned to this slot */ |
| 58 | bool touch_state; /* is the touch valid? */ | 59 | bool touch_state; /* is the touch valid? */ |
| 59 | bool seen_in_this_frame;/* has this slot been updated */ | ||
| 60 | }; | 60 | }; |
| 61 | 61 | ||
| 62 | struct mt_class { | 62 | struct mt_class { |
| @@ -92,8 +92,9 @@ struct mt_device { | |||
| 92 | __u8 touches_by_report; /* how many touches are present in one report: | 92 | __u8 touches_by_report; /* how many touches are present in one report: |
| 93 | * 1 means we should use a serial protocol | 93 | * 1 means we should use a serial protocol |
| 94 | * > 1 means hybrid (multitouch) protocol */ | 94 | * > 1 means hybrid (multitouch) protocol */ |
| 95 | bool serial_maybe; /* need to check for serial protocol */ | ||
| 95 | bool curvalid; /* is the current contact valid? */ | 96 | bool curvalid; /* is the current contact valid? */ |
| 96 | struct mt_slot *slots; | 97 | unsigned mt_flags; /* flags to pass to input-mt */ |
| 97 | }; | 98 | }; |
| 98 | 99 | ||
| 99 | /* classes of device behavior */ | 100 | /* classes of device behavior */ |
| @@ -115,6 +116,9 @@ struct mt_device { | |||
| 115 | #define MT_CLS_EGALAX_SERIAL 0x0104 | 116 | #define MT_CLS_EGALAX_SERIAL 0x0104 |
| 116 | #define MT_CLS_TOPSEED 0x0105 | 117 | #define MT_CLS_TOPSEED 0x0105 |
| 117 | #define MT_CLS_PANASONIC 0x0106 | 118 | #define MT_CLS_PANASONIC 0x0106 |
| 119 | #define MT_CLS_FLATFROG 0x0107 | ||
| 120 | #define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108 | ||
| 121 | #define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109 | ||
| 118 | 122 | ||
| 119 | #define MT_DEFAULT_MAXCONTACT 10 | 123 | #define MT_DEFAULT_MAXCONTACT 10 |
| 120 | 124 | ||
| @@ -134,25 +138,6 @@ static int cypress_compute_slot(struct mt_device *td) | |||
| 134 | return -1; | 138 | return -1; |
| 135 | } | 139 | } |
| 136 | 140 | ||
| 137 | static int find_slot_from_contactid(struct mt_device *td) | ||
| 138 | { | ||
| 139 | int i; | ||
| 140 | for (i = 0; i < td->maxcontacts; ++i) { | ||
| 141 | if (td->slots[i].contactid == td->curdata.contactid && | ||
| 142 | td->slots[i].touch_state) | ||
| 143 | return i; | ||
| 144 | } | ||
| 145 | for (i = 0; i < td->maxcontacts; ++i) { | ||
| 146 | if (!td->slots[i].seen_in_this_frame && | ||
| 147 | !td->slots[i].touch_state) | ||
| 148 | return i; | ||
| 149 | } | ||
| 150 | /* should not occurs. If this happens that means | ||
| 151 | * that the device sent more touches that it says | ||
| 152 | * in the report descriptor. It is ignored then. */ | ||
| 153 | return -1; | ||
| 154 | } | ||
| 155 | |||
| 156 | static struct mt_class mt_classes[] = { | 141 | static struct mt_class mt_classes[] = { |
| 157 | { .name = MT_CLS_DEFAULT, | 142 | { .name = MT_CLS_DEFAULT, |
| 158 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, | 143 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, |
| @@ -190,7 +175,9 @@ static struct mt_class mt_classes[] = { | |||
| 190 | MT_QUIRK_SLOT_IS_CONTACTID, | 175 | MT_QUIRK_SLOT_IS_CONTACTID, |
| 191 | .sn_move = 2048, | 176 | .sn_move = 2048, |
| 192 | .sn_width = 128, | 177 | .sn_width = 128, |
| 193 | .sn_height = 128 }, | 178 | .sn_height = 128, |
| 179 | .maxcontacts = 60, | ||
| 180 | }, | ||
| 194 | { .name = MT_CLS_CYPRESS, | 181 | { .name = MT_CLS_CYPRESS, |
| 195 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | | 182 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | |
| 196 | MT_QUIRK_CYPRESS, | 183 | MT_QUIRK_CYPRESS, |
| @@ -215,7 +202,24 @@ static struct mt_class mt_classes[] = { | |||
| 215 | { .name = MT_CLS_PANASONIC, | 202 | { .name = MT_CLS_PANASONIC, |
| 216 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP, | 203 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP, |
| 217 | .maxcontacts = 4 }, | 204 | .maxcontacts = 4 }, |
| 205 | { .name = MT_CLS_GENERALTOUCH_TWOFINGERS, | ||
| 206 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | | ||
| 207 | MT_QUIRK_VALID_IS_INRANGE | | ||
| 208 | MT_QUIRK_SLOT_IS_CONTACTNUMBER, | ||
| 209 | .maxcontacts = 2 | ||
| 210 | }, | ||
| 211 | { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, | ||
| 212 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | | ||
| 213 | MT_QUIRK_SLOT_IS_CONTACTNUMBER, | ||
| 214 | .maxcontacts = 10 | ||
| 215 | }, | ||
| 218 | 216 | ||
| 217 | { .name = MT_CLS_FLATFROG, | ||
| 218 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | | ||
| 219 | MT_QUIRK_NO_AREA, | ||
| 220 | .sn_move = 2048, | ||
| 221 | .maxcontacts = 40, | ||
| 222 | }, | ||
| 219 | { } | 223 | { } |
| 220 | }; | 224 | }; |
| 221 | 225 | ||
| @@ -319,24 +323,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 319 | * We need to ignore fields that belong to other collections | 323 | * We need to ignore fields that belong to other collections |
| 320 | * such as Mouse that might have the same GenericDesktop usages. */ | 324 | * such as Mouse that might have the same GenericDesktop usages. */ |
| 321 | if (field->application == HID_DG_TOUCHSCREEN) | 325 | if (field->application == HID_DG_TOUCHSCREEN) |
| 322 | set_bit(INPUT_PROP_DIRECT, hi->input->propbit); | 326 | td->mt_flags |= INPUT_MT_DIRECT; |
| 323 | else if (field->application != HID_DG_TOUCHPAD) | 327 | else if (field->application != HID_DG_TOUCHPAD) |
| 324 | return 0; | 328 | return 0; |
| 325 | 329 | ||
| 326 | /* In case of an indirect device (touchpad), we need to add | 330 | /* |
| 327 | * specific BTN_TOOL_* to be handled by the synaptics xorg | 331 | * Model touchscreens providing buttons as touchpads. |
| 328 | * driver. | ||
| 329 | * We also consider that touchscreens providing buttons are touchpads. | ||
| 330 | */ | 332 | */ |
| 331 | if (field->application == HID_DG_TOUCHPAD || | 333 | if (field->application == HID_DG_TOUCHPAD || |
| 332 | (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON || | 334 | (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) |
| 333 | cls->is_indirect) { | 335 | td->mt_flags |= INPUT_MT_POINTER; |
| 334 | set_bit(INPUT_PROP_POINTER, hi->input->propbit); | ||
| 335 | set_bit(BTN_TOOL_FINGER, hi->input->keybit); | ||
| 336 | set_bit(BTN_TOOL_DOUBLETAP, hi->input->keybit); | ||
| 337 | set_bit(BTN_TOOL_TRIPLETAP, hi->input->keybit); | ||
| 338 | set_bit(BTN_TOOL_QUADTAP, hi->input->keybit); | ||
| 339 | } | ||
| 340 | 336 | ||
| 341 | /* eGalax devices provide a Digitizer.Stylus input which overrides | 337 | /* eGalax devices provide a Digitizer.Stylus input which overrides |
| 342 | * the correct Digitizers.Finger X/Y ranges. | 338 | * the correct Digitizers.Finger X/Y ranges. |
| @@ -353,8 +349,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 353 | EV_ABS, ABS_MT_POSITION_X); | 349 | EV_ABS, ABS_MT_POSITION_X); |
| 354 | set_abs(hi->input, ABS_MT_POSITION_X, field, | 350 | set_abs(hi->input, ABS_MT_POSITION_X, field, |
| 355 | cls->sn_move); | 351 | cls->sn_move); |
| 356 | /* touchscreen emulation */ | ||
| 357 | set_abs(hi->input, ABS_X, field, cls->sn_move); | ||
| 358 | mt_store_field(usage, td, hi); | 352 | mt_store_field(usage, td, hi); |
| 359 | td->last_field_index = field->index; | 353 | td->last_field_index = field->index; |
| 360 | return 1; | 354 | return 1; |
| @@ -363,8 +357,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 363 | EV_ABS, ABS_MT_POSITION_Y); | 357 | EV_ABS, ABS_MT_POSITION_Y); |
| 364 | set_abs(hi->input, ABS_MT_POSITION_Y, field, | 358 | set_abs(hi->input, ABS_MT_POSITION_Y, field, |
| 365 | cls->sn_move); | 359 | cls->sn_move); |
| 366 | /* touchscreen emulation */ | ||
| 367 | set_abs(hi->input, ABS_Y, field, cls->sn_move); | ||
| 368 | mt_store_field(usage, td, hi); | 360 | mt_store_field(usage, td, hi); |
| 369 | td->last_field_index = field->index; | 361 | td->last_field_index = field->index; |
| 370 | return 1; | 362 | return 1; |
| @@ -388,9 +380,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 388 | td->last_field_index = field->index; | 380 | td->last_field_index = field->index; |
| 389 | return 1; | 381 | return 1; |
| 390 | case HID_DG_CONTACTID: | 382 | case HID_DG_CONTACTID: |
| 391 | if (!td->maxcontacts) | ||
| 392 | td->maxcontacts = MT_DEFAULT_MAXCONTACT; | ||
| 393 | input_mt_init_slots(hi->input, td->maxcontacts); | ||
| 394 | mt_store_field(usage, td, hi); | 383 | mt_store_field(usage, td, hi); |
| 395 | td->last_field_index = field->index; | 384 | td->last_field_index = field->index; |
| 396 | td->touches_by_report++; | 385 | td->touches_by_report++; |
| @@ -398,18 +387,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 398 | case HID_DG_WIDTH: | 387 | case HID_DG_WIDTH: |
| 399 | hid_map_usage(hi, usage, bit, max, | 388 | hid_map_usage(hi, usage, bit, max, |
| 400 | EV_ABS, ABS_MT_TOUCH_MAJOR); | 389 | EV_ABS, ABS_MT_TOUCH_MAJOR); |
| 401 | set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, | 390 | if (!(cls->quirks & MT_QUIRK_NO_AREA)) |
| 402 | cls->sn_width); | 391 | set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, |
| 392 | cls->sn_width); | ||
| 403 | mt_store_field(usage, td, hi); | 393 | mt_store_field(usage, td, hi); |
| 404 | td->last_field_index = field->index; | 394 | td->last_field_index = field->index; |
| 405 | return 1; | 395 | return 1; |
| 406 | case HID_DG_HEIGHT: | 396 | case HID_DG_HEIGHT: |
| 407 | hid_map_usage(hi, usage, bit, max, | 397 | hid_map_usage(hi, usage, bit, max, |
| 408 | EV_ABS, ABS_MT_TOUCH_MINOR); | 398 | EV_ABS, ABS_MT_TOUCH_MINOR); |
| 409 | set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, | 399 | if (!(cls->quirks & MT_QUIRK_NO_AREA)) { |
| 410 | cls->sn_height); | 400 | set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, |
| 411 | input_set_abs_params(hi->input, | 401 | cls->sn_height); |
| 402 | input_set_abs_params(hi->input, | ||
| 412 | ABS_MT_ORIENTATION, 0, 1, 0, 0); | 403 | ABS_MT_ORIENTATION, 0, 1, 0, 0); |
| 404 | } | ||
| 413 | mt_store_field(usage, td, hi); | 405 | mt_store_field(usage, td, hi); |
| 414 | td->last_field_index = field->index; | 406 | td->last_field_index = field->index; |
| 415 | return 1; | 407 | return 1; |
| @@ -418,9 +410,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 418 | EV_ABS, ABS_MT_PRESSURE); | 410 | EV_ABS, ABS_MT_PRESSURE); |
| 419 | set_abs(hi->input, ABS_MT_PRESSURE, field, | 411 | set_abs(hi->input, ABS_MT_PRESSURE, field, |
| 420 | cls->sn_pressure); | 412 | cls->sn_pressure); |
| 421 | /* touchscreen emulation */ | ||
| 422 | set_abs(hi->input, ABS_PRESSURE, field, | ||
| 423 | cls->sn_pressure); | ||
| 424 | mt_store_field(usage, td, hi); | 413 | mt_store_field(usage, td, hi); |
| 425 | td->last_field_index = field->index; | 414 | td->last_field_index = field->index; |
| 426 | return 1; | 415 | return 1; |
| @@ -464,7 +453,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
| 464 | return -1; | 453 | return -1; |
| 465 | } | 454 | } |
| 466 | 455 | ||
| 467 | static int mt_compute_slot(struct mt_device *td) | 456 | static int mt_compute_slot(struct mt_device *td, struct input_dev *input) |
| 468 | { | 457 | { |
| 469 | __s32 quirks = td->mtclass.quirks; | 458 | __s32 quirks = td->mtclass.quirks; |
| 470 | 459 | ||
| @@ -480,42 +469,23 @@ static int mt_compute_slot(struct mt_device *td) | |||
| 480 | if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE) | 469 | if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE) |
| 481 | return td->curdata.contactid - 1; | 470 | return td->curdata.contactid - 1; |
| 482 | 471 | ||
| 483 | return find_slot_from_contactid(td); | 472 | return input_mt_get_slot_by_key(input, td->curdata.contactid); |
| 484 | } | 473 | } |
| 485 | 474 | ||
| 486 | /* | 475 | /* |
| 487 | * this function is called when a whole contact has been processed, | 476 | * this function is called when a whole contact has been processed, |
| 488 | * so that it can assign it to a slot and store the data there | 477 | * so that it can assign it to a slot and store the data there |
| 489 | */ | 478 | */ |
| 490 | static void mt_complete_slot(struct mt_device *td) | 479 | static void mt_complete_slot(struct mt_device *td, struct input_dev *input) |
| 491 | { | 480 | { |
| 492 | td->curdata.seen_in_this_frame = true; | ||
| 493 | if (td->curvalid) { | 481 | if (td->curvalid) { |
| 494 | int slotnum = mt_compute_slot(td); | 482 | int slotnum = mt_compute_slot(td, input); |
| 495 | 483 | struct mt_slot *s = &td->curdata; | |
| 496 | if (slotnum >= 0 && slotnum < td->maxcontacts) | ||
| 497 | td->slots[slotnum] = td->curdata; | ||
| 498 | } | ||
| 499 | td->num_received++; | ||
| 500 | } | ||
| 501 | 484 | ||
| 485 | if (slotnum < 0 || slotnum >= td->maxcontacts) | ||
| 486 | return; | ||
| 502 | 487 | ||
| 503 | /* | 488 | input_mt_slot(input, slotnum); |
| 504 | * this function is called when a whole packet has been received and processed, | ||
| 505 | * so that it can decide what to send to the input layer. | ||
| 506 | */ | ||
| 507 | static void mt_emit_event(struct mt_device *td, struct input_dev *input) | ||
| 508 | { | ||
| 509 | int i; | ||
| 510 | |||
| 511 | for (i = 0; i < td->maxcontacts; ++i) { | ||
| 512 | struct mt_slot *s = &(td->slots[i]); | ||
| 513 | if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && | ||
| 514 | !s->seen_in_this_frame) { | ||
| 515 | s->touch_state = false; | ||
| 516 | } | ||
| 517 | |||
| 518 | input_mt_slot(input, i); | ||
| 519 | input_mt_report_slot_state(input, MT_TOOL_FINGER, | 489 | input_mt_report_slot_state(input, MT_TOOL_FINGER, |
| 520 | s->touch_state); | 490 | s->touch_state); |
| 521 | if (s->touch_state) { | 491 | if (s->touch_state) { |
| @@ -532,24 +502,29 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input) | |||
| 532 | input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); | 502 | input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); |
| 533 | input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); | 503 | input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); |
| 534 | } | 504 | } |
| 535 | s->seen_in_this_frame = false; | ||
| 536 | |||
| 537 | } | 505 | } |
| 538 | 506 | ||
| 539 | input_mt_report_pointer_emulation(input, true); | 507 | td->num_received++; |
| 508 | } | ||
| 509 | |||
| 510 | /* | ||
| 511 | * this function is called when a whole packet has been received and processed, | ||
| 512 | * so that it can decide what to send to the input layer. | ||
| 513 | */ | ||
| 514 | static void mt_sync_frame(struct mt_device *td, struct input_dev *input) | ||
| 515 | { | ||
| 516 | input_mt_sync_frame(input); | ||
| 540 | input_sync(input); | 517 | input_sync(input); |
| 541 | td->num_received = 0; | 518 | td->num_received = 0; |
| 542 | } | 519 | } |
| 543 | 520 | ||
| 544 | |||
| 545 | |||
| 546 | static int mt_event(struct hid_device *hid, struct hid_field *field, | 521 | static int mt_event(struct hid_device *hid, struct hid_field *field, |
| 547 | struct hid_usage *usage, __s32 value) | 522 | struct hid_usage *usage, __s32 value) |
| 548 | { | 523 | { |
| 549 | struct mt_device *td = hid_get_drvdata(hid); | 524 | struct mt_device *td = hid_get_drvdata(hid); |
| 550 | __s32 quirks = td->mtclass.quirks; | 525 | __s32 quirks = td->mtclass.quirks; |
| 551 | 526 | ||
| 552 | if (hid->claimed & HID_CLAIMED_INPUT && td->slots) { | 527 | if (hid->claimed & HID_CLAIMED_INPUT) { |
| 553 | switch (usage->hid) { | 528 | switch (usage->hid) { |
| 554 | case HID_DG_INRANGE: | 529 | case HID_DG_INRANGE: |
| 555 | if (quirks & MT_QUIRK_ALWAYS_VALID) | 530 | if (quirks & MT_QUIRK_ALWAYS_VALID) |
| @@ -602,11 +577,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, | |||
| 602 | } | 577 | } |
| 603 | 578 | ||
| 604 | if (usage->hid == td->last_slot_field) | 579 | if (usage->hid == td->last_slot_field) |
| 605 | mt_complete_slot(td); | 580 | mt_complete_slot(td, field->hidinput->input); |
| 606 | 581 | ||
| 607 | if (field->index == td->last_field_index | 582 | if (field->index == td->last_field_index |
| 608 | && td->num_received >= td->num_expected) | 583 | && td->num_received >= td->num_expected) |
| 609 | mt_emit_event(td, field->hidinput->input); | 584 | mt_sync_frame(td, field->hidinput->input); |
| 610 | 585 | ||
| 611 | } | 586 | } |
| 612 | 587 | ||
| @@ -685,18 +660,45 @@ static void mt_post_parse(struct mt_device *td) | |||
| 685 | } | 660 | } |
| 686 | } | 661 | } |
| 687 | 662 | ||
| 663 | static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) | ||
| 664 | |||
| 665 | { | ||
| 666 | struct mt_device *td = hid_get_drvdata(hdev); | ||
| 667 | struct mt_class *cls = &td->mtclass; | ||
| 668 | struct input_dev *input = hi->input; | ||
| 669 | |||
| 670 | /* Only initialize slots for MT input devices */ | ||
| 671 | if (!test_bit(ABS_MT_POSITION_X, input->absbit)) | ||
| 672 | return; | ||
| 673 | |||
| 674 | if (!td->maxcontacts) | ||
| 675 | td->maxcontacts = MT_DEFAULT_MAXCONTACT; | ||
| 676 | |||
| 677 | mt_post_parse(td); | ||
| 678 | if (td->serial_maybe) | ||
| 679 | mt_post_parse_default_settings(td); | ||
| 680 | |||
| 681 | if (cls->is_indirect) | ||
| 682 | td->mt_flags |= INPUT_MT_POINTER; | ||
| 683 | |||
| 684 | if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) | ||
| 685 | td->mt_flags |= INPUT_MT_DROP_UNUSED; | ||
| 686 | |||
| 687 | input_mt_init_slots(input, td->maxcontacts, td->mt_flags); | ||
| 688 | |||
| 689 | td->mt_flags = 0; | ||
| 690 | } | ||
| 691 | |||
| 688 | static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) | 692 | static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) |
| 689 | { | 693 | { |
| 690 | int ret, i; | 694 | int ret, i; |
| 691 | struct mt_device *td; | 695 | struct mt_device *td; |
| 692 | struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ | 696 | struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ |
| 693 | 697 | ||
| 694 | if (id) { | 698 | for (i = 0; mt_classes[i].name ; i++) { |
| 695 | for (i = 0; mt_classes[i].name ; i++) { | 699 | if (id->driver_data == mt_classes[i].name) { |
| 696 | if (id->driver_data == mt_classes[i].name) { | 700 | mtclass = &(mt_classes[i]); |
| 697 | mtclass = &(mt_classes[i]); | 701 | break; |
| 698 | break; | ||
| 699 | } | ||
| 700 | } | 702 | } |
| 701 | } | 703 | } |
| 702 | 704 | ||
| @@ -722,6 +724,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 722 | goto fail; | 724 | goto fail; |
| 723 | } | 725 | } |
| 724 | 726 | ||
| 727 | if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) | ||
| 728 | td->serial_maybe = true; | ||
| 729 | |||
| 725 | ret = hid_parse(hdev); | 730 | ret = hid_parse(hdev); |
| 726 | if (ret != 0) | 731 | if (ret != 0) |
| 727 | goto fail; | 732 | goto fail; |
| @@ -730,20 +735,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 730 | if (ret) | 735 | if (ret) |
| 731 | goto fail; | 736 | goto fail; |
| 732 | 737 | ||
| 733 | mt_post_parse(td); | ||
| 734 | |||
| 735 | if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) | ||
| 736 | mt_post_parse_default_settings(td); | ||
| 737 | |||
| 738 | td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot), | ||
| 739 | GFP_KERNEL); | ||
| 740 | if (!td->slots) { | ||
| 741 | dev_err(&hdev->dev, "cannot allocate multitouch slots\n"); | ||
| 742 | hid_hw_stop(hdev); | ||
| 743 | ret = -ENOMEM; | ||
| 744 | goto fail; | ||
| 745 | } | ||
| 746 | |||
| 747 | ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); | 738 | ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); |
| 748 | 739 | ||
| 749 | mt_set_maxcontacts(hdev); | 740 | mt_set_maxcontacts(hdev); |
| @@ -767,6 +758,32 @@ static int mt_reset_resume(struct hid_device *hdev) | |||
| 767 | mt_set_input_mode(hdev); | 758 | mt_set_input_mode(hdev); |
| 768 | return 0; | 759 | return 0; |
| 769 | } | 760 | } |
| 761 | |||
| 762 | static int mt_resume(struct hid_device *hdev) | ||
| 763 | { | ||
| 764 | struct usb_interface *intf; | ||
| 765 | struct usb_host_interface *interface; | ||
| 766 | struct usb_device *dev; | ||
| 767 | |||
| 768 | if (hdev->bus != BUS_USB) | ||
| 769 | return 0; | ||
| 770 | |||
| 771 | intf = to_usb_interface(hdev->dev.parent); | ||
| 772 | interface = intf->cur_altsetting; | ||
| 773 | dev = hid_to_usb_dev(hdev); | ||
| 774 | |||
| 775 | /* Some Elan legacy devices require SET_IDLE to be set on resume. | ||
| 776 | * It should be safe to send it to other devices too. | ||
| 777 | * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */ | ||
| 778 | |||
| 779 | usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | ||
| 780 | HID_REQ_SET_IDLE, | ||
| 781 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, | ||
| 782 | 0, interface->desc.bInterfaceNumber, | ||
| 783 | NULL, 0, USB_CTRL_SET_TIMEOUT); | ||
| 784 | |||
| 785 | return 0; | ||
| 786 | } | ||
| 770 | #endif | 787 | #endif |
| 771 | 788 | ||
| 772 | static void mt_remove(struct hid_device *hdev) | 789 | static void mt_remove(struct hid_device *hdev) |
| @@ -774,7 +791,6 @@ static void mt_remove(struct hid_device *hdev) | |||
| 774 | struct mt_device *td = hid_get_drvdata(hdev); | 791 | struct mt_device *td = hid_get_drvdata(hdev); |
| 775 | sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); | 792 | sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); |
| 776 | hid_hw_stop(hdev); | 793 | hid_hw_stop(hdev); |
| 777 | kfree(td->slots); | ||
| 778 | kfree(td); | 794 | kfree(td); |
| 779 | hid_set_drvdata(hdev, NULL); | 795 | hid_set_drvdata(hdev, NULL); |
| 780 | } | 796 | } |
| @@ -885,17 +901,37 @@ static const struct hid_device_id mt_devices[] = { | |||
| 885 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) }, | 901 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) }, |
| 886 | { .driver_data = MT_CLS_EGALAX_SERIAL, | 902 | { .driver_data = MT_CLS_EGALAX_SERIAL, |
| 887 | MT_USB_DEVICE(USB_VENDOR_ID_DWAV, | 903 | MT_USB_DEVICE(USB_VENDOR_ID_DWAV, |
| 904 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7) }, | ||
| 905 | { .driver_data = MT_CLS_EGALAX_SERIAL, | ||
| 906 | MT_USB_DEVICE(USB_VENDOR_ID_DWAV, | ||
| 888 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, | 907 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, |
| 908 | { .driver_data = MT_CLS_EGALAX, | ||
| 909 | HID_USB_DEVICE(USB_VENDOR_ID_DWAV, | ||
| 910 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, | ||
| 911 | { .driver_data = MT_CLS_EGALAX, | ||
| 912 | HID_USB_DEVICE(USB_VENDOR_ID_DWAV, | ||
| 913 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, | ||
| 914 | { .driver_data = MT_CLS_EGALAX, | ||
| 915 | HID_USB_DEVICE(USB_VENDOR_ID_DWAV, | ||
| 916 | USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, | ||
| 889 | 917 | ||
| 890 | /* Elo TouchSystems IntelliTouch Plus panel */ | 918 | /* Elo TouchSystems IntelliTouch Plus panel */ |
| 891 | { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, | 919 | { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, |
| 892 | MT_USB_DEVICE(USB_VENDOR_ID_ELO, | 920 | MT_USB_DEVICE(USB_VENDOR_ID_ELO, |
| 893 | USB_DEVICE_ID_ELO_TS2515) }, | 921 | USB_DEVICE_ID_ELO_TS2515) }, |
| 894 | 922 | ||
| 923 | /* Flatfrog Panels */ | ||
| 924 | { .driver_data = MT_CLS_FLATFROG, | ||
| 925 | MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG, | ||
| 926 | USB_DEVICE_ID_MULTITOUCH_3200) }, | ||
| 927 | |||
| 895 | /* GeneralTouch panel */ | 928 | /* GeneralTouch panel */ |
| 896 | { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, | 929 | { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS, |
| 897 | MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, | 930 | MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, |
| 898 | USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, | 931 | USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, |
| 932 | { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, | ||
| 933 | MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, | ||
| 934 | USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) }, | ||
| 899 | 935 | ||
| 900 | /* Gametel game controller */ | 936 | /* Gametel game controller */ |
| 901 | { .driver_data = MT_CLS_DEFAULT, | 937 | { .driver_data = MT_CLS_DEFAULT, |
| @@ -1087,11 +1123,13 @@ static struct hid_driver mt_driver = { | |||
| 1087 | .remove = mt_remove, | 1123 | .remove = mt_remove, |
| 1088 | .input_mapping = mt_input_mapping, | 1124 | .input_mapping = mt_input_mapping, |
| 1089 | .input_mapped = mt_input_mapped, | 1125 | .input_mapped = mt_input_mapped, |
| 1126 | .input_configured = mt_input_configured, | ||
| 1090 | .feature_mapping = mt_feature_mapping, | 1127 | .feature_mapping = mt_feature_mapping, |
| 1091 | .usage_table = mt_grabbed_usages, | 1128 | .usage_table = mt_grabbed_usages, |
| 1092 | .event = mt_event, | 1129 | .event = mt_event, |
| 1093 | #ifdef CONFIG_PM | 1130 | #ifdef CONFIG_PM |
| 1094 | .reset_resume = mt_reset_resume, | 1131 | .reset_resume = mt_reset_resume, |
| 1132 | .resume = mt_resume, | ||
| 1095 | #endif | 1133 | #endif |
| 1096 | }; | 1134 | }; |
| 1097 | 1135 | ||
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 9fae2ebdd758..86a969f63292 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c | |||
| @@ -882,10 +882,10 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 882 | nd->activate_slack = activate_slack; | 882 | nd->activate_slack = activate_slack; |
| 883 | nd->act_state = activate_slack; | 883 | nd->act_state = activate_slack; |
| 884 | nd->deactivate_slack = -deactivate_slack; | 884 | nd->deactivate_slack = -deactivate_slack; |
| 885 | nd->sensor_logical_width = 0; | 885 | nd->sensor_logical_width = 1; |
| 886 | nd->sensor_logical_height = 0; | 886 | nd->sensor_logical_height = 1; |
| 887 | nd->sensor_physical_width = 0; | 887 | nd->sensor_physical_width = 1; |
| 888 | nd->sensor_physical_height = 0; | 888 | nd->sensor_physical_height = 1; |
| 889 | 889 | ||
| 890 | hid_set_drvdata(hdev, nd); | 890 | hid_set_drvdata(hdev, nd); |
| 891 | 891 | ||
diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index f1ea3ff8a98d..4c521de4e7e6 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c deleted file mode 100644 index 27c8ebdfad01..000000000000 --- a/drivers/hid/hid-picolcd.c +++ /dev/null | |||
| @@ -1,2748 +0,0 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include <linux/hid-debug.h> | ||
| 22 | #include <linux/input.h> | ||
| 23 | #include "hid-ids.h" | ||
| 24 | #include "usbhid/usbhid.h" | ||
| 25 | #include <linux/usb.h> | ||
| 26 | |||
| 27 | #include <linux/fb.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 29 | #include <linux/backlight.h> | ||
| 30 | #include <linux/lcd.h> | ||
| 31 | |||
| 32 | #include <linux/leds.h> | ||
| 33 | |||
| 34 | #include <linux/seq_file.h> | ||
| 35 | #include <linux/debugfs.h> | ||
| 36 | |||
| 37 | #include <linux/completion.h> | ||
| 38 | #include <linux/uaccess.h> | ||
| 39 | #include <linux/module.h> | ||
| 40 | |||
| 41 | #define PICOLCD_NAME "PicoLCD (graphic)" | ||
| 42 | |||
| 43 | /* Report numbers */ | ||
| 44 | #define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */ | ||
| 45 | #define ERR_SUCCESS 0x00 | ||
| 46 | #define ERR_PARAMETER_MISSING 0x01 | ||
| 47 | #define ERR_DATA_MISSING 0x02 | ||
| 48 | #define ERR_BLOCK_READ_ONLY 0x03 | ||
| 49 | #define ERR_BLOCK_NOT_ERASABLE 0x04 | ||
| 50 | #define ERR_BLOCK_TOO_BIG 0x05 | ||
| 51 | #define ERR_SECTION_OVERFLOW 0x06 | ||
| 52 | #define ERR_INVALID_CMD_LEN 0x07 | ||
| 53 | #define ERR_INVALID_DATA_LEN 0x08 | ||
| 54 | #define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */ | ||
| 55 | #define REPORT_IR_DATA 0x21 /* LCD: IN[63] */ | ||
| 56 | #define REPORT_EE_DATA 0x32 /* LCD: IN[63] */ | ||
| 57 | #define REPORT_MEMORY 0x41 /* LCD: IN[63] */ | ||
| 58 | #define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */ | ||
| 59 | #define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */ | ||
| 60 | #define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */ | ||
| 61 | #define REPORT_RESET 0x93 /* LCD: OUT[2] */ | ||
| 62 | #define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */ | ||
| 63 | #define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */ | ||
| 64 | #define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */ | ||
| 65 | #define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */ | ||
| 66 | #define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */ | ||
| 67 | #define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */ | ||
| 68 | #define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */ | ||
| 69 | #define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */ | ||
| 70 | #define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */ | ||
| 71 | #define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */ | ||
| 72 | #define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */ | ||
| 73 | #define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */ | ||
| 74 | #define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */ | ||
| 75 | #define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */ | ||
| 76 | #define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */ | ||
| 77 | #define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */ | ||
| 78 | #define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */ | ||
| 79 | #define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */ | ||
| 80 | |||
| 81 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 82 | /* Framebuffer | ||
| 83 | * | ||
| 84 | * The PicoLCD use a Topway LCD module of 256x64 pixel | ||
| 85 | * This display area is tiled over 4 controllers with 8 tiles | ||
| 86 | * each. Each tile has 8x64 pixel, each data byte representing | ||
| 87 | * a 1-bit wide vertical line of the tile. | ||
| 88 | * | ||
| 89 | * The display can be updated at a tile granularity. | ||
| 90 | * | ||
| 91 | * Chip 1 Chip 2 Chip 3 Chip 4 | ||
| 92 | * +----------------+----------------+----------------+----------------+ | ||
| 93 | * | Tile 1 | Tile 1 | Tile 1 | Tile 1 | | ||
| 94 | * +----------------+----------------+----------------+----------------+ | ||
| 95 | * | Tile 2 | Tile 2 | Tile 2 | Tile 2 | | ||
| 96 | * +----------------+----------------+----------------+----------------+ | ||
| 97 | * ... | ||
| 98 | * +----------------+----------------+----------------+----------------+ | ||
| 99 | * | Tile 8 | Tile 8 | Tile 8 | Tile 8 | | ||
| 100 | * +----------------+----------------+----------------+----------------+ | ||
| 101 | */ | ||
| 102 | #define PICOLCDFB_NAME "picolcdfb" | ||
| 103 | #define PICOLCDFB_WIDTH (256) | ||
| 104 | #define PICOLCDFB_HEIGHT (64) | ||
| 105 | #define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8) | ||
| 106 | |||
| 107 | #define PICOLCDFB_UPDATE_RATE_LIMIT 10 | ||
| 108 | #define PICOLCDFB_UPDATE_RATE_DEFAULT 2 | ||
| 109 | |||
| 110 | /* Framebuffer visual structures */ | ||
| 111 | static const struct fb_fix_screeninfo picolcdfb_fix = { | ||
| 112 | .id = PICOLCDFB_NAME, | ||
| 113 | .type = FB_TYPE_PACKED_PIXELS, | ||
| 114 | .visual = FB_VISUAL_MONO01, | ||
| 115 | .xpanstep = 0, | ||
| 116 | .ypanstep = 0, | ||
| 117 | .ywrapstep = 0, | ||
| 118 | .line_length = PICOLCDFB_WIDTH / 8, | ||
| 119 | .accel = FB_ACCEL_NONE, | ||
| 120 | }; | ||
| 121 | |||
| 122 | static const struct fb_var_screeninfo picolcdfb_var = { | ||
| 123 | .xres = PICOLCDFB_WIDTH, | ||
| 124 | .yres = PICOLCDFB_HEIGHT, | ||
| 125 | .xres_virtual = PICOLCDFB_WIDTH, | ||
| 126 | .yres_virtual = PICOLCDFB_HEIGHT, | ||
| 127 | .width = 103, | ||
| 128 | .height = 26, | ||
| 129 | .bits_per_pixel = 1, | ||
| 130 | .grayscale = 1, | ||
| 131 | .red = { | ||
| 132 | .offset = 0, | ||
| 133 | .length = 1, | ||
| 134 | .msb_right = 0, | ||
| 135 | }, | ||
| 136 | .green = { | ||
| 137 | .offset = 0, | ||
| 138 | .length = 1, | ||
| 139 | .msb_right = 0, | ||
| 140 | }, | ||
| 141 | .blue = { | ||
| 142 | .offset = 0, | ||
| 143 | .length = 1, | ||
| 144 | .msb_right = 0, | ||
| 145 | }, | ||
| 146 | .transp = { | ||
| 147 | .offset = 0, | ||
| 148 | .length = 0, | ||
| 149 | .msb_right = 0, | ||
| 150 | }, | ||
| 151 | }; | ||
| 152 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 153 | |||
| 154 | /* Input device | ||
| 155 | * | ||
| 156 | * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys | ||
| 157 | * and header for 4x4 key matrix. The built-in keys are part of the matrix. | ||
| 158 | */ | ||
| 159 | static const unsigned short def_keymap[] = { | ||
| 160 | KEY_RESERVED, /* none */ | ||
| 161 | KEY_BACK, /* col 4 + row 1 */ | ||
| 162 | KEY_HOMEPAGE, /* col 3 + row 1 */ | ||
| 163 | KEY_RESERVED, /* col 2 + row 1 */ | ||
| 164 | KEY_RESERVED, /* col 1 + row 1 */ | ||
| 165 | KEY_SCROLLUP, /* col 4 + row 2 */ | ||
| 166 | KEY_OK, /* col 3 + row 2 */ | ||
| 167 | KEY_SCROLLDOWN, /* col 2 + row 2 */ | ||
| 168 | KEY_RESERVED, /* col 1 + row 2 */ | ||
| 169 | KEY_RESERVED, /* col 4 + row 3 */ | ||
| 170 | KEY_RESERVED, /* col 3 + row 3 */ | ||
| 171 | KEY_RESERVED, /* col 2 + row 3 */ | ||
| 172 | KEY_RESERVED, /* col 1 + row 3 */ | ||
| 173 | KEY_RESERVED, /* col 4 + row 4 */ | ||
| 174 | KEY_RESERVED, /* col 3 + row 4 */ | ||
| 175 | KEY_RESERVED, /* col 2 + row 4 */ | ||
| 176 | KEY_RESERVED, /* col 1 + row 4 */ | ||
| 177 | }; | ||
| 178 | #define PICOLCD_KEYS ARRAY_SIZE(def_keymap) | ||
| 179 | |||
| 180 | /* Description of in-progress IO operation, used for operations | ||
| 181 | * that trigger response from device */ | ||
| 182 | struct picolcd_pending { | ||
| 183 | struct hid_report *out_report; | ||
| 184 | struct hid_report *in_report; | ||
| 185 | struct completion ready; | ||
| 186 | int raw_size; | ||
| 187 | u8 raw_data[64]; | ||
| 188 | }; | ||
| 189 | |||
| 190 | /* Per device data structure */ | ||
| 191 | struct picolcd_data { | ||
| 192 | struct hid_device *hdev; | ||
| 193 | #ifdef CONFIG_DEBUG_FS | ||
| 194 | struct dentry *debug_reset; | ||
| 195 | struct dentry *debug_eeprom; | ||
| 196 | struct dentry *debug_flash; | ||
| 197 | struct mutex mutex_flash; | ||
| 198 | int addr_sz; | ||
| 199 | #endif | ||
| 200 | u8 version[2]; | ||
| 201 | unsigned short opmode_delay; | ||
| 202 | /* input stuff */ | ||
| 203 | u8 pressed_keys[2]; | ||
| 204 | struct input_dev *input_keys; | ||
| 205 | struct input_dev *input_cir; | ||
| 206 | unsigned short keycode[PICOLCD_KEYS]; | ||
| 207 | |||
| 208 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 209 | /* Framebuffer stuff */ | ||
| 210 | u8 fb_update_rate; | ||
| 211 | u8 fb_bpp; | ||
| 212 | u8 fb_force; | ||
| 213 | u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */ | ||
| 214 | u8 *fb_bitmap; /* framebuffer */ | ||
| 215 | struct fb_info *fb_info; | ||
| 216 | struct fb_deferred_io fb_defio; | ||
| 217 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 218 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
| 219 | struct lcd_device *lcd; | ||
| 220 | u8 lcd_contrast; | ||
| 221 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
| 222 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
| 223 | struct backlight_device *backlight; | ||
| 224 | u8 lcd_brightness; | ||
| 225 | u8 lcd_power; | ||
| 226 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
| 227 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
| 228 | /* LED stuff */ | ||
| 229 | u8 led_state; | ||
| 230 | struct led_classdev *led[8]; | ||
| 231 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
| 232 | |||
| 233 | /* Housekeeping stuff */ | ||
| 234 | spinlock_t lock; | ||
| 235 | struct mutex mutex; | ||
| 236 | struct picolcd_pending *pending; | ||
| 237 | int status; | ||
| 238 | #define PICOLCD_BOOTLOADER 1 | ||
| 239 | #define PICOLCD_FAILED 2 | ||
| 240 | #define PICOLCD_READY_FB 4 | ||
| 241 | }; | ||
| 242 | |||
| 243 | |||
| 244 | /* Find a given report */ | ||
| 245 | #define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT) | ||
| 246 | #define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT) | ||
| 247 | |||
| 248 | static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir) | ||
| 249 | { | ||
| 250 | struct list_head *feature_report_list = &hdev->report_enum[dir].report_list; | ||
| 251 | struct hid_report *report = NULL; | ||
| 252 | |||
| 253 | list_for_each_entry(report, feature_report_list, list) { | ||
| 254 | if (report->id == id) | ||
| 255 | return report; | ||
| 256 | } | ||
| 257 | hid_warn(hdev, "No report with id 0x%x found\n", id); | ||
| 258 | return NULL; | ||
| 259 | } | ||
| 260 | |||
| 261 | #ifdef CONFIG_DEBUG_FS | ||
| 262 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
| 263 | struct hid_device *hdev, struct hid_report *report); | ||
| 264 | #define usbhid_submit_report(a, b, c) \ | ||
| 265 | do { \ | ||
| 266 | picolcd_debug_out_report(hid_get_drvdata(a), a, b); \ | ||
| 267 | usbhid_submit_report(a, b, c); \ | ||
| 268 | } while (0) | ||
| 269 | #endif | ||
| 270 | |||
| 271 | /* Submit a report and wait for a reply from device - if device fades away | ||
| 272 | * or does not respond in time, return NULL */ | ||
| 273 | static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, | ||
| 274 | int report_id, const u8 *raw_data, int size) | ||
| 275 | { | ||
| 276 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 277 | struct picolcd_pending *work; | ||
| 278 | struct hid_report *report = picolcd_out_report(report_id, hdev); | ||
| 279 | unsigned long flags; | ||
| 280 | int i, j, k; | ||
| 281 | |||
| 282 | if (!report || !data) | ||
| 283 | return NULL; | ||
| 284 | if (data->status & PICOLCD_FAILED) | ||
| 285 | return NULL; | ||
| 286 | work = kzalloc(sizeof(*work), GFP_KERNEL); | ||
| 287 | if (!work) | ||
| 288 | return NULL; | ||
| 289 | |||
| 290 | init_completion(&work->ready); | ||
| 291 | work->out_report = report; | ||
| 292 | work->in_report = NULL; | ||
| 293 | work->raw_size = 0; | ||
| 294 | |||
| 295 | mutex_lock(&data->mutex); | ||
| 296 | spin_lock_irqsave(&data->lock, flags); | ||
| 297 | for (i = k = 0; i < report->maxfield; i++) | ||
| 298 | for (j = 0; j < report->field[i]->report_count; j++) { | ||
| 299 | hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0); | ||
| 300 | k++; | ||
| 301 | } | ||
| 302 | data->pending = work; | ||
| 303 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 304 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 305 | wait_for_completion_interruptible_timeout(&work->ready, HZ*2); | ||
| 306 | spin_lock_irqsave(&data->lock, flags); | ||
| 307 | data->pending = NULL; | ||
| 308 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 309 | mutex_unlock(&data->mutex); | ||
| 310 | return work; | ||
| 311 | } | ||
| 312 | |||
| 313 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 314 | /* Send a given tile to PicoLCD */ | ||
| 315 | static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile) | ||
| 316 | { | ||
| 317 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 318 | struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev); | ||
| 319 | struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev); | ||
| 320 | unsigned long flags; | ||
| 321 | u8 *tdata; | ||
| 322 | int i; | ||
| 323 | |||
| 324 | if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1) | ||
| 325 | return -ENODEV; | ||
| 326 | |||
| 327 | spin_lock_irqsave(&data->lock, flags); | ||
| 328 | hid_set_field(report1->field[0], 0, chip << 2); | ||
| 329 | hid_set_field(report1->field[0], 1, 0x02); | ||
| 330 | hid_set_field(report1->field[0], 2, 0x00); | ||
| 331 | hid_set_field(report1->field[0], 3, 0x00); | ||
| 332 | hid_set_field(report1->field[0], 4, 0xb8 | tile); | ||
| 333 | hid_set_field(report1->field[0], 5, 0x00); | ||
| 334 | hid_set_field(report1->field[0], 6, 0x00); | ||
| 335 | hid_set_field(report1->field[0], 7, 0x40); | ||
| 336 | hid_set_field(report1->field[0], 8, 0x00); | ||
| 337 | hid_set_field(report1->field[0], 9, 0x00); | ||
| 338 | hid_set_field(report1->field[0], 10, 32); | ||
| 339 | |||
| 340 | hid_set_field(report2->field[0], 0, (chip << 2) | 0x01); | ||
| 341 | hid_set_field(report2->field[0], 1, 0x00); | ||
| 342 | hid_set_field(report2->field[0], 2, 0x00); | ||
| 343 | hid_set_field(report2->field[0], 3, 32); | ||
| 344 | |||
| 345 | tdata = data->fb_vbitmap + (tile * 4 + chip) * 64; | ||
| 346 | for (i = 0; i < 64; i++) | ||
| 347 | if (i < 32) | ||
| 348 | hid_set_field(report1->field[0], 11 + i, tdata[i]); | ||
| 349 | else | ||
| 350 | hid_set_field(report2->field[0], 4 + i - 32, tdata[i]); | ||
| 351 | |||
| 352 | usbhid_submit_report(data->hdev, report1, USB_DIR_OUT); | ||
| 353 | usbhid_submit_report(data->hdev, report2, USB_DIR_OUT); | ||
| 354 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 355 | return 0; | ||
| 356 | } | ||
| 357 | |||
| 358 | /* Translate a single tile*/ | ||
| 359 | static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp, | ||
| 360 | int chip, int tile) | ||
| 361 | { | ||
| 362 | int i, b, changed = 0; | ||
| 363 | u8 tdata[64]; | ||
| 364 | u8 *vdata = vbitmap + (tile * 4 + chip) * 64; | ||
| 365 | |||
| 366 | if (bpp == 1) { | ||
| 367 | for (b = 7; b >= 0; b--) { | ||
| 368 | const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32; | ||
| 369 | for (i = 0; i < 64; i++) { | ||
| 370 | tdata[i] <<= 1; | ||
| 371 | tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | } else if (bpp == 8) { | ||
| 375 | for (b = 7; b >= 0; b--) { | ||
| 376 | const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8; | ||
| 377 | for (i = 0; i < 64; i++) { | ||
| 378 | tdata[i] <<= 1; | ||
| 379 | tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00; | ||
| 380 | } | ||
| 381 | } | ||
| 382 | } else { | ||
| 383 | /* Oops, we should never get here! */ | ||
| 384 | WARN_ON(1); | ||
| 385 | return 0; | ||
| 386 | } | ||
| 387 | |||
| 388 | for (i = 0; i < 64; i++) | ||
| 389 | if (tdata[i] != vdata[i]) { | ||
| 390 | changed = 1; | ||
| 391 | vdata[i] = tdata[i]; | ||
| 392 | } | ||
| 393 | return changed; | ||
| 394 | } | ||
| 395 | |||
| 396 | /* Reconfigure LCD display */ | ||
| 397 | static int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
| 398 | { | ||
| 399 | struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev); | ||
| 400 | int i, j; | ||
| 401 | unsigned long flags; | ||
| 402 | static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 }; | ||
| 403 | |||
| 404 | if (!report || report->maxfield != 1) | ||
| 405 | return -ENODEV; | ||
| 406 | |||
| 407 | spin_lock_irqsave(&data->lock, flags); | ||
| 408 | for (i = 0; i < 4; i++) { | ||
| 409 | for (j = 0; j < report->field[0]->maxusage; j++) | ||
| 410 | if (j == 0) | ||
| 411 | hid_set_field(report->field[0], j, i << 2); | ||
| 412 | else if (j < sizeof(mapcmd)) | ||
| 413 | hid_set_field(report->field[0], j, mapcmd[j]); | ||
| 414 | else | ||
| 415 | hid_set_field(report->field[0], j, 0); | ||
| 416 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 417 | } | ||
| 418 | |||
| 419 | data->status |= PICOLCD_READY_FB; | ||
| 420 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 421 | |||
| 422 | if (data->fb_bitmap) { | ||
| 423 | if (clear) { | ||
| 424 | memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE); | ||
| 425 | memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp); | ||
| 426 | } | ||
| 427 | data->fb_force = 1; | ||
| 428 | } | ||
| 429 | |||
| 430 | /* schedule first output of framebuffer */ | ||
| 431 | if (data->fb_info) | ||
| 432 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
| 433 | |||
| 434 | return 0; | ||
| 435 | } | ||
| 436 | |||
| 437 | /* Update fb_vbitmap from the screen_base and send changed tiles to device */ | ||
| 438 | static void picolcd_fb_update(struct picolcd_data *data) | ||
| 439 | { | ||
| 440 | int chip, tile, n; | ||
| 441 | unsigned long flags; | ||
| 442 | |||
| 443 | if (!data) | ||
| 444 | return; | ||
| 445 | |||
| 446 | spin_lock_irqsave(&data->lock, flags); | ||
| 447 | if (!(data->status & PICOLCD_READY_FB)) { | ||
| 448 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 449 | picolcd_fb_reset(data, 0); | ||
| 450 | } else { | ||
| 451 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 452 | } | ||
| 453 | |||
| 454 | /* | ||
| 455 | * Translate the framebuffer into the format needed by the PicoLCD. | ||
| 456 | * See display layout above. | ||
| 457 | * Do this one tile after the other and push those tiles that changed. | ||
| 458 | * | ||
| 459 | * Wait for our IO to complete as otherwise we might flood the queue! | ||
| 460 | */ | ||
| 461 | n = 0; | ||
| 462 | for (chip = 0; chip < 4; chip++) | ||
| 463 | for (tile = 0; tile < 8; tile++) | ||
| 464 | if (picolcd_fb_update_tile(data->fb_vbitmap, | ||
| 465 | data->fb_bitmap, data->fb_bpp, chip, tile) || | ||
| 466 | data->fb_force) { | ||
| 467 | n += 2; | ||
| 468 | if (!data->fb_info->par) | ||
| 469 | return; /* device lost! */ | ||
| 470 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { | ||
| 471 | usbhid_wait_io(data->hdev); | ||
| 472 | n = 0; | ||
| 473 | } | ||
| 474 | picolcd_fb_send_tile(data->hdev, chip, tile); | ||
| 475 | } | ||
| 476 | data->fb_force = false; | ||
| 477 | if (n) | ||
| 478 | usbhid_wait_io(data->hdev); | ||
| 479 | } | ||
| 480 | |||
| 481 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 482 | static void picolcd_fb_fillrect(struct fb_info *info, | ||
| 483 | const struct fb_fillrect *rect) | ||
| 484 | { | ||
| 485 | if (!info->par) | ||
| 486 | return; | ||
| 487 | sys_fillrect(info, rect); | ||
| 488 | |||
| 489 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 490 | } | ||
| 491 | |||
| 492 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 493 | static void picolcd_fb_copyarea(struct fb_info *info, | ||
| 494 | const struct fb_copyarea *area) | ||
| 495 | { | ||
| 496 | if (!info->par) | ||
| 497 | return; | ||
| 498 | sys_copyarea(info, area); | ||
| 499 | |||
| 500 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 501 | } | ||
| 502 | |||
| 503 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 504 | static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image) | ||
| 505 | { | ||
| 506 | if (!info->par) | ||
| 507 | return; | ||
| 508 | sys_imageblit(info, image); | ||
| 509 | |||
| 510 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 511 | } | ||
| 512 | |||
| 513 | /* | ||
| 514 | * this is the slow path from userspace. they can seek and write to | ||
| 515 | * the fb. it's inefficient to do anything less than a full screen draw | ||
| 516 | */ | ||
| 517 | static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf, | ||
| 518 | size_t count, loff_t *ppos) | ||
| 519 | { | ||
| 520 | ssize_t ret; | ||
| 521 | if (!info->par) | ||
| 522 | return -ENODEV; | ||
| 523 | ret = fb_sys_write(info, buf, count, ppos); | ||
| 524 | if (ret >= 0) | ||
| 525 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 526 | return ret; | ||
| 527 | } | ||
| 528 | |||
| 529 | static int picolcd_fb_blank(int blank, struct fb_info *info) | ||
| 530 | { | ||
| 531 | if (!info->par) | ||
| 532 | return -ENODEV; | ||
| 533 | /* We let fb notification do this for us via lcd/backlight device */ | ||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 537 | static void picolcd_fb_destroy(struct fb_info *info) | ||
| 538 | { | ||
| 539 | struct picolcd_data *data = info->par; | ||
| 540 | u32 *ref_cnt = info->pseudo_palette; | ||
| 541 | int may_release; | ||
| 542 | |||
| 543 | info->par = NULL; | ||
| 544 | if (data) | ||
| 545 | data->fb_info = NULL; | ||
| 546 | fb_deferred_io_cleanup(info); | ||
| 547 | |||
| 548 | ref_cnt--; | ||
| 549 | mutex_lock(&info->lock); | ||
| 550 | (*ref_cnt)--; | ||
| 551 | may_release = !*ref_cnt; | ||
| 552 | mutex_unlock(&info->lock); | ||
| 553 | if (may_release) { | ||
| 554 | vfree((u8 *)info->fix.smem_start); | ||
| 555 | framebuffer_release(info); | ||
| 556 | } | ||
| 557 | } | ||
| 558 | |||
| 559 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
| 560 | { | ||
| 561 | __u32 bpp = var->bits_per_pixel; | ||
| 562 | __u32 activate = var->activate; | ||
| 563 | |||
| 564 | /* only allow 1/8 bit depth (8-bit is grayscale) */ | ||
| 565 | *var = picolcdfb_var; | ||
| 566 | var->activate = activate; | ||
| 567 | if (bpp >= 8) { | ||
| 568 | var->bits_per_pixel = 8; | ||
| 569 | var->red.length = 8; | ||
| 570 | var->green.length = 8; | ||
| 571 | var->blue.length = 8; | ||
| 572 | } else { | ||
| 573 | var->bits_per_pixel = 1; | ||
| 574 | var->red.length = 1; | ||
| 575 | var->green.length = 1; | ||
| 576 | var->blue.length = 1; | ||
| 577 | } | ||
| 578 | return 0; | ||
| 579 | } | ||
| 580 | |||
| 581 | static int picolcd_set_par(struct fb_info *info) | ||
| 582 | { | ||
| 583 | struct picolcd_data *data = info->par; | ||
| 584 | u8 *tmp_fb, *o_fb; | ||
| 585 | if (!data) | ||
| 586 | return -ENODEV; | ||
| 587 | if (info->var.bits_per_pixel == data->fb_bpp) | ||
| 588 | return 0; | ||
| 589 | /* switch between 1/8 bit depths */ | ||
| 590 | if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8) | ||
| 591 | return -EINVAL; | ||
| 592 | |||
| 593 | o_fb = data->fb_bitmap; | ||
| 594 | tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL); | ||
| 595 | if (!tmp_fb) | ||
| 596 | return -ENOMEM; | ||
| 597 | |||
| 598 | /* translate FB content to new bits-per-pixel */ | ||
| 599 | if (info->var.bits_per_pixel == 1) { | ||
| 600 | int i, b; | ||
| 601 | for (i = 0; i < PICOLCDFB_SIZE; i++) { | ||
| 602 | u8 p = 0; | ||
| 603 | for (b = 0; b < 8; b++) { | ||
| 604 | p <<= 1; | ||
| 605 | p |= o_fb[i*8+b] ? 0x01 : 0x00; | ||
| 606 | } | ||
| 607 | tmp_fb[i] = p; | ||
| 608 | } | ||
| 609 | memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE); | ||
| 610 | info->fix.visual = FB_VISUAL_MONO01; | ||
| 611 | info->fix.line_length = PICOLCDFB_WIDTH / 8; | ||
| 612 | } else { | ||
| 613 | int i; | ||
| 614 | memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE); | ||
| 615 | for (i = 0; i < PICOLCDFB_SIZE * 8; i++) | ||
| 616 | o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; | ||
| 617 | info->fix.visual = FB_VISUAL_DIRECTCOLOR; | ||
| 618 | info->fix.line_length = PICOLCDFB_WIDTH; | ||
| 619 | } | ||
| 620 | |||
| 621 | kfree(tmp_fb); | ||
| 622 | data->fb_bpp = info->var.bits_per_pixel; | ||
| 623 | return 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | /* Do refcounting on our FB and cleanup per worker if FB is | ||
| 627 | * closed after unplug of our device | ||
| 628 | * (fb_release holds info->lock and still touches info after | ||
| 629 | * we return so we can't release it immediately. | ||
| 630 | */ | ||
| 631 | struct picolcd_fb_cleanup_item { | ||
| 632 | struct fb_info *info; | ||
| 633 | struct picolcd_fb_cleanup_item *next; | ||
| 634 | }; | ||
| 635 | static struct picolcd_fb_cleanup_item *fb_pending; | ||
| 636 | static DEFINE_SPINLOCK(fb_pending_lock); | ||
| 637 | |||
| 638 | static void picolcd_fb_do_cleanup(struct work_struct *data) | ||
| 639 | { | ||
| 640 | struct picolcd_fb_cleanup_item *item; | ||
| 641 | unsigned long flags; | ||
| 642 | |||
| 643 | do { | ||
| 644 | spin_lock_irqsave(&fb_pending_lock, flags); | ||
| 645 | item = fb_pending; | ||
| 646 | fb_pending = item ? item->next : NULL; | ||
| 647 | spin_unlock_irqrestore(&fb_pending_lock, flags); | ||
| 648 | |||
| 649 | if (item) { | ||
| 650 | u8 *fb = (u8 *)item->info->fix.smem_start; | ||
| 651 | /* make sure we do not race against fb core when | ||
| 652 | * releasing */ | ||
| 653 | mutex_lock(&item->info->lock); | ||
| 654 | mutex_unlock(&item->info->lock); | ||
| 655 | framebuffer_release(item->info); | ||
| 656 | vfree(fb); | ||
| 657 | } | ||
| 658 | } while (item); | ||
| 659 | } | ||
| 660 | |||
| 661 | static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup); | ||
| 662 | |||
| 663 | static int picolcd_fb_open(struct fb_info *info, int u) | ||
| 664 | { | ||
| 665 | u32 *ref_cnt = info->pseudo_palette; | ||
| 666 | ref_cnt--; | ||
| 667 | |||
| 668 | (*ref_cnt)++; | ||
| 669 | return 0; | ||
| 670 | } | ||
| 671 | |||
| 672 | static int picolcd_fb_release(struct fb_info *info, int u) | ||
| 673 | { | ||
| 674 | u32 *ref_cnt = info->pseudo_palette; | ||
| 675 | ref_cnt--; | ||
| 676 | |||
| 677 | (*ref_cnt)++; | ||
| 678 | if (!*ref_cnt) { | ||
| 679 | unsigned long flags; | ||
| 680 | struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt; | ||
| 681 | item--; | ||
| 682 | spin_lock_irqsave(&fb_pending_lock, flags); | ||
| 683 | item->next = fb_pending; | ||
| 684 | fb_pending = item; | ||
| 685 | spin_unlock_irqrestore(&fb_pending_lock, flags); | ||
| 686 | schedule_work(&picolcd_fb_cleanup); | ||
| 687 | } | ||
| 688 | return 0; | ||
| 689 | } | ||
| 690 | |||
| 691 | /* Note this can't be const because of struct fb_info definition */ | ||
| 692 | static struct fb_ops picolcdfb_ops = { | ||
| 693 | .owner = THIS_MODULE, | ||
| 694 | .fb_destroy = picolcd_fb_destroy, | ||
| 695 | .fb_open = picolcd_fb_open, | ||
| 696 | .fb_release = picolcd_fb_release, | ||
| 697 | .fb_read = fb_sys_read, | ||
| 698 | .fb_write = picolcd_fb_write, | ||
| 699 | .fb_blank = picolcd_fb_blank, | ||
| 700 | .fb_fillrect = picolcd_fb_fillrect, | ||
| 701 | .fb_copyarea = picolcd_fb_copyarea, | ||
| 702 | .fb_imageblit = picolcd_fb_imageblit, | ||
| 703 | .fb_check_var = picolcd_fb_check_var, | ||
| 704 | .fb_set_par = picolcd_set_par, | ||
| 705 | }; | ||
| 706 | |||
| 707 | |||
| 708 | /* Callback from deferred IO workqueue */ | ||
| 709 | static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist) | ||
| 710 | { | ||
| 711 | picolcd_fb_update(info->par); | ||
| 712 | } | ||
| 713 | |||
| 714 | static const struct fb_deferred_io picolcd_fb_defio = { | ||
| 715 | .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT, | ||
| 716 | .deferred_io = picolcd_fb_deferred_io, | ||
| 717 | }; | ||
| 718 | |||
| 719 | |||
| 720 | /* | ||
| 721 | * The "fb_update_rate" sysfs attribute | ||
| 722 | */ | ||
| 723 | static ssize_t picolcd_fb_update_rate_show(struct device *dev, | ||
| 724 | struct device_attribute *attr, char *buf) | ||
| 725 | { | ||
| 726 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 727 | unsigned i, fb_update_rate = data->fb_update_rate; | ||
| 728 | size_t ret = 0; | ||
| 729 | |||
| 730 | for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) | ||
| 731 | if (ret >= PAGE_SIZE) | ||
| 732 | break; | ||
| 733 | else if (i == fb_update_rate) | ||
| 734 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i); | ||
| 735 | else | ||
| 736 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i); | ||
| 737 | if (ret > 0) | ||
| 738 | buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n'; | ||
| 739 | return ret; | ||
| 740 | } | ||
| 741 | |||
| 742 | static ssize_t picolcd_fb_update_rate_store(struct device *dev, | ||
| 743 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 744 | { | ||
| 745 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 746 | int i; | ||
| 747 | unsigned u; | ||
| 748 | |||
| 749 | if (count < 1 || count > 10) | ||
| 750 | return -EINVAL; | ||
| 751 | |||
| 752 | i = sscanf(buf, "%u", &u); | ||
| 753 | if (i != 1) | ||
| 754 | return -EINVAL; | ||
| 755 | |||
| 756 | if (u > PICOLCDFB_UPDATE_RATE_LIMIT) | ||
| 757 | return -ERANGE; | ||
| 758 | else if (u == 0) | ||
| 759 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
| 760 | |||
| 761 | data->fb_update_rate = u; | ||
| 762 | data->fb_defio.delay = HZ / data->fb_update_rate; | ||
| 763 | return count; | ||
| 764 | } | ||
| 765 | |||
| 766 | static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show, | ||
| 767 | picolcd_fb_update_rate_store); | ||
| 768 | |||
| 769 | /* initialize Framebuffer device */ | ||
| 770 | static int picolcd_init_framebuffer(struct picolcd_data *data) | ||
| 771 | { | ||
| 772 | struct device *dev = &data->hdev->dev; | ||
| 773 | struct fb_info *info = NULL; | ||
| 774 | int i, error = -ENOMEM; | ||
| 775 | u8 *fb_vbitmap = NULL; | ||
| 776 | u8 *fb_bitmap = NULL; | ||
| 777 | u32 *palette; | ||
| 778 | |||
| 779 | fb_bitmap = vmalloc(PICOLCDFB_SIZE*8); | ||
| 780 | if (fb_bitmap == NULL) { | ||
| 781 | dev_err(dev, "can't get a free page for framebuffer\n"); | ||
| 782 | goto err_nomem; | ||
| 783 | } | ||
| 784 | |||
| 785 | fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL); | ||
| 786 | if (fb_vbitmap == NULL) { | ||
| 787 | dev_err(dev, "can't alloc vbitmap image buffer\n"); | ||
| 788 | goto err_nomem; | ||
| 789 | } | ||
| 790 | |||
| 791 | data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
| 792 | data->fb_defio = picolcd_fb_defio; | ||
| 793 | /* The extra memory is: | ||
| 794 | * - struct picolcd_fb_cleanup_item | ||
| 795 | * - u32 for ref_count | ||
| 796 | * - 256*u32 for pseudo_palette | ||
| 797 | */ | ||
| 798 | info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev); | ||
| 799 | if (info == NULL) { | ||
| 800 | dev_err(dev, "failed to allocate a framebuffer\n"); | ||
| 801 | goto err_nomem; | ||
| 802 | } | ||
| 803 | |||
| 804 | palette = info->par + sizeof(struct picolcd_fb_cleanup_item); | ||
| 805 | *palette = 1; | ||
| 806 | palette++; | ||
| 807 | for (i = 0; i < 256; i++) | ||
| 808 | palette[i] = i > 0 && i < 16 ? 0xff : 0; | ||
| 809 | info->pseudo_palette = palette; | ||
| 810 | info->fbdefio = &data->fb_defio; | ||
| 811 | info->screen_base = (char __force __iomem *)fb_bitmap; | ||
| 812 | info->fbops = &picolcdfb_ops; | ||
| 813 | info->var = picolcdfb_var; | ||
| 814 | info->fix = picolcdfb_fix; | ||
| 815 | info->fix.smem_len = PICOLCDFB_SIZE*8; | ||
| 816 | info->fix.smem_start = (unsigned long)fb_bitmap; | ||
| 817 | info->par = data; | ||
| 818 | info->flags = FBINFO_FLAG_DEFAULT; | ||
| 819 | |||
| 820 | data->fb_vbitmap = fb_vbitmap; | ||
| 821 | data->fb_bitmap = fb_bitmap; | ||
| 822 | data->fb_bpp = picolcdfb_var.bits_per_pixel; | ||
| 823 | error = picolcd_fb_reset(data, 1); | ||
| 824 | if (error) { | ||
| 825 | dev_err(dev, "failed to configure display\n"); | ||
| 826 | goto err_cleanup; | ||
| 827 | } | ||
| 828 | error = device_create_file(dev, &dev_attr_fb_update_rate); | ||
| 829 | if (error) { | ||
| 830 | dev_err(dev, "failed to create sysfs attributes\n"); | ||
| 831 | goto err_cleanup; | ||
| 832 | } | ||
| 833 | fb_deferred_io_init(info); | ||
| 834 | data->fb_info = info; | ||
| 835 | error = register_framebuffer(info); | ||
| 836 | if (error) { | ||
| 837 | dev_err(dev, "failed to register framebuffer\n"); | ||
| 838 | goto err_sysfs; | ||
| 839 | } | ||
| 840 | /* schedule first output of framebuffer */ | ||
| 841 | data->fb_force = 1; | ||
| 842 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 843 | return 0; | ||
| 844 | |||
| 845 | err_sysfs: | ||
| 846 | fb_deferred_io_cleanup(info); | ||
| 847 | device_remove_file(dev, &dev_attr_fb_update_rate); | ||
| 848 | err_cleanup: | ||
| 849 | data->fb_vbitmap = NULL; | ||
| 850 | data->fb_bitmap = NULL; | ||
| 851 | data->fb_bpp = 0; | ||
| 852 | data->fb_info = NULL; | ||
| 853 | |||
| 854 | err_nomem: | ||
| 855 | framebuffer_release(info); | ||
| 856 | vfree(fb_bitmap); | ||
| 857 | kfree(fb_vbitmap); | ||
| 858 | return error; | ||
| 859 | } | ||
| 860 | |||
| 861 | static void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
| 862 | { | ||
| 863 | struct fb_info *info = data->fb_info; | ||
| 864 | u8 *fb_vbitmap = data->fb_vbitmap; | ||
| 865 | |||
| 866 | if (!info) | ||
| 867 | return; | ||
| 868 | |||
| 869 | info->par = NULL; | ||
| 870 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); | ||
| 871 | unregister_framebuffer(info); | ||
| 872 | data->fb_vbitmap = NULL; | ||
| 873 | data->fb_bitmap = NULL; | ||
| 874 | data->fb_bpp = 0; | ||
| 875 | data->fb_info = NULL; | ||
| 876 | kfree(fb_vbitmap); | ||
| 877 | } | ||
| 878 | |||
| 879 | #define picolcd_fbinfo(d) ((d)->fb_info) | ||
| 880 | #else | ||
| 881 | static inline int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
| 882 | { | ||
| 883 | return 0; | ||
| 884 | } | ||
| 885 | static inline int picolcd_init_framebuffer(struct picolcd_data *data) | ||
| 886 | { | ||
| 887 | return 0; | ||
| 888 | } | ||
| 889 | static inline void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
| 890 | { | ||
| 891 | } | ||
| 892 | #define picolcd_fbinfo(d) NULL | ||
| 893 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 894 | |||
| 895 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
| 896 | /* | ||
| 897 | * backlight class device | ||
| 898 | */ | ||
| 899 | static int picolcd_get_brightness(struct backlight_device *bdev) | ||
| 900 | { | ||
| 901 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 902 | return data->lcd_brightness; | ||
| 903 | } | ||
| 904 | |||
| 905 | static int picolcd_set_brightness(struct backlight_device *bdev) | ||
| 906 | { | ||
| 907 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 908 | struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev); | ||
| 909 | unsigned long flags; | ||
| 910 | |||
| 911 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 912 | return -ENODEV; | ||
| 913 | |||
| 914 | data->lcd_brightness = bdev->props.brightness & 0x0ff; | ||
| 915 | data->lcd_power = bdev->props.power; | ||
| 916 | spin_lock_irqsave(&data->lock, flags); | ||
| 917 | hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); | ||
| 918 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 919 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 920 | return 0; | ||
| 921 | } | ||
| 922 | |||
| 923 | static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb) | ||
| 924 | { | ||
| 925 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev)); | ||
| 926 | } | ||
| 927 | |||
| 928 | static const struct backlight_ops picolcd_blops = { | ||
| 929 | .update_status = picolcd_set_brightness, | ||
| 930 | .get_brightness = picolcd_get_brightness, | ||
| 931 | .check_fb = picolcd_check_bl_fb, | ||
| 932 | }; | ||
| 933 | |||
| 934 | static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report) | ||
| 935 | { | ||
| 936 | struct device *dev = &data->hdev->dev; | ||
| 937 | struct backlight_device *bdev; | ||
| 938 | struct backlight_properties props; | ||
| 939 | if (!report) | ||
| 940 | return -ENODEV; | ||
| 941 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 942 | report->field[0]->report_size != 8) { | ||
| 943 | dev_err(dev, "unsupported BRIGHTNESS report"); | ||
| 944 | return -EINVAL; | ||
| 945 | } | ||
| 946 | |||
| 947 | memset(&props, 0, sizeof(props)); | ||
| 948 | props.type = BACKLIGHT_RAW; | ||
| 949 | props.max_brightness = 0xff; | ||
| 950 | bdev = backlight_device_register(dev_name(dev), dev, data, | ||
| 951 | &picolcd_blops, &props); | ||
| 952 | if (IS_ERR(bdev)) { | ||
| 953 | dev_err(dev, "failed to register backlight\n"); | ||
| 954 | return PTR_ERR(bdev); | ||
| 955 | } | ||
| 956 | bdev->props.brightness = 0xff; | ||
| 957 | data->lcd_brightness = 0xff; | ||
| 958 | data->backlight = bdev; | ||
| 959 | picolcd_set_brightness(bdev); | ||
| 960 | return 0; | ||
| 961 | } | ||
| 962 | |||
| 963 | static void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 964 | { | ||
| 965 | struct backlight_device *bdev = data->backlight; | ||
| 966 | |||
| 967 | data->backlight = NULL; | ||
| 968 | if (bdev) | ||
| 969 | backlight_device_unregister(bdev); | ||
| 970 | } | ||
| 971 | |||
| 972 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 973 | { | ||
| 974 | if (!data->backlight) | ||
| 975 | return 0; | ||
| 976 | return picolcd_set_brightness(data->backlight); | ||
| 977 | } | ||
| 978 | |||
| 979 | #ifdef CONFIG_PM | ||
| 980 | static void picolcd_suspend_backlight(struct picolcd_data *data) | ||
| 981 | { | ||
| 982 | int bl_power = data->lcd_power; | ||
| 983 | if (!data->backlight) | ||
| 984 | return; | ||
| 985 | |||
| 986 | data->backlight->props.power = FB_BLANK_POWERDOWN; | ||
| 987 | picolcd_set_brightness(data->backlight); | ||
| 988 | data->lcd_power = data->backlight->props.power = bl_power; | ||
| 989 | } | ||
| 990 | #endif /* CONFIG_PM */ | ||
| 991 | #else | ||
| 992 | static inline int picolcd_init_backlight(struct picolcd_data *data, | ||
| 993 | struct hid_report *report) | ||
| 994 | { | ||
| 995 | return 0; | ||
| 996 | } | ||
| 997 | static inline void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 998 | { | ||
| 999 | } | ||
| 1000 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 1001 | { | ||
| 1002 | return 0; | ||
| 1003 | } | ||
| 1004 | static inline void picolcd_suspend_backlight(struct picolcd_data *data) | ||
| 1005 | { | ||
| 1006 | } | ||
| 1007 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
| 1008 | |||
| 1009 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
| 1010 | /* | ||
| 1011 | * lcd class device | ||
| 1012 | */ | ||
| 1013 | static int picolcd_get_contrast(struct lcd_device *ldev) | ||
| 1014 | { | ||
| 1015 | struct picolcd_data *data = lcd_get_data(ldev); | ||
| 1016 | return data->lcd_contrast; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) | ||
| 1020 | { | ||
| 1021 | struct picolcd_data *data = lcd_get_data(ldev); | ||
| 1022 | struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev); | ||
| 1023 | unsigned long flags; | ||
| 1024 | |||
| 1025 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 1026 | return -ENODEV; | ||
| 1027 | |||
| 1028 | data->lcd_contrast = contrast & 0x0ff; | ||
| 1029 | spin_lock_irqsave(&data->lock, flags); | ||
| 1030 | hid_set_field(report->field[0], 0, data->lcd_contrast); | ||
| 1031 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 1032 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 1033 | return 0; | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb) | ||
| 1037 | { | ||
| 1038 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev)); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | static struct lcd_ops picolcd_lcdops = { | ||
| 1042 | .get_contrast = picolcd_get_contrast, | ||
| 1043 | .set_contrast = picolcd_set_contrast, | ||
| 1044 | .check_fb = picolcd_check_lcd_fb, | ||
| 1045 | }; | ||
| 1046 | |||
| 1047 | static int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report) | ||
| 1048 | { | ||
| 1049 | struct device *dev = &data->hdev->dev; | ||
| 1050 | struct lcd_device *ldev; | ||
| 1051 | |||
| 1052 | if (!report) | ||
| 1053 | return -ENODEV; | ||
| 1054 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 1055 | report->field[0]->report_size != 8) { | ||
| 1056 | dev_err(dev, "unsupported CONTRAST report"); | ||
| 1057 | return -EINVAL; | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops); | ||
| 1061 | if (IS_ERR(ldev)) { | ||
| 1062 | dev_err(dev, "failed to register LCD\n"); | ||
| 1063 | return PTR_ERR(ldev); | ||
| 1064 | } | ||
| 1065 | ldev->props.max_contrast = 0x0ff; | ||
| 1066 | data->lcd_contrast = 0xe5; | ||
| 1067 | data->lcd = ldev; | ||
| 1068 | picolcd_set_contrast(ldev, 0xe5); | ||
| 1069 | return 0; | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | static void picolcd_exit_lcd(struct picolcd_data *data) | ||
| 1073 | { | ||
| 1074 | struct lcd_device *ldev = data->lcd; | ||
| 1075 | |||
| 1076 | data->lcd = NULL; | ||
| 1077 | if (ldev) | ||
| 1078 | lcd_device_unregister(ldev); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | static inline int picolcd_resume_lcd(struct picolcd_data *data) | ||
| 1082 | { | ||
| 1083 | if (!data->lcd) | ||
| 1084 | return 0; | ||
| 1085 | return picolcd_set_contrast(data->lcd, data->lcd_contrast); | ||
| 1086 | } | ||
| 1087 | #else | ||
| 1088 | static inline int picolcd_init_lcd(struct picolcd_data *data, | ||
| 1089 | struct hid_report *report) | ||
| 1090 | { | ||
| 1091 | return 0; | ||
| 1092 | } | ||
| 1093 | static inline void picolcd_exit_lcd(struct picolcd_data *data) | ||
| 1094 | { | ||
| 1095 | } | ||
| 1096 | static inline int picolcd_resume_lcd(struct picolcd_data *data) | ||
| 1097 | { | ||
| 1098 | return 0; | ||
| 1099 | } | ||
| 1100 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
| 1101 | |||
| 1102 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
| 1103 | /** | ||
| 1104 | * LED class device | ||
| 1105 | */ | ||
| 1106 | static void picolcd_leds_set(struct picolcd_data *data) | ||
| 1107 | { | ||
| 1108 | struct hid_report *report; | ||
| 1109 | unsigned long flags; | ||
| 1110 | |||
| 1111 | if (!data->led[0]) | ||
| 1112 | return; | ||
| 1113 | report = picolcd_out_report(REPORT_LED_STATE, data->hdev); | ||
| 1114 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 1115 | return; | ||
| 1116 | |||
| 1117 | spin_lock_irqsave(&data->lock, flags); | ||
| 1118 | hid_set_field(report->field[0], 0, data->led_state); | ||
| 1119 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 1120 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | static void picolcd_led_set_brightness(struct led_classdev *led_cdev, | ||
| 1124 | enum led_brightness value) | ||
| 1125 | { | ||
| 1126 | struct device *dev; | ||
| 1127 | struct hid_device *hdev; | ||
| 1128 | struct picolcd_data *data; | ||
| 1129 | int i, state = 0; | ||
| 1130 | |||
| 1131 | dev = led_cdev->dev->parent; | ||
| 1132 | hdev = container_of(dev, struct hid_device, dev); | ||
| 1133 | data = hid_get_drvdata(hdev); | ||
| 1134 | for (i = 0; i < 8; i++) { | ||
| 1135 | if (led_cdev != data->led[i]) | ||
| 1136 | continue; | ||
| 1137 | state = (data->led_state >> i) & 1; | ||
| 1138 | if (value == LED_OFF && state) { | ||
| 1139 | data->led_state &= ~(1 << i); | ||
| 1140 | picolcd_leds_set(data); | ||
| 1141 | } else if (value != LED_OFF && !state) { | ||
| 1142 | data->led_state |= 1 << i; | ||
| 1143 | picolcd_leds_set(data); | ||
| 1144 | } | ||
| 1145 | break; | ||
| 1146 | } | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev) | ||
| 1150 | { | ||
| 1151 | struct device *dev; | ||
| 1152 | struct hid_device *hdev; | ||
| 1153 | struct picolcd_data *data; | ||
| 1154 | int i, value = 0; | ||
| 1155 | |||
| 1156 | dev = led_cdev->dev->parent; | ||
| 1157 | hdev = container_of(dev, struct hid_device, dev); | ||
| 1158 | data = hid_get_drvdata(hdev); | ||
| 1159 | for (i = 0; i < 8; i++) | ||
| 1160 | if (led_cdev == data->led[i]) { | ||
| 1161 | value = (data->led_state >> i) & 1; | ||
| 1162 | break; | ||
| 1163 | } | ||
| 1164 | return value ? LED_FULL : LED_OFF; | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report) | ||
| 1168 | { | ||
| 1169 | struct device *dev = &data->hdev->dev; | ||
| 1170 | struct led_classdev *led; | ||
| 1171 | size_t name_sz = strlen(dev_name(dev)) + 8; | ||
| 1172 | char *name; | ||
| 1173 | int i, ret = 0; | ||
| 1174 | |||
| 1175 | if (!report) | ||
| 1176 | return -ENODEV; | ||
| 1177 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 1178 | report->field[0]->report_size != 8) { | ||
| 1179 | dev_err(dev, "unsupported LED_STATE report"); | ||
| 1180 | return -EINVAL; | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | for (i = 0; i < 8; i++) { | ||
| 1184 | led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); | ||
| 1185 | if (!led) { | ||
| 1186 | dev_err(dev, "can't allocate memory for LED %d\n", i); | ||
| 1187 | ret = -ENOMEM; | ||
| 1188 | goto err; | ||
| 1189 | } | ||
| 1190 | name = (void *)(&led[1]); | ||
| 1191 | snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i); | ||
| 1192 | led->name = name; | ||
| 1193 | led->brightness = 0; | ||
| 1194 | led->max_brightness = 1; | ||
| 1195 | led->brightness_get = picolcd_led_get_brightness; | ||
| 1196 | led->brightness_set = picolcd_led_set_brightness; | ||
| 1197 | |||
| 1198 | data->led[i] = led; | ||
| 1199 | ret = led_classdev_register(dev, data->led[i]); | ||
| 1200 | if (ret) { | ||
| 1201 | data->led[i] = NULL; | ||
| 1202 | kfree(led); | ||
| 1203 | dev_err(dev, "can't register LED %d\n", i); | ||
| 1204 | goto err; | ||
| 1205 | } | ||
| 1206 | } | ||
| 1207 | return 0; | ||
| 1208 | err: | ||
| 1209 | for (i = 0; i < 8; i++) | ||
| 1210 | if (data->led[i]) { | ||
| 1211 | led = data->led[i]; | ||
| 1212 | data->led[i] = NULL; | ||
| 1213 | led_classdev_unregister(led); | ||
| 1214 | kfree(led); | ||
| 1215 | } | ||
| 1216 | return ret; | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | static void picolcd_exit_leds(struct picolcd_data *data) | ||
| 1220 | { | ||
| 1221 | struct led_classdev *led; | ||
| 1222 | int i; | ||
| 1223 | |||
| 1224 | for (i = 0; i < 8; i++) { | ||
| 1225 | led = data->led[i]; | ||
| 1226 | data->led[i] = NULL; | ||
| 1227 | if (!led) | ||
| 1228 | continue; | ||
| 1229 | led_classdev_unregister(led); | ||
| 1230 | kfree(led); | ||
| 1231 | } | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | #else | ||
| 1235 | static inline int picolcd_init_leds(struct picolcd_data *data, | ||
| 1236 | struct hid_report *report) | ||
| 1237 | { | ||
| 1238 | return 0; | ||
| 1239 | } | ||
| 1240 | static inline void picolcd_exit_leds(struct picolcd_data *data) | ||
| 1241 | { | ||
| 1242 | } | ||
| 1243 | static inline int picolcd_leds_set(struct picolcd_data *data) | ||
| 1244 | { | ||
| 1245 | return 0; | ||
| 1246 | } | ||
| 1247 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
| 1248 | |||
| 1249 | /* | ||
| 1250 | * input class device | ||
| 1251 | */ | ||
| 1252 | static int picolcd_raw_keypad(struct picolcd_data *data, | ||
| 1253 | struct hid_report *report, u8 *raw_data, int size) | ||
| 1254 | { | ||
| 1255 | /* | ||
| 1256 | * Keypad event | ||
| 1257 | * First and second data bytes list currently pressed keys, | ||
| 1258 | * 0x00 means no key and at most 2 keys may be pressed at same time | ||
| 1259 | */ | ||
| 1260 | int i, j; | ||
| 1261 | |||
| 1262 | /* determine newly pressed keys */ | ||
| 1263 | for (i = 0; i < size; i++) { | ||
| 1264 | unsigned int key_code; | ||
| 1265 | if (raw_data[i] == 0) | ||
| 1266 | continue; | ||
| 1267 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
| 1268 | if (data->pressed_keys[j] == raw_data[i]) | ||
| 1269 | goto key_already_down; | ||
| 1270 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
| 1271 | if (data->pressed_keys[j] == 0) { | ||
| 1272 | data->pressed_keys[j] = raw_data[i]; | ||
| 1273 | break; | ||
| 1274 | } | ||
| 1275 | input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]); | ||
| 1276 | if (raw_data[i] < PICOLCD_KEYS) | ||
| 1277 | key_code = data->keycode[raw_data[i]]; | ||
| 1278 | else | ||
| 1279 | key_code = KEY_UNKNOWN; | ||
| 1280 | if (key_code != KEY_UNKNOWN) { | ||
| 1281 | dbg_hid(PICOLCD_NAME " got key press for %u:%d", | ||
| 1282 | raw_data[i], key_code); | ||
| 1283 | input_report_key(data->input_keys, key_code, 1); | ||
| 1284 | } | ||
| 1285 | input_sync(data->input_keys); | ||
| 1286 | key_already_down: | ||
| 1287 | continue; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | /* determine newly released keys */ | ||
| 1291 | for (j = 0; j < sizeof(data->pressed_keys); j++) { | ||
| 1292 | unsigned int key_code; | ||
| 1293 | if (data->pressed_keys[j] == 0) | ||
| 1294 | continue; | ||
| 1295 | for (i = 0; i < size; i++) | ||
| 1296 | if (data->pressed_keys[j] == raw_data[i]) | ||
| 1297 | goto key_still_down; | ||
| 1298 | input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]); | ||
| 1299 | if (data->pressed_keys[j] < PICOLCD_KEYS) | ||
| 1300 | key_code = data->keycode[data->pressed_keys[j]]; | ||
| 1301 | else | ||
| 1302 | key_code = KEY_UNKNOWN; | ||
| 1303 | if (key_code != KEY_UNKNOWN) { | ||
| 1304 | dbg_hid(PICOLCD_NAME " got key release for %u:%d", | ||
| 1305 | data->pressed_keys[j], key_code); | ||
| 1306 | input_report_key(data->input_keys, key_code, 0); | ||
| 1307 | } | ||
| 1308 | input_sync(data->input_keys); | ||
| 1309 | data->pressed_keys[j] = 0; | ||
| 1310 | key_still_down: | ||
| 1311 | continue; | ||
| 1312 | } | ||
| 1313 | return 1; | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | static int picolcd_raw_cir(struct picolcd_data *data, | ||
| 1317 | struct hid_report *report, u8 *raw_data, int size) | ||
| 1318 | { | ||
| 1319 | /* Need understanding of CIR data format to implement ... */ | ||
| 1320 | return 1; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | static int picolcd_check_version(struct hid_device *hdev) | ||
| 1324 | { | ||
| 1325 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 1326 | struct picolcd_pending *verinfo; | ||
| 1327 | int ret = 0; | ||
| 1328 | |||
| 1329 | if (!data) | ||
| 1330 | return -ENODEV; | ||
| 1331 | |||
| 1332 | verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0); | ||
| 1333 | if (!verinfo) { | ||
| 1334 | hid_err(hdev, "no version response from PicoLCD\n"); | ||
| 1335 | return -ENODEV; | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | if (verinfo->raw_size == 2) { | ||
| 1339 | data->version[0] = verinfo->raw_data[1]; | ||
| 1340 | data->version[1] = verinfo->raw_data[0]; | ||
| 1341 | if (data->status & PICOLCD_BOOTLOADER) { | ||
| 1342 | hid_info(hdev, "PicoLCD, bootloader version %d.%d\n", | ||
| 1343 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
| 1344 | } else { | ||
| 1345 | hid_info(hdev, "PicoLCD, firmware version %d.%d\n", | ||
| 1346 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
| 1347 | } | ||
| 1348 | } else { | ||
| 1349 | hid_err(hdev, "confused, got unexpected version response from PicoLCD\n"); | ||
| 1350 | ret = -EINVAL; | ||
| 1351 | } | ||
| 1352 | kfree(verinfo); | ||
| 1353 | return ret; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | /* | ||
| 1357 | * Reset our device and wait for answer to VERSION request | ||
| 1358 | */ | ||
| 1359 | static int picolcd_reset(struct hid_device *hdev) | ||
| 1360 | { | ||
| 1361 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 1362 | struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev); | ||
| 1363 | unsigned long flags; | ||
| 1364 | int error; | ||
| 1365 | |||
| 1366 | if (!data || !report || report->maxfield != 1) | ||
| 1367 | return -ENODEV; | ||
| 1368 | |||
| 1369 | spin_lock_irqsave(&data->lock, flags); | ||
| 1370 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
| 1371 | data->status |= PICOLCD_BOOTLOADER; | ||
| 1372 | |||
| 1373 | /* perform the reset */ | ||
| 1374 | hid_set_field(report->field[0], 0, 1); | ||
| 1375 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | ||
| 1376 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 1377 | |||
| 1378 | error = picolcd_check_version(hdev); | ||
| 1379 | if (error) | ||
| 1380 | return error; | ||
| 1381 | |||
| 1382 | picolcd_resume_lcd(data); | ||
| 1383 | picolcd_resume_backlight(data); | ||
| 1384 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 1385 | if (data->fb_info) | ||
| 1386 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
| 1387 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 1388 | |||
| 1389 | picolcd_leds_set(data); | ||
| 1390 | return 0; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | /* | ||
| 1394 | * The "operation_mode" sysfs attribute | ||
| 1395 | */ | ||
| 1396 | static ssize_t picolcd_operation_mode_show(struct device *dev, | ||
| 1397 | struct device_attribute *attr, char *buf) | ||
| 1398 | { | ||
| 1399 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1400 | |||
| 1401 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 1402 | return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n"); | ||
| 1403 | else | ||
| 1404 | return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n"); | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | static ssize_t picolcd_operation_mode_store(struct device *dev, | ||
| 1408 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 1409 | { | ||
| 1410 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1411 | struct hid_report *report = NULL; | ||
| 1412 | size_t cnt = count; | ||
| 1413 | int timeout = data->opmode_delay; | ||
| 1414 | unsigned long flags; | ||
| 1415 | |||
| 1416 | if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) { | ||
| 1417 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 1418 | report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev); | ||
| 1419 | buf += 3; | ||
| 1420 | cnt -= 3; | ||
| 1421 | } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) { | ||
| 1422 | if (!(data->status & PICOLCD_BOOTLOADER)) | ||
| 1423 | report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev); | ||
| 1424 | buf += 10; | ||
| 1425 | cnt -= 10; | ||
| 1426 | } | ||
| 1427 | if (!report) | ||
| 1428 | return -EINVAL; | ||
| 1429 | |||
| 1430 | while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) | ||
| 1431 | cnt--; | ||
| 1432 | if (cnt != 0) | ||
| 1433 | return -EINVAL; | ||
| 1434 | |||
| 1435 | spin_lock_irqsave(&data->lock, flags); | ||
| 1436 | hid_set_field(report->field[0], 0, timeout & 0xff); | ||
| 1437 | hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff); | ||
| 1438 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 1439 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 1440 | return count; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show, | ||
| 1444 | picolcd_operation_mode_store); | ||
| 1445 | |||
| 1446 | /* | ||
| 1447 | * The "operation_mode_delay" sysfs attribute | ||
| 1448 | */ | ||
| 1449 | static ssize_t picolcd_operation_mode_delay_show(struct device *dev, | ||
| 1450 | struct device_attribute *attr, char *buf) | ||
| 1451 | { | ||
| 1452 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1453 | |||
| 1454 | return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay); | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | static ssize_t picolcd_operation_mode_delay_store(struct device *dev, | ||
| 1458 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 1459 | { | ||
| 1460 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1461 | unsigned u; | ||
| 1462 | if (sscanf(buf, "%u", &u) != 1) | ||
| 1463 | return -EINVAL; | ||
| 1464 | if (u > 30000) | ||
| 1465 | return -EINVAL; | ||
| 1466 | else | ||
| 1467 | data->opmode_delay = u; | ||
| 1468 | return count; | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show, | ||
| 1472 | picolcd_operation_mode_delay_store); | ||
| 1473 | |||
| 1474 | |||
| 1475 | #ifdef CONFIG_DEBUG_FS | ||
| 1476 | /* | ||
| 1477 | * The "reset" file | ||
| 1478 | */ | ||
| 1479 | static int picolcd_debug_reset_show(struct seq_file *f, void *p) | ||
| 1480 | { | ||
| 1481 | if (picolcd_fbinfo((struct picolcd_data *)f->private)) | ||
| 1482 | seq_printf(f, "all fb\n"); | ||
| 1483 | else | ||
| 1484 | seq_printf(f, "all\n"); | ||
| 1485 | return 0; | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | static int picolcd_debug_reset_open(struct inode *inode, struct file *f) | ||
| 1489 | { | ||
| 1490 | return single_open(f, picolcd_debug_reset_show, inode->i_private); | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf, | ||
| 1494 | size_t count, loff_t *ppos) | ||
| 1495 | { | ||
| 1496 | struct picolcd_data *data = ((struct seq_file *)f->private_data)->private; | ||
| 1497 | char buf[32]; | ||
| 1498 | size_t cnt = min(count, sizeof(buf)-1); | ||
| 1499 | if (copy_from_user(buf, user_buf, cnt)) | ||
| 1500 | return -EFAULT; | ||
| 1501 | |||
| 1502 | while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n')) | ||
| 1503 | cnt--; | ||
| 1504 | buf[cnt] = '\0'; | ||
| 1505 | if (strcmp(buf, "all") == 0) { | ||
| 1506 | picolcd_reset(data->hdev); | ||
| 1507 | picolcd_fb_reset(data, 1); | ||
| 1508 | } else if (strcmp(buf, "fb") == 0) { | ||
| 1509 | picolcd_fb_reset(data, 1); | ||
| 1510 | } else { | ||
| 1511 | return -EINVAL; | ||
| 1512 | } | ||
| 1513 | return count; | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | static const struct file_operations picolcd_debug_reset_fops = { | ||
| 1517 | .owner = THIS_MODULE, | ||
| 1518 | .open = picolcd_debug_reset_open, | ||
| 1519 | .read = seq_read, | ||
| 1520 | .llseek = seq_lseek, | ||
| 1521 | .write = picolcd_debug_reset_write, | ||
| 1522 | .release = single_release, | ||
| 1523 | }; | ||
| 1524 | |||
| 1525 | /* | ||
| 1526 | * The "eeprom" file | ||
| 1527 | */ | ||
| 1528 | static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, | ||
| 1529 | size_t s, loff_t *off) | ||
| 1530 | { | ||
| 1531 | struct picolcd_data *data = f->private_data; | ||
| 1532 | struct picolcd_pending *resp; | ||
| 1533 | u8 raw_data[3]; | ||
| 1534 | ssize_t ret = -EIO; | ||
| 1535 | |||
| 1536 | if (s == 0) | ||
| 1537 | return -EINVAL; | ||
| 1538 | if (*off > 0x0ff) | ||
| 1539 | return 0; | ||
| 1540 | |||
| 1541 | /* prepare buffer with info about what we want to read (addr & len) */ | ||
| 1542 | raw_data[0] = *off & 0xff; | ||
| 1543 | raw_data[1] = (*off >> 8) & 0xff; | ||
| 1544 | raw_data[2] = s < 20 ? s : 20; | ||
| 1545 | if (*off + raw_data[2] > 0xff) | ||
| 1546 | raw_data[2] = 0x100 - *off; | ||
| 1547 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data, | ||
| 1548 | sizeof(raw_data)); | ||
| 1549 | if (!resp) | ||
| 1550 | return -EIO; | ||
| 1551 | |||
| 1552 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
| 1553 | /* successful read :) */ | ||
| 1554 | ret = resp->raw_data[2]; | ||
| 1555 | if (ret > s) | ||
| 1556 | ret = s; | ||
| 1557 | if (copy_to_user(u, resp->raw_data+3, ret)) | ||
| 1558 | ret = -EFAULT; | ||
| 1559 | else | ||
| 1560 | *off += ret; | ||
| 1561 | } /* anything else is some kind of IO error */ | ||
| 1562 | |||
| 1563 | kfree(resp); | ||
| 1564 | return ret; | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u, | ||
| 1568 | size_t s, loff_t *off) | ||
| 1569 | { | ||
| 1570 | struct picolcd_data *data = f->private_data; | ||
| 1571 | struct picolcd_pending *resp; | ||
| 1572 | ssize_t ret = -EIO; | ||
| 1573 | u8 raw_data[23]; | ||
| 1574 | |||
| 1575 | if (s == 0) | ||
| 1576 | return -EINVAL; | ||
| 1577 | if (*off > 0x0ff) | ||
| 1578 | return -ENOSPC; | ||
| 1579 | |||
| 1580 | memset(raw_data, 0, sizeof(raw_data)); | ||
| 1581 | raw_data[0] = *off & 0xff; | ||
| 1582 | raw_data[1] = (*off >> 8) & 0xff; | ||
| 1583 | raw_data[2] = min((size_t)20, s); | ||
| 1584 | if (*off + raw_data[2] > 0xff) | ||
| 1585 | raw_data[2] = 0x100 - *off; | ||
| 1586 | |||
| 1587 | if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2]))) | ||
| 1588 | return -EFAULT; | ||
| 1589 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data, | ||
| 1590 | sizeof(raw_data)); | ||
| 1591 | |||
| 1592 | if (!resp) | ||
| 1593 | return -EIO; | ||
| 1594 | |||
| 1595 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
| 1596 | /* check if written data matches */ | ||
| 1597 | if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) { | ||
| 1598 | *off += raw_data[2]; | ||
| 1599 | ret = raw_data[2]; | ||
| 1600 | } | ||
| 1601 | } | ||
| 1602 | kfree(resp); | ||
| 1603 | return ret; | ||
| 1604 | } | ||
| 1605 | |||
| 1606 | /* | ||
| 1607 | * Notes: | ||
| 1608 | * - read/write happens in chunks of at most 20 bytes, it's up to userspace | ||
| 1609 | * to loop in order to get more data. | ||
| 1610 | * - on write errors on otherwise correct write request the bytes | ||
| 1611 | * that should have been written are in undefined state. | ||
| 1612 | */ | ||
| 1613 | static const struct file_operations picolcd_debug_eeprom_fops = { | ||
| 1614 | .owner = THIS_MODULE, | ||
| 1615 | .open = simple_open, | ||
| 1616 | .read = picolcd_debug_eeprom_read, | ||
| 1617 | .write = picolcd_debug_eeprom_write, | ||
| 1618 | .llseek = generic_file_llseek, | ||
| 1619 | }; | ||
| 1620 | |||
| 1621 | /* | ||
| 1622 | * The "flash" file | ||
| 1623 | */ | ||
| 1624 | /* record a flash address to buf (bounds check to be done by caller) */ | ||
| 1625 | static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off) | ||
| 1626 | { | ||
| 1627 | buf[0] = off & 0xff; | ||
| 1628 | buf[1] = (off >> 8) & 0xff; | ||
| 1629 | if (data->addr_sz == 3) | ||
| 1630 | buf[2] = (off >> 16) & 0xff; | ||
| 1631 | return data->addr_sz == 2 ? 2 : 3; | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | /* read a given size of data (bounds check to be done by caller) */ | ||
| 1635 | static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id, | ||
| 1636 | char __user *u, size_t s, loff_t *off) | ||
| 1637 | { | ||
| 1638 | struct picolcd_pending *resp; | ||
| 1639 | u8 raw_data[4]; | ||
| 1640 | ssize_t ret = 0; | ||
| 1641 | int len_off, err = -EIO; | ||
| 1642 | |||
| 1643 | while (s > 0) { | ||
| 1644 | err = -EIO; | ||
| 1645 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 1646 | raw_data[len_off] = s > 32 ? 32 : s; | ||
| 1647 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1); | ||
| 1648 | if (!resp || !resp->in_report) | ||
| 1649 | goto skip; | ||
| 1650 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 1651 | resp->in_report->id == REPORT_BL_READ_MEMORY) { | ||
| 1652 | if (memcmp(raw_data, resp->raw_data, len_off+1) != 0) | ||
| 1653 | goto skip; | ||
| 1654 | if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) { | ||
| 1655 | err = -EFAULT; | ||
| 1656 | goto skip; | ||
| 1657 | } | ||
| 1658 | *off += raw_data[len_off]; | ||
| 1659 | s -= raw_data[len_off]; | ||
| 1660 | ret += raw_data[len_off]; | ||
| 1661 | err = 0; | ||
| 1662 | } | ||
| 1663 | skip: | ||
| 1664 | kfree(resp); | ||
| 1665 | if (err) | ||
| 1666 | return ret > 0 ? ret : err; | ||
| 1667 | } | ||
| 1668 | return ret; | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u, | ||
| 1672 | size_t s, loff_t *off) | ||
| 1673 | { | ||
| 1674 | struct picolcd_data *data = f->private_data; | ||
| 1675 | |||
| 1676 | if (s == 0) | ||
| 1677 | return -EINVAL; | ||
| 1678 | if (*off > 0x05fff) | ||
| 1679 | return 0; | ||
| 1680 | if (*off + s > 0x05fff) | ||
| 1681 | s = 0x06000 - *off; | ||
| 1682 | |||
| 1683 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 1684 | return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off); | ||
| 1685 | else | ||
| 1686 | return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off); | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | /* erase block aligned to 64bytes boundary */ | ||
| 1690 | static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id, | ||
| 1691 | loff_t *off) | ||
| 1692 | { | ||
| 1693 | struct picolcd_pending *resp; | ||
| 1694 | u8 raw_data[3]; | ||
| 1695 | int len_off; | ||
| 1696 | ssize_t ret = -EIO; | ||
| 1697 | |||
| 1698 | if (*off & 0x3f) | ||
| 1699 | return -EINVAL; | ||
| 1700 | |||
| 1701 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 1702 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off); | ||
| 1703 | if (!resp || !resp->in_report) | ||
| 1704 | goto skip; | ||
| 1705 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 1706 | resp->in_report->id == REPORT_BL_ERASE_MEMORY) { | ||
| 1707 | if (memcmp(raw_data, resp->raw_data, len_off) != 0) | ||
| 1708 | goto skip; | ||
| 1709 | ret = 0; | ||
| 1710 | } | ||
| 1711 | skip: | ||
| 1712 | kfree(resp); | ||
| 1713 | return ret; | ||
| 1714 | } | ||
| 1715 | |||
| 1716 | /* write a given size of data (bounds check to be done by caller) */ | ||
| 1717 | static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id, | ||
| 1718 | const char __user *u, size_t s, loff_t *off) | ||
| 1719 | { | ||
| 1720 | struct picolcd_pending *resp; | ||
| 1721 | u8 raw_data[36]; | ||
| 1722 | ssize_t ret = 0; | ||
| 1723 | int len_off, err = -EIO; | ||
| 1724 | |||
| 1725 | while (s > 0) { | ||
| 1726 | err = -EIO; | ||
| 1727 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 1728 | raw_data[len_off] = s > 32 ? 32 : s; | ||
| 1729 | if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) { | ||
| 1730 | err = -EFAULT; | ||
| 1731 | break; | ||
| 1732 | } | ||
| 1733 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, | ||
| 1734 | len_off+1+raw_data[len_off]); | ||
| 1735 | if (!resp || !resp->in_report) | ||
| 1736 | goto skip; | ||
| 1737 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 1738 | resp->in_report->id == REPORT_BL_WRITE_MEMORY) { | ||
| 1739 | if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0) | ||
| 1740 | goto skip; | ||
| 1741 | *off += raw_data[len_off]; | ||
| 1742 | s -= raw_data[len_off]; | ||
| 1743 | ret += raw_data[len_off]; | ||
| 1744 | err = 0; | ||
| 1745 | } | ||
| 1746 | skip: | ||
| 1747 | kfree(resp); | ||
| 1748 | if (err) | ||
| 1749 | break; | ||
| 1750 | } | ||
| 1751 | return ret > 0 ? ret : err; | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u, | ||
| 1755 | size_t s, loff_t *off) | ||
| 1756 | { | ||
| 1757 | struct picolcd_data *data = f->private_data; | ||
| 1758 | ssize_t err, ret = 0; | ||
| 1759 | int report_erase, report_write; | ||
| 1760 | |||
| 1761 | if (s == 0) | ||
| 1762 | return -EINVAL; | ||
| 1763 | if (*off > 0x5fff) | ||
| 1764 | return -ENOSPC; | ||
| 1765 | if (s & 0x3f) | ||
| 1766 | return -EINVAL; | ||
| 1767 | if (*off & 0x3f) | ||
| 1768 | return -EINVAL; | ||
| 1769 | |||
| 1770 | if (data->status & PICOLCD_BOOTLOADER) { | ||
| 1771 | report_erase = REPORT_BL_ERASE_MEMORY; | ||
| 1772 | report_write = REPORT_BL_WRITE_MEMORY; | ||
| 1773 | } else { | ||
| 1774 | report_erase = REPORT_ERASE_MEMORY; | ||
| 1775 | report_write = REPORT_WRITE_MEMORY; | ||
| 1776 | } | ||
| 1777 | mutex_lock(&data->mutex_flash); | ||
| 1778 | while (s > 0) { | ||
| 1779 | err = _picolcd_flash_erase64(data, report_erase, off); | ||
| 1780 | if (err) | ||
| 1781 | break; | ||
| 1782 | err = _picolcd_flash_write(data, report_write, u, 64, off); | ||
| 1783 | if (err < 0) | ||
| 1784 | break; | ||
| 1785 | ret += err; | ||
| 1786 | *off += err; | ||
| 1787 | s -= err; | ||
| 1788 | if (err != 64) | ||
| 1789 | break; | ||
| 1790 | } | ||
| 1791 | mutex_unlock(&data->mutex_flash); | ||
| 1792 | return ret > 0 ? ret : err; | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | /* | ||
| 1796 | * Notes: | ||
| 1797 | * - concurrent writing is prevented by mutex and all writes must be | ||
| 1798 | * n*64 bytes and 64-byte aligned, each write being preceded by an | ||
| 1799 | * ERASE which erases a 64byte block. | ||
| 1800 | * If less than requested was written or an error is returned for an | ||
| 1801 | * otherwise correct write request the next 64-byte block which should | ||
| 1802 | * have been written is in undefined state (mostly: original, erased, | ||
| 1803 | * (half-)written with write error) | ||
| 1804 | * - reading can happen without special restriction | ||
| 1805 | */ | ||
| 1806 | static const struct file_operations picolcd_debug_flash_fops = { | ||
| 1807 | .owner = THIS_MODULE, | ||
| 1808 | .open = simple_open, | ||
| 1809 | .read = picolcd_debug_flash_read, | ||
| 1810 | .write = picolcd_debug_flash_write, | ||
| 1811 | .llseek = generic_file_llseek, | ||
| 1812 | }; | ||
| 1813 | |||
| 1814 | |||
| 1815 | /* | ||
| 1816 | * Helper code for HID report level dumping/debugging | ||
| 1817 | */ | ||
| 1818 | static const char *error_codes[] = { | ||
| 1819 | "success", "parameter missing", "data_missing", "block readonly", | ||
| 1820 | "block not erasable", "block too big", "section overflow", | ||
| 1821 | "invalid command length", "invalid data length", | ||
| 1822 | }; | ||
| 1823 | |||
| 1824 | static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, | ||
| 1825 | const size_t data_len) | ||
| 1826 | { | ||
| 1827 | int i, j; | ||
| 1828 | for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) { | ||
| 1829 | dst[j++] = hex_asc[(data[i] >> 4) & 0x0f]; | ||
| 1830 | dst[j++] = hex_asc[data[i] & 0x0f]; | ||
| 1831 | dst[j++] = ' '; | ||
| 1832 | } | ||
| 1833 | if (j < dst_sz) { | ||
| 1834 | dst[j--] = '\0'; | ||
| 1835 | dst[j] = '\n'; | ||
| 1836 | } else | ||
| 1837 | dst[j] = '\0'; | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
| 1841 | struct hid_device *hdev, struct hid_report *report) | ||
| 1842 | { | ||
| 1843 | u8 raw_data[70]; | ||
| 1844 | int raw_size = (report->size >> 3) + 1; | ||
| 1845 | char *buff; | ||
| 1846 | #define BUFF_SZ 256 | ||
| 1847 | |||
| 1848 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
| 1849 | if (list_empty(&hdev->debug_list)) | ||
| 1850 | return; | ||
| 1851 | |||
| 1852 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
| 1853 | if (!buff) | ||
| 1854 | return; | ||
| 1855 | |||
| 1856 | snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", | ||
| 1857 | report->id, raw_size); | ||
| 1858 | hid_debug_event(hdev, buff); | ||
| 1859 | if (raw_size + 5 > sizeof(raw_data)) { | ||
| 1860 | kfree(buff); | ||
| 1861 | hid_debug_event(hdev, " TOO BIG\n"); | ||
| 1862 | return; | ||
| 1863 | } else { | ||
| 1864 | raw_data[0] = report->id; | ||
| 1865 | hid_output_report(report, raw_data); | ||
| 1866 | dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); | ||
| 1867 | hid_debug_event(hdev, buff); | ||
| 1868 | } | ||
| 1869 | |||
| 1870 | switch (report->id) { | ||
| 1871 | case REPORT_LED_STATE: | ||
| 1872 | /* 1 data byte with GPO state */ | ||
| 1873 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1874 | "REPORT_LED_STATE", report->id, raw_size-1); | ||
| 1875 | hid_debug_event(hdev, buff); | ||
| 1876 | snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]); | ||
| 1877 | hid_debug_event(hdev, buff); | ||
| 1878 | break; | ||
| 1879 | case REPORT_BRIGHTNESS: | ||
| 1880 | /* 1 data byte with brightness */ | ||
| 1881 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1882 | "REPORT_BRIGHTNESS", report->id, raw_size-1); | ||
| 1883 | hid_debug_event(hdev, buff); | ||
| 1884 | snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]); | ||
| 1885 | hid_debug_event(hdev, buff); | ||
| 1886 | break; | ||
| 1887 | case REPORT_CONTRAST: | ||
| 1888 | /* 1 data byte with contrast */ | ||
| 1889 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1890 | "REPORT_CONTRAST", report->id, raw_size-1); | ||
| 1891 | hid_debug_event(hdev, buff); | ||
| 1892 | snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]); | ||
| 1893 | hid_debug_event(hdev, buff); | ||
| 1894 | break; | ||
| 1895 | case REPORT_RESET: | ||
| 1896 | /* 2 data bytes with reset duration in ms */ | ||
| 1897 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1898 | "REPORT_RESET", report->id, raw_size-1); | ||
| 1899 | hid_debug_event(hdev, buff); | ||
| 1900 | snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n", | ||
| 1901 | raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]); | ||
| 1902 | hid_debug_event(hdev, buff); | ||
| 1903 | break; | ||
| 1904 | case REPORT_LCD_CMD: | ||
| 1905 | /* 63 data bytes with LCD commands */ | ||
| 1906 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1907 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 1908 | hid_debug_event(hdev, buff); | ||
| 1909 | /* TODO: format decoding */ | ||
| 1910 | break; | ||
| 1911 | case REPORT_LCD_DATA: | ||
| 1912 | /* 63 data bytes with LCD data */ | ||
| 1913 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1914 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 1915 | /* TODO: format decoding */ | ||
| 1916 | hid_debug_event(hdev, buff); | ||
| 1917 | break; | ||
| 1918 | case REPORT_LCD_CMD_DATA: | ||
| 1919 | /* 63 data bytes with LCD commands and data */ | ||
| 1920 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1921 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 1922 | /* TODO: format decoding */ | ||
| 1923 | hid_debug_event(hdev, buff); | ||
| 1924 | break; | ||
| 1925 | case REPORT_EE_READ: | ||
| 1926 | /* 3 data bytes with read area description */ | ||
| 1927 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1928 | "REPORT_EE_READ", report->id, raw_size-1); | ||
| 1929 | hid_debug_event(hdev, buff); | ||
| 1930 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 1931 | raw_data[2], raw_data[1]); | ||
| 1932 | hid_debug_event(hdev, buff); | ||
| 1933 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 1934 | hid_debug_event(hdev, buff); | ||
| 1935 | break; | ||
| 1936 | case REPORT_EE_WRITE: | ||
| 1937 | /* 3+1..20 data bytes with write area description */ | ||
| 1938 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1939 | "REPORT_EE_WRITE", report->id, raw_size-1); | ||
| 1940 | hid_debug_event(hdev, buff); | ||
| 1941 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 1942 | raw_data[2], raw_data[1]); | ||
| 1943 | hid_debug_event(hdev, buff); | ||
| 1944 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 1945 | hid_debug_event(hdev, buff); | ||
| 1946 | if (raw_data[3] == 0) { | ||
| 1947 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 1948 | } else if (raw_data[3] + 4 <= raw_size) { | ||
| 1949 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 1950 | hid_debug_event(hdev, buff); | ||
| 1951 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 1952 | } else { | ||
| 1953 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 1954 | } | ||
| 1955 | hid_debug_event(hdev, buff); | ||
| 1956 | break; | ||
| 1957 | case REPORT_ERASE_MEMORY: | ||
| 1958 | case REPORT_BL_ERASE_MEMORY: | ||
| 1959 | /* 3 data bytes with pointer inside erase block */ | ||
| 1960 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1961 | "REPORT_ERASE_MEMORY", report->id, raw_size-1); | ||
| 1962 | hid_debug_event(hdev, buff); | ||
| 1963 | switch (data->addr_sz) { | ||
| 1964 | case 2: | ||
| 1965 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n", | ||
| 1966 | raw_data[2], raw_data[1]); | ||
| 1967 | break; | ||
| 1968 | case 3: | ||
| 1969 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n", | ||
| 1970 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 1971 | break; | ||
| 1972 | default: | ||
| 1973 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 1974 | } | ||
| 1975 | hid_debug_event(hdev, buff); | ||
| 1976 | break; | ||
| 1977 | case REPORT_READ_MEMORY: | ||
| 1978 | case REPORT_BL_READ_MEMORY: | ||
| 1979 | /* 4 data bytes with read area description */ | ||
| 1980 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1981 | "REPORT_READ_MEMORY", report->id, raw_size-1); | ||
| 1982 | hid_debug_event(hdev, buff); | ||
| 1983 | switch (data->addr_sz) { | ||
| 1984 | case 2: | ||
| 1985 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 1986 | raw_data[2], raw_data[1]); | ||
| 1987 | hid_debug_event(hdev, buff); | ||
| 1988 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 1989 | break; | ||
| 1990 | case 3: | ||
| 1991 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 1992 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 1993 | hid_debug_event(hdev, buff); | ||
| 1994 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 1995 | break; | ||
| 1996 | default: | ||
| 1997 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 1998 | } | ||
| 1999 | hid_debug_event(hdev, buff); | ||
| 2000 | break; | ||
| 2001 | case REPORT_WRITE_MEMORY: | ||
| 2002 | case REPORT_BL_WRITE_MEMORY: | ||
| 2003 | /* 4+1..32 data bytes with write adrea description */ | ||
| 2004 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2005 | "REPORT_WRITE_MEMORY", report->id, raw_size-1); | ||
| 2006 | hid_debug_event(hdev, buff); | ||
| 2007 | switch (data->addr_sz) { | ||
| 2008 | case 2: | ||
| 2009 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 2010 | raw_data[2], raw_data[1]); | ||
| 2011 | hid_debug_event(hdev, buff); | ||
| 2012 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 2013 | hid_debug_event(hdev, buff); | ||
| 2014 | if (raw_data[3] == 0) { | ||
| 2015 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2016 | } else if (raw_data[3] + 4 <= raw_size) { | ||
| 2017 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2018 | hid_debug_event(hdev, buff); | ||
| 2019 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 2020 | } else { | ||
| 2021 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2022 | } | ||
| 2023 | break; | ||
| 2024 | case 3: | ||
| 2025 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 2026 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 2027 | hid_debug_event(hdev, buff); | ||
| 2028 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 2029 | hid_debug_event(hdev, buff); | ||
| 2030 | if (raw_data[4] == 0) { | ||
| 2031 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2032 | } else if (raw_data[4] + 5 <= raw_size) { | ||
| 2033 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2034 | hid_debug_event(hdev, buff); | ||
| 2035 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
| 2036 | } else { | ||
| 2037 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2038 | } | ||
| 2039 | break; | ||
| 2040 | default: | ||
| 2041 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 2042 | } | ||
| 2043 | hid_debug_event(hdev, buff); | ||
| 2044 | break; | ||
| 2045 | case REPORT_SPLASH_RESTART: | ||
| 2046 | /* TODO */ | ||
| 2047 | break; | ||
| 2048 | case REPORT_EXIT_KEYBOARD: | ||
| 2049 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2050 | "REPORT_EXIT_KEYBOARD", report->id, raw_size-1); | ||
| 2051 | hid_debug_event(hdev, buff); | ||
| 2052 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
| 2053 | raw_data[1] | (raw_data[2] << 8), | ||
| 2054 | raw_data[2], raw_data[1]); | ||
| 2055 | hid_debug_event(hdev, buff); | ||
| 2056 | break; | ||
| 2057 | case REPORT_VERSION: | ||
| 2058 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2059 | "REPORT_VERSION", report->id, raw_size-1); | ||
| 2060 | hid_debug_event(hdev, buff); | ||
| 2061 | break; | ||
| 2062 | case REPORT_DEVID: | ||
| 2063 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2064 | "REPORT_DEVID", report->id, raw_size-1); | ||
| 2065 | hid_debug_event(hdev, buff); | ||
| 2066 | break; | ||
| 2067 | case REPORT_SPLASH_SIZE: | ||
| 2068 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2069 | "REPORT_SPLASH_SIZE", report->id, raw_size-1); | ||
| 2070 | hid_debug_event(hdev, buff); | ||
| 2071 | break; | ||
| 2072 | case REPORT_HOOK_VERSION: | ||
| 2073 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2074 | "REPORT_HOOK_VERSION", report->id, raw_size-1); | ||
| 2075 | hid_debug_event(hdev, buff); | ||
| 2076 | break; | ||
| 2077 | case REPORT_EXIT_FLASHER: | ||
| 2078 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2079 | "REPORT_VERSION", report->id, raw_size-1); | ||
| 2080 | hid_debug_event(hdev, buff); | ||
| 2081 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
| 2082 | raw_data[1] | (raw_data[2] << 8), | ||
| 2083 | raw_data[2], raw_data[1]); | ||
| 2084 | hid_debug_event(hdev, buff); | ||
| 2085 | break; | ||
| 2086 | default: | ||
| 2087 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 2088 | "<unknown>", report->id, raw_size-1); | ||
| 2089 | hid_debug_event(hdev, buff); | ||
| 2090 | break; | ||
| 2091 | } | ||
| 2092 | wake_up_interruptible(&hdev->debug_wait); | ||
| 2093 | kfree(buff); | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | static void picolcd_debug_raw_event(struct picolcd_data *data, | ||
| 2097 | struct hid_device *hdev, struct hid_report *report, | ||
| 2098 | u8 *raw_data, int size) | ||
| 2099 | { | ||
| 2100 | char *buff; | ||
| 2101 | |||
| 2102 | #define BUFF_SZ 256 | ||
| 2103 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
| 2104 | if (!hdev->debug_events) | ||
| 2105 | return; | ||
| 2106 | |||
| 2107 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
| 2108 | if (!buff) | ||
| 2109 | return; | ||
| 2110 | |||
| 2111 | switch (report->id) { | ||
| 2112 | case REPORT_ERROR_CODE: | ||
| 2113 | /* 2 data bytes with affected report and error code */ | ||
| 2114 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2115 | "REPORT_ERROR_CODE", report->id, size-1); | ||
| 2116 | hid_debug_event(hdev, buff); | ||
| 2117 | if (raw_data[2] < ARRAY_SIZE(error_codes)) | ||
| 2118 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n", | ||
| 2119 | raw_data[2], error_codes[raw_data[2]], raw_data[1]); | ||
| 2120 | else | ||
| 2121 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n", | ||
| 2122 | raw_data[2], raw_data[1]); | ||
| 2123 | hid_debug_event(hdev, buff); | ||
| 2124 | break; | ||
| 2125 | case REPORT_KEY_STATE: | ||
| 2126 | /* 2 data bytes with key state */ | ||
| 2127 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2128 | "REPORT_KEY_STATE", report->id, size-1); | ||
| 2129 | hid_debug_event(hdev, buff); | ||
| 2130 | if (raw_data[1] == 0) | ||
| 2131 | snprintf(buff, BUFF_SZ, "\tNo key pressed\n"); | ||
| 2132 | else if (raw_data[2] == 0) | ||
| 2133 | snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n", | ||
| 2134 | raw_data[1], raw_data[1]); | ||
| 2135 | else | ||
| 2136 | snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n", | ||
| 2137 | raw_data[1], raw_data[1], raw_data[2], raw_data[2]); | ||
| 2138 | hid_debug_event(hdev, buff); | ||
| 2139 | break; | ||
| 2140 | case REPORT_IR_DATA: | ||
| 2141 | /* Up to 20 byes of IR scancode data */ | ||
| 2142 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2143 | "REPORT_IR_DATA", report->id, size-1); | ||
| 2144 | hid_debug_event(hdev, buff); | ||
| 2145 | if (raw_data[1] == 0) { | ||
| 2146 | snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n"); | ||
| 2147 | hid_debug_event(hdev, buff); | ||
| 2148 | } else if (raw_data[1] + 1 <= size) { | ||
| 2149 | snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ", | ||
| 2150 | raw_data[1]-1); | ||
| 2151 | hid_debug_event(hdev, buff); | ||
| 2152 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1); | ||
| 2153 | hid_debug_event(hdev, buff); | ||
| 2154 | } else { | ||
| 2155 | snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n", | ||
| 2156 | raw_data[1]-1); | ||
| 2157 | hid_debug_event(hdev, buff); | ||
| 2158 | } | ||
| 2159 | break; | ||
| 2160 | case REPORT_EE_DATA: | ||
| 2161 | /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */ | ||
| 2162 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2163 | "REPORT_EE_DATA", report->id, size-1); | ||
| 2164 | hid_debug_event(hdev, buff); | ||
| 2165 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 2166 | raw_data[2], raw_data[1]); | ||
| 2167 | hid_debug_event(hdev, buff); | ||
| 2168 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 2169 | hid_debug_event(hdev, buff); | ||
| 2170 | if (raw_data[3] == 0) { | ||
| 2171 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2172 | hid_debug_event(hdev, buff); | ||
| 2173 | } else if (raw_data[3] + 4 <= size) { | ||
| 2174 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2175 | hid_debug_event(hdev, buff); | ||
| 2176 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 2177 | hid_debug_event(hdev, buff); | ||
| 2178 | } else { | ||
| 2179 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2180 | hid_debug_event(hdev, buff); | ||
| 2181 | } | ||
| 2182 | break; | ||
| 2183 | case REPORT_MEMORY: | ||
| 2184 | /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */ | ||
| 2185 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2186 | "REPORT_MEMORY", report->id, size-1); | ||
| 2187 | hid_debug_event(hdev, buff); | ||
| 2188 | switch (data->addr_sz) { | ||
| 2189 | case 2: | ||
| 2190 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 2191 | raw_data[2], raw_data[1]); | ||
| 2192 | hid_debug_event(hdev, buff); | ||
| 2193 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 2194 | hid_debug_event(hdev, buff); | ||
| 2195 | if (raw_data[3] == 0) { | ||
| 2196 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2197 | } else if (raw_data[3] + 4 <= size) { | ||
| 2198 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2199 | hid_debug_event(hdev, buff); | ||
| 2200 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 2201 | } else { | ||
| 2202 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2203 | } | ||
| 2204 | break; | ||
| 2205 | case 3: | ||
| 2206 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 2207 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 2208 | hid_debug_event(hdev, buff); | ||
| 2209 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 2210 | hid_debug_event(hdev, buff); | ||
| 2211 | if (raw_data[4] == 0) { | ||
| 2212 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2213 | } else if (raw_data[4] + 5 <= size) { | ||
| 2214 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2215 | hid_debug_event(hdev, buff); | ||
| 2216 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
| 2217 | } else { | ||
| 2218 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2219 | } | ||
| 2220 | break; | ||
| 2221 | default: | ||
| 2222 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 2223 | } | ||
| 2224 | hid_debug_event(hdev, buff); | ||
| 2225 | break; | ||
| 2226 | case REPORT_VERSION: | ||
| 2227 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2228 | "REPORT_VERSION", report->id, size-1); | ||
| 2229 | hid_debug_event(hdev, buff); | ||
| 2230 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
| 2231 | raw_data[2], raw_data[1]); | ||
| 2232 | hid_debug_event(hdev, buff); | ||
| 2233 | break; | ||
| 2234 | case REPORT_BL_ERASE_MEMORY: | ||
| 2235 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2236 | "REPORT_BL_ERASE_MEMORY", report->id, size-1); | ||
| 2237 | hid_debug_event(hdev, buff); | ||
| 2238 | /* TODO */ | ||
| 2239 | break; | ||
| 2240 | case REPORT_BL_READ_MEMORY: | ||
| 2241 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2242 | "REPORT_BL_READ_MEMORY", report->id, size-1); | ||
| 2243 | hid_debug_event(hdev, buff); | ||
| 2244 | /* TODO */ | ||
| 2245 | break; | ||
| 2246 | case REPORT_BL_WRITE_MEMORY: | ||
| 2247 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2248 | "REPORT_BL_WRITE_MEMORY", report->id, size-1); | ||
| 2249 | hid_debug_event(hdev, buff); | ||
| 2250 | /* TODO */ | ||
| 2251 | break; | ||
| 2252 | case REPORT_DEVID: | ||
| 2253 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2254 | "REPORT_DEVID", report->id, size-1); | ||
| 2255 | hid_debug_event(hdev, buff); | ||
| 2256 | snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n", | ||
| 2257 | raw_data[1], raw_data[2], raw_data[3], raw_data[4]); | ||
| 2258 | hid_debug_event(hdev, buff); | ||
| 2259 | snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n", | ||
| 2260 | raw_data[5]); | ||
| 2261 | hid_debug_event(hdev, buff); | ||
| 2262 | break; | ||
| 2263 | case REPORT_SPLASH_SIZE: | ||
| 2264 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2265 | "REPORT_SPLASH_SIZE", report->id, size-1); | ||
| 2266 | hid_debug_event(hdev, buff); | ||
| 2267 | snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n", | ||
| 2268 | (raw_data[2] << 8) | raw_data[1]); | ||
| 2269 | hid_debug_event(hdev, buff); | ||
| 2270 | snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n", | ||
| 2271 | (raw_data[4] << 8) | raw_data[3]); | ||
| 2272 | hid_debug_event(hdev, buff); | ||
| 2273 | break; | ||
| 2274 | case REPORT_HOOK_VERSION: | ||
| 2275 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2276 | "REPORT_HOOK_VERSION", report->id, size-1); | ||
| 2277 | hid_debug_event(hdev, buff); | ||
| 2278 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
| 2279 | raw_data[1], raw_data[2]); | ||
| 2280 | hid_debug_event(hdev, buff); | ||
| 2281 | break; | ||
| 2282 | default: | ||
| 2283 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2284 | "<unknown>", report->id, size-1); | ||
| 2285 | hid_debug_event(hdev, buff); | ||
| 2286 | break; | ||
| 2287 | } | ||
| 2288 | wake_up_interruptible(&hdev->debug_wait); | ||
| 2289 | kfree(buff); | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | static void picolcd_init_devfs(struct picolcd_data *data, | ||
| 2293 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
| 2294 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
| 2295 | struct hid_report *reset) | ||
| 2296 | { | ||
| 2297 | struct hid_device *hdev = data->hdev; | ||
| 2298 | |||
| 2299 | mutex_init(&data->mutex_flash); | ||
| 2300 | |||
| 2301 | /* reset */ | ||
| 2302 | if (reset) | ||
| 2303 | data->debug_reset = debugfs_create_file("reset", 0600, | ||
| 2304 | hdev->debug_dir, data, &picolcd_debug_reset_fops); | ||
| 2305 | |||
| 2306 | /* eeprom */ | ||
| 2307 | if (eeprom_r || eeprom_w) | ||
| 2308 | data->debug_eeprom = debugfs_create_file("eeprom", | ||
| 2309 | (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0), | ||
| 2310 | hdev->debug_dir, data, &picolcd_debug_eeprom_fops); | ||
| 2311 | |||
| 2312 | /* flash */ | ||
| 2313 | if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8) | ||
| 2314 | data->addr_sz = flash_r->field[0]->report_count - 1; | ||
| 2315 | else | ||
| 2316 | data->addr_sz = -1; | ||
| 2317 | if (data->addr_sz == 2 || data->addr_sz == 3) { | ||
| 2318 | data->debug_flash = debugfs_create_file("flash", | ||
| 2319 | (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0), | ||
| 2320 | hdev->debug_dir, data, &picolcd_debug_flash_fops); | ||
| 2321 | } else if (flash_r || flash_w) | ||
| 2322 | hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n"); | ||
| 2323 | } | ||
| 2324 | |||
| 2325 | static void picolcd_exit_devfs(struct picolcd_data *data) | ||
| 2326 | { | ||
| 2327 | struct dentry *dent; | ||
| 2328 | |||
| 2329 | dent = data->debug_reset; | ||
| 2330 | data->debug_reset = NULL; | ||
| 2331 | if (dent) | ||
| 2332 | debugfs_remove(dent); | ||
| 2333 | dent = data->debug_eeprom; | ||
| 2334 | data->debug_eeprom = NULL; | ||
| 2335 | if (dent) | ||
| 2336 | debugfs_remove(dent); | ||
| 2337 | dent = data->debug_flash; | ||
| 2338 | data->debug_flash = NULL; | ||
| 2339 | if (dent) | ||
| 2340 | debugfs_remove(dent); | ||
| 2341 | mutex_destroy(&data->mutex_flash); | ||
| 2342 | } | ||
| 2343 | #else | ||
| 2344 | static inline void picolcd_debug_raw_event(struct picolcd_data *data, | ||
| 2345 | struct hid_device *hdev, struct hid_report *report, | ||
| 2346 | u8 *raw_data, int size) | ||
| 2347 | { | ||
| 2348 | } | ||
| 2349 | static inline void picolcd_init_devfs(struct picolcd_data *data, | ||
| 2350 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
| 2351 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
| 2352 | struct hid_report *reset) | ||
| 2353 | { | ||
| 2354 | } | ||
| 2355 | static inline void picolcd_exit_devfs(struct picolcd_data *data) | ||
| 2356 | { | ||
| 2357 | } | ||
| 2358 | #endif /* CONFIG_DEBUG_FS */ | ||
| 2359 | |||
| 2360 | /* | ||
| 2361 | * Handle raw report as sent by device | ||
| 2362 | */ | ||
| 2363 | static int picolcd_raw_event(struct hid_device *hdev, | ||
| 2364 | struct hid_report *report, u8 *raw_data, int size) | ||
| 2365 | { | ||
| 2366 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 2367 | unsigned long flags; | ||
| 2368 | int ret = 0; | ||
| 2369 | |||
| 2370 | if (!data) | ||
| 2371 | return 1; | ||
| 2372 | |||
| 2373 | if (report->id == REPORT_KEY_STATE) { | ||
| 2374 | if (data->input_keys) | ||
| 2375 | ret = picolcd_raw_keypad(data, report, raw_data+1, size-1); | ||
| 2376 | } else if (report->id == REPORT_IR_DATA) { | ||
| 2377 | if (data->input_cir) | ||
| 2378 | ret = picolcd_raw_cir(data, report, raw_data+1, size-1); | ||
| 2379 | } else { | ||
| 2380 | spin_lock_irqsave(&data->lock, flags); | ||
| 2381 | /* | ||
| 2382 | * We let the caller of picolcd_send_and_wait() check if the | ||
| 2383 | * report we got is one of the expected ones or not. | ||
| 2384 | */ | ||
| 2385 | if (data->pending) { | ||
| 2386 | memcpy(data->pending->raw_data, raw_data+1, size-1); | ||
| 2387 | data->pending->raw_size = size-1; | ||
| 2388 | data->pending->in_report = report; | ||
| 2389 | complete(&data->pending->ready); | ||
| 2390 | } | ||
| 2391 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 2392 | } | ||
| 2393 | |||
| 2394 | picolcd_debug_raw_event(data, hdev, report, raw_data, size); | ||
| 2395 | return 1; | ||
| 2396 | } | ||
| 2397 | |||
| 2398 | #ifdef CONFIG_PM | ||
| 2399 | static int picolcd_suspend(struct hid_device *hdev, pm_message_t message) | ||
| 2400 | { | ||
| 2401 | if (PMSG_IS_AUTO(message)) | ||
| 2402 | return 0; | ||
| 2403 | |||
| 2404 | picolcd_suspend_backlight(hid_get_drvdata(hdev)); | ||
| 2405 | dbg_hid(PICOLCD_NAME " device ready for suspend\n"); | ||
| 2406 | return 0; | ||
| 2407 | } | ||
| 2408 | |||
| 2409 | static int picolcd_resume(struct hid_device *hdev) | ||
| 2410 | { | ||
| 2411 | int ret; | ||
| 2412 | ret = picolcd_resume_backlight(hid_get_drvdata(hdev)); | ||
| 2413 | if (ret) | ||
| 2414 | dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret); | ||
| 2415 | return 0; | ||
| 2416 | } | ||
| 2417 | |||
| 2418 | static int picolcd_reset_resume(struct hid_device *hdev) | ||
| 2419 | { | ||
| 2420 | int ret; | ||
| 2421 | ret = picolcd_reset(hdev); | ||
| 2422 | if (ret) | ||
| 2423 | dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret); | ||
| 2424 | ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0); | ||
| 2425 | if (ret) | ||
| 2426 | dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret); | ||
| 2427 | ret = picolcd_resume_lcd(hid_get_drvdata(hdev)); | ||
| 2428 | if (ret) | ||
| 2429 | dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret); | ||
| 2430 | ret = picolcd_resume_backlight(hid_get_drvdata(hdev)); | ||
| 2431 | if (ret) | ||
| 2432 | dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret); | ||
| 2433 | picolcd_leds_set(hid_get_drvdata(hdev)); | ||
| 2434 | return 0; | ||
| 2435 | } | ||
| 2436 | #endif | ||
| 2437 | |||
| 2438 | /* initialize keypad input device */ | ||
| 2439 | static int picolcd_init_keys(struct picolcd_data *data, | ||
| 2440 | struct hid_report *report) | ||
| 2441 | { | ||
| 2442 | struct hid_device *hdev = data->hdev; | ||
| 2443 | struct input_dev *idev; | ||
| 2444 | int error, i; | ||
| 2445 | |||
| 2446 | if (!report) | ||
| 2447 | return -ENODEV; | ||
| 2448 | if (report->maxfield != 1 || report->field[0]->report_count != 2 || | ||
| 2449 | report->field[0]->report_size != 8) { | ||
| 2450 | hid_err(hdev, "unsupported KEY_STATE report\n"); | ||
| 2451 | return -EINVAL; | ||
| 2452 | } | ||
| 2453 | |||
| 2454 | idev = input_allocate_device(); | ||
| 2455 | if (idev == NULL) { | ||
| 2456 | hid_err(hdev, "failed to allocate input device\n"); | ||
| 2457 | return -ENOMEM; | ||
| 2458 | } | ||
| 2459 | input_set_drvdata(idev, hdev); | ||
| 2460 | memcpy(data->keycode, def_keymap, sizeof(def_keymap)); | ||
| 2461 | idev->name = hdev->name; | ||
| 2462 | idev->phys = hdev->phys; | ||
| 2463 | idev->uniq = hdev->uniq; | ||
| 2464 | idev->id.bustype = hdev->bus; | ||
| 2465 | idev->id.vendor = hdev->vendor; | ||
| 2466 | idev->id.product = hdev->product; | ||
| 2467 | idev->id.version = hdev->version; | ||
| 2468 | idev->dev.parent = hdev->dev.parent; | ||
| 2469 | idev->keycode = &data->keycode; | ||
| 2470 | idev->keycodemax = PICOLCD_KEYS; | ||
| 2471 | idev->keycodesize = sizeof(data->keycode[0]); | ||
| 2472 | input_set_capability(idev, EV_MSC, MSC_SCAN); | ||
| 2473 | set_bit(EV_REP, idev->evbit); | ||
| 2474 | for (i = 0; i < PICOLCD_KEYS; i++) | ||
| 2475 | input_set_capability(idev, EV_KEY, data->keycode[i]); | ||
| 2476 | error = input_register_device(idev); | ||
| 2477 | if (error) { | ||
| 2478 | hid_err(hdev, "error registering the input device\n"); | ||
| 2479 | input_free_device(idev); | ||
| 2480 | return error; | ||
| 2481 | } | ||
| 2482 | data->input_keys = idev; | ||
| 2483 | return 0; | ||
| 2484 | } | ||
| 2485 | |||
| 2486 | static void picolcd_exit_keys(struct picolcd_data *data) | ||
| 2487 | { | ||
| 2488 | struct input_dev *idev = data->input_keys; | ||
| 2489 | |||
| 2490 | data->input_keys = NULL; | ||
| 2491 | if (idev) | ||
| 2492 | input_unregister_device(idev); | ||
| 2493 | } | ||
| 2494 | |||
| 2495 | /* initialize CIR input device */ | ||
| 2496 | static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) | ||
| 2497 | { | ||
| 2498 | /* support not implemented yet */ | ||
| 2499 | return 0; | ||
| 2500 | } | ||
| 2501 | |||
| 2502 | static inline void picolcd_exit_cir(struct picolcd_data *data) | ||
| 2503 | { | ||
| 2504 | } | ||
| 2505 | |||
| 2506 | static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | ||
| 2507 | { | ||
| 2508 | int error; | ||
| 2509 | |||
| 2510 | error = picolcd_check_version(hdev); | ||
| 2511 | if (error) | ||
| 2512 | return error; | ||
| 2513 | |||
| 2514 | if (data->version[0] != 0 && data->version[1] != 3) | ||
| 2515 | hid_info(hdev, "Device with untested firmware revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
| 2516 | dev_name(&hdev->dev)); | ||
| 2517 | |||
| 2518 | /* Setup keypad input device */ | ||
| 2519 | error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev)); | ||
| 2520 | if (error) | ||
| 2521 | goto err; | ||
| 2522 | |||
| 2523 | /* Setup CIR input device */ | ||
| 2524 | error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev)); | ||
| 2525 | if (error) | ||
| 2526 | goto err; | ||
| 2527 | |||
| 2528 | /* Set up the framebuffer device */ | ||
| 2529 | error = picolcd_init_framebuffer(data); | ||
| 2530 | if (error) | ||
| 2531 | goto err; | ||
| 2532 | |||
| 2533 | /* Setup lcd class device */ | ||
| 2534 | error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev)); | ||
| 2535 | if (error) | ||
| 2536 | goto err; | ||
| 2537 | |||
| 2538 | /* Setup backlight class device */ | ||
| 2539 | error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev)); | ||
| 2540 | if (error) | ||
| 2541 | goto err; | ||
| 2542 | |||
| 2543 | /* Setup the LED class devices */ | ||
| 2544 | error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev)); | ||
| 2545 | if (error) | ||
| 2546 | goto err; | ||
| 2547 | |||
| 2548 | picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev), | ||
| 2549 | picolcd_out_report(REPORT_EE_WRITE, hdev), | ||
| 2550 | picolcd_out_report(REPORT_READ_MEMORY, hdev), | ||
| 2551 | picolcd_out_report(REPORT_WRITE_MEMORY, hdev), | ||
| 2552 | picolcd_out_report(REPORT_RESET, hdev)); | ||
| 2553 | return 0; | ||
| 2554 | err: | ||
| 2555 | picolcd_exit_leds(data); | ||
| 2556 | picolcd_exit_backlight(data); | ||
| 2557 | picolcd_exit_lcd(data); | ||
| 2558 | picolcd_exit_framebuffer(data); | ||
| 2559 | picolcd_exit_cir(data); | ||
| 2560 | picolcd_exit_keys(data); | ||
| 2561 | return error; | ||
| 2562 | } | ||
| 2563 | |||
| 2564 | static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data) | ||
| 2565 | { | ||
| 2566 | int error; | ||
| 2567 | |||
| 2568 | error = picolcd_check_version(hdev); | ||
| 2569 | if (error) | ||
| 2570 | return error; | ||
| 2571 | |||
| 2572 | if (data->version[0] != 1 && data->version[1] != 0) | ||
| 2573 | hid_info(hdev, "Device with untested bootloader revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
| 2574 | dev_name(&hdev->dev)); | ||
| 2575 | |||
| 2576 | picolcd_init_devfs(data, NULL, NULL, | ||
| 2577 | picolcd_out_report(REPORT_BL_READ_MEMORY, hdev), | ||
| 2578 | picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL); | ||
| 2579 | return 0; | ||
| 2580 | } | ||
| 2581 | |||
| 2582 | static int picolcd_probe(struct hid_device *hdev, | ||
| 2583 | const struct hid_device_id *id) | ||
| 2584 | { | ||
| 2585 | struct picolcd_data *data; | ||
| 2586 | int error = -ENOMEM; | ||
| 2587 | |||
| 2588 | dbg_hid(PICOLCD_NAME " hardware probe...\n"); | ||
| 2589 | |||
| 2590 | /* | ||
| 2591 | * Let's allocate the picolcd data structure, set some reasonable | ||
| 2592 | * defaults, and associate it with the device | ||
| 2593 | */ | ||
| 2594 | data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL); | ||
| 2595 | if (data == NULL) { | ||
| 2596 | hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n"); | ||
| 2597 | error = -ENOMEM; | ||
| 2598 | goto err_no_cleanup; | ||
| 2599 | } | ||
| 2600 | |||
| 2601 | spin_lock_init(&data->lock); | ||
| 2602 | mutex_init(&data->mutex); | ||
| 2603 | data->hdev = hdev; | ||
| 2604 | data->opmode_delay = 5000; | ||
| 2605 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
| 2606 | data->status |= PICOLCD_BOOTLOADER; | ||
| 2607 | hid_set_drvdata(hdev, data); | ||
| 2608 | |||
| 2609 | /* Parse the device reports and start it up */ | ||
| 2610 | error = hid_parse(hdev); | ||
| 2611 | if (error) { | ||
| 2612 | hid_err(hdev, "device report parse failed\n"); | ||
| 2613 | goto err_cleanup_data; | ||
| 2614 | } | ||
| 2615 | |||
| 2616 | error = hid_hw_start(hdev, 0); | ||
| 2617 | if (error) { | ||
| 2618 | hid_err(hdev, "hardware start failed\n"); | ||
| 2619 | goto err_cleanup_data; | ||
| 2620 | } | ||
| 2621 | |||
| 2622 | error = hid_hw_open(hdev); | ||
| 2623 | if (error) { | ||
| 2624 | hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n"); | ||
| 2625 | goto err_cleanup_hid_hw; | ||
| 2626 | } | ||
| 2627 | |||
| 2628 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 2629 | if (error) { | ||
| 2630 | hid_err(hdev, "failed to create sysfs attributes\n"); | ||
| 2631 | goto err_cleanup_hid_ll; | ||
| 2632 | } | ||
| 2633 | |||
| 2634 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 2635 | if (error) { | ||
| 2636 | hid_err(hdev, "failed to create sysfs attributes\n"); | ||
| 2637 | goto err_cleanup_sysfs1; | ||
| 2638 | } | ||
| 2639 | |||
| 2640 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 2641 | error = picolcd_probe_bootloader(hdev, data); | ||
| 2642 | else | ||
| 2643 | error = picolcd_probe_lcd(hdev, data); | ||
| 2644 | if (error) | ||
| 2645 | goto err_cleanup_sysfs2; | ||
| 2646 | |||
| 2647 | dbg_hid(PICOLCD_NAME " activated and initialized\n"); | ||
| 2648 | return 0; | ||
| 2649 | |||
| 2650 | err_cleanup_sysfs2: | ||
| 2651 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 2652 | err_cleanup_sysfs1: | ||
| 2653 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 2654 | err_cleanup_hid_ll: | ||
| 2655 | hid_hw_close(hdev); | ||
| 2656 | err_cleanup_hid_hw: | ||
| 2657 | hid_hw_stop(hdev); | ||
| 2658 | err_cleanup_data: | ||
| 2659 | kfree(data); | ||
| 2660 | err_no_cleanup: | ||
| 2661 | hid_set_drvdata(hdev, NULL); | ||
| 2662 | |||
| 2663 | return error; | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | static void picolcd_remove(struct hid_device *hdev) | ||
| 2667 | { | ||
| 2668 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 2669 | unsigned long flags; | ||
| 2670 | |||
| 2671 | dbg_hid(PICOLCD_NAME " hardware remove...\n"); | ||
| 2672 | spin_lock_irqsave(&data->lock, flags); | ||
| 2673 | data->status |= PICOLCD_FAILED; | ||
| 2674 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 2675 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 2676 | /* short-circuit FB as early as possible in order to | ||
| 2677 | * avoid long delays if we host console. | ||
| 2678 | */ | ||
| 2679 | if (data->fb_info) | ||
| 2680 | data->fb_info->par = NULL; | ||
| 2681 | #endif | ||
| 2682 | |||
| 2683 | picolcd_exit_devfs(data); | ||
| 2684 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 2685 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 2686 | hid_hw_close(hdev); | ||
| 2687 | hid_hw_stop(hdev); | ||
| 2688 | hid_set_drvdata(hdev, NULL); | ||
| 2689 | |||
| 2690 | /* Shortcut potential pending reply that will never arrive */ | ||
| 2691 | spin_lock_irqsave(&data->lock, flags); | ||
| 2692 | if (data->pending) | ||
| 2693 | complete(&data->pending->ready); | ||
| 2694 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 2695 | |||
| 2696 | /* Cleanup LED */ | ||
| 2697 | picolcd_exit_leds(data); | ||
| 2698 | /* Clean up the framebuffer */ | ||
| 2699 | picolcd_exit_backlight(data); | ||
| 2700 | picolcd_exit_lcd(data); | ||
| 2701 | picolcd_exit_framebuffer(data); | ||
| 2702 | /* Cleanup input */ | ||
| 2703 | picolcd_exit_cir(data); | ||
| 2704 | picolcd_exit_keys(data); | ||
| 2705 | |||
| 2706 | mutex_destroy(&data->mutex); | ||
| 2707 | /* Finally, clean up the picolcd data itself */ | ||
| 2708 | kfree(data); | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | static const struct hid_device_id picolcd_devices[] = { | ||
| 2712 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | ||
| 2713 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | ||
| 2714 | { } | ||
| 2715 | }; | ||
| 2716 | MODULE_DEVICE_TABLE(hid, picolcd_devices); | ||
| 2717 | |||
| 2718 | static struct hid_driver picolcd_driver = { | ||
| 2719 | .name = "hid-picolcd", | ||
| 2720 | .id_table = picolcd_devices, | ||
| 2721 | .probe = picolcd_probe, | ||
| 2722 | .remove = picolcd_remove, | ||
| 2723 | .raw_event = picolcd_raw_event, | ||
| 2724 | #ifdef CONFIG_PM | ||
| 2725 | .suspend = picolcd_suspend, | ||
| 2726 | .resume = picolcd_resume, | ||
| 2727 | .reset_resume = picolcd_reset_resume, | ||
| 2728 | #endif | ||
| 2729 | }; | ||
| 2730 | |||
| 2731 | static int __init picolcd_init(void) | ||
| 2732 | { | ||
| 2733 | return hid_register_driver(&picolcd_driver); | ||
| 2734 | } | ||
| 2735 | |||
| 2736 | static void __exit picolcd_exit(void) | ||
| 2737 | { | ||
| 2738 | hid_unregister_driver(&picolcd_driver); | ||
| 2739 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 2740 | flush_work_sync(&picolcd_fb_cleanup); | ||
| 2741 | WARN_ON(fb_pending); | ||
| 2742 | #endif | ||
| 2743 | } | ||
| 2744 | |||
| 2745 | module_init(picolcd_init); | ||
| 2746 | module_exit(picolcd_exit); | ||
| 2747 | MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver"); | ||
| 2748 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h new file mode 100644 index 000000000000..020cef69f6a1 --- /dev/null +++ b/drivers/hid/hid-picolcd.h | |||
| @@ -0,0 +1,309 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #define PICOLCD_NAME "PicoLCD (graphic)" | ||
| 21 | |||
| 22 | /* Report numbers */ | ||
| 23 | #define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */ | ||
| 24 | #define ERR_SUCCESS 0x00 | ||
| 25 | #define ERR_PARAMETER_MISSING 0x01 | ||
| 26 | #define ERR_DATA_MISSING 0x02 | ||
| 27 | #define ERR_BLOCK_READ_ONLY 0x03 | ||
| 28 | #define ERR_BLOCK_NOT_ERASABLE 0x04 | ||
| 29 | #define ERR_BLOCK_TOO_BIG 0x05 | ||
| 30 | #define ERR_SECTION_OVERFLOW 0x06 | ||
| 31 | #define ERR_INVALID_CMD_LEN 0x07 | ||
| 32 | #define ERR_INVALID_DATA_LEN 0x08 | ||
| 33 | #define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */ | ||
| 34 | #define REPORT_IR_DATA 0x21 /* LCD: IN[63] */ | ||
| 35 | #define REPORT_EE_DATA 0x32 /* LCD: IN[63] */ | ||
| 36 | #define REPORT_MEMORY 0x41 /* LCD: IN[63] */ | ||
| 37 | #define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */ | ||
| 38 | #define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */ | ||
| 39 | #define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */ | ||
| 40 | #define REPORT_RESET 0x93 /* LCD: OUT[2] */ | ||
| 41 | #define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */ | ||
| 42 | #define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */ | ||
| 43 | #define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */ | ||
| 44 | #define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */ | ||
| 45 | #define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */ | ||
| 46 | #define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */ | ||
| 47 | #define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */ | ||
| 48 | #define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */ | ||
| 49 | #define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */ | ||
| 50 | #define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */ | ||
| 51 | #define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */ | ||
| 52 | #define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */ | ||
| 53 | #define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */ | ||
| 54 | #define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */ | ||
| 55 | #define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */ | ||
| 56 | #define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */ | ||
| 57 | #define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */ | ||
| 58 | #define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */ | ||
| 59 | |||
| 60 | /* Description of in-progress IO operation, used for operations | ||
| 61 | * that trigger response from device */ | ||
| 62 | struct picolcd_pending { | ||
| 63 | struct hid_report *out_report; | ||
| 64 | struct hid_report *in_report; | ||
| 65 | struct completion ready; | ||
| 66 | int raw_size; | ||
| 67 | u8 raw_data[64]; | ||
| 68 | }; | ||
| 69 | |||
| 70 | |||
| 71 | #define PICOLCD_KEYS 17 | ||
| 72 | |||
| 73 | /* Per device data structure */ | ||
| 74 | struct picolcd_data { | ||
| 75 | struct hid_device *hdev; | ||
| 76 | #ifdef CONFIG_DEBUG_FS | ||
| 77 | struct dentry *debug_reset; | ||
| 78 | struct dentry *debug_eeprom; | ||
| 79 | struct dentry *debug_flash; | ||
| 80 | struct mutex mutex_flash; | ||
| 81 | int addr_sz; | ||
| 82 | #endif | ||
| 83 | u8 version[2]; | ||
| 84 | unsigned short opmode_delay; | ||
| 85 | /* input stuff */ | ||
| 86 | u8 pressed_keys[2]; | ||
| 87 | struct input_dev *input_keys; | ||
| 88 | #ifdef CONFIG_HID_PICOLCD_CIR | ||
| 89 | struct rc_dev *rc_dev; | ||
| 90 | #endif | ||
| 91 | unsigned short keycode[PICOLCD_KEYS]; | ||
| 92 | |||
| 93 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 94 | /* Framebuffer stuff */ | ||
| 95 | struct fb_info *fb_info; | ||
| 96 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 97 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
| 98 | struct lcd_device *lcd; | ||
| 99 | u8 lcd_contrast; | ||
| 100 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
| 101 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
| 102 | struct backlight_device *backlight; | ||
| 103 | u8 lcd_brightness; | ||
| 104 | u8 lcd_power; | ||
| 105 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
| 106 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
| 107 | /* LED stuff */ | ||
| 108 | u8 led_state; | ||
| 109 | struct led_classdev *led[8]; | ||
| 110 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
| 111 | |||
| 112 | /* Housekeeping stuff */ | ||
| 113 | spinlock_t lock; | ||
| 114 | struct mutex mutex; | ||
| 115 | struct picolcd_pending *pending; | ||
| 116 | int status; | ||
| 117 | #define PICOLCD_BOOTLOADER 1 | ||
| 118 | #define PICOLCD_FAILED 2 | ||
| 119 | #define PICOLCD_CIR_SHUN 4 | ||
| 120 | }; | ||
| 121 | |||
| 122 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 123 | struct picolcd_fb_data { | ||
| 124 | /* Framebuffer stuff */ | ||
| 125 | spinlock_t lock; | ||
| 126 | struct picolcd_data *picolcd; | ||
| 127 | u8 update_rate; | ||
| 128 | u8 bpp; | ||
| 129 | u8 force; | ||
| 130 | u8 ready; | ||
| 131 | u8 *vbitmap; /* local copy of what was sent to PicoLCD */ | ||
| 132 | u8 *bitmap; /* framebuffer */ | ||
| 133 | }; | ||
| 134 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 135 | |||
| 136 | /* Find a given report */ | ||
| 137 | #define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT) | ||
| 138 | #define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT) | ||
| 139 | |||
| 140 | struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir); | ||
| 141 | |||
| 142 | #ifdef CONFIG_DEBUG_FS | ||
| 143 | void picolcd_debug_out_report(struct picolcd_data *data, | ||
| 144 | struct hid_device *hdev, struct hid_report *report); | ||
| 145 | #define usbhid_submit_report(a, b, c) \ | ||
| 146 | do { \ | ||
| 147 | picolcd_debug_out_report(hid_get_drvdata(a), a, b); \ | ||
| 148 | usbhid_submit_report(a, b, c); \ | ||
| 149 | } while (0) | ||
| 150 | |||
| 151 | void picolcd_debug_raw_event(struct picolcd_data *data, | ||
| 152 | struct hid_device *hdev, struct hid_report *report, | ||
| 153 | u8 *raw_data, int size); | ||
| 154 | |||
| 155 | void picolcd_init_devfs(struct picolcd_data *data, | ||
| 156 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
| 157 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
| 158 | struct hid_report *reset); | ||
| 159 | |||
| 160 | void picolcd_exit_devfs(struct picolcd_data *data); | ||
| 161 | #else | ||
| 162 | static inline void picolcd_debug_out_report(struct picolcd_data *data, | ||
| 163 | struct hid_device *hdev, struct hid_report *report) | ||
| 164 | { | ||
| 165 | } | ||
| 166 | static inline void picolcd_debug_raw_event(struct picolcd_data *data, | ||
| 167 | struct hid_device *hdev, struct hid_report *report, | ||
| 168 | u8 *raw_data, int size) | ||
| 169 | { | ||
| 170 | } | ||
| 171 | static inline void picolcd_init_devfs(struct picolcd_data *data, | ||
| 172 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
| 173 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
| 174 | struct hid_report *reset) | ||
| 175 | { | ||
| 176 | } | ||
| 177 | static inline void picolcd_exit_devfs(struct picolcd_data *data) | ||
| 178 | { | ||
| 179 | } | ||
| 180 | #endif /* CONFIG_DEBUG_FS */ | ||
| 181 | |||
| 182 | |||
| 183 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 184 | int picolcd_fb_reset(struct picolcd_data *data, int clear); | ||
| 185 | |||
| 186 | int picolcd_init_framebuffer(struct picolcd_data *data); | ||
| 187 | |||
| 188 | void picolcd_exit_framebuffer(struct picolcd_data *data); | ||
| 189 | |||
| 190 | void picolcd_fb_refresh(struct picolcd_data *data); | ||
| 191 | #define picolcd_fbinfo(d) ((d)->fb_info) | ||
| 192 | #else | ||
| 193 | static inline int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
| 194 | { | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | static inline int picolcd_init_framebuffer(struct picolcd_data *data) | ||
| 198 | { | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | static inline void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
| 202 | { | ||
| 203 | } | ||
| 204 | static inline void picolcd_fb_refresh(struct picolcd_data *data) | ||
| 205 | { | ||
| 206 | } | ||
| 207 | #define picolcd_fbinfo(d) NULL | ||
| 208 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 209 | |||
| 210 | |||
| 211 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
| 212 | int picolcd_init_backlight(struct picolcd_data *data, | ||
| 213 | struct hid_report *report); | ||
| 214 | |||
| 215 | void picolcd_exit_backlight(struct picolcd_data *data); | ||
| 216 | |||
| 217 | int picolcd_resume_backlight(struct picolcd_data *data); | ||
| 218 | |||
| 219 | void picolcd_suspend_backlight(struct picolcd_data *data); | ||
| 220 | #else | ||
| 221 | static inline int picolcd_init_backlight(struct picolcd_data *data, | ||
| 222 | struct hid_report *report) | ||
| 223 | { | ||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | static inline void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 227 | { | ||
| 228 | } | ||
| 229 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 230 | { | ||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | static inline void picolcd_suspend_backlight(struct picolcd_data *data) | ||
| 234 | { | ||
| 235 | } | ||
| 236 | |||
| 237 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
| 238 | |||
| 239 | |||
| 240 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
| 241 | int picolcd_init_lcd(struct picolcd_data *data, | ||
| 242 | struct hid_report *report); | ||
| 243 | |||
| 244 | void picolcd_exit_lcd(struct picolcd_data *data); | ||
| 245 | |||
| 246 | int picolcd_resume_lcd(struct picolcd_data *data); | ||
| 247 | #else | ||
| 248 | static inline int picolcd_init_lcd(struct picolcd_data *data, | ||
| 249 | struct hid_report *report) | ||
| 250 | { | ||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | static inline void picolcd_exit_lcd(struct picolcd_data *data) | ||
| 254 | { | ||
| 255 | } | ||
| 256 | static inline int picolcd_resume_lcd(struct picolcd_data *data) | ||
| 257 | { | ||
| 258 | return 0; | ||
| 259 | } | ||
| 260 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
| 261 | |||
| 262 | |||
| 263 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
| 264 | int picolcd_init_leds(struct picolcd_data *data, | ||
| 265 | struct hid_report *report); | ||
| 266 | |||
| 267 | void picolcd_exit_leds(struct picolcd_data *data); | ||
| 268 | |||
| 269 | void picolcd_leds_set(struct picolcd_data *data); | ||
| 270 | #else | ||
| 271 | static inline int picolcd_init_leds(struct picolcd_data *data, | ||
| 272 | struct hid_report *report) | ||
| 273 | { | ||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | static inline void picolcd_exit_leds(struct picolcd_data *data) | ||
| 277 | { | ||
| 278 | } | ||
| 279 | static inline void picolcd_leds_set(struct picolcd_data *data) | ||
| 280 | { | ||
| 281 | } | ||
| 282 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
| 283 | |||
| 284 | |||
| 285 | #ifdef CONFIG_HID_PICOLCD_CIR | ||
| 286 | int picolcd_raw_cir(struct picolcd_data *data, | ||
| 287 | struct hid_report *report, u8 *raw_data, int size); | ||
| 288 | |||
| 289 | int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report); | ||
| 290 | |||
| 291 | void picolcd_exit_cir(struct picolcd_data *data); | ||
| 292 | #else | ||
| 293 | static inline int picolcd_raw_cir(struct picolcd_data *data, | ||
| 294 | struct hid_report *report, u8 *raw_data, int size) | ||
| 295 | { | ||
| 296 | return 1; | ||
| 297 | } | ||
| 298 | static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) | ||
| 299 | { | ||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | static inline void picolcd_exit_cir(struct picolcd_data *data) | ||
| 303 | { | ||
| 304 | } | ||
| 305 | #endif /* CONFIG_HID_PICOLCD_LIRC */ | ||
| 306 | |||
| 307 | int picolcd_reset(struct hid_device *hdev); | ||
| 308 | struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, | ||
| 309 | int report_id, const u8 *raw_data, int size); | ||
diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c new file mode 100644 index 000000000000..b91f30945f9c --- /dev/null +++ b/drivers/hid/hid-picolcd_backlight.c | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include "usbhid/usbhid.h" | ||
| 22 | #include <linux/usb.h> | ||
| 23 | |||
| 24 | #include <linux/fb.h> | ||
| 25 | #include <linux/backlight.h> | ||
| 26 | |||
| 27 | #include "hid-picolcd.h" | ||
| 28 | |||
| 29 | static int picolcd_get_brightness(struct backlight_device *bdev) | ||
| 30 | { | ||
| 31 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 32 | return data->lcd_brightness; | ||
| 33 | } | ||
| 34 | |||
| 35 | static int picolcd_set_brightness(struct backlight_device *bdev) | ||
| 36 | { | ||
| 37 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 38 | struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev); | ||
| 39 | unsigned long flags; | ||
| 40 | |||
| 41 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 42 | return -ENODEV; | ||
| 43 | |||
| 44 | data->lcd_brightness = bdev->props.brightness & 0x0ff; | ||
| 45 | data->lcd_power = bdev->props.power; | ||
| 46 | spin_lock_irqsave(&data->lock, flags); | ||
| 47 | hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); | ||
| 48 | if (!(data->status & PICOLCD_FAILED)) | ||
| 49 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 50 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb) | ||
| 55 | { | ||
| 56 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev)); | ||
| 57 | } | ||
| 58 | |||
| 59 | static const struct backlight_ops picolcd_blops = { | ||
| 60 | .update_status = picolcd_set_brightness, | ||
| 61 | .get_brightness = picolcd_get_brightness, | ||
| 62 | .check_fb = picolcd_check_bl_fb, | ||
| 63 | }; | ||
| 64 | |||
| 65 | int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report) | ||
| 66 | { | ||
| 67 | struct device *dev = &data->hdev->dev; | ||
| 68 | struct backlight_device *bdev; | ||
| 69 | struct backlight_properties props; | ||
| 70 | if (!report) | ||
| 71 | return -ENODEV; | ||
| 72 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 73 | report->field[0]->report_size != 8) { | ||
| 74 | dev_err(dev, "unsupported BRIGHTNESS report"); | ||
| 75 | return -EINVAL; | ||
| 76 | } | ||
| 77 | |||
| 78 | memset(&props, 0, sizeof(props)); | ||
| 79 | props.type = BACKLIGHT_RAW; | ||
| 80 | props.max_brightness = 0xff; | ||
| 81 | bdev = backlight_device_register(dev_name(dev), dev, data, | ||
| 82 | &picolcd_blops, &props); | ||
| 83 | if (IS_ERR(bdev)) { | ||
| 84 | dev_err(dev, "failed to register backlight\n"); | ||
| 85 | return PTR_ERR(bdev); | ||
| 86 | } | ||
| 87 | bdev->props.brightness = 0xff; | ||
| 88 | data->lcd_brightness = 0xff; | ||
| 89 | data->backlight = bdev; | ||
| 90 | picolcd_set_brightness(bdev); | ||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 95 | { | ||
| 96 | struct backlight_device *bdev = data->backlight; | ||
| 97 | |||
| 98 | data->backlight = NULL; | ||
| 99 | if (bdev) | ||
| 100 | backlight_device_unregister(bdev); | ||
| 101 | } | ||
| 102 | |||
| 103 | int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 104 | { | ||
| 105 | if (!data->backlight) | ||
| 106 | return 0; | ||
| 107 | return picolcd_set_brightness(data->backlight); | ||
| 108 | } | ||
| 109 | |||
| 110 | #ifdef CONFIG_PM | ||
| 111 | void picolcd_suspend_backlight(struct picolcd_data *data) | ||
| 112 | { | ||
| 113 | int bl_power = data->lcd_power; | ||
| 114 | if (!data->backlight) | ||
| 115 | return; | ||
| 116 | |||
| 117 | data->backlight->props.power = FB_BLANK_POWERDOWN; | ||
| 118 | picolcd_set_brightness(data->backlight); | ||
| 119 | data->lcd_power = data->backlight->props.power = bl_power; | ||
| 120 | } | ||
| 121 | #endif /* CONFIG_PM */ | ||
| 122 | |||
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c new file mode 100644 index 000000000000..13ca9191b630 --- /dev/null +++ b/drivers/hid/hid-picolcd_cir.c | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include <linux/hid-debug.h> | ||
| 22 | #include <linux/input.h> | ||
| 23 | #include "hid-ids.h" | ||
| 24 | #include "usbhid/usbhid.h" | ||
| 25 | #include <linux/usb.h> | ||
| 26 | |||
| 27 | #include <linux/fb.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 29 | #include <linux/backlight.h> | ||
| 30 | #include <linux/lcd.h> | ||
| 31 | |||
| 32 | #include <linux/leds.h> | ||
| 33 | |||
| 34 | #include <linux/seq_file.h> | ||
| 35 | #include <linux/debugfs.h> | ||
| 36 | |||
| 37 | #include <linux/completion.h> | ||
| 38 | #include <linux/uaccess.h> | ||
| 39 | #include <linux/module.h> | ||
| 40 | #include <media/rc-core.h> | ||
| 41 | |||
| 42 | #include "hid-picolcd.h" | ||
| 43 | |||
| 44 | |||
| 45 | int picolcd_raw_cir(struct picolcd_data *data, | ||
| 46 | struct hid_report *report, u8 *raw_data, int size) | ||
| 47 | { | ||
| 48 | unsigned long flags; | ||
| 49 | int i, w, sz; | ||
| 50 | DEFINE_IR_RAW_EVENT(rawir); | ||
| 51 | |||
| 52 | /* ignore if rc_dev is NULL or status is shunned */ | ||
| 53 | spin_lock_irqsave(&data->lock, flags); | ||
| 54 | if (!data->rc_dev || (data->status & PICOLCD_CIR_SHUN)) { | ||
| 55 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 56 | return 1; | ||
| 57 | } | ||
| 58 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 59 | |||
| 60 | /* PicoLCD USB packets contain 16-bit intervals in network order, | ||
| 61 | * with value negated for pulse. Intervals are in microseconds. | ||
| 62 | * | ||
| 63 | * Note: some userspace LIRC code for PicoLCD says negated values | ||
| 64 | * for space - is it a matter of IR chip? (pulse for my TSOP2236) | ||
| 65 | * | ||
| 66 | * In addition, the first interval seems to be around 15000 + base | ||
| 67 | * interval for non-first report of IR data - thus the quirk below | ||
| 68 | * to get RC_CODE to understand Sony and JVC remotes I have at hand | ||
| 69 | */ | ||
| 70 | sz = size > 0 ? min((int)raw_data[0], size-1) : 0; | ||
| 71 | for (i = 0; i+1 < sz; i += 2) { | ||
| 72 | init_ir_raw_event(&rawir); | ||
| 73 | w = (raw_data[i] << 8) | (raw_data[i+1]); | ||
| 74 | rawir.pulse = !!(w & 0x8000); | ||
| 75 | rawir.duration = US_TO_NS(rawir.pulse ? (65536 - w) : w); | ||
| 76 | /* Quirk!! - see above */ | ||
| 77 | if (i == 0 && rawir.duration > 15000000) | ||
| 78 | rawir.duration -= 15000000; | ||
| 79 | ir_raw_event_store(data->rc_dev, &rawir); | ||
| 80 | } | ||
| 81 | ir_raw_event_handle(data->rc_dev); | ||
| 82 | |||
| 83 | return 1; | ||
| 84 | } | ||
| 85 | |||
| 86 | static int picolcd_cir_open(struct rc_dev *dev) | ||
| 87 | { | ||
| 88 | struct picolcd_data *data = dev->priv; | ||
| 89 | unsigned long flags; | ||
| 90 | |||
| 91 | spin_lock_irqsave(&data->lock, flags); | ||
| 92 | data->status &= ~PICOLCD_CIR_SHUN; | ||
| 93 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | static void picolcd_cir_close(struct rc_dev *dev) | ||
| 98 | { | ||
| 99 | struct picolcd_data *data = dev->priv; | ||
| 100 | unsigned long flags; | ||
| 101 | |||
| 102 | spin_lock_irqsave(&data->lock, flags); | ||
| 103 | data->status |= PICOLCD_CIR_SHUN; | ||
| 104 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* initialize CIR input device */ | ||
| 108 | int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) | ||
| 109 | { | ||
| 110 | struct rc_dev *rdev; | ||
| 111 | int ret = 0; | ||
| 112 | |||
| 113 | rdev = rc_allocate_device(); | ||
| 114 | if (!rdev) | ||
| 115 | return -ENOMEM; | ||
| 116 | |||
| 117 | rdev->priv = data; | ||
| 118 | rdev->driver_type = RC_DRIVER_IR_RAW; | ||
| 119 | rdev->allowed_protos = RC_TYPE_ALL; | ||
| 120 | rdev->open = picolcd_cir_open; | ||
| 121 | rdev->close = picolcd_cir_close; | ||
| 122 | rdev->input_name = data->hdev->name; | ||
| 123 | rdev->input_phys = data->hdev->phys; | ||
| 124 | rdev->input_id.bustype = data->hdev->bus; | ||
| 125 | rdev->input_id.vendor = data->hdev->vendor; | ||
| 126 | rdev->input_id.product = data->hdev->product; | ||
| 127 | rdev->input_id.version = data->hdev->version; | ||
| 128 | rdev->dev.parent = &data->hdev->dev; | ||
| 129 | rdev->driver_name = PICOLCD_NAME; | ||
| 130 | rdev->map_name = RC_MAP_RC6_MCE; | ||
| 131 | rdev->timeout = MS_TO_NS(100); | ||
| 132 | rdev->rx_resolution = US_TO_NS(1); | ||
| 133 | |||
| 134 | ret = rc_register_device(rdev); | ||
| 135 | if (ret) | ||
| 136 | goto err; | ||
| 137 | data->rc_dev = rdev; | ||
| 138 | return 0; | ||
| 139 | |||
| 140 | err: | ||
| 141 | rc_free_device(rdev); | ||
| 142 | return ret; | ||
| 143 | } | ||
| 144 | |||
| 145 | void picolcd_exit_cir(struct picolcd_data *data) | ||
| 146 | { | ||
| 147 | struct rc_dev *rdev = data->rc_dev; | ||
| 148 | |||
| 149 | data->rc_dev = NULL; | ||
| 150 | rc_unregister_device(rdev); | ||
| 151 | } | ||
| 152 | |||
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c new file mode 100644 index 000000000000..86df26e58aba --- /dev/null +++ b/drivers/hid/hid-picolcd_core.c | |||
| @@ -0,0 +1,689 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include <linux/hid-debug.h> | ||
| 22 | #include <linux/input.h> | ||
| 23 | #include "hid-ids.h" | ||
| 24 | #include "usbhid/usbhid.h" | ||
| 25 | #include <linux/usb.h> | ||
| 26 | |||
| 27 | #include <linux/fb.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 29 | |||
| 30 | #include <linux/completion.h> | ||
| 31 | #include <linux/uaccess.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | |||
| 34 | #include "hid-picolcd.h" | ||
| 35 | |||
| 36 | |||
| 37 | /* Input device | ||
| 38 | * | ||
| 39 | * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys | ||
| 40 | * and header for 4x4 key matrix. The built-in keys are part of the matrix. | ||
| 41 | */ | ||
| 42 | static const unsigned short def_keymap[PICOLCD_KEYS] = { | ||
| 43 | KEY_RESERVED, /* none */ | ||
| 44 | KEY_BACK, /* col 4 + row 1 */ | ||
| 45 | KEY_HOMEPAGE, /* col 3 + row 1 */ | ||
| 46 | KEY_RESERVED, /* col 2 + row 1 */ | ||
| 47 | KEY_RESERVED, /* col 1 + row 1 */ | ||
| 48 | KEY_SCROLLUP, /* col 4 + row 2 */ | ||
| 49 | KEY_OK, /* col 3 + row 2 */ | ||
| 50 | KEY_SCROLLDOWN, /* col 2 + row 2 */ | ||
| 51 | KEY_RESERVED, /* col 1 + row 2 */ | ||
| 52 | KEY_RESERVED, /* col 4 + row 3 */ | ||
| 53 | KEY_RESERVED, /* col 3 + row 3 */ | ||
| 54 | KEY_RESERVED, /* col 2 + row 3 */ | ||
| 55 | KEY_RESERVED, /* col 1 + row 3 */ | ||
| 56 | KEY_RESERVED, /* col 4 + row 4 */ | ||
| 57 | KEY_RESERVED, /* col 3 + row 4 */ | ||
| 58 | KEY_RESERVED, /* col 2 + row 4 */ | ||
| 59 | KEY_RESERVED, /* col 1 + row 4 */ | ||
| 60 | }; | ||
| 61 | |||
| 62 | |||
| 63 | /* Find a given report */ | ||
| 64 | struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir) | ||
| 65 | { | ||
| 66 | struct list_head *feature_report_list = &hdev->report_enum[dir].report_list; | ||
| 67 | struct hid_report *report = NULL; | ||
| 68 | |||
| 69 | list_for_each_entry(report, feature_report_list, list) { | ||
| 70 | if (report->id == id) | ||
| 71 | return report; | ||
| 72 | } | ||
| 73 | hid_warn(hdev, "No report with id 0x%x found\n", id); | ||
| 74 | return NULL; | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Submit a report and wait for a reply from device - if device fades away | ||
| 78 | * or does not respond in time, return NULL */ | ||
| 79 | struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, | ||
| 80 | int report_id, const u8 *raw_data, int size) | ||
| 81 | { | ||
| 82 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 83 | struct picolcd_pending *work; | ||
| 84 | struct hid_report *report = picolcd_out_report(report_id, hdev); | ||
| 85 | unsigned long flags; | ||
| 86 | int i, j, k; | ||
| 87 | |||
| 88 | if (!report || !data) | ||
| 89 | return NULL; | ||
| 90 | if (data->status & PICOLCD_FAILED) | ||
| 91 | return NULL; | ||
| 92 | work = kzalloc(sizeof(*work), GFP_KERNEL); | ||
| 93 | if (!work) | ||
| 94 | return NULL; | ||
| 95 | |||
| 96 | init_completion(&work->ready); | ||
| 97 | work->out_report = report; | ||
| 98 | work->in_report = NULL; | ||
| 99 | work->raw_size = 0; | ||
| 100 | |||
| 101 | mutex_lock(&data->mutex); | ||
| 102 | spin_lock_irqsave(&data->lock, flags); | ||
| 103 | for (i = k = 0; i < report->maxfield; i++) | ||
| 104 | for (j = 0; j < report->field[i]->report_count; j++) { | ||
| 105 | hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0); | ||
| 106 | k++; | ||
| 107 | } | ||
| 108 | if (data->status & PICOLCD_FAILED) { | ||
| 109 | kfree(work); | ||
| 110 | work = NULL; | ||
| 111 | } else { | ||
| 112 | data->pending = work; | ||
| 113 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 114 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 115 | wait_for_completion_interruptible_timeout(&work->ready, HZ*2); | ||
| 116 | spin_lock_irqsave(&data->lock, flags); | ||
| 117 | data->pending = NULL; | ||
| 118 | } | ||
| 119 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 120 | mutex_unlock(&data->mutex); | ||
| 121 | return work; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* | ||
| 125 | * input class device | ||
| 126 | */ | ||
| 127 | static int picolcd_raw_keypad(struct picolcd_data *data, | ||
| 128 | struct hid_report *report, u8 *raw_data, int size) | ||
| 129 | { | ||
| 130 | /* | ||
| 131 | * Keypad event | ||
| 132 | * First and second data bytes list currently pressed keys, | ||
| 133 | * 0x00 means no key and at most 2 keys may be pressed at same time | ||
| 134 | */ | ||
| 135 | int i, j; | ||
| 136 | |||
| 137 | /* determine newly pressed keys */ | ||
| 138 | for (i = 0; i < size; i++) { | ||
| 139 | unsigned int key_code; | ||
| 140 | if (raw_data[i] == 0) | ||
| 141 | continue; | ||
| 142 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
| 143 | if (data->pressed_keys[j] == raw_data[i]) | ||
| 144 | goto key_already_down; | ||
| 145 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
| 146 | if (data->pressed_keys[j] == 0) { | ||
| 147 | data->pressed_keys[j] = raw_data[i]; | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]); | ||
| 151 | if (raw_data[i] < PICOLCD_KEYS) | ||
| 152 | key_code = data->keycode[raw_data[i]]; | ||
| 153 | else | ||
| 154 | key_code = KEY_UNKNOWN; | ||
| 155 | if (key_code != KEY_UNKNOWN) { | ||
| 156 | dbg_hid(PICOLCD_NAME " got key press for %u:%d", | ||
| 157 | raw_data[i], key_code); | ||
| 158 | input_report_key(data->input_keys, key_code, 1); | ||
| 159 | } | ||
| 160 | input_sync(data->input_keys); | ||
| 161 | key_already_down: | ||
| 162 | continue; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* determine newly released keys */ | ||
| 166 | for (j = 0; j < sizeof(data->pressed_keys); j++) { | ||
| 167 | unsigned int key_code; | ||
| 168 | if (data->pressed_keys[j] == 0) | ||
| 169 | continue; | ||
| 170 | for (i = 0; i < size; i++) | ||
| 171 | if (data->pressed_keys[j] == raw_data[i]) | ||
| 172 | goto key_still_down; | ||
| 173 | input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]); | ||
| 174 | if (data->pressed_keys[j] < PICOLCD_KEYS) | ||
| 175 | key_code = data->keycode[data->pressed_keys[j]]; | ||
| 176 | else | ||
| 177 | key_code = KEY_UNKNOWN; | ||
| 178 | if (key_code != KEY_UNKNOWN) { | ||
| 179 | dbg_hid(PICOLCD_NAME " got key release for %u:%d", | ||
| 180 | data->pressed_keys[j], key_code); | ||
| 181 | input_report_key(data->input_keys, key_code, 0); | ||
| 182 | } | ||
| 183 | input_sync(data->input_keys); | ||
| 184 | data->pressed_keys[j] = 0; | ||
| 185 | key_still_down: | ||
| 186 | continue; | ||
| 187 | } | ||
| 188 | return 1; | ||
| 189 | } | ||
| 190 | |||
| 191 | static int picolcd_check_version(struct hid_device *hdev) | ||
| 192 | { | ||
| 193 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 194 | struct picolcd_pending *verinfo; | ||
| 195 | int ret = 0; | ||
| 196 | |||
| 197 | if (!data) | ||
| 198 | return -ENODEV; | ||
| 199 | |||
| 200 | verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0); | ||
| 201 | if (!verinfo) { | ||
| 202 | hid_err(hdev, "no version response from PicoLCD\n"); | ||
| 203 | return -ENODEV; | ||
| 204 | } | ||
| 205 | |||
| 206 | if (verinfo->raw_size == 2) { | ||
| 207 | data->version[0] = verinfo->raw_data[1]; | ||
| 208 | data->version[1] = verinfo->raw_data[0]; | ||
| 209 | if (data->status & PICOLCD_BOOTLOADER) { | ||
| 210 | hid_info(hdev, "PicoLCD, bootloader version %d.%d\n", | ||
| 211 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
| 212 | } else { | ||
| 213 | hid_info(hdev, "PicoLCD, firmware version %d.%d\n", | ||
| 214 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
| 215 | } | ||
| 216 | } else { | ||
| 217 | hid_err(hdev, "confused, got unexpected version response from PicoLCD\n"); | ||
| 218 | ret = -EINVAL; | ||
| 219 | } | ||
| 220 | kfree(verinfo); | ||
| 221 | return ret; | ||
| 222 | } | ||
| 223 | |||
| 224 | /* | ||
| 225 | * Reset our device and wait for answer to VERSION request | ||
| 226 | */ | ||
| 227 | int picolcd_reset(struct hid_device *hdev) | ||
| 228 | { | ||
| 229 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 230 | struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev); | ||
| 231 | unsigned long flags; | ||
| 232 | int error; | ||
| 233 | |||
| 234 | if (!data || !report || report->maxfield != 1) | ||
| 235 | return -ENODEV; | ||
| 236 | |||
| 237 | spin_lock_irqsave(&data->lock, flags); | ||
| 238 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
| 239 | data->status |= PICOLCD_BOOTLOADER; | ||
| 240 | |||
| 241 | /* perform the reset */ | ||
| 242 | hid_set_field(report->field[0], 0, 1); | ||
| 243 | if (data->status & PICOLCD_FAILED) { | ||
| 244 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 245 | return -ENODEV; | ||
| 246 | } | ||
| 247 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | ||
| 248 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 249 | |||
| 250 | error = picolcd_check_version(hdev); | ||
| 251 | if (error) | ||
| 252 | return error; | ||
| 253 | |||
| 254 | picolcd_resume_lcd(data); | ||
| 255 | picolcd_resume_backlight(data); | ||
| 256 | picolcd_fb_refresh(data); | ||
| 257 | picolcd_leds_set(data); | ||
| 258 | return 0; | ||
| 259 | } | ||
| 260 | |||
| 261 | /* | ||
| 262 | * The "operation_mode" sysfs attribute | ||
| 263 | */ | ||
| 264 | static ssize_t picolcd_operation_mode_show(struct device *dev, | ||
| 265 | struct device_attribute *attr, char *buf) | ||
| 266 | { | ||
| 267 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 268 | |||
| 269 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 270 | return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n"); | ||
| 271 | else | ||
| 272 | return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n"); | ||
| 273 | } | ||
| 274 | |||
| 275 | static ssize_t picolcd_operation_mode_store(struct device *dev, | ||
| 276 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 277 | { | ||
| 278 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 279 | struct hid_report *report = NULL; | ||
| 280 | size_t cnt = count; | ||
| 281 | int timeout = data->opmode_delay; | ||
| 282 | unsigned long flags; | ||
| 283 | |||
| 284 | if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) { | ||
| 285 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 286 | report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev); | ||
| 287 | buf += 3; | ||
| 288 | cnt -= 3; | ||
| 289 | } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) { | ||
| 290 | if (!(data->status & PICOLCD_BOOTLOADER)) | ||
| 291 | report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev); | ||
| 292 | buf += 10; | ||
| 293 | cnt -= 10; | ||
| 294 | } | ||
| 295 | if (!report) | ||
| 296 | return -EINVAL; | ||
| 297 | |||
| 298 | while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) | ||
| 299 | cnt--; | ||
| 300 | if (cnt != 0) | ||
| 301 | return -EINVAL; | ||
| 302 | |||
| 303 | spin_lock_irqsave(&data->lock, flags); | ||
| 304 | hid_set_field(report->field[0], 0, timeout & 0xff); | ||
| 305 | hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff); | ||
| 306 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 307 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 308 | return count; | ||
| 309 | } | ||
| 310 | |||
| 311 | static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show, | ||
| 312 | picolcd_operation_mode_store); | ||
| 313 | |||
| 314 | /* | ||
| 315 | * The "operation_mode_delay" sysfs attribute | ||
| 316 | */ | ||
| 317 | static ssize_t picolcd_operation_mode_delay_show(struct device *dev, | ||
| 318 | struct device_attribute *attr, char *buf) | ||
| 319 | { | ||
| 320 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 321 | |||
| 322 | return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay); | ||
| 323 | } | ||
| 324 | |||
| 325 | static ssize_t picolcd_operation_mode_delay_store(struct device *dev, | ||
| 326 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 327 | { | ||
| 328 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 329 | unsigned u; | ||
| 330 | if (sscanf(buf, "%u", &u) != 1) | ||
| 331 | return -EINVAL; | ||
| 332 | if (u > 30000) | ||
| 333 | return -EINVAL; | ||
| 334 | else | ||
| 335 | data->opmode_delay = u; | ||
| 336 | return count; | ||
| 337 | } | ||
| 338 | |||
| 339 | static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show, | ||
| 340 | picolcd_operation_mode_delay_store); | ||
| 341 | |||
| 342 | /* | ||
| 343 | * Handle raw report as sent by device | ||
| 344 | */ | ||
| 345 | static int picolcd_raw_event(struct hid_device *hdev, | ||
| 346 | struct hid_report *report, u8 *raw_data, int size) | ||
| 347 | { | ||
| 348 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 349 | unsigned long flags; | ||
| 350 | int ret = 0; | ||
| 351 | |||
| 352 | if (!data) | ||
| 353 | return 1; | ||
| 354 | |||
| 355 | if (report->id == REPORT_KEY_STATE) { | ||
| 356 | if (data->input_keys) | ||
| 357 | ret = picolcd_raw_keypad(data, report, raw_data+1, size-1); | ||
| 358 | } else if (report->id == REPORT_IR_DATA) { | ||
| 359 | ret = picolcd_raw_cir(data, report, raw_data+1, size-1); | ||
| 360 | } else { | ||
| 361 | spin_lock_irqsave(&data->lock, flags); | ||
| 362 | /* | ||
| 363 | * We let the caller of picolcd_send_and_wait() check if the | ||
| 364 | * report we got is one of the expected ones or not. | ||
| 365 | */ | ||
| 366 | if (data->pending) { | ||
| 367 | memcpy(data->pending->raw_data, raw_data+1, size-1); | ||
| 368 | data->pending->raw_size = size-1; | ||
| 369 | data->pending->in_report = report; | ||
| 370 | complete(&data->pending->ready); | ||
| 371 | } | ||
| 372 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 373 | } | ||
| 374 | |||
| 375 | picolcd_debug_raw_event(data, hdev, report, raw_data, size); | ||
| 376 | return 1; | ||
| 377 | } | ||
| 378 | |||
| 379 | #ifdef CONFIG_PM | ||
| 380 | static int picolcd_suspend(struct hid_device *hdev, pm_message_t message) | ||
| 381 | { | ||
| 382 | if (PMSG_IS_AUTO(message)) | ||
| 383 | return 0; | ||
| 384 | |||
| 385 | picolcd_suspend_backlight(hid_get_drvdata(hdev)); | ||
| 386 | dbg_hid(PICOLCD_NAME " device ready for suspend\n"); | ||
| 387 | return 0; | ||
| 388 | } | ||
| 389 | |||
| 390 | static int picolcd_resume(struct hid_device *hdev) | ||
| 391 | { | ||
| 392 | int ret; | ||
| 393 | ret = picolcd_resume_backlight(hid_get_drvdata(hdev)); | ||
| 394 | if (ret) | ||
| 395 | dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret); | ||
| 396 | return 0; | ||
| 397 | } | ||
| 398 | |||
| 399 | static int picolcd_reset_resume(struct hid_device *hdev) | ||
| 400 | { | ||
| 401 | int ret; | ||
| 402 | ret = picolcd_reset(hdev); | ||
| 403 | if (ret) | ||
| 404 | dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret); | ||
| 405 | ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0); | ||
| 406 | if (ret) | ||
| 407 | dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret); | ||
| 408 | ret = picolcd_resume_lcd(hid_get_drvdata(hdev)); | ||
| 409 | if (ret) | ||
| 410 | dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret); | ||
| 411 | ret = picolcd_resume_backlight(hid_get_drvdata(hdev)); | ||
| 412 | if (ret) | ||
| 413 | dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret); | ||
| 414 | picolcd_leds_set(hid_get_drvdata(hdev)); | ||
| 415 | return 0; | ||
| 416 | } | ||
| 417 | #endif | ||
| 418 | |||
| 419 | /* initialize keypad input device */ | ||
| 420 | static int picolcd_init_keys(struct picolcd_data *data, | ||
| 421 | struct hid_report *report) | ||
| 422 | { | ||
| 423 | struct hid_device *hdev = data->hdev; | ||
| 424 | struct input_dev *idev; | ||
| 425 | int error, i; | ||
| 426 | |||
| 427 | if (!report) | ||
| 428 | return -ENODEV; | ||
| 429 | if (report->maxfield != 1 || report->field[0]->report_count != 2 || | ||
| 430 | report->field[0]->report_size != 8) { | ||
| 431 | hid_err(hdev, "unsupported KEY_STATE report\n"); | ||
| 432 | return -EINVAL; | ||
| 433 | } | ||
| 434 | |||
| 435 | idev = input_allocate_device(); | ||
| 436 | if (idev == NULL) { | ||
| 437 | hid_err(hdev, "failed to allocate input device\n"); | ||
| 438 | return -ENOMEM; | ||
| 439 | } | ||
| 440 | input_set_drvdata(idev, hdev); | ||
| 441 | memcpy(data->keycode, def_keymap, sizeof(def_keymap)); | ||
| 442 | idev->name = hdev->name; | ||
| 443 | idev->phys = hdev->phys; | ||
| 444 | idev->uniq = hdev->uniq; | ||
| 445 | idev->id.bustype = hdev->bus; | ||
| 446 | idev->id.vendor = hdev->vendor; | ||
| 447 | idev->id.product = hdev->product; | ||
| 448 | idev->id.version = hdev->version; | ||
| 449 | idev->dev.parent = &hdev->dev; | ||
| 450 | idev->keycode = &data->keycode; | ||
| 451 | idev->keycodemax = PICOLCD_KEYS; | ||
| 452 | idev->keycodesize = sizeof(data->keycode[0]); | ||
| 453 | input_set_capability(idev, EV_MSC, MSC_SCAN); | ||
| 454 | set_bit(EV_REP, idev->evbit); | ||
| 455 | for (i = 0; i < PICOLCD_KEYS; i++) | ||
| 456 | input_set_capability(idev, EV_KEY, data->keycode[i]); | ||
| 457 | error = input_register_device(idev); | ||
| 458 | if (error) { | ||
| 459 | hid_err(hdev, "error registering the input device\n"); | ||
| 460 | input_free_device(idev); | ||
| 461 | return error; | ||
| 462 | } | ||
| 463 | data->input_keys = idev; | ||
| 464 | return 0; | ||
| 465 | } | ||
| 466 | |||
| 467 | static void picolcd_exit_keys(struct picolcd_data *data) | ||
| 468 | { | ||
| 469 | struct input_dev *idev = data->input_keys; | ||
| 470 | |||
| 471 | data->input_keys = NULL; | ||
| 472 | if (idev) | ||
| 473 | input_unregister_device(idev); | ||
| 474 | } | ||
| 475 | |||
| 476 | static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | ||
| 477 | { | ||
| 478 | int error; | ||
| 479 | |||
| 480 | /* Setup keypad input device */ | ||
| 481 | error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev)); | ||
| 482 | if (error) | ||
| 483 | goto err; | ||
| 484 | |||
| 485 | /* Setup CIR input device */ | ||
| 486 | error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev)); | ||
| 487 | if (error) | ||
| 488 | goto err; | ||
| 489 | |||
| 490 | /* Set up the framebuffer device */ | ||
| 491 | error = picolcd_init_framebuffer(data); | ||
| 492 | if (error) | ||
| 493 | goto err; | ||
| 494 | |||
| 495 | /* Setup lcd class device */ | ||
| 496 | error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev)); | ||
| 497 | if (error) | ||
| 498 | goto err; | ||
| 499 | |||
| 500 | /* Setup backlight class device */ | ||
| 501 | error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev)); | ||
| 502 | if (error) | ||
| 503 | goto err; | ||
| 504 | |||
| 505 | /* Setup the LED class devices */ | ||
| 506 | error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev)); | ||
| 507 | if (error) | ||
| 508 | goto err; | ||
| 509 | |||
| 510 | picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev), | ||
| 511 | picolcd_out_report(REPORT_EE_WRITE, hdev), | ||
| 512 | picolcd_out_report(REPORT_READ_MEMORY, hdev), | ||
| 513 | picolcd_out_report(REPORT_WRITE_MEMORY, hdev), | ||
| 514 | picolcd_out_report(REPORT_RESET, hdev)); | ||
| 515 | return 0; | ||
| 516 | err: | ||
| 517 | picolcd_exit_leds(data); | ||
| 518 | picolcd_exit_backlight(data); | ||
| 519 | picolcd_exit_lcd(data); | ||
| 520 | picolcd_exit_framebuffer(data); | ||
| 521 | picolcd_exit_cir(data); | ||
| 522 | picolcd_exit_keys(data); | ||
| 523 | return error; | ||
| 524 | } | ||
| 525 | |||
| 526 | static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data) | ||
| 527 | { | ||
| 528 | picolcd_init_devfs(data, NULL, NULL, | ||
| 529 | picolcd_out_report(REPORT_BL_READ_MEMORY, hdev), | ||
| 530 | picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL); | ||
| 531 | return 0; | ||
| 532 | } | ||
| 533 | |||
| 534 | static int picolcd_probe(struct hid_device *hdev, | ||
| 535 | const struct hid_device_id *id) | ||
| 536 | { | ||
| 537 | struct picolcd_data *data; | ||
| 538 | int error = -ENOMEM; | ||
| 539 | |||
| 540 | dbg_hid(PICOLCD_NAME " hardware probe...\n"); | ||
| 541 | |||
| 542 | /* | ||
| 543 | * Let's allocate the picolcd data structure, set some reasonable | ||
| 544 | * defaults, and associate it with the device | ||
| 545 | */ | ||
| 546 | data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL); | ||
| 547 | if (data == NULL) { | ||
| 548 | hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n"); | ||
| 549 | error = -ENOMEM; | ||
| 550 | goto err_no_cleanup; | ||
| 551 | } | ||
| 552 | |||
| 553 | spin_lock_init(&data->lock); | ||
| 554 | mutex_init(&data->mutex); | ||
| 555 | data->hdev = hdev; | ||
| 556 | data->opmode_delay = 5000; | ||
| 557 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
| 558 | data->status |= PICOLCD_BOOTLOADER; | ||
| 559 | hid_set_drvdata(hdev, data); | ||
| 560 | |||
| 561 | /* Parse the device reports and start it up */ | ||
| 562 | error = hid_parse(hdev); | ||
| 563 | if (error) { | ||
| 564 | hid_err(hdev, "device report parse failed\n"); | ||
| 565 | goto err_cleanup_data; | ||
| 566 | } | ||
| 567 | |||
| 568 | error = hid_hw_start(hdev, 0); | ||
| 569 | if (error) { | ||
| 570 | hid_err(hdev, "hardware start failed\n"); | ||
| 571 | goto err_cleanup_data; | ||
| 572 | } | ||
| 573 | |||
| 574 | error = hid_hw_open(hdev); | ||
| 575 | if (error) { | ||
| 576 | hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n"); | ||
| 577 | goto err_cleanup_hid_hw; | ||
| 578 | } | ||
| 579 | |||
| 580 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 581 | if (error) { | ||
| 582 | hid_err(hdev, "failed to create sysfs attributes\n"); | ||
| 583 | goto err_cleanup_hid_ll; | ||
| 584 | } | ||
| 585 | |||
| 586 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 587 | if (error) { | ||
| 588 | hid_err(hdev, "failed to create sysfs attributes\n"); | ||
| 589 | goto err_cleanup_sysfs1; | ||
| 590 | } | ||
| 591 | |||
| 592 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 593 | error = picolcd_probe_bootloader(hdev, data); | ||
| 594 | else | ||
| 595 | error = picolcd_probe_lcd(hdev, data); | ||
| 596 | if (error) | ||
| 597 | goto err_cleanup_sysfs2; | ||
| 598 | |||
| 599 | dbg_hid(PICOLCD_NAME " activated and initialized\n"); | ||
| 600 | return 0; | ||
| 601 | |||
| 602 | err_cleanup_sysfs2: | ||
| 603 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 604 | err_cleanup_sysfs1: | ||
| 605 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 606 | err_cleanup_hid_ll: | ||
| 607 | hid_hw_close(hdev); | ||
| 608 | err_cleanup_hid_hw: | ||
| 609 | hid_hw_stop(hdev); | ||
| 610 | err_cleanup_data: | ||
| 611 | kfree(data); | ||
| 612 | err_no_cleanup: | ||
| 613 | hid_set_drvdata(hdev, NULL); | ||
| 614 | |||
| 615 | return error; | ||
| 616 | } | ||
| 617 | |||
| 618 | static void picolcd_remove(struct hid_device *hdev) | ||
| 619 | { | ||
| 620 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 621 | unsigned long flags; | ||
| 622 | |||
| 623 | dbg_hid(PICOLCD_NAME " hardware remove...\n"); | ||
| 624 | spin_lock_irqsave(&data->lock, flags); | ||
| 625 | data->status |= PICOLCD_FAILED; | ||
| 626 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 627 | |||
| 628 | picolcd_exit_devfs(data); | ||
| 629 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 630 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 631 | hid_hw_close(hdev); | ||
| 632 | hid_hw_stop(hdev); | ||
| 633 | |||
| 634 | /* Shortcut potential pending reply that will never arrive */ | ||
| 635 | spin_lock_irqsave(&data->lock, flags); | ||
| 636 | if (data->pending) | ||
| 637 | complete(&data->pending->ready); | ||
| 638 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 639 | |||
| 640 | /* Cleanup LED */ | ||
| 641 | picolcd_exit_leds(data); | ||
| 642 | /* Clean up the framebuffer */ | ||
| 643 | picolcd_exit_backlight(data); | ||
| 644 | picolcd_exit_lcd(data); | ||
| 645 | picolcd_exit_framebuffer(data); | ||
| 646 | /* Cleanup input */ | ||
| 647 | picolcd_exit_cir(data); | ||
| 648 | picolcd_exit_keys(data); | ||
| 649 | |||
| 650 | hid_set_drvdata(hdev, NULL); | ||
| 651 | mutex_destroy(&data->mutex); | ||
| 652 | /* Finally, clean up the picolcd data itself */ | ||
| 653 | kfree(data); | ||
| 654 | } | ||
| 655 | |||
| 656 | static const struct hid_device_id picolcd_devices[] = { | ||
| 657 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | ||
| 658 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | ||
| 659 | { } | ||
| 660 | }; | ||
| 661 | MODULE_DEVICE_TABLE(hid, picolcd_devices); | ||
| 662 | |||
| 663 | static struct hid_driver picolcd_driver = { | ||
| 664 | .name = "hid-picolcd", | ||
| 665 | .id_table = picolcd_devices, | ||
| 666 | .probe = picolcd_probe, | ||
| 667 | .remove = picolcd_remove, | ||
| 668 | .raw_event = picolcd_raw_event, | ||
| 669 | #ifdef CONFIG_PM | ||
| 670 | .suspend = picolcd_suspend, | ||
| 671 | .resume = picolcd_resume, | ||
| 672 | .reset_resume = picolcd_reset_resume, | ||
| 673 | #endif | ||
| 674 | }; | ||
| 675 | |||
| 676 | static int __init picolcd_init(void) | ||
| 677 | { | ||
| 678 | return hid_register_driver(&picolcd_driver); | ||
| 679 | } | ||
| 680 | |||
| 681 | static void __exit picolcd_exit(void) | ||
| 682 | { | ||
| 683 | hid_unregister_driver(&picolcd_driver); | ||
| 684 | } | ||
| 685 | |||
| 686 | module_init(picolcd_init); | ||
| 687 | module_exit(picolcd_exit); | ||
| 688 | MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver"); | ||
| 689 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c new file mode 100644 index 000000000000..4809aa1bdb9c --- /dev/null +++ b/drivers/hid/hid-picolcd_debugfs.c | |||
| @@ -0,0 +1,899 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include <linux/hid-debug.h> | ||
| 22 | #include "usbhid/usbhid.h" | ||
| 23 | #include <linux/usb.h> | ||
| 24 | |||
| 25 | #include <linux/fb.h> | ||
| 26 | #include <linux/seq_file.h> | ||
| 27 | #include <linux/debugfs.h> | ||
| 28 | |||
| 29 | #include <linux/module.h> | ||
| 30 | #include <linux/uaccess.h> | ||
| 31 | |||
| 32 | #include "hid-picolcd.h" | ||
| 33 | |||
| 34 | |||
| 35 | static int picolcd_debug_reset_show(struct seq_file *f, void *p) | ||
| 36 | { | ||
| 37 | if (picolcd_fbinfo((struct picolcd_data *)f->private)) | ||
| 38 | seq_printf(f, "all fb\n"); | ||
| 39 | else | ||
| 40 | seq_printf(f, "all\n"); | ||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | static int picolcd_debug_reset_open(struct inode *inode, struct file *f) | ||
| 45 | { | ||
| 46 | return single_open(f, picolcd_debug_reset_show, inode->i_private); | ||
| 47 | } | ||
| 48 | |||
| 49 | static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf, | ||
| 50 | size_t count, loff_t *ppos) | ||
| 51 | { | ||
| 52 | struct picolcd_data *data = ((struct seq_file *)f->private_data)->private; | ||
| 53 | char buf[32]; | ||
| 54 | size_t cnt = min(count, sizeof(buf)-1); | ||
| 55 | if (copy_from_user(buf, user_buf, cnt)) | ||
| 56 | return -EFAULT; | ||
| 57 | |||
| 58 | while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n')) | ||
| 59 | cnt--; | ||
| 60 | buf[cnt] = '\0'; | ||
| 61 | if (strcmp(buf, "all") == 0) { | ||
| 62 | picolcd_reset(data->hdev); | ||
| 63 | picolcd_fb_reset(data, 1); | ||
| 64 | } else if (strcmp(buf, "fb") == 0) { | ||
| 65 | picolcd_fb_reset(data, 1); | ||
| 66 | } else { | ||
| 67 | return -EINVAL; | ||
| 68 | } | ||
| 69 | return count; | ||
| 70 | } | ||
| 71 | |||
| 72 | static const struct file_operations picolcd_debug_reset_fops = { | ||
| 73 | .owner = THIS_MODULE, | ||
| 74 | .open = picolcd_debug_reset_open, | ||
| 75 | .read = seq_read, | ||
| 76 | .llseek = seq_lseek, | ||
| 77 | .write = picolcd_debug_reset_write, | ||
| 78 | .release = single_release, | ||
| 79 | }; | ||
| 80 | |||
| 81 | /* | ||
| 82 | * The "eeprom" file | ||
| 83 | */ | ||
| 84 | static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, | ||
| 85 | size_t s, loff_t *off) | ||
| 86 | { | ||
| 87 | struct picolcd_data *data = f->private_data; | ||
| 88 | struct picolcd_pending *resp; | ||
| 89 | u8 raw_data[3]; | ||
| 90 | ssize_t ret = -EIO; | ||
| 91 | |||
| 92 | if (s == 0) | ||
| 93 | return -EINVAL; | ||
| 94 | if (*off > 0x0ff) | ||
| 95 | return 0; | ||
| 96 | |||
| 97 | /* prepare buffer with info about what we want to read (addr & len) */ | ||
| 98 | raw_data[0] = *off & 0xff; | ||
| 99 | raw_data[1] = (*off >> 8) & 0xff; | ||
| 100 | raw_data[2] = s < 20 ? s : 20; | ||
| 101 | if (*off + raw_data[2] > 0xff) | ||
| 102 | raw_data[2] = 0x100 - *off; | ||
| 103 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data, | ||
| 104 | sizeof(raw_data)); | ||
| 105 | if (!resp) | ||
| 106 | return -EIO; | ||
| 107 | |||
| 108 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
| 109 | /* successful read :) */ | ||
| 110 | ret = resp->raw_data[2]; | ||
| 111 | if (ret > s) | ||
| 112 | ret = s; | ||
| 113 | if (copy_to_user(u, resp->raw_data+3, ret)) | ||
| 114 | ret = -EFAULT; | ||
| 115 | else | ||
| 116 | *off += ret; | ||
| 117 | } /* anything else is some kind of IO error */ | ||
| 118 | |||
| 119 | kfree(resp); | ||
| 120 | return ret; | ||
| 121 | } | ||
| 122 | |||
| 123 | static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u, | ||
| 124 | size_t s, loff_t *off) | ||
| 125 | { | ||
| 126 | struct picolcd_data *data = f->private_data; | ||
| 127 | struct picolcd_pending *resp; | ||
| 128 | ssize_t ret = -EIO; | ||
| 129 | u8 raw_data[23]; | ||
| 130 | |||
| 131 | if (s == 0) | ||
| 132 | return -EINVAL; | ||
| 133 | if (*off > 0x0ff) | ||
| 134 | return -ENOSPC; | ||
| 135 | |||
| 136 | memset(raw_data, 0, sizeof(raw_data)); | ||
| 137 | raw_data[0] = *off & 0xff; | ||
| 138 | raw_data[1] = (*off >> 8) & 0xff; | ||
| 139 | raw_data[2] = min_t(size_t, 20, s); | ||
| 140 | if (*off + raw_data[2] > 0xff) | ||
| 141 | raw_data[2] = 0x100 - *off; | ||
| 142 | |||
| 143 | if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2]))) | ||
| 144 | return -EFAULT; | ||
| 145 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data, | ||
| 146 | sizeof(raw_data)); | ||
| 147 | |||
| 148 | if (!resp) | ||
| 149 | return -EIO; | ||
| 150 | |||
| 151 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
| 152 | /* check if written data matches */ | ||
| 153 | if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) { | ||
| 154 | *off += raw_data[2]; | ||
| 155 | ret = raw_data[2]; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | kfree(resp); | ||
| 159 | return ret; | ||
| 160 | } | ||
| 161 | |||
| 162 | /* | ||
| 163 | * Notes: | ||
| 164 | * - read/write happens in chunks of at most 20 bytes, it's up to userspace | ||
| 165 | * to loop in order to get more data. | ||
| 166 | * - on write errors on otherwise correct write request the bytes | ||
| 167 | * that should have been written are in undefined state. | ||
| 168 | */ | ||
| 169 | static const struct file_operations picolcd_debug_eeprom_fops = { | ||
| 170 | .owner = THIS_MODULE, | ||
| 171 | .open = simple_open, | ||
| 172 | .read = picolcd_debug_eeprom_read, | ||
| 173 | .write = picolcd_debug_eeprom_write, | ||
| 174 | .llseek = generic_file_llseek, | ||
| 175 | }; | ||
| 176 | |||
| 177 | /* | ||
| 178 | * The "flash" file | ||
| 179 | */ | ||
| 180 | /* record a flash address to buf (bounds check to be done by caller) */ | ||
| 181 | static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off) | ||
| 182 | { | ||
| 183 | buf[0] = off & 0xff; | ||
| 184 | buf[1] = (off >> 8) & 0xff; | ||
| 185 | if (data->addr_sz == 3) | ||
| 186 | buf[2] = (off >> 16) & 0xff; | ||
| 187 | return data->addr_sz == 2 ? 2 : 3; | ||
| 188 | } | ||
| 189 | |||
| 190 | /* read a given size of data (bounds check to be done by caller) */ | ||
| 191 | static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id, | ||
| 192 | char __user *u, size_t s, loff_t *off) | ||
| 193 | { | ||
| 194 | struct picolcd_pending *resp; | ||
| 195 | u8 raw_data[4]; | ||
| 196 | ssize_t ret = 0; | ||
| 197 | int len_off, err = -EIO; | ||
| 198 | |||
| 199 | while (s > 0) { | ||
| 200 | err = -EIO; | ||
| 201 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 202 | raw_data[len_off] = s > 32 ? 32 : s; | ||
| 203 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1); | ||
| 204 | if (!resp || !resp->in_report) | ||
| 205 | goto skip; | ||
| 206 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 207 | resp->in_report->id == REPORT_BL_READ_MEMORY) { | ||
| 208 | if (memcmp(raw_data, resp->raw_data, len_off+1) != 0) | ||
| 209 | goto skip; | ||
| 210 | if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) { | ||
| 211 | err = -EFAULT; | ||
| 212 | goto skip; | ||
| 213 | } | ||
| 214 | *off += raw_data[len_off]; | ||
| 215 | s -= raw_data[len_off]; | ||
| 216 | ret += raw_data[len_off]; | ||
| 217 | err = 0; | ||
| 218 | } | ||
| 219 | skip: | ||
| 220 | kfree(resp); | ||
| 221 | if (err) | ||
| 222 | return ret > 0 ? ret : err; | ||
| 223 | } | ||
| 224 | return ret; | ||
| 225 | } | ||
| 226 | |||
| 227 | static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u, | ||
| 228 | size_t s, loff_t *off) | ||
| 229 | { | ||
| 230 | struct picolcd_data *data = f->private_data; | ||
| 231 | |||
| 232 | if (s == 0) | ||
| 233 | return -EINVAL; | ||
| 234 | if (*off > 0x05fff) | ||
| 235 | return 0; | ||
| 236 | if (*off + s > 0x05fff) | ||
| 237 | s = 0x06000 - *off; | ||
| 238 | |||
| 239 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 240 | return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off); | ||
| 241 | else | ||
| 242 | return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off); | ||
| 243 | } | ||
| 244 | |||
| 245 | /* erase block aligned to 64bytes boundary */ | ||
| 246 | static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id, | ||
| 247 | loff_t *off) | ||
| 248 | { | ||
| 249 | struct picolcd_pending *resp; | ||
| 250 | u8 raw_data[3]; | ||
| 251 | int len_off; | ||
| 252 | ssize_t ret = -EIO; | ||
| 253 | |||
| 254 | if (*off & 0x3f) | ||
| 255 | return -EINVAL; | ||
| 256 | |||
| 257 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 258 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off); | ||
| 259 | if (!resp || !resp->in_report) | ||
| 260 | goto skip; | ||
| 261 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 262 | resp->in_report->id == REPORT_BL_ERASE_MEMORY) { | ||
| 263 | if (memcmp(raw_data, resp->raw_data, len_off) != 0) | ||
| 264 | goto skip; | ||
| 265 | ret = 0; | ||
| 266 | } | ||
| 267 | skip: | ||
| 268 | kfree(resp); | ||
| 269 | return ret; | ||
| 270 | } | ||
| 271 | |||
| 272 | /* write a given size of data (bounds check to be done by caller) */ | ||
| 273 | static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id, | ||
| 274 | const char __user *u, size_t s, loff_t *off) | ||
| 275 | { | ||
| 276 | struct picolcd_pending *resp; | ||
| 277 | u8 raw_data[36]; | ||
| 278 | ssize_t ret = 0; | ||
| 279 | int len_off, err = -EIO; | ||
| 280 | |||
| 281 | while (s > 0) { | ||
| 282 | err = -EIO; | ||
| 283 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 284 | raw_data[len_off] = s > 32 ? 32 : s; | ||
| 285 | if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) { | ||
| 286 | err = -EFAULT; | ||
| 287 | break; | ||
| 288 | } | ||
| 289 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, | ||
| 290 | len_off+1+raw_data[len_off]); | ||
| 291 | if (!resp || !resp->in_report) | ||
| 292 | goto skip; | ||
| 293 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 294 | resp->in_report->id == REPORT_BL_WRITE_MEMORY) { | ||
| 295 | if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0) | ||
| 296 | goto skip; | ||
| 297 | *off += raw_data[len_off]; | ||
| 298 | s -= raw_data[len_off]; | ||
| 299 | ret += raw_data[len_off]; | ||
| 300 | err = 0; | ||
| 301 | } | ||
| 302 | skip: | ||
| 303 | kfree(resp); | ||
| 304 | if (err) | ||
| 305 | break; | ||
| 306 | } | ||
| 307 | return ret > 0 ? ret : err; | ||
| 308 | } | ||
| 309 | |||
| 310 | static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u, | ||
| 311 | size_t s, loff_t *off) | ||
| 312 | { | ||
| 313 | struct picolcd_data *data = f->private_data; | ||
| 314 | ssize_t err, ret = 0; | ||
| 315 | int report_erase, report_write; | ||
| 316 | |||
| 317 | if (s == 0) | ||
| 318 | return -EINVAL; | ||
| 319 | if (*off > 0x5fff) | ||
| 320 | return -ENOSPC; | ||
| 321 | if (s & 0x3f) | ||
| 322 | return -EINVAL; | ||
| 323 | if (*off & 0x3f) | ||
| 324 | return -EINVAL; | ||
| 325 | |||
| 326 | if (data->status & PICOLCD_BOOTLOADER) { | ||
| 327 | report_erase = REPORT_BL_ERASE_MEMORY; | ||
| 328 | report_write = REPORT_BL_WRITE_MEMORY; | ||
| 329 | } else { | ||
| 330 | report_erase = REPORT_ERASE_MEMORY; | ||
| 331 | report_write = REPORT_WRITE_MEMORY; | ||
| 332 | } | ||
| 333 | mutex_lock(&data->mutex_flash); | ||
| 334 | while (s > 0) { | ||
| 335 | err = _picolcd_flash_erase64(data, report_erase, off); | ||
| 336 | if (err) | ||
| 337 | break; | ||
| 338 | err = _picolcd_flash_write(data, report_write, u, 64, off); | ||
| 339 | if (err < 0) | ||
| 340 | break; | ||
| 341 | ret += err; | ||
| 342 | *off += err; | ||
| 343 | s -= err; | ||
| 344 | if (err != 64) | ||
| 345 | break; | ||
| 346 | } | ||
| 347 | mutex_unlock(&data->mutex_flash); | ||
| 348 | return ret > 0 ? ret : err; | ||
| 349 | } | ||
| 350 | |||
| 351 | /* | ||
| 352 | * Notes: | ||
| 353 | * - concurrent writing is prevented by mutex and all writes must be | ||
| 354 | * n*64 bytes and 64-byte aligned, each write being preceded by an | ||
| 355 | * ERASE which erases a 64byte block. | ||
| 356 | * If less than requested was written or an error is returned for an | ||
| 357 | * otherwise correct write request the next 64-byte block which should | ||
| 358 | * have been written is in undefined state (mostly: original, erased, | ||
| 359 | * (half-)written with write error) | ||
| 360 | * - reading can happen without special restriction | ||
| 361 | */ | ||
| 362 | static const struct file_operations picolcd_debug_flash_fops = { | ||
| 363 | .owner = THIS_MODULE, | ||
| 364 | .open = simple_open, | ||
| 365 | .read = picolcd_debug_flash_read, | ||
| 366 | .write = picolcd_debug_flash_write, | ||
| 367 | .llseek = generic_file_llseek, | ||
| 368 | }; | ||
| 369 | |||
| 370 | |||
| 371 | /* | ||
| 372 | * Helper code for HID report level dumping/debugging | ||
| 373 | */ | ||
| 374 | static const char * const error_codes[] = { | ||
| 375 | "success", "parameter missing", "data_missing", "block readonly", | ||
| 376 | "block not erasable", "block too big", "section overflow", | ||
| 377 | "invalid command length", "invalid data length", | ||
| 378 | }; | ||
| 379 | |||
| 380 | static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, | ||
| 381 | const size_t data_len) | ||
| 382 | { | ||
| 383 | int i, j; | ||
| 384 | for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) { | ||
| 385 | dst[j++] = hex_asc[(data[i] >> 4) & 0x0f]; | ||
| 386 | dst[j++] = hex_asc[data[i] & 0x0f]; | ||
| 387 | dst[j++] = ' '; | ||
| 388 | } | ||
| 389 | dst[j] = '\0'; | ||
| 390 | if (j > 0) | ||
| 391 | dst[j-1] = '\n'; | ||
| 392 | if (i < data_len && j > 2) | ||
| 393 | dst[j-2] = dst[j-3] = '.'; | ||
| 394 | } | ||
| 395 | |||
| 396 | void picolcd_debug_out_report(struct picolcd_data *data, | ||
| 397 | struct hid_device *hdev, struct hid_report *report) | ||
| 398 | { | ||
| 399 | u8 raw_data[70]; | ||
| 400 | int raw_size = (report->size >> 3) + 1; | ||
| 401 | char *buff; | ||
| 402 | #define BUFF_SZ 256 | ||
| 403 | |||
| 404 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
| 405 | if (list_empty(&hdev->debug_list)) | ||
| 406 | return; | ||
| 407 | |||
| 408 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
| 409 | if (!buff) | ||
| 410 | return; | ||
| 411 | |||
| 412 | snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", | ||
| 413 | report->id, raw_size); | ||
| 414 | hid_debug_event(hdev, buff); | ||
| 415 | if (raw_size + 5 > sizeof(raw_data)) { | ||
| 416 | kfree(buff); | ||
| 417 | hid_debug_event(hdev, " TOO BIG\n"); | ||
| 418 | return; | ||
| 419 | } else { | ||
| 420 | raw_data[0] = report->id; | ||
| 421 | hid_output_report(report, raw_data); | ||
| 422 | dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); | ||
| 423 | hid_debug_event(hdev, buff); | ||
| 424 | } | ||
| 425 | |||
| 426 | switch (report->id) { | ||
| 427 | case REPORT_LED_STATE: | ||
| 428 | /* 1 data byte with GPO state */ | ||
| 429 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 430 | "REPORT_LED_STATE", report->id, raw_size-1); | ||
| 431 | hid_debug_event(hdev, buff); | ||
| 432 | snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]); | ||
| 433 | hid_debug_event(hdev, buff); | ||
| 434 | break; | ||
| 435 | case REPORT_BRIGHTNESS: | ||
| 436 | /* 1 data byte with brightness */ | ||
| 437 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 438 | "REPORT_BRIGHTNESS", report->id, raw_size-1); | ||
| 439 | hid_debug_event(hdev, buff); | ||
| 440 | snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]); | ||
| 441 | hid_debug_event(hdev, buff); | ||
| 442 | break; | ||
| 443 | case REPORT_CONTRAST: | ||
| 444 | /* 1 data byte with contrast */ | ||
| 445 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 446 | "REPORT_CONTRAST", report->id, raw_size-1); | ||
| 447 | hid_debug_event(hdev, buff); | ||
| 448 | snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]); | ||
| 449 | hid_debug_event(hdev, buff); | ||
| 450 | break; | ||
| 451 | case REPORT_RESET: | ||
| 452 | /* 2 data bytes with reset duration in ms */ | ||
| 453 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 454 | "REPORT_RESET", report->id, raw_size-1); | ||
| 455 | hid_debug_event(hdev, buff); | ||
| 456 | snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n", | ||
| 457 | raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]); | ||
| 458 | hid_debug_event(hdev, buff); | ||
| 459 | break; | ||
| 460 | case REPORT_LCD_CMD: | ||
| 461 | /* 63 data bytes with LCD commands */ | ||
| 462 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 463 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 464 | hid_debug_event(hdev, buff); | ||
| 465 | /* TODO: format decoding */ | ||
| 466 | break; | ||
| 467 | case REPORT_LCD_DATA: | ||
| 468 | /* 63 data bytes with LCD data */ | ||
| 469 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 470 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 471 | /* TODO: format decoding */ | ||
| 472 | hid_debug_event(hdev, buff); | ||
| 473 | break; | ||
| 474 | case REPORT_LCD_CMD_DATA: | ||
| 475 | /* 63 data bytes with LCD commands and data */ | ||
| 476 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 477 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 478 | /* TODO: format decoding */ | ||
| 479 | hid_debug_event(hdev, buff); | ||
| 480 | break; | ||
| 481 | case REPORT_EE_READ: | ||
| 482 | /* 3 data bytes with read area description */ | ||
| 483 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 484 | "REPORT_EE_READ", report->id, raw_size-1); | ||
| 485 | hid_debug_event(hdev, buff); | ||
| 486 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 487 | raw_data[2], raw_data[1]); | ||
| 488 | hid_debug_event(hdev, buff); | ||
| 489 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 490 | hid_debug_event(hdev, buff); | ||
| 491 | break; | ||
| 492 | case REPORT_EE_WRITE: | ||
| 493 | /* 3+1..20 data bytes with write area description */ | ||
| 494 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 495 | "REPORT_EE_WRITE", report->id, raw_size-1); | ||
| 496 | hid_debug_event(hdev, buff); | ||
| 497 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 498 | raw_data[2], raw_data[1]); | ||
| 499 | hid_debug_event(hdev, buff); | ||
| 500 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 501 | hid_debug_event(hdev, buff); | ||
| 502 | if (raw_data[3] == 0) { | ||
| 503 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 504 | } else if (raw_data[3] + 4 <= raw_size) { | ||
| 505 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 506 | hid_debug_event(hdev, buff); | ||
| 507 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 508 | } else { | ||
| 509 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 510 | } | ||
| 511 | hid_debug_event(hdev, buff); | ||
| 512 | break; | ||
| 513 | case REPORT_ERASE_MEMORY: | ||
| 514 | case REPORT_BL_ERASE_MEMORY: | ||
| 515 | /* 3 data bytes with pointer inside erase block */ | ||
| 516 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 517 | "REPORT_ERASE_MEMORY", report->id, raw_size-1); | ||
| 518 | hid_debug_event(hdev, buff); | ||
| 519 | switch (data->addr_sz) { | ||
| 520 | case 2: | ||
| 521 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n", | ||
| 522 | raw_data[2], raw_data[1]); | ||
| 523 | break; | ||
| 524 | case 3: | ||
| 525 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n", | ||
| 526 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 527 | break; | ||
| 528 | default: | ||
| 529 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 530 | } | ||
| 531 | hid_debug_event(hdev, buff); | ||
| 532 | break; | ||
| 533 | case REPORT_READ_MEMORY: | ||
| 534 | case REPORT_BL_READ_MEMORY: | ||
| 535 | /* 4 data bytes with read area description */ | ||
| 536 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 537 | "REPORT_READ_MEMORY", report->id, raw_size-1); | ||
| 538 | hid_debug_event(hdev, buff); | ||
| 539 | switch (data->addr_sz) { | ||
| 540 | case 2: | ||
| 541 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 542 | raw_data[2], raw_data[1]); | ||
| 543 | hid_debug_event(hdev, buff); | ||
| 544 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 545 | break; | ||
| 546 | case 3: | ||
| 547 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 548 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 549 | hid_debug_event(hdev, buff); | ||
| 550 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 551 | break; | ||
| 552 | default: | ||
| 553 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 554 | } | ||
| 555 | hid_debug_event(hdev, buff); | ||
| 556 | break; | ||
| 557 | case REPORT_WRITE_MEMORY: | ||
| 558 | case REPORT_BL_WRITE_MEMORY: | ||
| 559 | /* 4+1..32 data bytes with write adrea description */ | ||
| 560 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 561 | "REPORT_WRITE_MEMORY", report->id, raw_size-1); | ||
| 562 | hid_debug_event(hdev, buff); | ||
| 563 | switch (data->addr_sz) { | ||
| 564 | case 2: | ||
| 565 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 566 | raw_data[2], raw_data[1]); | ||
| 567 | hid_debug_event(hdev, buff); | ||
| 568 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 569 | hid_debug_event(hdev, buff); | ||
| 570 | if (raw_data[3] == 0) { | ||
| 571 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 572 | } else if (raw_data[3] + 4 <= raw_size) { | ||
| 573 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 574 | hid_debug_event(hdev, buff); | ||
| 575 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 576 | } else { | ||
| 577 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 578 | } | ||
| 579 | break; | ||
| 580 | case 3: | ||
| 581 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 582 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 583 | hid_debug_event(hdev, buff); | ||
| 584 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 585 | hid_debug_event(hdev, buff); | ||
| 586 | if (raw_data[4] == 0) { | ||
| 587 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 588 | } else if (raw_data[4] + 5 <= raw_size) { | ||
| 589 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 590 | hid_debug_event(hdev, buff); | ||
| 591 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
| 592 | } else { | ||
| 593 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 594 | } | ||
| 595 | break; | ||
| 596 | default: | ||
| 597 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 598 | } | ||
| 599 | hid_debug_event(hdev, buff); | ||
| 600 | break; | ||
| 601 | case REPORT_SPLASH_RESTART: | ||
| 602 | /* TODO */ | ||
| 603 | break; | ||
| 604 | case REPORT_EXIT_KEYBOARD: | ||
| 605 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 606 | "REPORT_EXIT_KEYBOARD", report->id, raw_size-1); | ||
| 607 | hid_debug_event(hdev, buff); | ||
| 608 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
| 609 | raw_data[1] | (raw_data[2] << 8), | ||
| 610 | raw_data[2], raw_data[1]); | ||
| 611 | hid_debug_event(hdev, buff); | ||
| 612 | break; | ||
| 613 | case REPORT_VERSION: | ||
| 614 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 615 | "REPORT_VERSION", report->id, raw_size-1); | ||
| 616 | hid_debug_event(hdev, buff); | ||
| 617 | break; | ||
| 618 | case REPORT_DEVID: | ||
| 619 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 620 | "REPORT_DEVID", report->id, raw_size-1); | ||
| 621 | hid_debug_event(hdev, buff); | ||
| 622 | break; | ||
| 623 | case REPORT_SPLASH_SIZE: | ||
| 624 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 625 | "REPORT_SPLASH_SIZE", report->id, raw_size-1); | ||
| 626 | hid_debug_event(hdev, buff); | ||
| 627 | break; | ||
| 628 | case REPORT_HOOK_VERSION: | ||
| 629 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 630 | "REPORT_HOOK_VERSION", report->id, raw_size-1); | ||
| 631 | hid_debug_event(hdev, buff); | ||
| 632 | break; | ||
| 633 | case REPORT_EXIT_FLASHER: | ||
| 634 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 635 | "REPORT_VERSION", report->id, raw_size-1); | ||
| 636 | hid_debug_event(hdev, buff); | ||
| 637 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
| 638 | raw_data[1] | (raw_data[2] << 8), | ||
| 639 | raw_data[2], raw_data[1]); | ||
| 640 | hid_debug_event(hdev, buff); | ||
| 641 | break; | ||
| 642 | default: | ||
| 643 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 644 | "<unknown>", report->id, raw_size-1); | ||
| 645 | hid_debug_event(hdev, buff); | ||
| 646 | break; | ||
| 647 | } | ||
| 648 | wake_up_interruptible(&hdev->debug_wait); | ||
| 649 | kfree(buff); | ||
| 650 | } | ||
| 651 | |||
| 652 | void picolcd_debug_raw_event(struct picolcd_data *data, | ||
| 653 | struct hid_device *hdev, struct hid_report *report, | ||
| 654 | u8 *raw_data, int size) | ||
| 655 | { | ||
| 656 | char *buff; | ||
| 657 | |||
| 658 | #define BUFF_SZ 256 | ||
| 659 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
| 660 | if (list_empty(&hdev->debug_list)) | ||
| 661 | return; | ||
| 662 | |||
| 663 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
| 664 | if (!buff) | ||
| 665 | return; | ||
| 666 | |||
| 667 | switch (report->id) { | ||
| 668 | case REPORT_ERROR_CODE: | ||
| 669 | /* 2 data bytes with affected report and error code */ | ||
| 670 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 671 | "REPORT_ERROR_CODE", report->id, size-1); | ||
| 672 | hid_debug_event(hdev, buff); | ||
| 673 | if (raw_data[2] < ARRAY_SIZE(error_codes)) | ||
| 674 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n", | ||
| 675 | raw_data[2], error_codes[raw_data[2]], raw_data[1]); | ||
| 676 | else | ||
| 677 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n", | ||
| 678 | raw_data[2], raw_data[1]); | ||
| 679 | hid_debug_event(hdev, buff); | ||
| 680 | break; | ||
| 681 | case REPORT_KEY_STATE: | ||
| 682 | /* 2 data bytes with key state */ | ||
| 683 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 684 | "REPORT_KEY_STATE", report->id, size-1); | ||
| 685 | hid_debug_event(hdev, buff); | ||
| 686 | if (raw_data[1] == 0) | ||
| 687 | snprintf(buff, BUFF_SZ, "\tNo key pressed\n"); | ||
| 688 | else if (raw_data[2] == 0) | ||
| 689 | snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n", | ||
| 690 | raw_data[1], raw_data[1]); | ||
| 691 | else | ||
| 692 | snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n", | ||
| 693 | raw_data[1], raw_data[1], raw_data[2], raw_data[2]); | ||
| 694 | hid_debug_event(hdev, buff); | ||
| 695 | break; | ||
| 696 | case REPORT_IR_DATA: | ||
| 697 | /* Up to 20 byes of IR scancode data */ | ||
| 698 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 699 | "REPORT_IR_DATA", report->id, size-1); | ||
| 700 | hid_debug_event(hdev, buff); | ||
| 701 | if (raw_data[1] == 0) { | ||
| 702 | snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n"); | ||
| 703 | hid_debug_event(hdev, buff); | ||
| 704 | } else if (raw_data[1] + 1 <= size) { | ||
| 705 | snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ", | ||
| 706 | raw_data[1]); | ||
| 707 | hid_debug_event(hdev, buff); | ||
| 708 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]); | ||
| 709 | hid_debug_event(hdev, buff); | ||
| 710 | } else { | ||
| 711 | snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n", | ||
| 712 | raw_data[1]-1); | ||
| 713 | hid_debug_event(hdev, buff); | ||
| 714 | } | ||
| 715 | break; | ||
| 716 | case REPORT_EE_DATA: | ||
| 717 | /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */ | ||
| 718 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 719 | "REPORT_EE_DATA", report->id, size-1); | ||
| 720 | hid_debug_event(hdev, buff); | ||
| 721 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 722 | raw_data[2], raw_data[1]); | ||
| 723 | hid_debug_event(hdev, buff); | ||
| 724 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 725 | hid_debug_event(hdev, buff); | ||
| 726 | if (raw_data[3] == 0) { | ||
| 727 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 728 | hid_debug_event(hdev, buff); | ||
| 729 | } else if (raw_data[3] + 4 <= size) { | ||
| 730 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 731 | hid_debug_event(hdev, buff); | ||
| 732 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 733 | hid_debug_event(hdev, buff); | ||
| 734 | } else { | ||
| 735 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 736 | hid_debug_event(hdev, buff); | ||
| 737 | } | ||
| 738 | break; | ||
| 739 | case REPORT_MEMORY: | ||
| 740 | /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */ | ||
| 741 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 742 | "REPORT_MEMORY", report->id, size-1); | ||
| 743 | hid_debug_event(hdev, buff); | ||
| 744 | switch (data->addr_sz) { | ||
| 745 | case 2: | ||
| 746 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 747 | raw_data[2], raw_data[1]); | ||
| 748 | hid_debug_event(hdev, buff); | ||
| 749 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 750 | hid_debug_event(hdev, buff); | ||
| 751 | if (raw_data[3] == 0) { | ||
| 752 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 753 | } else if (raw_data[3] + 4 <= size) { | ||
| 754 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 755 | hid_debug_event(hdev, buff); | ||
| 756 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 757 | } else { | ||
| 758 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 759 | } | ||
| 760 | break; | ||
| 761 | case 3: | ||
| 762 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 763 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 764 | hid_debug_event(hdev, buff); | ||
| 765 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 766 | hid_debug_event(hdev, buff); | ||
| 767 | if (raw_data[4] == 0) { | ||
| 768 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 769 | } else if (raw_data[4] + 5 <= size) { | ||
| 770 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 771 | hid_debug_event(hdev, buff); | ||
| 772 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
| 773 | } else { | ||
| 774 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 775 | } | ||
| 776 | break; | ||
| 777 | default: | ||
| 778 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 779 | } | ||
| 780 | hid_debug_event(hdev, buff); | ||
| 781 | break; | ||
| 782 | case REPORT_VERSION: | ||
| 783 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 784 | "REPORT_VERSION", report->id, size-1); | ||
| 785 | hid_debug_event(hdev, buff); | ||
| 786 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
| 787 | raw_data[2], raw_data[1]); | ||
| 788 | hid_debug_event(hdev, buff); | ||
| 789 | break; | ||
| 790 | case REPORT_BL_ERASE_MEMORY: | ||
| 791 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 792 | "REPORT_BL_ERASE_MEMORY", report->id, size-1); | ||
| 793 | hid_debug_event(hdev, buff); | ||
| 794 | /* TODO */ | ||
| 795 | break; | ||
| 796 | case REPORT_BL_READ_MEMORY: | ||
| 797 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 798 | "REPORT_BL_READ_MEMORY", report->id, size-1); | ||
| 799 | hid_debug_event(hdev, buff); | ||
| 800 | /* TODO */ | ||
| 801 | break; | ||
| 802 | case REPORT_BL_WRITE_MEMORY: | ||
| 803 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 804 | "REPORT_BL_WRITE_MEMORY", report->id, size-1); | ||
| 805 | hid_debug_event(hdev, buff); | ||
| 806 | /* TODO */ | ||
| 807 | break; | ||
| 808 | case REPORT_DEVID: | ||
| 809 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 810 | "REPORT_DEVID", report->id, size-1); | ||
| 811 | hid_debug_event(hdev, buff); | ||
| 812 | snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n", | ||
| 813 | raw_data[1], raw_data[2], raw_data[3], raw_data[4]); | ||
| 814 | hid_debug_event(hdev, buff); | ||
| 815 | snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n", | ||
| 816 | raw_data[5]); | ||
| 817 | hid_debug_event(hdev, buff); | ||
| 818 | break; | ||
| 819 | case REPORT_SPLASH_SIZE: | ||
| 820 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 821 | "REPORT_SPLASH_SIZE", report->id, size-1); | ||
| 822 | hid_debug_event(hdev, buff); | ||
| 823 | snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n", | ||
| 824 | (raw_data[2] << 8) | raw_data[1]); | ||
| 825 | hid_debug_event(hdev, buff); | ||
| 826 | snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n", | ||
| 827 | (raw_data[4] << 8) | raw_data[3]); | ||
| 828 | hid_debug_event(hdev, buff); | ||
| 829 | break; | ||
| 830 | case REPORT_HOOK_VERSION: | ||
| 831 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 832 | "REPORT_HOOK_VERSION", report->id, size-1); | ||
| 833 | hid_debug_event(hdev, buff); | ||
| 834 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
| 835 | raw_data[1], raw_data[2]); | ||
| 836 | hid_debug_event(hdev, buff); | ||
| 837 | break; | ||
| 838 | default: | ||
| 839 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 840 | "<unknown>", report->id, size-1); | ||
| 841 | hid_debug_event(hdev, buff); | ||
| 842 | break; | ||
| 843 | } | ||
| 844 | wake_up_interruptible(&hdev->debug_wait); | ||
| 845 | kfree(buff); | ||
| 846 | } | ||
| 847 | |||
| 848 | void picolcd_init_devfs(struct picolcd_data *data, | ||
| 849 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
| 850 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
| 851 | struct hid_report *reset) | ||
| 852 | { | ||
| 853 | struct hid_device *hdev = data->hdev; | ||
| 854 | |||
| 855 | mutex_init(&data->mutex_flash); | ||
| 856 | |||
| 857 | /* reset */ | ||
| 858 | if (reset) | ||
| 859 | data->debug_reset = debugfs_create_file("reset", 0600, | ||
| 860 | hdev->debug_dir, data, &picolcd_debug_reset_fops); | ||
| 861 | |||
| 862 | /* eeprom */ | ||
| 863 | if (eeprom_r || eeprom_w) | ||
| 864 | data->debug_eeprom = debugfs_create_file("eeprom", | ||
| 865 | (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0), | ||
| 866 | hdev->debug_dir, data, &picolcd_debug_eeprom_fops); | ||
| 867 | |||
| 868 | /* flash */ | ||
| 869 | if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8) | ||
| 870 | data->addr_sz = flash_r->field[0]->report_count - 1; | ||
| 871 | else | ||
| 872 | data->addr_sz = -1; | ||
| 873 | if (data->addr_sz == 2 || data->addr_sz == 3) { | ||
| 874 | data->debug_flash = debugfs_create_file("flash", | ||
| 875 | (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0), | ||
| 876 | hdev->debug_dir, data, &picolcd_debug_flash_fops); | ||
| 877 | } else if (flash_r || flash_w) | ||
| 878 | hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n"); | ||
| 879 | } | ||
| 880 | |||
| 881 | void picolcd_exit_devfs(struct picolcd_data *data) | ||
| 882 | { | ||
| 883 | struct dentry *dent; | ||
| 884 | |||
| 885 | dent = data->debug_reset; | ||
| 886 | data->debug_reset = NULL; | ||
| 887 | if (dent) | ||
| 888 | debugfs_remove(dent); | ||
| 889 | dent = data->debug_eeprom; | ||
| 890 | data->debug_eeprom = NULL; | ||
| 891 | if (dent) | ||
| 892 | debugfs_remove(dent); | ||
| 893 | dent = data->debug_flash; | ||
| 894 | data->debug_flash = NULL; | ||
| 895 | if (dent) | ||
| 896 | debugfs_remove(dent); | ||
| 897 | mutex_destroy(&data->mutex_flash); | ||
| 898 | } | ||
| 899 | |||
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c new file mode 100644 index 000000000000..0008a512211d --- /dev/null +++ b/drivers/hid/hid-picolcd_fb.c | |||
| @@ -0,0 +1,615 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include <linux/vmalloc.h> | ||
| 22 | #include "usbhid/usbhid.h" | ||
| 23 | #include <linux/usb.h> | ||
| 24 | |||
| 25 | #include <linux/fb.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | |||
| 28 | #include "hid-picolcd.h" | ||
| 29 | |||
| 30 | /* Framebuffer | ||
| 31 | * | ||
| 32 | * The PicoLCD use a Topway LCD module of 256x64 pixel | ||
| 33 | * This display area is tiled over 4 controllers with 8 tiles | ||
| 34 | * each. Each tile has 8x64 pixel, each data byte representing | ||
| 35 | * a 1-bit wide vertical line of the tile. | ||
| 36 | * | ||
| 37 | * The display can be updated at a tile granularity. | ||
| 38 | * | ||
| 39 | * Chip 1 Chip 2 Chip 3 Chip 4 | ||
| 40 | * +----------------+----------------+----------------+----------------+ | ||
| 41 | * | Tile 1 | Tile 1 | Tile 1 | Tile 1 | | ||
| 42 | * +----------------+----------------+----------------+----------------+ | ||
| 43 | * | Tile 2 | Tile 2 | Tile 2 | Tile 2 | | ||
| 44 | * +----------------+----------------+----------------+----------------+ | ||
| 45 | * ... | ||
| 46 | * +----------------+----------------+----------------+----------------+ | ||
| 47 | * | Tile 8 | Tile 8 | Tile 8 | Tile 8 | | ||
| 48 | * +----------------+----------------+----------------+----------------+ | ||
| 49 | */ | ||
| 50 | #define PICOLCDFB_NAME "picolcdfb" | ||
| 51 | #define PICOLCDFB_WIDTH (256) | ||
| 52 | #define PICOLCDFB_HEIGHT (64) | ||
| 53 | #define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8) | ||
| 54 | |||
| 55 | #define PICOLCDFB_UPDATE_RATE_LIMIT 10 | ||
| 56 | #define PICOLCDFB_UPDATE_RATE_DEFAULT 2 | ||
| 57 | |||
| 58 | /* Framebuffer visual structures */ | ||
| 59 | static const struct fb_fix_screeninfo picolcdfb_fix = { | ||
| 60 | .id = PICOLCDFB_NAME, | ||
| 61 | .type = FB_TYPE_PACKED_PIXELS, | ||
| 62 | .visual = FB_VISUAL_MONO01, | ||
| 63 | .xpanstep = 0, | ||
| 64 | .ypanstep = 0, | ||
| 65 | .ywrapstep = 0, | ||
| 66 | .line_length = PICOLCDFB_WIDTH / 8, | ||
| 67 | .accel = FB_ACCEL_NONE, | ||
| 68 | }; | ||
| 69 | |||
| 70 | static const struct fb_var_screeninfo picolcdfb_var = { | ||
| 71 | .xres = PICOLCDFB_WIDTH, | ||
| 72 | .yres = PICOLCDFB_HEIGHT, | ||
| 73 | .xres_virtual = PICOLCDFB_WIDTH, | ||
| 74 | .yres_virtual = PICOLCDFB_HEIGHT, | ||
| 75 | .width = 103, | ||
| 76 | .height = 26, | ||
| 77 | .bits_per_pixel = 1, | ||
| 78 | .grayscale = 1, | ||
| 79 | .red = { | ||
| 80 | .offset = 0, | ||
| 81 | .length = 1, | ||
| 82 | .msb_right = 0, | ||
| 83 | }, | ||
| 84 | .green = { | ||
| 85 | .offset = 0, | ||
| 86 | .length = 1, | ||
| 87 | .msb_right = 0, | ||
| 88 | }, | ||
| 89 | .blue = { | ||
| 90 | .offset = 0, | ||
| 91 | .length = 1, | ||
| 92 | .msb_right = 0, | ||
| 93 | }, | ||
| 94 | .transp = { | ||
| 95 | .offset = 0, | ||
| 96 | .length = 0, | ||
| 97 | .msb_right = 0, | ||
| 98 | }, | ||
| 99 | }; | ||
| 100 | |||
| 101 | /* Send a given tile to PicoLCD */ | ||
| 102 | static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap, | ||
| 103 | int chip, int tile) | ||
| 104 | { | ||
| 105 | struct hid_report *report1, *report2; | ||
| 106 | unsigned long flags; | ||
| 107 | u8 *tdata; | ||
| 108 | int i; | ||
| 109 | |||
| 110 | report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev); | ||
| 111 | if (!report1 || report1->maxfield != 1) | ||
| 112 | return -ENODEV; | ||
| 113 | report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev); | ||
| 114 | if (!report2 || report2->maxfield != 1) | ||
| 115 | return -ENODEV; | ||
| 116 | |||
| 117 | spin_lock_irqsave(&data->lock, flags); | ||
| 118 | if ((data->status & PICOLCD_FAILED)) { | ||
| 119 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 120 | return -ENODEV; | ||
| 121 | } | ||
| 122 | hid_set_field(report1->field[0], 0, chip << 2); | ||
| 123 | hid_set_field(report1->field[0], 1, 0x02); | ||
| 124 | hid_set_field(report1->field[0], 2, 0x00); | ||
| 125 | hid_set_field(report1->field[0], 3, 0x00); | ||
| 126 | hid_set_field(report1->field[0], 4, 0xb8 | tile); | ||
| 127 | hid_set_field(report1->field[0], 5, 0x00); | ||
| 128 | hid_set_field(report1->field[0], 6, 0x00); | ||
| 129 | hid_set_field(report1->field[0], 7, 0x40); | ||
| 130 | hid_set_field(report1->field[0], 8, 0x00); | ||
| 131 | hid_set_field(report1->field[0], 9, 0x00); | ||
| 132 | hid_set_field(report1->field[0], 10, 32); | ||
| 133 | |||
| 134 | hid_set_field(report2->field[0], 0, (chip << 2) | 0x01); | ||
| 135 | hid_set_field(report2->field[0], 1, 0x00); | ||
| 136 | hid_set_field(report2->field[0], 2, 0x00); | ||
| 137 | hid_set_field(report2->field[0], 3, 32); | ||
| 138 | |||
| 139 | tdata = vbitmap + (tile * 4 + chip) * 64; | ||
| 140 | for (i = 0; i < 64; i++) | ||
| 141 | if (i < 32) | ||
| 142 | hid_set_field(report1->field[0], 11 + i, tdata[i]); | ||
| 143 | else | ||
| 144 | hid_set_field(report2->field[0], 4 + i - 32, tdata[i]); | ||
| 145 | |||
| 146 | usbhid_submit_report(data->hdev, report1, USB_DIR_OUT); | ||
| 147 | usbhid_submit_report(data->hdev, report2, USB_DIR_OUT); | ||
| 148 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | |||
| 152 | /* Translate a single tile*/ | ||
| 153 | static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp, | ||
| 154 | int chip, int tile) | ||
| 155 | { | ||
| 156 | int i, b, changed = 0; | ||
| 157 | u8 tdata[64]; | ||
| 158 | u8 *vdata = vbitmap + (tile * 4 + chip) * 64; | ||
| 159 | |||
| 160 | if (bpp == 1) { | ||
| 161 | for (b = 7; b >= 0; b--) { | ||
| 162 | const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32; | ||
| 163 | for (i = 0; i < 64; i++) { | ||
| 164 | tdata[i] <<= 1; | ||
| 165 | tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } else if (bpp == 8) { | ||
| 169 | for (b = 7; b >= 0; b--) { | ||
| 170 | const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8; | ||
| 171 | for (i = 0; i < 64; i++) { | ||
| 172 | tdata[i] <<= 1; | ||
| 173 | tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } else { | ||
| 177 | /* Oops, we should never get here! */ | ||
| 178 | WARN_ON(1); | ||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | for (i = 0; i < 64; i++) | ||
| 183 | if (tdata[i] != vdata[i]) { | ||
| 184 | changed = 1; | ||
| 185 | vdata[i] = tdata[i]; | ||
| 186 | } | ||
| 187 | return changed; | ||
| 188 | } | ||
| 189 | |||
| 190 | void picolcd_fb_refresh(struct picolcd_data *data) | ||
| 191 | { | ||
| 192 | if (data->fb_info) | ||
| 193 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Reconfigure LCD display */ | ||
| 197 | int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
| 198 | { | ||
| 199 | struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev); | ||
| 200 | struct picolcd_fb_data *fbdata = data->fb_info->par; | ||
| 201 | int i, j; | ||
| 202 | unsigned long flags; | ||
| 203 | static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 }; | ||
| 204 | |||
| 205 | if (!report || report->maxfield != 1) | ||
| 206 | return -ENODEV; | ||
| 207 | |||
| 208 | spin_lock_irqsave(&data->lock, flags); | ||
| 209 | for (i = 0; i < 4; i++) { | ||
| 210 | for (j = 0; j < report->field[0]->maxusage; j++) | ||
| 211 | if (j == 0) | ||
| 212 | hid_set_field(report->field[0], j, i << 2); | ||
| 213 | else if (j < sizeof(mapcmd)) | ||
| 214 | hid_set_field(report->field[0], j, mapcmd[j]); | ||
| 215 | else | ||
| 216 | hid_set_field(report->field[0], j, 0); | ||
| 217 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 218 | } | ||
| 219 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 220 | |||
| 221 | if (clear) { | ||
| 222 | memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE); | ||
| 223 | memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp); | ||
| 224 | } | ||
| 225 | fbdata->force = 1; | ||
| 226 | |||
| 227 | /* schedule first output of framebuffer */ | ||
| 228 | if (fbdata->ready) | ||
| 229 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
| 230 | else | ||
| 231 | fbdata->ready = 1; | ||
| 232 | |||
| 233 | return 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | /* Update fb_vbitmap from the screen_base and send changed tiles to device */ | ||
| 237 | static void picolcd_fb_update(struct fb_info *info) | ||
| 238 | { | ||
| 239 | int chip, tile, n; | ||
| 240 | unsigned long flags; | ||
| 241 | struct picolcd_fb_data *fbdata = info->par; | ||
| 242 | struct picolcd_data *data; | ||
| 243 | |||
| 244 | mutex_lock(&info->lock); | ||
| 245 | |||
| 246 | spin_lock_irqsave(&fbdata->lock, flags); | ||
| 247 | if (!fbdata->ready && fbdata->picolcd) | ||
| 248 | picolcd_fb_reset(fbdata->picolcd, 0); | ||
| 249 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
| 250 | |||
| 251 | /* | ||
| 252 | * Translate the framebuffer into the format needed by the PicoLCD. | ||
| 253 | * See display layout above. | ||
| 254 | * Do this one tile after the other and push those tiles that changed. | ||
| 255 | * | ||
| 256 | * Wait for our IO to complete as otherwise we might flood the queue! | ||
| 257 | */ | ||
| 258 | n = 0; | ||
| 259 | for (chip = 0; chip < 4; chip++) | ||
| 260 | for (tile = 0; tile < 8; tile++) { | ||
| 261 | if (!fbdata->force && !picolcd_fb_update_tile( | ||
| 262 | fbdata->vbitmap, fbdata->bitmap, | ||
| 263 | fbdata->bpp, chip, tile)) | ||
| 264 | continue; | ||
| 265 | n += 2; | ||
| 266 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { | ||
| 267 | spin_lock_irqsave(&fbdata->lock, flags); | ||
| 268 | data = fbdata->picolcd; | ||
| 269 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
| 270 | mutex_unlock(&info->lock); | ||
| 271 | if (!data) | ||
| 272 | return; | ||
| 273 | usbhid_wait_io(data->hdev); | ||
| 274 | mutex_lock(&info->lock); | ||
| 275 | n = 0; | ||
| 276 | } | ||
| 277 | spin_lock_irqsave(&fbdata->lock, flags); | ||
| 278 | data = fbdata->picolcd; | ||
| 279 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
| 280 | if (!data || picolcd_fb_send_tile(data, | ||
| 281 | fbdata->vbitmap, chip, tile)) | ||
| 282 | goto out; | ||
| 283 | } | ||
| 284 | fbdata->force = false; | ||
| 285 | if (n) { | ||
| 286 | spin_lock_irqsave(&fbdata->lock, flags); | ||
| 287 | data = fbdata->picolcd; | ||
| 288 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
| 289 | mutex_unlock(&info->lock); | ||
| 290 | if (data) | ||
| 291 | usbhid_wait_io(data->hdev); | ||
| 292 | return; | ||
| 293 | } | ||
| 294 | out: | ||
| 295 | mutex_unlock(&info->lock); | ||
| 296 | } | ||
| 297 | |||
| 298 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 299 | static void picolcd_fb_fillrect(struct fb_info *info, | ||
| 300 | const struct fb_fillrect *rect) | ||
| 301 | { | ||
| 302 | if (!info->par) | ||
| 303 | return; | ||
| 304 | sys_fillrect(info, rect); | ||
| 305 | |||
| 306 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 307 | } | ||
| 308 | |||
| 309 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 310 | static void picolcd_fb_copyarea(struct fb_info *info, | ||
| 311 | const struct fb_copyarea *area) | ||
| 312 | { | ||
| 313 | if (!info->par) | ||
| 314 | return; | ||
| 315 | sys_copyarea(info, area); | ||
| 316 | |||
| 317 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 321 | static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image) | ||
| 322 | { | ||
| 323 | if (!info->par) | ||
| 324 | return; | ||
| 325 | sys_imageblit(info, image); | ||
| 326 | |||
| 327 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 328 | } | ||
| 329 | |||
| 330 | /* | ||
| 331 | * this is the slow path from userspace. they can seek and write to | ||
| 332 | * the fb. it's inefficient to do anything less than a full screen draw | ||
| 333 | */ | ||
| 334 | static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf, | ||
| 335 | size_t count, loff_t *ppos) | ||
| 336 | { | ||
| 337 | ssize_t ret; | ||
| 338 | if (!info->par) | ||
| 339 | return -ENODEV; | ||
| 340 | ret = fb_sys_write(info, buf, count, ppos); | ||
| 341 | if (ret >= 0) | ||
| 342 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 343 | return ret; | ||
| 344 | } | ||
| 345 | |||
| 346 | static int picolcd_fb_blank(int blank, struct fb_info *info) | ||
| 347 | { | ||
| 348 | /* We let fb notification do this for us via lcd/backlight device */ | ||
| 349 | return 0; | ||
| 350 | } | ||
| 351 | |||
| 352 | static void picolcd_fb_destroy(struct fb_info *info) | ||
| 353 | { | ||
| 354 | struct picolcd_fb_data *fbdata = info->par; | ||
| 355 | |||
| 356 | /* make sure no work is deferred */ | ||
| 357 | fb_deferred_io_cleanup(info); | ||
| 358 | |||
| 359 | /* No thridparty should ever unregister our framebuffer! */ | ||
| 360 | WARN_ON(fbdata->picolcd != NULL); | ||
| 361 | |||
| 362 | vfree((u8 *)info->fix.smem_start); | ||
| 363 | framebuffer_release(info); | ||
| 364 | } | ||
| 365 | |||
| 366 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
| 367 | { | ||
| 368 | __u32 bpp = var->bits_per_pixel; | ||
| 369 | __u32 activate = var->activate; | ||
| 370 | |||
| 371 | /* only allow 1/8 bit depth (8-bit is grayscale) */ | ||
| 372 | *var = picolcdfb_var; | ||
| 373 | var->activate = activate; | ||
| 374 | if (bpp >= 8) { | ||
| 375 | var->bits_per_pixel = 8; | ||
| 376 | var->red.length = 8; | ||
| 377 | var->green.length = 8; | ||
| 378 | var->blue.length = 8; | ||
| 379 | } else { | ||
| 380 | var->bits_per_pixel = 1; | ||
| 381 | var->red.length = 1; | ||
| 382 | var->green.length = 1; | ||
| 383 | var->blue.length = 1; | ||
| 384 | } | ||
| 385 | return 0; | ||
| 386 | } | ||
| 387 | |||
| 388 | static int picolcd_set_par(struct fb_info *info) | ||
| 389 | { | ||
| 390 | struct picolcd_fb_data *fbdata = info->par; | ||
| 391 | u8 *tmp_fb, *o_fb; | ||
| 392 | if (info->var.bits_per_pixel == fbdata->bpp) | ||
| 393 | return 0; | ||
| 394 | /* switch between 1/8 bit depths */ | ||
| 395 | if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8) | ||
| 396 | return -EINVAL; | ||
| 397 | |||
| 398 | o_fb = fbdata->bitmap; | ||
| 399 | tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL); | ||
| 400 | if (!tmp_fb) | ||
| 401 | return -ENOMEM; | ||
| 402 | |||
| 403 | /* translate FB content to new bits-per-pixel */ | ||
| 404 | if (info->var.bits_per_pixel == 1) { | ||
| 405 | int i, b; | ||
| 406 | for (i = 0; i < PICOLCDFB_SIZE; i++) { | ||
| 407 | u8 p = 0; | ||
| 408 | for (b = 0; b < 8; b++) { | ||
| 409 | p <<= 1; | ||
| 410 | p |= o_fb[i*8+b] ? 0x01 : 0x00; | ||
| 411 | } | ||
| 412 | tmp_fb[i] = p; | ||
| 413 | } | ||
| 414 | memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE); | ||
| 415 | info->fix.visual = FB_VISUAL_MONO01; | ||
| 416 | info->fix.line_length = PICOLCDFB_WIDTH / 8; | ||
| 417 | } else { | ||
| 418 | int i; | ||
| 419 | memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE); | ||
| 420 | for (i = 0; i < PICOLCDFB_SIZE * 8; i++) | ||
| 421 | o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; | ||
| 422 | info->fix.visual = FB_VISUAL_DIRECTCOLOR; | ||
| 423 | info->fix.line_length = PICOLCDFB_WIDTH; | ||
| 424 | } | ||
| 425 | |||
| 426 | kfree(tmp_fb); | ||
| 427 | fbdata->bpp = info->var.bits_per_pixel; | ||
| 428 | return 0; | ||
| 429 | } | ||
| 430 | |||
| 431 | /* Note this can't be const because of struct fb_info definition */ | ||
| 432 | static struct fb_ops picolcdfb_ops = { | ||
| 433 | .owner = THIS_MODULE, | ||
| 434 | .fb_destroy = picolcd_fb_destroy, | ||
| 435 | .fb_read = fb_sys_read, | ||
| 436 | .fb_write = picolcd_fb_write, | ||
| 437 | .fb_blank = picolcd_fb_blank, | ||
| 438 | .fb_fillrect = picolcd_fb_fillrect, | ||
| 439 | .fb_copyarea = picolcd_fb_copyarea, | ||
| 440 | .fb_imageblit = picolcd_fb_imageblit, | ||
| 441 | .fb_check_var = picolcd_fb_check_var, | ||
| 442 | .fb_set_par = picolcd_set_par, | ||
| 443 | }; | ||
| 444 | |||
| 445 | |||
| 446 | /* Callback from deferred IO workqueue */ | ||
| 447 | static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist) | ||
| 448 | { | ||
| 449 | picolcd_fb_update(info); | ||
| 450 | } | ||
| 451 | |||
| 452 | static const struct fb_deferred_io picolcd_fb_defio = { | ||
| 453 | .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT, | ||
| 454 | .deferred_io = picolcd_fb_deferred_io, | ||
| 455 | }; | ||
| 456 | |||
| 457 | |||
| 458 | /* | ||
| 459 | * The "fb_update_rate" sysfs attribute | ||
| 460 | */ | ||
| 461 | static ssize_t picolcd_fb_update_rate_show(struct device *dev, | ||
| 462 | struct device_attribute *attr, char *buf) | ||
| 463 | { | ||
| 464 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 465 | struct picolcd_fb_data *fbdata = data->fb_info->par; | ||
| 466 | unsigned i, fb_update_rate = fbdata->update_rate; | ||
| 467 | size_t ret = 0; | ||
| 468 | |||
| 469 | for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) | ||
| 470 | if (ret >= PAGE_SIZE) | ||
| 471 | break; | ||
| 472 | else if (i == fb_update_rate) | ||
| 473 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i); | ||
| 474 | else | ||
| 475 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i); | ||
| 476 | if (ret > 0) | ||
| 477 | buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n'; | ||
| 478 | return ret; | ||
| 479 | } | ||
| 480 | |||
| 481 | static ssize_t picolcd_fb_update_rate_store(struct device *dev, | ||
| 482 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 483 | { | ||
| 484 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 485 | struct picolcd_fb_data *fbdata = data->fb_info->par; | ||
| 486 | int i; | ||
| 487 | unsigned u; | ||
| 488 | |||
| 489 | if (count < 1 || count > 10) | ||
| 490 | return -EINVAL; | ||
| 491 | |||
| 492 | i = sscanf(buf, "%u", &u); | ||
| 493 | if (i != 1) | ||
| 494 | return -EINVAL; | ||
| 495 | |||
| 496 | if (u > PICOLCDFB_UPDATE_RATE_LIMIT) | ||
| 497 | return -ERANGE; | ||
| 498 | else if (u == 0) | ||
| 499 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
| 500 | |||
| 501 | fbdata->update_rate = u; | ||
| 502 | data->fb_info->fbdefio->delay = HZ / fbdata->update_rate; | ||
| 503 | return count; | ||
| 504 | } | ||
| 505 | |||
| 506 | static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show, | ||
| 507 | picolcd_fb_update_rate_store); | ||
| 508 | |||
| 509 | /* initialize Framebuffer device */ | ||
| 510 | int picolcd_init_framebuffer(struct picolcd_data *data) | ||
| 511 | { | ||
| 512 | struct device *dev = &data->hdev->dev; | ||
| 513 | struct fb_info *info = NULL; | ||
| 514 | struct picolcd_fb_data *fbdata = NULL; | ||
| 515 | int i, error = -ENOMEM; | ||
| 516 | u32 *palette; | ||
| 517 | |||
| 518 | /* The extra memory is: | ||
| 519 | * - 256*u32 for pseudo_palette | ||
| 520 | * - struct fb_deferred_io | ||
| 521 | */ | ||
| 522 | info = framebuffer_alloc(256 * sizeof(u32) + | ||
| 523 | sizeof(struct fb_deferred_io) + | ||
| 524 | sizeof(struct picolcd_fb_data) + | ||
| 525 | PICOLCDFB_SIZE, dev); | ||
| 526 | if (info == NULL) { | ||
| 527 | dev_err(dev, "failed to allocate a framebuffer\n"); | ||
| 528 | goto err_nomem; | ||
| 529 | } | ||
| 530 | |||
| 531 | info->fbdefio = info->par; | ||
| 532 | *info->fbdefio = picolcd_fb_defio; | ||
| 533 | info->par += sizeof(struct fb_deferred_io); | ||
| 534 | palette = info->par; | ||
| 535 | info->par += 256 * sizeof(u32); | ||
| 536 | for (i = 0; i < 256; i++) | ||
| 537 | palette[i] = i > 0 && i < 16 ? 0xff : 0; | ||
| 538 | info->pseudo_palette = palette; | ||
| 539 | info->fbops = &picolcdfb_ops; | ||
| 540 | info->var = picolcdfb_var; | ||
| 541 | info->fix = picolcdfb_fix; | ||
| 542 | info->fix.smem_len = PICOLCDFB_SIZE*8; | ||
| 543 | info->flags = FBINFO_FLAG_DEFAULT; | ||
| 544 | |||
| 545 | fbdata = info->par; | ||
| 546 | spin_lock_init(&fbdata->lock); | ||
| 547 | fbdata->picolcd = data; | ||
| 548 | fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
| 549 | fbdata->bpp = picolcdfb_var.bits_per_pixel; | ||
| 550 | fbdata->force = 1; | ||
| 551 | fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data); | ||
| 552 | fbdata->bitmap = vmalloc(PICOLCDFB_SIZE*8); | ||
| 553 | if (fbdata->bitmap == NULL) { | ||
| 554 | dev_err(dev, "can't get a free page for framebuffer\n"); | ||
| 555 | goto err_nomem; | ||
| 556 | } | ||
| 557 | info->screen_base = (char __force __iomem *)fbdata->bitmap; | ||
| 558 | info->fix.smem_start = (unsigned long)fbdata->bitmap; | ||
| 559 | memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE); | ||
| 560 | data->fb_info = info; | ||
| 561 | |||
| 562 | error = picolcd_fb_reset(data, 1); | ||
| 563 | if (error) { | ||
| 564 | dev_err(dev, "failed to configure display\n"); | ||
| 565 | goto err_cleanup; | ||
| 566 | } | ||
| 567 | |||
| 568 | error = device_create_file(dev, &dev_attr_fb_update_rate); | ||
| 569 | if (error) { | ||
| 570 | dev_err(dev, "failed to create sysfs attributes\n"); | ||
| 571 | goto err_cleanup; | ||
| 572 | } | ||
| 573 | |||
| 574 | fb_deferred_io_init(info); | ||
| 575 | error = register_framebuffer(info); | ||
| 576 | if (error) { | ||
| 577 | dev_err(dev, "failed to register framebuffer\n"); | ||
| 578 | goto err_sysfs; | ||
| 579 | } | ||
| 580 | return 0; | ||
| 581 | |||
| 582 | err_sysfs: | ||
| 583 | device_remove_file(dev, &dev_attr_fb_update_rate); | ||
| 584 | fb_deferred_io_cleanup(info); | ||
| 585 | err_cleanup: | ||
| 586 | data->fb_info = NULL; | ||
| 587 | |||
| 588 | err_nomem: | ||
| 589 | if (fbdata) | ||
| 590 | vfree(fbdata->bitmap); | ||
| 591 | framebuffer_release(info); | ||
| 592 | return error; | ||
| 593 | } | ||
| 594 | |||
| 595 | void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
| 596 | { | ||
| 597 | struct fb_info *info = data->fb_info; | ||
| 598 | struct picolcd_fb_data *fbdata = info->par; | ||
| 599 | unsigned long flags; | ||
| 600 | |||
| 601 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); | ||
| 602 | |||
| 603 | /* disconnect framebuffer from HID dev */ | ||
| 604 | spin_lock_irqsave(&fbdata->lock, flags); | ||
| 605 | fbdata->picolcd = NULL; | ||
| 606 | spin_unlock_irqrestore(&fbdata->lock, flags); | ||
| 607 | |||
| 608 | /* make sure there is no running update - thus that fbdata->picolcd | ||
| 609 | * once obtained under lock is guaranteed not to get free() under | ||
| 610 | * the feet of the deferred work */ | ||
| 611 | flush_delayed_work_sync(&info->deferred_work); | ||
| 612 | |||
| 613 | data->fb_info = NULL; | ||
| 614 | unregister_framebuffer(info); | ||
| 615 | } | ||
diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c new file mode 100644 index 000000000000..2d0ddc5ac65f --- /dev/null +++ b/drivers/hid/hid-picolcd_lcd.c | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include "usbhid/usbhid.h" | ||
| 22 | #include <linux/usb.h> | ||
| 23 | |||
| 24 | #include <linux/fb.h> | ||
| 25 | #include <linux/lcd.h> | ||
| 26 | |||
| 27 | #include "hid-picolcd.h" | ||
| 28 | |||
| 29 | /* | ||
| 30 | * lcd class device | ||
| 31 | */ | ||
| 32 | static int picolcd_get_contrast(struct lcd_device *ldev) | ||
| 33 | { | ||
| 34 | struct picolcd_data *data = lcd_get_data(ldev); | ||
| 35 | return data->lcd_contrast; | ||
| 36 | } | ||
| 37 | |||
| 38 | static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) | ||
| 39 | { | ||
| 40 | struct picolcd_data *data = lcd_get_data(ldev); | ||
| 41 | struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev); | ||
| 42 | unsigned long flags; | ||
| 43 | |||
| 44 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 45 | return -ENODEV; | ||
| 46 | |||
| 47 | data->lcd_contrast = contrast & 0x0ff; | ||
| 48 | spin_lock_irqsave(&data->lock, flags); | ||
| 49 | hid_set_field(report->field[0], 0, data->lcd_contrast); | ||
| 50 | if (!(data->status & PICOLCD_FAILED)) | ||
| 51 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 52 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb) | ||
| 57 | { | ||
| 58 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev)); | ||
| 59 | } | ||
| 60 | |||
| 61 | static struct lcd_ops picolcd_lcdops = { | ||
| 62 | .get_contrast = picolcd_get_contrast, | ||
| 63 | .set_contrast = picolcd_set_contrast, | ||
| 64 | .check_fb = picolcd_check_lcd_fb, | ||
| 65 | }; | ||
| 66 | |||
| 67 | int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report) | ||
| 68 | { | ||
| 69 | struct device *dev = &data->hdev->dev; | ||
| 70 | struct lcd_device *ldev; | ||
| 71 | |||
| 72 | if (!report) | ||
| 73 | return -ENODEV; | ||
| 74 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 75 | report->field[0]->report_size != 8) { | ||
| 76 | dev_err(dev, "unsupported CONTRAST report"); | ||
| 77 | return -EINVAL; | ||
| 78 | } | ||
| 79 | |||
| 80 | ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops); | ||
| 81 | if (IS_ERR(ldev)) { | ||
| 82 | dev_err(dev, "failed to register LCD\n"); | ||
| 83 | return PTR_ERR(ldev); | ||
| 84 | } | ||
| 85 | ldev->props.max_contrast = 0x0ff; | ||
| 86 | data->lcd_contrast = 0xe5; | ||
| 87 | data->lcd = ldev; | ||
| 88 | picolcd_set_contrast(ldev, 0xe5); | ||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | void picolcd_exit_lcd(struct picolcd_data *data) | ||
| 93 | { | ||
| 94 | struct lcd_device *ldev = data->lcd; | ||
| 95 | |||
| 96 | data->lcd = NULL; | ||
| 97 | if (ldev) | ||
| 98 | lcd_device_unregister(ldev); | ||
| 99 | } | ||
| 100 | |||
| 101 | int picolcd_resume_lcd(struct picolcd_data *data) | ||
| 102 | { | ||
| 103 | if (!data->lcd) | ||
| 104 | return 0; | ||
| 105 | return picolcd_set_contrast(data->lcd, data->lcd_contrast); | ||
| 106 | } | ||
| 107 | |||
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c new file mode 100644 index 000000000000..28cb6a4f9634 --- /dev/null +++ b/drivers/hid/hid-picolcd_leds.c | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * | ||
| 3 | * * | ||
| 4 | * Based on Logitech G13 driver (v0.4) * | ||
| 5 | * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * | ||
| 6 | * * | ||
| 7 | * This program is free software: you can redistribute it and/or modify * | ||
| 8 | * it under the terms of the GNU General Public License as published by * | ||
| 9 | * the Free Software Foundation, version 2 of the License. * | ||
| 10 | * * | ||
| 11 | * This driver is distributed in the hope that it will be useful, but * | ||
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | ||
| 14 | * General Public License for more details. * | ||
| 15 | * * | ||
| 16 | * You should have received a copy of the GNU General Public License * | ||
| 17 | * along with this software. If not see <http://www.gnu.org/licenses/>. * | ||
| 18 | ***************************************************************************/ | ||
| 19 | |||
| 20 | #include <linux/hid.h> | ||
| 21 | #include <linux/hid-debug.h> | ||
| 22 | #include <linux/input.h> | ||
| 23 | #include "hid-ids.h" | ||
| 24 | #include "usbhid/usbhid.h" | ||
| 25 | #include <linux/usb.h> | ||
| 26 | |||
| 27 | #include <linux/fb.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 29 | #include <linux/backlight.h> | ||
| 30 | #include <linux/lcd.h> | ||
| 31 | |||
| 32 | #include <linux/leds.h> | ||
| 33 | |||
| 34 | #include <linux/seq_file.h> | ||
| 35 | #include <linux/debugfs.h> | ||
| 36 | |||
| 37 | #include <linux/completion.h> | ||
| 38 | #include <linux/uaccess.h> | ||
| 39 | #include <linux/module.h> | ||
| 40 | |||
| 41 | #include "hid-picolcd.h" | ||
| 42 | |||
| 43 | |||
| 44 | void picolcd_leds_set(struct picolcd_data *data) | ||
| 45 | { | ||
| 46 | struct hid_report *report; | ||
| 47 | unsigned long flags; | ||
| 48 | |||
| 49 | if (!data->led[0]) | ||
| 50 | return; | ||
| 51 | report = picolcd_out_report(REPORT_LED_STATE, data->hdev); | ||
| 52 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 53 | return; | ||
| 54 | |||
| 55 | spin_lock_irqsave(&data->lock, flags); | ||
| 56 | hid_set_field(report->field[0], 0, data->led_state); | ||
| 57 | if (!(data->status & PICOLCD_FAILED)) | ||
| 58 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 59 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void picolcd_led_set_brightness(struct led_classdev *led_cdev, | ||
| 63 | enum led_brightness value) | ||
| 64 | { | ||
| 65 | struct device *dev; | ||
| 66 | struct hid_device *hdev; | ||
| 67 | struct picolcd_data *data; | ||
| 68 | int i, state = 0; | ||
| 69 | |||
| 70 | dev = led_cdev->dev->parent; | ||
| 71 | hdev = container_of(dev, struct hid_device, dev); | ||
| 72 | data = hid_get_drvdata(hdev); | ||
| 73 | if (!data) | ||
| 74 | return; | ||
| 75 | for (i = 0; i < 8; i++) { | ||
| 76 | if (led_cdev != data->led[i]) | ||
| 77 | continue; | ||
| 78 | state = (data->led_state >> i) & 1; | ||
| 79 | if (value == LED_OFF && state) { | ||
| 80 | data->led_state &= ~(1 << i); | ||
| 81 | picolcd_leds_set(data); | ||
| 82 | } else if (value != LED_OFF && !state) { | ||
| 83 | data->led_state |= 1 << i; | ||
| 84 | picolcd_leds_set(data); | ||
| 85 | } | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev) | ||
| 91 | { | ||
| 92 | struct device *dev; | ||
| 93 | struct hid_device *hdev; | ||
| 94 | struct picolcd_data *data; | ||
| 95 | int i, value = 0; | ||
| 96 | |||
| 97 | dev = led_cdev->dev->parent; | ||
| 98 | hdev = container_of(dev, struct hid_device, dev); | ||
| 99 | data = hid_get_drvdata(hdev); | ||
| 100 | for (i = 0; i < 8; i++) | ||
| 101 | if (led_cdev == data->led[i]) { | ||
| 102 | value = (data->led_state >> i) & 1; | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | return value ? LED_FULL : LED_OFF; | ||
| 106 | } | ||
| 107 | |||
| 108 | int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report) | ||
| 109 | { | ||
| 110 | struct device *dev = &data->hdev->dev; | ||
| 111 | struct led_classdev *led; | ||
| 112 | size_t name_sz = strlen(dev_name(dev)) + 8; | ||
| 113 | char *name; | ||
| 114 | int i, ret = 0; | ||
| 115 | |||
| 116 | if (!report) | ||
| 117 | return -ENODEV; | ||
| 118 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 119 | report->field[0]->report_size != 8) { | ||
| 120 | dev_err(dev, "unsupported LED_STATE report"); | ||
| 121 | return -EINVAL; | ||
| 122 | } | ||
| 123 | |||
| 124 | for (i = 0; i < 8; i++) { | ||
| 125 | led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); | ||
| 126 | if (!led) { | ||
| 127 | dev_err(dev, "can't allocate memory for LED %d\n", i); | ||
| 128 | ret = -ENOMEM; | ||
| 129 | goto err; | ||
| 130 | } | ||
| 131 | name = (void *)(&led[1]); | ||
| 132 | snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i); | ||
| 133 | led->name = name; | ||
| 134 | led->brightness = 0; | ||
| 135 | led->max_brightness = 1; | ||
| 136 | led->brightness_get = picolcd_led_get_brightness; | ||
| 137 | led->brightness_set = picolcd_led_set_brightness; | ||
| 138 | |||
| 139 | data->led[i] = led; | ||
| 140 | ret = led_classdev_register(dev, data->led[i]); | ||
| 141 | if (ret) { | ||
| 142 | data->led[i] = NULL; | ||
| 143 | kfree(led); | ||
| 144 | dev_err(dev, "can't register LED %d\n", i); | ||
| 145 | goto err; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | return 0; | ||
| 149 | err: | ||
| 150 | for (i = 0; i < 8; i++) | ||
| 151 | if (data->led[i]) { | ||
| 152 | led = data->led[i]; | ||
| 153 | data->led[i] = NULL; | ||
| 154 | led_classdev_unregister(led); | ||
| 155 | kfree(led); | ||
| 156 | } | ||
| 157 | return ret; | ||
| 158 | } | ||
| 159 | |||
| 160 | void picolcd_exit_leds(struct picolcd_data *data) | ||
| 161 | { | ||
| 162 | struct led_classdev *led; | ||
| 163 | int i; | ||
| 164 | |||
| 165 | for (i = 0; i < 8; i++) { | ||
| 166 | led = data->led[i]; | ||
| 167 | data->led[i] = NULL; | ||
| 168 | if (!led) | ||
| 169 | continue; | ||
| 170 | led_classdev_unregister(led); | ||
| 171 | kfree(led); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | |||
diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c index 4d3c60d88318..c15adb0c98a1 100644 --- a/drivers/hid/hid-primax.c +++ b/drivers/hid/hid-primax.c | |||
| @@ -64,29 +64,6 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report, | |||
| 64 | return 0; | 64 | return 0; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | static int px_probe(struct hid_device *hid, const struct hid_device_id *id) | ||
| 68 | { | ||
| 69 | int ret; | ||
| 70 | |||
| 71 | ret = hid_parse(hid); | ||
| 72 | if (ret) { | ||
| 73 | hid_err(hid, "parse failed\n"); | ||
| 74 | goto fail; | ||
| 75 | } | ||
| 76 | |||
| 77 | ret = hid_hw_start(hid, HID_CONNECT_DEFAULT); | ||
| 78 | if (ret) | ||
| 79 | hid_err(hid, "hw start failed\n"); | ||
| 80 | |||
| 81 | fail: | ||
| 82 | return ret; | ||
| 83 | } | ||
| 84 | |||
| 85 | static void px_remove(struct hid_device *hid) | ||
| 86 | { | ||
| 87 | hid_hw_stop(hid); | ||
| 88 | } | ||
| 89 | |||
| 90 | static const struct hid_device_id px_devices[] = { | 67 | static const struct hid_device_id px_devices[] = { |
| 91 | { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, | 68 | { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, |
| 92 | { } | 69 | { } |
| @@ -97,8 +74,6 @@ static struct hid_driver px_driver = { | |||
| 97 | .name = "primax", | 74 | .name = "primax", |
| 98 | .id_table = px_devices, | 75 | .id_table = px_devices, |
| 99 | .raw_event = px_raw_event, | 76 | .raw_event = px_raw_event, |
| 100 | .probe = px_probe, | ||
| 101 | .remove = px_remove, | ||
| 102 | }; | 77 | }; |
| 103 | 78 | ||
| 104 | static int __init px_init(void) | 79 | static int __init px_init(void) |
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index b71b77ab0dc7..ec8ca3336315 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c | |||
| @@ -105,7 +105,7 @@ static ssize_t show_channel(struct device *dev, | |||
| 105 | struct device_attribute *attr, char *buf) | 105 | struct device_attribute *attr, char *buf) |
| 106 | { | 106 | { |
| 107 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | 107 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 108 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 108 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 109 | 109 | ||
| 110 | dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel); | 110 | dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel); |
| 111 | 111 | ||
| @@ -118,7 +118,7 @@ static ssize_t store_channel(struct device *dev, | |||
| 118 | struct device_attribute *attr, const char *buf, size_t count) | 118 | struct device_attribute *attr, const char *buf, size_t count) |
| 119 | { | 119 | { |
| 120 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | 120 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 121 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 121 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 122 | 122 | ||
| 123 | unsigned channel = 0; | 123 | unsigned channel = 0; |
| 124 | 124 | ||
| @@ -142,7 +142,7 @@ static ssize_t show_sustain(struct device *dev, | |||
| 142 | struct device_attribute *attr, char *buf) | 142 | struct device_attribute *attr, char *buf) |
| 143 | { | 143 | { |
| 144 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | 144 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 145 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 145 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 146 | 146 | ||
| 147 | dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain); | 147 | dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain); |
| 148 | 148 | ||
| @@ -155,7 +155,7 @@ static ssize_t store_sustain(struct device *dev, | |||
| 155 | struct device_attribute *attr, const char *buf, size_t count) | 155 | struct device_attribute *attr, const char *buf, size_t count) |
| 156 | { | 156 | { |
| 157 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | 157 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 158 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 158 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 159 | 159 | ||
| 160 | unsigned sustain = 0; | 160 | unsigned sustain = 0; |
| 161 | 161 | ||
| @@ -181,7 +181,7 @@ static ssize_t show_octave(struct device *dev, | |||
| 181 | struct device_attribute *attr, char *buf) | 181 | struct device_attribute *attr, char *buf) |
| 182 | { | 182 | { |
| 183 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | 183 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 184 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 184 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 185 | 185 | ||
| 186 | dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave); | 186 | dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave); |
| 187 | 187 | ||
| @@ -194,7 +194,7 @@ static ssize_t store_octave(struct device *dev, | |||
| 194 | struct device_attribute *attr, const char *buf, size_t count) | 194 | struct device_attribute *attr, const char *buf, size_t count) |
| 195 | { | 195 | { |
| 196 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | 196 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
| 197 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 197 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 198 | 198 | ||
| 199 | int octave = 0; | 199 | int octave = 0; |
| 200 | 200 | ||
| @@ -759,7 +759,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 759 | struct hid_field *field, struct hid_usage *usage, | 759 | struct hid_field *field, struct hid_usage *usage, |
| 760 | unsigned long **bit, int *max) | 760 | unsigned long **bit, int *max) |
| 761 | { | 761 | { |
| 762 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 762 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 763 | struct pcmidi_snd *pm; | 763 | struct pcmidi_snd *pm; |
| 764 | 764 | ||
| 765 | pm = pk->pm; | 765 | pm = pk->pm; |
| @@ -777,7 +777,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 777 | static int pk_raw_event(struct hid_device *hdev, struct hid_report *report, | 777 | static int pk_raw_event(struct hid_device *hdev, struct hid_report *report, |
| 778 | u8 *data, int size) | 778 | u8 *data, int size) |
| 779 | { | 779 | { |
| 780 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 780 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 781 | int ret = 0; | 781 | int ret = 0; |
| 782 | 782 | ||
| 783 | if (1 == pk->pm->ifnum) { | 783 | if (1 == pk->pm->ifnum) { |
| @@ -858,7 +858,7 @@ err_free_pk: | |||
| 858 | 858 | ||
| 859 | static void pk_remove(struct hid_device *hdev) | 859 | static void pk_remove(struct hid_device *hdev) |
| 860 | { | 860 | { |
| 861 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | 861 | struct pk_device *pk = hid_get_drvdata(hdev); |
| 862 | struct pcmidi_snd *pm; | 862 | struct pcmidi_snd *pm; |
| 863 | 863 | ||
| 864 | pm = pk->pm; | 864 | pm = pk->pm; |
diff --git a/drivers/hid/hid-ps3remote.c b/drivers/hid/hid-ps3remote.c new file mode 100644 index 000000000000..03811e539d71 --- /dev/null +++ b/drivers/hid/hid-ps3remote.c | |||
| @@ -0,0 +1,215 @@ | |||
| 1 | /* | ||
| 2 | * HID driver for Sony PS3 BD Remote Control | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012 David Dillow <dave@thedillows.org> | ||
| 5 | * Based on a blend of the bluez fakehid user-space code by Marcel Holtmann | ||
| 6 | * and other kernel HID drivers. | ||
| 7 | */ | ||
| 8 | |||
| 9 | /* | ||
| 10 | * This program is free software; you can redistribute it and/or modify it | ||
| 11 | * under the terms of the GNU General Public License as published by the Free | ||
| 12 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 13 | * any later version. | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* NOTE: in order for the Sony PS3 BD Remote Control to be found by | ||
| 17 | * a Bluetooth host, the key combination Start+Enter has to be kept pressed | ||
| 18 | * for about 7 seconds with the Bluetooth Host Controller in discovering mode. | ||
| 19 | * | ||
| 20 | * There will be no PIN request from the device. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/device.h> | ||
| 24 | #include <linux/hid.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | |||
| 27 | #include "hid-ids.h" | ||
| 28 | |||
| 29 | static __u8 ps3remote_rdesc[] = { | ||
| 30 | 0x05, 0x01, /* GUsagePage Generic Desktop */ | ||
| 31 | 0x09, 0x05, /* LUsage 0x05 [Game Pad] */ | ||
| 32 | 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */ | ||
| 33 | |||
| 34 | /* Use collection 1 for joypad buttons */ | ||
| 35 | 0xA1, 0x02, /* MCollection Logical (interrelated data) */ | ||
| 36 | |||
| 37 | /* Ignore the 1st byte, maybe it is used for a controller | ||
| 38 | * number but it's not needed for correct operation */ | ||
| 39 | 0x75, 0x08, /* GReportSize 0x08 [8] */ | ||
| 40 | 0x95, 0x01, /* GReportCount 0x01 [1] */ | ||
| 41 | 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ | ||
| 42 | |||
| 43 | /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these | ||
| 44 | * buttons multiple keypresses are allowed */ | ||
| 45 | 0x05, 0x09, /* GUsagePage Button */ | ||
| 46 | 0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */ | ||
| 47 | 0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */ | ||
| 48 | 0x14, /* GLogicalMinimum [0] */ | ||
| 49 | 0x25, 0x01, /* GLogicalMaximum 0x01 [1] */ | ||
| 50 | 0x75, 0x01, /* GReportSize 0x01 [1] */ | ||
| 51 | 0x95, 0x18, /* GReportCount 0x18 [24] */ | ||
| 52 | 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ | ||
| 53 | |||
| 54 | 0xC0, /* MEndCollection */ | ||
| 55 | |||
| 56 | /* Use collection 2 for remote control buttons */ | ||
| 57 | 0xA1, 0x02, /* MCollection Logical (interrelated data) */ | ||
| 58 | |||
| 59 | /* 5th byte is used for remote control buttons */ | ||
| 60 | 0x05, 0x09, /* GUsagePage Button */ | ||
| 61 | 0x18, /* LUsageMinimum [No button pressed] */ | ||
| 62 | 0x29, 0xFE, /* LUsageMaximum 0xFE [Button 254] */ | ||
| 63 | 0x14, /* GLogicalMinimum [0] */ | ||
| 64 | 0x26, 0xFE, 0x00, /* GLogicalMaximum 0x00FE [254] */ | ||
| 65 | 0x75, 0x08, /* GReportSize 0x08 [8] */ | ||
| 66 | 0x95, 0x01, /* GReportCount 0x01 [1] */ | ||
| 67 | 0x80, /* MInput */ | ||
| 68 | |||
| 69 | /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at | ||
| 70 | * 0xff and 11th is for press indication */ | ||
| 71 | 0x75, 0x08, /* GReportSize 0x08 [8] */ | ||
| 72 | 0x95, 0x06, /* GReportCount 0x06 [6] */ | ||
| 73 | 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ | ||
| 74 | |||
| 75 | /* 12th byte is for battery strength */ | ||
| 76 | 0x05, 0x06, /* GUsagePage Generic Device Controls */ | ||
| 77 | 0x09, 0x20, /* LUsage 0x20 [Battery Strength] */ | ||
| 78 | 0x14, /* GLogicalMinimum [0] */ | ||
| 79 | 0x25, 0x05, /* GLogicalMaximum 0x05 [5] */ | ||
| 80 | 0x75, 0x08, /* GReportSize 0x08 [8] */ | ||
| 81 | 0x95, 0x01, /* GReportCount 0x01 [1] */ | ||
| 82 | 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ | ||
| 83 | |||
| 84 | 0xC0, /* MEndCollection */ | ||
| 85 | |||
| 86 | 0xC0 /* MEndCollection [Game Pad] */ | ||
| 87 | }; | ||
| 88 | |||
| 89 | static const unsigned int ps3remote_keymap_joypad_buttons[] = { | ||
| 90 | [0x01] = KEY_SELECT, | ||
| 91 | [0x02] = BTN_THUMBL, /* L3 */ | ||
| 92 | [0x03] = BTN_THUMBR, /* R3 */ | ||
| 93 | [0x04] = BTN_START, | ||
| 94 | [0x05] = KEY_UP, | ||
| 95 | [0x06] = KEY_RIGHT, | ||
| 96 | [0x07] = KEY_DOWN, | ||
| 97 | [0x08] = KEY_LEFT, | ||
| 98 | [0x09] = BTN_TL2, /* L2 */ | ||
| 99 | [0x0a] = BTN_TR2, /* R2 */ | ||
| 100 | [0x0b] = BTN_TL, /* L1 */ | ||
| 101 | [0x0c] = BTN_TR, /* R1 */ | ||
| 102 | [0x0d] = KEY_OPTION, /* options/triangle */ | ||
| 103 | [0x0e] = KEY_BACK, /* back/circle */ | ||
| 104 | [0x0f] = BTN_0, /* cross */ | ||
| 105 | [0x10] = KEY_SCREEN, /* view/square */ | ||
| 106 | [0x11] = KEY_HOMEPAGE, /* PS button */ | ||
| 107 | [0x14] = KEY_ENTER, | ||
| 108 | }; | ||
| 109 | static const unsigned int ps3remote_keymap_remote_buttons[] = { | ||
| 110 | [0x00] = KEY_1, | ||
| 111 | [0x01] = KEY_2, | ||
| 112 | [0x02] = KEY_3, | ||
| 113 | [0x03] = KEY_4, | ||
| 114 | [0x04] = KEY_5, | ||
| 115 | [0x05] = KEY_6, | ||
| 116 | [0x06] = KEY_7, | ||
| 117 | [0x07] = KEY_8, | ||
| 118 | [0x08] = KEY_9, | ||
| 119 | [0x09] = KEY_0, | ||
| 120 | [0x0e] = KEY_ESC, /* return */ | ||
| 121 | [0x0f] = KEY_CLEAR, | ||
| 122 | [0x16] = KEY_EJECTCD, | ||
| 123 | [0x1a] = KEY_MENU, /* top menu */ | ||
| 124 | [0x28] = KEY_TIME, | ||
| 125 | [0x30] = KEY_PREVIOUS, | ||
| 126 | [0x31] = KEY_NEXT, | ||
| 127 | [0x32] = KEY_PLAY, | ||
| 128 | [0x33] = KEY_REWIND, /* scan back */ | ||
| 129 | [0x34] = KEY_FORWARD, /* scan forward */ | ||
| 130 | [0x38] = KEY_STOP, | ||
| 131 | [0x39] = KEY_PAUSE, | ||
| 132 | [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */ | ||
| 133 | [0x60] = KEY_FRAMEBACK, /* slow/step back */ | ||
| 134 | [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */ | ||
| 135 | [0x63] = KEY_SUBTITLE, | ||
| 136 | [0x64] = KEY_AUDIO, | ||
| 137 | [0x65] = KEY_ANGLE, | ||
| 138 | [0x70] = KEY_INFO, /* display */ | ||
| 139 | [0x80] = KEY_BLUE, | ||
| 140 | [0x81] = KEY_RED, | ||
| 141 | [0x82] = KEY_GREEN, | ||
| 142 | [0x83] = KEY_YELLOW, | ||
| 143 | }; | ||
| 144 | |||
| 145 | static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, | ||
| 146 | unsigned int *rsize) | ||
| 147 | { | ||
| 148 | *rsize = sizeof(ps3remote_rdesc); | ||
| 149 | return ps3remote_rdesc; | ||
| 150 | } | ||
| 151 | |||
| 152 | static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, | ||
| 153 | struct hid_field *field, struct hid_usage *usage, | ||
| 154 | unsigned long **bit, int *max) | ||
| 155 | { | ||
| 156 | unsigned int key = usage->hid & HID_USAGE; | ||
| 157 | |||
| 158 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) | ||
| 159 | return -1; | ||
| 160 | |||
| 161 | switch (usage->collection_index) { | ||
| 162 | case 1: | ||
| 163 | if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons)) | ||
| 164 | return -1; | ||
| 165 | |||
| 166 | key = ps3remote_keymap_joypad_buttons[key]; | ||
| 167 | if (!key) | ||
| 168 | return -1; | ||
| 169 | break; | ||
| 170 | case 2: | ||
| 171 | if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons)) | ||
| 172 | return -1; | ||
| 173 | |||
| 174 | key = ps3remote_keymap_remote_buttons[key]; | ||
| 175 | if (!key) | ||
| 176 | return -1; | ||
| 177 | break; | ||
| 178 | default: | ||
| 179 | return -1; | ||
| 180 | } | ||
| 181 | |||
| 182 | hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); | ||
| 183 | return 1; | ||
| 184 | } | ||
| 185 | |||
| 186 | static const struct hid_device_id ps3remote_devices[] = { | ||
| 187 | /* PS3 BD Remote Control */ | ||
| 188 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, | ||
| 189 | /* Logitech Harmony Adapter for PS3 */ | ||
| 190 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) }, | ||
| 191 | { } | ||
| 192 | }; | ||
| 193 | MODULE_DEVICE_TABLE(hid, ps3remote_devices); | ||
| 194 | |||
| 195 | static struct hid_driver ps3remote_driver = { | ||
| 196 | .name = "ps3_remote", | ||
| 197 | .id_table = ps3remote_devices, | ||
| 198 | .report_fixup = ps3remote_fixup, | ||
| 199 | .input_mapping = ps3remote_mapping, | ||
| 200 | }; | ||
| 201 | |||
| 202 | static int __init ps3remote_init(void) | ||
| 203 | { | ||
| 204 | return hid_register_driver(&ps3remote_driver); | ||
| 205 | } | ||
| 206 | |||
| 207 | static void __exit ps3remote_exit(void) | ||
| 208 | { | ||
| 209 | hid_unregister_driver(&ps3remote_driver); | ||
| 210 | } | ||
| 211 | |||
| 212 | module_init(ps3remote_init); | ||
| 213 | module_exit(ps3remote_exit); | ||
| 214 | MODULE_LICENSE("GPL"); | ||
| 215 | MODULE_AUTHOR("David Dillow <dave@thedillows.org>, Antonio Ospite <ospite@studenti.unina.it>"); | ||
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index 3c1fd8af5e0c..a5821d317229 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | * Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk> | 9 | * Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk> |
| 11 | * | 10 | * |
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 5cd25bd907f8..7f33ebf299c2 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | * Copyright (c) 1999 Andreas Gal | 4 | * Copyright (c) 1999 Andreas Gal |
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2007 Paul Walmsley | ||
| 8 | * Copyright (c) 2008 Jiri Slaby | 7 | * Copyright (c) 2008 Jiri Slaby |
| 9 | * Copyright (c) 2006-2008 Jiri Kosina | 8 | * Copyright (c) 2006-2008 Jiri Kosina |
| 10 | */ | 9 | */ |
diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index d484a0043dd4..45b4b066a262 100644 --- a/drivers/hid/hid-sunplus.c +++ b/drivers/hid/hid-sunplus.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby | 8 | * Copyright (c) 2008 Jiri Slaby |
| 10 | */ | 9 | */ |
| 11 | 10 | ||
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index 3aba02be1f26..2e56a1fd2375 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c | |||
| @@ -466,6 +466,86 @@ static __u8 twhl850_rdesc_fixed2[] = { | |||
| 466 | 0xC0 /* End Collection */ | 466 | 0xC0 /* End Collection */ |
| 467 | }; | 467 | }; |
| 468 | 468 | ||
| 469 | /* | ||
| 470 | * See TWHA60 description, device and HID report descriptors at | ||
| 471 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60 | ||
| 472 | */ | ||
| 473 | |||
| 474 | /* Size of the original descriptors of TWHA60 tablet */ | ||
| 475 | #define TWHA60_RDESC_ORIG_SIZE0 254 | ||
| 476 | #define TWHA60_RDESC_ORIG_SIZE1 139 | ||
| 477 | |||
| 478 | /* Fixed TWHA60 report descriptor, interface 0 (stylus) */ | ||
| 479 | static __u8 twha60_rdesc_fixed0[] = { | ||
| 480 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | ||
| 481 | 0x09, 0x02, /* Usage (Pen), */ | ||
| 482 | 0xA1, 0x01, /* Collection (Application), */ | ||
| 483 | 0x85, 0x09, /* Report ID (9), */ | ||
| 484 | 0x09, 0x20, /* Usage (Stylus), */ | ||
| 485 | 0xA0, /* Collection (Physical), */ | ||
| 486 | 0x75, 0x01, /* Report Size (1), */ | ||
| 487 | 0x09, 0x42, /* Usage (Tip Switch), */ | ||
| 488 | 0x09, 0x44, /* Usage (Barrel Switch), */ | ||
| 489 | 0x09, 0x46, /* Usage (Tablet Pick), */ | ||
| 490 | 0x14, /* Logical Minimum (0), */ | ||
| 491 | 0x25, 0x01, /* Logical Maximum (1), */ | ||
| 492 | 0x95, 0x03, /* Report Count (3), */ | ||
| 493 | 0x81, 0x02, /* Input (Variable), */ | ||
| 494 | 0x95, 0x04, /* Report Count (4), */ | ||
| 495 | 0x81, 0x01, /* Input (Constant), */ | ||
| 496 | 0x09, 0x32, /* Usage (In Range), */ | ||
| 497 | 0x95, 0x01, /* Report Count (1), */ | ||
| 498 | 0x81, 0x02, /* Input (Variable), */ | ||
| 499 | 0x75, 0x10, /* Report Size (16), */ | ||
| 500 | 0x95, 0x01, /* Report Count (1), */ | ||
| 501 | 0x14, /* Logical Minimum (0), */ | ||
| 502 | 0xA4, /* Push, */ | ||
| 503 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
| 504 | 0x55, 0xFD, /* Unit Exponent (-3), */ | ||
| 505 | 0x65, 0x13, /* Unit (Inch), */ | ||
| 506 | 0x34, /* Physical Minimum (0), */ | ||
| 507 | 0x09, 0x30, /* Usage (X), */ | ||
| 508 | 0x46, 0x10, 0x27, /* Physical Maximum (10000), */ | ||
| 509 | 0x27, 0x3F, 0x9C, | ||
| 510 | 0x00, 0x00, /* Logical Maximum (39999), */ | ||
| 511 | 0x81, 0x02, /* Input (Variable), */ | ||
| 512 | 0x09, 0x31, /* Usage (Y), */ | ||
| 513 | 0x46, 0x6A, 0x18, /* Physical Maximum (6250), */ | ||
| 514 | 0x26, 0xA7, 0x61, /* Logical Maximum (24999), */ | ||
| 515 | 0x81, 0x02, /* Input (Variable), */ | ||
| 516 | 0xB4, /* Pop, */ | ||
| 517 | 0x09, 0x30, /* Usage (Tip Pressure), */ | ||
| 518 | 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ | ||
| 519 | 0x81, 0x02, /* Input (Variable), */ | ||
| 520 | 0xC0, /* End Collection, */ | ||
| 521 | 0xC0 /* End Collection */ | ||
| 522 | }; | ||
| 523 | |||
| 524 | /* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ | ||
| 525 | static __u8 twha60_rdesc_fixed1[] = { | ||
| 526 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
| 527 | 0x09, 0x06, /* Usage (Keyboard), */ | ||
| 528 | 0xA1, 0x01, /* Collection (Application), */ | ||
| 529 | 0x85, 0x05, /* Report ID (5), */ | ||
| 530 | 0x05, 0x07, /* Usage Page (Keyboard), */ | ||
| 531 | 0x14, /* Logical Minimum (0), */ | ||
| 532 | 0x25, 0x01, /* Logical Maximum (1), */ | ||
| 533 | 0x75, 0x01, /* Report Size (1), */ | ||
| 534 | 0x95, 0x08, /* Report Count (8), */ | ||
| 535 | 0x81, 0x01, /* Input (Constant), */ | ||
| 536 | 0x95, 0x0C, /* Report Count (12), */ | ||
| 537 | 0x19, 0x3A, /* Usage Minimum (KB F1), */ | ||
| 538 | 0x29, 0x45, /* Usage Maximum (KB F12), */ | ||
| 539 | 0x81, 0x02, /* Input (Variable), */ | ||
| 540 | 0x95, 0x0C, /* Report Count (12), */ | ||
| 541 | 0x19, 0x68, /* Usage Minimum (KB F13), */ | ||
| 542 | 0x29, 0x73, /* Usage Maximum (KB F24), */ | ||
| 543 | 0x81, 0x02, /* Input (Variable), */ | ||
| 544 | 0x95, 0x08, /* Report Count (8), */ | ||
| 545 | 0x81, 0x01, /* Input (Constant), */ | ||
| 546 | 0xC0 /* End Collection */ | ||
| 547 | }; | ||
| 548 | |||
| 469 | static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 549 | static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
| 470 | unsigned int *rsize) | 550 | unsigned int *rsize) |
| 471 | { | 551 | { |
| @@ -525,6 +605,22 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 525 | break; | 605 | break; |
| 526 | } | 606 | } |
| 527 | break; | 607 | break; |
| 608 | case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: | ||
| 609 | switch (iface_num) { | ||
| 610 | case 0: | ||
| 611 | if (*rsize == TWHA60_RDESC_ORIG_SIZE0) { | ||
| 612 | rdesc = twha60_rdesc_fixed0; | ||
| 613 | *rsize = sizeof(twha60_rdesc_fixed0); | ||
| 614 | } | ||
| 615 | break; | ||
| 616 | case 1: | ||
| 617 | if (*rsize == TWHA60_RDESC_ORIG_SIZE1) { | ||
| 618 | rdesc = twha60_rdesc_fixed1; | ||
| 619 | *rsize = sizeof(twha60_rdesc_fixed1); | ||
| 620 | } | ||
| 621 | break; | ||
| 622 | } | ||
| 623 | break; | ||
| 528 | } | 624 | } |
| 529 | 625 | ||
| 530 | return rdesc; | 626 | return rdesc; |
| @@ -543,6 +639,8 @@ static const struct hid_device_id uclogic_devices[] = { | |||
| 543 | USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, | 639 | USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, |
| 544 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | 640 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, |
| 545 | USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, | 641 | USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, |
| 642 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
| 643 | USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, | ||
| 546 | { } | 644 | { } |
| 547 | }; | 645 | }; |
| 548 | MODULE_DEVICE_TABLE(hid, uclogic_devices); | 646 | MODULE_DEVICE_TABLE(hid, uclogic_devices); |
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index fe23a1eb586b..2f60da9ed066 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> | 5 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
| 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
| 7 | * Copyright (c) 2006-2007 Jiri Kosina | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
| 8 | * Copyright (c) 2007 Paul Walmsley | ||
| 9 | * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> | 8 | * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> |
| 10 | * Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru> | 9 | * Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru> |
| 11 | * Copyright (c) 2009 Bastien Nocera <hadess@hadess.net> | 10 | * Copyright (c) 2009 Bastien Nocera <hadess@hadess.net> |
| @@ -33,6 +32,8 @@ | |||
| 33 | #define PAD_DEVICE_ID 0x0F | 32 | #define PAD_DEVICE_ID 0x0F |
| 34 | 33 | ||
| 35 | #define WAC_CMD_LED_CONTROL 0x20 | 34 | #define WAC_CMD_LED_CONTROL 0x20 |
| 35 | #define WAC_CMD_ICON_START_STOP 0x21 | ||
| 36 | #define WAC_CMD_ICON_TRANSFER 0x26 | ||
| 36 | 37 | ||
| 37 | struct wacom_data { | 38 | struct wacom_data { |
| 38 | __u16 tool; | 39 | __u16 tool; |
| @@ -69,6 +70,91 @@ static enum power_supply_property wacom_ac_props[] = { | |||
| 69 | POWER_SUPPLY_PROP_SCOPE, | 70 | POWER_SUPPLY_PROP_SCOPE, |
| 70 | }; | 71 | }; |
| 71 | 72 | ||
| 73 | static void wacom_scramble(__u8 *image) | ||
| 74 | { | ||
| 75 | __u16 mask; | ||
| 76 | __u16 s1; | ||
| 77 | __u16 s2; | ||
| 78 | __u16 r1 ; | ||
| 79 | __u16 r2 ; | ||
| 80 | __u16 r; | ||
| 81 | __u8 buf[256]; | ||
| 82 | int i, w, x, y, z; | ||
| 83 | |||
| 84 | for (x = 0; x < 32; x++) { | ||
| 85 | for (y = 0; y < 8; y++) | ||
| 86 | buf[(8 * x) + (7 - y)] = image[(8 * x) + y]; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Change 76543210 into GECA6420 as required by Intuos4 WL | ||
| 90 | * HGFEDCBA HFDB7531 | ||
| 91 | */ | ||
| 92 | for (x = 0; x < 4; x++) { | ||
| 93 | for (y = 0; y < 4; y++) { | ||
| 94 | for (z = 0; z < 8; z++) { | ||
| 95 | mask = 0x0001; | ||
| 96 | r1 = 0; | ||
| 97 | r2 = 0; | ||
| 98 | i = (x << 6) + (y << 4) + z; | ||
| 99 | s1 = buf[i]; | ||
| 100 | s2 = buf[i+8]; | ||
| 101 | for (w = 0; w < 8; w++) { | ||
| 102 | r1 |= (s1 & mask); | ||
| 103 | r2 |= (s2 & mask); | ||
| 104 | s1 <<= 1; | ||
| 105 | s2 <<= 1; | ||
| 106 | mask <<= 2; | ||
| 107 | } | ||
| 108 | r = r1 | (r2 << 1); | ||
| 109 | i = (x << 6) + (y << 4) + (z << 1); | ||
| 110 | image[i] = 0xFF & r; | ||
| 111 | image[i+1] = (0xFF00 & r) >> 8; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | static void wacom_set_image(struct hid_device *hdev, const char *image, | ||
| 118 | __u8 icon_no) | ||
| 119 | { | ||
| 120 | __u8 rep_data[68]; | ||
| 121 | __u8 p[256]; | ||
| 122 | int ret, i, j; | ||
| 123 | |||
| 124 | for (i = 0; i < 256; i++) | ||
| 125 | p[i] = image[i]; | ||
| 126 | |||
| 127 | rep_data[0] = WAC_CMD_ICON_START_STOP; | ||
| 128 | rep_data[1] = 0; | ||
| 129 | ret = hdev->hid_output_raw_report(hdev, rep_data, 2, | ||
| 130 | HID_FEATURE_REPORT); | ||
| 131 | if (ret < 0) | ||
| 132 | goto err; | ||
| 133 | |||
| 134 | rep_data[0] = WAC_CMD_ICON_TRANSFER; | ||
| 135 | rep_data[1] = icon_no & 0x07; | ||
| 136 | |||
| 137 | wacom_scramble(p); | ||
| 138 | |||
| 139 | for (i = 0; i < 4; i++) { | ||
| 140 | for (j = 0; j < 64; j++) | ||
| 141 | rep_data[j + 3] = p[(i << 6) + j]; | ||
| 142 | |||
| 143 | rep_data[2] = i; | ||
| 144 | ret = hdev->hid_output_raw_report(hdev, rep_data, 67, | ||
| 145 | HID_FEATURE_REPORT); | ||
| 146 | } | ||
| 147 | |||
| 148 | rep_data[0] = WAC_CMD_ICON_START_STOP; | ||
| 149 | rep_data[1] = 0; | ||
| 150 | |||
| 151 | ret = hdev->hid_output_raw_report(hdev, rep_data, 2, | ||
| 152 | HID_FEATURE_REPORT); | ||
| 153 | |||
| 154 | err: | ||
| 155 | return; | ||
| 156 | } | ||
| 157 | |||
| 72 | static void wacom_leds_set_brightness(struct led_classdev *led_dev, | 158 | static void wacom_leds_set_brightness(struct led_classdev *led_dev, |
| 73 | enum led_brightness value) | 159 | enum led_brightness value) |
| 74 | { | 160 | { |
| @@ -91,7 +177,10 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev, | |||
| 91 | if (buf) { | 177 | if (buf) { |
| 92 | buf[0] = WAC_CMD_LED_CONTROL; | 178 | buf[0] = WAC_CMD_LED_CONTROL; |
| 93 | buf[1] = led; | 179 | buf[1] = led; |
| 94 | buf[2] = value; | 180 | buf[2] = value >> 2; |
| 181 | buf[3] = value; | ||
| 182 | /* use fixed brightness for OLEDs */ | ||
| 183 | buf[4] = 0x08; | ||
| 95 | hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); | 184 | hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); |
| 96 | kfree(buf); | 185 | kfree(buf); |
| 97 | } | 186 | } |
| @@ -317,6 +406,34 @@ static ssize_t wacom_store_speed(struct device *dev, | |||
| 317 | static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, | 406 | static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, |
| 318 | wacom_show_speed, wacom_store_speed); | 407 | wacom_show_speed, wacom_store_speed); |
| 319 | 408 | ||
| 409 | #define WACOM_STORE(OLED_ID) \ | ||
| 410 | static ssize_t wacom_oled##OLED_ID##_store(struct device *dev, \ | ||
| 411 | struct device_attribute *attr, \ | ||
| 412 | const char *buf, size_t count) \ | ||
| 413 | { \ | ||
| 414 | struct hid_device *hdev = container_of(dev, struct hid_device, \ | ||
| 415 | dev); \ | ||
| 416 | \ | ||
| 417 | if (count != 256) \ | ||
| 418 | return -EINVAL; \ | ||
| 419 | \ | ||
| 420 | wacom_set_image(hdev, buf, OLED_ID); \ | ||
| 421 | \ | ||
| 422 | return count; \ | ||
| 423 | } \ | ||
| 424 | \ | ||
| 425 | static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL, \ | ||
| 426 | wacom_oled##OLED_ID##_store) | ||
| 427 | |||
| 428 | WACOM_STORE(0); | ||
| 429 | WACOM_STORE(1); | ||
| 430 | WACOM_STORE(2); | ||
| 431 | WACOM_STORE(3); | ||
| 432 | WACOM_STORE(4); | ||
| 433 | WACOM_STORE(5); | ||
| 434 | WACOM_STORE(6); | ||
| 435 | WACOM_STORE(7); | ||
| 436 | |||
| 320 | static int wacom_gr_parse_report(struct hid_device *hdev, | 437 | static int wacom_gr_parse_report(struct hid_device *hdev, |
| 321 | struct wacom_data *wdata, | 438 | struct wacom_data *wdata, |
| 322 | struct input_dev *input, unsigned char *data) | 439 | struct input_dev *input, unsigned char *data) |
| @@ -717,17 +834,33 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 717 | hid_warn(hdev, | 834 | hid_warn(hdev, |
| 718 | "can't create sysfs speed attribute err: %d\n", ret); | 835 | "can't create sysfs speed attribute err: %d\n", ret); |
| 719 | 836 | ||
| 837 | #define OLED_INIT(OLED_ID) \ | ||
| 838 | do { \ | ||
| 839 | ret = device_create_file(&hdev->dev, \ | ||
| 840 | &dev_attr_oled##OLED_ID##_img); \ | ||
| 841 | if (ret) \ | ||
| 842 | hid_warn(hdev, \ | ||
| 843 | "can't create sysfs oled attribute, err: %d\n", ret);\ | ||
| 844 | } while (0) | ||
| 845 | |||
| 846 | OLED_INIT(0); | ||
| 847 | OLED_INIT(1); | ||
| 848 | OLED_INIT(2); | ||
| 849 | OLED_INIT(3); | ||
| 850 | OLED_INIT(4); | ||
| 851 | OLED_INIT(5); | ||
| 852 | OLED_INIT(6); | ||
| 853 | OLED_INIT(7); | ||
| 854 | |||
| 720 | wdata->features = 0; | 855 | wdata->features = 0; |
| 721 | wacom_set_features(hdev, 1); | 856 | wacom_set_features(hdev, 1); |
| 722 | 857 | ||
| 723 | if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) { | 858 | if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) { |
| 724 | sprintf(hdev->name, "%s", "Wacom Intuos4 WL"); | 859 | sprintf(hdev->name, "%s", "Wacom Intuos4 WL"); |
| 725 | ret = wacom_initialize_leds(hdev); | 860 | ret = wacom_initialize_leds(hdev); |
| 726 | if (ret) { | 861 | if (ret) |
| 727 | hid_warn(hdev, | 862 | hid_warn(hdev, |
| 728 | "can't create led attribute, err: %d\n", ret); | 863 | "can't create led attribute, err: %d\n", ret); |
| 729 | goto destroy_leds; | ||
| 730 | } | ||
| 731 | } | 864 | } |
| 732 | 865 | ||
| 733 | wdata->battery.properties = wacom_battery_props; | 866 | wdata->battery.properties = wacom_battery_props; |
| @@ -740,8 +873,8 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 740 | 873 | ||
| 741 | ret = power_supply_register(&hdev->dev, &wdata->battery); | 874 | ret = power_supply_register(&hdev->dev, &wdata->battery); |
| 742 | if (ret) { | 875 | if (ret) { |
| 743 | hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n", | 876 | hid_err(hdev, "can't create sysfs battery attribute, err: %d\n", |
| 744 | ret); | 877 | ret); |
| 745 | goto err_battery; | 878 | goto err_battery; |
| 746 | } | 879 | } |
| 747 | 880 | ||
| @@ -756,8 +889,8 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 756 | 889 | ||
| 757 | ret = power_supply_register(&hdev->dev, &wdata->ac); | 890 | ret = power_supply_register(&hdev->dev, &wdata->ac); |
| 758 | if (ret) { | 891 | if (ret) { |
| 759 | hid_warn(hdev, | 892 | hid_err(hdev, |
| 760 | "can't create ac battery attribute, err: %d\n", ret); | 893 | "can't create ac battery attribute, err: %d\n", ret); |
| 761 | goto err_ac; | 894 | goto err_ac; |
| 762 | } | 895 | } |
| 763 | 896 | ||
| @@ -767,10 +900,17 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 767 | err_ac: | 900 | err_ac: |
| 768 | power_supply_unregister(&wdata->battery); | 901 | power_supply_unregister(&wdata->battery); |
| 769 | err_battery: | 902 | err_battery: |
| 903 | wacom_destroy_leds(hdev); | ||
| 904 | device_remove_file(&hdev->dev, &dev_attr_oled0_img); | ||
| 905 | device_remove_file(&hdev->dev, &dev_attr_oled1_img); | ||
| 906 | device_remove_file(&hdev->dev, &dev_attr_oled2_img); | ||
| 907 | device_remove_file(&hdev->dev, &dev_attr_oled3_img); | ||
| 908 | device_remove_file(&hdev->dev, &dev_attr_oled4_img); | ||
| 909 | device_remove_file(&hdev->dev, &dev_attr_oled5_img); | ||
| 910 | device_remove_file(&hdev->dev, &dev_attr_oled6_img); | ||
| 911 | device_remove_file(&hdev->dev, &dev_attr_oled7_img); | ||
| 770 | device_remove_file(&hdev->dev, &dev_attr_speed); | 912 | device_remove_file(&hdev->dev, &dev_attr_speed); |
| 771 | hid_hw_stop(hdev); | 913 | hid_hw_stop(hdev); |
| 772 | destroy_leds: | ||
| 773 | wacom_destroy_leds(hdev); | ||
| 774 | err_free: | 914 | err_free: |
| 775 | kfree(wdata); | 915 | kfree(wdata); |
| 776 | return ret; | 916 | return ret; |
| @@ -781,6 +921,14 @@ static void wacom_remove(struct hid_device *hdev) | |||
| 781 | struct wacom_data *wdata = hid_get_drvdata(hdev); | 921 | struct wacom_data *wdata = hid_get_drvdata(hdev); |
| 782 | 922 | ||
| 783 | wacom_destroy_leds(hdev); | 923 | wacom_destroy_leds(hdev); |
| 924 | device_remove_file(&hdev->dev, &dev_attr_oled0_img); | ||
| 925 | device_remove_file(&hdev->dev, &dev_attr_oled1_img); | ||
| 926 | device_remove_file(&hdev->dev, &dev_attr_oled2_img); | ||
| 927 | device_remove_file(&hdev->dev, &dev_attr_oled3_img); | ||
| 928 | device_remove_file(&hdev->dev, &dev_attr_oled4_img); | ||
| 929 | device_remove_file(&hdev->dev, &dev_attr_oled5_img); | ||
| 930 | device_remove_file(&hdev->dev, &dev_attr_oled6_img); | ||
| 931 | device_remove_file(&hdev->dev, &dev_attr_oled7_img); | ||
| 784 | device_remove_file(&hdev->dev, &dev_attr_speed); | 932 | device_remove_file(&hdev->dev, &dev_attr_speed); |
| 785 | hid_hw_stop(hdev); | 933 | hid_hw_stop(hdev); |
| 786 | 934 | ||
diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c index 745e4e9a8cf2..bb536ab5941e 100644 --- a/drivers/hid/hid-waltop.c +++ b/drivers/hid/hid-waltop.c | |||
| @@ -638,28 +638,6 @@ static __u8 sirius_battery_free_tablet_rdesc_fixed[] = { | |||
| 638 | 0xC0 /* End Collection */ | 638 | 0xC0 /* End Collection */ |
| 639 | }; | 639 | }; |
| 640 | 640 | ||
| 641 | static int waltop_probe(struct hid_device *hdev, | ||
| 642 | const struct hid_device_id *id) | ||
| 643 | { | ||
| 644 | int ret; | ||
| 645 | |||
| 646 | ret = hid_parse(hdev); | ||
| 647 | if (ret) { | ||
| 648 | hid_err(hdev, "parse failed\n"); | ||
| 649 | goto err; | ||
| 650 | } | ||
| 651 | |||
| 652 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
| 653 | if (ret) { | ||
| 654 | hid_err(hdev, "hw start failed\n"); | ||
| 655 | goto err; | ||
| 656 | } | ||
| 657 | |||
| 658 | return 0; | ||
| 659 | err: | ||
| 660 | return ret; | ||
| 661 | } | ||
| 662 | |||
| 663 | static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 641 | static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
| 664 | unsigned int *rsize) | 642 | unsigned int *rsize) |
| 665 | { | 643 | { |
| @@ -776,11 +754,6 @@ static int waltop_raw_event(struct hid_device *hdev, struct hid_report *report, | |||
| 776 | return 0; | 754 | return 0; |
| 777 | } | 755 | } |
| 778 | 756 | ||
| 779 | static void waltop_remove(struct hid_device *hdev) | ||
| 780 | { | ||
| 781 | hid_hw_stop(hdev); | ||
| 782 | } | ||
| 783 | |||
| 784 | static const struct hid_device_id waltop_devices[] = { | 757 | static const struct hid_device_id waltop_devices[] = { |
| 785 | { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, | 758 | { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, |
| 786 | USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, | 759 | USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, |
| @@ -803,10 +776,8 @@ MODULE_DEVICE_TABLE(hid, waltop_devices); | |||
| 803 | static struct hid_driver waltop_driver = { | 776 | static struct hid_driver waltop_driver = { |
| 804 | .name = "waltop", | 777 | .name = "waltop", |
| 805 | .id_table = waltop_devices, | 778 | .id_table = waltop_devices, |
| 806 | .probe = waltop_probe, | ||
| 807 | .report_fixup = waltop_report_fixup, | 779 | .report_fixup = waltop_report_fixup, |
| 808 | .raw_event = waltop_raw_event, | 780 | .raw_event = waltop_raw_event, |
| 809 | .remove = waltop_remove, | ||
| 810 | }; | 781 | }; |
| 811 | 782 | ||
| 812 | static int __init waltop_init(void) | 783 | static int __init waltop_init(void) |
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index 0a1805c9b0e5..bc85bf29062e 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c | |||
| @@ -28,12 +28,14 @@ struct wiimote_ext { | |||
| 28 | bool mp_plugged; | 28 | bool mp_plugged; |
| 29 | bool motionp; | 29 | bool motionp; |
| 30 | __u8 ext_type; | 30 | __u8 ext_type; |
| 31 | __u16 calib[4][3]; | ||
| 31 | }; | 32 | }; |
| 32 | 33 | ||
| 33 | enum wiiext_type { | 34 | enum wiiext_type { |
| 34 | WIIEXT_NONE, /* placeholder */ | 35 | WIIEXT_NONE, /* placeholder */ |
| 35 | WIIEXT_CLASSIC, /* Nintendo classic controller */ | 36 | WIIEXT_CLASSIC, /* Nintendo classic controller */ |
| 36 | WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */ | 37 | WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */ |
| 38 | WIIEXT_BALANCE_BOARD, /* Nintendo balance board controller */ | ||
| 37 | }; | 39 | }; |
| 38 | 40 | ||
| 39 | enum wiiext_keys { | 41 | enum wiiext_keys { |
| @@ -126,6 +128,7 @@ error: | |||
| 126 | static __u8 ext_read(struct wiimote_ext *ext) | 128 | static __u8 ext_read(struct wiimote_ext *ext) |
| 127 | { | 129 | { |
| 128 | ssize_t ret; | 130 | ssize_t ret; |
| 131 | __u8 buf[24], i, j, offs = 0; | ||
| 129 | __u8 rmem[2], wmem; | 132 | __u8 rmem[2], wmem; |
| 130 | __u8 type = WIIEXT_NONE; | 133 | __u8 type = WIIEXT_NONE; |
| 131 | 134 | ||
| @@ -151,6 +154,28 @@ static __u8 ext_read(struct wiimote_ext *ext) | |||
| 151 | type = WIIEXT_NUNCHUCK; | 154 | type = WIIEXT_NUNCHUCK; |
| 152 | else if (rmem[0] == 0x01 && rmem[1] == 0x01) | 155 | else if (rmem[0] == 0x01 && rmem[1] == 0x01) |
| 153 | type = WIIEXT_CLASSIC; | 156 | type = WIIEXT_CLASSIC; |
| 157 | else if (rmem[0] == 0x04 && rmem[1] == 0x02) | ||
| 158 | type = WIIEXT_BALANCE_BOARD; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* get balance board calibration data */ | ||
| 162 | if (type == WIIEXT_BALANCE_BOARD) { | ||
| 163 | ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12); | ||
| 164 | ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12, | ||
| 165 | buf + 12, 12); | ||
| 166 | |||
| 167 | if (ret != 24) { | ||
| 168 | type = WIIEXT_NONE; | ||
| 169 | } else { | ||
| 170 | for (i = 0; i < 3; i++) { | ||
| 171 | for (j = 0; j < 4; j++) { | ||
| 172 | ext->calib[j][i] = buf[offs]; | ||
| 173 | ext->calib[j][i] <<= 8; | ||
| 174 | ext->calib[j][i] |= buf[offs + 1]; | ||
| 175 | offs += 2; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 154 | } | 179 | } |
| 155 | 180 | ||
| 156 | wiimote_cmd_release(ext->wdata); | 181 | wiimote_cmd_release(ext->wdata); |
| @@ -509,6 +534,71 @@ static void handler_classic(struct wiimote_ext *ext, const __u8 *payload) | |||
| 509 | input_sync(ext->input); | 534 | input_sync(ext->input); |
| 510 | } | 535 | } |
| 511 | 536 | ||
| 537 | static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload) | ||
| 538 | { | ||
| 539 | __s32 val[4], tmp; | ||
| 540 | unsigned int i; | ||
| 541 | |||
| 542 | /* Byte | 8 7 6 5 4 3 2 1 | | ||
| 543 | * -----+--------------------------+ | ||
| 544 | * 1 | Top Right <15:8> | | ||
| 545 | * 2 | Top Right <7:0> | | ||
| 546 | * -----+--------------------------+ | ||
| 547 | * 3 | Bottom Right <15:8> | | ||
| 548 | * 4 | Bottom Right <7:0> | | ||
| 549 | * -----+--------------------------+ | ||
| 550 | * 5 | Top Left <15:8> | | ||
| 551 | * 6 | Top Left <7:0> | | ||
| 552 | * -----+--------------------------+ | ||
| 553 | * 7 | Bottom Left <15:8> | | ||
| 554 | * 8 | Bottom Left <7:0> | | ||
| 555 | * -----+--------------------------+ | ||
| 556 | * | ||
| 557 | * These values represent the weight-measurements of the Wii-balance | ||
| 558 | * board with 16bit precision. | ||
| 559 | * | ||
| 560 | * The balance-board is never reported interleaved with motionp. | ||
| 561 | */ | ||
| 562 | |||
| 563 | val[0] = payload[0]; | ||
| 564 | val[0] <<= 8; | ||
| 565 | val[0] |= payload[1]; | ||
| 566 | |||
| 567 | val[1] = payload[2]; | ||
| 568 | val[1] <<= 8; | ||
| 569 | val[1] |= payload[3]; | ||
| 570 | |||
| 571 | val[2] = payload[4]; | ||
| 572 | val[2] <<= 8; | ||
| 573 | val[2] |= payload[5]; | ||
| 574 | |||
| 575 | val[3] = payload[6]; | ||
| 576 | val[3] <<= 8; | ||
| 577 | val[3] |= payload[7]; | ||
| 578 | |||
| 579 | /* apply calibration data */ | ||
| 580 | for (i = 0; i < 4; i++) { | ||
| 581 | if (val[i] < ext->calib[i][1]) { | ||
| 582 | tmp = val[i] - ext->calib[i][0]; | ||
| 583 | tmp *= 1700; | ||
| 584 | tmp /= ext->calib[i][1] - ext->calib[i][0]; | ||
| 585 | } else { | ||
| 586 | tmp = val[i] - ext->calib[i][1]; | ||
| 587 | tmp *= 1700; | ||
| 588 | tmp /= ext->calib[i][2] - ext->calib[i][1]; | ||
| 589 | tmp += 1700; | ||
| 590 | } | ||
| 591 | val[i] = tmp; | ||
| 592 | } | ||
| 593 | |||
| 594 | input_report_abs(ext->input, ABS_HAT0X, val[0]); | ||
| 595 | input_report_abs(ext->input, ABS_HAT0Y, val[1]); | ||
| 596 | input_report_abs(ext->input, ABS_HAT1X, val[2]); | ||
| 597 | input_report_abs(ext->input, ABS_HAT1Y, val[3]); | ||
| 598 | |||
| 599 | input_sync(ext->input); | ||
| 600 | } | ||
| 601 | |||
| 512 | /* call this with state.lock spinlock held */ | 602 | /* call this with state.lock spinlock held */ |
| 513 | void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload) | 603 | void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload) |
| 514 | { | 604 | { |
| @@ -523,6 +613,8 @@ void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload) | |||
| 523 | handler_nunchuck(ext, payload); | 613 | handler_nunchuck(ext, payload); |
| 524 | } else if (ext->ext_type == WIIEXT_CLASSIC) { | 614 | } else if (ext->ext_type == WIIEXT_CLASSIC) { |
| 525 | handler_classic(ext, payload); | 615 | handler_classic(ext, payload); |
| 616 | } else if (ext->ext_type == WIIEXT_BALANCE_BOARD) { | ||
| 617 | handler_balance_board(ext, payload); | ||
| 526 | } | 618 | } |
| 527 | } | 619 | } |
| 528 | 620 | ||
| @@ -551,6 +643,11 @@ static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr, | |||
| 551 | return sprintf(buf, "motionp+classic\n"); | 643 | return sprintf(buf, "motionp+classic\n"); |
| 552 | else | 644 | else |
| 553 | return sprintf(buf, "classic\n"); | 645 | return sprintf(buf, "classic\n"); |
| 646 | } else if (type == WIIEXT_BALANCE_BOARD) { | ||
| 647 | if (motionp) | ||
| 648 | return sprintf(buf, "motionp+balanceboard\n"); | ||
| 649 | else | ||
| 650 | return sprintf(buf, "balanceboard\n"); | ||
| 554 | } else { | 651 | } else { |
| 555 | if (motionp) | 652 | if (motionp) |
| 556 | return sprintf(buf, "motionp\n"); | 653 | return sprintf(buf, "motionp\n"); |
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 3b6f7bf5a77e..17d15bb610d1 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c | |||
| @@ -42,6 +42,7 @@ static struct cdev hidraw_cdev; | |||
| 42 | static struct class *hidraw_class; | 42 | static struct class *hidraw_class; |
| 43 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; | 43 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; |
| 44 | static DEFINE_MUTEX(minors_lock); | 44 | static DEFINE_MUTEX(minors_lock); |
| 45 | static void drop_ref(struct hidraw *hid, int exists_bit); | ||
| 45 | 46 | ||
| 46 | static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) | 47 | static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) |
| 47 | { | 48 | { |
| @@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, | |||
| 113 | __u8 *buf; | 114 | __u8 *buf; |
| 114 | int ret = 0; | 115 | int ret = 0; |
| 115 | 116 | ||
| 116 | if (!hidraw_table[minor]) { | 117 | if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { |
| 117 | ret = -ENODEV; | 118 | ret = -ENODEV; |
| 118 | goto out; | 119 | goto out; |
| 119 | } | 120 | } |
| @@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file) | |||
| 261 | } | 262 | } |
| 262 | 263 | ||
| 263 | mutex_lock(&minors_lock); | 264 | mutex_lock(&minors_lock); |
| 264 | if (!hidraw_table[minor]) { | 265 | if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { |
| 265 | err = -ENODEV; | 266 | err = -ENODEV; |
| 266 | goto out_unlock; | 267 | goto out_unlock; |
| 267 | } | 268 | } |
| @@ -298,36 +299,12 @@ out: | |||
| 298 | static int hidraw_release(struct inode * inode, struct file * file) | 299 | static int hidraw_release(struct inode * inode, struct file * file) |
| 299 | { | 300 | { |
| 300 | unsigned int minor = iminor(inode); | 301 | unsigned int minor = iminor(inode); |
| 301 | struct hidraw *dev; | ||
| 302 | struct hidraw_list *list = file->private_data; | 302 | struct hidraw_list *list = file->private_data; |
| 303 | int ret; | ||
| 304 | int i; | ||
| 305 | |||
| 306 | mutex_lock(&minors_lock); | ||
| 307 | if (!hidraw_table[minor]) { | ||
| 308 | ret = -ENODEV; | ||
| 309 | goto unlock; | ||
| 310 | } | ||
| 311 | 303 | ||
| 304 | drop_ref(hidraw_table[minor], 0); | ||
| 312 | list_del(&list->node); | 305 | list_del(&list->node); |
| 313 | dev = hidraw_table[minor]; | ||
| 314 | if (!--dev->open) { | ||
| 315 | if (list->hidraw->exist) { | ||
| 316 | hid_hw_power(dev->hid, PM_HINT_NORMAL); | ||
| 317 | hid_hw_close(dev->hid); | ||
| 318 | } else { | ||
| 319 | kfree(list->hidraw); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) | ||
| 324 | kfree(list->buffer[i].value); | ||
| 325 | kfree(list); | 306 | kfree(list); |
| 326 | ret = 0; | 307 | return 0; |
| 327 | unlock: | ||
| 328 | mutex_unlock(&minors_lock); | ||
| 329 | |||
| 330 | return ret; | ||
| 331 | } | 308 | } |
| 332 | 309 | ||
| 333 | static long hidraw_ioctl(struct file *file, unsigned int cmd, | 310 | static long hidraw_ioctl(struct file *file, unsigned int cmd, |
| @@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect); | |||
| 529 | void hidraw_disconnect(struct hid_device *hid) | 506 | void hidraw_disconnect(struct hid_device *hid) |
| 530 | { | 507 | { |
| 531 | struct hidraw *hidraw = hid->hidraw; | 508 | struct hidraw *hidraw = hid->hidraw; |
| 532 | 509 | drop_ref(hidraw, 1); | |
| 533 | mutex_lock(&minors_lock); | ||
| 534 | hidraw->exist = 0; | ||
| 535 | |||
| 536 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||
| 537 | |||
| 538 | hidraw_table[hidraw->minor] = NULL; | ||
| 539 | |||
| 540 | if (hidraw->open) { | ||
| 541 | hid_hw_close(hid); | ||
| 542 | wake_up_interruptible(&hidraw->wait); | ||
| 543 | } else { | ||
| 544 | kfree(hidraw); | ||
| 545 | } | ||
| 546 | mutex_unlock(&minors_lock); | ||
| 547 | } | 510 | } |
| 548 | EXPORT_SYMBOL_GPL(hidraw_disconnect); | 511 | EXPORT_SYMBOL_GPL(hidraw_disconnect); |
| 549 | 512 | ||
| @@ -559,21 +522,28 @@ int __init hidraw_init(void) | |||
| 559 | 522 | ||
| 560 | if (result < 0) { | 523 | if (result < 0) { |
| 561 | pr_warn("can't get major number\n"); | 524 | pr_warn("can't get major number\n"); |
| 562 | result = 0; | ||
| 563 | goto out; | 525 | goto out; |
| 564 | } | 526 | } |
| 565 | 527 | ||
| 566 | hidraw_class = class_create(THIS_MODULE, "hidraw"); | 528 | hidraw_class = class_create(THIS_MODULE, "hidraw"); |
| 567 | if (IS_ERR(hidraw_class)) { | 529 | if (IS_ERR(hidraw_class)) { |
| 568 | result = PTR_ERR(hidraw_class); | 530 | result = PTR_ERR(hidraw_class); |
| 569 | unregister_chrdev(hidraw_major, "hidraw"); | 531 | goto error_cdev; |
| 570 | goto out; | ||
| 571 | } | 532 | } |
| 572 | 533 | ||
| 573 | cdev_init(&hidraw_cdev, &hidraw_ops); | 534 | cdev_init(&hidraw_cdev, &hidraw_ops); |
| 574 | cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); | 535 | result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); |
| 536 | if (result < 0) | ||
| 537 | goto error_class; | ||
| 538 | |||
| 575 | out: | 539 | out: |
| 576 | return result; | 540 | return result; |
| 541 | |||
| 542 | error_class: | ||
| 543 | class_destroy(hidraw_class); | ||
| 544 | error_cdev: | ||
| 545 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); | ||
| 546 | goto out; | ||
| 577 | } | 547 | } |
| 578 | 548 | ||
| 579 | void hidraw_exit(void) | 549 | void hidraw_exit(void) |
| @@ -585,3 +555,23 @@ void hidraw_exit(void) | |||
| 585 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); | 555 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); |
| 586 | 556 | ||
| 587 | } | 557 | } |
| 558 | |||
| 559 | static void drop_ref(struct hidraw *hidraw, int exists_bit) | ||
| 560 | { | ||
| 561 | mutex_lock(&minors_lock); | ||
| 562 | if (exists_bit) { | ||
| 563 | hid_hw_close(hidraw->hid); | ||
| 564 | hidraw->exist = 0; | ||
| 565 | if (hidraw->open) | ||
| 566 | wake_up_interruptible(&hidraw->wait); | ||
| 567 | } else { | ||
| 568 | --hidraw->open; | ||
| 569 | } | ||
| 570 | |||
| 571 | if (!hidraw->open && !hidraw->exist) { | ||
| 572 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||
| 573 | hidraw_table[hidraw->minor] = NULL; | ||
| 574 | kfree(hidraw); | ||
| 575 | } | ||
| 576 | mutex_unlock(&minors_lock); | ||
| 577 | } | ||
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index dedd8e4e5c6d..8e0c4bf94ebc 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
| @@ -1415,20 +1415,20 @@ static int hid_post_reset(struct usb_interface *intf) | |||
| 1415 | * configuration descriptors passed, we already know that | 1415 | * configuration descriptors passed, we already know that |
| 1416 | * the size of the HID report descriptor has not changed. | 1416 | * the size of the HID report descriptor has not changed. |
| 1417 | */ | 1417 | */ |
| 1418 | rdesc = kmalloc(hid->rsize, GFP_KERNEL); | 1418 | rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL); |
| 1419 | if (!rdesc) { | 1419 | if (!rdesc) { |
| 1420 | dbg_hid("couldn't allocate rdesc memory (post_reset)\n"); | 1420 | dbg_hid("couldn't allocate rdesc memory (post_reset)\n"); |
| 1421 | return 1; | 1421 | return 1; |
| 1422 | } | 1422 | } |
| 1423 | status = hid_get_class_descriptor(dev, | 1423 | status = hid_get_class_descriptor(dev, |
| 1424 | interface->desc.bInterfaceNumber, | 1424 | interface->desc.bInterfaceNumber, |
| 1425 | HID_DT_REPORT, rdesc, hid->rsize); | 1425 | HID_DT_REPORT, rdesc, hid->dev_rsize); |
| 1426 | if (status < 0) { | 1426 | if (status < 0) { |
| 1427 | dbg_hid("reading report descriptor failed (post_reset)\n"); | 1427 | dbg_hid("reading report descriptor failed (post_reset)\n"); |
| 1428 | kfree(rdesc); | 1428 | kfree(rdesc); |
| 1429 | return 1; | 1429 | return 1; |
| 1430 | } | 1430 | } |
| 1431 | status = memcmp(rdesc, hid->rdesc, hid->rsize); | 1431 | status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize); |
| 1432 | kfree(rdesc); | 1432 | kfree(rdesc); |
| 1433 | if (status != 0) { | 1433 | if (status != 0) { |
| 1434 | dbg_hid("report descriptor changed\n"); | 1434 | dbg_hid("report descriptor changed\n"); |
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 991e85c7325c..11c7932dc7e6 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c | |||
| @@ -70,12 +70,13 @@ static const struct hid_blacklist { | |||
| 70 | { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, | 70 | { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, |
| 71 | { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, | 71 | { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, |
| 72 | { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, | 72 | { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, |
| 73 | { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, | ||
| 73 | { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, | 74 | { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, |
| 74 | { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, | 75 | { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, |
| 75 | { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, | 76 | { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, |
| 76 | { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, | 77 | { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, |
| 77 | { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, | 78 | { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, |
| 78 | { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET }, | 79 | { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET }, |
| 79 | { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET }, | 80 | { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET }, |
| 80 | { USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET }, | 81 | { USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET }, |
| 81 | { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, | 82 | { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, |
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 6c58bfff01a3..118d0300f1fb 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c | |||
| @@ -54,16 +54,9 @@ struct evdev_client { | |||
| 54 | static struct evdev *evdev_table[EVDEV_MINORS]; | 54 | static struct evdev *evdev_table[EVDEV_MINORS]; |
| 55 | static DEFINE_MUTEX(evdev_table_mutex); | 55 | static DEFINE_MUTEX(evdev_table_mutex); |
| 56 | 56 | ||
| 57 | static void evdev_pass_event(struct evdev_client *client, | 57 | static void __pass_event(struct evdev_client *client, |
| 58 | struct input_event *event, | 58 | const struct input_event *event) |
| 59 | ktime_t mono, ktime_t real) | ||
| 60 | { | 59 | { |
| 61 | event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? | ||
| 62 | mono : real); | ||
| 63 | |||
| 64 | /* Interrupts are disabled, just acquire the lock. */ | ||
| 65 | spin_lock(&client->buffer_lock); | ||
| 66 | |||
| 67 | client->buffer[client->head++] = *event; | 60 | client->buffer[client->head++] = *event; |
| 68 | client->head &= client->bufsize - 1; | 61 | client->head &= client->bufsize - 1; |
| 69 | 62 | ||
| @@ -86,42 +79,74 @@ static void evdev_pass_event(struct evdev_client *client, | |||
| 86 | client->packet_head = client->head; | 79 | client->packet_head = client->head; |
| 87 | kill_fasync(&client->fasync, SIGIO, POLL_IN); | 80 | kill_fasync(&client->fasync, SIGIO, POLL_IN); |
| 88 | } | 81 | } |
| 82 | } | ||
| 83 | |||
| 84 | static void evdev_pass_values(struct evdev_client *client, | ||
| 85 | const struct input_value *vals, unsigned int count, | ||
| 86 | ktime_t mono, ktime_t real) | ||
| 87 | { | ||
| 88 | struct evdev *evdev = client->evdev; | ||
| 89 | const struct input_value *v; | ||
| 90 | struct input_event event; | ||
| 91 | bool wakeup = false; | ||
| 92 | |||
| 93 | event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? | ||
| 94 | mono : real); | ||
| 95 | |||
| 96 | /* Interrupts are disabled, just acquire the lock. */ | ||
| 97 | spin_lock(&client->buffer_lock); | ||
| 98 | |||
| 99 | for (v = vals; v != vals + count; v++) { | ||
| 100 | event.type = v->type; | ||
| 101 | event.code = v->code; | ||
| 102 | event.value = v->value; | ||
| 103 | __pass_event(client, &event); | ||
| 104 | if (v->type == EV_SYN && v->code == SYN_REPORT) | ||
| 105 | wakeup = true; | ||
| 106 | } | ||
| 89 | 107 | ||
| 90 | spin_unlock(&client->buffer_lock); | 108 | spin_unlock(&client->buffer_lock); |
| 109 | |||
| 110 | if (wakeup) | ||
| 111 | wake_up_interruptible(&evdev->wait); | ||
| 91 | } | 112 | } |
| 92 | 113 | ||
| 93 | /* | 114 | /* |
| 94 | * Pass incoming event to all connected clients. | 115 | * Pass incoming events to all connected clients. |
| 95 | */ | 116 | */ |
| 96 | static void evdev_event(struct input_handle *handle, | 117 | static void evdev_events(struct input_handle *handle, |
| 97 | unsigned int type, unsigned int code, int value) | 118 | const struct input_value *vals, unsigned int count) |
| 98 | { | 119 | { |
| 99 | struct evdev *evdev = handle->private; | 120 | struct evdev *evdev = handle->private; |
| 100 | struct evdev_client *client; | 121 | struct evdev_client *client; |
| 101 | struct input_event event; | ||
| 102 | ktime_t time_mono, time_real; | 122 | ktime_t time_mono, time_real; |
| 103 | 123 | ||
| 104 | time_mono = ktime_get(); | 124 | time_mono = ktime_get(); |
| 105 | time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); | 125 | time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); |
| 106 | 126 | ||
| 107 | event.type = type; | ||
| 108 | event.code = code; | ||
| 109 | event.value = value; | ||
| 110 | |||
| 111 | rcu_read_lock(); | 127 | rcu_read_lock(); |
| 112 | 128 | ||
| 113 | client = rcu_dereference(evdev->grab); | 129 | client = rcu_dereference(evdev->grab); |
| 114 | 130 | ||
| 115 | if (client) | 131 | if (client) |
| 116 | evdev_pass_event(client, &event, time_mono, time_real); | 132 | evdev_pass_values(client, vals, count, time_mono, time_real); |
| 117 | else | 133 | else |
| 118 | list_for_each_entry_rcu(client, &evdev->client_list, node) | 134 | list_for_each_entry_rcu(client, &evdev->client_list, node) |
| 119 | evdev_pass_event(client, &event, time_mono, time_real); | 135 | evdev_pass_values(client, vals, count, |
| 136 | time_mono, time_real); | ||
| 120 | 137 | ||
| 121 | rcu_read_unlock(); | 138 | rcu_read_unlock(); |
| 139 | } | ||
| 122 | 140 | ||
| 123 | if (type == EV_SYN && code == SYN_REPORT) | 141 | /* |
| 124 | wake_up_interruptible(&evdev->wait); | 142 | * Pass incoming event to all connected clients. |
| 143 | */ | ||
| 144 | static void evdev_event(struct input_handle *handle, | ||
| 145 | unsigned int type, unsigned int code, int value) | ||
| 146 | { | ||
| 147 | struct input_value vals[] = { { type, code, value } }; | ||
| 148 | |||
| 149 | evdev_events(handle, vals, 1); | ||
| 125 | } | 150 | } |
| 126 | 151 | ||
| 127 | static int evdev_fasync(int fd, struct file *file, int on) | 152 | static int evdev_fasync(int fd, struct file *file, int on) |
| @@ -653,20 +678,22 @@ static int evdev_handle_mt_request(struct input_dev *dev, | |||
| 653 | unsigned int size, | 678 | unsigned int size, |
| 654 | int __user *ip) | 679 | int __user *ip) |
| 655 | { | 680 | { |
| 656 | const struct input_mt_slot *mt = dev->mt; | 681 | const struct input_mt *mt = dev->mt; |
| 657 | unsigned int code; | 682 | unsigned int code; |
| 658 | int max_slots; | 683 | int max_slots; |
| 659 | int i; | 684 | int i; |
| 660 | 685 | ||
| 661 | if (get_user(code, &ip[0])) | 686 | if (get_user(code, &ip[0])) |
| 662 | return -EFAULT; | 687 | return -EFAULT; |
| 663 | if (!input_is_mt_value(code)) | 688 | if (!mt || !input_is_mt_value(code)) |
| 664 | return -EINVAL; | 689 | return -EINVAL; |
| 665 | 690 | ||
| 666 | max_slots = (size - sizeof(__u32)) / sizeof(__s32); | 691 | max_slots = (size - sizeof(__u32)) / sizeof(__s32); |
| 667 | for (i = 0; i < dev->mtsize && i < max_slots; i++) | 692 | for (i = 0; i < mt->num_slots && i < max_slots; i++) { |
| 668 | if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i])) | 693 | int value = input_mt_get_value(&mt->slots[i], code); |
| 694 | if (put_user(value, &ip[1 + i])) | ||
| 669 | return -EFAULT; | 695 | return -EFAULT; |
| 696 | } | ||
| 670 | 697 | ||
| 671 | return 0; | 698 | return 0; |
| 672 | } | 699 | } |
| @@ -1048,6 +1075,7 @@ MODULE_DEVICE_TABLE(input, evdev_ids); | |||
| 1048 | 1075 | ||
| 1049 | static struct input_handler evdev_handler = { | 1076 | static struct input_handler evdev_handler = { |
| 1050 | .event = evdev_event, | 1077 | .event = evdev_event, |
| 1078 | .events = evdev_events, | ||
| 1051 | .connect = evdev_connect, | 1079 | .connect = evdev_connect, |
| 1052 | .disconnect = evdev_disconnect, | 1080 | .disconnect = evdev_disconnect, |
| 1053 | .fops = &evdev_fops, | 1081 | .fops = &evdev_fops, |
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 70a16c7da8cc..c0ec7d42c3be 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c | |||
| @@ -14,6 +14,14 @@ | |||
| 14 | 14 | ||
| 15 | #define TRKID_SGN ((TRKID_MAX + 1) >> 1) | 15 | #define TRKID_SGN ((TRKID_MAX + 1) >> 1) |
| 16 | 16 | ||
| 17 | static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src) | ||
| 18 | { | ||
| 19 | if (dev->absinfo && test_bit(src, dev->absbit)) { | ||
| 20 | dev->absinfo[dst] = dev->absinfo[src]; | ||
| 21 | dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 17 | /** | 25 | /** |
| 18 | * input_mt_init_slots() - initialize MT input slots | 26 | * input_mt_init_slots() - initialize MT input slots |
| 19 | * @dev: input device supporting MT events and finger tracking | 27 | * @dev: input device supporting MT events and finger tracking |
| @@ -25,29 +33,63 @@ | |||
| 25 | * May be called repeatedly. Returns -EINVAL if attempting to | 33 | * May be called repeatedly. Returns -EINVAL if attempting to |
| 26 | * reinitialize with a different number of slots. | 34 | * reinitialize with a different number of slots. |
| 27 | */ | 35 | */ |
| 28 | int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) | 36 | int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, |
| 37 | unsigned int flags) | ||
| 29 | { | 38 | { |
| 39 | struct input_mt *mt = dev->mt; | ||
| 30 | int i; | 40 | int i; |
| 31 | 41 | ||
| 32 | if (!num_slots) | 42 | if (!num_slots) |
| 33 | return 0; | 43 | return 0; |
| 34 | if (dev->mt) | 44 | if (mt) |
| 35 | return dev->mtsize != num_slots ? -EINVAL : 0; | 45 | return mt->num_slots != num_slots ? -EINVAL : 0; |
| 36 | 46 | ||
| 37 | dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); | 47 | mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL); |
| 38 | if (!dev->mt) | 48 | if (!mt) |
| 39 | return -ENOMEM; | 49 | goto err_mem; |
| 40 | 50 | ||
| 41 | dev->mtsize = num_slots; | 51 | mt->num_slots = num_slots; |
| 52 | mt->flags = flags; | ||
| 42 | input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); | 53 | input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); |
| 43 | input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); | 54 | input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); |
| 44 | input_set_events_per_packet(dev, 6 * num_slots); | 55 | |
| 56 | if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) { | ||
| 57 | __set_bit(EV_KEY, dev->evbit); | ||
| 58 | __set_bit(BTN_TOUCH, dev->keybit); | ||
| 59 | |||
| 60 | copy_abs(dev, ABS_X, ABS_MT_POSITION_X); | ||
| 61 | copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y); | ||
| 62 | copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE); | ||
| 63 | } | ||
| 64 | if (flags & INPUT_MT_POINTER) { | ||
| 65 | __set_bit(BTN_TOOL_FINGER, dev->keybit); | ||
| 66 | __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); | ||
| 67 | if (num_slots >= 3) | ||
| 68 | __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); | ||
| 69 | if (num_slots >= 4) | ||
| 70 | __set_bit(BTN_TOOL_QUADTAP, dev->keybit); | ||
| 71 | if (num_slots >= 5) | ||
| 72 | __set_bit(BTN_TOOL_QUINTTAP, dev->keybit); | ||
| 73 | __set_bit(INPUT_PROP_POINTER, dev->propbit); | ||
| 74 | } | ||
| 75 | if (flags & INPUT_MT_DIRECT) | ||
| 76 | __set_bit(INPUT_PROP_DIRECT, dev->propbit); | ||
| 77 | if (flags & INPUT_MT_TRACK) { | ||
| 78 | unsigned int n2 = num_slots * num_slots; | ||
| 79 | mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL); | ||
| 80 | if (!mt->red) | ||
| 81 | goto err_mem; | ||
| 82 | } | ||
| 45 | 83 | ||
| 46 | /* Mark slots as 'unused' */ | 84 | /* Mark slots as 'unused' */ |
| 47 | for (i = 0; i < num_slots; i++) | 85 | for (i = 0; i < num_slots; i++) |
| 48 | input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1); | 86 | input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1); |
| 49 | 87 | ||
| 88 | dev->mt = mt; | ||
| 50 | return 0; | 89 | return 0; |
| 90 | err_mem: | ||
| 91 | kfree(mt); | ||
| 92 | return -ENOMEM; | ||
| 51 | } | 93 | } |
| 52 | EXPORT_SYMBOL(input_mt_init_slots); | 94 | EXPORT_SYMBOL(input_mt_init_slots); |
| 53 | 95 | ||
| @@ -60,11 +102,11 @@ EXPORT_SYMBOL(input_mt_init_slots); | |||
| 60 | */ | 102 | */ |
| 61 | void input_mt_destroy_slots(struct input_dev *dev) | 103 | void input_mt_destroy_slots(struct input_dev *dev) |
| 62 | { | 104 | { |
| 63 | kfree(dev->mt); | 105 | if (dev->mt) { |
| 106 | kfree(dev->mt->red); | ||
| 107 | kfree(dev->mt); | ||
| 108 | } | ||
| 64 | dev->mt = NULL; | 109 | dev->mt = NULL; |
| 65 | dev->mtsize = 0; | ||
| 66 | dev->slot = 0; | ||
| 67 | dev->trkid = 0; | ||
| 68 | } | 110 | } |
| 69 | EXPORT_SYMBOL(input_mt_destroy_slots); | 111 | EXPORT_SYMBOL(input_mt_destroy_slots); |
| 70 | 112 | ||
| @@ -83,18 +125,24 @@ EXPORT_SYMBOL(input_mt_destroy_slots); | |||
| 83 | void input_mt_report_slot_state(struct input_dev *dev, | 125 | void input_mt_report_slot_state(struct input_dev *dev, |
| 84 | unsigned int tool_type, bool active) | 126 | unsigned int tool_type, bool active) |
| 85 | { | 127 | { |
| 86 | struct input_mt_slot *mt; | 128 | struct input_mt *mt = dev->mt; |
| 129 | struct input_mt_slot *slot; | ||
| 87 | int id; | 130 | int id; |
| 88 | 131 | ||
| 89 | if (!dev->mt || !active) { | 132 | if (!mt) |
| 133 | return; | ||
| 134 | |||
| 135 | slot = &mt->slots[mt->slot]; | ||
| 136 | slot->frame = mt->frame; | ||
| 137 | |||
| 138 | if (!active) { | ||
| 90 | input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); | 139 | input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); |
| 91 | return; | 140 | return; |
| 92 | } | 141 | } |
| 93 | 142 | ||
| 94 | mt = &dev->mt[dev->slot]; | 143 | id = input_mt_get_value(slot, ABS_MT_TRACKING_ID); |
| 95 | id = input_mt_get_value(mt, ABS_MT_TRACKING_ID); | 144 | if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type) |
| 96 | if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type) | 145 | id = input_mt_new_trkid(mt); |
| 97 | id = input_mt_new_trkid(dev); | ||
| 98 | 146 | ||
| 99 | input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); | 147 | input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); |
| 100 | input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type); | 148 | input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type); |
| @@ -135,13 +183,19 @@ EXPORT_SYMBOL(input_mt_report_finger_count); | |||
| 135 | */ | 183 | */ |
| 136 | void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) | 184 | void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) |
| 137 | { | 185 | { |
| 138 | struct input_mt_slot *oldest = NULL; | 186 | struct input_mt *mt = dev->mt; |
| 139 | int oldid = dev->trkid; | 187 | struct input_mt_slot *oldest; |
| 140 | int count = 0; | 188 | int oldid, count, i; |
| 141 | int i; | 189 | |
| 190 | if (!mt) | ||
| 191 | return; | ||
| 192 | |||
| 193 | oldest = 0; | ||
| 194 | oldid = mt->trkid; | ||
| 195 | count = 0; | ||
| 142 | 196 | ||
| 143 | for (i = 0; i < dev->mtsize; ++i) { | 197 | for (i = 0; i < mt->num_slots; ++i) { |
| 144 | struct input_mt_slot *ps = &dev->mt[i]; | 198 | struct input_mt_slot *ps = &mt->slots[i]; |
| 145 | int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID); | 199 | int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID); |
| 146 | 200 | ||
| 147 | if (id < 0) | 201 | if (id < 0) |
| @@ -160,13 +214,208 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) | |||
| 160 | if (oldest) { | 214 | if (oldest) { |
| 161 | int x = input_mt_get_value(oldest, ABS_MT_POSITION_X); | 215 | int x = input_mt_get_value(oldest, ABS_MT_POSITION_X); |
| 162 | int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y); | 216 | int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y); |
| 163 | int p = input_mt_get_value(oldest, ABS_MT_PRESSURE); | ||
| 164 | 217 | ||
| 165 | input_event(dev, EV_ABS, ABS_X, x); | 218 | input_event(dev, EV_ABS, ABS_X, x); |
| 166 | input_event(dev, EV_ABS, ABS_Y, y); | 219 | input_event(dev, EV_ABS, ABS_Y, y); |
| 167 | input_event(dev, EV_ABS, ABS_PRESSURE, p); | 220 | |
| 221 | if (test_bit(ABS_MT_PRESSURE, dev->absbit)) { | ||
| 222 | int p = input_mt_get_value(oldest, ABS_MT_PRESSURE); | ||
| 223 | input_event(dev, EV_ABS, ABS_PRESSURE, p); | ||
| 224 | } | ||
| 168 | } else { | 225 | } else { |
| 169 | input_event(dev, EV_ABS, ABS_PRESSURE, 0); | 226 | if (test_bit(ABS_MT_PRESSURE, dev->absbit)) |
| 227 | input_event(dev, EV_ABS, ABS_PRESSURE, 0); | ||
| 170 | } | 228 | } |
| 171 | } | 229 | } |
| 172 | EXPORT_SYMBOL(input_mt_report_pointer_emulation); | 230 | EXPORT_SYMBOL(input_mt_report_pointer_emulation); |
| 231 | |||
| 232 | /** | ||
| 233 | * input_mt_sync_frame() - synchronize mt frame | ||
| 234 | * @dev: input device with allocated MT slots | ||
| 235 | * | ||
| 236 | * Close the frame and prepare the internal state for a new one. | ||
| 237 | * Depending on the flags, marks unused slots as inactive and performs | ||
| 238 | * pointer emulation. | ||
| 239 | */ | ||
| 240 | void input_mt_sync_frame(struct input_dev *dev) | ||
| 241 | { | ||
| 242 | struct input_mt *mt = dev->mt; | ||
| 243 | struct input_mt_slot *s; | ||
| 244 | |||
| 245 | if (!mt) | ||
| 246 | return; | ||
| 247 | |||
| 248 | if (mt->flags & INPUT_MT_DROP_UNUSED) { | ||
| 249 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { | ||
| 250 | if (s->frame == mt->frame) | ||
| 251 | continue; | ||
| 252 | input_mt_slot(dev, s - mt->slots); | ||
| 253 | input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER)); | ||
| 258 | |||
| 259 | mt->frame++; | ||
| 260 | } | ||
| 261 | EXPORT_SYMBOL(input_mt_sync_frame); | ||
| 262 | |||
| 263 | static int adjust_dual(int *begin, int step, int *end, int eq) | ||
| 264 | { | ||
| 265 | int f, *p, s, c; | ||
| 266 | |||
| 267 | if (begin == end) | ||
| 268 | return 0; | ||
| 269 | |||
| 270 | f = *begin; | ||
| 271 | p = begin + step; | ||
| 272 | s = p == end ? f + 1 : *p; | ||
| 273 | |||
| 274 | for (; p != end; p += step) | ||
| 275 | if (*p < f) | ||
| 276 | s = f, f = *p; | ||
| 277 | else if (*p < s) | ||
| 278 | s = *p; | ||
| 279 | |||
| 280 | c = (f + s + 1) / 2; | ||
| 281 | if (c == 0 || (c > 0 && !eq)) | ||
| 282 | return 0; | ||
| 283 | if (s < 0) | ||
| 284 | c *= 2; | ||
| 285 | |||
| 286 | for (p = begin; p != end; p += step) | ||
| 287 | *p -= c; | ||
| 288 | |||
| 289 | return (c < s && s <= 0) || (f >= 0 && f < c); | ||
| 290 | } | ||
| 291 | |||
| 292 | static void find_reduced_matrix(int *w, int nr, int nc, int nrc) | ||
| 293 | { | ||
| 294 | int i, k, sum; | ||
| 295 | |||
| 296 | for (k = 0; k < nrc; k++) { | ||
| 297 | for (i = 0; i < nr; i++) | ||
| 298 | adjust_dual(w + i, nr, w + i + nrc, nr <= nc); | ||
| 299 | sum = 0; | ||
| 300 | for (i = 0; i < nrc; i += nr) | ||
| 301 | sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr); | ||
| 302 | if (!sum) | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | static int input_mt_set_matrix(struct input_mt *mt, | ||
| 308 | const struct input_mt_pos *pos, int num_pos) | ||
| 309 | { | ||
| 310 | const struct input_mt_pos *p; | ||
| 311 | struct input_mt_slot *s; | ||
| 312 | int *w = mt->red; | ||
| 313 | int x, y; | ||
| 314 | |||
| 315 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { | ||
| 316 | if (!input_mt_is_active(s)) | ||
| 317 | continue; | ||
| 318 | x = input_mt_get_value(s, ABS_MT_POSITION_X); | ||
| 319 | y = input_mt_get_value(s, ABS_MT_POSITION_Y); | ||
| 320 | for (p = pos; p != pos + num_pos; p++) { | ||
| 321 | int dx = x - p->x, dy = y - p->y; | ||
| 322 | *w++ = dx * dx + dy * dy; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | return w - mt->red; | ||
| 327 | } | ||
| 328 | |||
| 329 | static void input_mt_set_slots(struct input_mt *mt, | ||
| 330 | int *slots, int num_pos) | ||
| 331 | { | ||
| 332 | struct input_mt_slot *s; | ||
| 333 | int *w = mt->red, *p; | ||
| 334 | |||
| 335 | for (p = slots; p != slots + num_pos; p++) | ||
| 336 | *p = -1; | ||
| 337 | |||
| 338 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { | ||
| 339 | if (!input_mt_is_active(s)) | ||
| 340 | continue; | ||
| 341 | for (p = slots; p != slots + num_pos; p++) | ||
| 342 | if (*w++ < 0) | ||
| 343 | *p = s - mt->slots; | ||
| 344 | } | ||
| 345 | |||
| 346 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { | ||
| 347 | if (input_mt_is_active(s)) | ||
| 348 | continue; | ||
| 349 | for (p = slots; p != slots + num_pos; p++) | ||
| 350 | if (*p < 0) { | ||
| 351 | *p = s - mt->slots; | ||
| 352 | break; | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | /** | ||
| 358 | * input_mt_assign_slots() - perform a best-match assignment | ||
| 359 | * @dev: input device with allocated MT slots | ||
| 360 | * @slots: the slot assignment to be filled | ||
| 361 | * @pos: the position array to match | ||
| 362 | * @num_pos: number of positions | ||
| 363 | * | ||
| 364 | * Performs a best match against the current contacts and returns | ||
| 365 | * the slot assignment list. New contacts are assigned to unused | ||
| 366 | * slots. | ||
| 367 | * | ||
| 368 | * Returns zero on success, or negative error in case of failure. | ||
| 369 | */ | ||
| 370 | int input_mt_assign_slots(struct input_dev *dev, int *slots, | ||
| 371 | const struct input_mt_pos *pos, int num_pos) | ||
| 372 | { | ||
| 373 | struct input_mt *mt = dev->mt; | ||
| 374 | int nrc; | ||
| 375 | |||
| 376 | if (!mt || !mt->red) | ||
| 377 | return -ENXIO; | ||
| 378 | if (num_pos > mt->num_slots) | ||
| 379 | return -EINVAL; | ||
| 380 | if (num_pos < 1) | ||
| 381 | return 0; | ||
| 382 | |||
| 383 | nrc = input_mt_set_matrix(mt, pos, num_pos); | ||
| 384 | find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc); | ||
| 385 | input_mt_set_slots(mt, slots, num_pos); | ||
| 386 | |||
| 387 | return 0; | ||
| 388 | } | ||
| 389 | EXPORT_SYMBOL(input_mt_assign_slots); | ||
| 390 | |||
| 391 | /** | ||
| 392 | * input_mt_get_slot_by_key() - return slot matching key | ||
| 393 | * @dev: input device with allocated MT slots | ||
| 394 | * @key: the key of the sought slot | ||
| 395 | * | ||
| 396 | * Returns the slot of the given key, if it exists, otherwise | ||
| 397 | * set the key on the first unused slot and return. | ||
| 398 | * | ||
| 399 | * If no available slot can be found, -1 is returned. | ||
| 400 | */ | ||
| 401 | int input_mt_get_slot_by_key(struct input_dev *dev, int key) | ||
| 402 | { | ||
| 403 | struct input_mt *mt = dev->mt; | ||
| 404 | struct input_mt_slot *s; | ||
| 405 | |||
| 406 | if (!mt) | ||
| 407 | return -1; | ||
| 408 | |||
| 409 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) | ||
| 410 | if (input_mt_is_active(s) && s->key == key) | ||
| 411 | return s - mt->slots; | ||
| 412 | |||
| 413 | for (s = mt->slots; s != mt->slots + mt->num_slots; s++) | ||
| 414 | if (!input_mt_is_active(s)) { | ||
| 415 | s->key = key; | ||
| 416 | return s - mt->slots; | ||
| 417 | } | ||
| 418 | |||
| 419 | return -1; | ||
| 420 | } | ||
| 421 | EXPORT_SYMBOL(input_mt_get_slot_by_key); | ||
diff --git a/drivers/input/input.c b/drivers/input/input.c index 8921c6180c51..5244f3d05b12 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c | |||
| @@ -47,6 +47,8 @@ static DEFINE_MUTEX(input_mutex); | |||
| 47 | 47 | ||
| 48 | static struct input_handler *input_table[8]; | 48 | static struct input_handler *input_table[8]; |
| 49 | 49 | ||
| 50 | static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 }; | ||
| 51 | |||
| 50 | static inline int is_event_supported(unsigned int code, | 52 | static inline int is_event_supported(unsigned int code, |
| 51 | unsigned long *bm, unsigned int max) | 53 | unsigned long *bm, unsigned int max) |
| 52 | { | 54 | { |
| @@ -69,42 +71,102 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz) | |||
| 69 | return value; | 71 | return value; |
| 70 | } | 72 | } |
| 71 | 73 | ||
| 74 | static void input_start_autorepeat(struct input_dev *dev, int code) | ||
| 75 | { | ||
| 76 | if (test_bit(EV_REP, dev->evbit) && | ||
| 77 | dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && | ||
| 78 | dev->timer.data) { | ||
| 79 | dev->repeat_key = code; | ||
| 80 | mod_timer(&dev->timer, | ||
| 81 | jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | static void input_stop_autorepeat(struct input_dev *dev) | ||
| 86 | { | ||
| 87 | del_timer(&dev->timer); | ||
| 88 | } | ||
| 89 | |||
| 72 | /* | 90 | /* |
| 73 | * Pass event first through all filters and then, if event has not been | 91 | * Pass event first through all filters and then, if event has not been |
| 74 | * filtered out, through all open handles. This function is called with | 92 | * filtered out, through all open handles. This function is called with |
| 75 | * dev->event_lock held and interrupts disabled. | 93 | * dev->event_lock held and interrupts disabled. |
| 76 | */ | 94 | */ |
| 77 | static void input_pass_event(struct input_dev *dev, | 95 | static unsigned int input_to_handler(struct input_handle *handle, |
| 78 | unsigned int type, unsigned int code, int value) | 96 | struct input_value *vals, unsigned int count) |
| 97 | { | ||
| 98 | struct input_handler *handler = handle->handler; | ||
| 99 | struct input_value *end = vals; | ||
| 100 | struct input_value *v; | ||
| 101 | |||
| 102 | for (v = vals; v != vals + count; v++) { | ||
| 103 | if (handler->filter && | ||
| 104 | handler->filter(handle, v->type, v->code, v->value)) | ||
| 105 | continue; | ||
| 106 | if (end != v) | ||
| 107 | *end = *v; | ||
| 108 | end++; | ||
| 109 | } | ||
| 110 | |||
| 111 | count = end - vals; | ||
| 112 | if (!count) | ||
| 113 | return 0; | ||
| 114 | |||
| 115 | if (handler->events) | ||
| 116 | handler->events(handle, vals, count); | ||
| 117 | else if (handler->event) | ||
| 118 | for (v = vals; v != end; v++) | ||
| 119 | handler->event(handle, v->type, v->code, v->value); | ||
| 120 | |||
| 121 | return count; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* | ||
| 125 | * Pass values first through all filters and then, if event has not been | ||
| 126 | * filtered out, through all open handles. This function is called with | ||
| 127 | * dev->event_lock held and interrupts disabled. | ||
| 128 | */ | ||
| 129 | static void input_pass_values(struct input_dev *dev, | ||
| 130 | struct input_value *vals, unsigned int count) | ||
| 79 | { | 131 | { |
| 80 | struct input_handler *handler; | ||
| 81 | struct input_handle *handle; | 132 | struct input_handle *handle; |
| 133 | struct input_value *v; | ||
| 134 | |||
| 135 | if (!count) | ||
| 136 | return; | ||
| 82 | 137 | ||
| 83 | rcu_read_lock(); | 138 | rcu_read_lock(); |
| 84 | 139 | ||
| 85 | handle = rcu_dereference(dev->grab); | 140 | handle = rcu_dereference(dev->grab); |
| 86 | if (handle) | 141 | if (handle) { |
| 87 | handle->handler->event(handle, type, code, value); | 142 | count = input_to_handler(handle, vals, count); |
| 88 | else { | 143 | } else { |
| 89 | bool filtered = false; | 144 | list_for_each_entry_rcu(handle, &dev->h_list, d_node) |
| 90 | 145 | if (handle->open) | |
| 91 | list_for_each_entry_rcu(handle, &dev->h_list, d_node) { | 146 | count = input_to_handler(handle, vals, count); |
| 92 | if (!handle->open) | 147 | } |
| 93 | continue; | ||
| 94 | 148 | ||
| 95 | handler = handle->handler; | 149 | rcu_read_unlock(); |
| 96 | if (!handler->filter) { | ||
| 97 | if (filtered) | ||
| 98 | break; | ||
| 99 | 150 | ||
| 100 | handler->event(handle, type, code, value); | 151 | add_input_randomness(vals->type, vals->code, vals->value); |
| 101 | 152 | ||
| 102 | } else if (handler->filter(handle, type, code, value)) | 153 | /* trigger auto repeat for key events */ |
| 103 | filtered = true; | 154 | for (v = vals; v != vals + count; v++) { |
| 155 | if (v->type == EV_KEY && v->value != 2) { | ||
| 156 | if (v->value) | ||
| 157 | input_start_autorepeat(dev, v->code); | ||
| 158 | else | ||
| 159 | input_stop_autorepeat(dev); | ||
| 104 | } | 160 | } |
| 105 | } | 161 | } |
| 162 | } | ||
| 106 | 163 | ||
| 107 | rcu_read_unlock(); | 164 | static void input_pass_event(struct input_dev *dev, |
| 165 | unsigned int type, unsigned int code, int value) | ||
| 166 | { | ||
| 167 | struct input_value vals[] = { { type, code, value } }; | ||
| 168 | |||
| 169 | input_pass_values(dev, vals, ARRAY_SIZE(vals)); | ||
| 108 | } | 170 | } |
| 109 | 171 | ||
| 110 | /* | 172 | /* |
| @@ -121,18 +183,12 @@ static void input_repeat_key(unsigned long data) | |||
| 121 | 183 | ||
| 122 | if (test_bit(dev->repeat_key, dev->key) && | 184 | if (test_bit(dev->repeat_key, dev->key) && |
| 123 | is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { | 185 | is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { |
| 186 | struct input_value vals[] = { | ||
| 187 | { EV_KEY, dev->repeat_key, 2 }, | ||
| 188 | input_value_sync | ||
| 189 | }; | ||
| 124 | 190 | ||
| 125 | input_pass_event(dev, EV_KEY, dev->repeat_key, 2); | 191 | input_pass_values(dev, vals, ARRAY_SIZE(vals)); |
| 126 | |||
| 127 | if (dev->sync) { | ||
| 128 | /* | ||
| 129 | * Only send SYN_REPORT if we are not in a middle | ||
| 130 | * of driver parsing a new hardware packet. | ||
| 131 | * Otherwise assume that the driver will send | ||
| 132 | * SYN_REPORT once it's done. | ||
| 133 | */ | ||
| 134 | input_pass_event(dev, EV_SYN, SYN_REPORT, 1); | ||
| 135 | } | ||
| 136 | 192 | ||
| 137 | if (dev->rep[REP_PERIOD]) | 193 | if (dev->rep[REP_PERIOD]) |
| 138 | mod_timer(&dev->timer, jiffies + | 194 | mod_timer(&dev->timer, jiffies + |
| @@ -142,30 +198,17 @@ static void input_repeat_key(unsigned long data) | |||
| 142 | spin_unlock_irqrestore(&dev->event_lock, flags); | 198 | spin_unlock_irqrestore(&dev->event_lock, flags); |
| 143 | } | 199 | } |
| 144 | 200 | ||
| 145 | static void input_start_autorepeat(struct input_dev *dev, int code) | ||
| 146 | { | ||
| 147 | if (test_bit(EV_REP, dev->evbit) && | ||
| 148 | dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && | ||
| 149 | dev->timer.data) { | ||
| 150 | dev->repeat_key = code; | ||
| 151 | mod_timer(&dev->timer, | ||
| 152 | jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | static void input_stop_autorepeat(struct input_dev *dev) | ||
| 157 | { | ||
| 158 | del_timer(&dev->timer); | ||
| 159 | } | ||
| 160 | |||
| 161 | #define INPUT_IGNORE_EVENT 0 | 201 | #define INPUT_IGNORE_EVENT 0 |
| 162 | #define INPUT_PASS_TO_HANDLERS 1 | 202 | #define INPUT_PASS_TO_HANDLERS 1 |
| 163 | #define INPUT_PASS_TO_DEVICE 2 | 203 | #define INPUT_PASS_TO_DEVICE 2 |
| 204 | #define INPUT_SLOT 4 | ||
| 205 | #define INPUT_FLUSH 8 | ||
| 164 | #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) | 206 | #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) |
| 165 | 207 | ||
| 166 | static int input_handle_abs_event(struct input_dev *dev, | 208 | static int input_handle_abs_event(struct input_dev *dev, |
| 167 | unsigned int code, int *pval) | 209 | unsigned int code, int *pval) |
| 168 | { | 210 | { |
| 211 | struct input_mt *mt = dev->mt; | ||
| 169 | bool is_mt_event; | 212 | bool is_mt_event; |
| 170 | int *pold; | 213 | int *pold; |
| 171 | 214 | ||
| @@ -174,8 +217,8 @@ static int input_handle_abs_event(struct input_dev *dev, | |||
| 174 | * "Stage" the event; we'll flush it later, when we | 217 | * "Stage" the event; we'll flush it later, when we |
| 175 | * get actual touch data. | 218 | * get actual touch data. |
| 176 | */ | 219 | */ |
| 177 | if (*pval >= 0 && *pval < dev->mtsize) | 220 | if (mt && *pval >= 0 && *pval < mt->num_slots) |
| 178 | dev->slot = *pval; | 221 | mt->slot = *pval; |
| 179 | 222 | ||
| 180 | return INPUT_IGNORE_EVENT; | 223 | return INPUT_IGNORE_EVENT; |
| 181 | } | 224 | } |
| @@ -184,9 +227,8 @@ static int input_handle_abs_event(struct input_dev *dev, | |||
| 184 | 227 | ||
| 185 | if (!is_mt_event) { | 228 | if (!is_mt_event) { |
| 186 | pold = &dev->absinfo[code].value; | 229 | pold = &dev->absinfo[code].value; |
| 187 | } else if (dev->mt) { | 230 | } else if (mt) { |
| 188 | struct input_mt_slot *mtslot = &dev->mt[dev->slot]; | 231 | pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST]; |
| 189 | pold = &mtslot->abs[code - ABS_MT_FIRST]; | ||
| 190 | } else { | 232 | } else { |
| 191 | /* | 233 | /* |
| 192 | * Bypass filtering for multi-touch events when | 234 | * Bypass filtering for multi-touch events when |
| @@ -205,16 +247,16 @@ static int input_handle_abs_event(struct input_dev *dev, | |||
| 205 | } | 247 | } |
| 206 | 248 | ||
| 207 | /* Flush pending "slot" event */ | 249 | /* Flush pending "slot" event */ |
| 208 | if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) { | 250 | if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) { |
| 209 | input_abs_set_val(dev, ABS_MT_SLOT, dev->slot); | 251 | input_abs_set_val(dev, ABS_MT_SLOT, mt->slot); |
| 210 | input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot); | 252 | return INPUT_PASS_TO_HANDLERS | INPUT_SLOT; |
| 211 | } | 253 | } |
| 212 | 254 | ||
| 213 | return INPUT_PASS_TO_HANDLERS; | 255 | return INPUT_PASS_TO_HANDLERS; |
| 214 | } | 256 | } |
| 215 | 257 | ||
| 216 | static void input_handle_event(struct input_dev *dev, | 258 | static int input_get_disposition(struct input_dev *dev, |
| 217 | unsigned int type, unsigned int code, int value) | 259 | unsigned int type, unsigned int code, int value) |
| 218 | { | 260 | { |
| 219 | int disposition = INPUT_IGNORE_EVENT; | 261 | int disposition = INPUT_IGNORE_EVENT; |
| 220 | 262 | ||
| @@ -227,37 +269,34 @@ static void input_handle_event(struct input_dev *dev, | |||
| 227 | break; | 269 | break; |
| 228 | 270 | ||
| 229 | case SYN_REPORT: | 271 | case SYN_REPORT: |
| 230 | if (!dev->sync) { | 272 | disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH; |
| 231 | dev->sync = true; | ||
| 232 | disposition = INPUT_PASS_TO_HANDLERS; | ||
| 233 | } | ||
| 234 | break; | 273 | break; |
| 235 | case SYN_MT_REPORT: | 274 | case SYN_MT_REPORT: |
| 236 | dev->sync = false; | ||
| 237 | disposition = INPUT_PASS_TO_HANDLERS; | 275 | disposition = INPUT_PASS_TO_HANDLERS; |
| 238 | break; | 276 | break; |
| 239 | } | 277 | } |
| 240 | break; | 278 | break; |
| 241 | 279 | ||
| 242 | case EV_KEY: | 280 | case EV_KEY: |
| 243 | if (is_event_supported(code, dev->keybit, KEY_MAX) && | 281 | if (is_event_supported(code, dev->keybit, KEY_MAX)) { |
| 244 | !!test_bit(code, dev->key) != value) { | ||
| 245 | 282 | ||
| 246 | if (value != 2) { | 283 | /* auto-repeat bypasses state updates */ |
| 247 | __change_bit(code, dev->key); | 284 | if (value == 2) { |
| 248 | if (value) | 285 | disposition = INPUT_PASS_TO_HANDLERS; |
| 249 | input_start_autorepeat(dev, code); | 286 | break; |
| 250 | else | ||
| 251 | input_stop_autorepeat(dev); | ||
| 252 | } | 287 | } |
| 253 | 288 | ||
| 254 | disposition = INPUT_PASS_TO_HANDLERS; | 289 | if (!!test_bit(code, dev->key) != !!value) { |
| 290 | |||
| 291 | __change_bit(code, dev->key); | ||
| 292 | disposition = INPUT_PASS_TO_HANDLERS; | ||
| 293 | } | ||
| 255 | } | 294 | } |
| 256 | break; | 295 | break; |
| 257 | 296 | ||
| 258 | case EV_SW: | 297 | case EV_SW: |
| 259 | if (is_event_supported(code, dev->swbit, SW_MAX) && | 298 | if (is_event_supported(code, dev->swbit, SW_MAX) && |
| 260 | !!test_bit(code, dev->sw) != value) { | 299 | !!test_bit(code, dev->sw) != !!value) { |
| 261 | 300 | ||
| 262 | __change_bit(code, dev->sw); | 301 | __change_bit(code, dev->sw); |
| 263 | disposition = INPUT_PASS_TO_HANDLERS; | 302 | disposition = INPUT_PASS_TO_HANDLERS; |
| @@ -284,7 +323,7 @@ static void input_handle_event(struct input_dev *dev, | |||
| 284 | 323 | ||
| 285 | case EV_LED: | 324 | case EV_LED: |
| 286 | if (is_event_supported(code, dev->ledbit, LED_MAX) && | 325 | if (is_event_supported(code, dev->ledbit, LED_MAX) && |
| 287 | !!test_bit(code, dev->led) != value) { | 326 | !!test_bit(code, dev->led) != !!value) { |
| 288 | 327 | ||
| 289 | __change_bit(code, dev->led); | 328 | __change_bit(code, dev->led); |
| 290 | disposition = INPUT_PASS_TO_ALL; | 329 | disposition = INPUT_PASS_TO_ALL; |
| @@ -317,14 +356,48 @@ static void input_handle_event(struct input_dev *dev, | |||
| 317 | break; | 356 | break; |
| 318 | } | 357 | } |
| 319 | 358 | ||
| 320 | if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) | 359 | return disposition; |
| 321 | dev->sync = false; | 360 | } |
| 361 | |||
| 362 | static void input_handle_event(struct input_dev *dev, | ||
| 363 | unsigned int type, unsigned int code, int value) | ||
| 364 | { | ||
| 365 | int disposition; | ||
| 366 | |||
| 367 | disposition = input_get_disposition(dev, type, code, value); | ||
| 322 | 368 | ||
| 323 | if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) | 369 | if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) |
| 324 | dev->event(dev, type, code, value); | 370 | dev->event(dev, type, code, value); |
| 325 | 371 | ||
| 326 | if (disposition & INPUT_PASS_TO_HANDLERS) | 372 | if (!dev->vals) |
| 327 | input_pass_event(dev, type, code, value); | 373 | return; |
| 374 | |||
| 375 | if (disposition & INPUT_PASS_TO_HANDLERS) { | ||
| 376 | struct input_value *v; | ||
| 377 | |||
| 378 | if (disposition & INPUT_SLOT) { | ||
| 379 | v = &dev->vals[dev->num_vals++]; | ||
| 380 | v->type = EV_ABS; | ||
| 381 | v->code = ABS_MT_SLOT; | ||
| 382 | v->value = dev->mt->slot; | ||
| 383 | } | ||
| 384 | |||
| 385 | v = &dev->vals[dev->num_vals++]; | ||
| 386 | v->type = type; | ||
| 387 | v->code = code; | ||
| 388 | v->value = value; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (disposition & INPUT_FLUSH) { | ||
| 392 | if (dev->num_vals >= 2) | ||
| 393 | input_pass_values(dev, dev->vals, dev->num_vals); | ||
| 394 | dev->num_vals = 0; | ||
| 395 | } else if (dev->num_vals >= dev->max_vals - 2) { | ||
| 396 | dev->vals[dev->num_vals++] = input_value_sync; | ||
| 397 | input_pass_values(dev, dev->vals, dev->num_vals); | ||
| 398 | dev->num_vals = 0; | ||
| 399 | } | ||
| 400 | |||
| 328 | } | 401 | } |
| 329 | 402 | ||
| 330 | /** | 403 | /** |
| @@ -352,7 +425,6 @@ void input_event(struct input_dev *dev, | |||
| 352 | if (is_event_supported(type, dev->evbit, EV_MAX)) { | 425 | if (is_event_supported(type, dev->evbit, EV_MAX)) { |
| 353 | 426 | ||
| 354 | spin_lock_irqsave(&dev->event_lock, flags); | 427 | spin_lock_irqsave(&dev->event_lock, flags); |
| 355 | add_input_randomness(type, code, value); | ||
| 356 | input_handle_event(dev, type, code, value); | 428 | input_handle_event(dev, type, code, value); |
| 357 | spin_unlock_irqrestore(&dev->event_lock, flags); | 429 | spin_unlock_irqrestore(&dev->event_lock, flags); |
| 358 | } | 430 | } |
| @@ -831,10 +903,12 @@ int input_set_keycode(struct input_dev *dev, | |||
| 831 | if (test_bit(EV_KEY, dev->evbit) && | 903 | if (test_bit(EV_KEY, dev->evbit) && |
| 832 | !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && | 904 | !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && |
| 833 | __test_and_clear_bit(old_keycode, dev->key)) { | 905 | __test_and_clear_bit(old_keycode, dev->key)) { |
| 906 | struct input_value vals[] = { | ||
| 907 | { EV_KEY, old_keycode, 0 }, | ||
| 908 | input_value_sync | ||
| 909 | }; | ||
| 834 | 910 | ||
| 835 | input_pass_event(dev, EV_KEY, old_keycode, 0); | 911 | input_pass_values(dev, vals, ARRAY_SIZE(vals)); |
| 836 | if (dev->sync) | ||
| 837 | input_pass_event(dev, EV_SYN, SYN_REPORT, 1); | ||
| 838 | } | 912 | } |
| 839 | 913 | ||
| 840 | out: | 914 | out: |
| @@ -1416,6 +1490,7 @@ static void input_dev_release(struct device *device) | |||
| 1416 | input_ff_destroy(dev); | 1490 | input_ff_destroy(dev); |
| 1417 | input_mt_destroy_slots(dev); | 1491 | input_mt_destroy_slots(dev); |
| 1418 | kfree(dev->absinfo); | 1492 | kfree(dev->absinfo); |
| 1493 | kfree(dev->vals); | ||
| 1419 | kfree(dev); | 1494 | kfree(dev); |
| 1420 | 1495 | ||
| 1421 | module_put(THIS_MODULE); | 1496 | module_put(THIS_MODULE); |
| @@ -1751,8 +1826,8 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev) | |||
| 1751 | int i; | 1826 | int i; |
| 1752 | unsigned int events; | 1827 | unsigned int events; |
| 1753 | 1828 | ||
| 1754 | if (dev->mtsize) { | 1829 | if (dev->mt) { |
| 1755 | mt_slots = dev->mtsize; | 1830 | mt_slots = dev->mt->num_slots; |
| 1756 | } else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) { | 1831 | } else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) { |
| 1757 | mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum - | 1832 | mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum - |
| 1758 | dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1, | 1833 | dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1, |
| @@ -1778,6 +1853,9 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev) | |||
| 1778 | if (test_bit(i, dev->relbit)) | 1853 | if (test_bit(i, dev->relbit)) |
| 1779 | events++; | 1854 | events++; |
| 1780 | 1855 | ||
| 1856 | /* Make room for KEY and MSC events */ | ||
| 1857 | events += 7; | ||
| 1858 | |||
| 1781 | return events; | 1859 | return events; |
| 1782 | } | 1860 | } |
| 1783 | 1861 | ||
| @@ -1816,6 +1894,7 @@ int input_register_device(struct input_dev *dev) | |||
| 1816 | { | 1894 | { |
| 1817 | static atomic_t input_no = ATOMIC_INIT(0); | 1895 | static atomic_t input_no = ATOMIC_INIT(0); |
| 1818 | struct input_handler *handler; | 1896 | struct input_handler *handler; |
| 1897 | unsigned int packet_size; | ||
| 1819 | const char *path; | 1898 | const char *path; |
| 1820 | int error; | 1899 | int error; |
| 1821 | 1900 | ||
| @@ -1828,9 +1907,14 @@ int input_register_device(struct input_dev *dev) | |||
| 1828 | /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ | 1907 | /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ |
| 1829 | input_cleanse_bitmasks(dev); | 1908 | input_cleanse_bitmasks(dev); |
| 1830 | 1909 | ||
| 1831 | if (!dev->hint_events_per_packet) | 1910 | packet_size = input_estimate_events_per_packet(dev); |
| 1832 | dev->hint_events_per_packet = | 1911 | if (dev->hint_events_per_packet < packet_size) |
| 1833 | input_estimate_events_per_packet(dev); | 1912 | dev->hint_events_per_packet = packet_size; |
| 1913 | |||
| 1914 | dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2; | ||
| 1915 | dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL); | ||
| 1916 | if (!dev->vals) | ||
| 1917 | return -ENOMEM; | ||
| 1834 | 1918 | ||
| 1835 | /* | 1919 | /* |
| 1836 | * If delay and period are pre-set by the driver, then autorepeating | 1920 | * If delay and period are pre-set by the driver, then autorepeating |
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 736056897e50..6b1797503e34 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c | |||
| @@ -405,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu | |||
| 405 | goto exit; | 405 | goto exit; |
| 406 | if (test_bit(ABS_MT_SLOT, dev->absbit)) { | 406 | if (test_bit(ABS_MT_SLOT, dev->absbit)) { |
| 407 | int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; | 407 | int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; |
| 408 | input_mt_init_slots(dev, nslot); | 408 | input_mt_init_slots(dev, nslot, 0); |
| 409 | } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { | 409 | } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { |
| 410 | input_set_events_per_packet(dev, 60); | 410 | input_set_events_per_packet(dev, 60); |
| 411 | } | 411 | } |
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 4a1347e91bdc..cf5af1f495ec 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c | |||
| @@ -1620,7 +1620,7 @@ int alps_init(struct psmouse *psmouse) | |||
| 1620 | case ALPS_PROTO_V3: | 1620 | case ALPS_PROTO_V3: |
| 1621 | case ALPS_PROTO_V4: | 1621 | case ALPS_PROTO_V4: |
| 1622 | set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); | 1622 | set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); |
| 1623 | input_mt_init_slots(dev1, 2); | 1623 | input_mt_init_slots(dev1, 2, 0); |
| 1624 | input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); | 1624 | input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); |
| 1625 | input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); | 1625 | input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); |
| 1626 | 1626 | ||
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index d528c23e194f..3a78f235fa3e 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | #include <linux/usb/input.h> | 40 | #include <linux/usb/input.h> |
| 41 | #include <linux/hid.h> | 41 | #include <linux/hid.h> |
| 42 | #include <linux/mutex.h> | 42 | #include <linux/mutex.h> |
| 43 | #include <linux/input/mt.h> | ||
| 43 | 44 | ||
| 44 | #define USB_VENDOR_ID_APPLE 0x05ac | 45 | #define USB_VENDOR_ID_APPLE 0x05ac |
| 45 | 46 | ||
| @@ -183,26 +184,26 @@ struct tp_finger { | |||
| 183 | __le16 abs_y; /* absolute y coodinate */ | 184 | __le16 abs_y; /* absolute y coodinate */ |
| 184 | __le16 rel_x; /* relative x coodinate */ | 185 | __le16 rel_x; /* relative x coodinate */ |
| 185 | __le16 rel_y; /* relative y coodinate */ | 186 | __le16 rel_y; /* relative y coodinate */ |
| 186 | __le16 size_major; /* finger size, major axis? */ | 187 | __le16 tool_major; /* tool area, major axis */ |
| 187 | __le16 size_minor; /* finger size, minor axis? */ | 188 | __le16 tool_minor; /* tool area, minor axis */ |
| 188 | __le16 orientation; /* 16384 when point, else 15 bit angle */ | 189 | __le16 orientation; /* 16384 when point, else 15 bit angle */ |
| 189 | __le16 force_major; /* trackpad force, major axis? */ | 190 | __le16 touch_major; /* touch area, major axis */ |
| 190 | __le16 force_minor; /* trackpad force, minor axis? */ | 191 | __le16 touch_minor; /* touch area, minor axis */ |
| 191 | __le16 unused[3]; /* zeros */ | 192 | __le16 unused[3]; /* zeros */ |
| 192 | __le16 multi; /* one finger: varies, more fingers: constant */ | 193 | __le16 multi; /* one finger: varies, more fingers: constant */ |
| 193 | } __attribute__((packed,aligned(2))); | 194 | } __attribute__((packed,aligned(2))); |
| 194 | 195 | ||
| 195 | /* trackpad finger data size, empirically at least ten fingers */ | 196 | /* trackpad finger data size, empirically at least ten fingers */ |
| 197 | #define MAX_FINGERS 16 | ||
| 196 | #define SIZEOF_FINGER sizeof(struct tp_finger) | 198 | #define SIZEOF_FINGER sizeof(struct tp_finger) |
| 197 | #define SIZEOF_ALL_FINGERS (16 * SIZEOF_FINGER) | 199 | #define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER) |
| 198 | #define MAX_FINGER_ORIENTATION 16384 | 200 | #define MAX_FINGER_ORIENTATION 16384 |
| 199 | 201 | ||
| 200 | /* device-specific parameters */ | 202 | /* device-specific parameters */ |
| 201 | struct bcm5974_param { | 203 | struct bcm5974_param { |
| 202 | int dim; /* logical dimension */ | 204 | int snratio; /* signal-to-noise ratio */ |
| 203 | int fuzz; /* logical noise value */ | 205 | int min; /* device minimum reading */ |
| 204 | int devmin; /* device minimum reading */ | 206 | int max; /* device maximum reading */ |
| 205 | int devmax; /* device maximum reading */ | ||
| 206 | }; | 207 | }; |
| 207 | 208 | ||
| 208 | /* device-specific configuration */ | 209 | /* device-specific configuration */ |
| @@ -219,6 +220,7 @@ struct bcm5974_config { | |||
| 219 | struct bcm5974_param w; /* finger width limits */ | 220 | struct bcm5974_param w; /* finger width limits */ |
| 220 | struct bcm5974_param x; /* horizontal limits */ | 221 | struct bcm5974_param x; /* horizontal limits */ |
| 221 | struct bcm5974_param y; /* vertical limits */ | 222 | struct bcm5974_param y; /* vertical limits */ |
| 223 | struct bcm5974_param o; /* orientation limits */ | ||
| 222 | }; | 224 | }; |
| 223 | 225 | ||
| 224 | /* logical device structure */ | 226 | /* logical device structure */ |
| @@ -234,23 +236,16 @@ struct bcm5974 { | |||
| 234 | struct bt_data *bt_data; /* button transferred data */ | 236 | struct bt_data *bt_data; /* button transferred data */ |
| 235 | struct urb *tp_urb; /* trackpad usb request block */ | 237 | struct urb *tp_urb; /* trackpad usb request block */ |
| 236 | u8 *tp_data; /* trackpad transferred data */ | 238 | u8 *tp_data; /* trackpad transferred data */ |
| 237 | int fingers; /* number of fingers on trackpad */ | 239 | const struct tp_finger *index[MAX_FINGERS]; /* finger index data */ |
| 240 | struct input_mt_pos pos[MAX_FINGERS]; /* position array */ | ||
| 241 | int slots[MAX_FINGERS]; /* slot assignments */ | ||
| 238 | }; | 242 | }; |
| 239 | 243 | ||
| 240 | /* logical dimensions */ | ||
| 241 | #define DIM_PRESSURE 256 /* maximum finger pressure */ | ||
| 242 | #define DIM_WIDTH 16 /* maximum finger width */ | ||
| 243 | #define DIM_X 1280 /* maximum trackpad x value */ | ||
| 244 | #define DIM_Y 800 /* maximum trackpad y value */ | ||
| 245 | |||
| 246 | /* logical signal quality */ | 244 | /* logical signal quality */ |
| 247 | #define SN_PRESSURE 45 /* pressure signal-to-noise ratio */ | 245 | #define SN_PRESSURE 45 /* pressure signal-to-noise ratio */ |
| 248 | #define SN_WIDTH 100 /* width signal-to-noise ratio */ | 246 | #define SN_WIDTH 25 /* width signal-to-noise ratio */ |
| 249 | #define SN_COORD 250 /* coordinate signal-to-noise ratio */ | 247 | #define SN_COORD 250 /* coordinate signal-to-noise ratio */ |
| 250 | 248 | #define SN_ORIENT 10 /* orientation signal-to-noise ratio */ | |
| 251 | /* pressure thresholds */ | ||
| 252 | #define PRESSURE_LOW (2 * DIM_PRESSURE / SN_PRESSURE) | ||
| 253 | #define PRESSURE_HIGH (3 * PRESSURE_LOW) | ||
| 254 | 249 | ||
| 255 | /* device constants */ | 250 | /* device constants */ |
| 256 | static const struct bcm5974_config bcm5974_config_table[] = { | 251 | static const struct bcm5974_config bcm5974_config_table[] = { |
| @@ -261,10 +256,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 261 | 0, | 256 | 0, |
| 262 | 0x84, sizeof(struct bt_data), | 257 | 0x84, sizeof(struct bt_data), |
| 263 | 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, | 258 | 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, |
| 264 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 }, | 259 | { SN_PRESSURE, 0, 256 }, |
| 265 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 260 | { SN_WIDTH, 0, 2048 }, |
| 266 | { DIM_X, DIM_X / SN_COORD, -4824, 5342 }, | 261 | { SN_COORD, -4824, 5342 }, |
| 267 | { DIM_Y, DIM_Y / SN_COORD, -172, 5820 } | 262 | { SN_COORD, -172, 5820 }, |
| 263 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 268 | }, | 264 | }, |
| 269 | { | 265 | { |
| 270 | USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, | 266 | USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, |
| @@ -273,10 +269,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 273 | 0, | 269 | 0, |
| 274 | 0x84, sizeof(struct bt_data), | 270 | 0x84, sizeof(struct bt_data), |
| 275 | 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, | 271 | 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, |
| 276 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 }, | 272 | { SN_PRESSURE, 0, 256 }, |
| 277 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 273 | { SN_WIDTH, 0, 2048 }, |
| 278 | { DIM_X, DIM_X / SN_COORD, -4824, 4824 }, | 274 | { SN_COORD, -4824, 4824 }, |
| 279 | { DIM_Y, DIM_Y / SN_COORD, -172, 4290 } | 275 | { SN_COORD, -172, 4290 }, |
| 276 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 280 | }, | 277 | }, |
| 281 | { | 278 | { |
| 282 | USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI, | 279 | USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI, |
| @@ -285,10 +282,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 285 | HAS_INTEGRATED_BUTTON, | 282 | HAS_INTEGRATED_BUTTON, |
| 286 | 0x84, sizeof(struct bt_data), | 283 | 0x84, sizeof(struct bt_data), |
| 287 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 284 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 288 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 285 | { SN_PRESSURE, 0, 300 }, |
| 289 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 286 | { SN_WIDTH, 0, 2048 }, |
| 290 | { DIM_X, DIM_X / SN_COORD, -4460, 5166 }, | 287 | { SN_COORD, -4460, 5166 }, |
| 291 | { DIM_Y, DIM_Y / SN_COORD, -75, 6700 } | 288 | { SN_COORD, -75, 6700 }, |
| 289 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 292 | }, | 290 | }, |
| 293 | { | 291 | { |
| 294 | USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI, | 292 | USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI, |
| @@ -297,10 +295,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 297 | HAS_INTEGRATED_BUTTON, | 295 | HAS_INTEGRATED_BUTTON, |
| 298 | 0x84, sizeof(struct bt_data), | 296 | 0x84, sizeof(struct bt_data), |
| 299 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 297 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 300 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 298 | { SN_PRESSURE, 0, 300 }, |
| 301 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 299 | { SN_WIDTH, 0, 2048 }, |
| 302 | { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, | 300 | { SN_COORD, -4620, 5140 }, |
| 303 | { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } | 301 | { SN_COORD, -150, 6600 }, |
| 302 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 304 | }, | 303 | }, |
| 305 | { | 304 | { |
| 306 | USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI, | 305 | USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI, |
| @@ -309,10 +308,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 309 | HAS_INTEGRATED_BUTTON, | 308 | HAS_INTEGRATED_BUTTON, |
| 310 | 0x84, sizeof(struct bt_data), | 309 | 0x84, sizeof(struct bt_data), |
| 311 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 310 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 312 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 311 | { SN_PRESSURE, 0, 300 }, |
| 313 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 312 | { SN_WIDTH, 0, 2048 }, |
| 314 | { DIM_X, DIM_X / SN_COORD, -4616, 5112 }, | 313 | { SN_COORD, -4616, 5112 }, |
| 315 | { DIM_Y, DIM_Y / SN_COORD, -142, 5234 } | 314 | { SN_COORD, -142, 5234 }, |
| 315 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 316 | }, | 316 | }, |
| 317 | { | 317 | { |
| 318 | USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, | 318 | USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, |
| @@ -321,10 +321,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 321 | HAS_INTEGRATED_BUTTON, | 321 | HAS_INTEGRATED_BUTTON, |
| 322 | 0x84, sizeof(struct bt_data), | 322 | 0x84, sizeof(struct bt_data), |
| 323 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 323 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 324 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 324 | { SN_PRESSURE, 0, 300 }, |
| 325 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 325 | { SN_WIDTH, 0, 2048 }, |
| 326 | { DIM_X, DIM_X / SN_COORD, -4415, 5050 }, | 326 | { SN_COORD, -4415, 5050 }, |
| 327 | { DIM_Y, DIM_Y / SN_COORD, -55, 6680 } | 327 | { SN_COORD, -55, 6680 }, |
| 328 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 328 | }, | 329 | }, |
| 329 | { | 330 | { |
| 330 | USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI, | 331 | USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI, |
| @@ -333,10 +334,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 333 | HAS_INTEGRATED_BUTTON, | 334 | HAS_INTEGRATED_BUTTON, |
| 334 | 0x84, sizeof(struct bt_data), | 335 | 0x84, sizeof(struct bt_data), |
| 335 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 336 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 336 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 337 | { SN_PRESSURE, 0, 300 }, |
| 337 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 338 | { SN_WIDTH, 0, 2048 }, |
| 338 | { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, | 339 | { SN_COORD, -4620, 5140 }, |
| 339 | { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } | 340 | { SN_COORD, -150, 6600 }, |
| 341 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 340 | }, | 342 | }, |
| 341 | { | 343 | { |
| 342 | USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI, | 344 | USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI, |
| @@ -345,10 +347,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 345 | HAS_INTEGRATED_BUTTON, | 347 | HAS_INTEGRATED_BUTTON, |
| 346 | 0x84, sizeof(struct bt_data), | 348 | 0x84, sizeof(struct bt_data), |
| 347 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 349 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 348 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 350 | { SN_PRESSURE, 0, 300 }, |
| 349 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 351 | { SN_WIDTH, 0, 2048 }, |
| 350 | { DIM_X, DIM_X / SN_COORD, -4750, 5280 }, | 352 | { SN_COORD, -4750, 5280 }, |
| 351 | { DIM_Y, DIM_Y / SN_COORD, -150, 6730 } | 353 | { SN_COORD, -150, 6730 }, |
| 354 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 352 | }, | 355 | }, |
| 353 | { | 356 | { |
| 354 | USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI, | 357 | USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI, |
| @@ -357,10 +360,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 357 | HAS_INTEGRATED_BUTTON, | 360 | HAS_INTEGRATED_BUTTON, |
| 358 | 0x84, sizeof(struct bt_data), | 361 | 0x84, sizeof(struct bt_data), |
| 359 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 362 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 360 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 363 | { SN_PRESSURE, 0, 300 }, |
| 361 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 364 | { SN_WIDTH, 0, 2048 }, |
| 362 | { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, | 365 | { SN_COORD, -4620, 5140 }, |
| 363 | { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } | 366 | { SN_COORD, -150, 6600 }, |
| 367 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 364 | }, | 368 | }, |
| 365 | { | 369 | { |
| 366 | USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI, | 370 | USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI, |
| @@ -369,10 +373,11 @@ static const struct bcm5974_config bcm5974_config_table[] = { | |||
| 369 | HAS_INTEGRATED_BUTTON, | 373 | HAS_INTEGRATED_BUTTON, |
| 370 | 0x84, sizeof(struct bt_data), | 374 | 0x84, sizeof(struct bt_data), |
| 371 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, | 375 | 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, |
| 372 | { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, | 376 | { SN_PRESSURE, 0, 300 }, |
| 373 | { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, | 377 | { SN_WIDTH, 0, 2048 }, |
| 374 | { DIM_X, DIM_X / SN_COORD, -4750, 5280 }, | 378 | { SN_COORD, -4750, 5280 }, |
| 375 | { DIM_Y, DIM_Y / SN_COORD, -150, 6730 } | 379 | { SN_COORD, -150, 6730 }, |
| 380 | { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } | ||
| 376 | }, | 381 | }, |
| 377 | {} | 382 | {} |
| 378 | }; | 383 | }; |
| @@ -396,18 +401,11 @@ static inline int raw2int(__le16 x) | |||
| 396 | return (signed short)le16_to_cpu(x); | 401 | return (signed short)le16_to_cpu(x); |
| 397 | } | 402 | } |
| 398 | 403 | ||
| 399 | /* scale device data to logical dimensions (asserts devmin < devmax) */ | 404 | static void set_abs(struct input_dev *input, unsigned int code, |
| 400 | static inline int int2scale(const struct bcm5974_param *p, int x) | 405 | const struct bcm5974_param *p) |
| 401 | { | ||
| 402 | return x * p->dim / (p->devmax - p->devmin); | ||
| 403 | } | ||
| 404 | |||
| 405 | /* all logical value ranges are [0,dim). */ | ||
| 406 | static inline int int2bound(const struct bcm5974_param *p, int x) | ||
| 407 | { | 406 | { |
| 408 | int s = int2scale(p, x); | 407 | int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0; |
| 409 | 408 | input_set_abs_params(input, code, p->min, p->max, fuzz, 0); | |
| 410 | return clamp_val(s, 0, p->dim - 1); | ||
| 411 | } | 409 | } |
| 412 | 410 | ||
| 413 | /* setup which logical events to report */ | 411 | /* setup which logical events to report */ |
| @@ -416,48 +414,30 @@ static void setup_events_to_report(struct input_dev *input_dev, | |||
| 416 | { | 414 | { |
| 417 | __set_bit(EV_ABS, input_dev->evbit); | 415 | __set_bit(EV_ABS, input_dev->evbit); |
| 418 | 416 | ||
| 419 | input_set_abs_params(input_dev, ABS_PRESSURE, | 417 | /* for synaptics only */ |
| 420 | 0, cfg->p.dim, cfg->p.fuzz, 0); | 418 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0); |
| 421 | input_set_abs_params(input_dev, ABS_TOOL_WIDTH, | 419 | input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0); |
| 422 | 0, cfg->w.dim, cfg->w.fuzz, 0); | ||
| 423 | input_set_abs_params(input_dev, ABS_X, | ||
| 424 | 0, cfg->x.dim, cfg->x.fuzz, 0); | ||
| 425 | input_set_abs_params(input_dev, ABS_Y, | ||
| 426 | 0, cfg->y.dim, cfg->y.fuzz, 0); | ||
| 427 | 420 | ||
| 428 | /* finger touch area */ | 421 | /* finger touch area */ |
| 429 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | 422 | set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w); |
| 430 | cfg->w.devmin, cfg->w.devmax, 0, 0); | 423 | set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w); |
| 431 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, | ||
| 432 | cfg->w.devmin, cfg->w.devmax, 0, 0); | ||
| 433 | /* finger approach area */ | 424 | /* finger approach area */ |
| 434 | input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, | 425 | set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w); |
| 435 | cfg->w.devmin, cfg->w.devmax, 0, 0); | 426 | set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w); |
| 436 | input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, | ||
| 437 | cfg->w.devmin, cfg->w.devmax, 0, 0); | ||
| 438 | /* finger orientation */ | 427 | /* finger orientation */ |
| 439 | input_set_abs_params(input_dev, ABS_MT_ORIENTATION, | 428 | set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o); |
| 440 | -MAX_FINGER_ORIENTATION, | ||
| 441 | MAX_FINGER_ORIENTATION, 0, 0); | ||
| 442 | /* finger position */ | 429 | /* finger position */ |
| 443 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, | 430 | set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x); |
| 444 | cfg->x.devmin, cfg->x.devmax, 0, 0); | 431 | set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y); |
| 445 | input_set_abs_params(input_dev, ABS_MT_POSITION_Y, | ||
| 446 | cfg->y.devmin, cfg->y.devmax, 0, 0); | ||
| 447 | 432 | ||
| 448 | __set_bit(EV_KEY, input_dev->evbit); | 433 | __set_bit(EV_KEY, input_dev->evbit); |
| 449 | __set_bit(BTN_TOUCH, input_dev->keybit); | ||
| 450 | __set_bit(BTN_TOOL_FINGER, input_dev->keybit); | ||
| 451 | __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); | ||
| 452 | __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); | ||
| 453 | __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); | ||
| 454 | __set_bit(BTN_LEFT, input_dev->keybit); | 434 | __set_bit(BTN_LEFT, input_dev->keybit); |
| 455 | 435 | ||
| 456 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 457 | if (cfg->caps & HAS_INTEGRATED_BUTTON) | 436 | if (cfg->caps & HAS_INTEGRATED_BUTTON) |
| 458 | __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); | 437 | __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); |
| 459 | 438 | ||
| 460 | input_set_events_per_packet(input_dev, 60); | 439 | input_mt_init_slots(input_dev, MAX_FINGERS, |
| 440 | INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK); | ||
| 461 | } | 441 | } |
| 462 | 442 | ||
| 463 | /* report button data as logical button state */ | 443 | /* report button data as logical button state */ |
| @@ -477,24 +457,44 @@ static int report_bt_state(struct bcm5974 *dev, int size) | |||
| 477 | return 0; | 457 | return 0; |
| 478 | } | 458 | } |
| 479 | 459 | ||
| 480 | static void report_finger_data(struct input_dev *input, | 460 | static void report_finger_data(struct input_dev *input, int slot, |
| 481 | const struct bcm5974_config *cfg, | 461 | const struct input_mt_pos *pos, |
| 482 | const struct tp_finger *f) | 462 | const struct tp_finger *f) |
| 483 | { | 463 | { |
| 464 | input_mt_slot(input, slot); | ||
| 465 | input_mt_report_slot_state(input, MT_TOOL_FINGER, true); | ||
| 466 | |||
| 484 | input_report_abs(input, ABS_MT_TOUCH_MAJOR, | 467 | input_report_abs(input, ABS_MT_TOUCH_MAJOR, |
| 485 | raw2int(f->force_major) << 1); | 468 | raw2int(f->touch_major) << 1); |
| 486 | input_report_abs(input, ABS_MT_TOUCH_MINOR, | 469 | input_report_abs(input, ABS_MT_TOUCH_MINOR, |
| 487 | raw2int(f->force_minor) << 1); | 470 | raw2int(f->touch_minor) << 1); |
| 488 | input_report_abs(input, ABS_MT_WIDTH_MAJOR, | 471 | input_report_abs(input, ABS_MT_WIDTH_MAJOR, |
| 489 | raw2int(f->size_major) << 1); | 472 | raw2int(f->tool_major) << 1); |
| 490 | input_report_abs(input, ABS_MT_WIDTH_MINOR, | 473 | input_report_abs(input, ABS_MT_WIDTH_MINOR, |
| 491 | raw2int(f->size_minor) << 1); | 474 | raw2int(f->tool_minor) << 1); |
| 492 | input_report_abs(input, ABS_MT_ORIENTATION, | 475 | input_report_abs(input, ABS_MT_ORIENTATION, |
| 493 | MAX_FINGER_ORIENTATION - raw2int(f->orientation)); | 476 | MAX_FINGER_ORIENTATION - raw2int(f->orientation)); |
| 494 | input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x)); | 477 | input_report_abs(input, ABS_MT_POSITION_X, pos->x); |
| 495 | input_report_abs(input, ABS_MT_POSITION_Y, | 478 | input_report_abs(input, ABS_MT_POSITION_Y, pos->y); |
| 496 | cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y)); | 479 | } |
| 497 | input_mt_sync(input); | 480 | |
| 481 | static void report_synaptics_data(struct input_dev *input, | ||
| 482 | const struct bcm5974_config *cfg, | ||
| 483 | const struct tp_finger *f, int raw_n) | ||
| 484 | { | ||
| 485 | int abs_p = 0, abs_w = 0; | ||
| 486 | |||
| 487 | if (raw_n) { | ||
| 488 | int p = raw2int(f->touch_major); | ||
| 489 | int w = raw2int(f->tool_major); | ||
| 490 | if (p > 0 && raw2int(f->origin)) { | ||
| 491 | abs_p = clamp_val(256 * p / cfg->p.max, 0, 255); | ||
| 492 | abs_w = clamp_val(16 * w / cfg->w.max, 0, 15); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | input_report_abs(input, ABS_PRESSURE, abs_p); | ||
| 497 | input_report_abs(input, ABS_TOOL_WIDTH, abs_w); | ||
| 498 | } | 498 | } |
| 499 | 499 | ||
| 500 | /* report trackpad data as logical trackpad state */ | 500 | /* report trackpad data as logical trackpad state */ |
| @@ -503,9 +503,7 @@ static int report_tp_state(struct bcm5974 *dev, int size) | |||
| 503 | const struct bcm5974_config *c = &dev->cfg; | 503 | const struct bcm5974_config *c = &dev->cfg; |
| 504 | const struct tp_finger *f; | 504 | const struct tp_finger *f; |
| 505 | struct input_dev *input = dev->input; | 505 | struct input_dev *input = dev->input; |
| 506 | int raw_p, raw_w, raw_x, raw_y, raw_n, i; | 506 | int raw_n, i, n = 0; |
| 507 | int ptest, origin, ibt = 0, nmin = 0, nmax = 0; | ||
| 508 | int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0; | ||
| 509 | 507 | ||
| 510 | if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0) | 508 | if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0) |
| 511 | return -EIO; | 509 | return -EIO; |
| @@ -514,76 +512,29 @@ static int report_tp_state(struct bcm5974 *dev, int size) | |||
| 514 | f = (const struct tp_finger *)(dev->tp_data + c->tp_offset); | 512 | f = (const struct tp_finger *)(dev->tp_data + c->tp_offset); |
| 515 | raw_n = (size - c->tp_offset) / SIZEOF_FINGER; | 513 | raw_n = (size - c->tp_offset) / SIZEOF_FINGER; |
| 516 | 514 | ||
| 517 | /* always track the first finger; when detached, start over */ | 515 | for (i = 0; i < raw_n; i++) { |
| 518 | if (raw_n) { | 516 | if (raw2int(f[i].touch_major) == 0) |
| 519 | 517 | continue; | |
| 520 | /* report raw trackpad data */ | 518 | dev->pos[n].x = raw2int(f[i].abs_x); |
| 521 | for (i = 0; i < raw_n; i++) | 519 | dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y); |
| 522 | report_finger_data(input, c, &f[i]); | 520 | dev->index[n++] = &f[i]; |
| 523 | |||
| 524 | raw_p = raw2int(f->force_major); | ||
| 525 | raw_w = raw2int(f->size_major); | ||
| 526 | raw_x = raw2int(f->abs_x); | ||
| 527 | raw_y = raw2int(f->abs_y); | ||
| 528 | |||
| 529 | dprintk(9, | ||
| 530 | "bcm5974: " | ||
| 531 | "raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n", | ||
| 532 | raw_p, raw_w, raw_x, raw_y, raw_n); | ||
| 533 | |||
| 534 | ptest = int2bound(&c->p, raw_p); | ||
| 535 | origin = raw2int(f->origin); | ||
| 536 | |||
| 537 | /* while tracking finger still valid, count all fingers */ | ||
| 538 | if (ptest > PRESSURE_LOW && origin) { | ||
| 539 | abs_p = ptest; | ||
| 540 | abs_w = int2bound(&c->w, raw_w); | ||
| 541 | abs_x = int2bound(&c->x, raw_x - c->x.devmin); | ||
| 542 | abs_y = int2bound(&c->y, c->y.devmax - raw_y); | ||
| 543 | while (raw_n--) { | ||
| 544 | ptest = int2bound(&c->p, | ||
| 545 | raw2int(f->force_major)); | ||
| 546 | if (ptest > PRESSURE_LOW) | ||
| 547 | nmax++; | ||
| 548 | if (ptest > PRESSURE_HIGH) | ||
| 549 | nmin++; | ||
| 550 | f++; | ||
| 551 | } | ||
| 552 | } | ||
| 553 | } | 521 | } |
| 554 | 522 | ||
| 555 | /* set the integrated button if applicable */ | 523 | input_mt_assign_slots(input, dev->slots, dev->pos, n); |
| 556 | if (c->tp_type == TYPE2) | ||
| 557 | ibt = raw2int(dev->tp_data[BUTTON_TYPE2]); | ||
| 558 | |||
| 559 | if (dev->fingers < nmin) | ||
| 560 | dev->fingers = nmin; | ||
| 561 | if (dev->fingers > nmax) | ||
| 562 | dev->fingers = nmax; | ||
| 563 | |||
| 564 | input_report_key(input, BTN_TOUCH, dev->fingers > 0); | ||
| 565 | input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1); | ||
| 566 | input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2); | ||
| 567 | input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3); | ||
| 568 | input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3); | ||
| 569 | |||
| 570 | input_report_abs(input, ABS_PRESSURE, abs_p); | ||
| 571 | input_report_abs(input, ABS_TOOL_WIDTH, abs_w); | ||
| 572 | 524 | ||
| 573 | if (abs_p) { | 525 | for (i = 0; i < n; i++) |
| 574 | input_report_abs(input, ABS_X, abs_x); | 526 | report_finger_data(input, dev->slots[i], |
| 575 | input_report_abs(input, ABS_Y, abs_y); | 527 | &dev->pos[i], dev->index[i]); |
| 576 | 528 | ||
| 577 | dprintk(8, | 529 | input_mt_sync_frame(input); |
| 578 | "bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d " | ||
| 579 | "nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w, | ||
| 580 | abs_x, abs_y, nmin, nmax, dev->fingers, ibt); | ||
| 581 | 530 | ||
| 582 | } | 531 | report_synaptics_data(input, c, f, raw_n); |
| 583 | 532 | ||
| 584 | /* type 2 reports button events via ibt only */ | 533 | /* type 2 reports button events via ibt only */ |
| 585 | if (c->tp_type == TYPE2) | 534 | if (c->tp_type == TYPE2) { |
| 535 | int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]); | ||
| 586 | input_report_key(input, BTN_LEFT, ibt); | 536 | input_report_key(input, BTN_LEFT, ibt); |
| 537 | } | ||
| 587 | 538 | ||
| 588 | input_sync(input); | 539 | input_sync(input); |
| 589 | 540 | ||
| @@ -742,9 +693,11 @@ static int bcm5974_start_traffic(struct bcm5974 *dev) | |||
| 742 | goto err_out; | 693 | goto err_out; |
| 743 | } | 694 | } |
| 744 | 695 | ||
| 745 | error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); | 696 | if (dev->bt_urb) { |
| 746 | if (error) | 697 | error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); |
| 747 | goto err_reset_mode; | 698 | if (error) |
| 699 | goto err_reset_mode; | ||
| 700 | } | ||
| 748 | 701 | ||
| 749 | error = usb_submit_urb(dev->tp_urb, GFP_KERNEL); | 702 | error = usb_submit_urb(dev->tp_urb, GFP_KERNEL); |
| 750 | if (error) | 703 | if (error) |
| @@ -868,19 +821,23 @@ static int bcm5974_probe(struct usb_interface *iface, | |||
| 868 | mutex_init(&dev->pm_mutex); | 821 | mutex_init(&dev->pm_mutex); |
| 869 | 822 | ||
| 870 | /* setup urbs */ | 823 | /* setup urbs */ |
| 871 | dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); | 824 | if (cfg->tp_type == TYPE1) { |
| 872 | if (!dev->bt_urb) | 825 | dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); |
| 873 | goto err_free_devs; | 826 | if (!dev->bt_urb) |
| 827 | goto err_free_devs; | ||
| 828 | } | ||
| 874 | 829 | ||
| 875 | dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL); | 830 | dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL); |
| 876 | if (!dev->tp_urb) | 831 | if (!dev->tp_urb) |
| 877 | goto err_free_bt_urb; | 832 | goto err_free_bt_urb; |
| 878 | 833 | ||
| 879 | dev->bt_data = usb_alloc_coherent(dev->udev, | 834 | if (dev->bt_urb) { |
| 835 | dev->bt_data = usb_alloc_coherent(dev->udev, | ||
| 880 | dev->cfg.bt_datalen, GFP_KERNEL, | 836 | dev->cfg.bt_datalen, GFP_KERNEL, |
| 881 | &dev->bt_urb->transfer_dma); | 837 | &dev->bt_urb->transfer_dma); |
| 882 | if (!dev->bt_data) | 838 | if (!dev->bt_data) |
| 883 | goto err_free_urb; | 839 | goto err_free_urb; |
| 840 | } | ||
| 884 | 841 | ||
| 885 | dev->tp_data = usb_alloc_coherent(dev->udev, | 842 | dev->tp_data = usb_alloc_coherent(dev->udev, |
| 886 | dev->cfg.tp_datalen, GFP_KERNEL, | 843 | dev->cfg.tp_datalen, GFP_KERNEL, |
| @@ -888,10 +845,11 @@ static int bcm5974_probe(struct usb_interface *iface, | |||
| 888 | if (!dev->tp_data) | 845 | if (!dev->tp_data) |
| 889 | goto err_free_bt_buffer; | 846 | goto err_free_bt_buffer; |
| 890 | 847 | ||
| 891 | usb_fill_int_urb(dev->bt_urb, udev, | 848 | if (dev->bt_urb) |
| 892 | usb_rcvintpipe(udev, cfg->bt_ep), | 849 | usb_fill_int_urb(dev->bt_urb, udev, |
| 893 | dev->bt_data, dev->cfg.bt_datalen, | 850 | usb_rcvintpipe(udev, cfg->bt_ep), |
| 894 | bcm5974_irq_button, dev, 1); | 851 | dev->bt_data, dev->cfg.bt_datalen, |
| 852 | bcm5974_irq_button, dev, 1); | ||
| 895 | 853 | ||
| 896 | usb_fill_int_urb(dev->tp_urb, udev, | 854 | usb_fill_int_urb(dev->tp_urb, udev, |
| 897 | usb_rcvintpipe(udev, cfg->tp_ep), | 855 | usb_rcvintpipe(udev, cfg->tp_ep), |
| @@ -929,8 +887,9 @@ err_free_buffer: | |||
| 929 | usb_free_coherent(dev->udev, dev->cfg.tp_datalen, | 887 | usb_free_coherent(dev->udev, dev->cfg.tp_datalen, |
| 930 | dev->tp_data, dev->tp_urb->transfer_dma); | 888 | dev->tp_data, dev->tp_urb->transfer_dma); |
| 931 | err_free_bt_buffer: | 889 | err_free_bt_buffer: |
| 932 | usb_free_coherent(dev->udev, dev->cfg.bt_datalen, | 890 | if (dev->bt_urb) |
| 933 | dev->bt_data, dev->bt_urb->transfer_dma); | 891 | usb_free_coherent(dev->udev, dev->cfg.bt_datalen, |
| 892 | dev->bt_data, dev->bt_urb->transfer_dma); | ||
| 934 | err_free_urb: | 893 | err_free_urb: |
| 935 | usb_free_urb(dev->tp_urb); | 894 | usb_free_urb(dev->tp_urb); |
| 936 | err_free_bt_urb: | 895 | err_free_bt_urb: |
| @@ -951,8 +910,9 @@ static void bcm5974_disconnect(struct usb_interface *iface) | |||
| 951 | input_unregister_device(dev->input); | 910 | input_unregister_device(dev->input); |
| 952 | usb_free_coherent(dev->udev, dev->cfg.tp_datalen, | 911 | usb_free_coherent(dev->udev, dev->cfg.tp_datalen, |
| 953 | dev->tp_data, dev->tp_urb->transfer_dma); | 912 | dev->tp_data, dev->tp_urb->transfer_dma); |
| 954 | usb_free_coherent(dev->udev, dev->cfg.bt_datalen, | 913 | if (dev->bt_urb) |
| 955 | dev->bt_data, dev->bt_urb->transfer_dma); | 914 | usb_free_coherent(dev->udev, dev->cfg.bt_datalen, |
| 915 | dev->bt_data, dev->bt_urb->transfer_dma); | ||
| 956 | usb_free_urb(dev->tp_urb); | 916 | usb_free_urb(dev->tp_urb); |
| 957 | usb_free_urb(dev->bt_urb); | 917 | usb_free_urb(dev->bt_urb); |
| 958 | kfree(dev); | 918 | kfree(dev); |
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 479011004a11..1e8e42fb03a4 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c | |||
| @@ -1004,7 +1004,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) | |||
| 1004 | input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, | 1004 | input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, |
| 1005 | ETP_WMAX_V2, 0, 0); | 1005 | ETP_WMAX_V2, 0, 0); |
| 1006 | } | 1006 | } |
| 1007 | input_mt_init_slots(dev, 2); | 1007 | input_mt_init_slots(dev, 2, 0); |
| 1008 | input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); | 1008 | input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); |
| 1009 | input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); | 1009 | input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); |
| 1010 | break; | 1010 | break; |
| @@ -1035,7 +1035,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) | |||
| 1035 | input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, | 1035 | input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, |
| 1036 | ETP_WMAX_V2, 0, 0); | 1036 | ETP_WMAX_V2, 0, 0); |
| 1037 | /* Multitouch capable pad, up to 5 fingers. */ | 1037 | /* Multitouch capable pad, up to 5 fingers. */ |
| 1038 | input_mt_init_slots(dev, ETP_MAX_FINGERS); | 1038 | input_mt_init_slots(dev, ETP_MAX_FINGERS, 0); |
| 1039 | input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); | 1039 | input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); |
| 1040 | input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); | 1040 | input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); |
| 1041 | input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); | 1041 | input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); |
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index a261d8576919..e582922bacf7 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c | |||
| @@ -971,7 +971,7 @@ static int fsp_set_input_params(struct psmouse *psmouse) | |||
| 971 | 971 | ||
| 972 | input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0); | 972 | input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0); |
| 973 | input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0); | 973 | input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0); |
| 974 | input_mt_init_slots(dev, 2); | 974 | input_mt_init_slots(dev, 2, 0); |
| 975 | input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0); | 975 | input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0); |
| 976 | input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0); | 976 | input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0); |
| 977 | } | 977 | } |
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 14eaecea2b70..37033ade79d3 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c | |||
| @@ -1232,7 +1232,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | |||
| 1232 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); | 1232 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); |
| 1233 | 1233 | ||
| 1234 | if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { | 1234 | if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { |
| 1235 | input_mt_init_slots(dev, 2); | 1235 | input_mt_init_slots(dev, 2, 0); |
| 1236 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, | 1236 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, |
| 1237 | ABS_MT_POSITION_Y); | 1237 | ABS_MT_POSITION_Y); |
| 1238 | /* Image sensors can report per-contact pressure */ | 1238 | /* Image sensors can report per-contact pressure */ |
| @@ -1244,7 +1244,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | |||
| 1244 | } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { | 1244 | } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { |
| 1245 | /* Non-image sensors with AGM use semi-mt */ | 1245 | /* Non-image sensors with AGM use semi-mt */ |
| 1246 | __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); | 1246 | __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); |
| 1247 | input_mt_init_slots(dev, 2); | 1247 | input_mt_init_slots(dev, 2, 0); |
| 1248 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, | 1248 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, |
| 1249 | ABS_MT_POSITION_Y); | 1249 | ABS_MT_POSITION_Y); |
| 1250 | } | 1250 | } |
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 532d067a9e07..2a81ce375f75 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c | |||
| @@ -1530,7 +1530,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, | |||
| 1530 | __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); | 1530 | __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); |
| 1531 | __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); | 1531 | __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); |
| 1532 | 1532 | ||
| 1533 | input_mt_init_slots(input_dev, features->touch_max); | 1533 | input_mt_init_slots(input_dev, features->touch_max, 0); |
| 1534 | 1534 | ||
| 1535 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | 1535 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, |
| 1536 | 0, 255, 0, 0); | 1536 | 0, 255, 0, 0); |
| @@ -1575,7 +1575,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, | |||
| 1575 | 1575 | ||
| 1576 | case TABLETPC2FG: | 1576 | case TABLETPC2FG: |
| 1577 | if (features->device_type == BTN_TOOL_FINGER) { | 1577 | if (features->device_type == BTN_TOOL_FINGER) { |
| 1578 | input_mt_init_slots(input_dev, features->touch_max); | 1578 | input_mt_init_slots(input_dev, features->touch_max, 0); |
| 1579 | input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, | 1579 | input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, |
| 1580 | 0, MT_TOOL_MAX, 0, 0); | 1580 | 0, MT_TOOL_MAX, 0, 0); |
| 1581 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, | 1581 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, |
| @@ -1631,7 +1631,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, | |||
| 1631 | 1631 | ||
| 1632 | __set_bit(BTN_TOOL_FINGER, input_dev->keybit); | 1632 | __set_bit(BTN_TOOL_FINGER, input_dev->keybit); |
| 1633 | __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); | 1633 | __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); |
| 1634 | input_mt_init_slots(input_dev, features->touch_max); | 1634 | input_mt_init_slots(input_dev, features->touch_max, 0); |
| 1635 | 1635 | ||
| 1636 | if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { | 1636 | if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { |
| 1637 | __set_bit(BTN_TOOL_TRIPLETAP, | 1637 | __set_bit(BTN_TOOL_TRIPLETAP, |
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4623cc69fc60..e92615d0b1b0 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c | |||
| @@ -1152,7 +1152,7 @@ static int __devinit mxt_probe(struct i2c_client *client, | |||
| 1152 | 1152 | ||
| 1153 | /* For multi touch */ | 1153 | /* For multi touch */ |
| 1154 | num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; | 1154 | num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; |
| 1155 | error = input_mt_init_slots(input_dev, num_mt_slots); | 1155 | error = input_mt_init_slots(input_dev, num_mt_slots, 0); |
| 1156 | if (error) | 1156 | if (error) |
| 1157 | goto err_free_object; | 1157 | goto err_free_object; |
| 1158 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | 1158 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, |
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index f030d9ec795d..8e60437ac85b 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c | |||
| @@ -571,7 +571,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, | |||
| 571 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | 571 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, |
| 572 | 0, CY_MAXZ, 0, 0); | 572 | 0, CY_MAXZ, 0, 0); |
| 573 | 573 | ||
| 574 | input_mt_init_slots(input_dev, CY_MAX_ID); | 574 | input_mt_init_slots(input_dev, CY_MAX_ID, 0); |
| 575 | 575 | ||
| 576 | error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, | 576 | error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, |
| 577 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | 577 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 64957770b522..099d144ab7c9 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c | |||
| @@ -782,7 +782,7 @@ static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client, | |||
| 782 | 0, tsdata->num_x * 64 - 1, 0, 0); | 782 | 0, tsdata->num_x * 64 - 1, 0, 0); |
| 783 | input_set_abs_params(input, ABS_MT_POSITION_Y, | 783 | input_set_abs_params(input, ABS_MT_POSITION_Y, |
| 784 | 0, tsdata->num_y * 64 - 1, 0, 0); | 784 | 0, tsdata->num_y * 64 - 1, 0, 0); |
| 785 | error = input_mt_init_slots(input, MAX_SUPPORT_POINTS); | 785 | error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); |
| 786 | if (error) { | 786 | if (error) { |
| 787 | dev_err(&client->dev, "Unable to init MT slots.\n"); | 787 | dev_err(&client->dev, "Unable to init MT slots.\n"); |
| 788 | goto err_free_mem; | 788 | goto err_free_mem; |
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 70524dd34f42..c1e3460f1195 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c | |||
| @@ -204,7 +204,7 @@ static int __devinit egalax_ts_probe(struct i2c_client *client, | |||
| 204 | ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); | 204 | ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); |
| 205 | input_set_abs_params(input_dev, | 205 | input_set_abs_params(input_dev, |
| 206 | ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0); | 206 | ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0); |
| 207 | input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS); | 207 | input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0); |
| 208 | 208 | ||
| 209 | input_set_drvdata(input_dev, ts); | 209 | input_set_drvdata(input_dev, ts); |
| 210 | 210 | ||
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index c0044175a921..4ac69760ec08 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c | |||
| @@ -252,7 +252,7 @@ static int __devinit ili210x_i2c_probe(struct i2c_client *client, | |||
| 252 | input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); | 252 | input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); |
| 253 | 253 | ||
| 254 | /* Multi touch */ | 254 | /* Multi touch */ |
| 255 | input_mt_init_slots(input, MAX_TOUCHES); | 255 | input_mt_init_slots(input, MAX_TOUCHES, 0); |
| 256 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); | 256 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); |
| 257 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); | 257 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); |
| 258 | 258 | ||
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 49c44bbf548d..560cf09d1c5a 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c | |||
| @@ -404,7 +404,7 @@ static int __devinit mms114_probe(struct i2c_client *client, | |||
| 404 | input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0); | 404 | input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0); |
| 405 | 405 | ||
| 406 | /* For multi touch */ | 406 | /* For multi touch */ |
| 407 | input_mt_init_slots(input_dev, MMS114_MAX_TOUCH); | 407 | input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0); |
| 408 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | 408 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, |
| 409 | 0, MMS114_MAX_AREA, 0, 0); | 409 | 0, MMS114_MAX_AREA, 0, 0); |
| 410 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, | 410 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, |
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c index 4ccde45b9da2..b49f0b836925 100644 --- a/drivers/input/touchscreen/penmount.c +++ b/drivers/input/touchscreen/penmount.c | |||
| @@ -264,7 +264,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv) | |||
| 264 | input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0); | 264 | input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0); |
| 265 | 265 | ||
| 266 | if (pm->maxcontacts > 1) { | 266 | if (pm->maxcontacts > 1) { |
| 267 | input_mt_init_slots(pm->dev, pm->maxcontacts); | 267 | input_mt_init_slots(pm->dev, pm->maxcontacts, 0); |
| 268 | input_set_abs_params(pm->dev, | 268 | input_set_abs_params(pm->dev, |
| 269 | ABS_MT_POSITION_X, 0, max_x, 0, 0); | 269 | ABS_MT_POSITION_X, 0, max_x, 0, 0); |
| 270 | input_set_abs_params(pm->dev, | 270 | input_set_abs_params(pm->dev, |
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 8f9ad2f893b8..9a83be6b6584 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c | |||
| @@ -471,7 +471,7 @@ static int w8001_setup(struct w8001 *w8001) | |||
| 471 | case 5: | 471 | case 5: |
| 472 | w8001->pktlen = W8001_PKTLEN_TOUCH2FG; | 472 | w8001->pktlen = W8001_PKTLEN_TOUCH2FG; |
| 473 | 473 | ||
| 474 | input_mt_init_slots(dev, 2); | 474 | input_mt_init_slots(dev, 2, 0); |
| 475 | input_set_abs_params(dev, ABS_MT_POSITION_X, | 475 | input_set_abs_params(dev, ABS_MT_POSITION_X, |
| 476 | 0, touch.x, 0, 0); | 476 | 0, touch.x, 0, 0); |
| 477 | input_set_abs_params(dev, ABS_MT_POSITION_Y, | 477 | input_set_abs_params(dev, ABS_MT_POSITION_Y, |
diff --git a/include/linux/hid.h b/include/linux/hid.h index 42970de1b40c..7e1f37db7582 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
| @@ -414,7 +414,7 @@ struct hid_field { | |||
| 414 | __u16 dpad; /* dpad input code */ | 414 | __u16 dpad; /* dpad input code */ |
| 415 | }; | 415 | }; |
| 416 | 416 | ||
| 417 | #define HID_MAX_FIELDS 128 | 417 | #define HID_MAX_FIELDS 256 |
| 418 | 418 | ||
| 419 | struct hid_report { | 419 | struct hid_report { |
| 420 | struct list_head list; | 420 | struct list_head list; |
| @@ -626,6 +626,7 @@ struct hid_usage_id { | |||
| 626 | * @report_fixup: called before report descriptor parsing (NULL means nop) | 626 | * @report_fixup: called before report descriptor parsing (NULL means nop) |
| 627 | * @input_mapping: invoked on input registering before mapping an usage | 627 | * @input_mapping: invoked on input registering before mapping an usage |
| 628 | * @input_mapped: invoked on input registering after mapping an usage | 628 | * @input_mapped: invoked on input registering after mapping an usage |
| 629 | * @input_configured: invoked just before the device is registered | ||
| 629 | * @feature_mapping: invoked on feature registering | 630 | * @feature_mapping: invoked on feature registering |
| 630 | * @suspend: invoked on suspend (NULL means nop) | 631 | * @suspend: invoked on suspend (NULL means nop) |
| 631 | * @resume: invoked on resume if device was not reset (NULL means nop) | 632 | * @resume: invoked on resume if device was not reset (NULL means nop) |
| @@ -670,6 +671,8 @@ struct hid_driver { | |||
| 670 | int (*input_mapped)(struct hid_device *hdev, | 671 | int (*input_mapped)(struct hid_device *hdev, |
| 671 | struct hid_input *hidinput, struct hid_field *field, | 672 | struct hid_input *hidinput, struct hid_field *field, |
| 672 | struct hid_usage *usage, unsigned long **bit, int *max); | 673 | struct hid_usage *usage, unsigned long **bit, int *max); |
| 674 | void (*input_configured)(struct hid_device *hdev, | ||
| 675 | struct hid_input *hidinput); | ||
| 673 | void (*feature_mapping)(struct hid_device *hdev, | 676 | void (*feature_mapping)(struct hid_device *hdev, |
| 674 | struct hid_field *field, | 677 | struct hid_field *field, |
| 675 | struct hid_usage *usage); | 678 | struct hid_usage *usage); |
diff --git a/include/linux/input.h b/include/linux/input.h index 725dcd0f63a4..ba4874302939 100644 --- a/include/linux/input.h +++ b/include/linux/input.h | |||
| @@ -1169,6 +1169,18 @@ struct ff_effect { | |||
| 1169 | #include <linux/mod_devicetable.h> | 1169 | #include <linux/mod_devicetable.h> |
| 1170 | 1170 | ||
| 1171 | /** | 1171 | /** |
| 1172 | * struct input_value - input value representation | ||
| 1173 | * @type: type of value (EV_KEY, EV_ABS, etc) | ||
| 1174 | * @code: the value code | ||
| 1175 | * @value: the value | ||
| 1176 | */ | ||
| 1177 | struct input_value { | ||
| 1178 | __u16 type; | ||
| 1179 | __u16 code; | ||
| 1180 | __s32 value; | ||
| 1181 | }; | ||
| 1182 | |||
| 1183 | /** | ||
| 1172 | * struct input_dev - represents an input device | 1184 | * struct input_dev - represents an input device |
| 1173 | * @name: name of the device | 1185 | * @name: name of the device |
| 1174 | * @phys: physical path to the device in the system hierarchy | 1186 | * @phys: physical path to the device in the system hierarchy |
| @@ -1203,11 +1215,7 @@ struct ff_effect { | |||
| 1203 | * software autorepeat | 1215 | * software autorepeat |
| 1204 | * @timer: timer for software autorepeat | 1216 | * @timer: timer for software autorepeat |
| 1205 | * @rep: current values for autorepeat parameters (delay, rate) | 1217 | * @rep: current values for autorepeat parameters (delay, rate) |
| 1206 | * @mt: pointer to array of struct input_mt_slot holding current values | 1218 | * @mt: pointer to multitouch state |
| 1207 | * of tracked contacts | ||
| 1208 | * @mtsize: number of MT slots the device uses | ||
| 1209 | * @slot: MT slot currently being transmitted | ||
| 1210 | * @trkid: stores MT tracking ID for the current contact | ||
| 1211 | * @absinfo: array of &struct input_absinfo elements holding information | 1219 | * @absinfo: array of &struct input_absinfo elements holding information |
| 1212 | * about absolute axes (current value, min, max, flat, fuzz, | 1220 | * about absolute axes (current value, min, max, flat, fuzz, |
| 1213 | * resolution) | 1221 | * resolution) |
| @@ -1244,7 +1252,6 @@ struct ff_effect { | |||
| 1244 | * last user closes the device | 1252 | * last user closes the device |
| 1245 | * @going_away: marks devices that are in a middle of unregistering and | 1253 | * @going_away: marks devices that are in a middle of unregistering and |
| 1246 | * causes input_open_device*() fail with -ENODEV. | 1254 | * causes input_open_device*() fail with -ENODEV. |
| 1247 | * @sync: set to %true when there were no new events since last EV_SYN | ||
| 1248 | * @dev: driver model's view of this device | 1255 | * @dev: driver model's view of this device |
| 1249 | * @h_list: list of input handles associated with the device. When | 1256 | * @h_list: list of input handles associated with the device. When |
| 1250 | * accessing the list dev->mutex must be held | 1257 | * accessing the list dev->mutex must be held |
| @@ -1287,10 +1294,7 @@ struct input_dev { | |||
| 1287 | 1294 | ||
| 1288 | int rep[REP_CNT]; | 1295 | int rep[REP_CNT]; |
| 1289 | 1296 | ||
| 1290 | struct input_mt_slot *mt; | 1297 | struct input_mt *mt; |
| 1291 | int mtsize; | ||
| 1292 | int slot; | ||
| 1293 | int trkid; | ||
| 1294 | 1298 | ||
| 1295 | struct input_absinfo *absinfo; | 1299 | struct input_absinfo *absinfo; |
| 1296 | 1300 | ||
| @@ -1312,12 +1316,14 @@ struct input_dev { | |||
| 1312 | unsigned int users; | 1316 | unsigned int users; |
| 1313 | bool going_away; | 1317 | bool going_away; |
| 1314 | 1318 | ||
| 1315 | bool sync; | ||
| 1316 | |||
| 1317 | struct device dev; | 1319 | struct device dev; |
| 1318 | 1320 | ||
| 1319 | struct list_head h_list; | 1321 | struct list_head h_list; |
| 1320 | struct list_head node; | 1322 | struct list_head node; |
| 1323 | |||
| 1324 | unsigned int num_vals; | ||
| 1325 | unsigned int max_vals; | ||
| 1326 | struct input_value *vals; | ||
| 1321 | }; | 1327 | }; |
| 1322 | #define to_input_dev(d) container_of(d, struct input_dev, dev) | 1328 | #define to_input_dev(d) container_of(d, struct input_dev, dev) |
| 1323 | 1329 | ||
| @@ -1378,6 +1384,9 @@ struct input_handle; | |||
| 1378 | * @event: event handler. This method is being called by input core with | 1384 | * @event: event handler. This method is being called by input core with |
| 1379 | * interrupts disabled and dev->event_lock spinlock held and so | 1385 | * interrupts disabled and dev->event_lock spinlock held and so |
| 1380 | * it may not sleep | 1386 | * it may not sleep |
| 1387 | * @events: event sequence handler. This method is being called by | ||
| 1388 | * input core with interrupts disabled and dev->event_lock | ||
| 1389 | * spinlock held and so it may not sleep | ||
| 1381 | * @filter: similar to @event; separates normal event handlers from | 1390 | * @filter: similar to @event; separates normal event handlers from |
| 1382 | * "filters". | 1391 | * "filters". |
| 1383 | * @match: called after comparing device's id with handler's id_table | 1392 | * @match: called after comparing device's id with handler's id_table |
| @@ -1414,6 +1423,8 @@ struct input_handler { | |||
| 1414 | void *private; | 1423 | void *private; |
| 1415 | 1424 | ||
| 1416 | void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); | 1425 | void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); |
| 1426 | void (*events)(struct input_handle *handle, | ||
| 1427 | const struct input_value *vals, unsigned int count); | ||
| 1417 | bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); | 1428 | bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); |
| 1418 | bool (*match)(struct input_handler *handler, struct input_dev *dev); | 1429 | bool (*match)(struct input_handler *handler, struct input_dev *dev); |
| 1419 | int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); | 1430 | int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); |
diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index f86737586e19..cc5cca774bab 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h | |||
| @@ -15,12 +15,41 @@ | |||
| 15 | 15 | ||
| 16 | #define TRKID_MAX 0xffff | 16 | #define TRKID_MAX 0xffff |
| 17 | 17 | ||
| 18 | #define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */ | ||
| 19 | #define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */ | ||
| 20 | #define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */ | ||
| 21 | #define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */ | ||
| 22 | |||
| 18 | /** | 23 | /** |
| 19 | * struct input_mt_slot - represents the state of an input MT slot | 24 | * struct input_mt_slot - represents the state of an input MT slot |
| 20 | * @abs: holds current values of ABS_MT axes for this slot | 25 | * @abs: holds current values of ABS_MT axes for this slot |
| 26 | * @frame: last frame at which input_mt_report_slot_state() was called | ||
| 27 | * @key: optional driver designation of this slot | ||
| 21 | */ | 28 | */ |
| 22 | struct input_mt_slot { | 29 | struct input_mt_slot { |
| 23 | int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; | 30 | int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; |
| 31 | unsigned int frame; | ||
| 32 | unsigned int key; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * struct input_mt - state of tracked contacts | ||
| 37 | * @trkid: stores MT tracking ID for the next contact | ||
| 38 | * @num_slots: number of MT slots the device uses | ||
| 39 | * @slot: MT slot currently being transmitted | ||
| 40 | * @flags: input_mt operation flags | ||
| 41 | * @frame: increases every time input_mt_sync_frame() is called | ||
| 42 | * @red: reduced cost matrix for in-kernel tracking | ||
| 43 | * @slots: array of slots holding current values of tracked contacts | ||
| 44 | */ | ||
| 45 | struct input_mt { | ||
| 46 | int trkid; | ||
| 47 | int num_slots; | ||
| 48 | int slot; | ||
| 49 | unsigned int flags; | ||
| 50 | unsigned int frame; | ||
| 51 | int *red; | ||
| 52 | struct input_mt_slot slots[]; | ||
| 24 | }; | 53 | }; |
| 25 | 54 | ||
| 26 | static inline void input_mt_set_value(struct input_mt_slot *slot, | 55 | static inline void input_mt_set_value(struct input_mt_slot *slot, |
| @@ -35,12 +64,18 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot, | |||
| 35 | return slot->abs[code - ABS_MT_FIRST]; | 64 | return slot->abs[code - ABS_MT_FIRST]; |
| 36 | } | 65 | } |
| 37 | 66 | ||
| 38 | int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots); | 67 | static inline bool input_mt_is_active(const struct input_mt_slot *slot) |
| 68 | { | ||
| 69 | return input_mt_get_value(slot, ABS_MT_TRACKING_ID) >= 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, | ||
| 73 | unsigned int flags); | ||
| 39 | void input_mt_destroy_slots(struct input_dev *dev); | 74 | void input_mt_destroy_slots(struct input_dev *dev); |
| 40 | 75 | ||
| 41 | static inline int input_mt_new_trkid(struct input_dev *dev) | 76 | static inline int input_mt_new_trkid(struct input_mt *mt) |
| 42 | { | 77 | { |
| 43 | return dev->trkid++ & TRKID_MAX; | 78 | return mt->trkid++ & TRKID_MAX; |
| 44 | } | 79 | } |
| 45 | 80 | ||
| 46 | static inline void input_mt_slot(struct input_dev *dev, int slot) | 81 | static inline void input_mt_slot(struct input_dev *dev, int slot) |
| @@ -64,4 +99,20 @@ void input_mt_report_slot_state(struct input_dev *dev, | |||
| 64 | void input_mt_report_finger_count(struct input_dev *dev, int count); | 99 | void input_mt_report_finger_count(struct input_dev *dev, int count); |
| 65 | void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count); | 100 | void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count); |
| 66 | 101 | ||
| 102 | void input_mt_sync_frame(struct input_dev *dev); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * struct input_mt_pos - contact position | ||
| 106 | * @x: horizontal coordinate | ||
| 107 | * @y: vertical coordinate | ||
| 108 | */ | ||
| 109 | struct input_mt_pos { | ||
| 110 | s16 x, y; | ||
| 111 | }; | ||
| 112 | |||
| 113 | int input_mt_assign_slots(struct input_dev *dev, int *slots, | ||
| 114 | const struct input_mt_pos *pos, int num_pos); | ||
| 115 | |||
| 116 | int input_mt_get_slot_by_key(struct input_dev *dev, int key); | ||
| 117 | |||
| 67 | #endif | 118 | #endif |
