diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-driver-hid-picolcd | 43 | ||||
| -rw-r--r-- | Documentation/ABI/testing/sysfs-driver-hid-prodikeys | 29 | ||||
| -rw-r--r-- | Documentation/ABI/testing/sysfs-driver-hid-roccat-kone | 111 | ||||
| -rw-r--r-- | drivers/hid/Kconfig | 79 | ||||
| -rw-r--r-- | drivers/hid/Makefile | 3 | ||||
| -rw-r--r-- | drivers/hid/hid-core.c | 4 | ||||
| -rw-r--r-- | drivers/hid/hid-ids.h | 8 | ||||
| -rw-r--r-- | drivers/hid/hid-ntrig.c | 526 | ||||
| -rw-r--r-- | drivers/hid/hid-picolcd.c | 2631 | ||||
| -rw-r--r-- | drivers/hid/hid-prodikeys.c | 910 | ||||
| -rw-r--r-- | drivers/hid/hid-roccat-kone.c | 994 | ||||
| -rw-r--r-- | drivers/hid/hid-roccat-kone.h | 224 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hid-core.c | 1 |
13 files changed, 5554 insertions, 9 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-picolcd b/Documentation/ABI/testing/sysfs-driver-hid-picolcd new file mode 100644 index 000000000000..08579e7e1e89 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-picolcd | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/operation_mode | ||
| 2 | Date: March 2010 | ||
| 3 | Contact: Bruno Prémont <bonbons@linux-vserver.org> | ||
| 4 | Description: Make it possible to switch the PicoLCD device between LCD | ||
| 5 | (firmware) and bootloader (flasher) operation modes. | ||
| 6 | |||
| 7 | Reading: returns list of available modes, the active mode being | ||
| 8 | enclosed in brackets ('[' and ']') | ||
| 9 | |||
| 10 | Writing: causes operation mode switch. Permitted values are | ||
| 11 | the non-active mode names listed when read. | ||
| 12 | |||
| 13 | Note: when switching mode the current PicoLCD HID device gets | ||
| 14 | disconnected and reconnects after above delay (see attribute | ||
| 15 | operation_mode_delay for its value). | ||
| 16 | |||
| 17 | |||
| 18 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/operation_mode_delay | ||
| 19 | Date: April 2010 | ||
| 20 | Contact: Bruno Prémont <bonbons@linux-vserver.org> | ||
| 21 | Description: Delay PicoLCD waits before restarting in new mode when | ||
| 22 | operation_mode has changed. | ||
| 23 | |||
| 24 | Reading/Writing: It is expressed in ms and permitted range is | ||
| 25 | 0..30000ms. | ||
| 26 | |||
| 27 | |||
| 28 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fb_update_rate | ||
| 29 | Date: March 2010 | ||
| 30 | Contact: Bruno Prémont <bonbons@linux-vserver.org> | ||
| 31 | Description: Make it possible to adjust defio refresh rate. | ||
| 32 | |||
| 33 | Reading: returns list of available refresh rates (expressed in Hz), | ||
| 34 | the active refresh rate being enclosed in brackets ('[' and ']') | ||
| 35 | |||
| 36 | Writing: accepts new refresh rate expressed in integer Hz | ||
| 37 | within permitted rates. | ||
| 38 | |||
| 39 | Note: As device can barely do 2 complete refreshes a second | ||
| 40 | it only makes sense to adjust this value if only one or two | ||
| 41 | tiles get changed and it's not appropriate to expect the application | ||
| 42 | to flush it's tiny changes explicitely at higher than default rate. | ||
| 43 | |||
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-prodikeys b/Documentation/ABI/testing/sysfs-driver-hid-prodikeys new file mode 100644 index 000000000000..05d988c29a83 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-prodikeys | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | What: /sys/bus/hid/drivers/prodikeys/.../channel | ||
| 2 | Date: April 2010 | ||
| 3 | KernelVersion: 2.6.34 | ||
| 4 | Contact: Don Prince <dhprince.devel@yahoo.co.uk> | ||
| 5 | Description: | ||
| 6 | Allows control (via software) the midi channel to which | ||
| 7 | that the pc-midi keyboard will output.midi data. | ||
| 8 | Range: 0..15 | ||
| 9 | Type: Read/write | ||
| 10 | What: /sys/bus/hid/drivers/prodikeys/.../sustain | ||
| 11 | Date: April 2010 | ||
| 12 | KernelVersion: 2.6.34 | ||
| 13 | Contact: Don Prince <dhprince.devel@yahoo.co.uk> | ||
| 14 | Description: | ||
| 15 | Allows control (via software) the sustain duration of a | ||
| 16 | note held by the pc-midi driver. | ||
| 17 | 0 means sustain mode is disabled. | ||
| 18 | Range: 0..5000 (milliseconds) | ||
| 19 | Type: Read/write | ||
| 20 | What: /sys/bus/hid/drivers/prodikeys/.../octave | ||
| 21 | Date: April 2010 | ||
| 22 | KernelVersion: 2.6.34 | ||
| 23 | Contact: Don Prince <dhprince.devel@yahoo.co.uk> | ||
| 24 | Description: | ||
| 25 | Controls the octave shift modifier in the pc-midi driver. | ||
| 26 | The octave can be shifted via software up/down 2 octaves. | ||
| 27 | 0 means the no ocatve shift. | ||
| 28 | Range: -2..2 (minus 2 to plus 2) | ||
| 29 | Type: Read/Write | ||
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone new file mode 100644 index 000000000000..88340a23ce91 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_dpi | ||
| 2 | Date: March 2010 | ||
| 3 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 4 | Description: It is possible to switch the dpi setting of the mouse with the | ||
| 5 | press of a button. | ||
| 6 | When read, this file returns the raw number of the actual dpi | ||
| 7 | setting reported by the mouse. This number has to be further | ||
| 8 | processed to receive the real dpi value. | ||
| 9 | |||
| 10 | VALUE DPI | ||
| 11 | 1 800 | ||
| 12 | 2 1200 | ||
| 13 | 3 1600 | ||
| 14 | 4 2000 | ||
| 15 | 5 2400 | ||
| 16 | 6 3200 | ||
| 17 | |||
| 18 | This file is readonly. | ||
| 19 | |||
| 20 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_profile | ||
| 21 | Date: March 2010 | ||
| 22 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 23 | Description: When read, this file returns the number of the actual profile. | ||
| 24 | This file is readonly. | ||
| 25 | |||
| 26 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/firmware_version | ||
| 27 | Date: March 2010 | ||
| 28 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 29 | Description: When read, this file returns the raw integer version number of the | ||
| 30 | firmware reported by the mouse. Using the integer value eases | ||
| 31 | further usage in other programs. To receive the real version | ||
| 32 | number the decimal point has to be shifted 2 positions to the | ||
| 33 | left. E.g. a returned value of 138 means 1.38 | ||
| 34 | This file is readonly. | ||
| 35 | |||
| 36 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/kone_driver_version | ||
| 37 | Date: March 2010 | ||
| 38 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 39 | Description: When read, this file returns the driver version. | ||
| 40 | The format of the string is "v<major>.<minor>.<patchlevel>". | ||
| 41 | This attribute is used by the userland tools to find the sysfs- | ||
| 42 | paths of installed kone-mice and determine the capabilites of | ||
| 43 | the driver. Versions of this driver for old kernels replace | ||
| 44 | usbhid instead of generic-usb. The way to scan for this file | ||
| 45 | has been chosen to provide a consistent way for all supported | ||
| 46 | kernel versions. | ||
| 47 | This file is readonly. | ||
| 48 | |||
| 49 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5] | ||
| 50 | Date: March 2010 | ||
| 51 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 52 | Description: The mouse can store 5 profiles which can be switched by the | ||
| 53 | press of a button. A profile holds informations like button | ||
| 54 | mappings, sensitivity, the colors of the 5 leds and light | ||
| 55 | effects. | ||
| 56 | When read, these files return the respective profile. The | ||
| 57 | returned data is 975 bytes in size. | ||
| 58 | When written, this file lets one write the respective profile | ||
| 59 | data back to the mouse. The data has to be 975 bytes long. | ||
| 60 | The mouse will reject invalid data, whereas the profile number | ||
| 61 | stored in the profile doesn't need to fit the number of the | ||
| 62 | store. | ||
| 63 | |||
| 64 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/settings | ||
| 65 | Date: March 2010 | ||
| 66 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 67 | Description: When read, this file returns the settings stored in the mouse. | ||
| 68 | The size of the data is 36 bytes and holds information like the | ||
| 69 | startup_profile, tcu state and calibration_data. | ||
| 70 | When written, this file lets write settings back to the mouse. | ||
| 71 | The data has to be 36 bytes long. The mouse will reject invalid | ||
| 72 | data. | ||
| 73 | |||
| 74 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/startup_profile | ||
| 75 | Date: March 2010 | ||
| 76 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 77 | Description: The integer value of this attribute ranges from 1 to 5. | ||
| 78 | When read, this attribute returns the number of the profile | ||
| 79 | that's active when the mouse is powered on. | ||
| 80 | When written, this file sets the number of the startup profile | ||
| 81 | and the mouse activates this profile immediately. | ||
| 82 | |||
| 83 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/tcu | ||
| 84 | Date: March 2010 | ||
| 85 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 86 | Description: The mouse has a "Tracking Control Unit" which lets the user | ||
| 87 | calibrate the laser power to fit the mousepad surface. | ||
| 88 | When read, this file returns the current state of the TCU, | ||
| 89 | where 0 means off and 1 means on. | ||
| 90 | Writing 0 in this file will switch the TCU off. | ||
| 91 | Writing 1 in this file will start the calibration which takes | ||
| 92 | around 6 seconds to complete and activates the TCU. | ||
| 93 | |||
| 94 | What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/weight | ||
| 95 | Date: March 2010 | ||
| 96 | Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 97 | Description: The mouse can be equipped with one of four supplied weights | ||
| 98 | ranging from 5 to 20 grams which are recognized by the mouse | ||
| 99 | and its value can be read out. When read, this file returns the | ||
| 100 | raw value returned by the mouse which eases further processing | ||
| 101 | in other software. | ||
| 102 | The values map to the weights as follows: | ||
| 103 | |||
| 104 | VALUE WEIGHT | ||
| 105 | 0 none | ||
| 106 | 1 5g | ||
| 107 | 2 10g | ||
| 108 | 3 15g | ||
| 109 | 4 20g | ||
| 110 | |||
| 111 | This file is readonly. | ||
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4ea926a21bc8..339c1eaa55ac 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
| @@ -106,6 +106,21 @@ config HID_CHICONY | |||
| 106 | ---help--- | 106 | ---help--- |
| 107 | Support for Chicony Tactical pad. | 107 | Support for Chicony Tactical pad. |
| 108 | 108 | ||
| 109 | config HID_PRODIKEYS | ||
| 110 | tristate "Prodikeys PC-MIDI Keyboard support" | ||
| 111 | depends on USB_HID && SND | ||
| 112 | select SND_RAWMIDI | ||
| 113 | ---help--- | ||
| 114 | Support for Prodikeys PC-MIDI Keyboard device support. | ||
| 115 | Say Y here to enable support for this device. | ||
| 116 | - Prodikeys PC-MIDI keyboard. | ||
| 117 | The Prodikeys PC-MIDI acts as a USB Audio device, with one MIDI | ||
| 118 | input and one MIDI output. These MIDI jacks appear as | ||
| 119 | a sound "card" in the ALSA sound system. | ||
| 120 | Note: if you say N here, this device will still function as a basic | ||
| 121 | multimedia keyboard, but will lack support for the musical keyboard | ||
| 122 | and some additional multimedia keys. | ||
| 123 | |||
| 109 | config HID_CYPRESS | 124 | config HID_CYPRESS |
| 110 | tristate "Cypress" if EMBEDDED | 125 | tristate "Cypress" if EMBEDDED |
| 111 | depends on USB_HID | 126 | depends on USB_HID |
| @@ -274,12 +289,76 @@ config HID_PETALYNX | |||
| 274 | ---help--- | 289 | ---help--- |
| 275 | Support for Petalynx Maxter remote control. | 290 | Support for Petalynx Maxter remote control. |
| 276 | 291 | ||
| 292 | config HID_PICOLCD | ||
| 293 | tristate "PicoLCD (graphic version)" | ||
| 294 | depends on USB_HID | ||
| 295 | ---help--- | ||
| 296 | This provides support for Minibox PicoLCD devices, currently | ||
| 297 | only the graphical ones are supported. | ||
| 298 | |||
| 299 | This includes support for the following device features: | ||
| 300 | - Keypad | ||
| 301 | - Switching between Firmware and Flash mode | ||
| 302 | - EEProm / Flash access (via debugfs) | ||
| 303 | Features selectively enabled: | ||
| 304 | - Framebuffer for monochrome 256x64 display | ||
| 305 | - Backlight control | ||
| 306 | - Contrast control | ||
| 307 | - General purpose outputs | ||
| 308 | Features that are not (yet) supported: | ||
| 309 | - IR | ||
| 310 | |||
| 311 | config HID_PICOLCD_FB | ||
| 312 | bool "Framebuffer support" if EMBEDDED | ||
| 313 | default !EMBEDDED | ||
| 314 | depends on HID_PICOLCD | ||
| 315 | depends on HID_PICOLCD=FB || FB=y | ||
| 316 | select FB_DEFERRED_IO | ||
| 317 | select FB_SYS_FILLRECT | ||
| 318 | select FB_SYS_COPYAREA | ||
| 319 | select FB_SYS_IMAGEBLIT | ||
| 320 | select FB_SYS_FOPS | ||
| 321 | ---help--- | ||
| 322 | Provide access to PicoLCD's 256x64 monochrome display via a | ||
| 323 | frambuffer device. | ||
| 324 | |||
| 325 | config HID_PICOLCD_BACKLIGHT | ||
| 326 | bool "Backlight control" if EMBEDDED | ||
| 327 | default !EMBEDDED | ||
| 328 | depends on HID_PICOLCD | ||
| 329 | depends on HID_PICOLCD=BACKLIGHT_CLASS_DEVICE || BACKLIGHT_CLASS_DEVICE=y | ||
| 330 | ---help--- | ||
| 331 | Provide access to PicoLCD's backlight control via backlight | ||
| 332 | class. | ||
| 333 | |||
| 334 | config HID_PICOLCD_LCD | ||
| 335 | bool "Contrast control" if EMBEDDED | ||
| 336 | default !EMBEDDED | ||
| 337 | depends on HID_PICOLCD | ||
| 338 | depends on HID_PICOLCD=LCD_CLASS_DEVICE || LCD_CLASS_DEVICE=y | ||
| 339 | ---help--- | ||
| 340 | Provide access to PicoLCD's LCD contrast via lcd class. | ||
| 341 | |||
| 342 | config HID_PICOLCD_LEDS | ||
| 343 | bool "GPO via leds class" if EMBEDDED | ||
| 344 | default !EMBEDDED | ||
| 345 | depends on HID_PICOLCD | ||
| 346 | depends on HID_PICOLCD=LEDS_CLASS || LEDS_CLASS=y | ||
| 347 | ---help--- | ||
| 348 | Provide access to PicoLCD's GPO pins via leds class. | ||
| 349 | |||
| 277 | config HID_QUANTA | 350 | config HID_QUANTA |
| 278 | tristate "Quanta Optical Touch" | 351 | tristate "Quanta Optical Touch" |
| 279 | depends on USB_HID | 352 | depends on USB_HID |
| 280 | ---help--- | 353 | ---help--- |
| 281 | Support for Quanta Optical Touch dual-touch panels. | 354 | Support for Quanta Optical Touch dual-touch panels. |
| 282 | 355 | ||
| 356 | config HID_ROCCAT_KONE | ||
| 357 | tristate "Roccat Kone Mouse support" | ||
| 358 | depends on USB_HID | ||
| 359 | ---help--- | ||
| 360 | Support for Roccat Kone mouse. | ||
| 361 | |||
| 283 | config HID_SAMSUNG | 362 | config HID_SAMSUNG |
| 284 | tristate "Samsung" if EMBEDDED | 363 | tristate "Samsung" if EMBEDDED |
| 285 | depends on USB_HID | 364 | depends on USB_HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2e196c734d2a..22e47eaeea32 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
| @@ -43,9 +43,12 @@ obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o | |||
| 43 | obj-$(CONFIG_HID_MOSART) += hid-mosart.o | 43 | obj-$(CONFIG_HID_MOSART) += hid-mosart.o |
| 44 | obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o | 44 | obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o |
| 45 | obj-$(CONFIG_HID_ORTEK) += hid-ortek.o | 45 | obj-$(CONFIG_HID_ORTEK) += hid-ortek.o |
| 46 | obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o | ||
| 46 | obj-$(CONFIG_HID_QUANTA) += hid-quanta.o | 47 | obj-$(CONFIG_HID_QUANTA) += hid-quanta.o |
| 47 | obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o | 48 | obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o |
| 48 | obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o | 49 | obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o |
| 50 | obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o | ||
| 51 | obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o | ||
| 49 | obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o | 52 | obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o |
| 50 | obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o | 53 | obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o |
| 51 | obj-$(CONFIG_HID_SONY) += hid-sony.o | 54 | obj-$(CONFIG_HID_SONY) += hid-sony.o |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ef492d3d52ee..e10e314d38cc 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
| @@ -1287,6 +1287,7 @@ static const struct hid_device_id hid_blacklist[] = { | |||
| 1287 | { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, | 1287 | { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, |
| 1288 | { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) }, | 1288 | { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) }, |
| 1289 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, | 1289 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, |
| 1290 | { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, | ||
| 1290 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, | 1291 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, |
| 1291 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, | 1292 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, |
| 1292 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, | 1293 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, |
| @@ -1326,6 +1327,8 @@ static const struct hid_device_id hid_blacklist[] = { | |||
| 1326 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, | 1327 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, |
| 1327 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, | 1328 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, |
| 1328 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, | 1329 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, |
| 1330 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | ||
| 1331 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | ||
| 1329 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, | 1332 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, |
| 1330 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, | 1333 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, |
| 1331 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, | 1334 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, |
| @@ -1337,6 +1340,7 @@ static const struct hid_device_id hid_blacklist[] = { | |||
| 1337 | { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, | 1340 | { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, |
| 1338 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, | 1341 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, |
| 1339 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, | 1342 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, |
| 1343 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, | ||
| 1340 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, | 1344 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, |
| 1341 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, | 1345 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, |
| 1342 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, | 1346 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 16c2f866821a..9776896cc4fc 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
| @@ -156,6 +156,9 @@ | |||
| 156 | #define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 | 156 | #define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 |
| 157 | #define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff | 157 | #define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff |
| 158 | 158 | ||
| 159 | #define USB_VENDOR_ID_CREATIVELABS 0x041e | ||
| 160 | #define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801 | ||
| 161 | |||
| 159 | #define USB_VENDOR_ID_CYGNAL 0x10c4 | 162 | #define USB_VENDOR_ID_CYGNAL 0x10c4 |
| 160 | #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a | 163 | #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a |
| 161 | 164 | ||
| @@ -354,6 +357,8 @@ | |||
| 354 | #define USB_VENDOR_ID_MICROCHIP 0x04d8 | 357 | #define USB_VENDOR_ID_MICROCHIP 0x04d8 |
| 355 | #define USB_DEVICE_ID_PICKIT1 0x0032 | 358 | #define USB_DEVICE_ID_PICKIT1 0x0032 |
| 356 | #define USB_DEVICE_ID_PICKIT2 0x0033 | 359 | #define USB_DEVICE_ID_PICKIT2 0x0033 |
| 360 | #define USB_DEVICE_ID_PICOLCD 0xc002 | ||
| 361 | #define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002 | ||
| 357 | 362 | ||
| 358 | #define USB_VENDOR_ID_MICROSOFT 0x045e | 363 | #define USB_VENDOR_ID_MICROSOFT 0x045e |
| 359 | #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b | 364 | #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b |
| @@ -412,6 +417,9 @@ | |||
| 412 | #define USB_VENDOR_ID_PRODIGE 0x05af | 417 | #define USB_VENDOR_ID_PRODIGE 0x05af |
| 413 | #define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 | 418 | #define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 |
| 414 | 419 | ||
| 420 | #define USB_VENDOR_ID_ROCCAT 0x1e7d | ||
| 421 | #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced | ||
| 422 | |||
| 415 | #define USB_VENDOR_ID_SAITEK 0x06a3 | 423 | #define USB_VENDOR_ID_SAITEK 0x06a3 |
| 416 | #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 | 424 | #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 |
| 417 | 425 | ||
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 4777bbfa1cc2..b6b0caeeac58 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c | |||
| @@ -24,6 +24,34 @@ | |||
| 24 | 24 | ||
| 25 | #define NTRIG_DUPLICATE_USAGES 0x001 | 25 | #define NTRIG_DUPLICATE_USAGES 0x001 |
| 26 | 26 | ||
| 27 | static unsigned int min_width; | ||
| 28 | module_param(min_width, uint, 0644); | ||
| 29 | MODULE_PARM_DESC(min_width, "Minimum touch contact width to accept."); | ||
| 30 | |||
| 31 | static unsigned int min_height; | ||
| 32 | module_param(min_height, uint, 0644); | ||
| 33 | MODULE_PARM_DESC(min_height, "Minimum touch contact height to accept."); | ||
| 34 | |||
| 35 | static unsigned int activate_slack = 1; | ||
| 36 | module_param(activate_slack, uint, 0644); | ||
| 37 | MODULE_PARM_DESC(activate_slack, "Number of touch frames to ignore at " | ||
| 38 | "the start of touch input."); | ||
| 39 | |||
| 40 | static unsigned int deactivate_slack = 4; | ||
| 41 | module_param(deactivate_slack, uint, 0644); | ||
| 42 | MODULE_PARM_DESC(deactivate_slack, "Number of empty frames to ignore before " | ||
| 43 | "deactivating touch."); | ||
| 44 | |||
| 45 | static unsigned int activation_width = 64; | ||
| 46 | module_param(activation_width, uint, 0644); | ||
| 47 | MODULE_PARM_DESC(activation_width, "Width threshold to immediately start " | ||
| 48 | "processing touch events."); | ||
| 49 | |||
| 50 | static unsigned int activation_height = 32; | ||
| 51 | module_param(activation_height, uint, 0644); | ||
| 52 | MODULE_PARM_DESC(activation_height, "Height threshold to immediately start " | ||
| 53 | "processing touch events."); | ||
| 54 | |||
| 27 | struct ntrig_data { | 55 | struct ntrig_data { |
| 28 | /* Incoming raw values for a single contact */ | 56 | /* Incoming raw values for a single contact */ |
| 29 | __u16 x, y, w, h; | 57 | __u16 x, y, w, h; |
| @@ -37,6 +65,309 @@ struct ntrig_data { | |||
| 37 | 65 | ||
| 38 | __u8 mt_footer[4]; | 66 | __u8 mt_footer[4]; |
| 39 | __u8 mt_foot_count; | 67 | __u8 mt_foot_count; |
| 68 | |||
| 69 | /* The current activation state. */ | ||
| 70 | __s8 act_state; | ||
| 71 | |||
| 72 | /* Empty frames to ignore before recognizing the end of activity */ | ||
| 73 | __s8 deactivate_slack; | ||
| 74 | |||
| 75 | /* Frames to ignore before acknowledging the start of activity */ | ||
| 76 | __s8 activate_slack; | ||
| 77 | |||
| 78 | /* Minimum size contact to accept */ | ||
| 79 | __u16 min_width; | ||
| 80 | __u16 min_height; | ||
| 81 | |||
| 82 | /* Threshold to override activation slack */ | ||
| 83 | __u16 activation_width; | ||
| 84 | __u16 activation_height; | ||
| 85 | |||
| 86 | __u16 sensor_logical_width; | ||
| 87 | __u16 sensor_logical_height; | ||
| 88 | __u16 sensor_physical_width; | ||
| 89 | __u16 sensor_physical_height; | ||
| 90 | }; | ||
| 91 | |||
| 92 | |||
| 93 | static ssize_t show_phys_width(struct device *dev, | ||
| 94 | struct device_attribute *attr, | ||
| 95 | char *buf) | ||
| 96 | { | ||
| 97 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 98 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 99 | |||
| 100 | return sprintf(buf, "%d\n", nd->sensor_physical_width); | ||
| 101 | } | ||
| 102 | |||
| 103 | static DEVICE_ATTR(sensor_physical_width, S_IRUGO, show_phys_width, NULL); | ||
| 104 | |||
| 105 | static ssize_t show_phys_height(struct device *dev, | ||
| 106 | struct device_attribute *attr, | ||
| 107 | char *buf) | ||
| 108 | { | ||
| 109 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 110 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 111 | |||
| 112 | return sprintf(buf, "%d\n", nd->sensor_physical_height); | ||
| 113 | } | ||
| 114 | |||
| 115 | static DEVICE_ATTR(sensor_physical_height, S_IRUGO, show_phys_height, NULL); | ||
| 116 | |||
| 117 | static ssize_t show_log_width(struct device *dev, | ||
| 118 | struct device_attribute *attr, | ||
| 119 | char *buf) | ||
| 120 | { | ||
| 121 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 122 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 123 | |||
| 124 | return sprintf(buf, "%d\n", nd->sensor_logical_width); | ||
| 125 | } | ||
| 126 | |||
| 127 | static DEVICE_ATTR(sensor_logical_width, S_IRUGO, show_log_width, NULL); | ||
| 128 | |||
| 129 | static ssize_t show_log_height(struct device *dev, | ||
| 130 | struct device_attribute *attr, | ||
| 131 | char *buf) | ||
| 132 | { | ||
| 133 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 134 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 135 | |||
| 136 | return sprintf(buf, "%d\n", nd->sensor_logical_height); | ||
| 137 | } | ||
| 138 | |||
| 139 | static DEVICE_ATTR(sensor_logical_height, S_IRUGO, show_log_height, NULL); | ||
| 140 | |||
| 141 | static ssize_t show_min_width(struct device *dev, | ||
| 142 | struct device_attribute *attr, | ||
| 143 | char *buf) | ||
| 144 | { | ||
| 145 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 146 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 147 | |||
| 148 | return sprintf(buf, "%d\n", nd->min_width * | ||
| 149 | nd->sensor_physical_width / | ||
| 150 | nd->sensor_logical_width); | ||
| 151 | } | ||
| 152 | |||
| 153 | static ssize_t set_min_width(struct device *dev, | ||
| 154 | struct device_attribute *attr, | ||
| 155 | const char *buf, size_t count) | ||
| 156 | { | ||
| 157 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 158 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 159 | |||
| 160 | unsigned long val; | ||
| 161 | |||
| 162 | if (strict_strtoul(buf, 0, &val)) | ||
| 163 | return -EINVAL; | ||
| 164 | |||
| 165 | if (val > nd->sensor_physical_width) | ||
| 166 | return -EINVAL; | ||
| 167 | |||
| 168 | nd->min_width = val * nd->sensor_logical_width / | ||
| 169 | nd->sensor_physical_width; | ||
| 170 | |||
| 171 | return count; | ||
| 172 | } | ||
| 173 | |||
| 174 | static DEVICE_ATTR(min_width, S_IWUSR | S_IRUGO, show_min_width, set_min_width); | ||
| 175 | |||
| 176 | static ssize_t show_min_height(struct device *dev, | ||
| 177 | struct device_attribute *attr, | ||
| 178 | char *buf) | ||
| 179 | { | ||
| 180 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 181 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 182 | |||
| 183 | return sprintf(buf, "%d\n", nd->min_height * | ||
| 184 | nd->sensor_physical_height / | ||
| 185 | nd->sensor_logical_height); | ||
| 186 | } | ||
| 187 | |||
| 188 | static ssize_t set_min_height(struct device *dev, | ||
| 189 | struct device_attribute *attr, | ||
| 190 | const char *buf, size_t count) | ||
| 191 | { | ||
| 192 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 193 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 194 | |||
| 195 | unsigned long val; | ||
| 196 | |||
| 197 | if (strict_strtoul(buf, 0, &val)) | ||
| 198 | return -EINVAL; | ||
| 199 | |||
| 200 | if (val > nd->sensor_physical_height) | ||
| 201 | return -EINVAL; | ||
| 202 | |||
| 203 | nd->min_height = val * nd->sensor_logical_height / | ||
| 204 | nd->sensor_physical_height; | ||
| 205 | |||
| 206 | return count; | ||
| 207 | } | ||
| 208 | |||
| 209 | static DEVICE_ATTR(min_height, S_IWUSR | S_IRUGO, show_min_height, | ||
| 210 | set_min_height); | ||
| 211 | |||
| 212 | static ssize_t show_activate_slack(struct device *dev, | ||
| 213 | struct device_attribute *attr, | ||
| 214 | char *buf) | ||
| 215 | { | ||
| 216 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 217 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 218 | |||
| 219 | return sprintf(buf, "%d\n", nd->activate_slack); | ||
| 220 | } | ||
| 221 | |||
| 222 | static ssize_t set_activate_slack(struct device *dev, | ||
| 223 | struct device_attribute *attr, | ||
| 224 | const char *buf, size_t count) | ||
| 225 | { | ||
| 226 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 227 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 228 | |||
| 229 | unsigned long val; | ||
| 230 | |||
| 231 | if (strict_strtoul(buf, 0, &val)) | ||
| 232 | return -EINVAL; | ||
| 233 | |||
| 234 | if (val > 0x7f) | ||
| 235 | return -EINVAL; | ||
| 236 | |||
| 237 | nd->activate_slack = val; | ||
| 238 | |||
| 239 | return count; | ||
| 240 | } | ||
| 241 | |||
| 242 | static DEVICE_ATTR(activate_slack, S_IWUSR | S_IRUGO, show_activate_slack, | ||
| 243 | set_activate_slack); | ||
| 244 | |||
| 245 | static ssize_t show_activation_width(struct device *dev, | ||
| 246 | struct device_attribute *attr, | ||
| 247 | char *buf) | ||
| 248 | { | ||
| 249 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 250 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 251 | |||
| 252 | return sprintf(buf, "%d\n", nd->activation_width * | ||
| 253 | nd->sensor_physical_width / | ||
| 254 | nd->sensor_logical_width); | ||
| 255 | } | ||
| 256 | |||
| 257 | static ssize_t set_activation_width(struct device *dev, | ||
| 258 | struct device_attribute *attr, | ||
| 259 | const char *buf, size_t count) | ||
| 260 | { | ||
| 261 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 262 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 263 | |||
| 264 | unsigned long val; | ||
| 265 | |||
| 266 | if (strict_strtoul(buf, 0, &val)) | ||
| 267 | return -EINVAL; | ||
| 268 | |||
| 269 | if (val > nd->sensor_physical_width) | ||
| 270 | return -EINVAL; | ||
| 271 | |||
| 272 | nd->activation_width = val * nd->sensor_logical_width / | ||
| 273 | nd->sensor_physical_width; | ||
| 274 | |||
| 275 | return count; | ||
| 276 | } | ||
| 277 | |||
| 278 | static DEVICE_ATTR(activation_width, S_IWUSR | S_IRUGO, show_activation_width, | ||
| 279 | set_activation_width); | ||
| 280 | |||
| 281 | static ssize_t show_activation_height(struct device *dev, | ||
| 282 | struct device_attribute *attr, | ||
| 283 | char *buf) | ||
| 284 | { | ||
| 285 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 286 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 287 | |||
| 288 | return sprintf(buf, "%d\n", nd->activation_height * | ||
| 289 | nd->sensor_physical_height / | ||
| 290 | nd->sensor_logical_height); | ||
| 291 | } | ||
| 292 | |||
| 293 | static ssize_t set_activation_height(struct device *dev, | ||
| 294 | struct device_attribute *attr, | ||
| 295 | const char *buf, size_t count) | ||
| 296 | { | ||
| 297 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 298 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 299 | |||
| 300 | unsigned long val; | ||
| 301 | |||
| 302 | if (strict_strtoul(buf, 0, &val)) | ||
| 303 | return -EINVAL; | ||
| 304 | |||
| 305 | if (val > nd->sensor_physical_height) | ||
| 306 | return -EINVAL; | ||
| 307 | |||
| 308 | nd->activation_height = val * nd->sensor_logical_height / | ||
| 309 | nd->sensor_physical_height; | ||
| 310 | |||
| 311 | return count; | ||
| 312 | } | ||
| 313 | |||
| 314 | static DEVICE_ATTR(activation_height, S_IWUSR | S_IRUGO, | ||
| 315 | show_activation_height, set_activation_height); | ||
| 316 | |||
| 317 | static ssize_t show_deactivate_slack(struct device *dev, | ||
| 318 | struct device_attribute *attr, | ||
| 319 | char *buf) | ||
| 320 | { | ||
| 321 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 322 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 323 | |||
| 324 | return sprintf(buf, "%d\n", -nd->deactivate_slack); | ||
| 325 | } | ||
| 326 | |||
| 327 | static ssize_t set_deactivate_slack(struct device *dev, | ||
| 328 | struct device_attribute *attr, | ||
| 329 | const char *buf, size_t count) | ||
| 330 | { | ||
| 331 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 332 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 333 | |||
| 334 | unsigned long val; | ||
| 335 | |||
| 336 | if (strict_strtoul(buf, 0, &val)) | ||
| 337 | return -EINVAL; | ||
| 338 | |||
| 339 | /* | ||
| 340 | * No more than 8 terminal frames have been observed so far | ||
| 341 | * and higher slack is highly likely to leave the single | ||
| 342 | * touch emulation stuck down. | ||
| 343 | */ | ||
| 344 | if (val > 7) | ||
| 345 | return -EINVAL; | ||
| 346 | |||
| 347 | nd->deactivate_slack = -val; | ||
| 348 | |||
| 349 | return count; | ||
| 350 | } | ||
| 351 | |||
| 352 | static DEVICE_ATTR(deactivate_slack, S_IWUSR | S_IRUGO, show_deactivate_slack, | ||
| 353 | set_deactivate_slack); | ||
| 354 | |||
| 355 | static struct attribute *sysfs_attrs[] = { | ||
| 356 | &dev_attr_sensor_physical_width.attr, | ||
| 357 | &dev_attr_sensor_physical_height.attr, | ||
| 358 | &dev_attr_sensor_logical_width.attr, | ||
| 359 | &dev_attr_sensor_logical_height.attr, | ||
| 360 | &dev_attr_min_height.attr, | ||
| 361 | &dev_attr_min_width.attr, | ||
| 362 | &dev_attr_activate_slack.attr, | ||
| 363 | &dev_attr_activation_width.attr, | ||
| 364 | &dev_attr_activation_height.attr, | ||
| 365 | &dev_attr_deactivate_slack.attr, | ||
| 366 | NULL | ||
| 367 | }; | ||
| 368 | |||
| 369 | static struct attribute_group ntrig_attribute_group = { | ||
| 370 | .attrs = sysfs_attrs | ||
| 40 | }; | 371 | }; |
| 41 | 372 | ||
| 42 | /* | 373 | /* |
| @@ -49,6 +380,8 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 49 | struct hid_field *field, struct hid_usage *usage, | 380 | struct hid_field *field, struct hid_usage *usage, |
| 50 | unsigned long **bit, int *max) | 381 | unsigned long **bit, int *max) |
| 51 | { | 382 | { |
| 383 | struct ntrig_data *nd = hid_get_drvdata(hdev); | ||
| 384 | |||
| 52 | /* No special mappings needed for the pen and single touch */ | 385 | /* No special mappings needed for the pen and single touch */ |
| 53 | if (field->physical) | 386 | if (field->physical) |
| 54 | return 0; | 387 | return 0; |
| @@ -62,6 +395,21 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 62 | input_set_abs_params(hi->input, ABS_X, | 395 | input_set_abs_params(hi->input, ABS_X, |
| 63 | field->logical_minimum, | 396 | field->logical_minimum, |
| 64 | field->logical_maximum, 0, 0); | 397 | field->logical_maximum, 0, 0); |
| 398 | |||
| 399 | if (!nd->sensor_logical_width) { | ||
| 400 | nd->sensor_logical_width = | ||
| 401 | field->logical_maximum - | ||
| 402 | field->logical_minimum; | ||
| 403 | nd->sensor_physical_width = | ||
| 404 | field->physical_maximum - | ||
| 405 | field->physical_minimum; | ||
| 406 | nd->activation_width = activation_width * | ||
| 407 | nd->sensor_logical_width / | ||
| 408 | nd->sensor_physical_width; | ||
| 409 | nd->min_width = min_width * | ||
| 410 | nd->sensor_logical_width / | ||
| 411 | nd->sensor_physical_width; | ||
| 412 | } | ||
| 65 | return 1; | 413 | return 1; |
| 66 | case HID_GD_Y: | 414 | case HID_GD_Y: |
| 67 | hid_map_usage(hi, usage, bit, max, | 415 | hid_map_usage(hi, usage, bit, max, |
| @@ -69,6 +417,21 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
| 69 | input_set_abs_params(hi->input, ABS_Y, | 417 | input_set_abs_params(hi->input, ABS_Y, |
| 70 | field->logical_minimum, | 418 | field->logical_minimum, |
| 71 | field->logical_maximum, 0, 0); | 419 | field->logical_maximum, 0, 0); |
| 420 | |||
| 421 | if (!nd->sensor_logical_height) { | ||
| 422 | nd->sensor_logical_height = | ||
| 423 | field->logical_maximum - | ||
| 424 | field->logical_minimum; | ||
| 425 | nd->sensor_physical_height = | ||
| 426 | field->physical_maximum - | ||
| 427 | field->physical_minimum; | ||
| 428 | nd->activation_height = activation_height * | ||
| 429 | nd->sensor_logical_height / | ||
| 430 | nd->sensor_physical_height; | ||
| 431 | nd->min_height = min_height * | ||
| 432 | nd->sensor_logical_height / | ||
| 433 | nd->sensor_physical_height; | ||
| 434 | } | ||
| 72 | return 1; | 435 | return 1; |
| 73 | } | 436 | } |
| 74 | return 0; | 437 | return 0; |
| @@ -201,20 +564,68 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, | |||
| 201 | if (nd->mt_foot_count != 4) | 564 | if (nd->mt_foot_count != 4) |
| 202 | break; | 565 | break; |
| 203 | 566 | ||
| 204 | /* Pen activity signal, trigger end of touch. */ | 567 | /* Pen activity signal. */ |
| 205 | if (nd->mt_footer[2]) { | 568 | if (nd->mt_footer[2]) { |
| 569 | /* | ||
| 570 | * When the pen deactivates touch, we see a | ||
| 571 | * bogus frame with ContactCount > 0. | ||
| 572 | * We can | ||
| 573 | * save a bit of work by ensuring act_state < 0 | ||
| 574 | * even if deactivation slack is turned off. | ||
| 575 | */ | ||
| 576 | nd->act_state = deactivate_slack - 1; | ||
| 206 | nd->confidence = 0; | 577 | nd->confidence = 0; |
| 207 | break; | 578 | break; |
| 208 | } | 579 | } |
| 209 | 580 | ||
| 210 | /* If the contact was invalid */ | 581 | /* |
| 211 | if (!(nd->confidence && nd->mt_footer[0]) | 582 | * The first footer value indicates the presence of a |
| 212 | || nd->w <= 250 | 583 | * finger. |
| 213 | || nd->h <= 190) { | 584 | */ |
| 214 | nd->confidence = 0; | 585 | if (nd->mt_footer[0]) { |
| 586 | /* | ||
| 587 | * We do not want to process contacts under | ||
| 588 | * the size threshold, but do not want to | ||
| 589 | * ignore them for activation state | ||
| 590 | */ | ||
| 591 | if (nd->w < nd->min_width || | ||
| 592 | nd->h < nd->min_height) | ||
| 593 | nd->confidence = 0; | ||
| 594 | } else | ||
| 215 | break; | 595 | break; |
| 596 | |||
| 597 | if (nd->act_state > 0) { | ||
| 598 | /* | ||
| 599 | * Contact meets the activation size threshold | ||
| 600 | */ | ||
| 601 | if (nd->w >= nd->activation_width && | ||
| 602 | nd->h >= nd->activation_height) { | ||
| 603 | if (nd->id) | ||
| 604 | /* | ||
| 605 | * first contact, activate now | ||
| 606 | */ | ||
| 607 | nd->act_state = 0; | ||
| 608 | else { | ||
| 609 | /* | ||
| 610 | * avoid corrupting this frame | ||
| 611 | * but ensure next frame will | ||
| 612 | * be active | ||
| 613 | */ | ||
| 614 | nd->act_state = 1; | ||
| 615 | break; | ||
| 616 | } | ||
| 617 | } else | ||
| 618 | /* | ||
| 619 | * Defer adjusting the activation state | ||
| 620 | * until the end of the frame. | ||
| 621 | */ | ||
| 622 | break; | ||
| 216 | } | 623 | } |
| 217 | 624 | ||
| 625 | /* Discarding this contact */ | ||
| 626 | if (!nd->confidence) | ||
| 627 | break; | ||
| 628 | |||
| 218 | /* emit a normal (X, Y) for the first point only */ | 629 | /* emit a normal (X, Y) for the first point only */ |
| 219 | if (nd->id == 0) { | 630 | if (nd->id == 0) { |
| 220 | /* | 631 | /* |
| @@ -227,8 +638,15 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, | |||
| 227 | input_event(input, EV_ABS, ABS_X, nd->x); | 638 | input_event(input, EV_ABS, ABS_X, nd->x); |
| 228 | input_event(input, EV_ABS, ABS_Y, nd->y); | 639 | input_event(input, EV_ABS, ABS_Y, nd->y); |
| 229 | } | 640 | } |
| 641 | |||
| 642 | /* Emit MT events */ | ||
| 230 | input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); | 643 | input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); |
| 231 | input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); | 644 | input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); |
| 645 | |||
| 646 | /* | ||
| 647 | * Translate from height and width to size | ||
| 648 | * and orientation. | ||
| 649 | */ | ||
| 232 | if (nd->w > nd->h) { | 650 | if (nd->w > nd->h) { |
| 233 | input_event(input, EV_ABS, | 651 | input_event(input, EV_ABS, |
| 234 | ABS_MT_ORIENTATION, 1); | 652 | ABS_MT_ORIENTATION, 1); |
| @@ -248,12 +666,88 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, | |||
| 248 | break; | 666 | break; |
| 249 | 667 | ||
| 250 | case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ | 668 | case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ |
| 251 | if (!nd->reading_mt) | 669 | if (!nd->reading_mt) /* Just to be sure */ |
| 252 | break; | 670 | break; |
| 253 | 671 | ||
| 254 | nd->reading_mt = 0; | 672 | nd->reading_mt = 0; |
| 255 | 673 | ||
| 256 | if (nd->first_contact_touch) { | 674 | |
| 675 | /* | ||
| 676 | * Activation state machine logic: | ||
| 677 | * | ||
| 678 | * Fundamental states: | ||
| 679 | * state > 0: Inactive | ||
| 680 | * state <= 0: Active | ||
| 681 | * state < -deactivate_slack: | ||
| 682 | * Pen termination of touch | ||
| 683 | * | ||
| 684 | * Specific values of interest | ||
| 685 | * state == activate_slack | ||
| 686 | * no valid input since the last reset | ||
| 687 | * | ||
| 688 | * state == 0 | ||
| 689 | * general operational state | ||
| 690 | * | ||
| 691 | * state == -deactivate_slack | ||
| 692 | * read sufficient empty frames to accept | ||
| 693 | * the end of input and reset | ||
| 694 | */ | ||
| 695 | |||
| 696 | if (nd->act_state > 0) { /* Currently inactive */ | ||
| 697 | if (value) | ||
| 698 | /* | ||
| 699 | * Consider each live contact as | ||
| 700 | * evidence of intentional activity. | ||
| 701 | */ | ||
| 702 | nd->act_state = (nd->act_state > value) | ||
| 703 | ? nd->act_state - value | ||
| 704 | : 0; | ||
| 705 | else | ||
| 706 | /* | ||
| 707 | * Empty frame before we hit the | ||
| 708 | * activity threshold, reset. | ||
| 709 | */ | ||
| 710 | nd->act_state = nd->activate_slack; | ||
| 711 | |||
| 712 | /* | ||
| 713 | * Entered this block inactive and no | ||
| 714 | * coordinates sent this frame, so hold off | ||
| 715 | * on button state. | ||
| 716 | */ | ||
| 717 | break; | ||
| 718 | } else { /* Currently active */ | ||
| 719 | if (value && nd->act_state >= | ||
| 720 | nd->deactivate_slack) | ||
| 721 | /* | ||
| 722 | * Live point: clear accumulated | ||
| 723 | * deactivation count. | ||
| 724 | */ | ||
| 725 | nd->act_state = 0; | ||
| 726 | else if (nd->act_state <= nd->deactivate_slack) | ||
| 727 | /* | ||
| 728 | * We've consumed the deactivation | ||
| 729 | * slack, time to deactivate and reset. | ||
| 730 | */ | ||
| 731 | nd->act_state = | ||
| 732 | nd->activate_slack; | ||
| 733 | else { /* Move towards deactivation */ | ||
| 734 | nd->act_state--; | ||
| 735 | break; | ||
| 736 | } | ||
| 737 | } | ||
| 738 | |||
| 739 | if (nd->first_contact_touch && nd->act_state <= 0) { | ||
| 740 | /* | ||
| 741 | * Check to see if we're ready to start | ||
| 742 | * emitting touch events. | ||
| 743 | * | ||
| 744 | * Note: activation slack will decrease over | ||
| 745 | * the course of the frame, and it will be | ||
| 746 | * inconsistent from the start to the end of | ||
| 747 | * the frame. However if the frame starts | ||
| 748 | * with slack, first_contact_touch will still | ||
| 749 | * be 0 and we will not get to this point. | ||
| 750 | */ | ||
| 257 | input_report_key(input, BTN_TOOL_DOUBLETAP, 1); | 751 | input_report_key(input, BTN_TOOL_DOUBLETAP, 1); |
| 258 | input_report_key(input, BTN_TOUCH, 1); | 752 | input_report_key(input, BTN_TOUCH, 1); |
| 259 | } else { | 753 | } else { |
| @@ -263,7 +757,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, | |||
| 263 | break; | 757 | break; |
| 264 | 758 | ||
| 265 | default: | 759 | default: |
| 266 | /* fallback to the generic hidinput handling */ | 760 | /* fall-back to the generic hidinput handling */ |
| 267 | return 0; | 761 | return 0; |
| 268 | } | 762 | } |
| 269 | } | 763 | } |
| @@ -293,6 +787,16 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 293 | } | 787 | } |
| 294 | 788 | ||
| 295 | nd->reading_mt = 0; | 789 | nd->reading_mt = 0; |
| 790 | nd->min_width = 0; | ||
| 791 | nd->min_height = 0; | ||
| 792 | nd->activate_slack = activate_slack; | ||
| 793 | nd->act_state = activate_slack; | ||
| 794 | nd->deactivate_slack = -deactivate_slack; | ||
| 795 | nd->sensor_logical_width = 0; | ||
| 796 | nd->sensor_logical_height = 0; | ||
| 797 | nd->sensor_physical_width = 0; | ||
| 798 | nd->sensor_physical_height = 0; | ||
| 799 | |||
| 296 | hid_set_drvdata(hdev, nd); | 800 | hid_set_drvdata(hdev, nd); |
| 297 | 801 | ||
| 298 | ret = hid_parse(hdev); | 802 | ret = hid_parse(hdev); |
| @@ -344,6 +848,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 344 | if (report) | 848 | if (report) |
| 345 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | 849 | usbhid_submit_report(hdev, report, USB_DIR_OUT); |
| 346 | 850 | ||
| 851 | ret = sysfs_create_group(&hdev->dev.kobj, | ||
| 852 | &ntrig_attribute_group); | ||
| 347 | 853 | ||
| 348 | return 0; | 854 | return 0; |
| 349 | err_free: | 855 | err_free: |
| @@ -353,6 +859,8 @@ err_free: | |||
| 353 | 859 | ||
| 354 | static void ntrig_remove(struct hid_device *hdev) | 860 | static void ntrig_remove(struct hid_device *hdev) |
| 355 | { | 861 | { |
| 862 | sysfs_remove_group(&hdev->dev.kobj, | ||
| 863 | &ntrig_attribute_group); | ||
| 356 | hid_hw_stop(hdev); | 864 | hid_hw_stop(hdev); |
| 357 | kfree(hid_get_drvdata(hdev)); | 865 | kfree(hid_get_drvdata(hdev)); |
| 358 | } | 866 | } |
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c new file mode 100644 index 000000000000..7aabf65c48ef --- /dev/null +++ b/drivers/hid/hid-picolcd.c | |||
| @@ -0,0 +1,2631 @@ | |||
| 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 | |||
| 40 | #define PICOLCD_NAME "PicoLCD (graphic)" | ||
| 41 | |||
| 42 | /* Report numbers */ | ||
| 43 | #define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */ | ||
| 44 | #define ERR_SUCCESS 0x00 | ||
| 45 | #define ERR_PARAMETER_MISSING 0x01 | ||
| 46 | #define ERR_DATA_MISSING 0x02 | ||
| 47 | #define ERR_BLOCK_READ_ONLY 0x03 | ||
| 48 | #define ERR_BLOCK_NOT_ERASABLE 0x04 | ||
| 49 | #define ERR_BLOCK_TOO_BIG 0x05 | ||
| 50 | #define ERR_SECTION_OVERFLOW 0x06 | ||
| 51 | #define ERR_INVALID_CMD_LEN 0x07 | ||
| 52 | #define ERR_INVALID_DATA_LEN 0x08 | ||
| 53 | #define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */ | ||
| 54 | #define REPORT_IR_DATA 0x21 /* LCD: IN[63] */ | ||
| 55 | #define REPORT_EE_DATA 0x32 /* LCD: IN[63] */ | ||
| 56 | #define REPORT_MEMORY 0x41 /* LCD: IN[63] */ | ||
| 57 | #define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */ | ||
| 58 | #define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */ | ||
| 59 | #define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */ | ||
| 60 | #define REPORT_RESET 0x93 /* LCD: OUT[2] */ | ||
| 61 | #define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */ | ||
| 62 | #define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */ | ||
| 63 | #define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */ | ||
| 64 | #define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */ | ||
| 65 | #define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */ | ||
| 66 | #define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */ | ||
| 67 | #define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */ | ||
| 68 | #define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */ | ||
| 69 | #define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */ | ||
| 70 | #define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */ | ||
| 71 | #define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */ | ||
| 72 | #define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */ | ||
| 73 | #define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */ | ||
| 74 | #define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */ | ||
| 75 | #define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */ | ||
| 76 | #define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */ | ||
| 77 | #define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */ | ||
| 78 | #define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */ | ||
| 79 | |||
| 80 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 81 | /* Framebuffer | ||
| 82 | * | ||
| 83 | * The PicoLCD use a Topway LCD module of 256x64 pixel | ||
| 84 | * This display area is tiled over 4 controllers with 8 tiles | ||
| 85 | * each. Each tile has 8x64 pixel, each data byte representing | ||
| 86 | * a 1-bit wide vertical line of the tile. | ||
| 87 | * | ||
| 88 | * The display can be updated at a tile granularity. | ||
| 89 | * | ||
| 90 | * Chip 1 Chip 2 Chip 3 Chip 4 | ||
| 91 | * +----------------+----------------+----------------+----------------+ | ||
| 92 | * | Tile 1 | Tile 1 | Tile 1 | Tile 1 | | ||
| 93 | * +----------------+----------------+----------------+----------------+ | ||
| 94 | * | Tile 2 | Tile 2 | Tile 2 | Tile 2 | | ||
| 95 | * +----------------+----------------+----------------+----------------+ | ||
| 96 | * ... | ||
| 97 | * +----------------+----------------+----------------+----------------+ | ||
| 98 | * | Tile 8 | Tile 8 | Tile 8 | Tile 8 | | ||
| 99 | * +----------------+----------------+----------------+----------------+ | ||
| 100 | */ | ||
| 101 | #define PICOLCDFB_NAME "picolcdfb" | ||
| 102 | #define PICOLCDFB_WIDTH (256) | ||
| 103 | #define PICOLCDFB_HEIGHT (64) | ||
| 104 | #define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8) | ||
| 105 | |||
| 106 | #define PICOLCDFB_UPDATE_RATE_LIMIT 10 | ||
| 107 | #define PICOLCDFB_UPDATE_RATE_DEFAULT 2 | ||
| 108 | |||
| 109 | /* Framebuffer visual structures */ | ||
| 110 | static const struct fb_fix_screeninfo picolcdfb_fix = { | ||
| 111 | .id = PICOLCDFB_NAME, | ||
| 112 | .type = FB_TYPE_PACKED_PIXELS, | ||
| 113 | .visual = FB_VISUAL_MONO01, | ||
| 114 | .xpanstep = 0, | ||
| 115 | .ypanstep = 0, | ||
| 116 | .ywrapstep = 0, | ||
| 117 | .line_length = PICOLCDFB_WIDTH / 8, | ||
| 118 | .accel = FB_ACCEL_NONE, | ||
| 119 | }; | ||
| 120 | |||
| 121 | static const struct fb_var_screeninfo picolcdfb_var = { | ||
| 122 | .xres = PICOLCDFB_WIDTH, | ||
| 123 | .yres = PICOLCDFB_HEIGHT, | ||
| 124 | .xres_virtual = PICOLCDFB_WIDTH, | ||
| 125 | .yres_virtual = PICOLCDFB_HEIGHT, | ||
| 126 | .width = 103, | ||
| 127 | .height = 26, | ||
| 128 | .bits_per_pixel = 1, | ||
| 129 | .grayscale = 1, | ||
| 130 | }; | ||
| 131 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 132 | |||
| 133 | /* Input device | ||
| 134 | * | ||
| 135 | * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys | ||
| 136 | * and header for 4x4 key matrix. The built-in keys are part of the matrix. | ||
| 137 | */ | ||
| 138 | static const unsigned short def_keymap[] = { | ||
| 139 | KEY_RESERVED, /* none */ | ||
| 140 | KEY_BACK, /* col 4 + row 1 */ | ||
| 141 | KEY_HOMEPAGE, /* col 3 + row 1 */ | ||
| 142 | KEY_RESERVED, /* col 2 + row 1 */ | ||
| 143 | KEY_RESERVED, /* col 1 + row 1 */ | ||
| 144 | KEY_SCROLLUP, /* col 4 + row 2 */ | ||
| 145 | KEY_OK, /* col 3 + row 2 */ | ||
| 146 | KEY_SCROLLDOWN, /* col 2 + row 2 */ | ||
| 147 | KEY_RESERVED, /* col 1 + row 2 */ | ||
| 148 | KEY_RESERVED, /* col 4 + row 3 */ | ||
| 149 | KEY_RESERVED, /* col 3 + row 3 */ | ||
| 150 | KEY_RESERVED, /* col 2 + row 3 */ | ||
| 151 | KEY_RESERVED, /* col 1 + row 3 */ | ||
| 152 | KEY_RESERVED, /* col 4 + row 4 */ | ||
| 153 | KEY_RESERVED, /* col 3 + row 4 */ | ||
| 154 | KEY_RESERVED, /* col 2 + row 4 */ | ||
| 155 | KEY_RESERVED, /* col 1 + row 4 */ | ||
| 156 | }; | ||
| 157 | #define PICOLCD_KEYS ARRAY_SIZE(def_keymap) | ||
| 158 | |||
| 159 | /* Description of in-progress IO operation, used for operations | ||
| 160 | * that trigger response from device */ | ||
| 161 | struct picolcd_pending { | ||
| 162 | struct hid_report *out_report; | ||
| 163 | struct hid_report *in_report; | ||
| 164 | struct completion ready; | ||
| 165 | int raw_size; | ||
| 166 | u8 raw_data[64]; | ||
| 167 | }; | ||
| 168 | |||
| 169 | /* Per device data structure */ | ||
| 170 | struct picolcd_data { | ||
| 171 | struct hid_device *hdev; | ||
| 172 | #ifdef CONFIG_DEBUG_FS | ||
| 173 | struct dentry *debug_reset; | ||
| 174 | struct dentry *debug_eeprom; | ||
| 175 | struct dentry *debug_flash; | ||
| 176 | struct mutex mutex_flash; | ||
| 177 | int addr_sz; | ||
| 178 | #endif | ||
| 179 | u8 version[2]; | ||
| 180 | unsigned short opmode_delay; | ||
| 181 | /* input stuff */ | ||
| 182 | u8 pressed_keys[2]; | ||
| 183 | struct input_dev *input_keys; | ||
| 184 | struct input_dev *input_cir; | ||
| 185 | unsigned short keycode[PICOLCD_KEYS]; | ||
| 186 | |||
| 187 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 188 | /* Framebuffer stuff */ | ||
| 189 | u8 fb_update_rate; | ||
| 190 | u8 fb_bpp; | ||
| 191 | u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */ | ||
| 192 | u8 *fb_bitmap; /* framebuffer */ | ||
| 193 | struct fb_info *fb_info; | ||
| 194 | struct fb_deferred_io fb_defio; | ||
| 195 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 196 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
| 197 | struct lcd_device *lcd; | ||
| 198 | u8 lcd_contrast; | ||
| 199 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
| 200 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
| 201 | struct backlight_device *backlight; | ||
| 202 | u8 lcd_brightness; | ||
| 203 | u8 lcd_power; | ||
| 204 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
| 205 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
| 206 | /* LED stuff */ | ||
| 207 | u8 led_state; | ||
| 208 | struct led_classdev *led[8]; | ||
| 209 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
| 210 | |||
| 211 | /* Housekeeping stuff */ | ||
| 212 | spinlock_t lock; | ||
| 213 | struct mutex mutex; | ||
| 214 | struct picolcd_pending *pending; | ||
| 215 | int status; | ||
| 216 | #define PICOLCD_BOOTLOADER 1 | ||
| 217 | #define PICOLCD_FAILED 2 | ||
| 218 | #define PICOLCD_READY_FB 4 | ||
| 219 | }; | ||
| 220 | |||
| 221 | |||
| 222 | /* Find a given report */ | ||
| 223 | #define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT) | ||
| 224 | #define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT) | ||
| 225 | |||
| 226 | static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir) | ||
| 227 | { | ||
| 228 | struct list_head *feature_report_list = &hdev->report_enum[dir].report_list; | ||
| 229 | struct hid_report *report = NULL; | ||
| 230 | |||
| 231 | list_for_each_entry(report, feature_report_list, list) { | ||
| 232 | if (report->id == id) | ||
| 233 | return report; | ||
| 234 | } | ||
| 235 | dev_warn(&hdev->dev, "No report with id 0x%x found\n", id); | ||
| 236 | return NULL; | ||
| 237 | } | ||
| 238 | |||
| 239 | #ifdef CONFIG_DEBUG_FS | ||
| 240 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
| 241 | struct hid_device *hdev, struct hid_report *report); | ||
| 242 | #define usbhid_submit_report(a, b, c) \ | ||
| 243 | do { \ | ||
| 244 | picolcd_debug_out_report(hid_get_drvdata(a), a, b); \ | ||
| 245 | usbhid_submit_report(a, b, c); \ | ||
| 246 | } while (0) | ||
| 247 | #endif | ||
| 248 | |||
| 249 | /* Submit a report and wait for a reply from device - if device fades away | ||
| 250 | * or does not respond in time, return NULL */ | ||
| 251 | static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, | ||
| 252 | int report_id, const u8 *raw_data, int size) | ||
| 253 | { | ||
| 254 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 255 | struct picolcd_pending *work; | ||
| 256 | struct hid_report *report = picolcd_out_report(report_id, hdev); | ||
| 257 | unsigned long flags; | ||
| 258 | int i, j, k; | ||
| 259 | |||
| 260 | if (!report || !data) | ||
| 261 | return NULL; | ||
| 262 | if (data->status & PICOLCD_FAILED) | ||
| 263 | return NULL; | ||
| 264 | work = kzalloc(sizeof(*work), GFP_KERNEL); | ||
| 265 | if (!work) | ||
| 266 | return NULL; | ||
| 267 | |||
| 268 | init_completion(&work->ready); | ||
| 269 | work->out_report = report; | ||
| 270 | work->in_report = NULL; | ||
| 271 | work->raw_size = 0; | ||
| 272 | |||
| 273 | mutex_lock(&data->mutex); | ||
| 274 | spin_lock_irqsave(&data->lock, flags); | ||
| 275 | for (i = k = 0; i < report->maxfield; i++) | ||
| 276 | for (j = 0; j < report->field[i]->report_count; j++) { | ||
| 277 | hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0); | ||
| 278 | k++; | ||
| 279 | } | ||
| 280 | data->pending = work; | ||
| 281 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 282 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 283 | wait_for_completion_interruptible_timeout(&work->ready, HZ*2); | ||
| 284 | spin_lock_irqsave(&data->lock, flags); | ||
| 285 | data->pending = NULL; | ||
| 286 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 287 | mutex_unlock(&data->mutex); | ||
| 288 | return work; | ||
| 289 | } | ||
| 290 | |||
| 291 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 292 | /* Send a given tile to PicoLCD */ | ||
| 293 | static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile) | ||
| 294 | { | ||
| 295 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 296 | struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev); | ||
| 297 | struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev); | ||
| 298 | unsigned long flags; | ||
| 299 | u8 *tdata; | ||
| 300 | int i; | ||
| 301 | |||
| 302 | if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1) | ||
| 303 | return -ENODEV; | ||
| 304 | |||
| 305 | spin_lock_irqsave(&data->lock, flags); | ||
| 306 | hid_set_field(report1->field[0], 0, chip << 2); | ||
| 307 | hid_set_field(report1->field[0], 1, 0x02); | ||
| 308 | hid_set_field(report1->field[0], 2, 0x00); | ||
| 309 | hid_set_field(report1->field[0], 3, 0x00); | ||
| 310 | hid_set_field(report1->field[0], 4, 0xb8 | tile); | ||
| 311 | hid_set_field(report1->field[0], 5, 0x00); | ||
| 312 | hid_set_field(report1->field[0], 6, 0x00); | ||
| 313 | hid_set_field(report1->field[0], 7, 0x40); | ||
| 314 | hid_set_field(report1->field[0], 8, 0x00); | ||
| 315 | hid_set_field(report1->field[0], 9, 0x00); | ||
| 316 | hid_set_field(report1->field[0], 10, 32); | ||
| 317 | |||
| 318 | hid_set_field(report2->field[0], 0, (chip << 2) | 0x01); | ||
| 319 | hid_set_field(report2->field[0], 1, 0x00); | ||
| 320 | hid_set_field(report2->field[0], 2, 0x00); | ||
| 321 | hid_set_field(report2->field[0], 3, 32); | ||
| 322 | |||
| 323 | tdata = data->fb_vbitmap + (tile * 4 + chip) * 64; | ||
| 324 | for (i = 0; i < 64; i++) | ||
| 325 | if (i < 32) | ||
| 326 | hid_set_field(report1->field[0], 11 + i, tdata[i]); | ||
| 327 | else | ||
| 328 | hid_set_field(report2->field[0], 4 + i - 32, tdata[i]); | ||
| 329 | |||
| 330 | usbhid_submit_report(data->hdev, report1, USB_DIR_OUT); | ||
| 331 | usbhid_submit_report(data->hdev, report2, USB_DIR_OUT); | ||
| 332 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 333 | return 0; | ||
| 334 | } | ||
| 335 | |||
| 336 | /* Translate a single tile*/ | ||
| 337 | static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp, | ||
| 338 | int chip, int tile) | ||
| 339 | { | ||
| 340 | int i, b, changed = 0; | ||
| 341 | u8 tdata[64]; | ||
| 342 | u8 *vdata = vbitmap + (tile * 4 + chip) * 64; | ||
| 343 | |||
| 344 | if (bpp == 1) { | ||
| 345 | for (b = 7; b >= 0; b--) { | ||
| 346 | const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32; | ||
| 347 | for (i = 0; i < 64; i++) { | ||
| 348 | tdata[i] <<= 1; | ||
| 349 | tdata[i] |= (bdata[i/8] >> (7 - i % 8)) & 0x01; | ||
| 350 | } | ||
| 351 | } | ||
| 352 | } else if (bpp == 8) { | ||
| 353 | for (b = 7; b >= 0; b--) { | ||
| 354 | const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8; | ||
| 355 | for (i = 0; i < 64; i++) { | ||
| 356 | tdata[i] <<= 1; | ||
| 357 | tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00; | ||
| 358 | } | ||
| 359 | } | ||
| 360 | } else { | ||
| 361 | /* Oops, we should never get here! */ | ||
| 362 | WARN_ON(1); | ||
| 363 | return 0; | ||
| 364 | } | ||
| 365 | |||
| 366 | for (i = 0; i < 64; i++) | ||
| 367 | if (tdata[i] != vdata[i]) { | ||
| 368 | changed = 1; | ||
| 369 | vdata[i] = tdata[i]; | ||
| 370 | } | ||
| 371 | return changed; | ||
| 372 | } | ||
| 373 | |||
| 374 | /* Reconfigure LCD display */ | ||
| 375 | static int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
| 376 | { | ||
| 377 | struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev); | ||
| 378 | int i, j; | ||
| 379 | unsigned long flags; | ||
| 380 | static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 }; | ||
| 381 | |||
| 382 | if (!report || report->maxfield != 1) | ||
| 383 | return -ENODEV; | ||
| 384 | |||
| 385 | spin_lock_irqsave(&data->lock, flags); | ||
| 386 | for (i = 0; i < 4; i++) { | ||
| 387 | for (j = 0; j < report->field[0]->maxusage; j++) | ||
| 388 | if (j == 0) | ||
| 389 | hid_set_field(report->field[0], j, i << 2); | ||
| 390 | else if (j < sizeof(mapcmd)) | ||
| 391 | hid_set_field(report->field[0], j, mapcmd[j]); | ||
| 392 | else | ||
| 393 | hid_set_field(report->field[0], j, 0); | ||
| 394 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 395 | } | ||
| 396 | |||
| 397 | data->status |= PICOLCD_READY_FB; | ||
| 398 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 399 | |||
| 400 | if (data->fb_bitmap) { | ||
| 401 | if (clear) { | ||
| 402 | memset(data->fb_vbitmap, 0xff, PICOLCDFB_SIZE); | ||
| 403 | memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp); | ||
| 404 | } else { | ||
| 405 | /* invert 1 byte in each tile to force resend */ | ||
| 406 | for (i = 0; i < PICOLCDFB_SIZE; i += 64) | ||
| 407 | data->fb_vbitmap[i] = ~data->fb_vbitmap[i]; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | /* schedule first output of framebuffer */ | ||
| 412 | if (data->fb_info) | ||
| 413 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
| 414 | |||
| 415 | return 0; | ||
| 416 | } | ||
| 417 | |||
| 418 | /* Update fb_vbitmap from the screen_base and send changed tiles to device */ | ||
| 419 | static void picolcd_fb_update(struct picolcd_data *data) | ||
| 420 | { | ||
| 421 | int chip, tile, n; | ||
| 422 | unsigned long flags; | ||
| 423 | |||
| 424 | spin_lock_irqsave(&data->lock, flags); | ||
| 425 | if (!(data->status & PICOLCD_READY_FB)) { | ||
| 426 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 427 | picolcd_fb_reset(data, 0); | ||
| 428 | } else { | ||
| 429 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 430 | } | ||
| 431 | |||
| 432 | /* | ||
| 433 | * Translate the framebuffer into the format needed by the PicoLCD. | ||
| 434 | * See display layout above. | ||
| 435 | * Do this one tile after the other and push those tiles that changed. | ||
| 436 | * | ||
| 437 | * Wait for our IO to complete as otherwise we might flood the queue! | ||
| 438 | */ | ||
| 439 | n = 0; | ||
| 440 | for (chip = 0; chip < 4; chip++) | ||
| 441 | for (tile = 0; tile < 8; tile++) | ||
| 442 | if (picolcd_fb_update_tile(data->fb_vbitmap, | ||
| 443 | data->fb_bitmap, data->fb_bpp, chip, tile)) { | ||
| 444 | n += 2; | ||
| 445 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { | ||
| 446 | usbhid_wait_io(data->hdev); | ||
| 447 | n = 0; | ||
| 448 | } | ||
| 449 | picolcd_fb_send_tile(data->hdev, chip, tile); | ||
| 450 | } | ||
| 451 | if (n) | ||
| 452 | usbhid_wait_io(data->hdev); | ||
| 453 | } | ||
| 454 | |||
| 455 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 456 | static void picolcd_fb_fillrect(struct fb_info *info, | ||
| 457 | const struct fb_fillrect *rect) | ||
| 458 | { | ||
| 459 | if (!info->par) | ||
| 460 | return; | ||
| 461 | sys_fillrect(info, rect); | ||
| 462 | |||
| 463 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 464 | } | ||
| 465 | |||
| 466 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 467 | static void picolcd_fb_copyarea(struct fb_info *info, | ||
| 468 | const struct fb_copyarea *area) | ||
| 469 | { | ||
| 470 | if (!info->par) | ||
| 471 | return; | ||
| 472 | sys_copyarea(info, area); | ||
| 473 | |||
| 474 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 475 | } | ||
| 476 | |||
| 477 | /* Stub to call the system default and update the image on the picoLCD */ | ||
| 478 | static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image) | ||
| 479 | { | ||
| 480 | if (!info->par) | ||
| 481 | return; | ||
| 482 | sys_imageblit(info, image); | ||
| 483 | |||
| 484 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 485 | } | ||
| 486 | |||
| 487 | /* | ||
| 488 | * this is the slow path from userspace. they can seek and write to | ||
| 489 | * the fb. it's inefficient to do anything less than a full screen draw | ||
| 490 | */ | ||
| 491 | static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf, | ||
| 492 | size_t count, loff_t *ppos) | ||
| 493 | { | ||
| 494 | ssize_t ret; | ||
| 495 | if (!info->par) | ||
| 496 | return -ENODEV; | ||
| 497 | ret = fb_sys_write(info, buf, count, ppos); | ||
| 498 | if (ret >= 0) | ||
| 499 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 500 | return ret; | ||
| 501 | } | ||
| 502 | |||
| 503 | static int picolcd_fb_blank(int blank, struct fb_info *info) | ||
| 504 | { | ||
| 505 | if (!info->par) | ||
| 506 | return -ENODEV; | ||
| 507 | /* We let fb notification do this for us via lcd/backlight device */ | ||
| 508 | return 0; | ||
| 509 | } | ||
| 510 | |||
| 511 | static void picolcd_fb_destroy(struct fb_info *info) | ||
| 512 | { | ||
| 513 | struct picolcd_data *data = info->par; | ||
| 514 | info->par = NULL; | ||
| 515 | if (data) | ||
| 516 | data->fb_info = NULL; | ||
| 517 | fb_deferred_io_cleanup(info); | ||
| 518 | framebuffer_release(info); | ||
| 519 | } | ||
| 520 | |||
| 521 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
| 522 | { | ||
| 523 | __u32 bpp = var->bits_per_pixel; | ||
| 524 | __u32 activate = var->activate; | ||
| 525 | |||
| 526 | /* only allow 1/8 bit depth (8-bit is grayscale) */ | ||
| 527 | *var = picolcdfb_var; | ||
| 528 | var->activate = activate; | ||
| 529 | if (bpp >= 8) | ||
| 530 | var->bits_per_pixel = 8; | ||
| 531 | else | ||
| 532 | var->bits_per_pixel = 1; | ||
| 533 | return 0; | ||
| 534 | } | ||
| 535 | |||
| 536 | static int picolcd_set_par(struct fb_info *info) | ||
| 537 | { | ||
| 538 | struct picolcd_data *data = info->par; | ||
| 539 | u8 *o_fb, *n_fb; | ||
| 540 | if (info->var.bits_per_pixel == data->fb_bpp) | ||
| 541 | return 0; | ||
| 542 | /* switch between 1/8 bit depths */ | ||
| 543 | if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8) | ||
| 544 | return -EINVAL; | ||
| 545 | |||
| 546 | o_fb = data->fb_bitmap; | ||
| 547 | n_fb = vmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel); | ||
| 548 | if (!n_fb) | ||
| 549 | return -ENOMEM; | ||
| 550 | |||
| 551 | fb_deferred_io_cleanup(info); | ||
| 552 | /* translate FB content to new bits-per-pixel */ | ||
| 553 | if (info->var.bits_per_pixel == 1) { | ||
| 554 | int i, b; | ||
| 555 | for (i = 0; i < PICOLCDFB_SIZE; i++) { | ||
| 556 | u8 p = 0; | ||
| 557 | for (b = 0; b < 8; b++) { | ||
| 558 | p <<= 1; | ||
| 559 | p |= o_fb[i*8+b] ? 0x01 : 0x00; | ||
| 560 | } | ||
| 561 | } | ||
| 562 | info->fix.visual = FB_VISUAL_MONO01; | ||
| 563 | info->fix.line_length = PICOLCDFB_WIDTH / 8; | ||
| 564 | } else { | ||
| 565 | int i; | ||
| 566 | for (i = 0; i < PICOLCDFB_SIZE * 8; i++) | ||
| 567 | n_fb[i] = o_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; | ||
| 568 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 569 | info->fix.line_length = PICOLCDFB_WIDTH; | ||
| 570 | } | ||
| 571 | |||
| 572 | data->fb_bitmap = n_fb; | ||
| 573 | data->fb_bpp = info->var.bits_per_pixel; | ||
| 574 | info->screen_base = (char __force __iomem *)n_fb; | ||
| 575 | info->fix.smem_start = (unsigned long)n_fb; | ||
| 576 | info->fix.smem_len = PICOLCDFB_SIZE*data->fb_bpp; | ||
| 577 | fb_deferred_io_init(info); | ||
| 578 | vfree(o_fb); | ||
| 579 | return 0; | ||
| 580 | } | ||
| 581 | |||
| 582 | /* Note this can't be const because of struct fb_info definition */ | ||
| 583 | static struct fb_ops picolcdfb_ops = { | ||
| 584 | .owner = THIS_MODULE, | ||
| 585 | .fb_destroy = picolcd_fb_destroy, | ||
| 586 | .fb_read = fb_sys_read, | ||
| 587 | .fb_write = picolcd_fb_write, | ||
| 588 | .fb_blank = picolcd_fb_blank, | ||
| 589 | .fb_fillrect = picolcd_fb_fillrect, | ||
| 590 | .fb_copyarea = picolcd_fb_copyarea, | ||
| 591 | .fb_imageblit = picolcd_fb_imageblit, | ||
| 592 | .fb_check_var = picolcd_fb_check_var, | ||
| 593 | .fb_set_par = picolcd_set_par, | ||
| 594 | }; | ||
| 595 | |||
| 596 | |||
| 597 | /* Callback from deferred IO workqueue */ | ||
| 598 | static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist) | ||
| 599 | { | ||
| 600 | picolcd_fb_update(info->par); | ||
| 601 | } | ||
| 602 | |||
| 603 | static const struct fb_deferred_io picolcd_fb_defio = { | ||
| 604 | .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT, | ||
| 605 | .deferred_io = picolcd_fb_deferred_io, | ||
| 606 | }; | ||
| 607 | |||
| 608 | |||
| 609 | /* | ||
| 610 | * The "fb_update_rate" sysfs attribute | ||
| 611 | */ | ||
| 612 | static ssize_t picolcd_fb_update_rate_show(struct device *dev, | ||
| 613 | struct device_attribute *attr, char *buf) | ||
| 614 | { | ||
| 615 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 616 | unsigned i, fb_update_rate = data->fb_update_rate; | ||
| 617 | size_t ret = 0; | ||
| 618 | |||
| 619 | for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) | ||
| 620 | if (ret >= PAGE_SIZE) | ||
| 621 | break; | ||
| 622 | else if (i == fb_update_rate) | ||
| 623 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i); | ||
| 624 | else | ||
| 625 | ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i); | ||
| 626 | if (ret > 0) | ||
| 627 | buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n'; | ||
| 628 | return ret; | ||
| 629 | } | ||
| 630 | |||
| 631 | static ssize_t picolcd_fb_update_rate_store(struct device *dev, | ||
| 632 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 633 | { | ||
| 634 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 635 | int i; | ||
| 636 | unsigned u; | ||
| 637 | |||
| 638 | if (count < 1 || count > 10) | ||
| 639 | return -EINVAL; | ||
| 640 | |||
| 641 | i = sscanf(buf, "%u", &u); | ||
| 642 | if (i != 1) | ||
| 643 | return -EINVAL; | ||
| 644 | |||
| 645 | if (u > PICOLCDFB_UPDATE_RATE_LIMIT) | ||
| 646 | return -ERANGE; | ||
| 647 | else if (u == 0) | ||
| 648 | u = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
| 649 | |||
| 650 | data->fb_update_rate = u; | ||
| 651 | data->fb_defio.delay = HZ / data->fb_update_rate; | ||
| 652 | return count; | ||
| 653 | } | ||
| 654 | |||
| 655 | static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show, | ||
| 656 | picolcd_fb_update_rate_store); | ||
| 657 | |||
| 658 | /* initialize Framebuffer device */ | ||
| 659 | static int picolcd_init_framebuffer(struct picolcd_data *data) | ||
| 660 | { | ||
| 661 | struct device *dev = &data->hdev->dev; | ||
| 662 | struct fb_info *info = NULL; | ||
| 663 | int error = -ENOMEM; | ||
| 664 | u8 *fb_vbitmap = NULL; | ||
| 665 | u8 *fb_bitmap = NULL; | ||
| 666 | |||
| 667 | fb_bitmap = vmalloc(PICOLCDFB_SIZE*picolcdfb_var.bits_per_pixel); | ||
| 668 | if (fb_bitmap == NULL) { | ||
| 669 | dev_err(dev, "can't get a free page for framebuffer\n"); | ||
| 670 | goto err_nomem; | ||
| 671 | } | ||
| 672 | |||
| 673 | fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL); | ||
| 674 | if (fb_vbitmap == NULL) { | ||
| 675 | dev_err(dev, "can't alloc vbitmap image buffer\n"); | ||
| 676 | goto err_nomem; | ||
| 677 | } | ||
| 678 | |||
| 679 | data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT; | ||
| 680 | data->fb_defio = picolcd_fb_defio; | ||
| 681 | info = framebuffer_alloc(0, dev); | ||
| 682 | if (info == NULL) { | ||
| 683 | dev_err(dev, "failed to allocate a framebuffer\n"); | ||
| 684 | goto err_nomem; | ||
| 685 | } | ||
| 686 | |||
| 687 | info->fbdefio = &data->fb_defio; | ||
| 688 | info->screen_base = (char __force __iomem *)fb_bitmap; | ||
| 689 | info->fbops = &picolcdfb_ops; | ||
| 690 | info->var = picolcdfb_var; | ||
| 691 | info->fix = picolcdfb_fix; | ||
| 692 | info->fix.smem_len = PICOLCDFB_SIZE; | ||
| 693 | info->fix.smem_start = (unsigned long)fb_bitmap; | ||
| 694 | info->par = data; | ||
| 695 | info->flags = FBINFO_FLAG_DEFAULT; | ||
| 696 | |||
| 697 | data->fb_vbitmap = fb_vbitmap; | ||
| 698 | data->fb_bitmap = fb_bitmap; | ||
| 699 | data->fb_bpp = picolcdfb_var.bits_per_pixel; | ||
| 700 | error = picolcd_fb_reset(data, 1); | ||
| 701 | if (error) { | ||
| 702 | dev_err(dev, "failed to configure display\n"); | ||
| 703 | goto err_cleanup; | ||
| 704 | } | ||
| 705 | error = device_create_file(dev, &dev_attr_fb_update_rate); | ||
| 706 | if (error) { | ||
| 707 | dev_err(dev, "failed to create sysfs attributes\n"); | ||
| 708 | goto err_cleanup; | ||
| 709 | } | ||
| 710 | data->fb_info = info; | ||
| 711 | error = register_framebuffer(info); | ||
| 712 | if (error) { | ||
| 713 | dev_err(dev, "failed to register framebuffer\n"); | ||
| 714 | goto err_sysfs; | ||
| 715 | } | ||
| 716 | fb_deferred_io_init(info); | ||
| 717 | /* schedule first output of framebuffer */ | ||
| 718 | schedule_delayed_work(&info->deferred_work, 0); | ||
| 719 | return 0; | ||
| 720 | |||
| 721 | err_sysfs: | ||
| 722 | device_remove_file(dev, &dev_attr_fb_update_rate); | ||
| 723 | err_cleanup: | ||
| 724 | data->fb_vbitmap = NULL; | ||
| 725 | data->fb_bitmap = NULL; | ||
| 726 | data->fb_bpp = 0; | ||
| 727 | data->fb_info = NULL; | ||
| 728 | |||
| 729 | err_nomem: | ||
| 730 | framebuffer_release(info); | ||
| 731 | vfree(fb_bitmap); | ||
| 732 | kfree(fb_vbitmap); | ||
| 733 | return error; | ||
| 734 | } | ||
| 735 | |||
| 736 | static void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
| 737 | { | ||
| 738 | struct fb_info *info = data->fb_info; | ||
| 739 | u8 *fb_vbitmap = data->fb_vbitmap; | ||
| 740 | u8 *fb_bitmap = data->fb_bitmap; | ||
| 741 | |||
| 742 | if (!info) | ||
| 743 | return; | ||
| 744 | |||
| 745 | data->fb_vbitmap = NULL; | ||
| 746 | data->fb_bitmap = NULL; | ||
| 747 | data->fb_bpp = 0; | ||
| 748 | data->fb_info = NULL; | ||
| 749 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); | ||
| 750 | fb_deferred_io_cleanup(info); | ||
| 751 | unregister_framebuffer(info); | ||
| 752 | vfree(fb_bitmap); | ||
| 753 | kfree(fb_vbitmap); | ||
| 754 | } | ||
| 755 | |||
| 756 | #define picolcd_fbinfo(d) ((d)->fb_info) | ||
| 757 | #else | ||
| 758 | static inline int picolcd_fb_reset(struct picolcd_data *data, int clear) | ||
| 759 | { | ||
| 760 | return 0; | ||
| 761 | } | ||
| 762 | static inline int picolcd_init_framebuffer(struct picolcd_data *data) | ||
| 763 | { | ||
| 764 | return 0; | ||
| 765 | } | ||
| 766 | static inline void picolcd_exit_framebuffer(struct picolcd_data *data) | ||
| 767 | { | ||
| 768 | } | ||
| 769 | #define picolcd_fbinfo(d) NULL | ||
| 770 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 771 | |||
| 772 | #ifdef CONFIG_HID_PICOLCD_BACKLIGHT | ||
| 773 | /* | ||
| 774 | * backlight class device | ||
| 775 | */ | ||
| 776 | static int picolcd_get_brightness(struct backlight_device *bdev) | ||
| 777 | { | ||
| 778 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 779 | return data->lcd_brightness; | ||
| 780 | } | ||
| 781 | |||
| 782 | static int picolcd_set_brightness(struct backlight_device *bdev) | ||
| 783 | { | ||
| 784 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 785 | struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev); | ||
| 786 | unsigned long flags; | ||
| 787 | |||
| 788 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 789 | return -ENODEV; | ||
| 790 | |||
| 791 | data->lcd_brightness = bdev->props.brightness & 0x0ff; | ||
| 792 | data->lcd_power = bdev->props.power; | ||
| 793 | spin_lock_irqsave(&data->lock, flags); | ||
| 794 | hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); | ||
| 795 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 796 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 797 | return 0; | ||
| 798 | } | ||
| 799 | |||
| 800 | static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb) | ||
| 801 | { | ||
| 802 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev)); | ||
| 803 | } | ||
| 804 | |||
| 805 | static const struct backlight_ops picolcd_blops = { | ||
| 806 | .update_status = picolcd_set_brightness, | ||
| 807 | .get_brightness = picolcd_get_brightness, | ||
| 808 | .check_fb = picolcd_check_bl_fb, | ||
| 809 | }; | ||
| 810 | |||
| 811 | static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report) | ||
| 812 | { | ||
| 813 | struct device *dev = &data->hdev->dev; | ||
| 814 | struct backlight_device *bdev; | ||
| 815 | struct backlight_properties props; | ||
| 816 | if (!report) | ||
| 817 | return -ENODEV; | ||
| 818 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 819 | report->field[0]->report_size != 8) { | ||
| 820 | dev_err(dev, "unsupported BRIGHTNESS report"); | ||
| 821 | return -EINVAL; | ||
| 822 | } | ||
| 823 | |||
| 824 | memset(&props, 0, sizeof(props)); | ||
| 825 | props.max_brightness = 0xff; | ||
| 826 | bdev = backlight_device_register(dev_name(dev), dev, data, | ||
| 827 | &picolcd_blops, &props); | ||
| 828 | if (IS_ERR(bdev)) { | ||
| 829 | dev_err(dev, "failed to register backlight\n"); | ||
| 830 | return PTR_ERR(bdev); | ||
| 831 | } | ||
| 832 | bdev->props.brightness = 0xff; | ||
| 833 | data->lcd_brightness = 0xff; | ||
| 834 | data->backlight = bdev; | ||
| 835 | picolcd_set_brightness(bdev); | ||
| 836 | return 0; | ||
| 837 | } | ||
| 838 | |||
| 839 | static void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 840 | { | ||
| 841 | struct backlight_device *bdev = data->backlight; | ||
| 842 | |||
| 843 | data->backlight = NULL; | ||
| 844 | if (bdev) | ||
| 845 | backlight_device_unregister(bdev); | ||
| 846 | } | ||
| 847 | |||
| 848 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 849 | { | ||
| 850 | if (!data->backlight) | ||
| 851 | return 0; | ||
| 852 | return picolcd_set_brightness(data->backlight); | ||
| 853 | } | ||
| 854 | |||
| 855 | #ifdef CONFIG_PM | ||
| 856 | static void picolcd_suspend_backlight(struct picolcd_data *data) | ||
| 857 | { | ||
| 858 | int bl_power = data->lcd_power; | ||
| 859 | if (!data->backlight) | ||
| 860 | return; | ||
| 861 | |||
| 862 | data->backlight->props.power = FB_BLANK_POWERDOWN; | ||
| 863 | picolcd_set_brightness(data->backlight); | ||
| 864 | data->lcd_power = data->backlight->props.power = bl_power; | ||
| 865 | } | ||
| 866 | #endif /* CONFIG_PM */ | ||
| 867 | #else | ||
| 868 | static inline int picolcd_init_backlight(struct picolcd_data *data, | ||
| 869 | struct hid_report *report) | ||
| 870 | { | ||
| 871 | return 0; | ||
| 872 | } | ||
| 873 | static inline void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 874 | { | ||
| 875 | } | ||
| 876 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 877 | { | ||
| 878 | return 0; | ||
| 879 | } | ||
| 880 | static inline void picolcd_suspend_backlight(struct picolcd_data *data) | ||
| 881 | { | ||
| 882 | } | ||
| 883 | #endif /* CONFIG_HID_PICOLCD_BACKLIGHT */ | ||
| 884 | |||
| 885 | #ifdef CONFIG_HID_PICOLCD_LCD | ||
| 886 | /* | ||
| 887 | * lcd class device | ||
| 888 | */ | ||
| 889 | static int picolcd_get_contrast(struct lcd_device *ldev) | ||
| 890 | { | ||
| 891 | struct picolcd_data *data = lcd_get_data(ldev); | ||
| 892 | return data->lcd_contrast; | ||
| 893 | } | ||
| 894 | |||
| 895 | static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) | ||
| 896 | { | ||
| 897 | struct picolcd_data *data = lcd_get_data(ldev); | ||
| 898 | struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev); | ||
| 899 | unsigned long flags; | ||
| 900 | |||
| 901 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 902 | return -ENODEV; | ||
| 903 | |||
| 904 | data->lcd_contrast = contrast & 0x0ff; | ||
| 905 | spin_lock_irqsave(&data->lock, flags); | ||
| 906 | hid_set_field(report->field[0], 0, data->lcd_contrast); | ||
| 907 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 908 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 909 | return 0; | ||
| 910 | } | ||
| 911 | |||
| 912 | static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb) | ||
| 913 | { | ||
| 914 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev)); | ||
| 915 | } | ||
| 916 | |||
| 917 | static struct lcd_ops picolcd_lcdops = { | ||
| 918 | .get_contrast = picolcd_get_contrast, | ||
| 919 | .set_contrast = picolcd_set_contrast, | ||
| 920 | .check_fb = picolcd_check_lcd_fb, | ||
| 921 | }; | ||
| 922 | |||
| 923 | static int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report) | ||
| 924 | { | ||
| 925 | struct device *dev = &data->hdev->dev; | ||
| 926 | struct lcd_device *ldev; | ||
| 927 | |||
| 928 | if (!report) | ||
| 929 | return -ENODEV; | ||
| 930 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 931 | report->field[0]->report_size != 8) { | ||
| 932 | dev_err(dev, "unsupported CONTRAST report"); | ||
| 933 | return -EINVAL; | ||
| 934 | } | ||
| 935 | |||
| 936 | ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops); | ||
| 937 | if (IS_ERR(ldev)) { | ||
| 938 | dev_err(dev, "failed to register LCD\n"); | ||
| 939 | return PTR_ERR(ldev); | ||
| 940 | } | ||
| 941 | ldev->props.max_contrast = 0x0ff; | ||
| 942 | data->lcd_contrast = 0xe5; | ||
| 943 | data->lcd = ldev; | ||
| 944 | picolcd_set_contrast(ldev, 0xe5); | ||
| 945 | return 0; | ||
| 946 | } | ||
| 947 | |||
| 948 | static void picolcd_exit_lcd(struct picolcd_data *data) | ||
| 949 | { | ||
| 950 | struct lcd_device *ldev = data->lcd; | ||
| 951 | |||
| 952 | data->lcd = NULL; | ||
| 953 | if (ldev) | ||
| 954 | lcd_device_unregister(ldev); | ||
| 955 | } | ||
| 956 | |||
| 957 | static inline int picolcd_resume_lcd(struct picolcd_data *data) | ||
| 958 | { | ||
| 959 | if (!data->lcd) | ||
| 960 | return 0; | ||
| 961 | return picolcd_set_contrast(data->lcd, data->lcd_contrast); | ||
| 962 | } | ||
| 963 | #else | ||
| 964 | static inline int picolcd_init_lcd(struct picolcd_data *data, | ||
| 965 | struct hid_report *report) | ||
| 966 | { | ||
| 967 | return 0; | ||
| 968 | } | ||
| 969 | static inline void picolcd_exit_lcd(struct picolcd_data *data) | ||
| 970 | { | ||
| 971 | } | ||
| 972 | static inline int picolcd_resume_lcd(struct picolcd_data *data) | ||
| 973 | { | ||
| 974 | return 0; | ||
| 975 | } | ||
| 976 | #endif /* CONFIG_HID_PICOLCD_LCD */ | ||
| 977 | |||
| 978 | #ifdef CONFIG_HID_PICOLCD_LEDS | ||
| 979 | /** | ||
| 980 | * LED class device | ||
| 981 | */ | ||
| 982 | static void picolcd_leds_set(struct picolcd_data *data) | ||
| 983 | { | ||
| 984 | struct hid_report *report; | ||
| 985 | unsigned long flags; | ||
| 986 | |||
| 987 | if (!data->led[0]) | ||
| 988 | return; | ||
| 989 | report = picolcd_out_report(REPORT_LED_STATE, data->hdev); | ||
| 990 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 991 | return; | ||
| 992 | |||
| 993 | spin_lock_irqsave(&data->lock, flags); | ||
| 994 | hid_set_field(report->field[0], 0, data->led_state); | ||
| 995 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 996 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 997 | } | ||
| 998 | |||
| 999 | static void picolcd_led_set_brightness(struct led_classdev *led_cdev, | ||
| 1000 | enum led_brightness value) | ||
| 1001 | { | ||
| 1002 | struct device *dev; | ||
| 1003 | struct hid_device *hdev; | ||
| 1004 | struct picolcd_data *data; | ||
| 1005 | int i, state = 0; | ||
| 1006 | |||
| 1007 | dev = led_cdev->dev->parent; | ||
| 1008 | hdev = container_of(dev, struct hid_device, dev); | ||
| 1009 | data = hid_get_drvdata(hdev); | ||
| 1010 | for (i = 0; i < 8; i++) { | ||
| 1011 | if (led_cdev != data->led[i]) | ||
| 1012 | continue; | ||
| 1013 | state = (data->led_state >> i) & 1; | ||
| 1014 | if (value == LED_OFF && state) { | ||
| 1015 | data->led_state &= ~(1 << i); | ||
| 1016 | picolcd_leds_set(data); | ||
| 1017 | } else if (value != LED_OFF && !state) { | ||
| 1018 | data->led_state |= 1 << i; | ||
| 1019 | picolcd_leds_set(data); | ||
| 1020 | } | ||
| 1021 | break; | ||
| 1022 | } | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev) | ||
| 1026 | { | ||
| 1027 | struct device *dev; | ||
| 1028 | struct hid_device *hdev; | ||
| 1029 | struct picolcd_data *data; | ||
| 1030 | int i, value = 0; | ||
| 1031 | |||
| 1032 | dev = led_cdev->dev->parent; | ||
| 1033 | hdev = container_of(dev, struct hid_device, dev); | ||
| 1034 | data = hid_get_drvdata(hdev); | ||
| 1035 | for (i = 0; i < 8; i++) | ||
| 1036 | if (led_cdev == data->led[i]) { | ||
| 1037 | value = (data->led_state >> i) & 1; | ||
| 1038 | break; | ||
| 1039 | } | ||
| 1040 | return value ? LED_FULL : LED_OFF; | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report) | ||
| 1044 | { | ||
| 1045 | struct device *dev = &data->hdev->dev; | ||
| 1046 | struct led_classdev *led; | ||
| 1047 | size_t name_sz = strlen(dev_name(dev)) + 8; | ||
| 1048 | char *name; | ||
| 1049 | int i, ret = 0; | ||
| 1050 | |||
| 1051 | if (!report) | ||
| 1052 | return -ENODEV; | ||
| 1053 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 1054 | report->field[0]->report_size != 8) { | ||
| 1055 | dev_err(dev, "unsupported LED_STATE report"); | ||
| 1056 | return -EINVAL; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | for (i = 0; i < 8; i++) { | ||
| 1060 | led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); | ||
| 1061 | if (!led) { | ||
| 1062 | dev_err(dev, "can't allocate memory for LED %d\n", i); | ||
| 1063 | ret = -ENOMEM; | ||
| 1064 | goto err; | ||
| 1065 | } | ||
| 1066 | name = (void *)(&led[1]); | ||
| 1067 | snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i); | ||
| 1068 | led->name = name; | ||
| 1069 | led->brightness = 0; | ||
| 1070 | led->max_brightness = 1; | ||
| 1071 | led->brightness_get = picolcd_led_get_brightness; | ||
| 1072 | led->brightness_set = picolcd_led_set_brightness; | ||
| 1073 | |||
| 1074 | data->led[i] = led; | ||
| 1075 | ret = led_classdev_register(dev, data->led[i]); | ||
| 1076 | if (ret) { | ||
| 1077 | data->led[i] = NULL; | ||
| 1078 | kfree(led); | ||
| 1079 | dev_err(dev, "can't register LED %d\n", i); | ||
| 1080 | goto err; | ||
| 1081 | } | ||
| 1082 | } | ||
| 1083 | return 0; | ||
| 1084 | err: | ||
| 1085 | for (i = 0; i < 8; i++) | ||
| 1086 | if (data->led[i]) { | ||
| 1087 | led = data->led[i]; | ||
| 1088 | data->led[i] = NULL; | ||
| 1089 | led_classdev_unregister(led); | ||
| 1090 | kfree(led); | ||
| 1091 | } | ||
| 1092 | return ret; | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | static void picolcd_exit_leds(struct picolcd_data *data) | ||
| 1096 | { | ||
| 1097 | struct led_classdev *led; | ||
| 1098 | int i; | ||
| 1099 | |||
| 1100 | for (i = 0; i < 8; i++) { | ||
| 1101 | led = data->led[i]; | ||
| 1102 | data->led[i] = NULL; | ||
| 1103 | if (!led) | ||
| 1104 | continue; | ||
| 1105 | led_classdev_unregister(led); | ||
| 1106 | kfree(led); | ||
| 1107 | } | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | #else | ||
| 1111 | static inline int picolcd_init_leds(struct picolcd_data *data, | ||
| 1112 | struct hid_report *report) | ||
| 1113 | { | ||
| 1114 | return 0; | ||
| 1115 | } | ||
| 1116 | static inline void picolcd_exit_leds(struct picolcd_data *data) | ||
| 1117 | { | ||
| 1118 | } | ||
| 1119 | static inline int picolcd_leds_set(struct picolcd_data *data) | ||
| 1120 | { | ||
| 1121 | return 0; | ||
| 1122 | } | ||
| 1123 | #endif /* CONFIG_HID_PICOLCD_LEDS */ | ||
| 1124 | |||
| 1125 | /* | ||
| 1126 | * input class device | ||
| 1127 | */ | ||
| 1128 | static int picolcd_raw_keypad(struct picolcd_data *data, | ||
| 1129 | struct hid_report *report, u8 *raw_data, int size) | ||
| 1130 | { | ||
| 1131 | /* | ||
| 1132 | * Keypad event | ||
| 1133 | * First and second data bytes list currently pressed keys, | ||
| 1134 | * 0x00 means no key and at most 2 keys may be pressed at same time | ||
| 1135 | */ | ||
| 1136 | int i, j; | ||
| 1137 | |||
| 1138 | /* determine newly pressed keys */ | ||
| 1139 | for (i = 0; i < size; i++) { | ||
| 1140 | unsigned int key_code; | ||
| 1141 | if (raw_data[i] == 0) | ||
| 1142 | continue; | ||
| 1143 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
| 1144 | if (data->pressed_keys[j] == raw_data[i]) | ||
| 1145 | goto key_already_down; | ||
| 1146 | for (j = 0; j < sizeof(data->pressed_keys); j++) | ||
| 1147 | if (data->pressed_keys[j] == 0) { | ||
| 1148 | data->pressed_keys[j] = raw_data[i]; | ||
| 1149 | break; | ||
| 1150 | } | ||
| 1151 | input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]); | ||
| 1152 | if (raw_data[i] < PICOLCD_KEYS) | ||
| 1153 | key_code = data->keycode[raw_data[i]]; | ||
| 1154 | else | ||
| 1155 | key_code = KEY_UNKNOWN; | ||
| 1156 | if (key_code != KEY_UNKNOWN) { | ||
| 1157 | dbg_hid(PICOLCD_NAME " got key press for %u:%d", | ||
| 1158 | raw_data[i], key_code); | ||
| 1159 | input_report_key(data->input_keys, key_code, 1); | ||
| 1160 | } | ||
| 1161 | input_sync(data->input_keys); | ||
| 1162 | key_already_down: | ||
| 1163 | continue; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /* determine newly released keys */ | ||
| 1167 | for (j = 0; j < sizeof(data->pressed_keys); j++) { | ||
| 1168 | unsigned int key_code; | ||
| 1169 | if (data->pressed_keys[j] == 0) | ||
| 1170 | continue; | ||
| 1171 | for (i = 0; i < size; i++) | ||
| 1172 | if (data->pressed_keys[j] == raw_data[i]) | ||
| 1173 | goto key_still_down; | ||
| 1174 | input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]); | ||
| 1175 | if (data->pressed_keys[j] < PICOLCD_KEYS) | ||
| 1176 | key_code = data->keycode[data->pressed_keys[j]]; | ||
| 1177 | else | ||
| 1178 | key_code = KEY_UNKNOWN; | ||
| 1179 | if (key_code != KEY_UNKNOWN) { | ||
| 1180 | dbg_hid(PICOLCD_NAME " got key release for %u:%d", | ||
| 1181 | data->pressed_keys[j], key_code); | ||
| 1182 | input_report_key(data->input_keys, key_code, 0); | ||
| 1183 | } | ||
| 1184 | input_sync(data->input_keys); | ||
| 1185 | data->pressed_keys[j] = 0; | ||
| 1186 | key_still_down: | ||
| 1187 | continue; | ||
| 1188 | } | ||
| 1189 | return 1; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | static int picolcd_raw_cir(struct picolcd_data *data, | ||
| 1193 | struct hid_report *report, u8 *raw_data, int size) | ||
| 1194 | { | ||
| 1195 | /* Need understanding of CIR data format to implement ... */ | ||
| 1196 | return 1; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | static int picolcd_check_version(struct hid_device *hdev) | ||
| 1200 | { | ||
| 1201 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 1202 | struct picolcd_pending *verinfo; | ||
| 1203 | int ret = 0; | ||
| 1204 | |||
| 1205 | if (!data) | ||
| 1206 | return -ENODEV; | ||
| 1207 | |||
| 1208 | verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0); | ||
| 1209 | if (!verinfo) { | ||
| 1210 | dev_err(&hdev->dev, "no version response from PicoLCD"); | ||
| 1211 | return -ENODEV; | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | if (verinfo->raw_size == 2) { | ||
| 1215 | data->version[0] = verinfo->raw_data[1]; | ||
| 1216 | data->version[1] = verinfo->raw_data[0]; | ||
| 1217 | if (data->status & PICOLCD_BOOTLOADER) { | ||
| 1218 | dev_info(&hdev->dev, "PicoLCD, bootloader version %d.%d\n", | ||
| 1219 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
| 1220 | } else { | ||
| 1221 | dev_info(&hdev->dev, "PicoLCD, firmware version %d.%d\n", | ||
| 1222 | verinfo->raw_data[1], verinfo->raw_data[0]); | ||
| 1223 | } | ||
| 1224 | } else { | ||
| 1225 | dev_err(&hdev->dev, "confused, got unexpected version response from PicoLCD\n"); | ||
| 1226 | ret = -EINVAL; | ||
| 1227 | } | ||
| 1228 | kfree(verinfo); | ||
| 1229 | return ret; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | /* | ||
| 1233 | * Reset our device and wait for answer to VERSION request | ||
| 1234 | */ | ||
| 1235 | static int picolcd_reset(struct hid_device *hdev) | ||
| 1236 | { | ||
| 1237 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 1238 | struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev); | ||
| 1239 | unsigned long flags; | ||
| 1240 | int error; | ||
| 1241 | |||
| 1242 | if (!data || !report || report->maxfield != 1) | ||
| 1243 | return -ENODEV; | ||
| 1244 | |||
| 1245 | spin_lock_irqsave(&data->lock, flags); | ||
| 1246 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
| 1247 | data->status |= PICOLCD_BOOTLOADER; | ||
| 1248 | |||
| 1249 | /* perform the reset */ | ||
| 1250 | hid_set_field(report->field[0], 0, 1); | ||
| 1251 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | ||
| 1252 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 1253 | |||
| 1254 | error = picolcd_check_version(hdev); | ||
| 1255 | if (error) | ||
| 1256 | return error; | ||
| 1257 | |||
| 1258 | picolcd_resume_lcd(data); | ||
| 1259 | picolcd_resume_backlight(data); | ||
| 1260 | #ifdef CONFIG_HID_PICOLCD_FB | ||
| 1261 | if (data->fb_info) | ||
| 1262 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | ||
| 1263 | #endif /* CONFIG_HID_PICOLCD_FB */ | ||
| 1264 | |||
| 1265 | picolcd_leds_set(data); | ||
| 1266 | return 0; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | /* | ||
| 1270 | * The "operation_mode" sysfs attribute | ||
| 1271 | */ | ||
| 1272 | static ssize_t picolcd_operation_mode_show(struct device *dev, | ||
| 1273 | struct device_attribute *attr, char *buf) | ||
| 1274 | { | ||
| 1275 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1276 | |||
| 1277 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 1278 | return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n"); | ||
| 1279 | else | ||
| 1280 | return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n"); | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | static ssize_t picolcd_operation_mode_store(struct device *dev, | ||
| 1284 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 1285 | { | ||
| 1286 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1287 | struct hid_report *report = NULL; | ||
| 1288 | size_t cnt = count; | ||
| 1289 | int timeout = data->opmode_delay; | ||
| 1290 | unsigned long flags; | ||
| 1291 | |||
| 1292 | if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) { | ||
| 1293 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 1294 | report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev); | ||
| 1295 | buf += 3; | ||
| 1296 | cnt -= 3; | ||
| 1297 | } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) { | ||
| 1298 | if (!(data->status & PICOLCD_BOOTLOADER)) | ||
| 1299 | report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev); | ||
| 1300 | buf += 10; | ||
| 1301 | cnt -= 10; | ||
| 1302 | } | ||
| 1303 | if (!report) | ||
| 1304 | return -EINVAL; | ||
| 1305 | |||
| 1306 | while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) | ||
| 1307 | cnt--; | ||
| 1308 | if (cnt != 0) | ||
| 1309 | return -EINVAL; | ||
| 1310 | |||
| 1311 | spin_lock_irqsave(&data->lock, flags); | ||
| 1312 | hid_set_field(report->field[0], 0, timeout & 0xff); | ||
| 1313 | hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff); | ||
| 1314 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 1315 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 1316 | return count; | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show, | ||
| 1320 | picolcd_operation_mode_store); | ||
| 1321 | |||
| 1322 | /* | ||
| 1323 | * The "operation_mode_delay" sysfs attribute | ||
| 1324 | */ | ||
| 1325 | static ssize_t picolcd_operation_mode_delay_show(struct device *dev, | ||
| 1326 | struct device_attribute *attr, char *buf) | ||
| 1327 | { | ||
| 1328 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1329 | |||
| 1330 | return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | static ssize_t picolcd_operation_mode_delay_store(struct device *dev, | ||
| 1334 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 1335 | { | ||
| 1336 | struct picolcd_data *data = dev_get_drvdata(dev); | ||
| 1337 | unsigned u; | ||
| 1338 | if (sscanf(buf, "%u", &u) != 1) | ||
| 1339 | return -EINVAL; | ||
| 1340 | if (u > 30000) | ||
| 1341 | return -EINVAL; | ||
| 1342 | else | ||
| 1343 | data->opmode_delay = u; | ||
| 1344 | return count; | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show, | ||
| 1348 | picolcd_operation_mode_delay_store); | ||
| 1349 | |||
| 1350 | |||
| 1351 | #ifdef CONFIG_DEBUG_FS | ||
| 1352 | /* | ||
| 1353 | * The "reset" file | ||
| 1354 | */ | ||
| 1355 | static int picolcd_debug_reset_show(struct seq_file *f, void *p) | ||
| 1356 | { | ||
| 1357 | if (picolcd_fbinfo((struct picolcd_data *)f->private)) | ||
| 1358 | seq_printf(f, "all fb\n"); | ||
| 1359 | else | ||
| 1360 | seq_printf(f, "all\n"); | ||
| 1361 | return 0; | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | static int picolcd_debug_reset_open(struct inode *inode, struct file *f) | ||
| 1365 | { | ||
| 1366 | return single_open(f, picolcd_debug_reset_show, inode->i_private); | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf, | ||
| 1370 | size_t count, loff_t *ppos) | ||
| 1371 | { | ||
| 1372 | struct picolcd_data *data = ((struct seq_file *)f->private_data)->private; | ||
| 1373 | char buf[32]; | ||
| 1374 | size_t cnt = min(count, sizeof(buf)-1); | ||
| 1375 | if (copy_from_user(buf, user_buf, cnt)) | ||
| 1376 | return -EFAULT; | ||
| 1377 | |||
| 1378 | while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n')) | ||
| 1379 | cnt--; | ||
| 1380 | buf[cnt] = '\0'; | ||
| 1381 | if (strcmp(buf, "all") == 0) { | ||
| 1382 | picolcd_reset(data->hdev); | ||
| 1383 | picolcd_fb_reset(data, 1); | ||
| 1384 | } else if (strcmp(buf, "fb") == 0) { | ||
| 1385 | picolcd_fb_reset(data, 1); | ||
| 1386 | } else { | ||
| 1387 | return -EINVAL; | ||
| 1388 | } | ||
| 1389 | return count; | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | static const struct file_operations picolcd_debug_reset_fops = { | ||
| 1393 | .owner = THIS_MODULE, | ||
| 1394 | .open = picolcd_debug_reset_open, | ||
| 1395 | .read = seq_read, | ||
| 1396 | .llseek = seq_lseek, | ||
| 1397 | .write = picolcd_debug_reset_write, | ||
| 1398 | .release = single_release, | ||
| 1399 | }; | ||
| 1400 | |||
| 1401 | /* | ||
| 1402 | * The "eeprom" file | ||
| 1403 | */ | ||
| 1404 | static int picolcd_debug_eeprom_open(struct inode *i, struct file *f) | ||
| 1405 | { | ||
| 1406 | f->private_data = i->i_private; | ||
| 1407 | return 0; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, | ||
| 1411 | size_t s, loff_t *off) | ||
| 1412 | { | ||
| 1413 | struct picolcd_data *data = f->private_data; | ||
| 1414 | struct picolcd_pending *resp; | ||
| 1415 | u8 raw_data[3]; | ||
| 1416 | ssize_t ret = -EIO; | ||
| 1417 | |||
| 1418 | if (s == 0) | ||
| 1419 | return -EINVAL; | ||
| 1420 | if (*off > 0x0ff) | ||
| 1421 | return 0; | ||
| 1422 | |||
| 1423 | /* prepare buffer with info about what we want to read (addr & len) */ | ||
| 1424 | raw_data[0] = *off & 0xff; | ||
| 1425 | raw_data[1] = (*off >> 8) && 0xff; | ||
| 1426 | raw_data[2] = s < 20 ? s : 20; | ||
| 1427 | if (*off + raw_data[2] > 0xff) | ||
| 1428 | raw_data[2] = 0x100 - *off; | ||
| 1429 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data, | ||
| 1430 | sizeof(raw_data)); | ||
| 1431 | if (!resp) | ||
| 1432 | return -EIO; | ||
| 1433 | |||
| 1434 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
| 1435 | /* successful read :) */ | ||
| 1436 | ret = resp->raw_data[2]; | ||
| 1437 | if (ret > s) | ||
| 1438 | ret = s; | ||
| 1439 | if (copy_to_user(u, resp->raw_data+3, ret)) | ||
| 1440 | ret = -EFAULT; | ||
| 1441 | else | ||
| 1442 | *off += ret; | ||
| 1443 | } /* anything else is some kind of IO error */ | ||
| 1444 | |||
| 1445 | kfree(resp); | ||
| 1446 | return ret; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u, | ||
| 1450 | size_t s, loff_t *off) | ||
| 1451 | { | ||
| 1452 | struct picolcd_data *data = f->private_data; | ||
| 1453 | struct picolcd_pending *resp; | ||
| 1454 | ssize_t ret = -EIO; | ||
| 1455 | u8 raw_data[23]; | ||
| 1456 | |||
| 1457 | if (s == 0) | ||
| 1458 | return -EINVAL; | ||
| 1459 | if (*off > 0x0ff) | ||
| 1460 | return -ENOSPC; | ||
| 1461 | |||
| 1462 | memset(raw_data, 0, sizeof(raw_data)); | ||
| 1463 | raw_data[0] = *off & 0xff; | ||
| 1464 | raw_data[1] = (*off >> 8) && 0xff; | ||
| 1465 | raw_data[2] = s < 20 ? s : 20; | ||
| 1466 | if (*off + raw_data[2] > 0xff) | ||
| 1467 | raw_data[2] = 0x100 - *off; | ||
| 1468 | |||
| 1469 | if (copy_from_user(raw_data+3, u, raw_data[2])) | ||
| 1470 | return -EFAULT; | ||
| 1471 | resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data, | ||
| 1472 | sizeof(raw_data)); | ||
| 1473 | |||
| 1474 | if (!resp) | ||
| 1475 | return -EIO; | ||
| 1476 | |||
| 1477 | if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) { | ||
| 1478 | /* check if written data matches */ | ||
| 1479 | if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) { | ||
| 1480 | *off += raw_data[2]; | ||
| 1481 | ret = raw_data[2]; | ||
| 1482 | } | ||
| 1483 | } | ||
| 1484 | kfree(resp); | ||
| 1485 | return ret; | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | /* | ||
| 1489 | * Notes: | ||
| 1490 | * - read/write happens in chunks of at most 20 bytes, it's up to userspace | ||
| 1491 | * to loop in order to get more data. | ||
| 1492 | * - on write errors on otherwise correct write request the bytes | ||
| 1493 | * that should have been written are in undefined state. | ||
| 1494 | */ | ||
| 1495 | static const struct file_operations picolcd_debug_eeprom_fops = { | ||
| 1496 | .owner = THIS_MODULE, | ||
| 1497 | .open = picolcd_debug_eeprom_open, | ||
| 1498 | .read = picolcd_debug_eeprom_read, | ||
| 1499 | .write = picolcd_debug_eeprom_write, | ||
| 1500 | .llseek = generic_file_llseek, | ||
| 1501 | }; | ||
| 1502 | |||
| 1503 | /* | ||
| 1504 | * The "flash" file | ||
| 1505 | */ | ||
| 1506 | static int picolcd_debug_flash_open(struct inode *i, struct file *f) | ||
| 1507 | { | ||
| 1508 | f->private_data = i->i_private; | ||
| 1509 | return 0; | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | /* record a flash address to buf (bounds check to be done by caller) */ | ||
| 1513 | static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off) | ||
| 1514 | { | ||
| 1515 | buf[0] = off & 0xff; | ||
| 1516 | buf[1] = (off >> 8) & 0xff; | ||
| 1517 | if (data->addr_sz == 3) | ||
| 1518 | buf[2] = (off >> 16) & 0xff; | ||
| 1519 | return data->addr_sz == 2 ? 2 : 3; | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | /* read a given size of data (bounds check to be done by caller) */ | ||
| 1523 | static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id, | ||
| 1524 | char __user *u, size_t s, loff_t *off) | ||
| 1525 | { | ||
| 1526 | struct picolcd_pending *resp; | ||
| 1527 | u8 raw_data[4]; | ||
| 1528 | ssize_t ret = 0; | ||
| 1529 | int len_off, err = -EIO; | ||
| 1530 | |||
| 1531 | while (s > 0) { | ||
| 1532 | err = -EIO; | ||
| 1533 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 1534 | raw_data[len_off] = s > 32 ? 32 : s; | ||
| 1535 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1); | ||
| 1536 | if (!resp || !resp->in_report) | ||
| 1537 | goto skip; | ||
| 1538 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 1539 | resp->in_report->id == REPORT_BL_READ_MEMORY) { | ||
| 1540 | if (memcmp(raw_data, resp->raw_data, len_off+1) != 0) | ||
| 1541 | goto skip; | ||
| 1542 | if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) { | ||
| 1543 | err = -EFAULT; | ||
| 1544 | goto skip; | ||
| 1545 | } | ||
| 1546 | *off += raw_data[len_off]; | ||
| 1547 | s -= raw_data[len_off]; | ||
| 1548 | ret += raw_data[len_off]; | ||
| 1549 | err = 0; | ||
| 1550 | } | ||
| 1551 | skip: | ||
| 1552 | kfree(resp); | ||
| 1553 | if (err) | ||
| 1554 | return ret > 0 ? ret : err; | ||
| 1555 | } | ||
| 1556 | return ret; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u, | ||
| 1560 | size_t s, loff_t *off) | ||
| 1561 | { | ||
| 1562 | struct picolcd_data *data = f->private_data; | ||
| 1563 | |||
| 1564 | if (s == 0) | ||
| 1565 | return -EINVAL; | ||
| 1566 | if (*off > 0x05fff) | ||
| 1567 | return 0; | ||
| 1568 | if (*off + s > 0x05fff) | ||
| 1569 | s = 0x06000 - *off; | ||
| 1570 | |||
| 1571 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 1572 | return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off); | ||
| 1573 | else | ||
| 1574 | return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | /* erase block aligned to 64bytes boundary */ | ||
| 1578 | static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id, | ||
| 1579 | loff_t *off) | ||
| 1580 | { | ||
| 1581 | struct picolcd_pending *resp; | ||
| 1582 | u8 raw_data[3]; | ||
| 1583 | int len_off; | ||
| 1584 | ssize_t ret = -EIO; | ||
| 1585 | |||
| 1586 | if (*off & 0x3f) | ||
| 1587 | return -EINVAL; | ||
| 1588 | |||
| 1589 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 1590 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off); | ||
| 1591 | if (!resp || !resp->in_report) | ||
| 1592 | goto skip; | ||
| 1593 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 1594 | resp->in_report->id == REPORT_BL_ERASE_MEMORY) { | ||
| 1595 | if (memcmp(raw_data, resp->raw_data, len_off) != 0) | ||
| 1596 | goto skip; | ||
| 1597 | ret = 0; | ||
| 1598 | } | ||
| 1599 | skip: | ||
| 1600 | kfree(resp); | ||
| 1601 | return ret; | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | /* write a given size of data (bounds check to be done by caller) */ | ||
| 1605 | static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id, | ||
| 1606 | const char __user *u, size_t s, loff_t *off) | ||
| 1607 | { | ||
| 1608 | struct picolcd_pending *resp; | ||
| 1609 | u8 raw_data[36]; | ||
| 1610 | ssize_t ret = 0; | ||
| 1611 | int len_off, err = -EIO; | ||
| 1612 | |||
| 1613 | while (s > 0) { | ||
| 1614 | err = -EIO; | ||
| 1615 | len_off = _picolcd_flash_setaddr(data, raw_data, *off); | ||
| 1616 | raw_data[len_off] = s > 32 ? 32 : s; | ||
| 1617 | if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) { | ||
| 1618 | err = -EFAULT; | ||
| 1619 | break; | ||
| 1620 | } | ||
| 1621 | resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, | ||
| 1622 | len_off+1+raw_data[len_off]); | ||
| 1623 | if (!resp || !resp->in_report) | ||
| 1624 | goto skip; | ||
| 1625 | if (resp->in_report->id == REPORT_MEMORY || | ||
| 1626 | resp->in_report->id == REPORT_BL_WRITE_MEMORY) { | ||
| 1627 | if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0) | ||
| 1628 | goto skip; | ||
| 1629 | *off += raw_data[len_off]; | ||
| 1630 | s -= raw_data[len_off]; | ||
| 1631 | ret += raw_data[len_off]; | ||
| 1632 | err = 0; | ||
| 1633 | } | ||
| 1634 | skip: | ||
| 1635 | kfree(resp); | ||
| 1636 | if (err) | ||
| 1637 | break; | ||
| 1638 | } | ||
| 1639 | return ret > 0 ? ret : err; | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u, | ||
| 1643 | size_t s, loff_t *off) | ||
| 1644 | { | ||
| 1645 | struct picolcd_data *data = f->private_data; | ||
| 1646 | ssize_t err, ret = 0; | ||
| 1647 | int report_erase, report_write; | ||
| 1648 | |||
| 1649 | if (s == 0) | ||
| 1650 | return -EINVAL; | ||
| 1651 | if (*off > 0x5fff) | ||
| 1652 | return -ENOSPC; | ||
| 1653 | if (s & 0x3f) | ||
| 1654 | return -EINVAL; | ||
| 1655 | if (*off & 0x3f) | ||
| 1656 | return -EINVAL; | ||
| 1657 | |||
| 1658 | if (data->status & PICOLCD_BOOTLOADER) { | ||
| 1659 | report_erase = REPORT_BL_ERASE_MEMORY; | ||
| 1660 | report_write = REPORT_BL_WRITE_MEMORY; | ||
| 1661 | } else { | ||
| 1662 | report_erase = REPORT_ERASE_MEMORY; | ||
| 1663 | report_write = REPORT_WRITE_MEMORY; | ||
| 1664 | } | ||
| 1665 | mutex_lock(&data->mutex_flash); | ||
| 1666 | while (s > 0) { | ||
| 1667 | err = _picolcd_flash_erase64(data, report_erase, off); | ||
| 1668 | if (err) | ||
| 1669 | break; | ||
| 1670 | err = _picolcd_flash_write(data, report_write, u, 64, off); | ||
| 1671 | if (err < 0) | ||
| 1672 | break; | ||
| 1673 | ret += err; | ||
| 1674 | *off += err; | ||
| 1675 | s -= err; | ||
| 1676 | if (err != 64) | ||
| 1677 | break; | ||
| 1678 | } | ||
| 1679 | mutex_unlock(&data->mutex_flash); | ||
| 1680 | return ret > 0 ? ret : err; | ||
| 1681 | } | ||
| 1682 | |||
| 1683 | /* | ||
| 1684 | * Notes: | ||
| 1685 | * - concurrent writing is prevented by mutex and all writes must be | ||
| 1686 | * n*64 bytes and 64-byte aligned, each write being preceeded by an | ||
| 1687 | * ERASE which erases a 64byte block. | ||
| 1688 | * If less than requested was written or an error is returned for an | ||
| 1689 | * otherwise correct write request the next 64-byte block which should | ||
| 1690 | * have been written is in undefined state (mostly: original, erased, | ||
| 1691 | * (half-)written with write error) | ||
| 1692 | * - reading can happend without special restriction | ||
| 1693 | */ | ||
| 1694 | static const struct file_operations picolcd_debug_flash_fops = { | ||
| 1695 | .owner = THIS_MODULE, | ||
| 1696 | .open = picolcd_debug_flash_open, | ||
| 1697 | .read = picolcd_debug_flash_read, | ||
| 1698 | .write = picolcd_debug_flash_write, | ||
| 1699 | .llseek = generic_file_llseek, | ||
| 1700 | }; | ||
| 1701 | |||
| 1702 | |||
| 1703 | /* | ||
| 1704 | * Helper code for HID report level dumping/debugging | ||
| 1705 | */ | ||
| 1706 | static const char *error_codes[] = { | ||
| 1707 | "success", "parameter missing", "data_missing", "block readonly", | ||
| 1708 | "block not erasable", "block too big", "section overflow", | ||
| 1709 | "invalid command length", "invalid data length", | ||
| 1710 | }; | ||
| 1711 | |||
| 1712 | static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, | ||
| 1713 | const size_t data_len) | ||
| 1714 | { | ||
| 1715 | int i, j; | ||
| 1716 | for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) { | ||
| 1717 | dst[j++] = hex_asc[(data[i] >> 4) & 0x0f]; | ||
| 1718 | dst[j++] = hex_asc[data[i] & 0x0f]; | ||
| 1719 | dst[j++] = ' '; | ||
| 1720 | } | ||
| 1721 | if (j < dst_sz) { | ||
| 1722 | dst[j--] = '\0'; | ||
| 1723 | dst[j] = '\n'; | ||
| 1724 | } else | ||
| 1725 | dst[j] = '\0'; | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | static void picolcd_debug_out_report(struct picolcd_data *data, | ||
| 1729 | struct hid_device *hdev, struct hid_report *report) | ||
| 1730 | { | ||
| 1731 | u8 raw_data[70]; | ||
| 1732 | int raw_size = (report->size >> 3) + 1; | ||
| 1733 | char *buff; | ||
| 1734 | #define BUFF_SZ 256 | ||
| 1735 | |||
| 1736 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
| 1737 | if (!hdev->debug_events) | ||
| 1738 | return; | ||
| 1739 | |||
| 1740 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
| 1741 | if (!buff) | ||
| 1742 | return; | ||
| 1743 | |||
| 1744 | snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", | ||
| 1745 | report->id, raw_size); | ||
| 1746 | hid_debug_event(hdev, buff); | ||
| 1747 | if (raw_size + 5 > sizeof(raw_data)) { | ||
| 1748 | hid_debug_event(hdev, " TOO BIG\n"); | ||
| 1749 | return; | ||
| 1750 | } else { | ||
| 1751 | raw_data[0] = report->id; | ||
| 1752 | hid_output_report(report, raw_data); | ||
| 1753 | dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); | ||
| 1754 | hid_debug_event(hdev, buff); | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | switch (report->id) { | ||
| 1758 | case REPORT_LED_STATE: | ||
| 1759 | /* 1 data byte with GPO state */ | ||
| 1760 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1761 | "REPORT_LED_STATE", report->id, raw_size-1); | ||
| 1762 | hid_debug_event(hdev, buff); | ||
| 1763 | snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]); | ||
| 1764 | hid_debug_event(hdev, buff); | ||
| 1765 | break; | ||
| 1766 | case REPORT_BRIGHTNESS: | ||
| 1767 | /* 1 data byte with brightness */ | ||
| 1768 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1769 | "REPORT_BRIGHTNESS", report->id, raw_size-1); | ||
| 1770 | hid_debug_event(hdev, buff); | ||
| 1771 | snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]); | ||
| 1772 | hid_debug_event(hdev, buff); | ||
| 1773 | break; | ||
| 1774 | case REPORT_CONTRAST: | ||
| 1775 | /* 1 data byte with contrast */ | ||
| 1776 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1777 | "REPORT_CONTRAST", report->id, raw_size-1); | ||
| 1778 | hid_debug_event(hdev, buff); | ||
| 1779 | snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]); | ||
| 1780 | hid_debug_event(hdev, buff); | ||
| 1781 | break; | ||
| 1782 | case REPORT_RESET: | ||
| 1783 | /* 2 data bytes with reset duration in ms */ | ||
| 1784 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1785 | "REPORT_RESET", report->id, raw_size-1); | ||
| 1786 | hid_debug_event(hdev, buff); | ||
| 1787 | snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n", | ||
| 1788 | raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]); | ||
| 1789 | hid_debug_event(hdev, buff); | ||
| 1790 | break; | ||
| 1791 | case REPORT_LCD_CMD: | ||
| 1792 | /* 63 data bytes with LCD commands */ | ||
| 1793 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1794 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 1795 | hid_debug_event(hdev, buff); | ||
| 1796 | /* TODO: format decoding */ | ||
| 1797 | break; | ||
| 1798 | case REPORT_LCD_DATA: | ||
| 1799 | /* 63 data bytes with LCD data */ | ||
| 1800 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1801 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 1802 | /* TODO: format decoding */ | ||
| 1803 | hid_debug_event(hdev, buff); | ||
| 1804 | break; | ||
| 1805 | case REPORT_LCD_CMD_DATA: | ||
| 1806 | /* 63 data bytes with LCD commands and data */ | ||
| 1807 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1808 | "REPORT_LCD_CMD", report->id, raw_size-1); | ||
| 1809 | /* TODO: format decoding */ | ||
| 1810 | hid_debug_event(hdev, buff); | ||
| 1811 | break; | ||
| 1812 | case REPORT_EE_READ: | ||
| 1813 | /* 3 data bytes with read area description */ | ||
| 1814 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1815 | "REPORT_EE_READ", report->id, raw_size-1); | ||
| 1816 | hid_debug_event(hdev, buff); | ||
| 1817 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 1818 | raw_data[2], raw_data[1]); | ||
| 1819 | hid_debug_event(hdev, buff); | ||
| 1820 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 1821 | hid_debug_event(hdev, buff); | ||
| 1822 | break; | ||
| 1823 | case REPORT_EE_WRITE: | ||
| 1824 | /* 3+1..20 data bytes with write area description */ | ||
| 1825 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1826 | "REPORT_EE_WRITE", report->id, raw_size-1); | ||
| 1827 | hid_debug_event(hdev, buff); | ||
| 1828 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 1829 | raw_data[2], raw_data[1]); | ||
| 1830 | hid_debug_event(hdev, buff); | ||
| 1831 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 1832 | hid_debug_event(hdev, buff); | ||
| 1833 | if (raw_data[3] == 0) { | ||
| 1834 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 1835 | } else if (raw_data[3] + 4 <= raw_size) { | ||
| 1836 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 1837 | hid_debug_event(hdev, buff); | ||
| 1838 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 1839 | } else { | ||
| 1840 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 1841 | } | ||
| 1842 | hid_debug_event(hdev, buff); | ||
| 1843 | break; | ||
| 1844 | case REPORT_ERASE_MEMORY: | ||
| 1845 | case REPORT_BL_ERASE_MEMORY: | ||
| 1846 | /* 3 data bytes with pointer inside erase block */ | ||
| 1847 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1848 | "REPORT_ERASE_MEMORY", report->id, raw_size-1); | ||
| 1849 | hid_debug_event(hdev, buff); | ||
| 1850 | switch (data->addr_sz) { | ||
| 1851 | case 2: | ||
| 1852 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n", | ||
| 1853 | raw_data[2], raw_data[1]); | ||
| 1854 | break; | ||
| 1855 | case 3: | ||
| 1856 | snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n", | ||
| 1857 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 1858 | break; | ||
| 1859 | default: | ||
| 1860 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 1861 | } | ||
| 1862 | hid_debug_event(hdev, buff); | ||
| 1863 | break; | ||
| 1864 | case REPORT_READ_MEMORY: | ||
| 1865 | case REPORT_BL_READ_MEMORY: | ||
| 1866 | /* 4 data bytes with read area description */ | ||
| 1867 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1868 | "REPORT_READ_MEMORY", report->id, raw_size-1); | ||
| 1869 | hid_debug_event(hdev, buff); | ||
| 1870 | switch (data->addr_sz) { | ||
| 1871 | case 2: | ||
| 1872 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 1873 | raw_data[2], raw_data[1]); | ||
| 1874 | hid_debug_event(hdev, buff); | ||
| 1875 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 1876 | break; | ||
| 1877 | case 3: | ||
| 1878 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 1879 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 1880 | hid_debug_event(hdev, buff); | ||
| 1881 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 1882 | break; | ||
| 1883 | default: | ||
| 1884 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 1885 | } | ||
| 1886 | hid_debug_event(hdev, buff); | ||
| 1887 | break; | ||
| 1888 | case REPORT_WRITE_MEMORY: | ||
| 1889 | case REPORT_BL_WRITE_MEMORY: | ||
| 1890 | /* 4+1..32 data bytes with write adrea description */ | ||
| 1891 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1892 | "REPORT_WRITE_MEMORY", report->id, raw_size-1); | ||
| 1893 | hid_debug_event(hdev, buff); | ||
| 1894 | switch (data->addr_sz) { | ||
| 1895 | case 2: | ||
| 1896 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 1897 | raw_data[2], raw_data[1]); | ||
| 1898 | hid_debug_event(hdev, buff); | ||
| 1899 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 1900 | hid_debug_event(hdev, buff); | ||
| 1901 | if (raw_data[3] == 0) { | ||
| 1902 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 1903 | } else if (raw_data[3] + 4 <= raw_size) { | ||
| 1904 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 1905 | hid_debug_event(hdev, buff); | ||
| 1906 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 1907 | } else { | ||
| 1908 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 1909 | } | ||
| 1910 | break; | ||
| 1911 | case 3: | ||
| 1912 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 1913 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 1914 | hid_debug_event(hdev, buff); | ||
| 1915 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 1916 | hid_debug_event(hdev, buff); | ||
| 1917 | if (raw_data[4] == 0) { | ||
| 1918 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 1919 | } else if (raw_data[4] + 5 <= raw_size) { | ||
| 1920 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 1921 | hid_debug_event(hdev, buff); | ||
| 1922 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
| 1923 | } else { | ||
| 1924 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 1925 | } | ||
| 1926 | break; | ||
| 1927 | default: | ||
| 1928 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 1929 | } | ||
| 1930 | hid_debug_event(hdev, buff); | ||
| 1931 | break; | ||
| 1932 | case REPORT_SPLASH_RESTART: | ||
| 1933 | /* TODO */ | ||
| 1934 | break; | ||
| 1935 | case REPORT_EXIT_KEYBOARD: | ||
| 1936 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1937 | "REPORT_EXIT_KEYBOARD", report->id, raw_size-1); | ||
| 1938 | hid_debug_event(hdev, buff); | ||
| 1939 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
| 1940 | raw_data[1] | (raw_data[2] << 8), | ||
| 1941 | raw_data[2], raw_data[1]); | ||
| 1942 | hid_debug_event(hdev, buff); | ||
| 1943 | break; | ||
| 1944 | case REPORT_VERSION: | ||
| 1945 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1946 | "REPORT_VERSION", report->id, raw_size-1); | ||
| 1947 | hid_debug_event(hdev, buff); | ||
| 1948 | break; | ||
| 1949 | case REPORT_DEVID: | ||
| 1950 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1951 | "REPORT_DEVID", report->id, raw_size-1); | ||
| 1952 | hid_debug_event(hdev, buff); | ||
| 1953 | break; | ||
| 1954 | case REPORT_SPLASH_SIZE: | ||
| 1955 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1956 | "REPORT_SPLASH_SIZE", report->id, raw_size-1); | ||
| 1957 | hid_debug_event(hdev, buff); | ||
| 1958 | break; | ||
| 1959 | case REPORT_HOOK_VERSION: | ||
| 1960 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1961 | "REPORT_HOOK_VERSION", report->id, raw_size-1); | ||
| 1962 | hid_debug_event(hdev, buff); | ||
| 1963 | break; | ||
| 1964 | case REPORT_EXIT_FLASHER: | ||
| 1965 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1966 | "REPORT_VERSION", report->id, raw_size-1); | ||
| 1967 | hid_debug_event(hdev, buff); | ||
| 1968 | snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n", | ||
| 1969 | raw_data[1] | (raw_data[2] << 8), | ||
| 1970 | raw_data[2], raw_data[1]); | ||
| 1971 | hid_debug_event(hdev, buff); | ||
| 1972 | break; | ||
| 1973 | default: | ||
| 1974 | snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n", | ||
| 1975 | "<unknown>", report->id, raw_size-1); | ||
| 1976 | hid_debug_event(hdev, buff); | ||
| 1977 | break; | ||
| 1978 | } | ||
| 1979 | wake_up_interruptible(&hdev->debug_wait); | ||
| 1980 | kfree(buff); | ||
| 1981 | } | ||
| 1982 | |||
| 1983 | static void picolcd_debug_raw_event(struct picolcd_data *data, | ||
| 1984 | struct hid_device *hdev, struct hid_report *report, | ||
| 1985 | u8 *raw_data, int size) | ||
| 1986 | { | ||
| 1987 | char *buff; | ||
| 1988 | |||
| 1989 | #define BUFF_SZ 256 | ||
| 1990 | /* Avoid unnecessary overhead if debugfs is disabled */ | ||
| 1991 | if (!hdev->debug_events) | ||
| 1992 | return; | ||
| 1993 | |||
| 1994 | buff = kmalloc(BUFF_SZ, GFP_ATOMIC); | ||
| 1995 | if (!buff) | ||
| 1996 | return; | ||
| 1997 | |||
| 1998 | switch (report->id) { | ||
| 1999 | case REPORT_ERROR_CODE: | ||
| 2000 | /* 2 data bytes with affected report and error code */ | ||
| 2001 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2002 | "REPORT_ERROR_CODE", report->id, size-1); | ||
| 2003 | hid_debug_event(hdev, buff); | ||
| 2004 | if (raw_data[2] < ARRAY_SIZE(error_codes)) | ||
| 2005 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n", | ||
| 2006 | raw_data[2], error_codes[raw_data[2]], raw_data[1]); | ||
| 2007 | else | ||
| 2008 | snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n", | ||
| 2009 | raw_data[2], raw_data[1]); | ||
| 2010 | hid_debug_event(hdev, buff); | ||
| 2011 | break; | ||
| 2012 | case REPORT_KEY_STATE: | ||
| 2013 | /* 2 data bytes with key state */ | ||
| 2014 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2015 | "REPORT_KEY_STATE", report->id, size-1); | ||
| 2016 | hid_debug_event(hdev, buff); | ||
| 2017 | if (raw_data[1] == 0) | ||
| 2018 | snprintf(buff, BUFF_SZ, "\tNo key pressed\n"); | ||
| 2019 | else if (raw_data[2] == 0) | ||
| 2020 | snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n", | ||
| 2021 | raw_data[1], raw_data[1]); | ||
| 2022 | else | ||
| 2023 | snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n", | ||
| 2024 | raw_data[1], raw_data[1], raw_data[2], raw_data[2]); | ||
| 2025 | hid_debug_event(hdev, buff); | ||
| 2026 | break; | ||
| 2027 | case REPORT_IR_DATA: | ||
| 2028 | /* Up to 20 byes of IR scancode data */ | ||
| 2029 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2030 | "REPORT_IR_DATA", report->id, size-1); | ||
| 2031 | hid_debug_event(hdev, buff); | ||
| 2032 | if (raw_data[1] == 0) { | ||
| 2033 | snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n"); | ||
| 2034 | hid_debug_event(hdev, buff); | ||
| 2035 | } else if (raw_data[1] + 1 <= size) { | ||
| 2036 | snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ", | ||
| 2037 | raw_data[1]-1); | ||
| 2038 | hid_debug_event(hdev, buff); | ||
| 2039 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1); | ||
| 2040 | hid_debug_event(hdev, buff); | ||
| 2041 | } else { | ||
| 2042 | snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n", | ||
| 2043 | raw_data[1]-1); | ||
| 2044 | hid_debug_event(hdev, buff); | ||
| 2045 | } | ||
| 2046 | break; | ||
| 2047 | case REPORT_EE_DATA: | ||
| 2048 | /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */ | ||
| 2049 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2050 | "REPORT_EE_DATA", report->id, size-1); | ||
| 2051 | hid_debug_event(hdev, buff); | ||
| 2052 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 2053 | raw_data[2], raw_data[1]); | ||
| 2054 | hid_debug_event(hdev, buff); | ||
| 2055 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 2056 | hid_debug_event(hdev, buff); | ||
| 2057 | if (raw_data[3] == 0) { | ||
| 2058 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2059 | hid_debug_event(hdev, buff); | ||
| 2060 | } else if (raw_data[3] + 4 <= size) { | ||
| 2061 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2062 | hid_debug_event(hdev, buff); | ||
| 2063 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 2064 | hid_debug_event(hdev, buff); | ||
| 2065 | } else { | ||
| 2066 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2067 | hid_debug_event(hdev, buff); | ||
| 2068 | } | ||
| 2069 | break; | ||
| 2070 | case REPORT_MEMORY: | ||
| 2071 | /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */ | ||
| 2072 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2073 | "REPORT_MEMORY", report->id, size-1); | ||
| 2074 | hid_debug_event(hdev, buff); | ||
| 2075 | switch (data->addr_sz) { | ||
| 2076 | case 2: | ||
| 2077 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n", | ||
| 2078 | raw_data[2], raw_data[1]); | ||
| 2079 | hid_debug_event(hdev, buff); | ||
| 2080 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]); | ||
| 2081 | hid_debug_event(hdev, buff); | ||
| 2082 | if (raw_data[3] == 0) { | ||
| 2083 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2084 | } else if (raw_data[3] + 4 <= size) { | ||
| 2085 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2086 | hid_debug_event(hdev, buff); | ||
| 2087 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]); | ||
| 2088 | } else { | ||
| 2089 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2090 | } | ||
| 2091 | break; | ||
| 2092 | case 3: | ||
| 2093 | snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n", | ||
| 2094 | raw_data[3], raw_data[2], raw_data[1]); | ||
| 2095 | hid_debug_event(hdev, buff); | ||
| 2096 | snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]); | ||
| 2097 | hid_debug_event(hdev, buff); | ||
| 2098 | if (raw_data[4] == 0) { | ||
| 2099 | snprintf(buff, BUFF_SZ, "\tNo data\n"); | ||
| 2100 | } else if (raw_data[4] + 5 <= size) { | ||
| 2101 | snprintf(buff, BUFF_SZ, "\tData: "); | ||
| 2102 | hid_debug_event(hdev, buff); | ||
| 2103 | dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]); | ||
| 2104 | } else { | ||
| 2105 | snprintf(buff, BUFF_SZ, "\tData overflowed\n"); | ||
| 2106 | } | ||
| 2107 | break; | ||
| 2108 | default: | ||
| 2109 | snprintf(buff, BUFF_SZ, "\tNot supported\n"); | ||
| 2110 | } | ||
| 2111 | hid_debug_event(hdev, buff); | ||
| 2112 | break; | ||
| 2113 | case REPORT_VERSION: | ||
| 2114 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2115 | "REPORT_VERSION", report->id, size-1); | ||
| 2116 | hid_debug_event(hdev, buff); | ||
| 2117 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
| 2118 | raw_data[2], raw_data[1]); | ||
| 2119 | hid_debug_event(hdev, buff); | ||
| 2120 | break; | ||
| 2121 | case REPORT_BL_ERASE_MEMORY: | ||
| 2122 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2123 | "REPORT_BL_ERASE_MEMORY", report->id, size-1); | ||
| 2124 | hid_debug_event(hdev, buff); | ||
| 2125 | /* TODO */ | ||
| 2126 | break; | ||
| 2127 | case REPORT_BL_READ_MEMORY: | ||
| 2128 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2129 | "REPORT_BL_READ_MEMORY", report->id, size-1); | ||
| 2130 | hid_debug_event(hdev, buff); | ||
| 2131 | /* TODO */ | ||
| 2132 | break; | ||
| 2133 | case REPORT_BL_WRITE_MEMORY: | ||
| 2134 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2135 | "REPORT_BL_WRITE_MEMORY", report->id, size-1); | ||
| 2136 | hid_debug_event(hdev, buff); | ||
| 2137 | /* TODO */ | ||
| 2138 | break; | ||
| 2139 | case REPORT_DEVID: | ||
| 2140 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2141 | "REPORT_DEVID", report->id, size-1); | ||
| 2142 | hid_debug_event(hdev, buff); | ||
| 2143 | snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n", | ||
| 2144 | raw_data[1], raw_data[2], raw_data[3], raw_data[4]); | ||
| 2145 | hid_debug_event(hdev, buff); | ||
| 2146 | snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n", | ||
| 2147 | raw_data[5]); | ||
| 2148 | hid_debug_event(hdev, buff); | ||
| 2149 | break; | ||
| 2150 | case REPORT_SPLASH_SIZE: | ||
| 2151 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2152 | "REPORT_SPLASH_SIZE", report->id, size-1); | ||
| 2153 | hid_debug_event(hdev, buff); | ||
| 2154 | snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n", | ||
| 2155 | (raw_data[2] << 8) | raw_data[1]); | ||
| 2156 | hid_debug_event(hdev, buff); | ||
| 2157 | snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n", | ||
| 2158 | (raw_data[4] << 8) | raw_data[3]); | ||
| 2159 | hid_debug_event(hdev, buff); | ||
| 2160 | break; | ||
| 2161 | case REPORT_HOOK_VERSION: | ||
| 2162 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2163 | "REPORT_HOOK_VERSION", report->id, size-1); | ||
| 2164 | hid_debug_event(hdev, buff); | ||
| 2165 | snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n", | ||
| 2166 | raw_data[1], raw_data[2]); | ||
| 2167 | hid_debug_event(hdev, buff); | ||
| 2168 | break; | ||
| 2169 | default: | ||
| 2170 | snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n", | ||
| 2171 | "<unknown>", report->id, size-1); | ||
| 2172 | hid_debug_event(hdev, buff); | ||
| 2173 | break; | ||
| 2174 | } | ||
| 2175 | wake_up_interruptible(&hdev->debug_wait); | ||
| 2176 | kfree(buff); | ||
| 2177 | } | ||
| 2178 | |||
| 2179 | static void picolcd_init_devfs(struct picolcd_data *data, | ||
| 2180 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
| 2181 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
| 2182 | struct hid_report *reset) | ||
| 2183 | { | ||
| 2184 | struct hid_device *hdev = data->hdev; | ||
| 2185 | |||
| 2186 | mutex_init(&data->mutex_flash); | ||
| 2187 | |||
| 2188 | /* reset */ | ||
| 2189 | if (reset) | ||
| 2190 | data->debug_reset = debugfs_create_file("reset", 0600, | ||
| 2191 | hdev->debug_dir, data, &picolcd_debug_reset_fops); | ||
| 2192 | |||
| 2193 | /* eeprom */ | ||
| 2194 | if (eeprom_r || eeprom_w) | ||
| 2195 | data->debug_eeprom = debugfs_create_file("eeprom", | ||
| 2196 | (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0), | ||
| 2197 | hdev->debug_dir, data, &picolcd_debug_eeprom_fops); | ||
| 2198 | |||
| 2199 | /* flash */ | ||
| 2200 | if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8) | ||
| 2201 | data->addr_sz = flash_r->field[0]->report_count - 1; | ||
| 2202 | else | ||
| 2203 | data->addr_sz = -1; | ||
| 2204 | if (data->addr_sz == 2 || data->addr_sz == 3) { | ||
| 2205 | data->debug_flash = debugfs_create_file("flash", | ||
| 2206 | (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0), | ||
| 2207 | hdev->debug_dir, data, &picolcd_debug_flash_fops); | ||
| 2208 | } else if (flash_r || flash_w) | ||
| 2209 | dev_warn(&hdev->dev, "Unexpected FLASH access reports, " | ||
| 2210 | "please submit rdesc for review\n"); | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | static void picolcd_exit_devfs(struct picolcd_data *data) | ||
| 2214 | { | ||
| 2215 | struct dentry *dent; | ||
| 2216 | |||
| 2217 | dent = data->debug_reset; | ||
| 2218 | data->debug_reset = NULL; | ||
| 2219 | if (dent) | ||
| 2220 | debugfs_remove(dent); | ||
| 2221 | dent = data->debug_eeprom; | ||
| 2222 | data->debug_eeprom = NULL; | ||
| 2223 | if (dent) | ||
| 2224 | debugfs_remove(dent); | ||
| 2225 | dent = data->debug_flash; | ||
| 2226 | data->debug_flash = NULL; | ||
| 2227 | if (dent) | ||
| 2228 | debugfs_remove(dent); | ||
| 2229 | mutex_destroy(&data->mutex_flash); | ||
| 2230 | } | ||
| 2231 | #else | ||
| 2232 | static inline void picolcd_debug_raw_event(struct picolcd_data *data, | ||
| 2233 | struct hid_device *hdev, struct hid_report *report, | ||
| 2234 | u8 *raw_data, int size) | ||
| 2235 | { | ||
| 2236 | } | ||
| 2237 | static inline void picolcd_init_devfs(struct picolcd_data *data, | ||
| 2238 | struct hid_report *eeprom_r, struct hid_report *eeprom_w, | ||
| 2239 | struct hid_report *flash_r, struct hid_report *flash_w, | ||
| 2240 | struct hid_report *reset) | ||
| 2241 | { | ||
| 2242 | } | ||
| 2243 | static inline void picolcd_exit_devfs(struct picolcd_data *data) | ||
| 2244 | { | ||
| 2245 | } | ||
| 2246 | #endif /* CONFIG_DEBUG_FS */ | ||
| 2247 | |||
| 2248 | /* | ||
| 2249 | * Handle raw report as sent by device | ||
| 2250 | */ | ||
| 2251 | static int picolcd_raw_event(struct hid_device *hdev, | ||
| 2252 | struct hid_report *report, u8 *raw_data, int size) | ||
| 2253 | { | ||
| 2254 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 2255 | unsigned long flags; | ||
| 2256 | int ret = 0; | ||
| 2257 | |||
| 2258 | if (!data) | ||
| 2259 | return 1; | ||
| 2260 | |||
| 2261 | if (report->id == REPORT_KEY_STATE) { | ||
| 2262 | if (data->input_keys) | ||
| 2263 | ret = picolcd_raw_keypad(data, report, raw_data+1, size-1); | ||
| 2264 | } else if (report->id == REPORT_IR_DATA) { | ||
| 2265 | if (data->input_cir) | ||
| 2266 | ret = picolcd_raw_cir(data, report, raw_data+1, size-1); | ||
| 2267 | } else { | ||
| 2268 | spin_lock_irqsave(&data->lock, flags); | ||
| 2269 | /* | ||
| 2270 | * We let the caller of picolcd_send_and_wait() check if the | ||
| 2271 | * report we got is one of the expected ones or not. | ||
| 2272 | */ | ||
| 2273 | if (data->pending) { | ||
| 2274 | memcpy(data->pending->raw_data, raw_data+1, size-1); | ||
| 2275 | data->pending->raw_size = size-1; | ||
| 2276 | data->pending->in_report = report; | ||
| 2277 | complete(&data->pending->ready); | ||
| 2278 | } | ||
| 2279 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 2280 | } | ||
| 2281 | |||
| 2282 | picolcd_debug_raw_event(data, hdev, report, raw_data, size); | ||
| 2283 | return 1; | ||
| 2284 | } | ||
| 2285 | |||
| 2286 | #ifdef CONFIG_PM | ||
| 2287 | static int picolcd_suspend(struct hid_device *hdev, pm_message_t message) | ||
| 2288 | { | ||
| 2289 | if (message.event & PM_EVENT_AUTO) | ||
| 2290 | return 0; | ||
| 2291 | |||
| 2292 | picolcd_suspend_backlight(hid_get_drvdata(hdev)); | ||
| 2293 | dbg_hid(PICOLCD_NAME " device ready for suspend\n"); | ||
| 2294 | return 0; | ||
| 2295 | } | ||
| 2296 | |||
| 2297 | static int picolcd_resume(struct hid_device *hdev) | ||
| 2298 | { | ||
| 2299 | int ret; | ||
| 2300 | ret = picolcd_resume_backlight(hid_get_drvdata(hdev)); | ||
| 2301 | if (ret) | ||
| 2302 | dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret); | ||
| 2303 | return 0; | ||
| 2304 | } | ||
| 2305 | |||
| 2306 | static int picolcd_reset_resume(struct hid_device *hdev) | ||
| 2307 | { | ||
| 2308 | int ret; | ||
| 2309 | ret = picolcd_reset(hdev); | ||
| 2310 | if (ret) | ||
| 2311 | dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret); | ||
| 2312 | ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0); | ||
| 2313 | if (ret) | ||
| 2314 | dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret); | ||
| 2315 | ret = picolcd_resume_lcd(hid_get_drvdata(hdev)); | ||
| 2316 | if (ret) | ||
| 2317 | dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret); | ||
| 2318 | ret = picolcd_resume_backlight(hid_get_drvdata(hdev)); | ||
| 2319 | if (ret) | ||
| 2320 | dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret); | ||
| 2321 | picolcd_leds_set(hid_get_drvdata(hdev)); | ||
| 2322 | return 0; | ||
| 2323 | } | ||
| 2324 | #endif | ||
| 2325 | |||
| 2326 | /* initialize keypad input device */ | ||
| 2327 | static int picolcd_init_keys(struct picolcd_data *data, | ||
| 2328 | struct hid_report *report) | ||
| 2329 | { | ||
| 2330 | struct hid_device *hdev = data->hdev; | ||
| 2331 | struct input_dev *idev; | ||
| 2332 | int error, i; | ||
| 2333 | |||
| 2334 | if (!report) | ||
| 2335 | return -ENODEV; | ||
| 2336 | if (report->maxfield != 1 || report->field[0]->report_count != 2 || | ||
| 2337 | report->field[0]->report_size != 8) { | ||
| 2338 | dev_err(&hdev->dev, "unsupported KEY_STATE report"); | ||
| 2339 | return -EINVAL; | ||
| 2340 | } | ||
| 2341 | |||
| 2342 | idev = input_allocate_device(); | ||
| 2343 | if (idev == NULL) { | ||
| 2344 | dev_err(&hdev->dev, "failed to allocate input device"); | ||
| 2345 | return -ENOMEM; | ||
| 2346 | } | ||
| 2347 | input_set_drvdata(idev, hdev); | ||
| 2348 | memcpy(data->keycode, def_keymap, sizeof(def_keymap)); | ||
| 2349 | idev->name = hdev->name; | ||
| 2350 | idev->phys = hdev->phys; | ||
| 2351 | idev->uniq = hdev->uniq; | ||
| 2352 | idev->id.bustype = hdev->bus; | ||
| 2353 | idev->id.vendor = hdev->vendor; | ||
| 2354 | idev->id.product = hdev->product; | ||
| 2355 | idev->id.version = hdev->version; | ||
| 2356 | idev->dev.parent = hdev->dev.parent; | ||
| 2357 | idev->keycode = &data->keycode; | ||
| 2358 | idev->keycodemax = PICOLCD_KEYS; | ||
| 2359 | idev->keycodesize = sizeof(data->keycode[0]); | ||
| 2360 | input_set_capability(idev, EV_MSC, MSC_SCAN); | ||
| 2361 | set_bit(EV_REP, idev->evbit); | ||
| 2362 | for (i = 0; i < PICOLCD_KEYS; i++) | ||
| 2363 | input_set_capability(idev, EV_KEY, data->keycode[i]); | ||
| 2364 | error = input_register_device(idev); | ||
| 2365 | if (error) { | ||
| 2366 | dev_err(&hdev->dev, "error registering the input device"); | ||
| 2367 | input_free_device(idev); | ||
| 2368 | return error; | ||
| 2369 | } | ||
| 2370 | data->input_keys = idev; | ||
| 2371 | return 0; | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | static void picolcd_exit_keys(struct picolcd_data *data) | ||
| 2375 | { | ||
| 2376 | struct input_dev *idev = data->input_keys; | ||
| 2377 | |||
| 2378 | data->input_keys = NULL; | ||
| 2379 | if (idev) | ||
| 2380 | input_unregister_device(idev); | ||
| 2381 | } | ||
| 2382 | |||
| 2383 | /* initialize CIR input device */ | ||
| 2384 | static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) | ||
| 2385 | { | ||
| 2386 | /* support not implemented yet */ | ||
| 2387 | return 0; | ||
| 2388 | } | ||
| 2389 | |||
| 2390 | static inline void picolcd_exit_cir(struct picolcd_data *data) | ||
| 2391 | { | ||
| 2392 | } | ||
| 2393 | |||
| 2394 | static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | ||
| 2395 | { | ||
| 2396 | int error; | ||
| 2397 | |||
| 2398 | error = picolcd_check_version(hdev); | ||
| 2399 | if (error) | ||
| 2400 | return error; | ||
| 2401 | |||
| 2402 | if (data->version[0] != 0 && data->version[1] != 3) | ||
| 2403 | dev_info(&hdev->dev, "Device with untested firmware revision, " | ||
| 2404 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
| 2405 | dev_name(&hdev->dev)); | ||
| 2406 | |||
| 2407 | /* Setup keypad input device */ | ||
| 2408 | error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev)); | ||
| 2409 | if (error) | ||
| 2410 | goto err; | ||
| 2411 | |||
| 2412 | /* Setup CIR input device */ | ||
| 2413 | error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev)); | ||
| 2414 | if (error) | ||
| 2415 | goto err; | ||
| 2416 | |||
| 2417 | /* Set up the framebuffer device */ | ||
| 2418 | error = picolcd_init_framebuffer(data); | ||
| 2419 | if (error) | ||
| 2420 | goto err; | ||
| 2421 | |||
| 2422 | /* Setup lcd class device */ | ||
| 2423 | error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev)); | ||
| 2424 | if (error) | ||
| 2425 | goto err; | ||
| 2426 | |||
| 2427 | /* Setup backlight class device */ | ||
| 2428 | error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev)); | ||
| 2429 | if (error) | ||
| 2430 | goto err; | ||
| 2431 | |||
| 2432 | /* Setup the LED class devices */ | ||
| 2433 | error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev)); | ||
| 2434 | if (error) | ||
| 2435 | goto err; | ||
| 2436 | |||
| 2437 | picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev), | ||
| 2438 | picolcd_out_report(REPORT_EE_WRITE, hdev), | ||
| 2439 | picolcd_out_report(REPORT_READ_MEMORY, hdev), | ||
| 2440 | picolcd_out_report(REPORT_WRITE_MEMORY, hdev), | ||
| 2441 | picolcd_out_report(REPORT_RESET, hdev)); | ||
| 2442 | return 0; | ||
| 2443 | err: | ||
| 2444 | picolcd_exit_leds(data); | ||
| 2445 | picolcd_exit_backlight(data); | ||
| 2446 | picolcd_exit_lcd(data); | ||
| 2447 | picolcd_exit_framebuffer(data); | ||
| 2448 | picolcd_exit_cir(data); | ||
| 2449 | picolcd_exit_keys(data); | ||
| 2450 | return error; | ||
| 2451 | } | ||
| 2452 | |||
| 2453 | static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data) | ||
| 2454 | { | ||
| 2455 | int error; | ||
| 2456 | |||
| 2457 | error = picolcd_check_version(hdev); | ||
| 2458 | if (error) | ||
| 2459 | return error; | ||
| 2460 | |||
| 2461 | if (data->version[0] != 1 && data->version[1] != 0) | ||
| 2462 | dev_info(&hdev->dev, "Device with untested bootloader revision, " | ||
| 2463 | "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n", | ||
| 2464 | dev_name(&hdev->dev)); | ||
| 2465 | |||
| 2466 | picolcd_init_devfs(data, NULL, NULL, | ||
| 2467 | picolcd_out_report(REPORT_BL_READ_MEMORY, hdev), | ||
| 2468 | picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL); | ||
| 2469 | return 0; | ||
| 2470 | } | ||
| 2471 | |||
| 2472 | static int picolcd_probe(struct hid_device *hdev, | ||
| 2473 | const struct hid_device_id *id) | ||
| 2474 | { | ||
| 2475 | struct picolcd_data *data; | ||
| 2476 | int error = -ENOMEM; | ||
| 2477 | |||
| 2478 | dbg_hid(PICOLCD_NAME " hardware probe...\n"); | ||
| 2479 | |||
| 2480 | /* | ||
| 2481 | * Let's allocate the picolcd data structure, set some reasonable | ||
| 2482 | * defaults, and associate it with the device | ||
| 2483 | */ | ||
| 2484 | data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL); | ||
| 2485 | if (data == NULL) { | ||
| 2486 | dev_err(&hdev->dev, "can't allocate space for Minibox PicoLCD device data\n"); | ||
| 2487 | error = -ENOMEM; | ||
| 2488 | goto err_no_cleanup; | ||
| 2489 | } | ||
| 2490 | |||
| 2491 | spin_lock_init(&data->lock); | ||
| 2492 | mutex_init(&data->mutex); | ||
| 2493 | data->hdev = hdev; | ||
| 2494 | data->opmode_delay = 5000; | ||
| 2495 | if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER) | ||
| 2496 | data->status |= PICOLCD_BOOTLOADER; | ||
| 2497 | hid_set_drvdata(hdev, data); | ||
| 2498 | |||
| 2499 | /* Parse the device reports and start it up */ | ||
| 2500 | error = hid_parse(hdev); | ||
| 2501 | if (error) { | ||
| 2502 | dev_err(&hdev->dev, "device report parse failed\n"); | ||
| 2503 | goto err_cleanup_data; | ||
| 2504 | } | ||
| 2505 | |||
| 2506 | /* We don't use hidinput but hid_hw_start() fails if nothing is | ||
| 2507 | * claimed. So spoof claimed input. */ | ||
| 2508 | hdev->claimed = HID_CLAIMED_INPUT; | ||
| 2509 | error = hid_hw_start(hdev, 0); | ||
| 2510 | hdev->claimed = 0; | ||
| 2511 | if (error) { | ||
| 2512 | dev_err(&hdev->dev, "hardware start failed\n"); | ||
| 2513 | goto err_cleanup_data; | ||
| 2514 | } | ||
| 2515 | |||
| 2516 | error = hdev->ll_driver->open(hdev); | ||
| 2517 | if (error) { | ||
| 2518 | dev_err(&hdev->dev, "failed to open input interrupt pipe for key and IR events\n"); | ||
| 2519 | goto err_cleanup_hid_hw; | ||
| 2520 | } | ||
| 2521 | |||
| 2522 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 2523 | if (error) { | ||
| 2524 | dev_err(&hdev->dev, "failed to create sysfs attributes\n"); | ||
| 2525 | goto err_cleanup_hid_ll; | ||
| 2526 | } | ||
| 2527 | |||
| 2528 | error = device_create_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 2529 | if (error) { | ||
| 2530 | dev_err(&hdev->dev, "failed to create sysfs attributes\n"); | ||
| 2531 | goto err_cleanup_sysfs1; | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | if (data->status & PICOLCD_BOOTLOADER) | ||
| 2535 | error = picolcd_probe_bootloader(hdev, data); | ||
| 2536 | else | ||
| 2537 | error = picolcd_probe_lcd(hdev, data); | ||
| 2538 | if (error) | ||
| 2539 | goto err_cleanup_sysfs2; | ||
| 2540 | |||
| 2541 | dbg_hid(PICOLCD_NAME " activated and initialized\n"); | ||
| 2542 | return 0; | ||
| 2543 | |||
| 2544 | err_cleanup_sysfs2: | ||
| 2545 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 2546 | err_cleanup_sysfs1: | ||
| 2547 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 2548 | err_cleanup_hid_ll: | ||
| 2549 | hdev->ll_driver->close(hdev); | ||
| 2550 | err_cleanup_hid_hw: | ||
| 2551 | hid_hw_stop(hdev); | ||
| 2552 | err_cleanup_data: | ||
| 2553 | kfree(data); | ||
| 2554 | err_no_cleanup: | ||
| 2555 | hid_set_drvdata(hdev, NULL); | ||
| 2556 | |||
| 2557 | return error; | ||
| 2558 | } | ||
| 2559 | |||
| 2560 | static void picolcd_remove(struct hid_device *hdev) | ||
| 2561 | { | ||
| 2562 | struct picolcd_data *data = hid_get_drvdata(hdev); | ||
| 2563 | unsigned long flags; | ||
| 2564 | |||
| 2565 | dbg_hid(PICOLCD_NAME " hardware remove...\n"); | ||
| 2566 | spin_lock_irqsave(&data->lock, flags); | ||
| 2567 | data->status |= PICOLCD_FAILED; | ||
| 2568 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 2569 | |||
| 2570 | picolcd_exit_devfs(data); | ||
| 2571 | device_remove_file(&hdev->dev, &dev_attr_operation_mode); | ||
| 2572 | device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay); | ||
| 2573 | hdev->ll_driver->close(hdev); | ||
| 2574 | hid_hw_stop(hdev); | ||
| 2575 | hid_set_drvdata(hdev, NULL); | ||
| 2576 | |||
| 2577 | /* Shortcut potential pending reply that will never arrive */ | ||
| 2578 | spin_lock_irqsave(&data->lock, flags); | ||
| 2579 | if (data->pending) | ||
| 2580 | complete(&data->pending->ready); | ||
| 2581 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 2582 | |||
| 2583 | /* Cleanup LED */ | ||
| 2584 | picolcd_exit_leds(data); | ||
| 2585 | /* Clean up the framebuffer */ | ||
| 2586 | picolcd_exit_backlight(data); | ||
| 2587 | picolcd_exit_lcd(data); | ||
| 2588 | picolcd_exit_framebuffer(data); | ||
| 2589 | /* Cleanup input */ | ||
| 2590 | picolcd_exit_cir(data); | ||
| 2591 | picolcd_exit_keys(data); | ||
| 2592 | |||
| 2593 | mutex_destroy(&data->mutex); | ||
| 2594 | /* Finally, clean up the picolcd data itself */ | ||
| 2595 | kfree(data); | ||
| 2596 | } | ||
| 2597 | |||
| 2598 | static const struct hid_device_id picolcd_devices[] = { | ||
| 2599 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | ||
| 2600 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | ||
| 2601 | { } | ||
| 2602 | }; | ||
| 2603 | MODULE_DEVICE_TABLE(hid, picolcd_devices); | ||
| 2604 | |||
| 2605 | static struct hid_driver picolcd_driver = { | ||
| 2606 | .name = "hid-picolcd", | ||
| 2607 | .id_table = picolcd_devices, | ||
| 2608 | .probe = picolcd_probe, | ||
| 2609 | .remove = picolcd_remove, | ||
| 2610 | .raw_event = picolcd_raw_event, | ||
| 2611 | #ifdef CONFIG_PM | ||
| 2612 | .suspend = picolcd_suspend, | ||
| 2613 | .resume = picolcd_resume, | ||
| 2614 | .reset_resume = picolcd_reset_resume, | ||
| 2615 | #endif | ||
| 2616 | }; | ||
| 2617 | |||
| 2618 | static int __init picolcd_init(void) | ||
| 2619 | { | ||
| 2620 | return hid_register_driver(&picolcd_driver); | ||
| 2621 | } | ||
| 2622 | |||
| 2623 | static void __exit picolcd_exit(void) | ||
| 2624 | { | ||
| 2625 | hid_unregister_driver(&picolcd_driver); | ||
| 2626 | } | ||
| 2627 | |||
| 2628 | module_init(picolcd_init); | ||
| 2629 | module_exit(picolcd_exit); | ||
| 2630 | MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver"); | ||
| 2631 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c new file mode 100644 index 000000000000..845f428b8090 --- /dev/null +++ b/drivers/hid/hid-prodikeys.c | |||
| @@ -0,0 +1,910 @@ | |||
| 1 | /* | ||
| 2 | * HID driver for the Prodikeys PC-MIDI Keyboard | ||
| 3 | * providing midi & extra multimedia keys functionality | ||
| 4 | * | ||
| 5 | * Copyright (c) 2009 Don Prince <dhprince.devel@yahoo.co.uk> | ||
| 6 | * | ||
| 7 | * Controls for Octave Shift Up/Down, Channel, and | ||
| 8 | * Sustain Duration available via sysfs. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * This program is free software; you can redistribute it and/or modify it | ||
| 14 | * under the terms of the GNU General Public License as published by the Free | ||
| 15 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 16 | * any later version. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/usb.h> | ||
| 22 | #include <linux/mutex.h> | ||
| 23 | #include <linux/hid.h> | ||
| 24 | #include <sound/core.h> | ||
| 25 | #include <sound/initval.h> | ||
| 26 | #include <sound/rawmidi.h> | ||
| 27 | #include "usbhid/usbhid.h" | ||
| 28 | #include "hid-ids.h" | ||
| 29 | |||
| 30 | |||
| 31 | #define pk_debug(format, arg...) \ | ||
| 32 | pr_debug("hid-prodikeys: " format "\n" , ## arg) | ||
| 33 | #define pk_error(format, arg...) \ | ||
| 34 | pr_err("hid-prodikeys: " format "\n" , ## arg) | ||
| 35 | |||
| 36 | struct pcmidi_snd; | ||
| 37 | |||
| 38 | struct pk_device { | ||
| 39 | unsigned long quirks; | ||
| 40 | |||
| 41 | struct hid_device *hdev; | ||
| 42 | struct pcmidi_snd *pm; /* pcmidi device context */ | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct pcmidi_snd; | ||
| 46 | |||
| 47 | struct pcmidi_sustain { | ||
| 48 | unsigned long in_use; | ||
| 49 | struct pcmidi_snd *pm; | ||
| 50 | struct timer_list timer; | ||
| 51 | unsigned char status; | ||
| 52 | unsigned char note; | ||
| 53 | unsigned char velocity; | ||
| 54 | }; | ||
| 55 | |||
| 56 | #define PCMIDI_SUSTAINED_MAX 32 | ||
| 57 | struct pcmidi_snd { | ||
| 58 | struct pk_device *pk; | ||
| 59 | unsigned short ifnum; | ||
| 60 | struct hid_report *pcmidi_report6; | ||
| 61 | struct input_dev *input_ep82; | ||
| 62 | unsigned short midi_mode; | ||
| 63 | unsigned short midi_sustain_mode; | ||
| 64 | unsigned short midi_sustain; | ||
| 65 | unsigned short midi_channel; | ||
| 66 | short midi_octave; | ||
| 67 | struct pcmidi_sustain sustained_notes[PCMIDI_SUSTAINED_MAX]; | ||
| 68 | unsigned short fn_state; | ||
| 69 | unsigned short last_key[24]; | ||
| 70 | spinlock_t rawmidi_in_lock; | ||
| 71 | struct snd_card *card; | ||
| 72 | struct snd_rawmidi *rwmidi; | ||
| 73 | struct snd_rawmidi_substream *in_substream; | ||
| 74 | struct snd_rawmidi_substream *out_substream; | ||
| 75 | unsigned long in_triggered; | ||
| 76 | unsigned long out_active; | ||
| 77 | }; | ||
| 78 | |||
| 79 | #define PK_QUIRK_NOGET 0x00010000 | ||
| 80 | #define PCMIDI_MIDDLE_C 60 | ||
| 81 | #define PCMIDI_CHANNEL_MIN 0 | ||
| 82 | #define PCMIDI_CHANNEL_MAX 15 | ||
| 83 | #define PCMIDI_OCTAVE_MIN (-2) | ||
| 84 | #define PCMIDI_OCTAVE_MAX 2 | ||
| 85 | #define PCMIDI_SUSTAIN_MIN 0 | ||
| 86 | #define PCMIDI_SUSTAIN_MAX 5000 | ||
| 87 | |||
| 88 | static const char shortname[] = "PC-MIDI"; | ||
| 89 | static const char longname[] = "Prodikeys PC-MIDI Keyboard"; | ||
| 90 | |||
| 91 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | ||
| 92 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | ||
| 93 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | ||
| 94 | |||
| 95 | module_param_array(index, int, NULL, 0444); | ||
| 96 | module_param_array(id, charp, NULL, 0444); | ||
| 97 | module_param_array(enable, bool, NULL, 0444); | ||
| 98 | MODULE_PARM_DESC(index, "Index value for the PC-MIDI virtual audio driver"); | ||
| 99 | MODULE_PARM_DESC(id, "ID string for the PC-MIDI virtual audio driver"); | ||
| 100 | MODULE_PARM_DESC(enable, "Enable for the PC-MIDI virtual audio driver"); | ||
| 101 | |||
| 102 | |||
| 103 | /* Output routine for the sysfs channel file */ | ||
| 104 | static ssize_t show_channel(struct device *dev, | ||
| 105 | struct device_attribute *attr, char *buf) | ||
| 106 | { | ||
| 107 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 108 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 109 | |||
| 110 | dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel); | ||
| 111 | |||
| 112 | return sprintf(buf, "%u (min:%u, max:%u)\n", pk->pm->midi_channel, | ||
| 113 | PCMIDI_CHANNEL_MIN, PCMIDI_CHANNEL_MAX); | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Input routine for the sysfs channel file */ | ||
| 117 | static ssize_t store_channel(struct device *dev, | ||
| 118 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 119 | { | ||
| 120 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 121 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 122 | |||
| 123 | unsigned channel = 0; | ||
| 124 | |||
| 125 | if (sscanf(buf, "%u", &channel) > 0 && channel <= PCMIDI_CHANNEL_MAX) { | ||
| 126 | dbg_hid("pcmidi sysfs write channel=%u\n", channel); | ||
| 127 | pk->pm->midi_channel = channel; | ||
| 128 | return strlen(buf); | ||
| 129 | } | ||
| 130 | return -EINVAL; | ||
| 131 | } | ||
| 132 | |||
| 133 | static DEVICE_ATTR(channel, S_IRUGO | S_IWUGO, show_channel, | ||
| 134 | store_channel); | ||
| 135 | |||
| 136 | static struct device_attribute *sysfs_device_attr_channel = { | ||
| 137 | &dev_attr_channel, | ||
| 138 | }; | ||
| 139 | |||
| 140 | /* Output routine for the sysfs sustain file */ | ||
| 141 | static ssize_t show_sustain(struct device *dev, | ||
| 142 | struct device_attribute *attr, char *buf) | ||
| 143 | { | ||
| 144 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 145 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 146 | |||
| 147 | dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain); | ||
| 148 | |||
| 149 | return sprintf(buf, "%u (off:%u, max:%u (ms))\n", pk->pm->midi_sustain, | ||
| 150 | PCMIDI_SUSTAIN_MIN, PCMIDI_SUSTAIN_MAX); | ||
| 151 | } | ||
| 152 | |||
| 153 | /* Input routine for the sysfs sustain file */ | ||
| 154 | static ssize_t store_sustain(struct device *dev, | ||
| 155 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 156 | { | ||
| 157 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 158 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 159 | |||
| 160 | unsigned sustain = 0; | ||
| 161 | |||
| 162 | if (sscanf(buf, "%u", &sustain) > 0 && sustain <= PCMIDI_SUSTAIN_MAX) { | ||
| 163 | dbg_hid("pcmidi sysfs write sustain=%u\n", sustain); | ||
| 164 | pk->pm->midi_sustain = sustain; | ||
| 165 | pk->pm->midi_sustain_mode = | ||
| 166 | (0 == sustain || !pk->pm->midi_mode) ? 0 : 1; | ||
| 167 | return strlen(buf); | ||
| 168 | } | ||
| 169 | return -EINVAL; | ||
| 170 | } | ||
| 171 | |||
| 172 | static DEVICE_ATTR(sustain, S_IRUGO | S_IWUGO, show_sustain, | ||
| 173 | store_sustain); | ||
| 174 | |||
| 175 | static struct device_attribute *sysfs_device_attr_sustain = { | ||
| 176 | &dev_attr_sustain, | ||
| 177 | }; | ||
| 178 | |||
| 179 | /* Output routine for the sysfs octave file */ | ||
| 180 | static ssize_t show_octave(struct device *dev, | ||
| 181 | struct device_attribute *attr, char *buf) | ||
| 182 | { | ||
| 183 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 184 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 185 | |||
| 186 | dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave); | ||
| 187 | |||
| 188 | return sprintf(buf, "%d (min:%d, max:%d)\n", pk->pm->midi_octave, | ||
| 189 | PCMIDI_OCTAVE_MIN, PCMIDI_OCTAVE_MAX); | ||
| 190 | } | ||
| 191 | |||
| 192 | /* Input routine for the sysfs octave file */ | ||
| 193 | static ssize_t store_octave(struct device *dev, | ||
| 194 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 195 | { | ||
| 196 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
| 197 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 198 | |||
| 199 | int octave = 0; | ||
| 200 | |||
| 201 | if (sscanf(buf, "%d", &octave) > 0 && | ||
| 202 | octave >= PCMIDI_OCTAVE_MIN && octave <= PCMIDI_OCTAVE_MAX) { | ||
| 203 | dbg_hid("pcmidi sysfs write octave=%d\n", octave); | ||
| 204 | pk->pm->midi_octave = octave; | ||
| 205 | return strlen(buf); | ||
| 206 | } | ||
| 207 | return -EINVAL; | ||
| 208 | } | ||
| 209 | |||
| 210 | static DEVICE_ATTR(octave, S_IRUGO | S_IWUGO, show_octave, | ||
| 211 | store_octave); | ||
| 212 | |||
| 213 | static struct device_attribute *sysfs_device_attr_octave = { | ||
| 214 | &dev_attr_octave, | ||
| 215 | }; | ||
| 216 | |||
| 217 | |||
| 218 | static void pcmidi_send_note(struct pcmidi_snd *pm, | ||
| 219 | unsigned char status, unsigned char note, unsigned char velocity) | ||
| 220 | { | ||
| 221 | unsigned long flags; | ||
| 222 | unsigned char buffer[3]; | ||
| 223 | |||
| 224 | buffer[0] = status; | ||
| 225 | buffer[1] = note; | ||
| 226 | buffer[2] = velocity; | ||
| 227 | |||
| 228 | spin_lock_irqsave(&pm->rawmidi_in_lock, flags); | ||
| 229 | |||
| 230 | if (!pm->in_substream) | ||
| 231 | goto drop_note; | ||
| 232 | if (!test_bit(pm->in_substream->number, &pm->in_triggered)) | ||
| 233 | goto drop_note; | ||
| 234 | |||
| 235 | snd_rawmidi_receive(pm->in_substream, buffer, 3); | ||
| 236 | |||
| 237 | drop_note: | ||
| 238 | spin_unlock_irqrestore(&pm->rawmidi_in_lock, flags); | ||
| 239 | |||
| 240 | return; | ||
| 241 | } | ||
| 242 | |||
| 243 | void pcmidi_sustained_note_release(unsigned long data) | ||
| 244 | { | ||
| 245 | struct pcmidi_sustain *pms = (struct pcmidi_sustain *)data; | ||
| 246 | |||
| 247 | pcmidi_send_note(pms->pm, pms->status, pms->note, pms->velocity); | ||
| 248 | pms->in_use = 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | void init_sustain_timers(struct pcmidi_snd *pm) | ||
| 252 | { | ||
| 253 | struct pcmidi_sustain *pms; | ||
| 254 | unsigned i; | ||
| 255 | |||
| 256 | for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) { | ||
| 257 | pms = &pm->sustained_notes[i]; | ||
| 258 | pms->in_use = 0; | ||
| 259 | pms->pm = pm; | ||
| 260 | setup_timer(&pms->timer, pcmidi_sustained_note_release, | ||
| 261 | (unsigned long)pms); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | void stop_sustain_timers(struct pcmidi_snd *pm) | ||
| 266 | { | ||
| 267 | struct pcmidi_sustain *pms; | ||
| 268 | unsigned i; | ||
| 269 | |||
| 270 | for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) { | ||
| 271 | pms = &pm->sustained_notes[i]; | ||
| 272 | pms->in_use = 1; | ||
| 273 | del_timer_sync(&pms->timer); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | static int pcmidi_get_output_report(struct pcmidi_snd *pm) | ||
| 278 | { | ||
| 279 | struct hid_device *hdev = pm->pk->hdev; | ||
| 280 | struct hid_report *report; | ||
| 281 | |||
| 282 | list_for_each_entry(report, | ||
| 283 | &hdev->report_enum[HID_OUTPUT_REPORT].report_list, list) { | ||
| 284 | if (!(6 == report->id)) | ||
| 285 | continue; | ||
| 286 | |||
| 287 | if (report->maxfield < 1) { | ||
| 288 | dev_err(&hdev->dev, "output report is empty\n"); | ||
| 289 | break; | ||
| 290 | } | ||
| 291 | if (report->field[0]->report_count != 2) { | ||
| 292 | dev_err(&hdev->dev, "field count too low\n"); | ||
| 293 | break; | ||
| 294 | } | ||
| 295 | pm->pcmidi_report6 = report; | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | /* should never get here */ | ||
| 299 | return -ENODEV; | ||
| 300 | } | ||
| 301 | |||
| 302 | static void pcmidi_submit_output_report(struct pcmidi_snd *pm, int state) | ||
| 303 | { | ||
| 304 | struct hid_device *hdev = pm->pk->hdev; | ||
| 305 | struct hid_report *report = pm->pcmidi_report6; | ||
| 306 | report->field[0]->value[0] = 0x01; | ||
| 307 | report->field[0]->value[1] = state; | ||
| 308 | |||
| 309 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | ||
| 310 | } | ||
| 311 | |||
| 312 | static int pcmidi_handle_report1(struct pcmidi_snd *pm, u8 *data) | ||
| 313 | { | ||
| 314 | u32 bit_mask; | ||
| 315 | |||
| 316 | bit_mask = data[1]; | ||
| 317 | bit_mask = (bit_mask << 8) | data[2]; | ||
| 318 | bit_mask = (bit_mask << 8) | data[3]; | ||
| 319 | |||
| 320 | dbg_hid("pcmidi mode: %d\n", pm->midi_mode); | ||
| 321 | |||
| 322 | /*KEY_MAIL or octave down*/ | ||
| 323 | if (pm->midi_mode && bit_mask == 0x004000) { | ||
| 324 | /* octave down */ | ||
| 325 | pm->midi_octave--; | ||
| 326 | if (pm->midi_octave < -2) | ||
| 327 | pm->midi_octave = -2; | ||
| 328 | dbg_hid("pcmidi mode: %d octave: %d\n", | ||
| 329 | pm->midi_mode, pm->midi_octave); | ||
| 330 | return 1; | ||
| 331 | } | ||
| 332 | /*KEY_WWW or sustain*/ | ||
| 333 | else if (pm->midi_mode && bit_mask == 0x000004) { | ||
| 334 | /* sustain on/off*/ | ||
| 335 | pm->midi_sustain_mode ^= 0x1; | ||
| 336 | return 1; | ||
| 337 | } | ||
| 338 | |||
| 339 | return 0; /* continue key processing */ | ||
| 340 | } | ||
| 341 | |||
| 342 | static int pcmidi_handle_report3(struct pcmidi_snd *pm, u8 *data, int size) | ||
| 343 | { | ||
| 344 | struct pcmidi_sustain *pms; | ||
| 345 | unsigned i, j; | ||
| 346 | unsigned char status, note, velocity; | ||
| 347 | |||
| 348 | unsigned num_notes = (size-1)/2; | ||
| 349 | for (j = 0; j < num_notes; j++) { | ||
| 350 | note = data[j*2+1]; | ||
| 351 | velocity = data[j*2+2]; | ||
| 352 | |||
| 353 | if (note < 0x81) { /* note on */ | ||
| 354 | status = 128 + 16 + pm->midi_channel; /* 1001nnnn */ | ||
| 355 | note = note - 0x54 + PCMIDI_MIDDLE_C + | ||
| 356 | (pm->midi_octave * 12); | ||
| 357 | if (0 == velocity) | ||
| 358 | velocity = 1; /* force note on */ | ||
| 359 | } else { /* note off */ | ||
| 360 | status = 128 + pm->midi_channel; /* 1000nnnn */ | ||
| 361 | note = note - 0x94 + PCMIDI_MIDDLE_C + | ||
| 362 | (pm->midi_octave*12); | ||
| 363 | |||
| 364 | if (pm->midi_sustain_mode) { | ||
| 365 | for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) { | ||
| 366 | pms = &pm->sustained_notes[i]; | ||
| 367 | if (!pms->in_use) { | ||
| 368 | pms->status = status; | ||
| 369 | pms->note = note; | ||
| 370 | pms->velocity = velocity; | ||
| 371 | pms->in_use = 1; | ||
| 372 | |||
| 373 | mod_timer(&pms->timer, | ||
| 374 | jiffies + | ||
| 375 | msecs_to_jiffies(pm->midi_sustain)); | ||
| 376 | return 1; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | } | ||
| 381 | pcmidi_send_note(pm, status, note, velocity); | ||
| 382 | } | ||
| 383 | |||
| 384 | return 1; | ||
| 385 | } | ||
| 386 | |||
| 387 | static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data) | ||
| 388 | { | ||
| 389 | unsigned key; | ||
| 390 | u32 bit_mask; | ||
| 391 | u32 bit_index; | ||
| 392 | |||
| 393 | bit_mask = data[1]; | ||
| 394 | bit_mask = (bit_mask << 8) | data[2]; | ||
| 395 | bit_mask = (bit_mask << 8) | data[3]; | ||
| 396 | |||
| 397 | /* break keys */ | ||
| 398 | for (bit_index = 0; bit_index < 24; bit_index++) { | ||
| 399 | key = pm->last_key[bit_index]; | ||
| 400 | if (!((0x01 << bit_index) & bit_mask)) { | ||
| 401 | input_event(pm->input_ep82, EV_KEY, | ||
| 402 | pm->last_key[bit_index], 0); | ||
| 403 | pm->last_key[bit_index] = 0; | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | /* make keys */ | ||
| 408 | for (bit_index = 0; bit_index < 24; bit_index++) { | ||
| 409 | key = 0; | ||
| 410 | switch ((0x01 << bit_index) & bit_mask) { | ||
| 411 | case 0x000010: /* Fn lock*/ | ||
| 412 | pm->fn_state ^= 0x000010; | ||
| 413 | if (pm->fn_state) | ||
| 414 | pcmidi_submit_output_report(pm, 0xc5); | ||
| 415 | else | ||
| 416 | pcmidi_submit_output_report(pm, 0xc6); | ||
| 417 | continue; | ||
| 418 | case 0x020000: /* midi launcher..send a key (qwerty) or not? */ | ||
| 419 | pcmidi_submit_output_report(pm, 0xc1); | ||
| 420 | pm->midi_mode ^= 0x01; | ||
| 421 | |||
| 422 | dbg_hid("pcmidi mode: %d\n", pm->midi_mode); | ||
| 423 | continue; | ||
| 424 | case 0x100000: /* KEY_MESSENGER or octave up */ | ||
| 425 | dbg_hid("pcmidi mode: %d\n", pm->midi_mode); | ||
| 426 | if (pm->midi_mode) { | ||
| 427 | pm->midi_octave++; | ||
| 428 | if (pm->midi_octave > 2) | ||
| 429 | pm->midi_octave = 2; | ||
| 430 | dbg_hid("pcmidi mode: %d octave: %d\n", | ||
| 431 | pm->midi_mode, pm->midi_octave); | ||
| 432 | continue; | ||
| 433 | } else | ||
| 434 | key = KEY_MESSENGER; | ||
| 435 | break; | ||
| 436 | case 0x400000: | ||
| 437 | key = KEY_CALENDAR; | ||
| 438 | break; | ||
| 439 | case 0x080000: | ||
| 440 | key = KEY_ADDRESSBOOK; | ||
| 441 | break; | ||
| 442 | case 0x040000: | ||
| 443 | key = KEY_DOCUMENTS; | ||
| 444 | break; | ||
| 445 | case 0x800000: | ||
| 446 | key = KEY_WORDPROCESSOR; | ||
| 447 | break; | ||
| 448 | case 0x200000: | ||
| 449 | key = KEY_SPREADSHEET; | ||
| 450 | break; | ||
| 451 | case 0x010000: | ||
| 452 | key = KEY_COFFEE; | ||
| 453 | break; | ||
| 454 | case 0x000100: | ||
| 455 | key = KEY_HELP; | ||
| 456 | break; | ||
| 457 | case 0x000200: | ||
| 458 | key = KEY_SEND; | ||
| 459 | break; | ||
| 460 | case 0x000400: | ||
| 461 | key = KEY_REPLY; | ||
| 462 | break; | ||
| 463 | case 0x000800: | ||
| 464 | key = KEY_FORWARDMAIL; | ||
| 465 | break; | ||
| 466 | case 0x001000: | ||
| 467 | key = KEY_NEW; | ||
| 468 | break; | ||
| 469 | case 0x002000: | ||
| 470 | key = KEY_OPEN; | ||
| 471 | break; | ||
| 472 | case 0x004000: | ||
| 473 | key = KEY_CLOSE; | ||
| 474 | break; | ||
| 475 | case 0x008000: | ||
| 476 | key = KEY_SAVE; | ||
| 477 | break; | ||
| 478 | case 0x000001: | ||
| 479 | key = KEY_UNDO; | ||
| 480 | break; | ||
| 481 | case 0x000002: | ||
| 482 | key = KEY_REDO; | ||
| 483 | break; | ||
| 484 | case 0x000004: | ||
| 485 | key = KEY_SPELLCHECK; | ||
| 486 | break; | ||
| 487 | case 0x000008: | ||
| 488 | key = KEY_PRINT; | ||
| 489 | break; | ||
| 490 | } | ||
| 491 | if (key) { | ||
| 492 | input_event(pm->input_ep82, EV_KEY, key, 1); | ||
| 493 | pm->last_key[bit_index] = key; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | return 1; | ||
| 498 | } | ||
| 499 | |||
| 500 | int pcmidi_handle_report( | ||
| 501 | struct pcmidi_snd *pm, unsigned report_id, u8 *data, int size) | ||
| 502 | { | ||
| 503 | int ret = 0; | ||
| 504 | |||
| 505 | switch (report_id) { | ||
| 506 | case 0x01: /* midi keys (qwerty)*/ | ||
| 507 | ret = pcmidi_handle_report1(pm, data); | ||
| 508 | break; | ||
| 509 | case 0x03: /* midi keyboard (musical)*/ | ||
| 510 | ret = pcmidi_handle_report3(pm, data, size); | ||
| 511 | break; | ||
| 512 | case 0x04: /* multimedia/midi keys (qwerty)*/ | ||
| 513 | ret = pcmidi_handle_report4(pm, data); | ||
| 514 | break; | ||
| 515 | } | ||
| 516 | return ret; | ||
| 517 | } | ||
| 518 | |||
| 519 | void pcmidi_setup_extra_keys(struct pcmidi_snd *pm, struct input_dev *input) | ||
| 520 | { | ||
| 521 | /* reassigned functionality for N/A keys | ||
| 522 | MY PICTURES => KEY_WORDPROCESSOR | ||
| 523 | MY MUSIC=> KEY_SPREADSHEET | ||
| 524 | */ | ||
| 525 | unsigned int keys[] = { | ||
| 526 | KEY_FN, | ||
| 527 | KEY_MESSENGER, KEY_CALENDAR, | ||
| 528 | KEY_ADDRESSBOOK, KEY_DOCUMENTS, | ||
| 529 | KEY_WORDPROCESSOR, | ||
| 530 | KEY_SPREADSHEET, | ||
| 531 | KEY_COFFEE, | ||
| 532 | KEY_HELP, KEY_SEND, | ||
| 533 | KEY_REPLY, KEY_FORWARDMAIL, | ||
| 534 | KEY_NEW, KEY_OPEN, | ||
| 535 | KEY_CLOSE, KEY_SAVE, | ||
| 536 | KEY_UNDO, KEY_REDO, | ||
| 537 | KEY_SPELLCHECK, KEY_PRINT, | ||
| 538 | 0 | ||
| 539 | }; | ||
| 540 | |||
| 541 | unsigned int *pkeys = &keys[0]; | ||
| 542 | unsigned short i; | ||
| 543 | |||
| 544 | if (pm->ifnum != 1) /* only set up ONCE for interace 1 */ | ||
| 545 | return; | ||
| 546 | |||
| 547 | pm->input_ep82 = input; | ||
| 548 | |||
| 549 | for (i = 0; i < 24; i++) | ||
| 550 | pm->last_key[i] = 0; | ||
| 551 | |||
| 552 | while (*pkeys != 0) { | ||
| 553 | set_bit(*pkeys, pm->input_ep82->keybit); | ||
| 554 | ++pkeys; | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | static int pcmidi_set_operational(struct pcmidi_snd *pm) | ||
| 559 | { | ||
| 560 | if (pm->ifnum != 1) | ||
| 561 | return 0; /* only set up ONCE for interace 1 */ | ||
| 562 | |||
| 563 | pcmidi_get_output_report(pm); | ||
| 564 | pcmidi_submit_output_report(pm, 0xc1); | ||
| 565 | return 0; | ||
| 566 | } | ||
| 567 | |||
| 568 | static int pcmidi_snd_free(struct snd_device *dev) | ||
| 569 | { | ||
| 570 | return 0; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int pcmidi_in_open(struct snd_rawmidi_substream *substream) | ||
| 574 | { | ||
| 575 | struct pcmidi_snd *pm = substream->rmidi->private_data; | ||
| 576 | |||
| 577 | dbg_hid("pcmidi in open\n"); | ||
| 578 | pm->in_substream = substream; | ||
| 579 | return 0; | ||
| 580 | } | ||
| 581 | |||
| 582 | static int pcmidi_in_close(struct snd_rawmidi_substream *substream) | ||
| 583 | { | ||
| 584 | dbg_hid("pcmidi in close\n"); | ||
| 585 | return 0; | ||
| 586 | } | ||
| 587 | |||
| 588 | static void pcmidi_in_trigger(struct snd_rawmidi_substream *substream, int up) | ||
| 589 | { | ||
| 590 | struct pcmidi_snd *pm = substream->rmidi->private_data; | ||
| 591 | |||
| 592 | dbg_hid("pcmidi in trigger %d\n", up); | ||
| 593 | |||
| 594 | pm->in_triggered = up; | ||
| 595 | } | ||
| 596 | |||
| 597 | static struct snd_rawmidi_ops pcmidi_in_ops = { | ||
| 598 | .open = pcmidi_in_open, | ||
| 599 | .close = pcmidi_in_close, | ||
| 600 | .trigger = pcmidi_in_trigger | ||
| 601 | }; | ||
| 602 | |||
| 603 | int pcmidi_snd_initialise(struct pcmidi_snd *pm) | ||
| 604 | { | ||
| 605 | static int dev; | ||
| 606 | struct snd_card *card; | ||
| 607 | struct snd_rawmidi *rwmidi; | ||
| 608 | int err; | ||
| 609 | |||
| 610 | static struct snd_device_ops ops = { | ||
| 611 | .dev_free = pcmidi_snd_free, | ||
| 612 | }; | ||
| 613 | |||
| 614 | if (pm->ifnum != 1) | ||
| 615 | return 0; /* only set up midi device ONCE for interace 1 */ | ||
| 616 | |||
| 617 | if (dev >= SNDRV_CARDS) | ||
| 618 | return -ENODEV; | ||
| 619 | |||
| 620 | if (!enable[dev]) { | ||
| 621 | dev++; | ||
| 622 | return -ENOENT; | ||
| 623 | } | ||
| 624 | |||
| 625 | /* Setup sound card */ | ||
| 626 | |||
| 627 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); | ||
| 628 | if (err < 0) { | ||
| 629 | pk_error("failed to create pc-midi sound card\n"); | ||
| 630 | err = -ENOMEM; | ||
| 631 | goto fail; | ||
| 632 | } | ||
| 633 | pm->card = card; | ||
| 634 | |||
| 635 | /* Setup sound device */ | ||
| 636 | err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pm, &ops); | ||
| 637 | if (err < 0) { | ||
| 638 | pk_error("failed to create pc-midi sound device: error %d\n", | ||
| 639 | err); | ||
| 640 | goto fail; | ||
| 641 | } | ||
| 642 | |||
| 643 | strncpy(card->driver, shortname, sizeof(card->driver)); | ||
| 644 | strncpy(card->shortname, shortname, sizeof(card->shortname)); | ||
| 645 | strncpy(card->longname, longname, sizeof(card->longname)); | ||
| 646 | |||
| 647 | /* Set up rawmidi */ | ||
| 648 | err = snd_rawmidi_new(card, card->shortname, 0, | ||
| 649 | 0, 1, &rwmidi); | ||
| 650 | if (err < 0) { | ||
| 651 | pk_error("failed to create pc-midi rawmidi device: error %d\n", | ||
| 652 | err); | ||
| 653 | goto fail; | ||
| 654 | } | ||
| 655 | pm->rwmidi = rwmidi; | ||
| 656 | strncpy(rwmidi->name, card->shortname, sizeof(rwmidi->name)); | ||
| 657 | rwmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT; | ||
| 658 | rwmidi->private_data = pm; | ||
| 659 | |||
| 660 | snd_rawmidi_set_ops(rwmidi, SNDRV_RAWMIDI_STREAM_INPUT, | ||
| 661 | &pcmidi_in_ops); | ||
| 662 | |||
| 663 | snd_card_set_dev(card, &pm->pk->hdev->dev); | ||
| 664 | |||
| 665 | /* create sysfs variables */ | ||
| 666 | err = device_create_file(&pm->pk->hdev->dev, | ||
| 667 | sysfs_device_attr_channel); | ||
| 668 | if (err < 0) { | ||
| 669 | pk_error("failed to create sysfs attribute channel: error %d\n", | ||
| 670 | err); | ||
| 671 | goto fail; | ||
| 672 | } | ||
| 673 | |||
| 674 | err = device_create_file(&pm->pk->hdev->dev, | ||
| 675 | sysfs_device_attr_sustain); | ||
| 676 | if (err < 0) { | ||
| 677 | pk_error("failed to create sysfs attribute sustain: error %d\n", | ||
| 678 | err); | ||
| 679 | goto fail_attr_sustain; | ||
| 680 | } | ||
| 681 | |||
| 682 | err = device_create_file(&pm->pk->hdev->dev, | ||
| 683 | sysfs_device_attr_octave); | ||
| 684 | if (err < 0) { | ||
| 685 | pk_error("failed to create sysfs attribute octave: error %d\n", | ||
| 686 | err); | ||
| 687 | goto fail_attr_octave; | ||
| 688 | } | ||
| 689 | |||
| 690 | spin_lock_init(&pm->rawmidi_in_lock); | ||
| 691 | |||
| 692 | init_sustain_timers(pm); | ||
| 693 | pcmidi_set_operational(pm); | ||
| 694 | |||
| 695 | /* register it */ | ||
| 696 | err = snd_card_register(card); | ||
| 697 | if (err < 0) { | ||
| 698 | pk_error("failed to register pc-midi sound card: error %d\n", | ||
| 699 | err); | ||
| 700 | goto fail_register; | ||
| 701 | } | ||
| 702 | |||
| 703 | dbg_hid("pcmidi_snd_initialise finished ok\n"); | ||
| 704 | return 0; | ||
| 705 | |||
| 706 | fail_register: | ||
| 707 | stop_sustain_timers(pm); | ||
| 708 | device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_octave); | ||
| 709 | fail_attr_octave: | ||
| 710 | device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_sustain); | ||
| 711 | fail_attr_sustain: | ||
| 712 | device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_channel); | ||
| 713 | fail: | ||
| 714 | if (pm->card) { | ||
| 715 | snd_card_free(pm->card); | ||
| 716 | pm->card = NULL; | ||
| 717 | } | ||
| 718 | return err; | ||
| 719 | } | ||
| 720 | |||
| 721 | int pcmidi_snd_terminate(struct pcmidi_snd *pm) | ||
| 722 | { | ||
| 723 | if (pm->card) { | ||
| 724 | stop_sustain_timers(pm); | ||
| 725 | |||
| 726 | device_remove_file(&pm->pk->hdev->dev, | ||
| 727 | sysfs_device_attr_channel); | ||
| 728 | device_remove_file(&pm->pk->hdev->dev, | ||
| 729 | sysfs_device_attr_sustain); | ||
| 730 | device_remove_file(&pm->pk->hdev->dev, | ||
| 731 | sysfs_device_attr_octave); | ||
| 732 | |||
| 733 | snd_card_disconnect(pm->card); | ||
| 734 | snd_card_free_when_closed(pm->card); | ||
| 735 | } | ||
| 736 | |||
| 737 | return 0; | ||
| 738 | } | ||
| 739 | |||
| 740 | /* | ||
| 741 | * PC-MIDI report descriptor for report id is wrong. | ||
| 742 | */ | ||
| 743 | static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc, | ||
| 744 | unsigned int rsize) | ||
| 745 | { | ||
| 746 | if (rsize == 178 && | ||
| 747 | rdesc[111] == 0x06 && rdesc[112] == 0x00 && | ||
| 748 | rdesc[113] == 0xff) { | ||
| 749 | dev_info(&hdev->dev, "fixing up pc-midi keyboard report " | ||
| 750 | "descriptor\n"); | ||
| 751 | |||
| 752 | rdesc[144] = 0x18; /* report 4: was 0x10 report count */ | ||
| 753 | } | ||
| 754 | } | ||
| 755 | |||
| 756 | static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi, | ||
| 757 | struct hid_field *field, struct hid_usage *usage, | ||
| 758 | unsigned long **bit, int *max) | ||
| 759 | { | ||
| 760 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 761 | struct pcmidi_snd *pm; | ||
| 762 | |||
| 763 | pm = pk->pm; | ||
| 764 | |||
| 765 | if (HID_UP_MSVENDOR == (usage->hid & HID_USAGE_PAGE) && | ||
| 766 | 1 == pm->ifnum) { | ||
| 767 | pcmidi_setup_extra_keys(pm, hi->input); | ||
| 768 | return 0; | ||
| 769 | } | ||
| 770 | |||
| 771 | return 0; | ||
| 772 | } | ||
| 773 | |||
| 774 | |||
| 775 | static int pk_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
| 776 | u8 *data, int size) | ||
| 777 | { | ||
| 778 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 779 | int ret = 0; | ||
| 780 | |||
| 781 | if (1 == pk->pm->ifnum) { | ||
| 782 | if (report->id == data[0]) | ||
| 783 | switch (report->id) { | ||
| 784 | case 0x01: /* midi keys (qwerty)*/ | ||
| 785 | case 0x03: /* midi keyboard (musical)*/ | ||
| 786 | case 0x04: /* extra/midi keys (qwerty)*/ | ||
| 787 | ret = pcmidi_handle_report(pk->pm, | ||
| 788 | report->id, data, size); | ||
| 789 | break; | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | return ret; | ||
| 794 | } | ||
| 795 | |||
| 796 | static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
| 797 | { | ||
| 798 | int ret; | ||
| 799 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
| 800 | unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber; | ||
| 801 | unsigned long quirks = id->driver_data; | ||
| 802 | struct pk_device *pk; | ||
| 803 | struct pcmidi_snd *pm = NULL; | ||
| 804 | |||
| 805 | pk = kzalloc(sizeof(*pk), GFP_KERNEL); | ||
| 806 | if (pk == NULL) { | ||
| 807 | dev_err(&hdev->dev, "prodikeys: can't alloc descriptor\n"); | ||
| 808 | return -ENOMEM; | ||
| 809 | } | ||
| 810 | |||
| 811 | pk->hdev = hdev; | ||
| 812 | |||
| 813 | pm = kzalloc(sizeof(*pm), GFP_KERNEL); | ||
| 814 | if (pm == NULL) { | ||
| 815 | dev_err(&hdev->dev, | ||
| 816 | "prodikeys: can't alloc descriptor\n"); | ||
| 817 | ret = -ENOMEM; | ||
| 818 | goto err_free; | ||
| 819 | } | ||
| 820 | |||
| 821 | pm->pk = pk; | ||
| 822 | pk->pm = pm; | ||
| 823 | pm->ifnum = ifnum; | ||
| 824 | |||
| 825 | hid_set_drvdata(hdev, pk); | ||
| 826 | |||
| 827 | ret = hid_parse(hdev); | ||
| 828 | if (ret) { | ||
| 829 | dev_err(&hdev->dev, "prodikeys: hid parse failed\n"); | ||
| 830 | goto err_free; | ||
| 831 | } | ||
| 832 | |||
| 833 | if (quirks & PK_QUIRK_NOGET) { /* hid_parse cleared all the quirks */ | ||
| 834 | hdev->quirks |= HID_QUIRK_NOGET; | ||
| 835 | } | ||
| 836 | |||
| 837 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
| 838 | if (ret) { | ||
| 839 | dev_err(&hdev->dev, "prodikeys: hw start failed\n"); | ||
| 840 | goto err_free; | ||
| 841 | } | ||
| 842 | |||
| 843 | ret = pcmidi_snd_initialise(pm); | ||
| 844 | if (ret < 0) | ||
| 845 | goto err_stop; | ||
| 846 | |||
| 847 | return 0; | ||
| 848 | err_stop: | ||
| 849 | hid_hw_stop(hdev); | ||
| 850 | err_free: | ||
| 851 | if (pm != NULL) | ||
| 852 | kfree(pm); | ||
| 853 | |||
| 854 | kfree(pk); | ||
| 855 | return ret; | ||
| 856 | } | ||
| 857 | |||
| 858 | static void pk_remove(struct hid_device *hdev) | ||
| 859 | { | ||
| 860 | struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev); | ||
| 861 | struct pcmidi_snd *pm; | ||
| 862 | |||
| 863 | pm = pk->pm; | ||
| 864 | if (pm) { | ||
| 865 | pcmidi_snd_terminate(pm); | ||
| 866 | kfree(pm); | ||
| 867 | } | ||
| 868 | |||
| 869 | hid_hw_stop(hdev); | ||
| 870 | |||
| 871 | kfree(pk); | ||
| 872 | } | ||
| 873 | |||
| 874 | static const struct hid_device_id pk_devices[] = { | ||
| 875 | {HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, | ||
| 876 | USB_DEVICE_ID_PRODIKEYS_PCMIDI), | ||
| 877 | .driver_data = PK_QUIRK_NOGET}, | ||
| 878 | { } | ||
| 879 | }; | ||
| 880 | MODULE_DEVICE_TABLE(hid, pk_devices); | ||
| 881 | |||
| 882 | static struct hid_driver pk_driver = { | ||
| 883 | .name = "prodikeys", | ||
| 884 | .id_table = pk_devices, | ||
| 885 | .report_fixup = pk_report_fixup, | ||
| 886 | .input_mapping = pk_input_mapping, | ||
| 887 | .raw_event = pk_raw_event, | ||
| 888 | .probe = pk_probe, | ||
| 889 | .remove = pk_remove, | ||
| 890 | }; | ||
| 891 | |||
| 892 | static int pk_init(void) | ||
| 893 | { | ||
| 894 | int ret; | ||
| 895 | |||
| 896 | ret = hid_register_driver(&pk_driver); | ||
| 897 | if (ret) | ||
| 898 | printk(KERN_ERR "can't register prodikeys driver\n"); | ||
| 899 | |||
| 900 | return ret; | ||
| 901 | } | ||
| 902 | |||
| 903 | static void pk_exit(void) | ||
| 904 | { | ||
| 905 | hid_unregister_driver(&pk_driver); | ||
| 906 | } | ||
| 907 | |||
| 908 | module_init(pk_init); | ||
| 909 | module_exit(pk_exit); | ||
| 910 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c new file mode 100644 index 000000000000..66e694054ba2 --- /dev/null +++ b/drivers/hid/hid-roccat-kone.c | |||
| @@ -0,0 +1,994 @@ | |||
| 1 | /* | ||
| 2 | * Roccat Kone driver for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 5 | */ | ||
| 6 | |||
| 7 | /* | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License as published by the Free | ||
| 10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 11 | * any later version. | ||
| 12 | */ | ||
| 13 | |||
| 14 | /* | ||
| 15 | * Roccat Kone is a gamer mouse which consists of a mouse part and a keyboard | ||
| 16 | * part. The keyboard part enables the mouse to execute stored macros with mixed | ||
| 17 | * key- and button-events. | ||
| 18 | * | ||
| 19 | * TODO implement on-the-fly polling-rate change | ||
| 20 | * The windows driver has the ability to change the polling rate of the | ||
| 21 | * device on the press of a mousebutton. | ||
| 22 | * Is it possible to remove and reinstall the urb in raw-event- or any | ||
| 23 | * other handler, or to defer this action to be executed somewhere else? | ||
| 24 | * | ||
| 25 | * TODO implement notification mechanism for overlong macro execution | ||
| 26 | * If user wants to execute an overlong macro only the names of macroset | ||
| 27 | * and macro are given. Should userland tap hidraw or is there an | ||
| 28 | * additional streaming mechanism? | ||
| 29 | * | ||
| 30 | * TODO is it possible to overwrite group for sysfs attributes via udev? | ||
| 31 | */ | ||
| 32 | |||
| 33 | #include <linux/device.h> | ||
| 34 | #include <linux/input.h> | ||
| 35 | #include <linux/hid.h> | ||
| 36 | #include <linux/usb.h> | ||
| 37 | #include <linux/module.h> | ||
| 38 | #include <linux/slab.h> | ||
| 39 | #include "hid-ids.h" | ||
| 40 | #include "hid-roccat-kone.h" | ||
| 41 | |||
| 42 | static void kone_set_settings_checksum(struct kone_settings *settings) | ||
| 43 | { | ||
| 44 | uint16_t checksum = 0; | ||
| 45 | unsigned char *address = (unsigned char *)settings; | ||
| 46 | int i; | ||
| 47 | |||
| 48 | for (i = 0; i < sizeof(struct kone_settings) - 2; ++i, ++address) | ||
| 49 | checksum += *address; | ||
| 50 | settings->checksum = cpu_to_le16(checksum); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* | ||
| 54 | * Checks success after writing data to mouse | ||
| 55 | * On success returns 0 | ||
| 56 | * On failure returns errno | ||
| 57 | */ | ||
| 58 | static int kone_check_write(struct usb_device *usb_dev) | ||
| 59 | { | ||
| 60 | int len; | ||
| 61 | unsigned char *data; | ||
| 62 | |||
| 63 | data = kmalloc(1, GFP_KERNEL); | ||
| 64 | if (!data) | ||
| 65 | return -ENOMEM; | ||
| 66 | |||
| 67 | do { | ||
| 68 | /* | ||
| 69 | * Mouse needs 50 msecs until it says ok, but there are | ||
| 70 | * 30 more msecs needed for next write to work. | ||
| 71 | */ | ||
| 72 | msleep(80); | ||
| 73 | |||
| 74 | len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
| 75 | USB_REQ_CLEAR_FEATURE, | ||
| 76 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | | ||
| 77 | USB_DIR_IN, | ||
| 78 | kone_command_confirm_write, 0, data, 1, | ||
| 79 | USB_CTRL_SET_TIMEOUT); | ||
| 80 | |||
| 81 | if (len != 1) { | ||
| 82 | kfree(data); | ||
| 83 | return -EIO; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* | ||
| 87 | * value of 3 seems to mean something like | ||
| 88 | * "not finished yet, but it looks good" | ||
| 89 | * So check again after a moment. | ||
| 90 | */ | ||
| 91 | } while (*data == 3); | ||
| 92 | |||
| 93 | if (*data == 1) { /* everything alright */ | ||
| 94 | kfree(data); | ||
| 95 | return 0; | ||
| 96 | } else { /* unknown answer */ | ||
| 97 | dev_err(&usb_dev->dev, "got retval %d when checking write\n", | ||
| 98 | *data); | ||
| 99 | kfree(data); | ||
| 100 | return -EIO; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | /* | ||
| 105 | * Reads settings from mouse and stores it in @buf | ||
| 106 | * @buf has to be alloced with GFP_KERNEL | ||
| 107 | * On success returns 0 | ||
| 108 | * On failure returns errno | ||
| 109 | */ | ||
| 110 | static int kone_get_settings(struct usb_device *usb_dev, | ||
| 111 | struct kone_settings *buf) | ||
| 112 | { | ||
| 113 | int len; | ||
| 114 | |||
| 115 | len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
| 116 | USB_REQ_CLEAR_FEATURE, | ||
| 117 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
| 118 | kone_command_settings, 0, buf, | ||
| 119 | sizeof(struct kone_settings), USB_CTRL_SET_TIMEOUT); | ||
| 120 | |||
| 121 | if (len != sizeof(struct kone_settings)) | ||
| 122 | return -EIO; | ||
| 123 | |||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | /* | ||
| 128 | * Writes settings from @buf to mouse | ||
| 129 | * On success returns 0 | ||
| 130 | * On failure returns errno | ||
| 131 | */ | ||
| 132 | static int kone_set_settings(struct usb_device *usb_dev, | ||
| 133 | struct kone_settings const *settings) | ||
| 134 | { | ||
| 135 | int len; | ||
| 136 | |||
| 137 | len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | ||
| 138 | USB_REQ_SET_CONFIGURATION, | ||
| 139 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | ||
| 140 | kone_command_settings, 0, (char *)settings, | ||
| 141 | sizeof(struct kone_settings), | ||
| 142 | USB_CTRL_SET_TIMEOUT); | ||
| 143 | |||
| 144 | if (len != sizeof(struct kone_settings)) | ||
| 145 | return -EIO; | ||
| 146 | |||
| 147 | if (kone_check_write(usb_dev)) | ||
| 148 | return -EIO; | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* | ||
| 154 | * Reads profile data from mouse and stores it in @buf | ||
| 155 | * @number: profile number to read | ||
| 156 | * On success returns 0 | ||
| 157 | * On failure returns errno | ||
| 158 | */ | ||
| 159 | static int kone_get_profile(struct usb_device *usb_dev, | ||
| 160 | struct kone_profile *buf, int number) | ||
| 161 | { | ||
| 162 | int len; | ||
| 163 | |||
| 164 | if (number < 1 || number > 5) | ||
| 165 | return -EINVAL; | ||
| 166 | |||
| 167 | len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
| 168 | USB_REQ_CLEAR_FEATURE, | ||
| 169 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
| 170 | kone_command_profile, number, buf, | ||
| 171 | sizeof(struct kone_profile), USB_CTRL_SET_TIMEOUT); | ||
| 172 | |||
| 173 | if (len != sizeof(struct kone_profile)) | ||
| 174 | return -EIO; | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* | ||
| 180 | * Writes profile data to mouse. | ||
| 181 | * @number: profile number to write | ||
| 182 | * On success returns 0 | ||
| 183 | * On failure returns errno | ||
| 184 | */ | ||
| 185 | static int kone_set_profile(struct usb_device *usb_dev, | ||
| 186 | struct kone_profile const *profile, int number) | ||
| 187 | { | ||
| 188 | int len; | ||
| 189 | |||
| 190 | if (number < 1 || number > 5) | ||
| 191 | return -EINVAL; | ||
| 192 | |||
| 193 | len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | ||
| 194 | USB_REQ_SET_CONFIGURATION, | ||
| 195 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | ||
| 196 | kone_command_profile, number, (char *)profile, | ||
| 197 | sizeof(struct kone_profile), | ||
| 198 | USB_CTRL_SET_TIMEOUT); | ||
| 199 | |||
| 200 | if (len != sizeof(struct kone_profile)) | ||
| 201 | return len; | ||
| 202 | |||
| 203 | if (kone_check_write(usb_dev)) | ||
| 204 | return -EIO; | ||
| 205 | |||
| 206 | return 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | /* | ||
| 210 | * Reads value of "fast-clip-weight" and stores it in @result | ||
| 211 | * On success returns 0 | ||
| 212 | * On failure returns errno | ||
| 213 | */ | ||
| 214 | static int kone_get_weight(struct usb_device *usb_dev, int *result) | ||
| 215 | { | ||
| 216 | int len; | ||
| 217 | uint8_t *data; | ||
| 218 | |||
| 219 | data = kmalloc(1, GFP_KERNEL); | ||
| 220 | if (!data) | ||
| 221 | return -ENOMEM; | ||
| 222 | |||
| 223 | len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
| 224 | USB_REQ_CLEAR_FEATURE, | ||
| 225 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
| 226 | kone_command_weight, 0, data, 1, USB_CTRL_SET_TIMEOUT); | ||
| 227 | |||
| 228 | if (len != 1) { | ||
| 229 | kfree(data); | ||
| 230 | return -EIO; | ||
| 231 | } | ||
| 232 | *result = (int)*data; | ||
| 233 | kfree(data); | ||
| 234 | return 0; | ||
| 235 | } | ||
| 236 | |||
| 237 | /* | ||
| 238 | * Reads firmware_version of mouse and stores it in @result | ||
| 239 | * On success returns 0 | ||
| 240 | * On failure returns errno | ||
| 241 | */ | ||
| 242 | static int kone_get_firmware_version(struct usb_device *usb_dev, int *result) | ||
| 243 | { | ||
| 244 | int len; | ||
| 245 | unsigned char *data; | ||
| 246 | |||
| 247 | data = kmalloc(2, GFP_KERNEL); | ||
| 248 | if (!data) | ||
| 249 | return -ENOMEM; | ||
| 250 | |||
| 251 | len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
| 252 | USB_REQ_CLEAR_FEATURE, | ||
| 253 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
| 254 | kone_command_firmware_version, 0, data, 2, | ||
| 255 | USB_CTRL_SET_TIMEOUT); | ||
| 256 | |||
| 257 | if (len != 2) { | ||
| 258 | kfree(data); | ||
| 259 | return -EIO; | ||
| 260 | } | ||
| 261 | *result = le16_to_cpu(*data); | ||
| 262 | kfree(data); | ||
| 263 | return 0; | ||
| 264 | } | ||
| 265 | |||
| 266 | static ssize_t kone_sysfs_read_settings(struct kobject *kobj, | ||
| 267 | struct bin_attribute *attr, char *buf, | ||
| 268 | loff_t off, size_t count) { | ||
| 269 | struct device *dev = container_of(kobj, struct device, kobj); | ||
| 270 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 271 | |||
| 272 | if (off >= sizeof(struct kone_settings)) | ||
| 273 | return 0; | ||
| 274 | |||
| 275 | if (off + count > sizeof(struct kone_settings)) | ||
| 276 | count = sizeof(struct kone_settings) - off; | ||
| 277 | |||
| 278 | mutex_lock(&kone->kone_lock); | ||
| 279 | memcpy(buf, &kone->settings + off, count); | ||
| 280 | mutex_unlock(&kone->kone_lock); | ||
| 281 | |||
| 282 | return count; | ||
| 283 | } | ||
| 284 | |||
| 285 | /* | ||
| 286 | * Writing settings automatically activates startup_profile. | ||
| 287 | * This function keeps values in kone_device up to date and assumes that in | ||
| 288 | * case of error the old data is still valid | ||
| 289 | */ | ||
| 290 | static ssize_t kone_sysfs_write_settings(struct kobject *kobj, | ||
| 291 | struct bin_attribute *attr, char *buf, | ||
| 292 | loff_t off, size_t count) { | ||
| 293 | struct device *dev = container_of(kobj, struct device, kobj); | ||
| 294 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 295 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | ||
| 296 | int retval = 0, difference; | ||
| 297 | |||
| 298 | /* I need to get my data in one piece */ | ||
| 299 | if (off != 0 || count != sizeof(struct kone_settings)) | ||
| 300 | return -EINVAL; | ||
| 301 | |||
| 302 | mutex_lock(&kone->kone_lock); | ||
| 303 | difference = memcmp(buf, &kone->settings, sizeof(struct kone_settings)); | ||
| 304 | if (difference) { | ||
| 305 | retval = kone_set_settings(usb_dev, | ||
| 306 | (struct kone_settings const *)buf); | ||
| 307 | if (!retval) | ||
| 308 | memcpy(&kone->settings, buf, | ||
| 309 | sizeof(struct kone_settings)); | ||
| 310 | } | ||
| 311 | mutex_unlock(&kone->kone_lock); | ||
| 312 | |||
| 313 | if (retval) | ||
| 314 | return retval; | ||
| 315 | |||
| 316 | /* | ||
| 317 | * If we get here, treat settings as okay and update actual values | ||
| 318 | * according to startup_profile | ||
| 319 | */ | ||
| 320 | kone->actual_profile = kone->settings.startup_profile; | ||
| 321 | kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi; | ||
| 322 | |||
| 323 | return sizeof(struct kone_settings); | ||
| 324 | } | ||
| 325 | |||
| 326 | static ssize_t kone_sysfs_read_profilex(struct kobject *kobj, | ||
| 327 | struct bin_attribute *attr, char *buf, | ||
| 328 | loff_t off, size_t count, int number) { | ||
| 329 | struct device *dev = container_of(kobj, struct device, kobj); | ||
| 330 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 331 | |||
| 332 | if (off >= sizeof(struct kone_profile)) | ||
| 333 | return 0; | ||
| 334 | |||
| 335 | if (off + count > sizeof(struct kone_profile)) | ||
| 336 | count = sizeof(struct kone_profile) - off; | ||
| 337 | |||
| 338 | mutex_lock(&kone->kone_lock); | ||
| 339 | memcpy(buf, &kone->profiles[number - 1], sizeof(struct kone_profile)); | ||
| 340 | mutex_unlock(&kone->kone_lock); | ||
| 341 | |||
| 342 | return count; | ||
| 343 | } | ||
| 344 | |||
| 345 | static ssize_t kone_sysfs_read_profile1(struct kobject *kobj, | ||
| 346 | struct bin_attribute *attr, char *buf, | ||
| 347 | loff_t off, size_t count) { | ||
| 348 | return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 1); | ||
| 349 | } | ||
| 350 | |||
| 351 | static ssize_t kone_sysfs_read_profile2(struct kobject *kobj, | ||
| 352 | struct bin_attribute *attr, char *buf, | ||
| 353 | loff_t off, size_t count) { | ||
| 354 | return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 2); | ||
| 355 | } | ||
| 356 | |||
| 357 | static ssize_t kone_sysfs_read_profile3(struct kobject *kobj, | ||
| 358 | struct bin_attribute *attr, char *buf, | ||
| 359 | loff_t off, size_t count) { | ||
| 360 | return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 3); | ||
| 361 | } | ||
| 362 | |||
| 363 | static ssize_t kone_sysfs_read_profile4(struct kobject *kobj, | ||
| 364 | struct bin_attribute *attr, char *buf, | ||
| 365 | loff_t off, size_t count) { | ||
| 366 | return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 4); | ||
| 367 | } | ||
| 368 | |||
| 369 | static ssize_t kone_sysfs_read_profile5(struct kobject *kobj, | ||
| 370 | struct bin_attribute *attr, char *buf, | ||
| 371 | loff_t off, size_t count) { | ||
| 372 | return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 5); | ||
| 373 | } | ||
| 374 | |||
| 375 | /* Writes data only if different to stored data */ | ||
| 376 | static ssize_t kone_sysfs_write_profilex(struct kobject *kobj, | ||
| 377 | struct bin_attribute *attr, char *buf, | ||
| 378 | loff_t off, size_t count, int number) { | ||
| 379 | struct device *dev = container_of(kobj, struct device, kobj); | ||
| 380 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 381 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | ||
| 382 | struct kone_profile *profile; | ||
| 383 | int retval = 0, difference; | ||
| 384 | |||
| 385 | /* I need to get my data in one piece */ | ||
| 386 | if (off != 0 || count != sizeof(struct kone_profile)) | ||
| 387 | return -EINVAL; | ||
| 388 | |||
| 389 | profile = &kone->profiles[number - 1]; | ||
| 390 | |||
| 391 | mutex_lock(&kone->kone_lock); | ||
| 392 | difference = memcmp(buf, profile, sizeof(struct kone_profile)); | ||
| 393 | if (difference) { | ||
| 394 | retval = kone_set_profile(usb_dev, | ||
| 395 | (struct kone_profile const *)buf, number); | ||
| 396 | if (!retval) | ||
| 397 | memcpy(profile, buf, sizeof(struct kone_profile)); | ||
| 398 | } | ||
| 399 | mutex_unlock(&kone->kone_lock); | ||
| 400 | |||
| 401 | if (retval) | ||
| 402 | return retval; | ||
| 403 | |||
| 404 | return sizeof(struct kone_profile); | ||
| 405 | } | ||
| 406 | |||
| 407 | static ssize_t kone_sysfs_write_profile1(struct kobject *kobj, | ||
| 408 | struct bin_attribute *attr, char *buf, | ||
| 409 | loff_t off, size_t count) { | ||
| 410 | return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 1); | ||
| 411 | } | ||
| 412 | |||
| 413 | static ssize_t kone_sysfs_write_profile2(struct kobject *kobj, | ||
| 414 | struct bin_attribute *attr, char *buf, | ||
| 415 | loff_t off, size_t count) { | ||
| 416 | return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 2); | ||
| 417 | } | ||
| 418 | |||
| 419 | static ssize_t kone_sysfs_write_profile3(struct kobject *kobj, | ||
| 420 | struct bin_attribute *attr, char *buf, | ||
| 421 | loff_t off, size_t count) { | ||
| 422 | return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 3); | ||
| 423 | } | ||
| 424 | |||
| 425 | static ssize_t kone_sysfs_write_profile4(struct kobject *kobj, | ||
| 426 | struct bin_attribute *attr, char *buf, | ||
| 427 | loff_t off, size_t count) { | ||
| 428 | return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 4); | ||
| 429 | } | ||
| 430 | |||
| 431 | static ssize_t kone_sysfs_write_profile5(struct kobject *kobj, | ||
| 432 | struct bin_attribute *attr, char *buf, | ||
| 433 | loff_t off, size_t count) { | ||
| 434 | return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 5); | ||
| 435 | } | ||
| 436 | |||
| 437 | static ssize_t kone_sysfs_show_actual_profile(struct device *dev, | ||
| 438 | struct device_attribute *attr, char *buf) | ||
| 439 | { | ||
| 440 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 441 | return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_profile); | ||
| 442 | } | ||
| 443 | |||
| 444 | static ssize_t kone_sysfs_show_actual_dpi(struct device *dev, | ||
| 445 | struct device_attribute *attr, char *buf) | ||
| 446 | { | ||
| 447 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 448 | return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_dpi); | ||
| 449 | } | ||
| 450 | |||
| 451 | /* weight is read each time, since we don't get informed when it's changed */ | ||
| 452 | static ssize_t kone_sysfs_show_weight(struct device *dev, | ||
| 453 | struct device_attribute *attr, char *buf) | ||
| 454 | { | ||
| 455 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 456 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | ||
| 457 | int weight = 0; | ||
| 458 | int retval; | ||
| 459 | |||
| 460 | mutex_lock(&kone->kone_lock); | ||
| 461 | retval = kone_get_weight(usb_dev, &weight); | ||
| 462 | mutex_unlock(&kone->kone_lock); | ||
| 463 | |||
| 464 | if (retval) | ||
| 465 | return retval; | ||
| 466 | return snprintf(buf, PAGE_SIZE, "%d\n", weight); | ||
| 467 | } | ||
| 468 | |||
| 469 | static ssize_t kone_sysfs_show_firmware_version(struct device *dev, | ||
| 470 | struct device_attribute *attr, char *buf) | ||
| 471 | { | ||
| 472 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 473 | return snprintf(buf, PAGE_SIZE, "%d\n", kone->firmware_version); | ||
| 474 | } | ||
| 475 | |||
| 476 | static ssize_t kone_sysfs_show_tcu(struct device *dev, | ||
| 477 | struct device_attribute *attr, char *buf) | ||
| 478 | { | ||
| 479 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 480 | return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.tcu); | ||
| 481 | } | ||
| 482 | |||
| 483 | static int kone_tcu_command(struct usb_device *usb_dev, int number) | ||
| 484 | { | ||
| 485 | int len; | ||
| 486 | char *value; | ||
| 487 | |||
| 488 | value = kmalloc(1, GFP_KERNEL); | ||
| 489 | if (!value) | ||
| 490 | return -ENOMEM; | ||
| 491 | |||
| 492 | *value = number; | ||
| 493 | |||
| 494 | len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | ||
| 495 | USB_REQ_SET_CONFIGURATION, | ||
| 496 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | ||
| 497 | kone_command_calibrate, 0, value, 1, | ||
| 498 | USB_CTRL_SET_TIMEOUT); | ||
| 499 | |||
| 500 | kfree(value); | ||
| 501 | return ((len != 1) ? -EIO : 0); | ||
| 502 | } | ||
| 503 | |||
| 504 | /* | ||
| 505 | * Calibrating the tcu is the only action that changes settings data inside the | ||
| 506 | * mouse, so this data needs to be reread | ||
| 507 | */ | ||
| 508 | static ssize_t kone_sysfs_set_tcu(struct device *dev, | ||
| 509 | struct device_attribute *attr, char const *buf, size_t size) | ||
| 510 | { | ||
| 511 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 512 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | ||
| 513 | int retval; | ||
| 514 | unsigned long state; | ||
| 515 | |||
| 516 | retval = strict_strtoul(buf, 10, &state); | ||
| 517 | if (retval) | ||
| 518 | return retval; | ||
| 519 | |||
| 520 | if (state != 0 && state != 1) | ||
| 521 | return -EINVAL; | ||
| 522 | |||
| 523 | mutex_lock(&kone->kone_lock); | ||
| 524 | |||
| 525 | if (state == 1) { /* state activate */ | ||
| 526 | retval = kone_tcu_command(usb_dev, 1); | ||
| 527 | if (retval) | ||
| 528 | goto exit_unlock; | ||
| 529 | retval = kone_tcu_command(usb_dev, 2); | ||
| 530 | if (retval) | ||
| 531 | goto exit_unlock; | ||
| 532 | ssleep(5); /* tcu needs this time for calibration */ | ||
| 533 | retval = kone_tcu_command(usb_dev, 3); | ||
| 534 | if (retval) | ||
| 535 | goto exit_unlock; | ||
| 536 | retval = kone_tcu_command(usb_dev, 0); | ||
| 537 | if (retval) | ||
| 538 | goto exit_unlock; | ||
| 539 | retval = kone_tcu_command(usb_dev, 4); | ||
| 540 | if (retval) | ||
| 541 | goto exit_unlock; | ||
| 542 | /* | ||
| 543 | * Kone needs this time to settle things. | ||
| 544 | * Reading settings too early will result in invalid data. | ||
| 545 | * Roccat's driver waits 1 sec, maybe this time could be | ||
| 546 | * shortened. | ||
| 547 | */ | ||
| 548 | ssleep(1); | ||
| 549 | } | ||
| 550 | |||
| 551 | /* calibration changes values in settings, so reread */ | ||
| 552 | retval = kone_get_settings(usb_dev, &kone->settings); | ||
| 553 | if (retval) | ||
| 554 | goto exit_no_settings; | ||
| 555 | |||
| 556 | /* only write settings back if activation state is different */ | ||
| 557 | if (kone->settings.tcu != state) { | ||
| 558 | kone->settings.tcu = state; | ||
| 559 | kone_set_settings_checksum(&kone->settings); | ||
| 560 | |||
| 561 | retval = kone_set_settings(usb_dev, &kone->settings); | ||
| 562 | if (retval) { | ||
| 563 | dev_err(&usb_dev->dev, "couldn't set tcu state\n"); | ||
| 564 | /* | ||
| 565 | * try to reread valid settings into buffer overwriting | ||
| 566 | * first error code | ||
| 567 | */ | ||
| 568 | retval = kone_get_settings(usb_dev, &kone->settings); | ||
| 569 | if (retval) | ||
| 570 | goto exit_no_settings; | ||
| 571 | goto exit_unlock; | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | retval = size; | ||
| 576 | exit_no_settings: | ||
| 577 | dev_err(&usb_dev->dev, "couldn't read settings\n"); | ||
| 578 | exit_unlock: | ||
| 579 | mutex_unlock(&kone->kone_lock); | ||
| 580 | return retval; | ||
| 581 | } | ||
| 582 | |||
| 583 | static ssize_t kone_sysfs_show_startup_profile(struct device *dev, | ||
| 584 | struct device_attribute *attr, char *buf) | ||
| 585 | { | ||
| 586 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 587 | return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.startup_profile); | ||
| 588 | } | ||
| 589 | |||
| 590 | static ssize_t kone_sysfs_set_startup_profile(struct device *dev, | ||
| 591 | struct device_attribute *attr, char const *buf, size_t size) | ||
| 592 | { | ||
| 593 | struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); | ||
| 594 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | ||
| 595 | int retval; | ||
| 596 | unsigned long new_startup_profile; | ||
| 597 | |||
| 598 | retval = strict_strtoul(buf, 10, &new_startup_profile); | ||
| 599 | if (retval) | ||
| 600 | return retval; | ||
| 601 | |||
| 602 | if (new_startup_profile < 1 || new_startup_profile > 5) | ||
| 603 | return -EINVAL; | ||
| 604 | |||
| 605 | mutex_lock(&kone->kone_lock); | ||
| 606 | |||
| 607 | kone->settings.startup_profile = new_startup_profile; | ||
| 608 | kone_set_settings_checksum(&kone->settings); | ||
| 609 | |||
| 610 | retval = kone_set_settings(usb_dev, &kone->settings); | ||
| 611 | |||
| 612 | mutex_unlock(&kone->kone_lock); | ||
| 613 | |||
| 614 | if (retval) | ||
| 615 | return retval; | ||
| 616 | |||
| 617 | /* changing the startup profile immediately activates this profile */ | ||
| 618 | kone->actual_profile = new_startup_profile; | ||
| 619 | kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi; | ||
| 620 | |||
| 621 | return size; | ||
| 622 | } | ||
| 623 | |||
| 624 | /* | ||
| 625 | * This file is used by userland software to find devices that are handled by | ||
| 626 | * this driver. This provides a consistent way for actual and older kernels | ||
| 627 | * where this driver replaced usbhid instead of generic-usb. | ||
| 628 | * Driver capabilities are determined by version number. | ||
| 629 | */ | ||
| 630 | static ssize_t kone_sysfs_show_driver_version(struct device *dev, | ||
| 631 | struct device_attribute *attr, char *buf) | ||
| 632 | { | ||
| 633 | return snprintf(buf, PAGE_SIZE, ROCCAT_KONE_DRIVER_VERSION "\n"); | ||
| 634 | } | ||
| 635 | |||
| 636 | /* | ||
| 637 | * Read actual dpi settings. | ||
| 638 | * Returns raw value for further processing. Refer to enum kone_polling_rates to | ||
| 639 | * get real value. | ||
| 640 | */ | ||
| 641 | static DEVICE_ATTR(actual_dpi, 0440, kone_sysfs_show_actual_dpi, NULL); | ||
| 642 | |||
| 643 | static DEVICE_ATTR(actual_profile, 0440, kone_sysfs_show_actual_profile, NULL); | ||
| 644 | |||
| 645 | /* | ||
| 646 | * The mouse can be equipped with one of four supplied weights from 5 to 20 | ||
| 647 | * grams which are recognized and its value can be read out. | ||
| 648 | * This returns the raw value reported by the mouse for easy evaluation by | ||
| 649 | * software. Refer to enum kone_weights to get corresponding real weight. | ||
| 650 | */ | ||
| 651 | static DEVICE_ATTR(weight, 0440, kone_sysfs_show_weight, NULL); | ||
| 652 | |||
| 653 | /* | ||
| 654 | * Prints firmware version stored in mouse as integer. | ||
| 655 | * The raw value reported by the mouse is returned for easy evaluation, to get | ||
| 656 | * the real version number the decimal point has to be shifted 2 positions to | ||
| 657 | * the left. E.g. a value of 138 means 1.38. | ||
| 658 | */ | ||
| 659 | static DEVICE_ATTR(firmware_version, 0440, | ||
| 660 | kone_sysfs_show_firmware_version, NULL); | ||
| 661 | |||
| 662 | /* | ||
| 663 | * Prints state of Tracking Control Unit as number where 0 = off and 1 = on | ||
| 664 | * Writing 0 deactivates tcu and writing 1 calibrates and activates the tcu | ||
| 665 | */ | ||
| 666 | static DEVICE_ATTR(tcu, 0660, kone_sysfs_show_tcu, kone_sysfs_set_tcu); | ||
| 667 | |||
| 668 | /* Prints and takes the number of the profile the mouse starts with */ | ||
| 669 | static DEVICE_ATTR(startup_profile, 0660, | ||
| 670 | kone_sysfs_show_startup_profile, | ||
| 671 | kone_sysfs_set_startup_profile); | ||
| 672 | |||
| 673 | static DEVICE_ATTR(kone_driver_version, 0440, | ||
| 674 | kone_sysfs_show_driver_version, NULL); | ||
| 675 | |||
| 676 | static struct attribute *kone_attributes[] = { | ||
| 677 | &dev_attr_actual_dpi.attr, | ||
| 678 | &dev_attr_actual_profile.attr, | ||
| 679 | &dev_attr_weight.attr, | ||
| 680 | &dev_attr_firmware_version.attr, | ||
| 681 | &dev_attr_tcu.attr, | ||
| 682 | &dev_attr_startup_profile.attr, | ||
| 683 | &dev_attr_kone_driver_version.attr, | ||
| 684 | NULL | ||
| 685 | }; | ||
| 686 | |||
| 687 | static struct attribute_group kone_attribute_group = { | ||
| 688 | .attrs = kone_attributes | ||
| 689 | }; | ||
| 690 | |||
| 691 | static struct bin_attribute kone_settings_attr = { | ||
| 692 | .attr = { .name = "settings", .mode = 0660 }, | ||
| 693 | .size = sizeof(struct kone_settings), | ||
| 694 | .read = kone_sysfs_read_settings, | ||
| 695 | .write = kone_sysfs_write_settings | ||
| 696 | }; | ||
| 697 | |||
| 698 | static struct bin_attribute kone_profile1_attr = { | ||
| 699 | .attr = { .name = "profile1", .mode = 0660 }, | ||
| 700 | .size = sizeof(struct kone_profile), | ||
| 701 | .read = kone_sysfs_read_profile1, | ||
| 702 | .write = kone_sysfs_write_profile1 | ||
| 703 | }; | ||
| 704 | |||
| 705 | static struct bin_attribute kone_profile2_attr = { | ||
| 706 | .attr = { .name = "profile2", .mode = 0660 }, | ||
| 707 | .size = sizeof(struct kone_profile), | ||
| 708 | .read = kone_sysfs_read_profile2, | ||
| 709 | .write = kone_sysfs_write_profile2 | ||
| 710 | }; | ||
| 711 | |||
| 712 | static struct bin_attribute kone_profile3_attr = { | ||
| 713 | .attr = { .name = "profile3", .mode = 0660 }, | ||
| 714 | .size = sizeof(struct kone_profile), | ||
| 715 | .read = kone_sysfs_read_profile3, | ||
| 716 | .write = kone_sysfs_write_profile3 | ||
| 717 | }; | ||
| 718 | |||
| 719 | static struct bin_attribute kone_profile4_attr = { | ||
| 720 | .attr = { .name = "profile4", .mode = 0660 }, | ||
| 721 | .size = sizeof(struct kone_profile), | ||
| 722 | .read = kone_sysfs_read_profile4, | ||
| 723 | .write = kone_sysfs_write_profile4 | ||
| 724 | }; | ||
| 725 | |||
| 726 | static struct bin_attribute kone_profile5_attr = { | ||
| 727 | .attr = { .name = "profile5", .mode = 0660 }, | ||
| 728 | .size = sizeof(struct kone_profile), | ||
| 729 | .read = kone_sysfs_read_profile5, | ||
| 730 | .write = kone_sysfs_write_profile5 | ||
| 731 | }; | ||
| 732 | |||
| 733 | static int kone_create_sysfs_attributes(struct usb_interface *intf) | ||
| 734 | { | ||
| 735 | int retval; | ||
| 736 | |||
| 737 | retval = sysfs_create_group(&intf->dev.kobj, &kone_attribute_group); | ||
| 738 | if (retval) | ||
| 739 | goto exit_1; | ||
| 740 | |||
| 741 | retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_settings_attr); | ||
| 742 | if (retval) | ||
| 743 | goto exit_2; | ||
| 744 | |||
| 745 | retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile1_attr); | ||
| 746 | if (retval) | ||
| 747 | goto exit_3; | ||
| 748 | |||
| 749 | retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile2_attr); | ||
| 750 | if (retval) | ||
| 751 | goto exit_4; | ||
| 752 | |||
| 753 | retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile3_attr); | ||
| 754 | if (retval) | ||
| 755 | goto exit_5; | ||
| 756 | |||
| 757 | retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile4_attr); | ||
| 758 | if (retval) | ||
| 759 | goto exit_6; | ||
| 760 | |||
| 761 | retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile5_attr); | ||
| 762 | if (retval) | ||
| 763 | goto exit_7; | ||
| 764 | |||
| 765 | return 0; | ||
| 766 | |||
| 767 | exit_7: | ||
| 768 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile4_attr); | ||
| 769 | exit_6: | ||
| 770 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile3_attr); | ||
| 771 | exit_5: | ||
| 772 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile2_attr); | ||
| 773 | exit_4: | ||
| 774 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile1_attr); | ||
| 775 | exit_3: | ||
| 776 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_settings_attr); | ||
| 777 | exit_2: | ||
| 778 | sysfs_remove_group(&intf->dev.kobj, &kone_attribute_group); | ||
| 779 | exit_1: | ||
| 780 | return retval; | ||
| 781 | } | ||
| 782 | |||
| 783 | static void kone_remove_sysfs_attributes(struct usb_interface *intf) | ||
| 784 | { | ||
| 785 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile5_attr); | ||
| 786 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile4_attr); | ||
| 787 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile3_attr); | ||
| 788 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile2_attr); | ||
| 789 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile1_attr); | ||
| 790 | sysfs_remove_bin_file(&intf->dev.kobj, &kone_settings_attr); | ||
| 791 | sysfs_remove_group(&intf->dev.kobj, &kone_attribute_group); | ||
| 792 | } | ||
| 793 | |||
| 794 | static int kone_init_kone_device_struct(struct usb_device *usb_dev, | ||
| 795 | struct kone_device *kone) | ||
| 796 | { | ||
| 797 | uint i; | ||
| 798 | int retval; | ||
| 799 | |||
| 800 | mutex_init(&kone->kone_lock); | ||
| 801 | |||
| 802 | for (i = 0; i < 5; ++i) { | ||
| 803 | retval = kone_get_profile(usb_dev, &kone->profiles[i], i + 1); | ||
| 804 | if (retval) | ||
| 805 | return retval; | ||
| 806 | } | ||
| 807 | |||
| 808 | retval = kone_get_settings(usb_dev, &kone->settings); | ||
| 809 | if (retval) | ||
| 810 | return retval; | ||
| 811 | |||
| 812 | retval = kone_get_firmware_version(usb_dev, &kone->firmware_version); | ||
| 813 | if (retval) | ||
| 814 | return retval; | ||
| 815 | |||
| 816 | kone->actual_profile = kone->settings.startup_profile; | ||
| 817 | kone->actual_dpi = kone->profiles[kone->actual_profile].startup_dpi; | ||
| 818 | |||
| 819 | return 0; | ||
| 820 | } | ||
| 821 | |||
| 822 | /* | ||
| 823 | * Since IGNORE_MOUSE quirk moved to hid-apple, there is no way to bind only to | ||
| 824 | * mousepart if usb_hid is compiled into the kernel and kone is compiled as | ||
| 825 | * module. | ||
| 826 | * Secial behaviour is bound only to mousepart since only mouseevents contain | ||
| 827 | * additional notifications. | ||
| 828 | */ | ||
| 829 | static int kone_init_specials(struct hid_device *hdev) | ||
| 830 | { | ||
| 831 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
| 832 | struct usb_device *usb_dev = interface_to_usbdev(intf); | ||
| 833 | struct kone_device *kone; | ||
| 834 | int retval; | ||
| 835 | |||
| 836 | if (intf->cur_altsetting->desc.bInterfaceProtocol | ||
| 837 | == USB_INTERFACE_PROTOCOL_MOUSE) { | ||
| 838 | |||
| 839 | kone = kzalloc(sizeof(*kone), GFP_KERNEL); | ||
| 840 | if (!kone) { | ||
| 841 | dev_err(&hdev->dev, "can't alloc device descriptor\n"); | ||
| 842 | return -ENOMEM; | ||
| 843 | } | ||
| 844 | hid_set_drvdata(hdev, kone); | ||
| 845 | |||
| 846 | retval = kone_init_kone_device_struct(usb_dev, kone); | ||
| 847 | if (retval) { | ||
| 848 | dev_err(&hdev->dev, | ||
| 849 | "couldn't init struct kone_device\n"); | ||
| 850 | goto exit_free; | ||
| 851 | } | ||
| 852 | retval = kone_create_sysfs_attributes(intf); | ||
| 853 | if (retval) { | ||
| 854 | dev_err(&hdev->dev, "cannot create sysfs files\n"); | ||
| 855 | goto exit_free; | ||
| 856 | } | ||
| 857 | } else { | ||
| 858 | hid_set_drvdata(hdev, NULL); | ||
| 859 | } | ||
| 860 | |||
| 861 | return 0; | ||
| 862 | exit_free: | ||
| 863 | kfree(kone); | ||
| 864 | return retval; | ||
| 865 | } | ||
| 866 | |||
| 867 | |||
| 868 | static void kone_remove_specials(struct hid_device *hdev) | ||
| 869 | { | ||
| 870 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
| 871 | |||
| 872 | if (intf->cur_altsetting->desc.bInterfaceProtocol | ||
| 873 | == USB_INTERFACE_PROTOCOL_MOUSE) { | ||
| 874 | kone_remove_sysfs_attributes(intf); | ||
| 875 | kfree(hid_get_drvdata(hdev)); | ||
| 876 | } | ||
| 877 | } | ||
| 878 | |||
| 879 | static int kone_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
| 880 | { | ||
| 881 | int retval; | ||
| 882 | |||
| 883 | retval = hid_parse(hdev); | ||
| 884 | if (retval) { | ||
| 885 | dev_err(&hdev->dev, "parse failed\n"); | ||
| 886 | goto exit; | ||
| 887 | } | ||
| 888 | |||
| 889 | retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
| 890 | if (retval) { | ||
| 891 | dev_err(&hdev->dev, "hw start failed\n"); | ||
| 892 | goto exit; | ||
| 893 | } | ||
| 894 | |||
| 895 | retval = kone_init_specials(hdev); | ||
| 896 | if (retval) { | ||
| 897 | dev_err(&hdev->dev, "couldn't install mouse\n"); | ||
| 898 | goto exit_stop; | ||
| 899 | } | ||
| 900 | |||
| 901 | return 0; | ||
| 902 | |||
| 903 | exit_stop: | ||
| 904 | hid_hw_stop(hdev); | ||
| 905 | exit: | ||
| 906 | return retval; | ||
| 907 | } | ||
| 908 | |||
| 909 | static void kone_remove(struct hid_device *hdev) | ||
| 910 | { | ||
| 911 | kone_remove_specials(hdev); | ||
| 912 | hid_hw_stop(hdev); | ||
| 913 | } | ||
| 914 | |||
| 915 | /* handle special events and keep actual profile and dpi values up to date */ | ||
| 916 | static void kone_keep_values_up_to_date(struct kone_device *kone, | ||
| 917 | struct kone_mouse_event const *event) | ||
| 918 | { | ||
| 919 | switch (event->event) { | ||
| 920 | case kone_mouse_event_switch_profile: | ||
| 921 | case kone_mouse_event_osd_profile: | ||
| 922 | kone->actual_profile = event->value; | ||
| 923 | kone->actual_dpi = kone->profiles[kone->actual_profile - 1]. | ||
| 924 | startup_dpi; | ||
| 925 | break; | ||
| 926 | case kone_mouse_event_switch_dpi: | ||
| 927 | case kone_mouse_event_osd_dpi: | ||
| 928 | kone->actual_dpi = event->value; | ||
| 929 | break; | ||
| 930 | } | ||
| 931 | } | ||
| 932 | |||
| 933 | /* | ||
| 934 | * Is called for keyboard- and mousepart. | ||
| 935 | * Only mousepart gets informations about special events in its extended event | ||
| 936 | * structure. | ||
| 937 | */ | ||
| 938 | static int kone_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
| 939 | u8 *data, int size) | ||
| 940 | { | ||
| 941 | struct kone_device *kone = hid_get_drvdata(hdev); | ||
| 942 | struct kone_mouse_event *event = (struct kone_mouse_event *)data; | ||
| 943 | |||
| 944 | /* keyboard events are always processed by default handler */ | ||
| 945 | if (size != sizeof(struct kone_mouse_event)) | ||
| 946 | return 0; | ||
| 947 | |||
| 948 | /* | ||
| 949 | * Firmware 1.38 introduced new behaviour for tilt and special buttons. | ||
| 950 | * Pressed button is reported in each movement event. | ||
| 951 | * Workaround sends only one event per press. | ||
| 952 | */ | ||
| 953 | if (memcmp(&kone->last_mouse_event.tilt, &event->tilt, 5)) | ||
| 954 | memcpy(&kone->last_mouse_event, event, | ||
| 955 | sizeof(struct kone_mouse_event)); | ||
| 956 | else | ||
| 957 | memset(&event->tilt, 0, 5); | ||
| 958 | |||
| 959 | kone_keep_values_up_to_date(kone, event); | ||
| 960 | |||
| 961 | return 0; /* always do further processing */ | ||
| 962 | } | ||
| 963 | |||
| 964 | static const struct hid_device_id kone_devices[] = { | ||
| 965 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, | ||
| 966 | { } | ||
| 967 | }; | ||
| 968 | |||
| 969 | MODULE_DEVICE_TABLE(hid, kone_devices); | ||
| 970 | |||
| 971 | static struct hid_driver kone_driver = { | ||
| 972 | .name = "kone", | ||
| 973 | .id_table = kone_devices, | ||
| 974 | .probe = kone_probe, | ||
| 975 | .remove = kone_remove, | ||
| 976 | .raw_event = kone_raw_event | ||
| 977 | }; | ||
| 978 | |||
| 979 | static int __init kone_init(void) | ||
| 980 | { | ||
| 981 | return hid_register_driver(&kone_driver); | ||
| 982 | } | ||
| 983 | |||
| 984 | static void __exit kone_exit(void) | ||
| 985 | { | ||
| 986 | hid_unregister_driver(&kone_driver); | ||
| 987 | } | ||
| 988 | |||
| 989 | module_init(kone_init); | ||
| 990 | module_exit(kone_exit); | ||
| 991 | |||
| 992 | MODULE_AUTHOR("Stefan Achatz"); | ||
| 993 | MODULE_DESCRIPTION("USB Roccat Kone driver"); | ||
| 994 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/hid/hid-roccat-kone.h b/drivers/hid/hid-roccat-kone.h new file mode 100644 index 000000000000..b413b10a7f8a --- /dev/null +++ b/drivers/hid/hid-roccat-kone.h | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | #ifndef __HID_ROCCAT_KONE_H | ||
| 2 | #define __HID_ROCCAT_KONE_H | ||
| 3 | |||
| 4 | /* | ||
| 5 | * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> | ||
| 6 | */ | ||
| 7 | |||
| 8 | /* | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License as published by the Free | ||
| 11 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 12 | * any later version. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/types.h> | ||
| 16 | |||
| 17 | #define ROCCAT_KONE_DRIVER_VERSION "v0.3.1" | ||
| 18 | |||
| 19 | #pragma pack(push) | ||
| 20 | #pragma pack(1) | ||
| 21 | |||
| 22 | struct kone_keystroke { | ||
| 23 | uint8_t key; | ||
| 24 | uint8_t action; | ||
| 25 | uint16_t period; /* in milliseconds */ | ||
| 26 | }; | ||
| 27 | |||
| 28 | enum kone_keystroke_buttons { | ||
| 29 | kone_keystroke_button_1 = 0xf0, /* left mouse button */ | ||
| 30 | kone_keystroke_button_2 = 0xf1, /* right mouse button */ | ||
| 31 | kone_keystroke_button_3 = 0xf2, /* wheel */ | ||
| 32 | kone_keystroke_button_9 = 0xf3, /* side button up */ | ||
| 33 | kone_keystroke_button_8 = 0xf4 /* side button down */ | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum kone_keystroke_actions { | ||
| 37 | kone_keystroke_action_press = 0, | ||
| 38 | kone_keystroke_action_release = 1 | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct kone_button_info { | ||
| 42 | uint8_t number; /* range 1-8 */ | ||
| 43 | uint8_t type; | ||
| 44 | uint8_t macro_type; /* 0 = short, 1 = overlong */ | ||
| 45 | uint8_t macro_set_name[16]; /* can be max 15 chars long */ | ||
| 46 | uint8_t macro_name[16]; /* can be max 15 chars long */ | ||
| 47 | uint8_t count; | ||
| 48 | struct kone_keystroke keystrokes[20]; | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum kone_button_info_types { | ||
| 52 | /* valid button types until firmware 1.32 */ | ||
| 53 | kone_button_info_type_button_1 = 0x1, /* click (left mouse button) */ | ||
| 54 | kone_button_info_type_button_2 = 0x2, /* menu (right mouse button)*/ | ||
| 55 | kone_button_info_type_button_3 = 0x3, /* scroll (wheel) */ | ||
| 56 | kone_button_info_type_double_click = 0x4, | ||
| 57 | kone_button_info_type_key = 0x5, | ||
| 58 | kone_button_info_type_macro = 0x6, | ||
| 59 | kone_button_info_type_off = 0x7, | ||
| 60 | /* TODO clarify function and rename */ | ||
| 61 | kone_button_info_type_osd_xy_prescaling = 0x8, | ||
| 62 | kone_button_info_type_osd_dpi = 0x9, | ||
| 63 | kone_button_info_type_osd_profile = 0xa, | ||
| 64 | kone_button_info_type_button_9 = 0xb, /* ie forward */ | ||
| 65 | kone_button_info_type_button_8 = 0xc, /* ie backward */ | ||
| 66 | kone_button_info_type_dpi_up = 0xd, /* internal */ | ||
| 67 | kone_button_info_type_dpi_down = 0xe, /* internal */ | ||
| 68 | kone_button_info_type_button_7 = 0xf, /* tilt left */ | ||
| 69 | kone_button_info_type_button_6 = 0x10, /* tilt right */ | ||
| 70 | kone_button_info_type_profile_up = 0x11, /* internal */ | ||
| 71 | kone_button_info_type_profile_down = 0x12, /* internal */ | ||
| 72 | /* additional valid button types since firmware 1.38 */ | ||
| 73 | kone_button_info_type_multimedia_open_player = 0x20, | ||
| 74 | kone_button_info_type_multimedia_next_track = 0x21, | ||
| 75 | kone_button_info_type_multimedia_prev_track = 0x22, | ||
| 76 | kone_button_info_type_multimedia_play_pause = 0x23, | ||
| 77 | kone_button_info_type_multimedia_stop = 0x24, | ||
| 78 | kone_button_info_type_multimedia_mute = 0x25, | ||
| 79 | kone_button_info_type_multimedia_volume_up = 0x26, | ||
| 80 | kone_button_info_type_multimedia_volume_down = 0x27 | ||
| 81 | }; | ||
| 82 | |||
| 83 | enum kone_button_info_numbers { | ||
| 84 | kone_button_top = 1, | ||
| 85 | kone_button_wheel_tilt_left = 2, | ||
| 86 | kone_button_wheel_tilt_right = 3, | ||
| 87 | kone_button_forward = 4, | ||
| 88 | kone_button_backward = 5, | ||
| 89 | kone_button_middle = 6, | ||
| 90 | kone_button_plus = 7, | ||
| 91 | kone_button_minus = 8, | ||
| 92 | }; | ||
| 93 | |||
| 94 | struct kone_light_info { | ||
| 95 | uint8_t number; /* number of light 1-5 */ | ||
| 96 | uint8_t mod; /* 1 = on, 2 = off */ | ||
| 97 | uint8_t red; /* range 0x00-0xff */ | ||
| 98 | uint8_t green; /* range 0x00-0xff */ | ||
| 99 | uint8_t blue; /* range 0x00-0xff */ | ||
| 100 | }; | ||
| 101 | |||
| 102 | struct kone_profile { | ||
| 103 | uint16_t size; /* always 975 */ | ||
| 104 | uint16_t unused; /* always 0 */ | ||
| 105 | |||
| 106 | /* | ||
| 107 | * range 1-5 | ||
| 108 | * This number does not need to correspond with location where profile | ||
| 109 | * saved | ||
| 110 | */ | ||
| 111 | uint8_t profile; /* range 1-5 */ | ||
| 112 | |||
| 113 | uint16_t main_sensitivity; /* range 100-1000 */ | ||
| 114 | uint8_t xy_sensitivity_enabled; /* 1 = on, 2 = off */ | ||
| 115 | uint16_t x_sensitivity; /* range 100-1000 */ | ||
| 116 | uint16_t y_sensitivity; /* range 100-1000 */ | ||
| 117 | uint8_t dpi_rate; /* bit 1 = 800, ... */ | ||
| 118 | uint8_t startup_dpi; /* range 1-6 */ | ||
| 119 | uint8_t polling_rate; /* 1 = 125Hz, 2 = 500Hz, 3 = 1000Hz */ | ||
| 120 | /* kone has no dcu | ||
| 121 | * value is always 2 in firmwares <= 1.32 and | ||
| 122 | * 1 in firmwares > 1.32 | ||
| 123 | */ | ||
| 124 | uint8_t dcu_flag; | ||
| 125 | uint8_t light_effect_1; /* range 1-3 */ | ||
| 126 | uint8_t light_effect_2; /* range 1-5 */ | ||
| 127 | uint8_t light_effect_3; /* range 1-4 */ | ||
| 128 | uint8_t light_effect_speed; /* range 0-255 */ | ||
| 129 | |||
| 130 | struct kone_light_info light_infos[5]; | ||
| 131 | /* offset is kone_button_info_numbers - 1 */ | ||
| 132 | struct kone_button_info button_infos[8]; | ||
| 133 | |||
| 134 | uint16_t checksum; /* \brief holds checksum of struct */ | ||
| 135 | }; | ||
| 136 | |||
| 137 | enum kone_polling_rates { | ||
| 138 | kone_polling_rate_125 = 1, | ||
| 139 | kone_polling_rate_500 = 2, | ||
| 140 | kone_polling_rate_1000 = 3 | ||
| 141 | }; | ||
| 142 | |||
| 143 | struct kone_settings { | ||
| 144 | uint16_t size; /* always 36 */ | ||
| 145 | uint8_t startup_profile; /* 1-5 */ | ||
| 146 | uint8_t unknown1; | ||
| 147 | uint8_t tcu; /* 0 = off, 1 = on */ | ||
| 148 | uint8_t unknown2[23]; | ||
| 149 | uint8_t calibration_data[4]; | ||
| 150 | uint8_t unknown3[2]; | ||
| 151 | uint16_t checksum; | ||
| 152 | }; | ||
| 153 | |||
| 154 | /* | ||
| 155 | * 12 byte mouse event read by interrupt_read | ||
| 156 | */ | ||
| 157 | struct kone_mouse_event { | ||
| 158 | uint8_t report_number; /* always 1 */ | ||
| 159 | uint8_t button; | ||
| 160 | uint16_t x; | ||
| 161 | uint16_t y; | ||
| 162 | uint8_t wheel; /* up = 1, down = -1 */ | ||
| 163 | uint8_t tilt; /* right = 1, left = -1 */ | ||
| 164 | uint8_t unknown; | ||
| 165 | uint8_t event; | ||
| 166 | uint8_t value; /* press = 0, release = 1 */ | ||
| 167 | uint8_t macro_key; /* 0 to 8 */ | ||
| 168 | }; | ||
| 169 | |||
| 170 | enum kone_mouse_events { | ||
| 171 | /* osd events are thought to be display on screen */ | ||
| 172 | kone_mouse_event_osd_dpi = 0xa0, | ||
| 173 | kone_mouse_event_osd_profile = 0xb0, | ||
| 174 | /* TODO clarify meaning and occurence of kone_mouse_event_calibration */ | ||
| 175 | kone_mouse_event_calibration = 0xc0, | ||
| 176 | kone_mouse_event_call_overlong_macro = 0xe0, | ||
| 177 | /* switch events notify if user changed values with mousebutton click */ | ||
| 178 | kone_mouse_event_switch_dpi = 0xf0, | ||
| 179 | kone_mouse_event_switch_profile = 0xf1 | ||
| 180 | }; | ||
| 181 | |||
| 182 | enum kone_commands { | ||
| 183 | kone_command_profile = 0x5a, | ||
| 184 | kone_command_settings = 0x15a, | ||
| 185 | kone_command_firmware_version = 0x25a, | ||
| 186 | kone_command_weight = 0x45a, | ||
| 187 | kone_command_calibrate = 0x55a, | ||
| 188 | kone_command_confirm_write = 0x65a, | ||
| 189 | kone_command_firmware = 0xe5a | ||
| 190 | }; | ||
| 191 | |||
| 192 | #pragma pack(pop) | ||
| 193 | |||
| 194 | struct kone_device { | ||
| 195 | /* | ||
| 196 | * Storing actual values when we get informed about changes since there | ||
| 197 | * is no way of getting this information from the device on demand | ||
| 198 | */ | ||
| 199 | int actual_profile, actual_dpi; | ||
| 200 | /* Used for neutralizing abnormal button behaviour */ | ||
| 201 | struct kone_mouse_event last_mouse_event; | ||
| 202 | |||
| 203 | /* | ||
| 204 | * It's unlikely that multiple sysfs attributes are accessed at a time, | ||
| 205 | * so only one mutex is used to secure hardware access and profiles and | ||
| 206 | * settings of this struct. | ||
| 207 | */ | ||
| 208 | struct mutex kone_lock; | ||
| 209 | |||
| 210 | /* | ||
| 211 | * Storing the data here reduces IO and ensures that data is available | ||
| 212 | * when its needed (E.g. interrupt handler). | ||
| 213 | */ | ||
| 214 | struct kone_profile profiles[5]; | ||
| 215 | struct kone_settings settings; | ||
| 216 | |||
| 217 | /* | ||
| 218 | * firmware doesn't change unless firmware update is implemented, | ||
| 219 | * so it's read only once | ||
| 220 | */ | ||
| 221 | int firmware_version; | ||
| 222 | }; | ||
| 223 | |||
| 224 | #endif | ||
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ca3751fd4473..a9364c36c42d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
| @@ -623,6 +623,7 @@ int usbhid_wait_io(struct hid_device *hid) | |||
| 623 | 623 | ||
| 624 | return 0; | 624 | return 0; |
| 625 | } | 625 | } |
| 626 | EXPORT_SYMBOL_GPL(usbhid_wait_io); | ||
| 626 | 627 | ||
| 627 | static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) | 628 | static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) |
| 628 | { | 629 | { |
