diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 21 | ||||
-rw-r--r-- | drivers/hid/Makefile | 21 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 11 | ||||
-rw-r--r-- | drivers/hid/hid-magicmouse.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 185 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd.c | 2748 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd.h | 309 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_backlight.c | 122 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_cir.c | 152 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_core.c | 689 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_debugfs.c | 899 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_fb.c | 615 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_lcd.c | 107 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_leds.c | 175 | ||||
-rw-r--r-- | drivers/hid/hid-ps3remote.c | 215 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic.c | 98 | ||||
-rw-r--r-- | drivers/hid/hid-wacom.c | 169 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote-ext.c | 97 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 69 |
21 files changed, 3808 insertions, 2905 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fbf49503508d..bf0617e47b89 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -527,6 +527,14 @@ config HID_PICOLCD_LEDS | |||
527 | ---help--- | 527 | ---help--- |
528 | Provide access to PicoLCD's GPO pins via leds class. | 528 | Provide access to PicoLCD's GPO pins via leds class. |
529 | 529 | ||
530 | config HID_PICOLCD_CIR | ||
531 | bool "CIR via RC class" if EXPERT | ||
532 | default !EXPERT | ||
533 | depends on HID_PICOLCD | ||
534 | depends on HID_PICOLCD=RC_CORE || RC_CORE=y | ||
535 | ---help--- | ||
536 | Provide access to PicoLCD's CIR interface via remote control (LIRC). | ||
537 | |||
530 | config HID_PRIMAX | 538 | config HID_PRIMAX |
531 | tristate "Primax non-fully HID-compliant devices" | 539 | tristate "Primax non-fully HID-compliant devices" |
532 | depends on USB_HID | 540 | depends on USB_HID |
@@ -534,6 +542,15 @@ config HID_PRIMAX | |||
534 | Support for Primax devices that are not fully compliant with the | 542 | Support for Primax devices that are not fully compliant with the |
535 | HID standard. | 543 | HID standard. |
536 | 544 | ||
545 | config HID_PS3REMOTE | ||
546 | tristate "Sony PS3 BD Remote Control" | ||
547 | depends on BT_HIDP | ||
548 | ---help--- | ||
549 | Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech | ||
550 | Harmony Adapter for PS3, which connect over Bluetooth. | ||
551 | |||
552 | Support for the 6-axis controllers is provided by HID_SONY. | ||
553 | |||
537 | config HID_ROCCAT | 554 | config HID_ROCCAT |
538 | tristate "Roccat device support" | 555 | tristate "Roccat device support" |
539 | depends on USB_HID | 556 | depends on USB_HID |
@@ -561,7 +578,9 @@ config HID_SONY | |||
561 | tristate "Sony PS3 controller" | 578 | tristate "Sony PS3 controller" |
562 | depends on USB_HID | 579 | depends on USB_HID |
563 | ---help--- | 580 | ---help--- |
564 | Support for Sony PS3 controller. | 581 | Support for Sony PS3 6-axis controllers. |
582 | |||
583 | Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE. | ||
565 | 584 | ||
566 | config HID_SPEEDLINK | 585 | config HID_SPEEDLINK |
567 | tristate "Speedlink VAD Cezanne mouse support" | 586 | 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-core.c b/drivers/hid/hid-core.c index 8bcd168fffae..9072e0ed1876 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1566,6 +1566,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1566 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, | 1566 | { 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) }, | 1567 | { 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) }, | 1568 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, |
1569 | { 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) }, | 1570 | { 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) }, | 1571 | { 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) }, | 1572 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, |
@@ -1639,6 +1640,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1639 | { 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_IR_REMOTE) }, |
1640 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, | 1641 | { 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) }, | 1642 | { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, |
1643 | { 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) }, | 1644 | { 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) }, | 1645 | { 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) }, | 1646 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, |
@@ -1663,6 +1665,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) }, | 1665 | { 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) }, | 1666 | { 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) }, | 1667 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, |
1668 | { 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) }, | 1669 | { 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) }, | 1670 | { 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) }, | 1671 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 1dcb76ff51e3..a534375fdf2e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -283,6 +283,9 @@ | |||
283 | #define USB_VENDOR_ID_EMS 0x2006 | 283 | #define USB_VENDOR_ID_EMS 0x2006 |
284 | #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 | 284 | #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118 |
285 | 285 | ||
286 | #define USB_VENDOR_ID_FLATFROG 0x25b5 | ||
287 | #define USB_DEVICE_ID_MULTITOUCH_3200 0x0002 | ||
288 | |||
286 | #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f | 289 | #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f |
287 | #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 | 290 | #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 |
288 | 291 | ||
@@ -496,6 +499,7 @@ | |||
496 | #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 | 499 | #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 |
497 | #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 | 500 | #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 |
498 | #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f | 501 | #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f |
502 | #define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306 | ||
499 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a | 503 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a |
500 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 | 504 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 |
501 | #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 | 505 | #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 |
@@ -683,6 +687,7 @@ | |||
683 | 687 | ||
684 | #define USB_VENDOR_ID_SONY 0x054c | 688 | #define USB_VENDOR_ID_SONY 0x054c |
685 | #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b | 689 | #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b |
690 | #define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 | ||
686 | #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 | 691 | #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 |
687 | #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f | 692 | #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f |
688 | 693 | ||
@@ -758,6 +763,7 @@ | |||
758 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 | 763 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 |
759 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 | 764 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 |
760 | #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 | 765 | #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 |
766 | #define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 | ||
761 | 767 | ||
762 | #define USB_VENDOR_ID_UNITEC 0x227d | 768 | #define USB_VENDOR_ID_UNITEC 0x227d |
763 | #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 | 769 | #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-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-multitouch.c b/drivers/hid/hid-multitouch.c index 59c8b5c1d2de..ee0b76b398cb 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,7 @@ 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 | ||
118 | 120 | ||
119 | #define MT_DEFAULT_MAXCONTACT 10 | 121 | #define MT_DEFAULT_MAXCONTACT 10 |
120 | 122 | ||
@@ -134,25 +136,6 @@ static int cypress_compute_slot(struct mt_device *td) | |||
134 | return -1; | 136 | return -1; |
135 | } | 137 | } |
136 | 138 | ||
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[] = { | 139 | static struct mt_class mt_classes[] = { |
157 | { .name = MT_CLS_DEFAULT, | 140 | { .name = MT_CLS_DEFAULT, |
158 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, | 141 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, |
@@ -190,7 +173,9 @@ static struct mt_class mt_classes[] = { | |||
190 | MT_QUIRK_SLOT_IS_CONTACTID, | 173 | MT_QUIRK_SLOT_IS_CONTACTID, |
191 | .sn_move = 2048, | 174 | .sn_move = 2048, |
192 | .sn_width = 128, | 175 | .sn_width = 128, |
193 | .sn_height = 128 }, | 176 | .sn_height = 128, |
177 | .maxcontacts = 60, | ||
178 | }, | ||
194 | { .name = MT_CLS_CYPRESS, | 179 | { .name = MT_CLS_CYPRESS, |
195 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | | 180 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | |
196 | MT_QUIRK_CYPRESS, | 181 | MT_QUIRK_CYPRESS, |
@@ -216,6 +201,12 @@ static struct mt_class mt_classes[] = { | |||
216 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP, | 201 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP, |
217 | .maxcontacts = 4 }, | 202 | .maxcontacts = 4 }, |
218 | 203 | ||
204 | { .name = MT_CLS_FLATFROG, | ||
205 | .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | | ||
206 | MT_QUIRK_NO_AREA, | ||
207 | .sn_move = 2048, | ||
208 | .maxcontacts = 40, | ||
209 | }, | ||
219 | { } | 210 | { } |
220 | }; | 211 | }; |
221 | 212 | ||
@@ -319,24 +310,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 | 310 | * We need to ignore fields that belong to other collections |
320 | * such as Mouse that might have the same GenericDesktop usages. */ | 311 | * such as Mouse that might have the same GenericDesktop usages. */ |
321 | if (field->application == HID_DG_TOUCHSCREEN) | 312 | if (field->application == HID_DG_TOUCHSCREEN) |
322 | set_bit(INPUT_PROP_DIRECT, hi->input->propbit); | 313 | td->mt_flags |= INPUT_MT_DIRECT; |
323 | else if (field->application != HID_DG_TOUCHPAD) | 314 | else if (field->application != HID_DG_TOUCHPAD) |
324 | return 0; | 315 | return 0; |
325 | 316 | ||
326 | /* In case of an indirect device (touchpad), we need to add | 317 | /* |
327 | * specific BTN_TOOL_* to be handled by the synaptics xorg | 318 | * Model touchscreens providing buttons as touchpads. |
328 | * driver. | ||
329 | * We also consider that touchscreens providing buttons are touchpads. | ||
330 | */ | 319 | */ |
331 | if (field->application == HID_DG_TOUCHPAD || | 320 | if (field->application == HID_DG_TOUCHPAD || |
332 | (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON || | 321 | (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) |
333 | cls->is_indirect) { | 322 | 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 | 323 | ||
341 | /* eGalax devices provide a Digitizer.Stylus input which overrides | 324 | /* eGalax devices provide a Digitizer.Stylus input which overrides |
342 | * the correct Digitizers.Finger X/Y ranges. | 325 | * the correct Digitizers.Finger X/Y ranges. |
@@ -353,8 +336,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
353 | EV_ABS, ABS_MT_POSITION_X); | 336 | EV_ABS, ABS_MT_POSITION_X); |
354 | set_abs(hi->input, ABS_MT_POSITION_X, field, | 337 | set_abs(hi->input, ABS_MT_POSITION_X, field, |
355 | cls->sn_move); | 338 | cls->sn_move); |
356 | /* touchscreen emulation */ | ||
357 | set_abs(hi->input, ABS_X, field, cls->sn_move); | ||
358 | mt_store_field(usage, td, hi); | 339 | mt_store_field(usage, td, hi); |
359 | td->last_field_index = field->index; | 340 | td->last_field_index = field->index; |
360 | return 1; | 341 | return 1; |
@@ -363,8 +344,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
363 | EV_ABS, ABS_MT_POSITION_Y); | 344 | EV_ABS, ABS_MT_POSITION_Y); |
364 | set_abs(hi->input, ABS_MT_POSITION_Y, field, | 345 | set_abs(hi->input, ABS_MT_POSITION_Y, field, |
365 | cls->sn_move); | 346 | cls->sn_move); |
366 | /* touchscreen emulation */ | ||
367 | set_abs(hi->input, ABS_Y, field, cls->sn_move); | ||
368 | mt_store_field(usage, td, hi); | 347 | mt_store_field(usage, td, hi); |
369 | td->last_field_index = field->index; | 348 | td->last_field_index = field->index; |
370 | return 1; | 349 | return 1; |
@@ -388,9 +367,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
388 | td->last_field_index = field->index; | 367 | td->last_field_index = field->index; |
389 | return 1; | 368 | return 1; |
390 | case HID_DG_CONTACTID: | 369 | 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); | 370 | mt_store_field(usage, td, hi); |
395 | td->last_field_index = field->index; | 371 | td->last_field_index = field->index; |
396 | td->touches_by_report++; | 372 | td->touches_by_report++; |
@@ -398,18 +374,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
398 | case HID_DG_WIDTH: | 374 | case HID_DG_WIDTH: |
399 | hid_map_usage(hi, usage, bit, max, | 375 | hid_map_usage(hi, usage, bit, max, |
400 | EV_ABS, ABS_MT_TOUCH_MAJOR); | 376 | EV_ABS, ABS_MT_TOUCH_MAJOR); |
401 | set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, | 377 | if (!(cls->quirks & MT_QUIRK_NO_AREA)) |
402 | cls->sn_width); | 378 | set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, |
379 | cls->sn_width); | ||
403 | mt_store_field(usage, td, hi); | 380 | mt_store_field(usage, td, hi); |
404 | td->last_field_index = field->index; | 381 | td->last_field_index = field->index; |
405 | return 1; | 382 | return 1; |
406 | case HID_DG_HEIGHT: | 383 | case HID_DG_HEIGHT: |
407 | hid_map_usage(hi, usage, bit, max, | 384 | hid_map_usage(hi, usage, bit, max, |
408 | EV_ABS, ABS_MT_TOUCH_MINOR); | 385 | EV_ABS, ABS_MT_TOUCH_MINOR); |
409 | set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, | 386 | if (!(cls->quirks & MT_QUIRK_NO_AREA)) { |
410 | cls->sn_height); | 387 | set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, |
411 | input_set_abs_params(hi->input, | 388 | cls->sn_height); |
389 | input_set_abs_params(hi->input, | ||
412 | ABS_MT_ORIENTATION, 0, 1, 0, 0); | 390 | ABS_MT_ORIENTATION, 0, 1, 0, 0); |
391 | } | ||
413 | mt_store_field(usage, td, hi); | 392 | mt_store_field(usage, td, hi); |
414 | td->last_field_index = field->index; | 393 | td->last_field_index = field->index; |
415 | return 1; | 394 | return 1; |
@@ -418,9 +397,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
418 | EV_ABS, ABS_MT_PRESSURE); | 397 | EV_ABS, ABS_MT_PRESSURE); |
419 | set_abs(hi->input, ABS_MT_PRESSURE, field, | 398 | set_abs(hi->input, ABS_MT_PRESSURE, field, |
420 | cls->sn_pressure); | 399 | 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); | 400 | mt_store_field(usage, td, hi); |
425 | td->last_field_index = field->index; | 401 | td->last_field_index = field->index; |
426 | return 1; | 402 | return 1; |
@@ -464,7 +440,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
464 | return -1; | 440 | return -1; |
465 | } | 441 | } |
466 | 442 | ||
467 | static int mt_compute_slot(struct mt_device *td) | 443 | static int mt_compute_slot(struct mt_device *td, struct input_dev *input) |
468 | { | 444 | { |
469 | __s32 quirks = td->mtclass.quirks; | 445 | __s32 quirks = td->mtclass.quirks; |
470 | 446 | ||
@@ -480,42 +456,23 @@ static int mt_compute_slot(struct mt_device *td) | |||
480 | if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE) | 456 | if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE) |
481 | return td->curdata.contactid - 1; | 457 | return td->curdata.contactid - 1; |
482 | 458 | ||
483 | return find_slot_from_contactid(td); | 459 | return input_mt_get_slot_by_key(input, td->curdata.contactid); |
484 | } | 460 | } |
485 | 461 | ||
486 | /* | 462 | /* |
487 | * this function is called when a whole contact has been processed, | 463 | * 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 | 464 | * so that it can assign it to a slot and store the data there |
489 | */ | 465 | */ |
490 | static void mt_complete_slot(struct mt_device *td) | 466 | static void mt_complete_slot(struct mt_device *td, struct input_dev *input) |
491 | { | 467 | { |
492 | td->curdata.seen_in_this_frame = true; | ||
493 | if (td->curvalid) { | 468 | if (td->curvalid) { |
494 | int slotnum = mt_compute_slot(td); | 469 | int slotnum = mt_compute_slot(td, input); |
495 | 470 | 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 | |||
502 | 471 | ||
503 | /* | 472 | if (slotnum < 0 || slotnum >= td->maxcontacts) |
504 | * this function is called when a whole packet has been received and processed, | 473 | return; |
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 | 474 | ||
511 | for (i = 0; i < td->maxcontacts; ++i) { | 475 | input_mt_slot(input, slotnum); |
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, | 476 | input_mt_report_slot_state(input, MT_TOOL_FINGER, |
520 | s->touch_state); | 477 | s->touch_state); |
521 | if (s->touch_state) { | 478 | if (s->touch_state) { |
@@ -532,24 +489,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); | 489 | input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); |
533 | input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); | 490 | input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); |
534 | } | 491 | } |
535 | s->seen_in_this_frame = false; | ||
536 | |||
537 | } | 492 | } |
538 | 493 | ||
539 | input_mt_report_pointer_emulation(input, true); | 494 | td->num_received++; |
495 | } | ||
496 | |||
497 | /* | ||
498 | * this function is called when a whole packet has been received and processed, | ||
499 | * so that it can decide what to send to the input layer. | ||
500 | */ | ||
501 | static void mt_sync_frame(struct mt_device *td, struct input_dev *input) | ||
502 | { | ||
503 | input_mt_sync_frame(input); | ||
540 | input_sync(input); | 504 | input_sync(input); |
541 | td->num_received = 0; | 505 | td->num_received = 0; |
542 | } | 506 | } |
543 | 507 | ||
544 | |||
545 | |||
546 | static int mt_event(struct hid_device *hid, struct hid_field *field, | 508 | static int mt_event(struct hid_device *hid, struct hid_field *field, |
547 | struct hid_usage *usage, __s32 value) | 509 | struct hid_usage *usage, __s32 value) |
548 | { | 510 | { |
549 | struct mt_device *td = hid_get_drvdata(hid); | 511 | struct mt_device *td = hid_get_drvdata(hid); |
550 | __s32 quirks = td->mtclass.quirks; | 512 | __s32 quirks = td->mtclass.quirks; |
551 | 513 | ||
552 | if (hid->claimed & HID_CLAIMED_INPUT && td->slots) { | 514 | if (hid->claimed & HID_CLAIMED_INPUT) { |
553 | switch (usage->hid) { | 515 | switch (usage->hid) { |
554 | case HID_DG_INRANGE: | 516 | case HID_DG_INRANGE: |
555 | if (quirks & MT_QUIRK_ALWAYS_VALID) | 517 | if (quirks & MT_QUIRK_ALWAYS_VALID) |
@@ -602,11 +564,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, | |||
602 | } | 564 | } |
603 | 565 | ||
604 | if (usage->hid == td->last_slot_field) | 566 | if (usage->hid == td->last_slot_field) |
605 | mt_complete_slot(td); | 567 | mt_complete_slot(td, field->hidinput->input); |
606 | 568 | ||
607 | if (field->index == td->last_field_index | 569 | if (field->index == td->last_field_index |
608 | && td->num_received >= td->num_expected) | 570 | && td->num_received >= td->num_expected) |
609 | mt_emit_event(td, field->hidinput->input); | 571 | mt_sync_frame(td, field->hidinput->input); |
610 | 572 | ||
611 | } | 573 | } |
612 | 574 | ||
@@ -685,6 +647,35 @@ static void mt_post_parse(struct mt_device *td) | |||
685 | } | 647 | } |
686 | } | 648 | } |
687 | 649 | ||
650 | static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) | ||
651 | |||
652 | { | ||
653 | struct mt_device *td = hid_get_drvdata(hdev); | ||
654 | struct mt_class *cls = &td->mtclass; | ||
655 | struct input_dev *input = hi->input; | ||
656 | |||
657 | /* Only initialize slots for MT input devices */ | ||
658 | if (!test_bit(ABS_MT_POSITION_X, input->absbit)) | ||
659 | return; | ||
660 | |||
661 | if (!td->maxcontacts) | ||
662 | td->maxcontacts = MT_DEFAULT_MAXCONTACT; | ||
663 | |||
664 | mt_post_parse(td); | ||
665 | if (td->serial_maybe) | ||
666 | mt_post_parse_default_settings(td); | ||
667 | |||
668 | if (cls->is_indirect) | ||
669 | td->mt_flags |= INPUT_MT_POINTER; | ||
670 | |||
671 | if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) | ||
672 | td->mt_flags |= INPUT_MT_DROP_UNUSED; | ||
673 | |||
674 | input_mt_init_slots(input, td->maxcontacts, td->mt_flags); | ||
675 | |||
676 | td->mt_flags = 0; | ||
677 | } | ||
678 | |||
688 | static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) | 679 | static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) |
689 | { | 680 | { |
690 | int ret, i; | 681 | int ret, i; |
@@ -722,6 +713,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
722 | goto fail; | 713 | goto fail; |
723 | } | 714 | } |
724 | 715 | ||
716 | if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) | ||
717 | td->serial_maybe = true; | ||
718 | |||
725 | ret = hid_parse(hdev); | 719 | ret = hid_parse(hdev); |
726 | if (ret != 0) | 720 | if (ret != 0) |
727 | goto fail; | 721 | goto fail; |
@@ -730,20 +724,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
730 | if (ret) | 724 | if (ret) |
731 | goto fail; | 725 | goto fail; |
732 | 726 | ||
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); | 727 | ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); |
748 | 728 | ||
749 | mt_set_maxcontacts(hdev); | 729 | mt_set_maxcontacts(hdev); |
@@ -774,7 +754,6 @@ static void mt_remove(struct hid_device *hdev) | |||
774 | struct mt_device *td = hid_get_drvdata(hdev); | 754 | struct mt_device *td = hid_get_drvdata(hdev); |
775 | sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); | 755 | sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); |
776 | hid_hw_stop(hdev); | 756 | hid_hw_stop(hdev); |
777 | kfree(td->slots); | ||
778 | kfree(td); | 757 | kfree(td); |
779 | hid_set_drvdata(hdev, NULL); | 758 | hid_set_drvdata(hdev, NULL); |
780 | } | 759 | } |
@@ -892,6 +871,11 @@ static const struct hid_device_id mt_devices[] = { | |||
892 | MT_USB_DEVICE(USB_VENDOR_ID_ELO, | 871 | MT_USB_DEVICE(USB_VENDOR_ID_ELO, |
893 | USB_DEVICE_ID_ELO_TS2515) }, | 872 | USB_DEVICE_ID_ELO_TS2515) }, |
894 | 873 | ||
874 | /* Flatfrog Panels */ | ||
875 | { .driver_data = MT_CLS_FLATFROG, | ||
876 | MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG, | ||
877 | USB_DEVICE_ID_MULTITOUCH_3200) }, | ||
878 | |||
895 | /* GeneralTouch panel */ | 879 | /* GeneralTouch panel */ |
896 | { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, | 880 | { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, |
897 | MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, | 881 | MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, |
@@ -1087,6 +1071,7 @@ static struct hid_driver mt_driver = { | |||
1087 | .remove = mt_remove, | 1071 | .remove = mt_remove, |
1088 | .input_mapping = mt_input_mapping, | 1072 | .input_mapping = mt_input_mapping, |
1089 | .input_mapped = mt_input_mapped, | 1073 | .input_mapped = mt_input_mapped, |
1074 | .input_configured = mt_input_configured, | ||
1090 | .feature_mapping = mt_feature_mapping, | 1075 | .feature_mapping = mt_feature_mapping, |
1091 | .usage_table = mt_grabbed_usages, | 1076 | .usage_table = mt_grabbed_usages, |
1092 | .event = mt_event, | 1077 | .event = mt_event, |
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-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-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..75b970f116ee 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #define PAD_DEVICE_ID 0x0F | 33 | #define PAD_DEVICE_ID 0x0F |
34 | 34 | ||
35 | #define WAC_CMD_LED_CONTROL 0x20 | 35 | #define WAC_CMD_LED_CONTROL 0x20 |
36 | #define WAC_CMD_ICON_START_STOP 0x21 | ||
37 | #define WAC_CMD_ICON_TRANSFER 0x26 | ||
36 | 38 | ||
37 | struct wacom_data { | 39 | struct wacom_data { |
38 | __u16 tool; | 40 | __u16 tool; |
@@ -69,6 +71,91 @@ static enum power_supply_property wacom_ac_props[] = { | |||
69 | POWER_SUPPLY_PROP_SCOPE, | 71 | POWER_SUPPLY_PROP_SCOPE, |
70 | }; | 72 | }; |
71 | 73 | ||
74 | static void wacom_scramble(__u8 *image) | ||
75 | { | ||
76 | __u16 mask; | ||
77 | __u16 s1; | ||
78 | __u16 s2; | ||
79 | __u16 r1 ; | ||
80 | __u16 r2 ; | ||
81 | __u16 r; | ||
82 | __u8 buf[256]; | ||
83 | int i, w, x, y, z; | ||
84 | |||
85 | for (x = 0; x < 32; x++) { | ||
86 | for (y = 0; y < 8; y++) | ||
87 | buf[(8 * x) + (7 - y)] = image[(8 * x) + y]; | ||
88 | } | ||
89 | |||
90 | /* Change 76543210 into GECA6420 as required by Intuos4 WL | ||
91 | * HGFEDCBA HFDB7531 | ||
92 | */ | ||
93 | for (x = 0; x < 4; x++) { | ||
94 | for (y = 0; y < 4; y++) { | ||
95 | for (z = 0; z < 8; z++) { | ||
96 | mask = 0x0001; | ||
97 | r1 = 0; | ||
98 | r2 = 0; | ||
99 | i = (x << 6) + (y << 4) + z; | ||
100 | s1 = buf[i]; | ||
101 | s2 = buf[i+8]; | ||
102 | for (w = 0; w < 8; w++) { | ||
103 | r1 |= (s1 & mask); | ||
104 | r2 |= (s2 & mask); | ||
105 | s1 <<= 1; | ||
106 | s2 <<= 1; | ||
107 | mask <<= 2; | ||
108 | } | ||
109 | r = r1 | (r2 << 1); | ||
110 | i = (x << 6) + (y << 4) + (z << 1); | ||
111 | image[i] = 0xFF & r; | ||
112 | image[i+1] = (0xFF00 & r) >> 8; | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static void wacom_set_image(struct hid_device *hdev, const char *image, | ||
119 | __u8 icon_no) | ||
120 | { | ||
121 | __u8 rep_data[68]; | ||
122 | __u8 p[256]; | ||
123 | int ret, i, j; | ||
124 | |||
125 | for (i = 0; i < 256; i++) | ||
126 | p[i] = image[i]; | ||
127 | |||
128 | rep_data[0] = WAC_CMD_ICON_START_STOP; | ||
129 | rep_data[1] = 0; | ||
130 | ret = hdev->hid_output_raw_report(hdev, rep_data, 2, | ||
131 | HID_FEATURE_REPORT); | ||
132 | if (ret < 0) | ||
133 | goto err; | ||
134 | |||
135 | rep_data[0] = WAC_CMD_ICON_TRANSFER; | ||
136 | rep_data[1] = icon_no & 0x07; | ||
137 | |||
138 | wacom_scramble(p); | ||
139 | |||
140 | for (i = 0; i < 4; i++) { | ||
141 | for (j = 0; j < 64; j++) | ||
142 | rep_data[j + 3] = p[(i << 6) + j]; | ||
143 | |||
144 | rep_data[2] = i; | ||
145 | ret = hdev->hid_output_raw_report(hdev, rep_data, 67, | ||
146 | HID_FEATURE_REPORT); | ||
147 | } | ||
148 | |||
149 | rep_data[0] = WAC_CMD_ICON_START_STOP; | ||
150 | rep_data[1] = 0; | ||
151 | |||
152 | ret = hdev->hid_output_raw_report(hdev, rep_data, 2, | ||
153 | HID_FEATURE_REPORT); | ||
154 | |||
155 | err: | ||
156 | return; | ||
157 | } | ||
158 | |||
72 | static void wacom_leds_set_brightness(struct led_classdev *led_dev, | 159 | static void wacom_leds_set_brightness(struct led_classdev *led_dev, |
73 | enum led_brightness value) | 160 | enum led_brightness value) |
74 | { | 161 | { |
@@ -91,7 +178,10 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev, | |||
91 | if (buf) { | 178 | if (buf) { |
92 | buf[0] = WAC_CMD_LED_CONTROL; | 179 | buf[0] = WAC_CMD_LED_CONTROL; |
93 | buf[1] = led; | 180 | buf[1] = led; |
94 | buf[2] = value; | 181 | buf[2] = value >> 2; |
182 | buf[3] = value; | ||
183 | /* use fixed brightness for OLEDs */ | ||
184 | buf[4] = 0x08; | ||
95 | hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); | 185 | hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); |
96 | kfree(buf); | 186 | kfree(buf); |
97 | } | 187 | } |
@@ -317,6 +407,34 @@ static ssize_t wacom_store_speed(struct device *dev, | |||
317 | static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, | 407 | static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, |
318 | wacom_show_speed, wacom_store_speed); | 408 | wacom_show_speed, wacom_store_speed); |
319 | 409 | ||
410 | #define WACOM_STORE(OLED_ID) \ | ||
411 | static ssize_t wacom_oled##OLED_ID##_store(struct device *dev, \ | ||
412 | struct device_attribute *attr, \ | ||
413 | const char *buf, size_t count) \ | ||
414 | { \ | ||
415 | struct hid_device *hdev = container_of(dev, struct hid_device, \ | ||
416 | dev); \ | ||
417 | \ | ||
418 | if (count != 256) \ | ||
419 | return -EINVAL; \ | ||
420 | \ | ||
421 | wacom_set_image(hdev, buf, OLED_ID); \ | ||
422 | \ | ||
423 | return count; \ | ||
424 | } \ | ||
425 | \ | ||
426 | static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL, \ | ||
427 | wacom_oled##OLED_ID##_store) | ||
428 | |||
429 | WACOM_STORE(0); | ||
430 | WACOM_STORE(1); | ||
431 | WACOM_STORE(2); | ||
432 | WACOM_STORE(3); | ||
433 | WACOM_STORE(4); | ||
434 | WACOM_STORE(5); | ||
435 | WACOM_STORE(6); | ||
436 | WACOM_STORE(7); | ||
437 | |||
320 | static int wacom_gr_parse_report(struct hid_device *hdev, | 438 | static int wacom_gr_parse_report(struct hid_device *hdev, |
321 | struct wacom_data *wdata, | 439 | struct wacom_data *wdata, |
322 | struct input_dev *input, unsigned char *data) | 440 | struct input_dev *input, unsigned char *data) |
@@ -717,17 +835,33 @@ static int wacom_probe(struct hid_device *hdev, | |||
717 | hid_warn(hdev, | 835 | hid_warn(hdev, |
718 | "can't create sysfs speed attribute err: %d\n", ret); | 836 | "can't create sysfs speed attribute err: %d\n", ret); |
719 | 837 | ||
838 | #define OLED_INIT(OLED_ID) \ | ||
839 | do { \ | ||
840 | ret = device_create_file(&hdev->dev, \ | ||
841 | &dev_attr_oled##OLED_ID##_img); \ | ||
842 | if (ret) \ | ||
843 | hid_warn(hdev, \ | ||
844 | "can't create sysfs oled attribute, err: %d\n", ret);\ | ||
845 | } while (0) | ||
846 | |||
847 | OLED_INIT(0); | ||
848 | OLED_INIT(1); | ||
849 | OLED_INIT(2); | ||
850 | OLED_INIT(3); | ||
851 | OLED_INIT(4); | ||
852 | OLED_INIT(5); | ||
853 | OLED_INIT(6); | ||
854 | OLED_INIT(7); | ||
855 | |||
720 | wdata->features = 0; | 856 | wdata->features = 0; |
721 | wacom_set_features(hdev, 1); | 857 | wacom_set_features(hdev, 1); |
722 | 858 | ||
723 | if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) { | 859 | if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) { |
724 | sprintf(hdev->name, "%s", "Wacom Intuos4 WL"); | 860 | sprintf(hdev->name, "%s", "Wacom Intuos4 WL"); |
725 | ret = wacom_initialize_leds(hdev); | 861 | ret = wacom_initialize_leds(hdev); |
726 | if (ret) { | 862 | if (ret) |
727 | hid_warn(hdev, | 863 | hid_warn(hdev, |
728 | "can't create led attribute, err: %d\n", ret); | 864 | "can't create led attribute, err: %d\n", ret); |
729 | goto destroy_leds; | ||
730 | } | ||
731 | } | 865 | } |
732 | 866 | ||
733 | wdata->battery.properties = wacom_battery_props; | 867 | wdata->battery.properties = wacom_battery_props; |
@@ -740,8 +874,8 @@ static int wacom_probe(struct hid_device *hdev, | |||
740 | 874 | ||
741 | ret = power_supply_register(&hdev->dev, &wdata->battery); | 875 | ret = power_supply_register(&hdev->dev, &wdata->battery); |
742 | if (ret) { | 876 | if (ret) { |
743 | hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n", | 877 | hid_err(hdev, "can't create sysfs battery attribute, err: %d\n", |
744 | ret); | 878 | ret); |
745 | goto err_battery; | 879 | goto err_battery; |
746 | } | 880 | } |
747 | 881 | ||
@@ -756,8 +890,8 @@ static int wacom_probe(struct hid_device *hdev, | |||
756 | 890 | ||
757 | ret = power_supply_register(&hdev->dev, &wdata->ac); | 891 | ret = power_supply_register(&hdev->dev, &wdata->ac); |
758 | if (ret) { | 892 | if (ret) { |
759 | hid_warn(hdev, | 893 | hid_err(hdev, |
760 | "can't create ac battery attribute, err: %d\n", ret); | 894 | "can't create ac battery attribute, err: %d\n", ret); |
761 | goto err_ac; | 895 | goto err_ac; |
762 | } | 896 | } |
763 | 897 | ||
@@ -767,10 +901,17 @@ static int wacom_probe(struct hid_device *hdev, | |||
767 | err_ac: | 901 | err_ac: |
768 | power_supply_unregister(&wdata->battery); | 902 | power_supply_unregister(&wdata->battery); |
769 | err_battery: | 903 | err_battery: |
904 | wacom_destroy_leds(hdev); | ||
905 | device_remove_file(&hdev->dev, &dev_attr_oled0_img); | ||
906 | device_remove_file(&hdev->dev, &dev_attr_oled1_img); | ||
907 | device_remove_file(&hdev->dev, &dev_attr_oled2_img); | ||
908 | device_remove_file(&hdev->dev, &dev_attr_oled3_img); | ||
909 | device_remove_file(&hdev->dev, &dev_attr_oled4_img); | ||
910 | device_remove_file(&hdev->dev, &dev_attr_oled5_img); | ||
911 | device_remove_file(&hdev->dev, &dev_attr_oled6_img); | ||
912 | device_remove_file(&hdev->dev, &dev_attr_oled7_img); | ||
770 | device_remove_file(&hdev->dev, &dev_attr_speed); | 913 | device_remove_file(&hdev->dev, &dev_attr_speed); |
771 | hid_hw_stop(hdev); | 914 | hid_hw_stop(hdev); |
772 | destroy_leds: | ||
773 | wacom_destroy_leds(hdev); | ||
774 | err_free: | 915 | err_free: |
775 | kfree(wdata); | 916 | kfree(wdata); |
776 | return ret; | 917 | return ret; |
@@ -781,6 +922,14 @@ static void wacom_remove(struct hid_device *hdev) | |||
781 | struct wacom_data *wdata = hid_get_drvdata(hdev); | 922 | struct wacom_data *wdata = hid_get_drvdata(hdev); |
782 | 923 | ||
783 | wacom_destroy_leds(hdev); | 924 | wacom_destroy_leds(hdev); |
925 | device_remove_file(&hdev->dev, &dev_attr_oled0_img); | ||
926 | device_remove_file(&hdev->dev, &dev_attr_oled1_img); | ||
927 | device_remove_file(&hdev->dev, &dev_attr_oled2_img); | ||
928 | device_remove_file(&hdev->dev, &dev_attr_oled3_img); | ||
929 | device_remove_file(&hdev->dev, &dev_attr_oled4_img); | ||
930 | device_remove_file(&hdev->dev, &dev_attr_oled5_img); | ||
931 | device_remove_file(&hdev->dev, &dev_attr_oled6_img); | ||
932 | device_remove_file(&hdev->dev, &dev_attr_oled7_img); | ||
784 | device_remove_file(&hdev->dev, &dev_attr_speed); | 933 | device_remove_file(&hdev->dev, &dev_attr_speed); |
785 | hid_hw_stop(hdev); | 934 | hid_hw_stop(hdev); |
786 | 935 | ||
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..c46c5f1037f4 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 | ||
@@ -585,3 +548,23 @@ void hidraw_exit(void) | |||
585 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); | 548 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); |
586 | 549 | ||
587 | } | 550 | } |
551 | |||
552 | static void drop_ref(struct hidraw *hidraw, int exists_bit) | ||
553 | { | ||
554 | mutex_lock(&minors_lock); | ||
555 | if (exists_bit) { | ||
556 | hid_hw_close(hidraw->hid); | ||
557 | hidraw->exist = 0; | ||
558 | if (hidraw->open) | ||
559 | wake_up_interruptible(&hidraw->wait); | ||
560 | } else { | ||
561 | --hidraw->open; | ||
562 | } | ||
563 | |||
564 | if (!hidraw->open && !hidraw->exist) { | ||
565 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||
566 | hidraw_table[hidraw->minor] = NULL; | ||
567 | kfree(hidraw); | ||
568 | } | ||
569 | mutex_unlock(&minors_lock); | ||
570 | } | ||