···11+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/operation_mode22+Date: March 201033+Contact: Bruno Prémont <bonbons@linux-vserver.org>44+Description: Make it possible to switch the PicoLCD device between LCD55+ (firmware) and bootloader (flasher) operation modes.66+77+ Reading: returns list of available modes, the active mode being88+ enclosed in brackets ('[' and ']')99+1010+ Writing: causes operation mode switch. Permitted values are1111+ the non-active mode names listed when read.1212+1313+ Note: when switching mode the current PicoLCD HID device gets1414+ disconnected and reconnects after above delay (see attribute1515+ operation_mode_delay for its value).1616+1717+1818+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/operation_mode_delay1919+Date: April 20102020+Contact: Bruno Prémont <bonbons@linux-vserver.org>2121+Description: Delay PicoLCD waits before restarting in new mode when2222+ operation_mode has changed.2323+2424+ Reading/Writing: It is expressed in ms and permitted range is2525+ 0..30000ms.2626+2727+2828+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fb_update_rate2929+Date: March 20103030+Contact: Bruno Prémont <bonbons@linux-vserver.org>3131+Description: Make it possible to adjust defio refresh rate.3232+3333+ Reading: returns list of available refresh rates (expressed in Hz),3434+ the active refresh rate being enclosed in brackets ('[' and ']')3535+3636+ Writing: accepts new refresh rate expressed in integer Hz3737+ within permitted rates.3838+3939+ Note: As device can barely do 2 complete refreshes a second4040+ it only makes sense to adjust this value if only one or two4141+ tiles get changed and it's not appropriate to expect the application4242+ to flush it's tiny changes explicitely at higher than default rate.4343+
···11+What: /sys/bus/hid/drivers/prodikeys/.../channel22+Date: April 201033+KernelVersion: 2.6.3444+Contact: Don Prince <dhprince.devel@yahoo.co.uk>55+Description:66+ Allows control (via software) the midi channel to which77+ that the pc-midi keyboard will output.midi data.88+ Range: 0..1599+ Type: Read/write1010+What: /sys/bus/hid/drivers/prodikeys/.../sustain1111+Date: April 20101212+KernelVersion: 2.6.341313+Contact: Don Prince <dhprince.devel@yahoo.co.uk>1414+Description:1515+ Allows control (via software) the sustain duration of a1616+ note held by the pc-midi driver.1717+ 0 means sustain mode is disabled.1818+ Range: 0..5000 (milliseconds)1919+ Type: Read/write2020+What: /sys/bus/hid/drivers/prodikeys/.../octave2121+Date: April 20102222+KernelVersion: 2.6.342323+Contact: Don Prince <dhprince.devel@yahoo.co.uk>2424+Description:2525+ Controls the octave shift modifier in the pc-midi driver.2626+ The octave can be shifted via software up/down 2 octaves.2727+ 0 means the no ocatve shift.2828+ Range: -2..2 (minus 2 to plus 2)2929+ Type: Read/Write
···11+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_dpi22+Date: March 201033+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>44+Description: It is possible to switch the dpi setting of the mouse with the55+ press of a button.66+ When read, this file returns the raw number of the actual dpi77+ setting reported by the mouse. This number has to be further88+ processed to receive the real dpi value.99+1010+ VALUE DPI1111+ 1 8001212+ 2 12001313+ 3 16001414+ 4 20001515+ 5 24001616+ 6 32001717+1818+ This file is readonly.1919+2020+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_profile2121+Date: March 20102222+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>2323+Description: When read, this file returns the number of the actual profile.2424+ This file is readonly.2525+2626+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/firmware_version2727+Date: March 20102828+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>2929+Description: When read, this file returns the raw integer version number of the3030+ firmware reported by the mouse. Using the integer value eases3131+ further usage in other programs. To receive the real version3232+ number the decimal point has to be shifted 2 positions to the3333+ left. E.g. a returned value of 138 means 1.383434+ This file is readonly.3535+3636+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/kone_driver_version3737+Date: March 20103838+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>3939+Description: When read, this file returns the driver version.4040+ The format of the string is "v<major>.<minor>.<patchlevel>".4141+ This attribute is used by the userland tools to find the sysfs-4242+ paths of installed kone-mice and determine the capabilites of4343+ the driver. Versions of this driver for old kernels replace4444+ usbhid instead of generic-usb. The way to scan for this file4545+ has been chosen to provide a consistent way for all supported4646+ kernel versions.4747+ This file is readonly.4848+4949+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]5050+Date: March 20105151+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>5252+Description: The mouse can store 5 profiles which can be switched by the5353+ press of a button. A profile holds informations like button5454+ mappings, sensitivity, the colors of the 5 leds and light5555+ effects.5656+ When read, these files return the respective profile. The5757+ returned data is 975 bytes in size.5858+ When written, this file lets one write the respective profile5959+ data back to the mouse. The data has to be 975 bytes long.6060+ The mouse will reject invalid data, whereas the profile number6161+ stored in the profile doesn't need to fit the number of the6262+ store.6363+6464+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/settings6565+Date: March 20106666+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>6767+Description: When read, this file returns the settings stored in the mouse.6868+ The size of the data is 36 bytes and holds information like the6969+ startup_profile, tcu state and calibration_data.7070+ When written, this file lets write settings back to the mouse.7171+ The data has to be 36 bytes long. The mouse will reject invalid7272+ data.7373+7474+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/startup_profile7575+Date: March 20107676+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>7777+Description: The integer value of this attribute ranges from 1 to 5.7878+ When read, this attribute returns the number of the profile7979+ that's active when the mouse is powered on.8080+ When written, this file sets the number of the startup profile8181+ and the mouse activates this profile immediately.8282+8383+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/tcu8484+Date: March 20108585+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>8686+Description: The mouse has a "Tracking Control Unit" which lets the user8787+ calibrate the laser power to fit the mousepad surface.8888+ When read, this file returns the current state of the TCU,8989+ where 0 means off and 1 means on.9090+ Writing 0 in this file will switch the TCU off.9191+ Writing 1 in this file will start the calibration which takes9292+ around 6 seconds to complete and activates the TCU.9393+9494+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/weight9595+Date: March 20109696+Contact: Stefan Achatz <erazor_de@users.sourceforge.net>9797+Description: The mouse can be equipped with one of four supplied weights9898+ ranging from 5 to 20 grams which are recognized by the mouse9999+ and its value can be read out. When read, this file returns the100100+ raw value returned by the mouse which eases further processing101101+ in other software.102102+ The values map to the weights as follows:103103+104104+ VALUE WEIGHT105105+ 0 none106106+ 1 5g107107+ 2 10g108108+ 3 15g109109+ 4 20g110110+111111+ This file is readonly.
+79
drivers/hid/Kconfig
···106106 ---help---107107 Support for Chicony Tactical pad.108108109109+config HID_PRODIKEYS110110+ tristate "Prodikeys PC-MIDI Keyboard support"111111+ depends on USB_HID && SND112112+ select SND_RAWMIDI113113+ ---help---114114+ Support for Prodikeys PC-MIDI Keyboard device support.115115+ Say Y here to enable support for this device.116116+ - Prodikeys PC-MIDI keyboard.117117+ The Prodikeys PC-MIDI acts as a USB Audio device, with one MIDI118118+ input and one MIDI output. These MIDI jacks appear as119119+ a sound "card" in the ALSA sound system.120120+ Note: if you say N here, this device will still function as a basic121121+ multimedia keyboard, but will lack support for the musical keyboard122122+ and some additional multimedia keys.123123+109124config HID_CYPRESS110125 tristate "Cypress" if EMBEDDED111126 depends on USB_HID···289274 ---help---290275 Support for Petalynx Maxter remote control.291276277277+config HID_PICOLCD278278+ tristate "PicoLCD (graphic version)"279279+ depends on USB_HID280280+ ---help---281281+ This provides support for Minibox PicoLCD devices, currently282282+ only the graphical ones are supported.283283+284284+ This includes support for the following device features:285285+ - Keypad286286+ - Switching between Firmware and Flash mode287287+ - EEProm / Flash access (via debugfs)288288+ Features selectively enabled:289289+ - Framebuffer for monochrome 256x64 display290290+ - Backlight control291291+ - Contrast control292292+ - General purpose outputs293293+ Features that are not (yet) supported:294294+ - IR295295+296296+config HID_PICOLCD_FB297297+ bool "Framebuffer support" if EMBEDDED298298+ default !EMBEDDED299299+ depends on HID_PICOLCD300300+ depends on HID_PICOLCD=FB || FB=y301301+ select FB_DEFERRED_IO302302+ select FB_SYS_FILLRECT303303+ select FB_SYS_COPYAREA304304+ select FB_SYS_IMAGEBLIT305305+ select FB_SYS_FOPS306306+ ---help---307307+ Provide access to PicoLCD's 256x64 monochrome display via a308308+ frambuffer device.309309+310310+config HID_PICOLCD_BACKLIGHT311311+ bool "Backlight control" if EMBEDDED312312+ default !EMBEDDED313313+ depends on HID_PICOLCD314314+ depends on HID_PICOLCD=BACKLIGHT_CLASS_DEVICE || BACKLIGHT_CLASS_DEVICE=y315315+ ---help---316316+ Provide access to PicoLCD's backlight control via backlight317317+ class.318318+319319+config HID_PICOLCD_LCD320320+ bool "Contrast control" if EMBEDDED321321+ default !EMBEDDED322322+ depends on HID_PICOLCD323323+ depends on HID_PICOLCD=LCD_CLASS_DEVICE || LCD_CLASS_DEVICE=y324324+ ---help---325325+ Provide access to PicoLCD's LCD contrast via lcd class.326326+327327+config HID_PICOLCD_LEDS328328+ bool "GPO via leds class" if EMBEDDED329329+ default !EMBEDDED330330+ depends on HID_PICOLCD331331+ depends on HID_PICOLCD=LEDS_CLASS || LEDS_CLASS=y332332+ ---help---333333+ Provide access to PicoLCD's GPO pins via leds class.334334+292335config HID_QUANTA293336 tristate "Quanta Optical Touch"294337 depends on USB_HID295338 ---help---296339 Support for Quanta Optical Touch dual-touch panels.340340+341341+config HID_ROCCAT_KONE342342+ tristate "Roccat Kone Mouse support"343343+ depends on USB_HID344344+ ---help---345345+ Support for Roccat Kone mouse.297346298347config HID_SAMSUNG299348 tristate "Samsung" if EMBEDDED
···24242525#define NTRIG_DUPLICATE_USAGES 0x00126262727+static unsigned int min_width;2828+module_param(min_width, uint, 0644);2929+MODULE_PARM_DESC(min_width, "Minimum touch contact width to accept.");3030+3131+static unsigned int min_height;3232+module_param(min_height, uint, 0644);3333+MODULE_PARM_DESC(min_height, "Minimum touch contact height to accept.");3434+3535+static unsigned int activate_slack = 1;3636+module_param(activate_slack, uint, 0644);3737+MODULE_PARM_DESC(activate_slack, "Number of touch frames to ignore at "3838+ "the start of touch input.");3939+4040+static unsigned int deactivate_slack = 4;4141+module_param(deactivate_slack, uint, 0644);4242+MODULE_PARM_DESC(deactivate_slack, "Number of empty frames to ignore before "4343+ "deactivating touch.");4444+4545+static unsigned int activation_width = 64;4646+module_param(activation_width, uint, 0644);4747+MODULE_PARM_DESC(activation_width, "Width threshold to immediately start "4848+ "processing touch events.");4949+5050+static unsigned int activation_height = 32;5151+module_param(activation_height, uint, 0644);5252+MODULE_PARM_DESC(activation_height, "Height threshold to immediately start "5353+ "processing touch events.");5454+2755struct ntrig_data {2856 /* Incoming raw values for a single contact */2957 __u16 x, y, w, h;···65376638 __u8 mt_footer[4];6739 __u8 mt_foot_count;4040+4141+ /* The current activation state. */4242+ __s8 act_state;4343+4444+ /* Empty frames to ignore before recognizing the end of activity */4545+ __s8 deactivate_slack;4646+4747+ /* Frames to ignore before acknowledging the start of activity */4848+ __s8 activate_slack;4949+5050+ /* Minimum size contact to accept */5151+ __u16 min_width;5252+ __u16 min_height;5353+5454+ /* Threshold to override activation slack */5555+ __u16 activation_width;5656+ __u16 activation_height;5757+5858+ __u16 sensor_logical_width;5959+ __u16 sensor_logical_height;6060+ __u16 sensor_physical_width;6161+ __u16 sensor_physical_height;6262+};6363+6464+6565+static ssize_t show_phys_width(struct device *dev,6666+ struct device_attribute *attr,6767+ char *buf)6868+{6969+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);7070+ struct ntrig_data *nd = hid_get_drvdata(hdev);7171+7272+ return sprintf(buf, "%d\n", nd->sensor_physical_width);7373+}7474+7575+static DEVICE_ATTR(sensor_physical_width, S_IRUGO, show_phys_width, NULL);7676+7777+static ssize_t show_phys_height(struct device *dev,7878+ struct device_attribute *attr,7979+ char *buf)8080+{8181+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);8282+ struct ntrig_data *nd = hid_get_drvdata(hdev);8383+8484+ return sprintf(buf, "%d\n", nd->sensor_physical_height);8585+}8686+8787+static DEVICE_ATTR(sensor_physical_height, S_IRUGO, show_phys_height, NULL);8888+8989+static ssize_t show_log_width(struct device *dev,9090+ struct device_attribute *attr,9191+ char *buf)9292+{9393+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);9494+ struct ntrig_data *nd = hid_get_drvdata(hdev);9595+9696+ return sprintf(buf, "%d\n", nd->sensor_logical_width);9797+}9898+9999+static DEVICE_ATTR(sensor_logical_width, S_IRUGO, show_log_width, NULL);100100+101101+static ssize_t show_log_height(struct device *dev,102102+ struct device_attribute *attr,103103+ char *buf)104104+{105105+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);106106+ struct ntrig_data *nd = hid_get_drvdata(hdev);107107+108108+ return sprintf(buf, "%d\n", nd->sensor_logical_height);109109+}110110+111111+static DEVICE_ATTR(sensor_logical_height, S_IRUGO, show_log_height, NULL);112112+113113+static ssize_t show_min_width(struct device *dev,114114+ struct device_attribute *attr,115115+ char *buf)116116+{117117+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);118118+ struct ntrig_data *nd = hid_get_drvdata(hdev);119119+120120+ return sprintf(buf, "%d\n", nd->min_width *121121+ nd->sensor_physical_width /122122+ nd->sensor_logical_width);123123+}124124+125125+static ssize_t set_min_width(struct device *dev,126126+ struct device_attribute *attr,127127+ const char *buf, size_t count)128128+{129129+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);130130+ struct ntrig_data *nd = hid_get_drvdata(hdev);131131+132132+ unsigned long val;133133+134134+ if (strict_strtoul(buf, 0, &val))135135+ return -EINVAL;136136+137137+ if (val > nd->sensor_physical_width)138138+ return -EINVAL;139139+140140+ nd->min_width = val * nd->sensor_logical_width /141141+ nd->sensor_physical_width;142142+143143+ return count;144144+}145145+146146+static DEVICE_ATTR(min_width, S_IWUSR | S_IRUGO, show_min_width, set_min_width);147147+148148+static ssize_t show_min_height(struct device *dev,149149+ struct device_attribute *attr,150150+ char *buf)151151+{152152+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);153153+ struct ntrig_data *nd = hid_get_drvdata(hdev);154154+155155+ return sprintf(buf, "%d\n", nd->min_height *156156+ nd->sensor_physical_height /157157+ nd->sensor_logical_height);158158+}159159+160160+static ssize_t set_min_height(struct device *dev,161161+ struct device_attribute *attr,162162+ const char *buf, size_t count)163163+{164164+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);165165+ struct ntrig_data *nd = hid_get_drvdata(hdev);166166+167167+ unsigned long val;168168+169169+ if (strict_strtoul(buf, 0, &val))170170+ return -EINVAL;171171+172172+ if (val > nd->sensor_physical_height)173173+ return -EINVAL;174174+175175+ nd->min_height = val * nd->sensor_logical_height /176176+ nd->sensor_physical_height;177177+178178+ return count;179179+}180180+181181+static DEVICE_ATTR(min_height, S_IWUSR | S_IRUGO, show_min_height,182182+ set_min_height);183183+184184+static ssize_t show_activate_slack(struct device *dev,185185+ struct device_attribute *attr,186186+ char *buf)187187+{188188+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);189189+ struct ntrig_data *nd = hid_get_drvdata(hdev);190190+191191+ return sprintf(buf, "%d\n", nd->activate_slack);192192+}193193+194194+static ssize_t set_activate_slack(struct device *dev,195195+ struct device_attribute *attr,196196+ const char *buf, size_t count)197197+{198198+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);199199+ struct ntrig_data *nd = hid_get_drvdata(hdev);200200+201201+ unsigned long val;202202+203203+ if (strict_strtoul(buf, 0, &val))204204+ return -EINVAL;205205+206206+ if (val > 0x7f)207207+ return -EINVAL;208208+209209+ nd->activate_slack = val;210210+211211+ return count;212212+}213213+214214+static DEVICE_ATTR(activate_slack, S_IWUSR | S_IRUGO, show_activate_slack,215215+ set_activate_slack);216216+217217+static ssize_t show_activation_width(struct device *dev,218218+ struct device_attribute *attr,219219+ char *buf)220220+{221221+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);222222+ struct ntrig_data *nd = hid_get_drvdata(hdev);223223+224224+ return sprintf(buf, "%d\n", nd->activation_width *225225+ nd->sensor_physical_width /226226+ nd->sensor_logical_width);227227+}228228+229229+static ssize_t set_activation_width(struct device *dev,230230+ struct device_attribute *attr,231231+ const char *buf, size_t count)232232+{233233+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);234234+ struct ntrig_data *nd = hid_get_drvdata(hdev);235235+236236+ unsigned long val;237237+238238+ if (strict_strtoul(buf, 0, &val))239239+ return -EINVAL;240240+241241+ if (val > nd->sensor_physical_width)242242+ return -EINVAL;243243+244244+ nd->activation_width = val * nd->sensor_logical_width /245245+ nd->sensor_physical_width;246246+247247+ return count;248248+}249249+250250+static DEVICE_ATTR(activation_width, S_IWUSR | S_IRUGO, show_activation_width,251251+ set_activation_width);252252+253253+static ssize_t show_activation_height(struct device *dev,254254+ struct device_attribute *attr,255255+ char *buf)256256+{257257+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);258258+ struct ntrig_data *nd = hid_get_drvdata(hdev);259259+260260+ return sprintf(buf, "%d\n", nd->activation_height *261261+ nd->sensor_physical_height /262262+ nd->sensor_logical_height);263263+}264264+265265+static ssize_t set_activation_height(struct device *dev,266266+ struct device_attribute *attr,267267+ const char *buf, size_t count)268268+{269269+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);270270+ struct ntrig_data *nd = hid_get_drvdata(hdev);271271+272272+ unsigned long val;273273+274274+ if (strict_strtoul(buf, 0, &val))275275+ return -EINVAL;276276+277277+ if (val > nd->sensor_physical_height)278278+ return -EINVAL;279279+280280+ nd->activation_height = val * nd->sensor_logical_height /281281+ nd->sensor_physical_height;282282+283283+ return count;284284+}285285+286286+static DEVICE_ATTR(activation_height, S_IWUSR | S_IRUGO,287287+ show_activation_height, set_activation_height);288288+289289+static ssize_t show_deactivate_slack(struct device *dev,290290+ struct device_attribute *attr,291291+ char *buf)292292+{293293+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);294294+ struct ntrig_data *nd = hid_get_drvdata(hdev);295295+296296+ return sprintf(buf, "%d\n", -nd->deactivate_slack);297297+}298298+299299+static ssize_t set_deactivate_slack(struct device *dev,300300+ struct device_attribute *attr,301301+ const char *buf, size_t count)302302+{303303+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);304304+ struct ntrig_data *nd = hid_get_drvdata(hdev);305305+306306+ unsigned long val;307307+308308+ if (strict_strtoul(buf, 0, &val))309309+ return -EINVAL;310310+311311+ /*312312+ * No more than 8 terminal frames have been observed so far313313+ * and higher slack is highly likely to leave the single314314+ * touch emulation stuck down.315315+ */316316+ if (val > 7)317317+ return -EINVAL;318318+319319+ nd->deactivate_slack = -val;320320+321321+ return count;322322+}323323+324324+static DEVICE_ATTR(deactivate_slack, S_IWUSR | S_IRUGO, show_deactivate_slack,325325+ set_deactivate_slack);326326+327327+static struct attribute *sysfs_attrs[] = {328328+ &dev_attr_sensor_physical_width.attr,329329+ &dev_attr_sensor_physical_height.attr,330330+ &dev_attr_sensor_logical_width.attr,331331+ &dev_attr_sensor_logical_height.attr,332332+ &dev_attr_min_height.attr,333333+ &dev_attr_min_width.attr,334334+ &dev_attr_activate_slack.attr,335335+ &dev_attr_activation_width.attr,336336+ &dev_attr_activation_height.attr,337337+ &dev_attr_deactivate_slack.attr,338338+ NULL339339+};340340+341341+static struct attribute_group ntrig_attribute_group = {342342+ .attrs = sysfs_attrs68343};6934470345/*···38049 struct hid_field *field, struct hid_usage *usage,38150 unsigned long **bit, int *max)38251{5252+ struct ntrig_data *nd = hid_get_drvdata(hdev);5353+38354 /* No special mappings needed for the pen and single touch */38455 if (field->physical)38556 return 0;···39562 input_set_abs_params(hi->input, ABS_X,39663 field->logical_minimum,39764 field->logical_maximum, 0, 0);6565+6666+ if (!nd->sensor_logical_width) {6767+ nd->sensor_logical_width =6868+ field->logical_maximum -6969+ field->logical_minimum;7070+ nd->sensor_physical_width =7171+ field->physical_maximum -7272+ field->physical_minimum;7373+ nd->activation_width = activation_width *7474+ nd->sensor_logical_width /7575+ nd->sensor_physical_width;7676+ nd->min_width = min_width *7777+ nd->sensor_logical_width /7878+ nd->sensor_physical_width;7979+ }39880 return 1;39981 case HID_GD_Y:40082 hid_map_usage(hi, usage, bit, max,···41769 input_set_abs_params(hi->input, ABS_Y,41870 field->logical_minimum,41971 field->logical_maximum, 0, 0);7272+7373+ if (!nd->sensor_logical_height) {7474+ nd->sensor_logical_height =7575+ field->logical_maximum -7676+ field->logical_minimum;7777+ nd->sensor_physical_height =7878+ field->physical_maximum -7979+ field->physical_minimum;8080+ nd->activation_height = activation_height *8181+ nd->sensor_logical_height /8282+ nd->sensor_physical_height;8383+ nd->min_height = min_height *8484+ nd->sensor_logical_height /8585+ nd->sensor_physical_height;8686+ }42087 return 1;42188 }42289 return 0;···564201 if (nd->mt_foot_count != 4)565202 break;566203567567- /* Pen activity signal, trigger end of touch. */204204+ /* Pen activity signal. */568205 if (nd->mt_footer[2]) {206206+ /*207207+ * When the pen deactivates touch, we see a208208+ * bogus frame with ContactCount > 0.209209+ * We can210210+ * save a bit of work by ensuring act_state < 0211211+ * even if deactivation slack is turned off.212212+ */213213+ nd->act_state = deactivate_slack - 1;569214 nd->confidence = 0;570215 break;571216 }572217573573- /* If the contact was invalid */574574- if (!(nd->confidence && nd->mt_footer[0])575575- || nd->w <= 250576576- || nd->h <= 190) {577577- nd->confidence = 0;218218+ /*219219+ * The first footer value indicates the presence of a220220+ * finger.221221+ */222222+ if (nd->mt_footer[0]) {223223+ /*224224+ * We do not want to process contacts under225225+ * the size threshold, but do not want to226226+ * ignore them for activation state227227+ */228228+ if (nd->w < nd->min_width ||229229+ nd->h < nd->min_height)230230+ nd->confidence = 0;231231+ } else578232 break;233233+234234+ if (nd->act_state > 0) {235235+ /*236236+ * Contact meets the activation size threshold237237+ */238238+ if (nd->w >= nd->activation_width &&239239+ nd->h >= nd->activation_height) {240240+ if (nd->id)241241+ /*242242+ * first contact, activate now243243+ */244244+ nd->act_state = 0;245245+ else {246246+ /*247247+ * avoid corrupting this frame248248+ * but ensure next frame will249249+ * be active250250+ */251251+ nd->act_state = 1;252252+ break;253253+ }254254+ } else255255+ /*256256+ * Defer adjusting the activation state257257+ * until the end of the frame.258258+ */259259+ break;579260 }261261+262262+ /* Discarding this contact */263263+ if (!nd->confidence)264264+ break;580265581266 /* emit a normal (X, Y) for the first point only */582267 if (nd->id == 0) {···638227 input_event(input, EV_ABS, ABS_X, nd->x);639228 input_event(input, EV_ABS, ABS_Y, nd->y);640229 }230230+231231+ /* Emit MT events */641232 input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x);642233 input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y);234234+235235+ /*236236+ * Translate from height and width to size237237+ * and orientation.238238+ */643239 if (nd->w > nd->h) {644240 input_event(input, EV_ABS,645241 ABS_MT_ORIENTATION, 1);···666248 break;667249668250 case HID_DG_CONTACTCOUNT: /* End of a multitouch group */669669- if (!nd->reading_mt)251251+ if (!nd->reading_mt) /* Just to be sure */670252 break;671253672254 nd->reading_mt = 0;673255674674- if (nd->first_contact_touch) {256256+257257+ /*258258+ * Activation state machine logic:259259+ *260260+ * Fundamental states:261261+ * state > 0: Inactive262262+ * state <= 0: Active263263+ * state < -deactivate_slack:264264+ * Pen termination of touch265265+ *266266+ * Specific values of interest267267+ * state == activate_slack268268+ * no valid input since the last reset269269+ *270270+ * state == 0271271+ * general operational state272272+ *273273+ * state == -deactivate_slack274274+ * read sufficient empty frames to accept275275+ * the end of input and reset276276+ */277277+278278+ if (nd->act_state > 0) { /* Currently inactive */279279+ if (value)280280+ /*281281+ * Consider each live contact as282282+ * evidence of intentional activity.283283+ */284284+ nd->act_state = (nd->act_state > value)285285+ ? nd->act_state - value286286+ : 0;287287+ else288288+ /*289289+ * Empty frame before we hit the290290+ * activity threshold, reset.291291+ */292292+ nd->act_state = nd->activate_slack;293293+294294+ /*295295+ * Entered this block inactive and no296296+ * coordinates sent this frame, so hold off297297+ * on button state.298298+ */299299+ break;300300+ } else { /* Currently active */301301+ if (value && nd->act_state >=302302+ nd->deactivate_slack)303303+ /*304304+ * Live point: clear accumulated305305+ * deactivation count.306306+ */307307+ nd->act_state = 0;308308+ else if (nd->act_state <= nd->deactivate_slack)309309+ /*310310+ * We've consumed the deactivation311311+ * slack, time to deactivate and reset.312312+ */313313+ nd->act_state =314314+ nd->activate_slack;315315+ else { /* Move towards deactivation */316316+ nd->act_state--;317317+ break;318318+ }319319+ }320320+321321+ if (nd->first_contact_touch && nd->act_state <= 0) {322322+ /*323323+ * Check to see if we're ready to start324324+ * emitting touch events.325325+ *326326+ * Note: activation slack will decrease over327327+ * the course of the frame, and it will be328328+ * inconsistent from the start to the end of329329+ * the frame. However if the frame starts330330+ * with slack, first_contact_touch will still331331+ * be 0 and we will not get to this point.332332+ */675333 input_report_key(input, BTN_TOOL_DOUBLETAP, 1);676334 input_report_key(input, BTN_TOUCH, 1);677335 } else {···757263 break;758264759265 default:760760- /* fallback to the generic hidinput handling */266266+ /* fall-back to the generic hidinput handling */761267 return 0;762268 }763269 }···787293 }788294789295 nd->reading_mt = 0;296296+ nd->min_width = 0;297297+ nd->min_height = 0;298298+ nd->activate_slack = activate_slack;299299+ nd->act_state = activate_slack;300300+ nd->deactivate_slack = -deactivate_slack;301301+ nd->sensor_logical_width = 0;302302+ nd->sensor_logical_height = 0;303303+ nd->sensor_physical_width = 0;304304+ nd->sensor_physical_height = 0;305305+790306 hid_set_drvdata(hdev, nd);791307792308 ret = hid_parse(hdev);···848344 if (report)849345 usbhid_submit_report(hdev, report, USB_DIR_OUT);850346347347+ ret = sysfs_create_group(&hdev->dev.kobj,348348+ &ntrig_attribute_group);851349852350 return 0;853351err_free:···859353860354static void ntrig_remove(struct hid_device *hdev)861355{356356+ sysfs_remove_group(&hdev->dev.kobj,357357+ &ntrig_attribute_group);862358 hid_hw_stop(hdev);863359 kfree(hid_get_drvdata(hdev));864360}
+2631
drivers/hid/hid-picolcd.c
···11+/***************************************************************************22+ * Copyright (C) 2010 by Bruno Prémont <bonbons@linux-vserver.org> *33+ * *44+ * Based on Logitech G13 driver (v0.4) *55+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *66+ * *77+ * This program is free software: you can redistribute it and/or modify *88+ * it under the terms of the GNU General Public License as published by *99+ * the Free Software Foundation, version 2 of the License. *1010+ * *1111+ * This driver is distributed in the hope that it will be useful, but *1212+ * WITHOUT ANY WARRANTY; without even the implied warranty of *1313+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *1414+ * General Public License for more details. *1515+ * *1616+ * You should have received a copy of the GNU General Public License *1717+ * along with this software. If not see <http://www.gnu.org/licenses/>. *1818+ ***************************************************************************/1919+2020+#include <linux/hid.h>2121+#include <linux/hid-debug.h>2222+#include <linux/input.h>2323+#include "hid-ids.h"2424+#include "usbhid/usbhid.h"2525+#include <linux/usb.h>2626+2727+#include <linux/fb.h>2828+#include <linux/vmalloc.h>2929+#include <linux/backlight.h>3030+#include <linux/lcd.h>3131+3232+#include <linux/leds.h>3333+3434+#include <linux/seq_file.h>3535+#include <linux/debugfs.h>3636+3737+#include <linux/completion.h>3838+#include <linux/uaccess.h>3939+4040+#define PICOLCD_NAME "PicoLCD (graphic)"4141+4242+/* Report numbers */4343+#define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */4444+#define ERR_SUCCESS 0x004545+#define ERR_PARAMETER_MISSING 0x014646+#define ERR_DATA_MISSING 0x024747+#define ERR_BLOCK_READ_ONLY 0x034848+#define ERR_BLOCK_NOT_ERASABLE 0x044949+#define ERR_BLOCK_TOO_BIG 0x055050+#define ERR_SECTION_OVERFLOW 0x065151+#define ERR_INVALID_CMD_LEN 0x075252+#define ERR_INVALID_DATA_LEN 0x085353+#define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */5454+#define REPORT_IR_DATA 0x21 /* LCD: IN[63] */5555+#define REPORT_EE_DATA 0x32 /* LCD: IN[63] */5656+#define REPORT_MEMORY 0x41 /* LCD: IN[63] */5757+#define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */5858+#define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */5959+#define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */6060+#define REPORT_RESET 0x93 /* LCD: OUT[2] */6161+#define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */6262+#define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */6363+#define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */6464+#define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */6565+#define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */6666+#define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */6767+#define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */6868+#define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */6969+#define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */7070+#define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */7171+#define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */7272+#define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */7373+#define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */7474+#define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */7575+#define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */7676+#define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */7777+#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */7878+#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */7979+8080+#ifdef CONFIG_HID_PICOLCD_FB8181+/* Framebuffer8282+ *8383+ * The PicoLCD use a Topway LCD module of 256x64 pixel8484+ * This display area is tiled over 4 controllers with 8 tiles8585+ * each. Each tile has 8x64 pixel, each data byte representing8686+ * a 1-bit wide vertical line of the tile.8787+ *8888+ * The display can be updated at a tile granularity.8989+ *9090+ * Chip 1 Chip 2 Chip 3 Chip 49191+ * +----------------+----------------+----------------+----------------+9292+ * | Tile 1 | Tile 1 | Tile 1 | Tile 1 |9393+ * +----------------+----------------+----------------+----------------+9494+ * | Tile 2 | Tile 2 | Tile 2 | Tile 2 |9595+ * +----------------+----------------+----------------+----------------+9696+ * ...9797+ * +----------------+----------------+----------------+----------------+9898+ * | Tile 8 | Tile 8 | Tile 8 | Tile 8 |9999+ * +----------------+----------------+----------------+----------------+100100+ */101101+#define PICOLCDFB_NAME "picolcdfb"102102+#define PICOLCDFB_WIDTH (256)103103+#define PICOLCDFB_HEIGHT (64)104104+#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)105105+106106+#define PICOLCDFB_UPDATE_RATE_LIMIT 10107107+#define PICOLCDFB_UPDATE_RATE_DEFAULT 2108108+109109+/* Framebuffer visual structures */110110+static const struct fb_fix_screeninfo picolcdfb_fix = {111111+ .id = PICOLCDFB_NAME,112112+ .type = FB_TYPE_PACKED_PIXELS,113113+ .visual = FB_VISUAL_MONO01,114114+ .xpanstep = 0,115115+ .ypanstep = 0,116116+ .ywrapstep = 0,117117+ .line_length = PICOLCDFB_WIDTH / 8,118118+ .accel = FB_ACCEL_NONE,119119+};120120+121121+static const struct fb_var_screeninfo picolcdfb_var = {122122+ .xres = PICOLCDFB_WIDTH,123123+ .yres = PICOLCDFB_HEIGHT,124124+ .xres_virtual = PICOLCDFB_WIDTH,125125+ .yres_virtual = PICOLCDFB_HEIGHT,126126+ .width = 103,127127+ .height = 26,128128+ .bits_per_pixel = 1,129129+ .grayscale = 1,130130+};131131+#endif /* CONFIG_HID_PICOLCD_FB */132132+133133+/* Input device134134+ *135135+ * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys136136+ * and header for 4x4 key matrix. The built-in keys are part of the matrix.137137+ */138138+static const unsigned short def_keymap[] = {139139+ KEY_RESERVED, /* none */140140+ KEY_BACK, /* col 4 + row 1 */141141+ KEY_HOMEPAGE, /* col 3 + row 1 */142142+ KEY_RESERVED, /* col 2 + row 1 */143143+ KEY_RESERVED, /* col 1 + row 1 */144144+ KEY_SCROLLUP, /* col 4 + row 2 */145145+ KEY_OK, /* col 3 + row 2 */146146+ KEY_SCROLLDOWN, /* col 2 + row 2 */147147+ KEY_RESERVED, /* col 1 + row 2 */148148+ KEY_RESERVED, /* col 4 + row 3 */149149+ KEY_RESERVED, /* col 3 + row 3 */150150+ KEY_RESERVED, /* col 2 + row 3 */151151+ KEY_RESERVED, /* col 1 + row 3 */152152+ KEY_RESERVED, /* col 4 + row 4 */153153+ KEY_RESERVED, /* col 3 + row 4 */154154+ KEY_RESERVED, /* col 2 + row 4 */155155+ KEY_RESERVED, /* col 1 + row 4 */156156+};157157+#define PICOLCD_KEYS ARRAY_SIZE(def_keymap)158158+159159+/* Description of in-progress IO operation, used for operations160160+ * that trigger response from device */161161+struct picolcd_pending {162162+ struct hid_report *out_report;163163+ struct hid_report *in_report;164164+ struct completion ready;165165+ int raw_size;166166+ u8 raw_data[64];167167+};168168+169169+/* Per device data structure */170170+struct picolcd_data {171171+ struct hid_device *hdev;172172+#ifdef CONFIG_DEBUG_FS173173+ struct dentry *debug_reset;174174+ struct dentry *debug_eeprom;175175+ struct dentry *debug_flash;176176+ struct mutex mutex_flash;177177+ int addr_sz;178178+#endif179179+ u8 version[2];180180+ unsigned short opmode_delay;181181+ /* input stuff */182182+ u8 pressed_keys[2];183183+ struct input_dev *input_keys;184184+ struct input_dev *input_cir;185185+ unsigned short keycode[PICOLCD_KEYS];186186+187187+#ifdef CONFIG_HID_PICOLCD_FB188188+ /* Framebuffer stuff */189189+ u8 fb_update_rate;190190+ u8 fb_bpp;191191+ u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */192192+ u8 *fb_bitmap; /* framebuffer */193193+ struct fb_info *fb_info;194194+ struct fb_deferred_io fb_defio;195195+#endif /* CONFIG_HID_PICOLCD_FB */196196+#ifdef CONFIG_HID_PICOLCD_LCD197197+ struct lcd_device *lcd;198198+ u8 lcd_contrast;199199+#endif /* CONFIG_HID_PICOLCD_LCD */200200+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT201201+ struct backlight_device *backlight;202202+ u8 lcd_brightness;203203+ u8 lcd_power;204204+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */205205+#ifdef CONFIG_HID_PICOLCD_LEDS206206+ /* LED stuff */207207+ u8 led_state;208208+ struct led_classdev *led[8];209209+#endif /* CONFIG_HID_PICOLCD_LEDS */210210+211211+ /* Housekeeping stuff */212212+ spinlock_t lock;213213+ struct mutex mutex;214214+ struct picolcd_pending *pending;215215+ int status;216216+#define PICOLCD_BOOTLOADER 1217217+#define PICOLCD_FAILED 2218218+#define PICOLCD_READY_FB 4219219+};220220+221221+222222+/* Find a given report */223223+#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)224224+#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)225225+226226+static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)227227+{228228+ struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;229229+ struct hid_report *report = NULL;230230+231231+ list_for_each_entry(report, feature_report_list, list) {232232+ if (report->id == id)233233+ return report;234234+ }235235+ dev_warn(&hdev->dev, "No report with id 0x%x found\n", id);236236+ return NULL;237237+}238238+239239+#ifdef CONFIG_DEBUG_FS240240+static void picolcd_debug_out_report(struct picolcd_data *data,241241+ struct hid_device *hdev, struct hid_report *report);242242+#define usbhid_submit_report(a, b, c) \243243+ do { \244244+ picolcd_debug_out_report(hid_get_drvdata(a), a, b); \245245+ usbhid_submit_report(a, b, c); \246246+ } while (0)247247+#endif248248+249249+/* Submit a report and wait for a reply from device - if device fades away250250+ * or does not respond in time, return NULL */251251+static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,252252+ int report_id, const u8 *raw_data, int size)253253+{254254+ struct picolcd_data *data = hid_get_drvdata(hdev);255255+ struct picolcd_pending *work;256256+ struct hid_report *report = picolcd_out_report(report_id, hdev);257257+ unsigned long flags;258258+ int i, j, k;259259+260260+ if (!report || !data)261261+ return NULL;262262+ if (data->status & PICOLCD_FAILED)263263+ return NULL;264264+ work = kzalloc(sizeof(*work), GFP_KERNEL);265265+ if (!work)266266+ return NULL;267267+268268+ init_completion(&work->ready);269269+ work->out_report = report;270270+ work->in_report = NULL;271271+ work->raw_size = 0;272272+273273+ mutex_lock(&data->mutex);274274+ spin_lock_irqsave(&data->lock, flags);275275+ for (i = k = 0; i < report->maxfield; i++)276276+ for (j = 0; j < report->field[i]->report_count; j++) {277277+ hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);278278+ k++;279279+ }280280+ data->pending = work;281281+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);282282+ spin_unlock_irqrestore(&data->lock, flags);283283+ wait_for_completion_interruptible_timeout(&work->ready, HZ*2);284284+ spin_lock_irqsave(&data->lock, flags);285285+ data->pending = NULL;286286+ spin_unlock_irqrestore(&data->lock, flags);287287+ mutex_unlock(&data->mutex);288288+ return work;289289+}290290+291291+#ifdef CONFIG_HID_PICOLCD_FB292292+/* Send a given tile to PicoLCD */293293+static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile)294294+{295295+ struct picolcd_data *data = hid_get_drvdata(hdev);296296+ struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev);297297+ struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev);298298+ unsigned long flags;299299+ u8 *tdata;300300+ int i;301301+302302+ if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1)303303+ return -ENODEV;304304+305305+ spin_lock_irqsave(&data->lock, flags);306306+ hid_set_field(report1->field[0], 0, chip << 2);307307+ hid_set_field(report1->field[0], 1, 0x02);308308+ hid_set_field(report1->field[0], 2, 0x00);309309+ hid_set_field(report1->field[0], 3, 0x00);310310+ hid_set_field(report1->field[0], 4, 0xb8 | tile);311311+ hid_set_field(report1->field[0], 5, 0x00);312312+ hid_set_field(report1->field[0], 6, 0x00);313313+ hid_set_field(report1->field[0], 7, 0x40);314314+ hid_set_field(report1->field[0], 8, 0x00);315315+ hid_set_field(report1->field[0], 9, 0x00);316316+ hid_set_field(report1->field[0], 10, 32);317317+318318+ hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);319319+ hid_set_field(report2->field[0], 1, 0x00);320320+ hid_set_field(report2->field[0], 2, 0x00);321321+ hid_set_field(report2->field[0], 3, 32);322322+323323+ tdata = data->fb_vbitmap + (tile * 4 + chip) * 64;324324+ for (i = 0; i < 64; i++)325325+ if (i < 32)326326+ hid_set_field(report1->field[0], 11 + i, tdata[i]);327327+ else328328+ hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);329329+330330+ usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);331331+ usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);332332+ spin_unlock_irqrestore(&data->lock, flags);333333+ return 0;334334+}335335+336336+/* Translate a single tile*/337337+static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,338338+ int chip, int tile)339339+{340340+ int i, b, changed = 0;341341+ u8 tdata[64];342342+ u8 *vdata = vbitmap + (tile * 4 + chip) * 64;343343+344344+ if (bpp == 1) {345345+ for (b = 7; b >= 0; b--) {346346+ const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;347347+ for (i = 0; i < 64; i++) {348348+ tdata[i] <<= 1;349349+ tdata[i] |= (bdata[i/8] >> (7 - i % 8)) & 0x01;350350+ }351351+ }352352+ } else if (bpp == 8) {353353+ for (b = 7; b >= 0; b--) {354354+ const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;355355+ for (i = 0; i < 64; i++) {356356+ tdata[i] <<= 1;357357+ tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;358358+ }359359+ }360360+ } else {361361+ /* Oops, we should never get here! */362362+ WARN_ON(1);363363+ return 0;364364+ }365365+366366+ for (i = 0; i < 64; i++)367367+ if (tdata[i] != vdata[i]) {368368+ changed = 1;369369+ vdata[i] = tdata[i];370370+ }371371+ return changed;372372+}373373+374374+/* Reconfigure LCD display */375375+static int picolcd_fb_reset(struct picolcd_data *data, int clear)376376+{377377+ struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);378378+ int i, j;379379+ unsigned long flags;380380+ static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };381381+382382+ if (!report || report->maxfield != 1)383383+ return -ENODEV;384384+385385+ spin_lock_irqsave(&data->lock, flags);386386+ for (i = 0; i < 4; i++) {387387+ for (j = 0; j < report->field[0]->maxusage; j++)388388+ if (j == 0)389389+ hid_set_field(report->field[0], j, i << 2);390390+ else if (j < sizeof(mapcmd))391391+ hid_set_field(report->field[0], j, mapcmd[j]);392392+ else393393+ hid_set_field(report->field[0], j, 0);394394+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);395395+ }396396+397397+ data->status |= PICOLCD_READY_FB;398398+ spin_unlock_irqrestore(&data->lock, flags);399399+400400+ if (data->fb_bitmap) {401401+ if (clear) {402402+ memset(data->fb_vbitmap, 0xff, PICOLCDFB_SIZE);403403+ memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp);404404+ } else {405405+ /* invert 1 byte in each tile to force resend */406406+ for (i = 0; i < PICOLCDFB_SIZE; i += 64)407407+ data->fb_vbitmap[i] = ~data->fb_vbitmap[i];408408+ }409409+ }410410+411411+ /* schedule first output of framebuffer */412412+ if (data->fb_info)413413+ schedule_delayed_work(&data->fb_info->deferred_work, 0);414414+415415+ return 0;416416+}417417+418418+/* Update fb_vbitmap from the screen_base and send changed tiles to device */419419+static void picolcd_fb_update(struct picolcd_data *data)420420+{421421+ int chip, tile, n;422422+ unsigned long flags;423423+424424+ spin_lock_irqsave(&data->lock, flags);425425+ if (!(data->status & PICOLCD_READY_FB)) {426426+ spin_unlock_irqrestore(&data->lock, flags);427427+ picolcd_fb_reset(data, 0);428428+ } else {429429+ spin_unlock_irqrestore(&data->lock, flags);430430+ }431431+432432+ /*433433+ * Translate the framebuffer into the format needed by the PicoLCD.434434+ * See display layout above.435435+ * Do this one tile after the other and push those tiles that changed.436436+ *437437+ * Wait for our IO to complete as otherwise we might flood the queue!438438+ */439439+ n = 0;440440+ for (chip = 0; chip < 4; chip++)441441+ for (tile = 0; tile < 8; tile++)442442+ if (picolcd_fb_update_tile(data->fb_vbitmap,443443+ data->fb_bitmap, data->fb_bpp, chip, tile)) {444444+ n += 2;445445+ if (n >= HID_OUTPUT_FIFO_SIZE / 2) {446446+ usbhid_wait_io(data->hdev);447447+ n = 0;448448+ }449449+ picolcd_fb_send_tile(data->hdev, chip, tile);450450+ }451451+ if (n)452452+ usbhid_wait_io(data->hdev);453453+}454454+455455+/* Stub to call the system default and update the image on the picoLCD */456456+static void picolcd_fb_fillrect(struct fb_info *info,457457+ const struct fb_fillrect *rect)458458+{459459+ if (!info->par)460460+ return;461461+ sys_fillrect(info, rect);462462+463463+ schedule_delayed_work(&info->deferred_work, 0);464464+}465465+466466+/* Stub to call the system default and update the image on the picoLCD */467467+static void picolcd_fb_copyarea(struct fb_info *info,468468+ const struct fb_copyarea *area)469469+{470470+ if (!info->par)471471+ return;472472+ sys_copyarea(info, area);473473+474474+ schedule_delayed_work(&info->deferred_work, 0);475475+}476476+477477+/* Stub to call the system default and update the image on the picoLCD */478478+static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)479479+{480480+ if (!info->par)481481+ return;482482+ sys_imageblit(info, image);483483+484484+ schedule_delayed_work(&info->deferred_work, 0);485485+}486486+487487+/*488488+ * this is the slow path from userspace. they can seek and write to489489+ * the fb. it's inefficient to do anything less than a full screen draw490490+ */491491+static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,492492+ size_t count, loff_t *ppos)493493+{494494+ ssize_t ret;495495+ if (!info->par)496496+ return -ENODEV;497497+ ret = fb_sys_write(info, buf, count, ppos);498498+ if (ret >= 0)499499+ schedule_delayed_work(&info->deferred_work, 0);500500+ return ret;501501+}502502+503503+static int picolcd_fb_blank(int blank, struct fb_info *info)504504+{505505+ if (!info->par)506506+ return -ENODEV;507507+ /* We let fb notification do this for us via lcd/backlight device */508508+ return 0;509509+}510510+511511+static void picolcd_fb_destroy(struct fb_info *info)512512+{513513+ struct picolcd_data *data = info->par;514514+ info->par = NULL;515515+ if (data)516516+ data->fb_info = NULL;517517+ fb_deferred_io_cleanup(info);518518+ framebuffer_release(info);519519+}520520+521521+static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)522522+{523523+ __u32 bpp = var->bits_per_pixel;524524+ __u32 activate = var->activate;525525+526526+ /* only allow 1/8 bit depth (8-bit is grayscale) */527527+ *var = picolcdfb_var;528528+ var->activate = activate;529529+ if (bpp >= 8)530530+ var->bits_per_pixel = 8;531531+ else532532+ var->bits_per_pixel = 1;533533+ return 0;534534+}535535+536536+static int picolcd_set_par(struct fb_info *info)537537+{538538+ struct picolcd_data *data = info->par;539539+ u8 *o_fb, *n_fb;540540+ if (info->var.bits_per_pixel == data->fb_bpp)541541+ return 0;542542+ /* switch between 1/8 bit depths */543543+ if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)544544+ return -EINVAL;545545+546546+ o_fb = data->fb_bitmap;547547+ n_fb = vmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel);548548+ if (!n_fb)549549+ return -ENOMEM;550550+551551+ fb_deferred_io_cleanup(info);552552+ /* translate FB content to new bits-per-pixel */553553+ if (info->var.bits_per_pixel == 1) {554554+ int i, b;555555+ for (i = 0; i < PICOLCDFB_SIZE; i++) {556556+ u8 p = 0;557557+ for (b = 0; b < 8; b++) {558558+ p <<= 1;559559+ p |= o_fb[i*8+b] ? 0x01 : 0x00;560560+ }561561+ }562562+ info->fix.visual = FB_VISUAL_MONO01;563563+ info->fix.line_length = PICOLCDFB_WIDTH / 8;564564+ } else {565565+ int i;566566+ for (i = 0; i < PICOLCDFB_SIZE * 8; i++)567567+ n_fb[i] = o_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;568568+ info->fix.visual = FB_VISUAL_TRUECOLOR;569569+ info->fix.line_length = PICOLCDFB_WIDTH;570570+ }571571+572572+ data->fb_bitmap = n_fb;573573+ data->fb_bpp = info->var.bits_per_pixel;574574+ info->screen_base = (char __force __iomem *)n_fb;575575+ info->fix.smem_start = (unsigned long)n_fb;576576+ info->fix.smem_len = PICOLCDFB_SIZE*data->fb_bpp;577577+ fb_deferred_io_init(info);578578+ vfree(o_fb);579579+ return 0;580580+}581581+582582+/* Note this can't be const because of struct fb_info definition */583583+static struct fb_ops picolcdfb_ops = {584584+ .owner = THIS_MODULE,585585+ .fb_destroy = picolcd_fb_destroy,586586+ .fb_read = fb_sys_read,587587+ .fb_write = picolcd_fb_write,588588+ .fb_blank = picolcd_fb_blank,589589+ .fb_fillrect = picolcd_fb_fillrect,590590+ .fb_copyarea = picolcd_fb_copyarea,591591+ .fb_imageblit = picolcd_fb_imageblit,592592+ .fb_check_var = picolcd_fb_check_var,593593+ .fb_set_par = picolcd_set_par,594594+};595595+596596+597597+/* Callback from deferred IO workqueue */598598+static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)599599+{600600+ picolcd_fb_update(info->par);601601+}602602+603603+static const struct fb_deferred_io picolcd_fb_defio = {604604+ .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,605605+ .deferred_io = picolcd_fb_deferred_io,606606+};607607+608608+609609+/*610610+ * The "fb_update_rate" sysfs attribute611611+ */612612+static ssize_t picolcd_fb_update_rate_show(struct device *dev,613613+ struct device_attribute *attr, char *buf)614614+{615615+ struct picolcd_data *data = dev_get_drvdata(dev);616616+ unsigned i, fb_update_rate = data->fb_update_rate;617617+ size_t ret = 0;618618+619619+ for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)620620+ if (ret >= PAGE_SIZE)621621+ break;622622+ else if (i == fb_update_rate)623623+ ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);624624+ else625625+ ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);626626+ if (ret > 0)627627+ buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';628628+ return ret;629629+}630630+631631+static ssize_t picolcd_fb_update_rate_store(struct device *dev,632632+ struct device_attribute *attr, const char *buf, size_t count)633633+{634634+ struct picolcd_data *data = dev_get_drvdata(dev);635635+ int i;636636+ unsigned u;637637+638638+ if (count < 1 || count > 10)639639+ return -EINVAL;640640+641641+ i = sscanf(buf, "%u", &u);642642+ if (i != 1)643643+ return -EINVAL;644644+645645+ if (u > PICOLCDFB_UPDATE_RATE_LIMIT)646646+ return -ERANGE;647647+ else if (u == 0)648648+ u = PICOLCDFB_UPDATE_RATE_DEFAULT;649649+650650+ data->fb_update_rate = u;651651+ data->fb_defio.delay = HZ / data->fb_update_rate;652652+ return count;653653+}654654+655655+static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,656656+ picolcd_fb_update_rate_store);657657+658658+/* initialize Framebuffer device */659659+static int picolcd_init_framebuffer(struct picolcd_data *data)660660+{661661+ struct device *dev = &data->hdev->dev;662662+ struct fb_info *info = NULL;663663+ int error = -ENOMEM;664664+ u8 *fb_vbitmap = NULL;665665+ u8 *fb_bitmap = NULL;666666+667667+ fb_bitmap = vmalloc(PICOLCDFB_SIZE*picolcdfb_var.bits_per_pixel);668668+ if (fb_bitmap == NULL) {669669+ dev_err(dev, "can't get a free page for framebuffer\n");670670+ goto err_nomem;671671+ }672672+673673+ fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL);674674+ if (fb_vbitmap == NULL) {675675+ dev_err(dev, "can't alloc vbitmap image buffer\n");676676+ goto err_nomem;677677+ }678678+679679+ data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;680680+ data->fb_defio = picolcd_fb_defio;681681+ info = framebuffer_alloc(0, dev);682682+ if (info == NULL) {683683+ dev_err(dev, "failed to allocate a framebuffer\n");684684+ goto err_nomem;685685+ }686686+687687+ info->fbdefio = &data->fb_defio;688688+ info->screen_base = (char __force __iomem *)fb_bitmap;689689+ info->fbops = &picolcdfb_ops;690690+ info->var = picolcdfb_var;691691+ info->fix = picolcdfb_fix;692692+ info->fix.smem_len = PICOLCDFB_SIZE;693693+ info->fix.smem_start = (unsigned long)fb_bitmap;694694+ info->par = data;695695+ info->flags = FBINFO_FLAG_DEFAULT;696696+697697+ data->fb_vbitmap = fb_vbitmap;698698+ data->fb_bitmap = fb_bitmap;699699+ data->fb_bpp = picolcdfb_var.bits_per_pixel;700700+ error = picolcd_fb_reset(data, 1);701701+ if (error) {702702+ dev_err(dev, "failed to configure display\n");703703+ goto err_cleanup;704704+ }705705+ error = device_create_file(dev, &dev_attr_fb_update_rate);706706+ if (error) {707707+ dev_err(dev, "failed to create sysfs attributes\n");708708+ goto err_cleanup;709709+ }710710+ data->fb_info = info;711711+ error = register_framebuffer(info);712712+ if (error) {713713+ dev_err(dev, "failed to register framebuffer\n");714714+ goto err_sysfs;715715+ }716716+ fb_deferred_io_init(info);717717+ /* schedule first output of framebuffer */718718+ schedule_delayed_work(&info->deferred_work, 0);719719+ return 0;720720+721721+err_sysfs:722722+ device_remove_file(dev, &dev_attr_fb_update_rate);723723+err_cleanup:724724+ data->fb_vbitmap = NULL;725725+ data->fb_bitmap = NULL;726726+ data->fb_bpp = 0;727727+ data->fb_info = NULL;728728+729729+err_nomem:730730+ framebuffer_release(info);731731+ vfree(fb_bitmap);732732+ kfree(fb_vbitmap);733733+ return error;734734+}735735+736736+static void picolcd_exit_framebuffer(struct picolcd_data *data)737737+{738738+ struct fb_info *info = data->fb_info;739739+ u8 *fb_vbitmap = data->fb_vbitmap;740740+ u8 *fb_bitmap = data->fb_bitmap;741741+742742+ if (!info)743743+ return;744744+745745+ data->fb_vbitmap = NULL;746746+ data->fb_bitmap = NULL;747747+ data->fb_bpp = 0;748748+ data->fb_info = NULL;749749+ device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);750750+ fb_deferred_io_cleanup(info);751751+ unregister_framebuffer(info);752752+ vfree(fb_bitmap);753753+ kfree(fb_vbitmap);754754+}755755+756756+#define picolcd_fbinfo(d) ((d)->fb_info)757757+#else758758+static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)759759+{760760+ return 0;761761+}762762+static inline int picolcd_init_framebuffer(struct picolcd_data *data)763763+{764764+ return 0;765765+}766766+static inline void picolcd_exit_framebuffer(struct picolcd_data *data)767767+{768768+}769769+#define picolcd_fbinfo(d) NULL770770+#endif /* CONFIG_HID_PICOLCD_FB */771771+772772+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT773773+/*774774+ * backlight class device775775+ */776776+static int picolcd_get_brightness(struct backlight_device *bdev)777777+{778778+ struct picolcd_data *data = bl_get_data(bdev);779779+ return data->lcd_brightness;780780+}781781+782782+static int picolcd_set_brightness(struct backlight_device *bdev)783783+{784784+ struct picolcd_data *data = bl_get_data(bdev);785785+ struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);786786+ unsigned long flags;787787+788788+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)789789+ return -ENODEV;790790+791791+ data->lcd_brightness = bdev->props.brightness & 0x0ff;792792+ data->lcd_power = bdev->props.power;793793+ spin_lock_irqsave(&data->lock, flags);794794+ hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);795795+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);796796+ spin_unlock_irqrestore(&data->lock, flags);797797+ return 0;798798+}799799+800800+static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)801801+{802802+ return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));803803+}804804+805805+static const struct backlight_ops picolcd_blops = {806806+ .update_status = picolcd_set_brightness,807807+ .get_brightness = picolcd_get_brightness,808808+ .check_fb = picolcd_check_bl_fb,809809+};810810+811811+static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)812812+{813813+ struct device *dev = &data->hdev->dev;814814+ struct backlight_device *bdev;815815+ struct backlight_properties props;816816+ if (!report)817817+ return -ENODEV;818818+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||819819+ report->field[0]->report_size != 8) {820820+ dev_err(dev, "unsupported BRIGHTNESS report");821821+ return -EINVAL;822822+ }823823+824824+ memset(&props, 0, sizeof(props));825825+ props.max_brightness = 0xff;826826+ bdev = backlight_device_register(dev_name(dev), dev, data,827827+ &picolcd_blops, &props);828828+ if (IS_ERR(bdev)) {829829+ dev_err(dev, "failed to register backlight\n");830830+ return PTR_ERR(bdev);831831+ }832832+ bdev->props.brightness = 0xff;833833+ data->lcd_brightness = 0xff;834834+ data->backlight = bdev;835835+ picolcd_set_brightness(bdev);836836+ return 0;837837+}838838+839839+static void picolcd_exit_backlight(struct picolcd_data *data)840840+{841841+ struct backlight_device *bdev = data->backlight;842842+843843+ data->backlight = NULL;844844+ if (bdev)845845+ backlight_device_unregister(bdev);846846+}847847+848848+static inline int picolcd_resume_backlight(struct picolcd_data *data)849849+{850850+ if (!data->backlight)851851+ return 0;852852+ return picolcd_set_brightness(data->backlight);853853+}854854+855855+#ifdef CONFIG_PM856856+static void picolcd_suspend_backlight(struct picolcd_data *data)857857+{858858+ int bl_power = data->lcd_power;859859+ if (!data->backlight)860860+ return;861861+862862+ data->backlight->props.power = FB_BLANK_POWERDOWN;863863+ picolcd_set_brightness(data->backlight);864864+ data->lcd_power = data->backlight->props.power = bl_power;865865+}866866+#endif /* CONFIG_PM */867867+#else868868+static inline int picolcd_init_backlight(struct picolcd_data *data,869869+ struct hid_report *report)870870+{871871+ return 0;872872+}873873+static inline void picolcd_exit_backlight(struct picolcd_data *data)874874+{875875+}876876+static inline int picolcd_resume_backlight(struct picolcd_data *data)877877+{878878+ return 0;879879+}880880+static inline void picolcd_suspend_backlight(struct picolcd_data *data)881881+{882882+}883883+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */884884+885885+#ifdef CONFIG_HID_PICOLCD_LCD886886+/*887887+ * lcd class device888888+ */889889+static int picolcd_get_contrast(struct lcd_device *ldev)890890+{891891+ struct picolcd_data *data = lcd_get_data(ldev);892892+ return data->lcd_contrast;893893+}894894+895895+static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)896896+{897897+ struct picolcd_data *data = lcd_get_data(ldev);898898+ struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);899899+ unsigned long flags;900900+901901+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)902902+ return -ENODEV;903903+904904+ data->lcd_contrast = contrast & 0x0ff;905905+ spin_lock_irqsave(&data->lock, flags);906906+ hid_set_field(report->field[0], 0, data->lcd_contrast);907907+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);908908+ spin_unlock_irqrestore(&data->lock, flags);909909+ return 0;910910+}911911+912912+static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)913913+{914914+ return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));915915+}916916+917917+static struct lcd_ops picolcd_lcdops = {918918+ .get_contrast = picolcd_get_contrast,919919+ .set_contrast = picolcd_set_contrast,920920+ .check_fb = picolcd_check_lcd_fb,921921+};922922+923923+static int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)924924+{925925+ struct device *dev = &data->hdev->dev;926926+ struct lcd_device *ldev;927927+928928+ if (!report)929929+ return -ENODEV;930930+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||931931+ report->field[0]->report_size != 8) {932932+ dev_err(dev, "unsupported CONTRAST report");933933+ return -EINVAL;934934+ }935935+936936+ ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);937937+ if (IS_ERR(ldev)) {938938+ dev_err(dev, "failed to register LCD\n");939939+ return PTR_ERR(ldev);940940+ }941941+ ldev->props.max_contrast = 0x0ff;942942+ data->lcd_contrast = 0xe5;943943+ data->lcd = ldev;944944+ picolcd_set_contrast(ldev, 0xe5);945945+ return 0;946946+}947947+948948+static void picolcd_exit_lcd(struct picolcd_data *data)949949+{950950+ struct lcd_device *ldev = data->lcd;951951+952952+ data->lcd = NULL;953953+ if (ldev)954954+ lcd_device_unregister(ldev);955955+}956956+957957+static inline int picolcd_resume_lcd(struct picolcd_data *data)958958+{959959+ if (!data->lcd)960960+ return 0;961961+ return picolcd_set_contrast(data->lcd, data->lcd_contrast);962962+}963963+#else964964+static inline int picolcd_init_lcd(struct picolcd_data *data,965965+ struct hid_report *report)966966+{967967+ return 0;968968+}969969+static inline void picolcd_exit_lcd(struct picolcd_data *data)970970+{971971+}972972+static inline int picolcd_resume_lcd(struct picolcd_data *data)973973+{974974+ return 0;975975+}976976+#endif /* CONFIG_HID_PICOLCD_LCD */977977+978978+#ifdef CONFIG_HID_PICOLCD_LEDS979979+/**980980+ * LED class device981981+ */982982+static void picolcd_leds_set(struct picolcd_data *data)983983+{984984+ struct hid_report *report;985985+ unsigned long flags;986986+987987+ if (!data->led[0])988988+ return;989989+ report = picolcd_out_report(REPORT_LED_STATE, data->hdev);990990+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)991991+ return;992992+993993+ spin_lock_irqsave(&data->lock, flags);994994+ hid_set_field(report->field[0], 0, data->led_state);995995+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);996996+ spin_unlock_irqrestore(&data->lock, flags);997997+}998998+999999+static void picolcd_led_set_brightness(struct led_classdev *led_cdev,10001000+ enum led_brightness value)10011001+{10021002+ struct device *dev;10031003+ struct hid_device *hdev;10041004+ struct picolcd_data *data;10051005+ int i, state = 0;10061006+10071007+ dev = led_cdev->dev->parent;10081008+ hdev = container_of(dev, struct hid_device, dev);10091009+ data = hid_get_drvdata(hdev);10101010+ for (i = 0; i < 8; i++) {10111011+ if (led_cdev != data->led[i])10121012+ continue;10131013+ state = (data->led_state >> i) & 1;10141014+ if (value == LED_OFF && state) {10151015+ data->led_state &= ~(1 << i);10161016+ picolcd_leds_set(data);10171017+ } else if (value != LED_OFF && !state) {10181018+ data->led_state |= 1 << i;10191019+ picolcd_leds_set(data);10201020+ }10211021+ break;10221022+ }10231023+}10241024+10251025+static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)10261026+{10271027+ struct device *dev;10281028+ struct hid_device *hdev;10291029+ struct picolcd_data *data;10301030+ int i, value = 0;10311031+10321032+ dev = led_cdev->dev->parent;10331033+ hdev = container_of(dev, struct hid_device, dev);10341034+ data = hid_get_drvdata(hdev);10351035+ for (i = 0; i < 8; i++)10361036+ if (led_cdev == data->led[i]) {10371037+ value = (data->led_state >> i) & 1;10381038+ break;10391039+ }10401040+ return value ? LED_FULL : LED_OFF;10411041+}10421042+10431043+static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)10441044+{10451045+ struct device *dev = &data->hdev->dev;10461046+ struct led_classdev *led;10471047+ size_t name_sz = strlen(dev_name(dev)) + 8;10481048+ char *name;10491049+ int i, ret = 0;10501050+10511051+ if (!report)10521052+ return -ENODEV;10531053+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||10541054+ report->field[0]->report_size != 8) {10551055+ dev_err(dev, "unsupported LED_STATE report");10561056+ return -EINVAL;10571057+ }10581058+10591059+ for (i = 0; i < 8; i++) {10601060+ led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);10611061+ if (!led) {10621062+ dev_err(dev, "can't allocate memory for LED %d\n", i);10631063+ ret = -ENOMEM;10641064+ goto err;10651065+ }10661066+ name = (void *)(&led[1]);10671067+ snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);10681068+ led->name = name;10691069+ led->brightness = 0;10701070+ led->max_brightness = 1;10711071+ led->brightness_get = picolcd_led_get_brightness;10721072+ led->brightness_set = picolcd_led_set_brightness;10731073+10741074+ data->led[i] = led;10751075+ ret = led_classdev_register(dev, data->led[i]);10761076+ if (ret) {10771077+ data->led[i] = NULL;10781078+ kfree(led);10791079+ dev_err(dev, "can't register LED %d\n", i);10801080+ goto err;10811081+ }10821082+ }10831083+ return 0;10841084+err:10851085+ for (i = 0; i < 8; i++)10861086+ if (data->led[i]) {10871087+ led = data->led[i];10881088+ data->led[i] = NULL;10891089+ led_classdev_unregister(led);10901090+ kfree(led);10911091+ }10921092+ return ret;10931093+}10941094+10951095+static void picolcd_exit_leds(struct picolcd_data *data)10961096+{10971097+ struct led_classdev *led;10981098+ int i;10991099+11001100+ for (i = 0; i < 8; i++) {11011101+ led = data->led[i];11021102+ data->led[i] = NULL;11031103+ if (!led)11041104+ continue;11051105+ led_classdev_unregister(led);11061106+ kfree(led);11071107+ }11081108+}11091109+11101110+#else11111111+static inline int picolcd_init_leds(struct picolcd_data *data,11121112+ struct hid_report *report)11131113+{11141114+ return 0;11151115+}11161116+static inline void picolcd_exit_leds(struct picolcd_data *data)11171117+{11181118+}11191119+static inline int picolcd_leds_set(struct picolcd_data *data)11201120+{11211121+ return 0;11221122+}11231123+#endif /* CONFIG_HID_PICOLCD_LEDS */11241124+11251125+/*11261126+ * input class device11271127+ */11281128+static int picolcd_raw_keypad(struct picolcd_data *data,11291129+ struct hid_report *report, u8 *raw_data, int size)11301130+{11311131+ /*11321132+ * Keypad event11331133+ * First and second data bytes list currently pressed keys,11341134+ * 0x00 means no key and at most 2 keys may be pressed at same time11351135+ */11361136+ int i, j;11371137+11381138+ /* determine newly pressed keys */11391139+ for (i = 0; i < size; i++) {11401140+ unsigned int key_code;11411141+ if (raw_data[i] == 0)11421142+ continue;11431143+ for (j = 0; j < sizeof(data->pressed_keys); j++)11441144+ if (data->pressed_keys[j] == raw_data[i])11451145+ goto key_already_down;11461146+ for (j = 0; j < sizeof(data->pressed_keys); j++)11471147+ if (data->pressed_keys[j] == 0) {11481148+ data->pressed_keys[j] = raw_data[i];11491149+ break;11501150+ }11511151+ input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);11521152+ if (raw_data[i] < PICOLCD_KEYS)11531153+ key_code = data->keycode[raw_data[i]];11541154+ else11551155+ key_code = KEY_UNKNOWN;11561156+ if (key_code != KEY_UNKNOWN) {11571157+ dbg_hid(PICOLCD_NAME " got key press for %u:%d",11581158+ raw_data[i], key_code);11591159+ input_report_key(data->input_keys, key_code, 1);11601160+ }11611161+ input_sync(data->input_keys);11621162+key_already_down:11631163+ continue;11641164+ }11651165+11661166+ /* determine newly released keys */11671167+ for (j = 0; j < sizeof(data->pressed_keys); j++) {11681168+ unsigned int key_code;11691169+ if (data->pressed_keys[j] == 0)11701170+ continue;11711171+ for (i = 0; i < size; i++)11721172+ if (data->pressed_keys[j] == raw_data[i])11731173+ goto key_still_down;11741174+ input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);11751175+ if (data->pressed_keys[j] < PICOLCD_KEYS)11761176+ key_code = data->keycode[data->pressed_keys[j]];11771177+ else11781178+ key_code = KEY_UNKNOWN;11791179+ if (key_code != KEY_UNKNOWN) {11801180+ dbg_hid(PICOLCD_NAME " got key release for %u:%d",11811181+ data->pressed_keys[j], key_code);11821182+ input_report_key(data->input_keys, key_code, 0);11831183+ }11841184+ input_sync(data->input_keys);11851185+ data->pressed_keys[j] = 0;11861186+key_still_down:11871187+ continue;11881188+ }11891189+ return 1;11901190+}11911191+11921192+static int picolcd_raw_cir(struct picolcd_data *data,11931193+ struct hid_report *report, u8 *raw_data, int size)11941194+{11951195+ /* Need understanding of CIR data format to implement ... */11961196+ return 1;11971197+}11981198+11991199+static int picolcd_check_version(struct hid_device *hdev)12001200+{12011201+ struct picolcd_data *data = hid_get_drvdata(hdev);12021202+ struct picolcd_pending *verinfo;12031203+ int ret = 0;12041204+12051205+ if (!data)12061206+ return -ENODEV;12071207+12081208+ verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);12091209+ if (!verinfo) {12101210+ dev_err(&hdev->dev, "no version response from PicoLCD");12111211+ return -ENODEV;12121212+ }12131213+12141214+ if (verinfo->raw_size == 2) {12151215+ data->version[0] = verinfo->raw_data[1];12161216+ data->version[1] = verinfo->raw_data[0];12171217+ if (data->status & PICOLCD_BOOTLOADER) {12181218+ dev_info(&hdev->dev, "PicoLCD, bootloader version %d.%d\n",12191219+ verinfo->raw_data[1], verinfo->raw_data[0]);12201220+ } else {12211221+ dev_info(&hdev->dev, "PicoLCD, firmware version %d.%d\n",12221222+ verinfo->raw_data[1], verinfo->raw_data[0]);12231223+ }12241224+ } else {12251225+ dev_err(&hdev->dev, "confused, got unexpected version response from PicoLCD\n");12261226+ ret = -EINVAL;12271227+ }12281228+ kfree(verinfo);12291229+ return ret;12301230+}12311231+12321232+/*12331233+ * Reset our device and wait for answer to VERSION request12341234+ */12351235+static int picolcd_reset(struct hid_device *hdev)12361236+{12371237+ struct picolcd_data *data = hid_get_drvdata(hdev);12381238+ struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);12391239+ unsigned long flags;12401240+ int error;12411241+12421242+ if (!data || !report || report->maxfield != 1)12431243+ return -ENODEV;12441244+12451245+ spin_lock_irqsave(&data->lock, flags);12461246+ if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)12471247+ data->status |= PICOLCD_BOOTLOADER;12481248+12491249+ /* perform the reset */12501250+ hid_set_field(report->field[0], 0, 1);12511251+ usbhid_submit_report(hdev, report, USB_DIR_OUT);12521252+ spin_unlock_irqrestore(&data->lock, flags);12531253+12541254+ error = picolcd_check_version(hdev);12551255+ if (error)12561256+ return error;12571257+12581258+ picolcd_resume_lcd(data);12591259+ picolcd_resume_backlight(data);12601260+#ifdef CONFIG_HID_PICOLCD_FB12611261+ if (data->fb_info)12621262+ schedule_delayed_work(&data->fb_info->deferred_work, 0);12631263+#endif /* CONFIG_HID_PICOLCD_FB */12641264+12651265+ picolcd_leds_set(data);12661266+ return 0;12671267+}12681268+12691269+/*12701270+ * The "operation_mode" sysfs attribute12711271+ */12721272+static ssize_t picolcd_operation_mode_show(struct device *dev,12731273+ struct device_attribute *attr, char *buf)12741274+{12751275+ struct picolcd_data *data = dev_get_drvdata(dev);12761276+12771277+ if (data->status & PICOLCD_BOOTLOADER)12781278+ return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");12791279+ else12801280+ return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");12811281+}12821282+12831283+static ssize_t picolcd_operation_mode_store(struct device *dev,12841284+ struct device_attribute *attr, const char *buf, size_t count)12851285+{12861286+ struct picolcd_data *data = dev_get_drvdata(dev);12871287+ struct hid_report *report = NULL;12881288+ size_t cnt = count;12891289+ int timeout = data->opmode_delay;12901290+ unsigned long flags;12911291+12921292+ if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {12931293+ if (data->status & PICOLCD_BOOTLOADER)12941294+ report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);12951295+ buf += 3;12961296+ cnt -= 3;12971297+ } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {12981298+ if (!(data->status & PICOLCD_BOOTLOADER))12991299+ report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);13001300+ buf += 10;13011301+ cnt -= 10;13021302+ }13031303+ if (!report)13041304+ return -EINVAL;13051305+13061306+ while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))13071307+ cnt--;13081308+ if (cnt != 0)13091309+ return -EINVAL;13101310+13111311+ spin_lock_irqsave(&data->lock, flags);13121312+ hid_set_field(report->field[0], 0, timeout & 0xff);13131313+ hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);13141314+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);13151315+ spin_unlock_irqrestore(&data->lock, flags);13161316+ return count;13171317+}13181318+13191319+static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,13201320+ picolcd_operation_mode_store);13211321+13221322+/*13231323+ * The "operation_mode_delay" sysfs attribute13241324+ */13251325+static ssize_t picolcd_operation_mode_delay_show(struct device *dev,13261326+ struct device_attribute *attr, char *buf)13271327+{13281328+ struct picolcd_data *data = dev_get_drvdata(dev);13291329+13301330+ return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);13311331+}13321332+13331333+static ssize_t picolcd_operation_mode_delay_store(struct device *dev,13341334+ struct device_attribute *attr, const char *buf, size_t count)13351335+{13361336+ struct picolcd_data *data = dev_get_drvdata(dev);13371337+ unsigned u;13381338+ if (sscanf(buf, "%u", &u) != 1)13391339+ return -EINVAL;13401340+ if (u > 30000)13411341+ return -EINVAL;13421342+ else13431343+ data->opmode_delay = u;13441344+ return count;13451345+}13461346+13471347+static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,13481348+ picolcd_operation_mode_delay_store);13491349+13501350+13511351+#ifdef CONFIG_DEBUG_FS13521352+/*13531353+ * The "reset" file13541354+ */13551355+static int picolcd_debug_reset_show(struct seq_file *f, void *p)13561356+{13571357+ if (picolcd_fbinfo((struct picolcd_data *)f->private))13581358+ seq_printf(f, "all fb\n");13591359+ else13601360+ seq_printf(f, "all\n");13611361+ return 0;13621362+}13631363+13641364+static int picolcd_debug_reset_open(struct inode *inode, struct file *f)13651365+{13661366+ return single_open(f, picolcd_debug_reset_show, inode->i_private);13671367+}13681368+13691369+static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,13701370+ size_t count, loff_t *ppos)13711371+{13721372+ struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;13731373+ char buf[32];13741374+ size_t cnt = min(count, sizeof(buf)-1);13751375+ if (copy_from_user(buf, user_buf, cnt))13761376+ return -EFAULT;13771377+13781378+ while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))13791379+ cnt--;13801380+ buf[cnt] = '\0';13811381+ if (strcmp(buf, "all") == 0) {13821382+ picolcd_reset(data->hdev);13831383+ picolcd_fb_reset(data, 1);13841384+ } else if (strcmp(buf, "fb") == 0) {13851385+ picolcd_fb_reset(data, 1);13861386+ } else {13871387+ return -EINVAL;13881388+ }13891389+ return count;13901390+}13911391+13921392+static const struct file_operations picolcd_debug_reset_fops = {13931393+ .owner = THIS_MODULE,13941394+ .open = picolcd_debug_reset_open,13951395+ .read = seq_read,13961396+ .llseek = seq_lseek,13971397+ .write = picolcd_debug_reset_write,13981398+ .release = single_release,13991399+};14001400+14011401+/*14021402+ * The "eeprom" file14031403+ */14041404+static int picolcd_debug_eeprom_open(struct inode *i, struct file *f)14051405+{14061406+ f->private_data = i->i_private;14071407+ return 0;14081408+}14091409+14101410+static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,14111411+ size_t s, loff_t *off)14121412+{14131413+ struct picolcd_data *data = f->private_data;14141414+ struct picolcd_pending *resp;14151415+ u8 raw_data[3];14161416+ ssize_t ret = -EIO;14171417+14181418+ if (s == 0)14191419+ return -EINVAL;14201420+ if (*off > 0x0ff)14211421+ return 0;14221422+14231423+ /* prepare buffer with info about what we want to read (addr & len) */14241424+ raw_data[0] = *off & 0xff;14251425+ raw_data[1] = (*off >> 8) && 0xff;14261426+ raw_data[2] = s < 20 ? s : 20;14271427+ if (*off + raw_data[2] > 0xff)14281428+ raw_data[2] = 0x100 - *off;14291429+ resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,14301430+ sizeof(raw_data));14311431+ if (!resp)14321432+ return -EIO;14331433+14341434+ if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {14351435+ /* successful read :) */14361436+ ret = resp->raw_data[2];14371437+ if (ret > s)14381438+ ret = s;14391439+ if (copy_to_user(u, resp->raw_data+3, ret))14401440+ ret = -EFAULT;14411441+ else14421442+ *off += ret;14431443+ } /* anything else is some kind of IO error */14441444+14451445+ kfree(resp);14461446+ return ret;14471447+}14481448+14491449+static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,14501450+ size_t s, loff_t *off)14511451+{14521452+ struct picolcd_data *data = f->private_data;14531453+ struct picolcd_pending *resp;14541454+ ssize_t ret = -EIO;14551455+ u8 raw_data[23];14561456+14571457+ if (s == 0)14581458+ return -EINVAL;14591459+ if (*off > 0x0ff)14601460+ return -ENOSPC;14611461+14621462+ memset(raw_data, 0, sizeof(raw_data));14631463+ raw_data[0] = *off & 0xff;14641464+ raw_data[1] = (*off >> 8) && 0xff;14651465+ raw_data[2] = s < 20 ? s : 20;14661466+ if (*off + raw_data[2] > 0xff)14671467+ raw_data[2] = 0x100 - *off;14681468+14691469+ if (copy_from_user(raw_data+3, u, raw_data[2]))14701470+ return -EFAULT;14711471+ resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,14721472+ sizeof(raw_data));14731473+14741474+ if (!resp)14751475+ return -EIO;14761476+14771477+ if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {14781478+ /* check if written data matches */14791479+ if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {14801480+ *off += raw_data[2];14811481+ ret = raw_data[2];14821482+ }14831483+ }14841484+ kfree(resp);14851485+ return ret;14861486+}14871487+14881488+/*14891489+ * Notes:14901490+ * - read/write happens in chunks of at most 20 bytes, it's up to userspace14911491+ * to loop in order to get more data.14921492+ * - on write errors on otherwise correct write request the bytes14931493+ * that should have been written are in undefined state.14941494+ */14951495+static const struct file_operations picolcd_debug_eeprom_fops = {14961496+ .owner = THIS_MODULE,14971497+ .open = picolcd_debug_eeprom_open,14981498+ .read = picolcd_debug_eeprom_read,14991499+ .write = picolcd_debug_eeprom_write,15001500+ .llseek = generic_file_llseek,15011501+};15021502+15031503+/*15041504+ * The "flash" file15051505+ */15061506+static int picolcd_debug_flash_open(struct inode *i, struct file *f)15071507+{15081508+ f->private_data = i->i_private;15091509+ return 0;15101510+}15111511+15121512+/* record a flash address to buf (bounds check to be done by caller) */15131513+static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)15141514+{15151515+ buf[0] = off & 0xff;15161516+ buf[1] = (off >> 8) & 0xff;15171517+ if (data->addr_sz == 3)15181518+ buf[2] = (off >> 16) & 0xff;15191519+ return data->addr_sz == 2 ? 2 : 3;15201520+}15211521+15221522+/* read a given size of data (bounds check to be done by caller) */15231523+static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,15241524+ char __user *u, size_t s, loff_t *off)15251525+{15261526+ struct picolcd_pending *resp;15271527+ u8 raw_data[4];15281528+ ssize_t ret = 0;15291529+ int len_off, err = -EIO;15301530+15311531+ while (s > 0) {15321532+ err = -EIO;15331533+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);15341534+ raw_data[len_off] = s > 32 ? 32 : s;15351535+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);15361536+ if (!resp || !resp->in_report)15371537+ goto skip;15381538+ if (resp->in_report->id == REPORT_MEMORY ||15391539+ resp->in_report->id == REPORT_BL_READ_MEMORY) {15401540+ if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)15411541+ goto skip;15421542+ if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {15431543+ err = -EFAULT;15441544+ goto skip;15451545+ }15461546+ *off += raw_data[len_off];15471547+ s -= raw_data[len_off];15481548+ ret += raw_data[len_off];15491549+ err = 0;15501550+ }15511551+skip:15521552+ kfree(resp);15531553+ if (err)15541554+ return ret > 0 ? ret : err;15551555+ }15561556+ return ret;15571557+}15581558+15591559+static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,15601560+ size_t s, loff_t *off)15611561+{15621562+ struct picolcd_data *data = f->private_data;15631563+15641564+ if (s == 0)15651565+ return -EINVAL;15661566+ if (*off > 0x05fff)15671567+ return 0;15681568+ if (*off + s > 0x05fff)15691569+ s = 0x06000 - *off;15701570+15711571+ if (data->status & PICOLCD_BOOTLOADER)15721572+ return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);15731573+ else15741574+ return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);15751575+}15761576+15771577+/* erase block aligned to 64bytes boundary */15781578+static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,15791579+ loff_t *off)15801580+{15811581+ struct picolcd_pending *resp;15821582+ u8 raw_data[3];15831583+ int len_off;15841584+ ssize_t ret = -EIO;15851585+15861586+ if (*off & 0x3f)15871587+ return -EINVAL;15881588+15891589+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);15901590+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);15911591+ if (!resp || !resp->in_report)15921592+ goto skip;15931593+ if (resp->in_report->id == REPORT_MEMORY ||15941594+ resp->in_report->id == REPORT_BL_ERASE_MEMORY) {15951595+ if (memcmp(raw_data, resp->raw_data, len_off) != 0)15961596+ goto skip;15971597+ ret = 0;15981598+ }15991599+skip:16001600+ kfree(resp);16011601+ return ret;16021602+}16031603+16041604+/* write a given size of data (bounds check to be done by caller) */16051605+static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,16061606+ const char __user *u, size_t s, loff_t *off)16071607+{16081608+ struct picolcd_pending *resp;16091609+ u8 raw_data[36];16101610+ ssize_t ret = 0;16111611+ int len_off, err = -EIO;16121612+16131613+ while (s > 0) {16141614+ err = -EIO;16151615+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);16161616+ raw_data[len_off] = s > 32 ? 32 : s;16171617+ if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {16181618+ err = -EFAULT;16191619+ break;16201620+ }16211621+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,16221622+ len_off+1+raw_data[len_off]);16231623+ if (!resp || !resp->in_report)16241624+ goto skip;16251625+ if (resp->in_report->id == REPORT_MEMORY ||16261626+ resp->in_report->id == REPORT_BL_WRITE_MEMORY) {16271627+ if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)16281628+ goto skip;16291629+ *off += raw_data[len_off];16301630+ s -= raw_data[len_off];16311631+ ret += raw_data[len_off];16321632+ err = 0;16331633+ }16341634+skip:16351635+ kfree(resp);16361636+ if (err)16371637+ break;16381638+ }16391639+ return ret > 0 ? ret : err;16401640+}16411641+16421642+static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,16431643+ size_t s, loff_t *off)16441644+{16451645+ struct picolcd_data *data = f->private_data;16461646+ ssize_t err, ret = 0;16471647+ int report_erase, report_write;16481648+16491649+ if (s == 0)16501650+ return -EINVAL;16511651+ if (*off > 0x5fff)16521652+ return -ENOSPC;16531653+ if (s & 0x3f)16541654+ return -EINVAL;16551655+ if (*off & 0x3f)16561656+ return -EINVAL;16571657+16581658+ if (data->status & PICOLCD_BOOTLOADER) {16591659+ report_erase = REPORT_BL_ERASE_MEMORY;16601660+ report_write = REPORT_BL_WRITE_MEMORY;16611661+ } else {16621662+ report_erase = REPORT_ERASE_MEMORY;16631663+ report_write = REPORT_WRITE_MEMORY;16641664+ }16651665+ mutex_lock(&data->mutex_flash);16661666+ while (s > 0) {16671667+ err = _picolcd_flash_erase64(data, report_erase, off);16681668+ if (err)16691669+ break;16701670+ err = _picolcd_flash_write(data, report_write, u, 64, off);16711671+ if (err < 0)16721672+ break;16731673+ ret += err;16741674+ *off += err;16751675+ s -= err;16761676+ if (err != 64)16771677+ break;16781678+ }16791679+ mutex_unlock(&data->mutex_flash);16801680+ return ret > 0 ? ret : err;16811681+}16821682+16831683+/*16841684+ * Notes:16851685+ * - concurrent writing is prevented by mutex and all writes must be16861686+ * n*64 bytes and 64-byte aligned, each write being preceeded by an16871687+ * ERASE which erases a 64byte block.16881688+ * If less than requested was written or an error is returned for an16891689+ * otherwise correct write request the next 64-byte block which should16901690+ * have been written is in undefined state (mostly: original, erased,16911691+ * (half-)written with write error)16921692+ * - reading can happend without special restriction16931693+ */16941694+static const struct file_operations picolcd_debug_flash_fops = {16951695+ .owner = THIS_MODULE,16961696+ .open = picolcd_debug_flash_open,16971697+ .read = picolcd_debug_flash_read,16981698+ .write = picolcd_debug_flash_write,16991699+ .llseek = generic_file_llseek,17001700+};17011701+17021702+17031703+/*17041704+ * Helper code for HID report level dumping/debugging17051705+ */17061706+static const char *error_codes[] = {17071707+ "success", "parameter missing", "data_missing", "block readonly",17081708+ "block not erasable", "block too big", "section overflow",17091709+ "invalid command length", "invalid data length",17101710+};17111711+17121712+static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,17131713+ const size_t data_len)17141714+{17151715+ int i, j;17161716+ for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) {17171717+ dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];17181718+ dst[j++] = hex_asc[data[i] & 0x0f];17191719+ dst[j++] = ' ';17201720+ }17211721+ if (j < dst_sz) {17221722+ dst[j--] = '\0';17231723+ dst[j] = '\n';17241724+ } else17251725+ dst[j] = '\0';17261726+}17271727+17281728+static void picolcd_debug_out_report(struct picolcd_data *data,17291729+ struct hid_device *hdev, struct hid_report *report)17301730+{17311731+ u8 raw_data[70];17321732+ int raw_size = (report->size >> 3) + 1;17331733+ char *buff;17341734+#define BUFF_SZ 25617351735+17361736+ /* Avoid unnecessary overhead if debugfs is disabled */17371737+ if (!hdev->debug_events)17381738+ return;17391739+17401740+ buff = kmalloc(BUFF_SZ, GFP_ATOMIC);17411741+ if (!buff)17421742+ return;17431743+17441744+ snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",17451745+ report->id, raw_size);17461746+ hid_debug_event(hdev, buff);17471747+ if (raw_size + 5 > sizeof(raw_data)) {17481748+ hid_debug_event(hdev, " TOO BIG\n");17491749+ return;17501750+ } else {17511751+ raw_data[0] = report->id;17521752+ hid_output_report(report, raw_data);17531753+ dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);17541754+ hid_debug_event(hdev, buff);17551755+ }17561756+17571757+ switch (report->id) {17581758+ case REPORT_LED_STATE:17591759+ /* 1 data byte with GPO state */17601760+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",17611761+ "REPORT_LED_STATE", report->id, raw_size-1);17621762+ hid_debug_event(hdev, buff);17631763+ snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);17641764+ hid_debug_event(hdev, buff);17651765+ break;17661766+ case REPORT_BRIGHTNESS:17671767+ /* 1 data byte with brightness */17681768+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",17691769+ "REPORT_BRIGHTNESS", report->id, raw_size-1);17701770+ hid_debug_event(hdev, buff);17711771+ snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);17721772+ hid_debug_event(hdev, buff);17731773+ break;17741774+ case REPORT_CONTRAST:17751775+ /* 1 data byte with contrast */17761776+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",17771777+ "REPORT_CONTRAST", report->id, raw_size-1);17781778+ hid_debug_event(hdev, buff);17791779+ snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);17801780+ hid_debug_event(hdev, buff);17811781+ break;17821782+ case REPORT_RESET:17831783+ /* 2 data bytes with reset duration in ms */17841784+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",17851785+ "REPORT_RESET", report->id, raw_size-1);17861786+ hid_debug_event(hdev, buff);17871787+ snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",17881788+ raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);17891789+ hid_debug_event(hdev, buff);17901790+ break;17911791+ case REPORT_LCD_CMD:17921792+ /* 63 data bytes with LCD commands */17931793+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",17941794+ "REPORT_LCD_CMD", report->id, raw_size-1);17951795+ hid_debug_event(hdev, buff);17961796+ /* TODO: format decoding */17971797+ break;17981798+ case REPORT_LCD_DATA:17991799+ /* 63 data bytes with LCD data */18001800+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",18011801+ "REPORT_LCD_CMD", report->id, raw_size-1);18021802+ /* TODO: format decoding */18031803+ hid_debug_event(hdev, buff);18041804+ break;18051805+ case REPORT_LCD_CMD_DATA:18061806+ /* 63 data bytes with LCD commands and data */18071807+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",18081808+ "REPORT_LCD_CMD", report->id, raw_size-1);18091809+ /* TODO: format decoding */18101810+ hid_debug_event(hdev, buff);18111811+ break;18121812+ case REPORT_EE_READ:18131813+ /* 3 data bytes with read area description */18141814+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",18151815+ "REPORT_EE_READ", report->id, raw_size-1);18161816+ hid_debug_event(hdev, buff);18171817+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",18181818+ raw_data[2], raw_data[1]);18191819+ hid_debug_event(hdev, buff);18201820+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);18211821+ hid_debug_event(hdev, buff);18221822+ break;18231823+ case REPORT_EE_WRITE:18241824+ /* 3+1..20 data bytes with write area description */18251825+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",18261826+ "REPORT_EE_WRITE", report->id, raw_size-1);18271827+ hid_debug_event(hdev, buff);18281828+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",18291829+ raw_data[2], raw_data[1]);18301830+ hid_debug_event(hdev, buff);18311831+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);18321832+ hid_debug_event(hdev, buff);18331833+ if (raw_data[3] == 0) {18341834+ snprintf(buff, BUFF_SZ, "\tNo data\n");18351835+ } else if (raw_data[3] + 4 <= raw_size) {18361836+ snprintf(buff, BUFF_SZ, "\tData: ");18371837+ hid_debug_event(hdev, buff);18381838+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);18391839+ } else {18401840+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");18411841+ }18421842+ hid_debug_event(hdev, buff);18431843+ break;18441844+ case REPORT_ERASE_MEMORY:18451845+ case REPORT_BL_ERASE_MEMORY:18461846+ /* 3 data bytes with pointer inside erase block */18471847+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",18481848+ "REPORT_ERASE_MEMORY", report->id, raw_size-1);18491849+ hid_debug_event(hdev, buff);18501850+ switch (data->addr_sz) {18511851+ case 2:18521852+ snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",18531853+ raw_data[2], raw_data[1]);18541854+ break;18551855+ case 3:18561856+ snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",18571857+ raw_data[3], raw_data[2], raw_data[1]);18581858+ break;18591859+ default:18601860+ snprintf(buff, BUFF_SZ, "\tNot supported\n");18611861+ }18621862+ hid_debug_event(hdev, buff);18631863+ break;18641864+ case REPORT_READ_MEMORY:18651865+ case REPORT_BL_READ_MEMORY:18661866+ /* 4 data bytes with read area description */18671867+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",18681868+ "REPORT_READ_MEMORY", report->id, raw_size-1);18691869+ hid_debug_event(hdev, buff);18701870+ switch (data->addr_sz) {18711871+ case 2:18721872+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",18731873+ raw_data[2], raw_data[1]);18741874+ hid_debug_event(hdev, buff);18751875+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);18761876+ break;18771877+ case 3:18781878+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",18791879+ raw_data[3], raw_data[2], raw_data[1]);18801880+ hid_debug_event(hdev, buff);18811881+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);18821882+ break;18831883+ default:18841884+ snprintf(buff, BUFF_SZ, "\tNot supported\n");18851885+ }18861886+ hid_debug_event(hdev, buff);18871887+ break;18881888+ case REPORT_WRITE_MEMORY:18891889+ case REPORT_BL_WRITE_MEMORY:18901890+ /* 4+1..32 data bytes with write adrea description */18911891+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",18921892+ "REPORT_WRITE_MEMORY", report->id, raw_size-1);18931893+ hid_debug_event(hdev, buff);18941894+ switch (data->addr_sz) {18951895+ case 2:18961896+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",18971897+ raw_data[2], raw_data[1]);18981898+ hid_debug_event(hdev, buff);18991899+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);19001900+ hid_debug_event(hdev, buff);19011901+ if (raw_data[3] == 0) {19021902+ snprintf(buff, BUFF_SZ, "\tNo data\n");19031903+ } else if (raw_data[3] + 4 <= raw_size) {19041904+ snprintf(buff, BUFF_SZ, "\tData: ");19051905+ hid_debug_event(hdev, buff);19061906+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);19071907+ } else {19081908+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");19091909+ }19101910+ break;19111911+ case 3:19121912+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",19131913+ raw_data[3], raw_data[2], raw_data[1]);19141914+ hid_debug_event(hdev, buff);19151915+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);19161916+ hid_debug_event(hdev, buff);19171917+ if (raw_data[4] == 0) {19181918+ snprintf(buff, BUFF_SZ, "\tNo data\n");19191919+ } else if (raw_data[4] + 5 <= raw_size) {19201920+ snprintf(buff, BUFF_SZ, "\tData: ");19211921+ hid_debug_event(hdev, buff);19221922+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);19231923+ } else {19241924+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");19251925+ }19261926+ break;19271927+ default:19281928+ snprintf(buff, BUFF_SZ, "\tNot supported\n");19291929+ }19301930+ hid_debug_event(hdev, buff);19311931+ break;19321932+ case REPORT_SPLASH_RESTART:19331933+ /* TODO */19341934+ break;19351935+ case REPORT_EXIT_KEYBOARD:19361936+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",19371937+ "REPORT_EXIT_KEYBOARD", report->id, raw_size-1);19381938+ hid_debug_event(hdev, buff);19391939+ snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",19401940+ raw_data[1] | (raw_data[2] << 8),19411941+ raw_data[2], raw_data[1]);19421942+ hid_debug_event(hdev, buff);19431943+ break;19441944+ case REPORT_VERSION:19451945+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",19461946+ "REPORT_VERSION", report->id, raw_size-1);19471947+ hid_debug_event(hdev, buff);19481948+ break;19491949+ case REPORT_DEVID:19501950+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",19511951+ "REPORT_DEVID", report->id, raw_size-1);19521952+ hid_debug_event(hdev, buff);19531953+ break;19541954+ case REPORT_SPLASH_SIZE:19551955+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",19561956+ "REPORT_SPLASH_SIZE", report->id, raw_size-1);19571957+ hid_debug_event(hdev, buff);19581958+ break;19591959+ case REPORT_HOOK_VERSION:19601960+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",19611961+ "REPORT_HOOK_VERSION", report->id, raw_size-1);19621962+ hid_debug_event(hdev, buff);19631963+ break;19641964+ case REPORT_EXIT_FLASHER:19651965+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",19661966+ "REPORT_VERSION", report->id, raw_size-1);19671967+ hid_debug_event(hdev, buff);19681968+ snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",19691969+ raw_data[1] | (raw_data[2] << 8),19701970+ raw_data[2], raw_data[1]);19711971+ hid_debug_event(hdev, buff);19721972+ break;19731973+ default:19741974+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",19751975+ "<unknown>", report->id, raw_size-1);19761976+ hid_debug_event(hdev, buff);19771977+ break;19781978+ }19791979+ wake_up_interruptible(&hdev->debug_wait);19801980+ kfree(buff);19811981+}19821982+19831983+static void picolcd_debug_raw_event(struct picolcd_data *data,19841984+ struct hid_device *hdev, struct hid_report *report,19851985+ u8 *raw_data, int size)19861986+{19871987+ char *buff;19881988+19891989+#define BUFF_SZ 25619901990+ /* Avoid unnecessary overhead if debugfs is disabled */19911991+ if (!hdev->debug_events)19921992+ return;19931993+19941994+ buff = kmalloc(BUFF_SZ, GFP_ATOMIC);19951995+ if (!buff)19961996+ return;19971997+19981998+ switch (report->id) {19991999+ case REPORT_ERROR_CODE:20002000+ /* 2 data bytes with affected report and error code */20012001+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",20022002+ "REPORT_ERROR_CODE", report->id, size-1);20032003+ hid_debug_event(hdev, buff);20042004+ if (raw_data[2] < ARRAY_SIZE(error_codes))20052005+ snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",20062006+ raw_data[2], error_codes[raw_data[2]], raw_data[1]);20072007+ else20082008+ snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",20092009+ raw_data[2], raw_data[1]);20102010+ hid_debug_event(hdev, buff);20112011+ break;20122012+ case REPORT_KEY_STATE:20132013+ /* 2 data bytes with key state */20142014+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",20152015+ "REPORT_KEY_STATE", report->id, size-1);20162016+ hid_debug_event(hdev, buff);20172017+ if (raw_data[1] == 0)20182018+ snprintf(buff, BUFF_SZ, "\tNo key pressed\n");20192019+ else if (raw_data[2] == 0)20202020+ snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",20212021+ raw_data[1], raw_data[1]);20222022+ else20232023+ snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",20242024+ raw_data[1], raw_data[1], raw_data[2], raw_data[2]);20252025+ hid_debug_event(hdev, buff);20262026+ break;20272027+ case REPORT_IR_DATA:20282028+ /* Up to 20 byes of IR scancode data */20292029+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",20302030+ "REPORT_IR_DATA", report->id, size-1);20312031+ hid_debug_event(hdev, buff);20322032+ if (raw_data[1] == 0) {20332033+ snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");20342034+ hid_debug_event(hdev, buff);20352035+ } else if (raw_data[1] + 1 <= size) {20362036+ snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",20372037+ raw_data[1]-1);20382038+ hid_debug_event(hdev, buff);20392039+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1);20402040+ hid_debug_event(hdev, buff);20412041+ } else {20422042+ snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",20432043+ raw_data[1]-1);20442044+ hid_debug_event(hdev, buff);20452045+ }20462046+ break;20472047+ case REPORT_EE_DATA:20482048+ /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */20492049+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",20502050+ "REPORT_EE_DATA", report->id, size-1);20512051+ hid_debug_event(hdev, buff);20522052+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",20532053+ raw_data[2], raw_data[1]);20542054+ hid_debug_event(hdev, buff);20552055+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);20562056+ hid_debug_event(hdev, buff);20572057+ if (raw_data[3] == 0) {20582058+ snprintf(buff, BUFF_SZ, "\tNo data\n");20592059+ hid_debug_event(hdev, buff);20602060+ } else if (raw_data[3] + 4 <= size) {20612061+ snprintf(buff, BUFF_SZ, "\tData: ");20622062+ hid_debug_event(hdev, buff);20632063+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);20642064+ hid_debug_event(hdev, buff);20652065+ } else {20662066+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");20672067+ hid_debug_event(hdev, buff);20682068+ }20692069+ break;20702070+ case REPORT_MEMORY:20712071+ /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */20722072+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",20732073+ "REPORT_MEMORY", report->id, size-1);20742074+ hid_debug_event(hdev, buff);20752075+ switch (data->addr_sz) {20762076+ case 2:20772077+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",20782078+ raw_data[2], raw_data[1]);20792079+ hid_debug_event(hdev, buff);20802080+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);20812081+ hid_debug_event(hdev, buff);20822082+ if (raw_data[3] == 0) {20832083+ snprintf(buff, BUFF_SZ, "\tNo data\n");20842084+ } else if (raw_data[3] + 4 <= size) {20852085+ snprintf(buff, BUFF_SZ, "\tData: ");20862086+ hid_debug_event(hdev, buff);20872087+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);20882088+ } else {20892089+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");20902090+ }20912091+ break;20922092+ case 3:20932093+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",20942094+ raw_data[3], raw_data[2], raw_data[1]);20952095+ hid_debug_event(hdev, buff);20962096+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);20972097+ hid_debug_event(hdev, buff);20982098+ if (raw_data[4] == 0) {20992099+ snprintf(buff, BUFF_SZ, "\tNo data\n");21002100+ } else if (raw_data[4] + 5 <= size) {21012101+ snprintf(buff, BUFF_SZ, "\tData: ");21022102+ hid_debug_event(hdev, buff);21032103+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);21042104+ } else {21052105+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");21062106+ }21072107+ break;21082108+ default:21092109+ snprintf(buff, BUFF_SZ, "\tNot supported\n");21102110+ }21112111+ hid_debug_event(hdev, buff);21122112+ break;21132113+ case REPORT_VERSION:21142114+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21152115+ "REPORT_VERSION", report->id, size-1);21162116+ hid_debug_event(hdev, buff);21172117+ snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",21182118+ raw_data[2], raw_data[1]);21192119+ hid_debug_event(hdev, buff);21202120+ break;21212121+ case REPORT_BL_ERASE_MEMORY:21222122+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21232123+ "REPORT_BL_ERASE_MEMORY", report->id, size-1);21242124+ hid_debug_event(hdev, buff);21252125+ /* TODO */21262126+ break;21272127+ case REPORT_BL_READ_MEMORY:21282128+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21292129+ "REPORT_BL_READ_MEMORY", report->id, size-1);21302130+ hid_debug_event(hdev, buff);21312131+ /* TODO */21322132+ break;21332133+ case REPORT_BL_WRITE_MEMORY:21342134+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21352135+ "REPORT_BL_WRITE_MEMORY", report->id, size-1);21362136+ hid_debug_event(hdev, buff);21372137+ /* TODO */21382138+ break;21392139+ case REPORT_DEVID:21402140+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21412141+ "REPORT_DEVID", report->id, size-1);21422142+ hid_debug_event(hdev, buff);21432143+ snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",21442144+ raw_data[1], raw_data[2], raw_data[3], raw_data[4]);21452145+ hid_debug_event(hdev, buff);21462146+ snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",21472147+ raw_data[5]);21482148+ hid_debug_event(hdev, buff);21492149+ break;21502150+ case REPORT_SPLASH_SIZE:21512151+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21522152+ "REPORT_SPLASH_SIZE", report->id, size-1);21532153+ hid_debug_event(hdev, buff);21542154+ snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",21552155+ (raw_data[2] << 8) | raw_data[1]);21562156+ hid_debug_event(hdev, buff);21572157+ snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",21582158+ (raw_data[4] << 8) | raw_data[3]);21592159+ hid_debug_event(hdev, buff);21602160+ break;21612161+ case REPORT_HOOK_VERSION:21622162+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21632163+ "REPORT_HOOK_VERSION", report->id, size-1);21642164+ hid_debug_event(hdev, buff);21652165+ snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",21662166+ raw_data[1], raw_data[2]);21672167+ hid_debug_event(hdev, buff);21682168+ break;21692169+ default:21702170+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",21712171+ "<unknown>", report->id, size-1);21722172+ hid_debug_event(hdev, buff);21732173+ break;21742174+ }21752175+ wake_up_interruptible(&hdev->debug_wait);21762176+ kfree(buff);21772177+}21782178+21792179+static void picolcd_init_devfs(struct picolcd_data *data,21802180+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,21812181+ struct hid_report *flash_r, struct hid_report *flash_w,21822182+ struct hid_report *reset)21832183+{21842184+ struct hid_device *hdev = data->hdev;21852185+21862186+ mutex_init(&data->mutex_flash);21872187+21882188+ /* reset */21892189+ if (reset)21902190+ data->debug_reset = debugfs_create_file("reset", 0600,21912191+ hdev->debug_dir, data, &picolcd_debug_reset_fops);21922192+21932193+ /* eeprom */21942194+ if (eeprom_r || eeprom_w)21952195+ data->debug_eeprom = debugfs_create_file("eeprom",21962196+ (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),21972197+ hdev->debug_dir, data, &picolcd_debug_eeprom_fops);21982198+21992199+ /* flash */22002200+ if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)22012201+ data->addr_sz = flash_r->field[0]->report_count - 1;22022202+ else22032203+ data->addr_sz = -1;22042204+ if (data->addr_sz == 2 || data->addr_sz == 3) {22052205+ data->debug_flash = debugfs_create_file("flash",22062206+ (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),22072207+ hdev->debug_dir, data, &picolcd_debug_flash_fops);22082208+ } else if (flash_r || flash_w)22092209+ dev_warn(&hdev->dev, "Unexpected FLASH access reports, "22102210+ "please submit rdesc for review\n");22112211+}22122212+22132213+static void picolcd_exit_devfs(struct picolcd_data *data)22142214+{22152215+ struct dentry *dent;22162216+22172217+ dent = data->debug_reset;22182218+ data->debug_reset = NULL;22192219+ if (dent)22202220+ debugfs_remove(dent);22212221+ dent = data->debug_eeprom;22222222+ data->debug_eeprom = NULL;22232223+ if (dent)22242224+ debugfs_remove(dent);22252225+ dent = data->debug_flash;22262226+ data->debug_flash = NULL;22272227+ if (dent)22282228+ debugfs_remove(dent);22292229+ mutex_destroy(&data->mutex_flash);22302230+}22312231+#else22322232+static inline void picolcd_debug_raw_event(struct picolcd_data *data,22332233+ struct hid_device *hdev, struct hid_report *report,22342234+ u8 *raw_data, int size)22352235+{22362236+}22372237+static inline void picolcd_init_devfs(struct picolcd_data *data,22382238+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,22392239+ struct hid_report *flash_r, struct hid_report *flash_w,22402240+ struct hid_report *reset)22412241+{22422242+}22432243+static inline void picolcd_exit_devfs(struct picolcd_data *data)22442244+{22452245+}22462246+#endif /* CONFIG_DEBUG_FS */22472247+22482248+/*22492249+ * Handle raw report as sent by device22502250+ */22512251+static int picolcd_raw_event(struct hid_device *hdev,22522252+ struct hid_report *report, u8 *raw_data, int size)22532253+{22542254+ struct picolcd_data *data = hid_get_drvdata(hdev);22552255+ unsigned long flags;22562256+ int ret = 0;22572257+22582258+ if (!data)22592259+ return 1;22602260+22612261+ if (report->id == REPORT_KEY_STATE) {22622262+ if (data->input_keys)22632263+ ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);22642264+ } else if (report->id == REPORT_IR_DATA) {22652265+ if (data->input_cir)22662266+ ret = picolcd_raw_cir(data, report, raw_data+1, size-1);22672267+ } else {22682268+ spin_lock_irqsave(&data->lock, flags);22692269+ /*22702270+ * We let the caller of picolcd_send_and_wait() check if the22712271+ * report we got is one of the expected ones or not.22722272+ */22732273+ if (data->pending) {22742274+ memcpy(data->pending->raw_data, raw_data+1, size-1);22752275+ data->pending->raw_size = size-1;22762276+ data->pending->in_report = report;22772277+ complete(&data->pending->ready);22782278+ }22792279+ spin_unlock_irqrestore(&data->lock, flags);22802280+ }22812281+22822282+ picolcd_debug_raw_event(data, hdev, report, raw_data, size);22832283+ return 1;22842284+}22852285+22862286+#ifdef CONFIG_PM22872287+static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)22882288+{22892289+ if (message.event & PM_EVENT_AUTO)22902290+ return 0;22912291+22922292+ picolcd_suspend_backlight(hid_get_drvdata(hdev));22932293+ dbg_hid(PICOLCD_NAME " device ready for suspend\n");22942294+ return 0;22952295+}22962296+22972297+static int picolcd_resume(struct hid_device *hdev)22982298+{22992299+ int ret;23002300+ ret = picolcd_resume_backlight(hid_get_drvdata(hdev));23012301+ if (ret)23022302+ dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);23032303+ return 0;23042304+}23052305+23062306+static int picolcd_reset_resume(struct hid_device *hdev)23072307+{23082308+ int ret;23092309+ ret = picolcd_reset(hdev);23102310+ if (ret)23112311+ dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);23122312+ ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);23132313+ if (ret)23142314+ dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);23152315+ ret = picolcd_resume_lcd(hid_get_drvdata(hdev));23162316+ if (ret)23172317+ dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);23182318+ ret = picolcd_resume_backlight(hid_get_drvdata(hdev));23192319+ if (ret)23202320+ dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);23212321+ picolcd_leds_set(hid_get_drvdata(hdev));23222322+ return 0;23232323+}23242324+#endif23252325+23262326+/* initialize keypad input device */23272327+static int picolcd_init_keys(struct picolcd_data *data,23282328+ struct hid_report *report)23292329+{23302330+ struct hid_device *hdev = data->hdev;23312331+ struct input_dev *idev;23322332+ int error, i;23332333+23342334+ if (!report)23352335+ return -ENODEV;23362336+ if (report->maxfield != 1 || report->field[0]->report_count != 2 ||23372337+ report->field[0]->report_size != 8) {23382338+ dev_err(&hdev->dev, "unsupported KEY_STATE report");23392339+ return -EINVAL;23402340+ }23412341+23422342+ idev = input_allocate_device();23432343+ if (idev == NULL) {23442344+ dev_err(&hdev->dev, "failed to allocate input device");23452345+ return -ENOMEM;23462346+ }23472347+ input_set_drvdata(idev, hdev);23482348+ memcpy(data->keycode, def_keymap, sizeof(def_keymap));23492349+ idev->name = hdev->name;23502350+ idev->phys = hdev->phys;23512351+ idev->uniq = hdev->uniq;23522352+ idev->id.bustype = hdev->bus;23532353+ idev->id.vendor = hdev->vendor;23542354+ idev->id.product = hdev->product;23552355+ idev->id.version = hdev->version;23562356+ idev->dev.parent = hdev->dev.parent;23572357+ idev->keycode = &data->keycode;23582358+ idev->keycodemax = PICOLCD_KEYS;23592359+ idev->keycodesize = sizeof(data->keycode[0]);23602360+ input_set_capability(idev, EV_MSC, MSC_SCAN);23612361+ set_bit(EV_REP, idev->evbit);23622362+ for (i = 0; i < PICOLCD_KEYS; i++)23632363+ input_set_capability(idev, EV_KEY, data->keycode[i]);23642364+ error = input_register_device(idev);23652365+ if (error) {23662366+ dev_err(&hdev->dev, "error registering the input device");23672367+ input_free_device(idev);23682368+ return error;23692369+ }23702370+ data->input_keys = idev;23712371+ return 0;23722372+}23732373+23742374+static void picolcd_exit_keys(struct picolcd_data *data)23752375+{23762376+ struct input_dev *idev = data->input_keys;23772377+23782378+ data->input_keys = NULL;23792379+ if (idev)23802380+ input_unregister_device(idev);23812381+}23822382+23832383+/* initialize CIR input device */23842384+static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)23852385+{23862386+ /* support not implemented yet */23872387+ return 0;23882388+}23892389+23902390+static inline void picolcd_exit_cir(struct picolcd_data *data)23912391+{23922392+}23932393+23942394+static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)23952395+{23962396+ int error;23972397+23982398+ error = picolcd_check_version(hdev);23992399+ if (error)24002400+ return error;24012401+24022402+ if (data->version[0] != 0 && data->version[1] != 3)24032403+ dev_info(&hdev->dev, "Device with untested firmware revision, "24042404+ "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",24052405+ dev_name(&hdev->dev));24062406+24072407+ /* Setup keypad input device */24082408+ error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));24092409+ if (error)24102410+ goto err;24112411+24122412+ /* Setup CIR input device */24132413+ error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));24142414+ if (error)24152415+ goto err;24162416+24172417+ /* Set up the framebuffer device */24182418+ error = picolcd_init_framebuffer(data);24192419+ if (error)24202420+ goto err;24212421+24222422+ /* Setup lcd class device */24232423+ error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));24242424+ if (error)24252425+ goto err;24262426+24272427+ /* Setup backlight class device */24282428+ error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));24292429+ if (error)24302430+ goto err;24312431+24322432+ /* Setup the LED class devices */24332433+ error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));24342434+ if (error)24352435+ goto err;24362436+24372437+ picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),24382438+ picolcd_out_report(REPORT_EE_WRITE, hdev),24392439+ picolcd_out_report(REPORT_READ_MEMORY, hdev),24402440+ picolcd_out_report(REPORT_WRITE_MEMORY, hdev),24412441+ picolcd_out_report(REPORT_RESET, hdev));24422442+ return 0;24432443+err:24442444+ picolcd_exit_leds(data);24452445+ picolcd_exit_backlight(data);24462446+ picolcd_exit_lcd(data);24472447+ picolcd_exit_framebuffer(data);24482448+ picolcd_exit_cir(data);24492449+ picolcd_exit_keys(data);24502450+ return error;24512451+}24522452+24532453+static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)24542454+{24552455+ int error;24562456+24572457+ error = picolcd_check_version(hdev);24582458+ if (error)24592459+ return error;24602460+24612461+ if (data->version[0] != 1 && data->version[1] != 0)24622462+ dev_info(&hdev->dev, "Device with untested bootloader revision, "24632463+ "please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",24642464+ dev_name(&hdev->dev));24652465+24662466+ picolcd_init_devfs(data, NULL, NULL,24672467+ picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),24682468+ picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);24692469+ return 0;24702470+}24712471+24722472+static int picolcd_probe(struct hid_device *hdev,24732473+ const struct hid_device_id *id)24742474+{24752475+ struct picolcd_data *data;24762476+ int error = -ENOMEM;24772477+24782478+ dbg_hid(PICOLCD_NAME " hardware probe...\n");24792479+24802480+ /*24812481+ * Let's allocate the picolcd data structure, set some reasonable24822482+ * defaults, and associate it with the device24832483+ */24842484+ data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);24852485+ if (data == NULL) {24862486+ dev_err(&hdev->dev, "can't allocate space for Minibox PicoLCD device data\n");24872487+ error = -ENOMEM;24882488+ goto err_no_cleanup;24892489+ }24902490+24912491+ spin_lock_init(&data->lock);24922492+ mutex_init(&data->mutex);24932493+ data->hdev = hdev;24942494+ data->opmode_delay = 5000;24952495+ if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)24962496+ data->status |= PICOLCD_BOOTLOADER;24972497+ hid_set_drvdata(hdev, data);24982498+24992499+ /* Parse the device reports and start it up */25002500+ error = hid_parse(hdev);25012501+ if (error) {25022502+ dev_err(&hdev->dev, "device report parse failed\n");25032503+ goto err_cleanup_data;25042504+ }25052505+25062506+ /* We don't use hidinput but hid_hw_start() fails if nothing is25072507+ * claimed. So spoof claimed input. */25082508+ hdev->claimed = HID_CLAIMED_INPUT;25092509+ error = hid_hw_start(hdev, 0);25102510+ hdev->claimed = 0;25112511+ if (error) {25122512+ dev_err(&hdev->dev, "hardware start failed\n");25132513+ goto err_cleanup_data;25142514+ }25152515+25162516+ error = hdev->ll_driver->open(hdev);25172517+ if (error) {25182518+ dev_err(&hdev->dev, "failed to open input interrupt pipe for key and IR events\n");25192519+ goto err_cleanup_hid_hw;25202520+ }25212521+25222522+ error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);25232523+ if (error) {25242524+ dev_err(&hdev->dev, "failed to create sysfs attributes\n");25252525+ goto err_cleanup_hid_ll;25262526+ }25272527+25282528+ error = device_create_file(&hdev->dev, &dev_attr_operation_mode);25292529+ if (error) {25302530+ dev_err(&hdev->dev, "failed to create sysfs attributes\n");25312531+ goto err_cleanup_sysfs1;25322532+ }25332533+25342534+ if (data->status & PICOLCD_BOOTLOADER)25352535+ error = picolcd_probe_bootloader(hdev, data);25362536+ else25372537+ error = picolcd_probe_lcd(hdev, data);25382538+ if (error)25392539+ goto err_cleanup_sysfs2;25402540+25412541+ dbg_hid(PICOLCD_NAME " activated and initialized\n");25422542+ return 0;25432543+25442544+err_cleanup_sysfs2:25452545+ device_remove_file(&hdev->dev, &dev_attr_operation_mode);25462546+err_cleanup_sysfs1:25472547+ device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);25482548+err_cleanup_hid_ll:25492549+ hdev->ll_driver->close(hdev);25502550+err_cleanup_hid_hw:25512551+ hid_hw_stop(hdev);25522552+err_cleanup_data:25532553+ kfree(data);25542554+err_no_cleanup:25552555+ hid_set_drvdata(hdev, NULL);25562556+25572557+ return error;25582558+}25592559+25602560+static void picolcd_remove(struct hid_device *hdev)25612561+{25622562+ struct picolcd_data *data = hid_get_drvdata(hdev);25632563+ unsigned long flags;25642564+25652565+ dbg_hid(PICOLCD_NAME " hardware remove...\n");25662566+ spin_lock_irqsave(&data->lock, flags);25672567+ data->status |= PICOLCD_FAILED;25682568+ spin_unlock_irqrestore(&data->lock, flags);25692569+25702570+ picolcd_exit_devfs(data);25712571+ device_remove_file(&hdev->dev, &dev_attr_operation_mode);25722572+ device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);25732573+ hdev->ll_driver->close(hdev);25742574+ hid_hw_stop(hdev);25752575+ hid_set_drvdata(hdev, NULL);25762576+25772577+ /* Shortcut potential pending reply that will never arrive */25782578+ spin_lock_irqsave(&data->lock, flags);25792579+ if (data->pending)25802580+ complete(&data->pending->ready);25812581+ spin_unlock_irqrestore(&data->lock, flags);25822582+25832583+ /* Cleanup LED */25842584+ picolcd_exit_leds(data);25852585+ /* Clean up the framebuffer */25862586+ picolcd_exit_backlight(data);25872587+ picolcd_exit_lcd(data);25882588+ picolcd_exit_framebuffer(data);25892589+ /* Cleanup input */25902590+ picolcd_exit_cir(data);25912591+ picolcd_exit_keys(data);25922592+25932593+ mutex_destroy(&data->mutex);25942594+ /* Finally, clean up the picolcd data itself */25952595+ kfree(data);25962596+}25972597+25982598+static const struct hid_device_id picolcd_devices[] = {25992599+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },26002600+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },26012601+ { }26022602+};26032603+MODULE_DEVICE_TABLE(hid, picolcd_devices);26042604+26052605+static struct hid_driver picolcd_driver = {26062606+ .name = "hid-picolcd",26072607+ .id_table = picolcd_devices,26082608+ .probe = picolcd_probe,26092609+ .remove = picolcd_remove,26102610+ .raw_event = picolcd_raw_event,26112611+#ifdef CONFIG_PM26122612+ .suspend = picolcd_suspend,26132613+ .resume = picolcd_resume,26142614+ .reset_resume = picolcd_reset_resume,26152615+#endif26162616+};26172617+26182618+static int __init picolcd_init(void)26192619+{26202620+ return hid_register_driver(&picolcd_driver);26212621+}26222622+26232623+static void __exit picolcd_exit(void)26242624+{26252625+ hid_unregister_driver(&picolcd_driver);26262626+}26272627+26282628+module_init(picolcd_init);26292629+module_exit(picolcd_exit);26302630+MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");26312631+MODULE_LICENSE("GPL v2");
+910
drivers/hid/hid-prodikeys.c
···11+/*22+ * HID driver for the Prodikeys PC-MIDI Keyboard33+ * providing midi & extra multimedia keys functionality44+ *55+ * Copyright (c) 2009 Don Prince <dhprince.devel@yahoo.co.uk>66+ *77+ * Controls for Octave Shift Up/Down, Channel, and88+ * Sustain Duration available via sysfs.99+ *1010+ */1111+1212+/*1313+ * This program is free software; you can redistribute it and/or modify it1414+ * under the terms of the GNU General Public License as published by the Free1515+ * Software Foundation; either version 2 of the License, or (at your option)1616+ * any later version.1717+ */1818+1919+#include <linux/device.h>2020+#include <linux/module.h>2121+#include <linux/usb.h>2222+#include <linux/mutex.h>2323+#include <linux/hid.h>2424+#include <sound/core.h>2525+#include <sound/initval.h>2626+#include <sound/rawmidi.h>2727+#include "usbhid/usbhid.h"2828+#include "hid-ids.h"2929+3030+3131+#define pk_debug(format, arg...) \3232+ pr_debug("hid-prodikeys: " format "\n" , ## arg)3333+#define pk_error(format, arg...) \3434+ pr_err("hid-prodikeys: " format "\n" , ## arg)3535+3636+struct pcmidi_snd;3737+3838+struct pk_device {3939+ unsigned long quirks;4040+4141+ struct hid_device *hdev;4242+ struct pcmidi_snd *pm; /* pcmidi device context */4343+};4444+4545+struct pcmidi_snd;4646+4747+struct pcmidi_sustain {4848+ unsigned long in_use;4949+ struct pcmidi_snd *pm;5050+ struct timer_list timer;5151+ unsigned char status;5252+ unsigned char note;5353+ unsigned char velocity;5454+};5555+5656+#define PCMIDI_SUSTAINED_MAX 325757+struct pcmidi_snd {5858+ struct pk_device *pk;5959+ unsigned short ifnum;6060+ struct hid_report *pcmidi_report6;6161+ struct input_dev *input_ep82;6262+ unsigned short midi_mode;6363+ unsigned short midi_sustain_mode;6464+ unsigned short midi_sustain;6565+ unsigned short midi_channel;6666+ short midi_octave;6767+ struct pcmidi_sustain sustained_notes[PCMIDI_SUSTAINED_MAX];6868+ unsigned short fn_state;6969+ unsigned short last_key[24];7070+ spinlock_t rawmidi_in_lock;7171+ struct snd_card *card;7272+ struct snd_rawmidi *rwmidi;7373+ struct snd_rawmidi_substream *in_substream;7474+ struct snd_rawmidi_substream *out_substream;7575+ unsigned long in_triggered;7676+ unsigned long out_active;7777+};7878+7979+#define PK_QUIRK_NOGET 0x000100008080+#define PCMIDI_MIDDLE_C 608181+#define PCMIDI_CHANNEL_MIN 08282+#define PCMIDI_CHANNEL_MAX 158383+#define PCMIDI_OCTAVE_MIN (-2)8484+#define PCMIDI_OCTAVE_MAX 28585+#define PCMIDI_SUSTAIN_MIN 08686+#define PCMIDI_SUSTAIN_MAX 50008787+8888+static const char shortname[] = "PC-MIDI";8989+static const char longname[] = "Prodikeys PC-MIDI Keyboard";9090+9191+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;9292+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;9393+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;9494+9595+module_param_array(index, int, NULL, 0444);9696+module_param_array(id, charp, NULL, 0444);9797+module_param_array(enable, bool, NULL, 0444);9898+MODULE_PARM_DESC(index, "Index value for the PC-MIDI virtual audio driver");9999+MODULE_PARM_DESC(id, "ID string for the PC-MIDI virtual audio driver");100100+MODULE_PARM_DESC(enable, "Enable for the PC-MIDI virtual audio driver");101101+102102+103103+/* Output routine for the sysfs channel file */104104+static ssize_t show_channel(struct device *dev,105105+ struct device_attribute *attr, char *buf)106106+{107107+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);108108+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);109109+110110+ dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);111111+112112+ return sprintf(buf, "%u (min:%u, max:%u)\n", pk->pm->midi_channel,113113+ PCMIDI_CHANNEL_MIN, PCMIDI_CHANNEL_MAX);114114+}115115+116116+/* Input routine for the sysfs channel file */117117+static ssize_t store_channel(struct device *dev,118118+ struct device_attribute *attr, const char *buf, size_t count)119119+{120120+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);121121+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);122122+123123+ unsigned channel = 0;124124+125125+ if (sscanf(buf, "%u", &channel) > 0 && channel <= PCMIDI_CHANNEL_MAX) {126126+ dbg_hid("pcmidi sysfs write channel=%u\n", channel);127127+ pk->pm->midi_channel = channel;128128+ return strlen(buf);129129+ }130130+ return -EINVAL;131131+}132132+133133+static DEVICE_ATTR(channel, S_IRUGO | S_IWUGO, show_channel,134134+ store_channel);135135+136136+static struct device_attribute *sysfs_device_attr_channel = {137137+ &dev_attr_channel,138138+ };139139+140140+/* Output routine for the sysfs sustain file */141141+static ssize_t show_sustain(struct device *dev,142142+ struct device_attribute *attr, char *buf)143143+{144144+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);145145+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);146146+147147+ dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);148148+149149+ return sprintf(buf, "%u (off:%u, max:%u (ms))\n", pk->pm->midi_sustain,150150+ PCMIDI_SUSTAIN_MIN, PCMIDI_SUSTAIN_MAX);151151+}152152+153153+/* Input routine for the sysfs sustain file */154154+static ssize_t store_sustain(struct device *dev,155155+ struct device_attribute *attr, const char *buf, size_t count)156156+{157157+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);158158+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);159159+160160+ unsigned sustain = 0;161161+162162+ if (sscanf(buf, "%u", &sustain) > 0 && sustain <= PCMIDI_SUSTAIN_MAX) {163163+ dbg_hid("pcmidi sysfs write sustain=%u\n", sustain);164164+ pk->pm->midi_sustain = sustain;165165+ pk->pm->midi_sustain_mode =166166+ (0 == sustain || !pk->pm->midi_mode) ? 0 : 1;167167+ return strlen(buf);168168+ }169169+ return -EINVAL;170170+}171171+172172+static DEVICE_ATTR(sustain, S_IRUGO | S_IWUGO, show_sustain,173173+ store_sustain);174174+175175+static struct device_attribute *sysfs_device_attr_sustain = {176176+ &dev_attr_sustain,177177+ };178178+179179+/* Output routine for the sysfs octave file */180180+static ssize_t show_octave(struct device *dev,181181+ struct device_attribute *attr, char *buf)182182+{183183+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);184184+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);185185+186186+ dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);187187+188188+ return sprintf(buf, "%d (min:%d, max:%d)\n", pk->pm->midi_octave,189189+ PCMIDI_OCTAVE_MIN, PCMIDI_OCTAVE_MAX);190190+}191191+192192+/* Input routine for the sysfs octave file */193193+static ssize_t store_octave(struct device *dev,194194+ struct device_attribute *attr, const char *buf, size_t count)195195+{196196+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);197197+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);198198+199199+ int octave = 0;200200+201201+ if (sscanf(buf, "%d", &octave) > 0 &&202202+ octave >= PCMIDI_OCTAVE_MIN && octave <= PCMIDI_OCTAVE_MAX) {203203+ dbg_hid("pcmidi sysfs write octave=%d\n", octave);204204+ pk->pm->midi_octave = octave;205205+ return strlen(buf);206206+ }207207+ return -EINVAL;208208+}209209+210210+static DEVICE_ATTR(octave, S_IRUGO | S_IWUGO, show_octave,211211+ store_octave);212212+213213+static struct device_attribute *sysfs_device_attr_octave = {214214+ &dev_attr_octave,215215+ };216216+217217+218218+static void pcmidi_send_note(struct pcmidi_snd *pm,219219+ unsigned char status, unsigned char note, unsigned char velocity)220220+{221221+ unsigned long flags;222222+ unsigned char buffer[3];223223+224224+ buffer[0] = status;225225+ buffer[1] = note;226226+ buffer[2] = velocity;227227+228228+ spin_lock_irqsave(&pm->rawmidi_in_lock, flags);229229+230230+ if (!pm->in_substream)231231+ goto drop_note;232232+ if (!test_bit(pm->in_substream->number, &pm->in_triggered))233233+ goto drop_note;234234+235235+ snd_rawmidi_receive(pm->in_substream, buffer, 3);236236+237237+drop_note:238238+ spin_unlock_irqrestore(&pm->rawmidi_in_lock, flags);239239+240240+ return;241241+}242242+243243+void pcmidi_sustained_note_release(unsigned long data)244244+{245245+ struct pcmidi_sustain *pms = (struct pcmidi_sustain *)data;246246+247247+ pcmidi_send_note(pms->pm, pms->status, pms->note, pms->velocity);248248+ pms->in_use = 0;249249+}250250+251251+void init_sustain_timers(struct pcmidi_snd *pm)252252+{253253+ struct pcmidi_sustain *pms;254254+ unsigned i;255255+256256+ for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {257257+ pms = &pm->sustained_notes[i];258258+ pms->in_use = 0;259259+ pms->pm = pm;260260+ setup_timer(&pms->timer, pcmidi_sustained_note_release,261261+ (unsigned long)pms);262262+ }263263+}264264+265265+void stop_sustain_timers(struct pcmidi_snd *pm)266266+{267267+ struct pcmidi_sustain *pms;268268+ unsigned i;269269+270270+ for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {271271+ pms = &pm->sustained_notes[i];272272+ pms->in_use = 1;273273+ del_timer_sync(&pms->timer);274274+ }275275+}276276+277277+static int pcmidi_get_output_report(struct pcmidi_snd *pm)278278+{279279+ struct hid_device *hdev = pm->pk->hdev;280280+ struct hid_report *report;281281+282282+ list_for_each_entry(report,283283+ &hdev->report_enum[HID_OUTPUT_REPORT].report_list, list) {284284+ if (!(6 == report->id))285285+ continue;286286+287287+ if (report->maxfield < 1) {288288+ dev_err(&hdev->dev, "output report is empty\n");289289+ break;290290+ }291291+ if (report->field[0]->report_count != 2) {292292+ dev_err(&hdev->dev, "field count too low\n");293293+ break;294294+ }295295+ pm->pcmidi_report6 = report;296296+ return 0;297297+ }298298+ /* should never get here */299299+ return -ENODEV;300300+}301301+302302+static void pcmidi_submit_output_report(struct pcmidi_snd *pm, int state)303303+{304304+ struct hid_device *hdev = pm->pk->hdev;305305+ struct hid_report *report = pm->pcmidi_report6;306306+ report->field[0]->value[0] = 0x01;307307+ report->field[0]->value[1] = state;308308+309309+ usbhid_submit_report(hdev, report, USB_DIR_OUT);310310+}311311+312312+static int pcmidi_handle_report1(struct pcmidi_snd *pm, u8 *data)313313+{314314+ u32 bit_mask;315315+316316+ bit_mask = data[1];317317+ bit_mask = (bit_mask << 8) | data[2];318318+ bit_mask = (bit_mask << 8) | data[3];319319+320320+ dbg_hid("pcmidi mode: %d\n", pm->midi_mode);321321+322322+ /*KEY_MAIL or octave down*/323323+ if (pm->midi_mode && bit_mask == 0x004000) {324324+ /* octave down */325325+ pm->midi_octave--;326326+ if (pm->midi_octave < -2)327327+ pm->midi_octave = -2;328328+ dbg_hid("pcmidi mode: %d octave: %d\n",329329+ pm->midi_mode, pm->midi_octave);330330+ return 1;331331+ }332332+ /*KEY_WWW or sustain*/333333+ else if (pm->midi_mode && bit_mask == 0x000004) {334334+ /* sustain on/off*/335335+ pm->midi_sustain_mode ^= 0x1;336336+ return 1;337337+ }338338+339339+ return 0; /* continue key processing */340340+}341341+342342+static int pcmidi_handle_report3(struct pcmidi_snd *pm, u8 *data, int size)343343+{344344+ struct pcmidi_sustain *pms;345345+ unsigned i, j;346346+ unsigned char status, note, velocity;347347+348348+ unsigned num_notes = (size-1)/2;349349+ for (j = 0; j < num_notes; j++) {350350+ note = data[j*2+1];351351+ velocity = data[j*2+2];352352+353353+ if (note < 0x81) { /* note on */354354+ status = 128 + 16 + pm->midi_channel; /* 1001nnnn */355355+ note = note - 0x54 + PCMIDI_MIDDLE_C +356356+ (pm->midi_octave * 12);357357+ if (0 == velocity)358358+ velocity = 1; /* force note on */359359+ } else { /* note off */360360+ status = 128 + pm->midi_channel; /* 1000nnnn */361361+ note = note - 0x94 + PCMIDI_MIDDLE_C +362362+ (pm->midi_octave*12);363363+364364+ if (pm->midi_sustain_mode) {365365+ for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {366366+ pms = &pm->sustained_notes[i];367367+ if (!pms->in_use) {368368+ pms->status = status;369369+ pms->note = note;370370+ pms->velocity = velocity;371371+ pms->in_use = 1;372372+373373+ mod_timer(&pms->timer,374374+ jiffies +375375+ msecs_to_jiffies(pm->midi_sustain));376376+ return 1;377377+ }378378+ }379379+ }380380+ }381381+ pcmidi_send_note(pm, status, note, velocity);382382+ }383383+384384+ return 1;385385+}386386+387387+static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)388388+{389389+ unsigned key;390390+ u32 bit_mask;391391+ u32 bit_index;392392+393393+ bit_mask = data[1];394394+ bit_mask = (bit_mask << 8) | data[2];395395+ bit_mask = (bit_mask << 8) | data[3];396396+397397+ /* break keys */398398+ for (bit_index = 0; bit_index < 24; bit_index++) {399399+ key = pm->last_key[bit_index];400400+ if (!((0x01 << bit_index) & bit_mask)) {401401+ input_event(pm->input_ep82, EV_KEY,402402+ pm->last_key[bit_index], 0);403403+ pm->last_key[bit_index] = 0;404404+ }405405+ }406406+407407+ /* make keys */408408+ for (bit_index = 0; bit_index < 24; bit_index++) {409409+ key = 0;410410+ switch ((0x01 << bit_index) & bit_mask) {411411+ case 0x000010: /* Fn lock*/412412+ pm->fn_state ^= 0x000010;413413+ if (pm->fn_state)414414+ pcmidi_submit_output_report(pm, 0xc5);415415+ else416416+ pcmidi_submit_output_report(pm, 0xc6);417417+ continue;418418+ case 0x020000: /* midi launcher..send a key (qwerty) or not? */419419+ pcmidi_submit_output_report(pm, 0xc1);420420+ pm->midi_mode ^= 0x01;421421+422422+ dbg_hid("pcmidi mode: %d\n", pm->midi_mode);423423+ continue;424424+ case 0x100000: /* KEY_MESSENGER or octave up */425425+ dbg_hid("pcmidi mode: %d\n", pm->midi_mode);426426+ if (pm->midi_mode) {427427+ pm->midi_octave++;428428+ if (pm->midi_octave > 2)429429+ pm->midi_octave = 2;430430+ dbg_hid("pcmidi mode: %d octave: %d\n",431431+ pm->midi_mode, pm->midi_octave);432432+ continue;433433+ } else434434+ key = KEY_MESSENGER;435435+ break;436436+ case 0x400000:437437+ key = KEY_CALENDAR;438438+ break;439439+ case 0x080000:440440+ key = KEY_ADDRESSBOOK;441441+ break;442442+ case 0x040000:443443+ key = KEY_DOCUMENTS;444444+ break;445445+ case 0x800000:446446+ key = KEY_WORDPROCESSOR;447447+ break;448448+ case 0x200000:449449+ key = KEY_SPREADSHEET;450450+ break;451451+ case 0x010000:452452+ key = KEY_COFFEE;453453+ break;454454+ case 0x000100:455455+ key = KEY_HELP;456456+ break;457457+ case 0x000200:458458+ key = KEY_SEND;459459+ break;460460+ case 0x000400:461461+ key = KEY_REPLY;462462+ break;463463+ case 0x000800:464464+ key = KEY_FORWARDMAIL;465465+ break;466466+ case 0x001000:467467+ key = KEY_NEW;468468+ break;469469+ case 0x002000:470470+ key = KEY_OPEN;471471+ break;472472+ case 0x004000:473473+ key = KEY_CLOSE;474474+ break;475475+ case 0x008000:476476+ key = KEY_SAVE;477477+ break;478478+ case 0x000001:479479+ key = KEY_UNDO;480480+ break;481481+ case 0x000002:482482+ key = KEY_REDO;483483+ break;484484+ case 0x000004:485485+ key = KEY_SPELLCHECK;486486+ break;487487+ case 0x000008:488488+ key = KEY_PRINT;489489+ break;490490+ }491491+ if (key) {492492+ input_event(pm->input_ep82, EV_KEY, key, 1);493493+ pm->last_key[bit_index] = key;494494+ }495495+ }496496+497497+ return 1;498498+}499499+500500+int pcmidi_handle_report(501501+ struct pcmidi_snd *pm, unsigned report_id, u8 *data, int size)502502+{503503+ int ret = 0;504504+505505+ switch (report_id) {506506+ case 0x01: /* midi keys (qwerty)*/507507+ ret = pcmidi_handle_report1(pm, data);508508+ break;509509+ case 0x03: /* midi keyboard (musical)*/510510+ ret = pcmidi_handle_report3(pm, data, size);511511+ break;512512+ case 0x04: /* multimedia/midi keys (qwerty)*/513513+ ret = pcmidi_handle_report4(pm, data);514514+ break;515515+ }516516+ return ret;517517+}518518+519519+void pcmidi_setup_extra_keys(struct pcmidi_snd *pm, struct input_dev *input)520520+{521521+ /* reassigned functionality for N/A keys522522+ MY PICTURES => KEY_WORDPROCESSOR523523+ MY MUSIC=> KEY_SPREADSHEET524524+ */525525+ unsigned int keys[] = {526526+ KEY_FN,527527+ KEY_MESSENGER, KEY_CALENDAR,528528+ KEY_ADDRESSBOOK, KEY_DOCUMENTS,529529+ KEY_WORDPROCESSOR,530530+ KEY_SPREADSHEET,531531+ KEY_COFFEE,532532+ KEY_HELP, KEY_SEND,533533+ KEY_REPLY, KEY_FORWARDMAIL,534534+ KEY_NEW, KEY_OPEN,535535+ KEY_CLOSE, KEY_SAVE,536536+ KEY_UNDO, KEY_REDO,537537+ KEY_SPELLCHECK, KEY_PRINT,538538+ 0539539+ };540540+541541+ unsigned int *pkeys = &keys[0];542542+ unsigned short i;543543+544544+ if (pm->ifnum != 1) /* only set up ONCE for interace 1 */545545+ return;546546+547547+ pm->input_ep82 = input;548548+549549+ for (i = 0; i < 24; i++)550550+ pm->last_key[i] = 0;551551+552552+ while (*pkeys != 0) {553553+ set_bit(*pkeys, pm->input_ep82->keybit);554554+ ++pkeys;555555+ }556556+}557557+558558+static int pcmidi_set_operational(struct pcmidi_snd *pm)559559+{560560+ if (pm->ifnum != 1)561561+ return 0; /* only set up ONCE for interace 1 */562562+563563+ pcmidi_get_output_report(pm);564564+ pcmidi_submit_output_report(pm, 0xc1);565565+ return 0;566566+}567567+568568+static int pcmidi_snd_free(struct snd_device *dev)569569+{570570+ return 0;571571+}572572+573573+static int pcmidi_in_open(struct snd_rawmidi_substream *substream)574574+{575575+ struct pcmidi_snd *pm = substream->rmidi->private_data;576576+577577+ dbg_hid("pcmidi in open\n");578578+ pm->in_substream = substream;579579+ return 0;580580+}581581+582582+static int pcmidi_in_close(struct snd_rawmidi_substream *substream)583583+{584584+ dbg_hid("pcmidi in close\n");585585+ return 0;586586+}587587+588588+static void pcmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)589589+{590590+ struct pcmidi_snd *pm = substream->rmidi->private_data;591591+592592+ dbg_hid("pcmidi in trigger %d\n", up);593593+594594+ pm->in_triggered = up;595595+}596596+597597+static struct snd_rawmidi_ops pcmidi_in_ops = {598598+ .open = pcmidi_in_open,599599+ .close = pcmidi_in_close,600600+ .trigger = pcmidi_in_trigger601601+};602602+603603+int pcmidi_snd_initialise(struct pcmidi_snd *pm)604604+{605605+ static int dev;606606+ struct snd_card *card;607607+ struct snd_rawmidi *rwmidi;608608+ int err;609609+610610+ static struct snd_device_ops ops = {611611+ .dev_free = pcmidi_snd_free,612612+ };613613+614614+ if (pm->ifnum != 1)615615+ return 0; /* only set up midi device ONCE for interace 1 */616616+617617+ if (dev >= SNDRV_CARDS)618618+ return -ENODEV;619619+620620+ if (!enable[dev]) {621621+ dev++;622622+ return -ENOENT;623623+ }624624+625625+ /* Setup sound card */626626+627627+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);628628+ if (err < 0) {629629+ pk_error("failed to create pc-midi sound card\n");630630+ err = -ENOMEM;631631+ goto fail;632632+ }633633+ pm->card = card;634634+635635+ /* Setup sound device */636636+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pm, &ops);637637+ if (err < 0) {638638+ pk_error("failed to create pc-midi sound device: error %d\n",639639+ err);640640+ goto fail;641641+ }642642+643643+ strncpy(card->driver, shortname, sizeof(card->driver));644644+ strncpy(card->shortname, shortname, sizeof(card->shortname));645645+ strncpy(card->longname, longname, sizeof(card->longname));646646+647647+ /* Set up rawmidi */648648+ err = snd_rawmidi_new(card, card->shortname, 0,649649+ 0, 1, &rwmidi);650650+ if (err < 0) {651651+ pk_error("failed to create pc-midi rawmidi device: error %d\n",652652+ err);653653+ goto fail;654654+ }655655+ pm->rwmidi = rwmidi;656656+ strncpy(rwmidi->name, card->shortname, sizeof(rwmidi->name));657657+ rwmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT;658658+ rwmidi->private_data = pm;659659+660660+ snd_rawmidi_set_ops(rwmidi, SNDRV_RAWMIDI_STREAM_INPUT,661661+ &pcmidi_in_ops);662662+663663+ snd_card_set_dev(card, &pm->pk->hdev->dev);664664+665665+ /* create sysfs variables */666666+ err = device_create_file(&pm->pk->hdev->dev,667667+ sysfs_device_attr_channel);668668+ if (err < 0) {669669+ pk_error("failed to create sysfs attribute channel: error %d\n",670670+ err);671671+ goto fail;672672+ }673673+674674+ err = device_create_file(&pm->pk->hdev->dev,675675+ sysfs_device_attr_sustain);676676+ if (err < 0) {677677+ pk_error("failed to create sysfs attribute sustain: error %d\n",678678+ err);679679+ goto fail_attr_sustain;680680+ }681681+682682+ err = device_create_file(&pm->pk->hdev->dev,683683+ sysfs_device_attr_octave);684684+ if (err < 0) {685685+ pk_error("failed to create sysfs attribute octave: error %d\n",686686+ err);687687+ goto fail_attr_octave;688688+ }689689+690690+ spin_lock_init(&pm->rawmidi_in_lock);691691+692692+ init_sustain_timers(pm);693693+ pcmidi_set_operational(pm);694694+695695+ /* register it */696696+ err = snd_card_register(card);697697+ if (err < 0) {698698+ pk_error("failed to register pc-midi sound card: error %d\n",699699+ err);700700+ goto fail_register;701701+ }702702+703703+ dbg_hid("pcmidi_snd_initialise finished ok\n");704704+ return 0;705705+706706+fail_register:707707+ stop_sustain_timers(pm);708708+ device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_octave);709709+fail_attr_octave:710710+ device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_sustain);711711+fail_attr_sustain:712712+ device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_channel);713713+fail:714714+ if (pm->card) {715715+ snd_card_free(pm->card);716716+ pm->card = NULL;717717+ }718718+ return err;719719+}720720+721721+int pcmidi_snd_terminate(struct pcmidi_snd *pm)722722+{723723+ if (pm->card) {724724+ stop_sustain_timers(pm);725725+726726+ device_remove_file(&pm->pk->hdev->dev,727727+ sysfs_device_attr_channel);728728+ device_remove_file(&pm->pk->hdev->dev,729729+ sysfs_device_attr_sustain);730730+ device_remove_file(&pm->pk->hdev->dev,731731+ sysfs_device_attr_octave);732732+733733+ snd_card_disconnect(pm->card);734734+ snd_card_free_when_closed(pm->card);735735+ }736736+737737+ return 0;738738+}739739+740740+/*741741+ * PC-MIDI report descriptor for report id is wrong.742742+ */743743+static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,744744+ unsigned int rsize)745745+{746746+ if (rsize == 178 &&747747+ rdesc[111] == 0x06 && rdesc[112] == 0x00 &&748748+ rdesc[113] == 0xff) {749749+ dev_info(&hdev->dev, "fixing up pc-midi keyboard report "750750+ "descriptor\n");751751+752752+ rdesc[144] = 0x18; /* report 4: was 0x10 report count */753753+ }754754+}755755+756756+static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,757757+ struct hid_field *field, struct hid_usage *usage,758758+ unsigned long **bit, int *max)759759+{760760+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);761761+ struct pcmidi_snd *pm;762762+763763+ pm = pk->pm;764764+765765+ if (HID_UP_MSVENDOR == (usage->hid & HID_USAGE_PAGE) &&766766+ 1 == pm->ifnum) {767767+ pcmidi_setup_extra_keys(pm, hi->input);768768+ return 0;769769+ }770770+771771+ return 0;772772+}773773+774774+775775+static int pk_raw_event(struct hid_device *hdev, struct hid_report *report,776776+ u8 *data, int size)777777+{778778+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);779779+ int ret = 0;780780+781781+ if (1 == pk->pm->ifnum) {782782+ if (report->id == data[0])783783+ switch (report->id) {784784+ case 0x01: /* midi keys (qwerty)*/785785+ case 0x03: /* midi keyboard (musical)*/786786+ case 0x04: /* extra/midi keys (qwerty)*/787787+ ret = pcmidi_handle_report(pk->pm,788788+ report->id, data, size);789789+ break;790790+ }791791+ }792792+793793+ return ret;794794+}795795+796796+static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id)797797+{798798+ int ret;799799+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);800800+ unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;801801+ unsigned long quirks = id->driver_data;802802+ struct pk_device *pk;803803+ struct pcmidi_snd *pm = NULL;804804+805805+ pk = kzalloc(sizeof(*pk), GFP_KERNEL);806806+ if (pk == NULL) {807807+ dev_err(&hdev->dev, "prodikeys: can't alloc descriptor\n");808808+ return -ENOMEM;809809+ }810810+811811+ pk->hdev = hdev;812812+813813+ pm = kzalloc(sizeof(*pm), GFP_KERNEL);814814+ if (pm == NULL) {815815+ dev_err(&hdev->dev,816816+ "prodikeys: can't alloc descriptor\n");817817+ ret = -ENOMEM;818818+ goto err_free;819819+ }820820+821821+ pm->pk = pk;822822+ pk->pm = pm;823823+ pm->ifnum = ifnum;824824+825825+ hid_set_drvdata(hdev, pk);826826+827827+ ret = hid_parse(hdev);828828+ if (ret) {829829+ dev_err(&hdev->dev, "prodikeys: hid parse failed\n");830830+ goto err_free;831831+ }832832+833833+ if (quirks & PK_QUIRK_NOGET) { /* hid_parse cleared all the quirks */834834+ hdev->quirks |= HID_QUIRK_NOGET;835835+ }836836+837837+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);838838+ if (ret) {839839+ dev_err(&hdev->dev, "prodikeys: hw start failed\n");840840+ goto err_free;841841+ }842842+843843+ ret = pcmidi_snd_initialise(pm);844844+ if (ret < 0)845845+ goto err_stop;846846+847847+ return 0;848848+err_stop:849849+ hid_hw_stop(hdev);850850+err_free:851851+ if (pm != NULL)852852+ kfree(pm);853853+854854+ kfree(pk);855855+ return ret;856856+}857857+858858+static void pk_remove(struct hid_device *hdev)859859+{860860+ struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);861861+ struct pcmidi_snd *pm;862862+863863+ pm = pk->pm;864864+ if (pm) {865865+ pcmidi_snd_terminate(pm);866866+ kfree(pm);867867+ }868868+869869+ hid_hw_stop(hdev);870870+871871+ kfree(pk);872872+}873873+874874+static const struct hid_device_id pk_devices[] = {875875+ {HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS,876876+ USB_DEVICE_ID_PRODIKEYS_PCMIDI),877877+ .driver_data = PK_QUIRK_NOGET},878878+ { }879879+};880880+MODULE_DEVICE_TABLE(hid, pk_devices);881881+882882+static struct hid_driver pk_driver = {883883+ .name = "prodikeys",884884+ .id_table = pk_devices,885885+ .report_fixup = pk_report_fixup,886886+ .input_mapping = pk_input_mapping,887887+ .raw_event = pk_raw_event,888888+ .probe = pk_probe,889889+ .remove = pk_remove,890890+};891891+892892+static int pk_init(void)893893+{894894+ int ret;895895+896896+ ret = hid_register_driver(&pk_driver);897897+ if (ret)898898+ printk(KERN_ERR "can't register prodikeys driver\n");899899+900900+ return ret;901901+}902902+903903+static void pk_exit(void)904904+{905905+ hid_unregister_driver(&pk_driver);906906+}907907+908908+module_init(pk_init);909909+module_exit(pk_exit);910910+MODULE_LICENSE("GPL");
+994
drivers/hid/hid-roccat-kone.c
···11+/*22+ * Roccat Kone driver for Linux33+ *44+ * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>55+ */66+77+/*88+ * This program is free software; you can redistribute it and/or modify it99+ * under the terms of the GNU General Public License as published by the Free1010+ * Software Foundation; either version 2 of the License, or (at your option)1111+ * any later version.1212+ */1313+1414+/*1515+ * Roccat Kone is a gamer mouse which consists of a mouse part and a keyboard1616+ * part. The keyboard part enables the mouse to execute stored macros with mixed1717+ * key- and button-events.1818+ *1919+ * TODO implement on-the-fly polling-rate change2020+ * The windows driver has the ability to change the polling rate of the2121+ * device on the press of a mousebutton.2222+ * Is it possible to remove and reinstall the urb in raw-event- or any2323+ * other handler, or to defer this action to be executed somewhere else?2424+ *2525+ * TODO implement notification mechanism for overlong macro execution2626+ * If user wants to execute an overlong macro only the names of macroset2727+ * and macro are given. Should userland tap hidraw or is there an2828+ * additional streaming mechanism?2929+ *3030+ * TODO is it possible to overwrite group for sysfs attributes via udev?3131+ */3232+3333+#include <linux/device.h>3434+#include <linux/input.h>3535+#include <linux/hid.h>3636+#include <linux/usb.h>3737+#include <linux/module.h>3838+#include <linux/slab.h>3939+#include "hid-ids.h"4040+#include "hid-roccat-kone.h"4141+4242+static void kone_set_settings_checksum(struct kone_settings *settings)4343+{4444+ uint16_t checksum = 0;4545+ unsigned char *address = (unsigned char *)settings;4646+ int i;4747+4848+ for (i = 0; i < sizeof(struct kone_settings) - 2; ++i, ++address)4949+ checksum += *address;5050+ settings->checksum = cpu_to_le16(checksum);5151+}5252+5353+/*5454+ * Checks success after writing data to mouse5555+ * On success returns 05656+ * On failure returns errno5757+ */5858+static int kone_check_write(struct usb_device *usb_dev)5959+{6060+ int len;6161+ unsigned char *data;6262+6363+ data = kmalloc(1, GFP_KERNEL);6464+ if (!data)6565+ return -ENOMEM;6666+6767+ do {6868+ /*6969+ * Mouse needs 50 msecs until it says ok, but there are7070+ * 30 more msecs needed for next write to work.7171+ */7272+ msleep(80);7373+7474+ len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),7575+ USB_REQ_CLEAR_FEATURE,7676+ USB_TYPE_CLASS | USB_RECIP_INTERFACE |7777+ USB_DIR_IN,7878+ kone_command_confirm_write, 0, data, 1,7979+ USB_CTRL_SET_TIMEOUT);8080+8181+ if (len != 1) {8282+ kfree(data);8383+ return -EIO;8484+ }8585+8686+ /*8787+ * value of 3 seems to mean something like8888+ * "not finished yet, but it looks good"8989+ * So check again after a moment.9090+ */9191+ } while (*data == 3);9292+9393+ if (*data == 1) { /* everything alright */9494+ kfree(data);9595+ return 0;9696+ } else { /* unknown answer */9797+ dev_err(&usb_dev->dev, "got retval %d when checking write\n",9898+ *data);9999+ kfree(data);100100+ return -EIO;101101+ }102102+}103103+104104+/*105105+ * Reads settings from mouse and stores it in @buf106106+ * @buf has to be alloced with GFP_KERNEL107107+ * On success returns 0108108+ * On failure returns errno109109+ */110110+static int kone_get_settings(struct usb_device *usb_dev,111111+ struct kone_settings *buf)112112+{113113+ int len;114114+115115+ len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),116116+ USB_REQ_CLEAR_FEATURE,117117+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,118118+ kone_command_settings, 0, buf,119119+ sizeof(struct kone_settings), USB_CTRL_SET_TIMEOUT);120120+121121+ if (len != sizeof(struct kone_settings))122122+ return -EIO;123123+124124+ return 0;125125+}126126+127127+/*128128+ * Writes settings from @buf to mouse129129+ * On success returns 0130130+ * On failure returns errno131131+ */132132+static int kone_set_settings(struct usb_device *usb_dev,133133+ struct kone_settings const *settings)134134+{135135+ int len;136136+137137+ len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),138138+ USB_REQ_SET_CONFIGURATION,139139+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,140140+ kone_command_settings, 0, (char *)settings,141141+ sizeof(struct kone_settings),142142+ USB_CTRL_SET_TIMEOUT);143143+144144+ if (len != sizeof(struct kone_settings))145145+ return -EIO;146146+147147+ if (kone_check_write(usb_dev))148148+ return -EIO;149149+150150+ return 0;151151+}152152+153153+/*154154+ * Reads profile data from mouse and stores it in @buf155155+ * @number: profile number to read156156+ * On success returns 0157157+ * On failure returns errno158158+ */159159+static int kone_get_profile(struct usb_device *usb_dev,160160+ struct kone_profile *buf, int number)161161+{162162+ int len;163163+164164+ if (number < 1 || number > 5)165165+ return -EINVAL;166166+167167+ len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),168168+ USB_REQ_CLEAR_FEATURE,169169+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,170170+ kone_command_profile, number, buf,171171+ sizeof(struct kone_profile), USB_CTRL_SET_TIMEOUT);172172+173173+ if (len != sizeof(struct kone_profile))174174+ return -EIO;175175+176176+ return 0;177177+}178178+179179+/*180180+ * Writes profile data to mouse.181181+ * @number: profile number to write182182+ * On success returns 0183183+ * On failure returns errno184184+ */185185+static int kone_set_profile(struct usb_device *usb_dev,186186+ struct kone_profile const *profile, int number)187187+{188188+ int len;189189+190190+ if (number < 1 || number > 5)191191+ return -EINVAL;192192+193193+ len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),194194+ USB_REQ_SET_CONFIGURATION,195195+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,196196+ kone_command_profile, number, (char *)profile,197197+ sizeof(struct kone_profile),198198+ USB_CTRL_SET_TIMEOUT);199199+200200+ if (len != sizeof(struct kone_profile))201201+ return len;202202+203203+ if (kone_check_write(usb_dev))204204+ return -EIO;205205+206206+ return 0;207207+}208208+209209+/*210210+ * Reads value of "fast-clip-weight" and stores it in @result211211+ * On success returns 0212212+ * On failure returns errno213213+ */214214+static int kone_get_weight(struct usb_device *usb_dev, int *result)215215+{216216+ int len;217217+ uint8_t *data;218218+219219+ data = kmalloc(1, GFP_KERNEL);220220+ if (!data)221221+ return -ENOMEM;222222+223223+ len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),224224+ USB_REQ_CLEAR_FEATURE,225225+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,226226+ kone_command_weight, 0, data, 1, USB_CTRL_SET_TIMEOUT);227227+228228+ if (len != 1) {229229+ kfree(data);230230+ return -EIO;231231+ }232232+ *result = (int)*data;233233+ kfree(data);234234+ return 0;235235+}236236+237237+/*238238+ * Reads firmware_version of mouse and stores it in @result239239+ * On success returns 0240240+ * On failure returns errno241241+ */242242+static int kone_get_firmware_version(struct usb_device *usb_dev, int *result)243243+{244244+ int len;245245+ unsigned char *data;246246+247247+ data = kmalloc(2, GFP_KERNEL);248248+ if (!data)249249+ return -ENOMEM;250250+251251+ len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),252252+ USB_REQ_CLEAR_FEATURE,253253+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,254254+ kone_command_firmware_version, 0, data, 2,255255+ USB_CTRL_SET_TIMEOUT);256256+257257+ if (len != 2) {258258+ kfree(data);259259+ return -EIO;260260+ }261261+ *result = le16_to_cpu(*data);262262+ kfree(data);263263+ return 0;264264+}265265+266266+static ssize_t kone_sysfs_read_settings(struct kobject *kobj,267267+ struct bin_attribute *attr, char *buf,268268+ loff_t off, size_t count) {269269+ struct device *dev = container_of(kobj, struct device, kobj);270270+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));271271+272272+ if (off >= sizeof(struct kone_settings))273273+ return 0;274274+275275+ if (off + count > sizeof(struct kone_settings))276276+ count = sizeof(struct kone_settings) - off;277277+278278+ mutex_lock(&kone->kone_lock);279279+ memcpy(buf, &kone->settings + off, count);280280+ mutex_unlock(&kone->kone_lock);281281+282282+ return count;283283+}284284+285285+/*286286+ * Writing settings automatically activates startup_profile.287287+ * This function keeps values in kone_device up to date and assumes that in288288+ * case of error the old data is still valid289289+ */290290+static ssize_t kone_sysfs_write_settings(struct kobject *kobj,291291+ struct bin_attribute *attr, char *buf,292292+ loff_t off, size_t count) {293293+ struct device *dev = container_of(kobj, struct device, kobj);294294+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));295295+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));296296+ int retval = 0, difference;297297+298298+ /* I need to get my data in one piece */299299+ if (off != 0 || count != sizeof(struct kone_settings))300300+ return -EINVAL;301301+302302+ mutex_lock(&kone->kone_lock);303303+ difference = memcmp(buf, &kone->settings, sizeof(struct kone_settings));304304+ if (difference) {305305+ retval = kone_set_settings(usb_dev,306306+ (struct kone_settings const *)buf);307307+ if (!retval)308308+ memcpy(&kone->settings, buf,309309+ sizeof(struct kone_settings));310310+ }311311+ mutex_unlock(&kone->kone_lock);312312+313313+ if (retval)314314+ return retval;315315+316316+ /*317317+ * If we get here, treat settings as okay and update actual values318318+ * according to startup_profile319319+ */320320+ kone->actual_profile = kone->settings.startup_profile;321321+ kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;322322+323323+ return sizeof(struct kone_settings);324324+}325325+326326+static ssize_t kone_sysfs_read_profilex(struct kobject *kobj,327327+ struct bin_attribute *attr, char *buf,328328+ loff_t off, size_t count, int number) {329329+ struct device *dev = container_of(kobj, struct device, kobj);330330+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));331331+332332+ if (off >= sizeof(struct kone_profile))333333+ return 0;334334+335335+ if (off + count > sizeof(struct kone_profile))336336+ count = sizeof(struct kone_profile) - off;337337+338338+ mutex_lock(&kone->kone_lock);339339+ memcpy(buf, &kone->profiles[number - 1], sizeof(struct kone_profile));340340+ mutex_unlock(&kone->kone_lock);341341+342342+ return count;343343+}344344+345345+static ssize_t kone_sysfs_read_profile1(struct kobject *kobj,346346+ struct bin_attribute *attr, char *buf,347347+ loff_t off, size_t count) {348348+ return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 1);349349+}350350+351351+static ssize_t kone_sysfs_read_profile2(struct kobject *kobj,352352+ struct bin_attribute *attr, char *buf,353353+ loff_t off, size_t count) {354354+ return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 2);355355+}356356+357357+static ssize_t kone_sysfs_read_profile3(struct kobject *kobj,358358+ struct bin_attribute *attr, char *buf,359359+ loff_t off, size_t count) {360360+ return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 3);361361+}362362+363363+static ssize_t kone_sysfs_read_profile4(struct kobject *kobj,364364+ struct bin_attribute *attr, char *buf,365365+ loff_t off, size_t count) {366366+ return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 4);367367+}368368+369369+static ssize_t kone_sysfs_read_profile5(struct kobject *kobj,370370+ struct bin_attribute *attr, char *buf,371371+ loff_t off, size_t count) {372372+ return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 5);373373+}374374+375375+/* Writes data only if different to stored data */376376+static ssize_t kone_sysfs_write_profilex(struct kobject *kobj,377377+ struct bin_attribute *attr, char *buf,378378+ loff_t off, size_t count, int number) {379379+ struct device *dev = container_of(kobj, struct device, kobj);380380+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));381381+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));382382+ struct kone_profile *profile;383383+ int retval = 0, difference;384384+385385+ /* I need to get my data in one piece */386386+ if (off != 0 || count != sizeof(struct kone_profile))387387+ return -EINVAL;388388+389389+ profile = &kone->profiles[number - 1];390390+391391+ mutex_lock(&kone->kone_lock);392392+ difference = memcmp(buf, profile, sizeof(struct kone_profile));393393+ if (difference) {394394+ retval = kone_set_profile(usb_dev,395395+ (struct kone_profile const *)buf, number);396396+ if (!retval)397397+ memcpy(profile, buf, sizeof(struct kone_profile));398398+ }399399+ mutex_unlock(&kone->kone_lock);400400+401401+ if (retval)402402+ return retval;403403+404404+ return sizeof(struct kone_profile);405405+}406406+407407+static ssize_t kone_sysfs_write_profile1(struct kobject *kobj,408408+ struct bin_attribute *attr, char *buf,409409+ loff_t off, size_t count) {410410+ return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 1);411411+}412412+413413+static ssize_t kone_sysfs_write_profile2(struct kobject *kobj,414414+ struct bin_attribute *attr, char *buf,415415+ loff_t off, size_t count) {416416+ return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 2);417417+}418418+419419+static ssize_t kone_sysfs_write_profile3(struct kobject *kobj,420420+ struct bin_attribute *attr, char *buf,421421+ loff_t off, size_t count) {422422+ return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 3);423423+}424424+425425+static ssize_t kone_sysfs_write_profile4(struct kobject *kobj,426426+ struct bin_attribute *attr, char *buf,427427+ loff_t off, size_t count) {428428+ return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 4);429429+}430430+431431+static ssize_t kone_sysfs_write_profile5(struct kobject *kobj,432432+ struct bin_attribute *attr, char *buf,433433+ loff_t off, size_t count) {434434+ return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 5);435435+}436436+437437+static ssize_t kone_sysfs_show_actual_profile(struct device *dev,438438+ struct device_attribute *attr, char *buf)439439+{440440+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));441441+ return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_profile);442442+}443443+444444+static ssize_t kone_sysfs_show_actual_dpi(struct device *dev,445445+ struct device_attribute *attr, char *buf)446446+{447447+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));448448+ return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_dpi);449449+}450450+451451+/* weight is read each time, since we don't get informed when it's changed */452452+static ssize_t kone_sysfs_show_weight(struct device *dev,453453+ struct device_attribute *attr, char *buf)454454+{455455+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));456456+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));457457+ int weight = 0;458458+ int retval;459459+460460+ mutex_lock(&kone->kone_lock);461461+ retval = kone_get_weight(usb_dev, &weight);462462+ mutex_unlock(&kone->kone_lock);463463+464464+ if (retval)465465+ return retval;466466+ return snprintf(buf, PAGE_SIZE, "%d\n", weight);467467+}468468+469469+static ssize_t kone_sysfs_show_firmware_version(struct device *dev,470470+ struct device_attribute *attr, char *buf)471471+{472472+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));473473+ return snprintf(buf, PAGE_SIZE, "%d\n", kone->firmware_version);474474+}475475+476476+static ssize_t kone_sysfs_show_tcu(struct device *dev,477477+ struct device_attribute *attr, char *buf)478478+{479479+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));480480+ return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.tcu);481481+}482482+483483+static int kone_tcu_command(struct usb_device *usb_dev, int number)484484+{485485+ int len;486486+ char *value;487487+488488+ value = kmalloc(1, GFP_KERNEL);489489+ if (!value)490490+ return -ENOMEM;491491+492492+ *value = number;493493+494494+ len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),495495+ USB_REQ_SET_CONFIGURATION,496496+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,497497+ kone_command_calibrate, 0, value, 1,498498+ USB_CTRL_SET_TIMEOUT);499499+500500+ kfree(value);501501+ return ((len != 1) ? -EIO : 0);502502+}503503+504504+/*505505+ * Calibrating the tcu is the only action that changes settings data inside the506506+ * mouse, so this data needs to be reread507507+ */508508+static ssize_t kone_sysfs_set_tcu(struct device *dev,509509+ struct device_attribute *attr, char const *buf, size_t size)510510+{511511+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));512512+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));513513+ int retval;514514+ unsigned long state;515515+516516+ retval = strict_strtoul(buf, 10, &state);517517+ if (retval)518518+ return retval;519519+520520+ if (state != 0 && state != 1)521521+ return -EINVAL;522522+523523+ mutex_lock(&kone->kone_lock);524524+525525+ if (state == 1) { /* state activate */526526+ retval = kone_tcu_command(usb_dev, 1);527527+ if (retval)528528+ goto exit_unlock;529529+ retval = kone_tcu_command(usb_dev, 2);530530+ if (retval)531531+ goto exit_unlock;532532+ ssleep(5); /* tcu needs this time for calibration */533533+ retval = kone_tcu_command(usb_dev, 3);534534+ if (retval)535535+ goto exit_unlock;536536+ retval = kone_tcu_command(usb_dev, 0);537537+ if (retval)538538+ goto exit_unlock;539539+ retval = kone_tcu_command(usb_dev, 4);540540+ if (retval)541541+ goto exit_unlock;542542+ /*543543+ * Kone needs this time to settle things.544544+ * Reading settings too early will result in invalid data.545545+ * Roccat's driver waits 1 sec, maybe this time could be546546+ * shortened.547547+ */548548+ ssleep(1);549549+ }550550+551551+ /* calibration changes values in settings, so reread */552552+ retval = kone_get_settings(usb_dev, &kone->settings);553553+ if (retval)554554+ goto exit_no_settings;555555+556556+ /* only write settings back if activation state is different */557557+ if (kone->settings.tcu != state) {558558+ kone->settings.tcu = state;559559+ kone_set_settings_checksum(&kone->settings);560560+561561+ retval = kone_set_settings(usb_dev, &kone->settings);562562+ if (retval) {563563+ dev_err(&usb_dev->dev, "couldn't set tcu state\n");564564+ /*565565+ * try to reread valid settings into buffer overwriting566566+ * first error code567567+ */568568+ retval = kone_get_settings(usb_dev, &kone->settings);569569+ if (retval)570570+ goto exit_no_settings;571571+ goto exit_unlock;572572+ }573573+ }574574+575575+ retval = size;576576+exit_no_settings:577577+ dev_err(&usb_dev->dev, "couldn't read settings\n");578578+exit_unlock:579579+ mutex_unlock(&kone->kone_lock);580580+ return retval;581581+}582582+583583+static ssize_t kone_sysfs_show_startup_profile(struct device *dev,584584+ struct device_attribute *attr, char *buf)585585+{586586+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));587587+ return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.startup_profile);588588+}589589+590590+static ssize_t kone_sysfs_set_startup_profile(struct device *dev,591591+ struct device_attribute *attr, char const *buf, size_t size)592592+{593593+ struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));594594+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));595595+ int retval;596596+ unsigned long new_startup_profile;597597+598598+ retval = strict_strtoul(buf, 10, &new_startup_profile);599599+ if (retval)600600+ return retval;601601+602602+ if (new_startup_profile < 1 || new_startup_profile > 5)603603+ return -EINVAL;604604+605605+ mutex_lock(&kone->kone_lock);606606+607607+ kone->settings.startup_profile = new_startup_profile;608608+ kone_set_settings_checksum(&kone->settings);609609+610610+ retval = kone_set_settings(usb_dev, &kone->settings);611611+612612+ mutex_unlock(&kone->kone_lock);613613+614614+ if (retval)615615+ return retval;616616+617617+ /* changing the startup profile immediately activates this profile */618618+ kone->actual_profile = new_startup_profile;619619+ kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;620620+621621+ return size;622622+}623623+624624+/*625625+ * This file is used by userland software to find devices that are handled by626626+ * this driver. This provides a consistent way for actual and older kernels627627+ * where this driver replaced usbhid instead of generic-usb.628628+ * Driver capabilities are determined by version number.629629+ */630630+static ssize_t kone_sysfs_show_driver_version(struct device *dev,631631+ struct device_attribute *attr, char *buf)632632+{633633+ return snprintf(buf, PAGE_SIZE, ROCCAT_KONE_DRIVER_VERSION "\n");634634+}635635+636636+/*637637+ * Read actual dpi settings.638638+ * Returns raw value for further processing. Refer to enum kone_polling_rates to639639+ * get real value.640640+ */641641+static DEVICE_ATTR(actual_dpi, 0440, kone_sysfs_show_actual_dpi, NULL);642642+643643+static DEVICE_ATTR(actual_profile, 0440, kone_sysfs_show_actual_profile, NULL);644644+645645+/*646646+ * The mouse can be equipped with one of four supplied weights from 5 to 20647647+ * grams which are recognized and its value can be read out.648648+ * This returns the raw value reported by the mouse for easy evaluation by649649+ * software. Refer to enum kone_weights to get corresponding real weight.650650+ */651651+static DEVICE_ATTR(weight, 0440, kone_sysfs_show_weight, NULL);652652+653653+/*654654+ * Prints firmware version stored in mouse as integer.655655+ * The raw value reported by the mouse is returned for easy evaluation, to get656656+ * the real version number the decimal point has to be shifted 2 positions to657657+ * the left. E.g. a value of 138 means 1.38.658658+ */659659+static DEVICE_ATTR(firmware_version, 0440,660660+ kone_sysfs_show_firmware_version, NULL);661661+662662+/*663663+ * Prints state of Tracking Control Unit as number where 0 = off and 1 = on664664+ * Writing 0 deactivates tcu and writing 1 calibrates and activates the tcu665665+ */666666+static DEVICE_ATTR(tcu, 0660, kone_sysfs_show_tcu, kone_sysfs_set_tcu);667667+668668+/* Prints and takes the number of the profile the mouse starts with */669669+static DEVICE_ATTR(startup_profile, 0660,670670+ kone_sysfs_show_startup_profile,671671+ kone_sysfs_set_startup_profile);672672+673673+static DEVICE_ATTR(kone_driver_version, 0440,674674+ kone_sysfs_show_driver_version, NULL);675675+676676+static struct attribute *kone_attributes[] = {677677+ &dev_attr_actual_dpi.attr,678678+ &dev_attr_actual_profile.attr,679679+ &dev_attr_weight.attr,680680+ &dev_attr_firmware_version.attr,681681+ &dev_attr_tcu.attr,682682+ &dev_attr_startup_profile.attr,683683+ &dev_attr_kone_driver_version.attr,684684+ NULL685685+};686686+687687+static struct attribute_group kone_attribute_group = {688688+ .attrs = kone_attributes689689+};690690+691691+static struct bin_attribute kone_settings_attr = {692692+ .attr = { .name = "settings", .mode = 0660 },693693+ .size = sizeof(struct kone_settings),694694+ .read = kone_sysfs_read_settings,695695+ .write = kone_sysfs_write_settings696696+};697697+698698+static struct bin_attribute kone_profile1_attr = {699699+ .attr = { .name = "profile1", .mode = 0660 },700700+ .size = sizeof(struct kone_profile),701701+ .read = kone_sysfs_read_profile1,702702+ .write = kone_sysfs_write_profile1703703+};704704+705705+static struct bin_attribute kone_profile2_attr = {706706+ .attr = { .name = "profile2", .mode = 0660 },707707+ .size = sizeof(struct kone_profile),708708+ .read = kone_sysfs_read_profile2,709709+ .write = kone_sysfs_write_profile2710710+};711711+712712+static struct bin_attribute kone_profile3_attr = {713713+ .attr = { .name = "profile3", .mode = 0660 },714714+ .size = sizeof(struct kone_profile),715715+ .read = kone_sysfs_read_profile3,716716+ .write = kone_sysfs_write_profile3717717+};718718+719719+static struct bin_attribute kone_profile4_attr = {720720+ .attr = { .name = "profile4", .mode = 0660 },721721+ .size = sizeof(struct kone_profile),722722+ .read = kone_sysfs_read_profile4,723723+ .write = kone_sysfs_write_profile4724724+};725725+726726+static struct bin_attribute kone_profile5_attr = {727727+ .attr = { .name = "profile5", .mode = 0660 },728728+ .size = sizeof(struct kone_profile),729729+ .read = kone_sysfs_read_profile5,730730+ .write = kone_sysfs_write_profile5731731+};732732+733733+static int kone_create_sysfs_attributes(struct usb_interface *intf)734734+{735735+ int retval;736736+737737+ retval = sysfs_create_group(&intf->dev.kobj, &kone_attribute_group);738738+ if (retval)739739+ goto exit_1;740740+741741+ retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_settings_attr);742742+ if (retval)743743+ goto exit_2;744744+745745+ retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile1_attr);746746+ if (retval)747747+ goto exit_3;748748+749749+ retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile2_attr);750750+ if (retval)751751+ goto exit_4;752752+753753+ retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile3_attr);754754+ if (retval)755755+ goto exit_5;756756+757757+ retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile4_attr);758758+ if (retval)759759+ goto exit_6;760760+761761+ retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile5_attr);762762+ if (retval)763763+ goto exit_7;764764+765765+ return 0;766766+767767+exit_7:768768+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile4_attr);769769+exit_6:770770+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile3_attr);771771+exit_5:772772+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile2_attr);773773+exit_4:774774+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile1_attr);775775+exit_3:776776+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_settings_attr);777777+exit_2:778778+ sysfs_remove_group(&intf->dev.kobj, &kone_attribute_group);779779+exit_1:780780+ return retval;781781+}782782+783783+static void kone_remove_sysfs_attributes(struct usb_interface *intf)784784+{785785+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile5_attr);786786+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile4_attr);787787+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile3_attr);788788+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile2_attr);789789+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile1_attr);790790+ sysfs_remove_bin_file(&intf->dev.kobj, &kone_settings_attr);791791+ sysfs_remove_group(&intf->dev.kobj, &kone_attribute_group);792792+}793793+794794+static int kone_init_kone_device_struct(struct usb_device *usb_dev,795795+ struct kone_device *kone)796796+{797797+ uint i;798798+ int retval;799799+800800+ mutex_init(&kone->kone_lock);801801+802802+ for (i = 0; i < 5; ++i) {803803+ retval = kone_get_profile(usb_dev, &kone->profiles[i], i + 1);804804+ if (retval)805805+ return retval;806806+ }807807+808808+ retval = kone_get_settings(usb_dev, &kone->settings);809809+ if (retval)810810+ return retval;811811+812812+ retval = kone_get_firmware_version(usb_dev, &kone->firmware_version);813813+ if (retval)814814+ return retval;815815+816816+ kone->actual_profile = kone->settings.startup_profile;817817+ kone->actual_dpi = kone->profiles[kone->actual_profile].startup_dpi;818818+819819+ return 0;820820+}821821+822822+/*823823+ * Since IGNORE_MOUSE quirk moved to hid-apple, there is no way to bind only to824824+ * mousepart if usb_hid is compiled into the kernel and kone is compiled as825825+ * module.826826+ * Secial behaviour is bound only to mousepart since only mouseevents contain827827+ * additional notifications.828828+ */829829+static int kone_init_specials(struct hid_device *hdev)830830+{831831+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);832832+ struct usb_device *usb_dev = interface_to_usbdev(intf);833833+ struct kone_device *kone;834834+ int retval;835835+836836+ if (intf->cur_altsetting->desc.bInterfaceProtocol837837+ == USB_INTERFACE_PROTOCOL_MOUSE) {838838+839839+ kone = kzalloc(sizeof(*kone), GFP_KERNEL);840840+ if (!kone) {841841+ dev_err(&hdev->dev, "can't alloc device descriptor\n");842842+ return -ENOMEM;843843+ }844844+ hid_set_drvdata(hdev, kone);845845+846846+ retval = kone_init_kone_device_struct(usb_dev, kone);847847+ if (retval) {848848+ dev_err(&hdev->dev,849849+ "couldn't init struct kone_device\n");850850+ goto exit_free;851851+ }852852+ retval = kone_create_sysfs_attributes(intf);853853+ if (retval) {854854+ dev_err(&hdev->dev, "cannot create sysfs files\n");855855+ goto exit_free;856856+ }857857+ } else {858858+ hid_set_drvdata(hdev, NULL);859859+ }860860+861861+ return 0;862862+exit_free:863863+ kfree(kone);864864+ return retval;865865+}866866+867867+868868+static void kone_remove_specials(struct hid_device *hdev)869869+{870870+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);871871+872872+ if (intf->cur_altsetting->desc.bInterfaceProtocol873873+ == USB_INTERFACE_PROTOCOL_MOUSE) {874874+ kone_remove_sysfs_attributes(intf);875875+ kfree(hid_get_drvdata(hdev));876876+ }877877+}878878+879879+static int kone_probe(struct hid_device *hdev, const struct hid_device_id *id)880880+{881881+ int retval;882882+883883+ retval = hid_parse(hdev);884884+ if (retval) {885885+ dev_err(&hdev->dev, "parse failed\n");886886+ goto exit;887887+ }888888+889889+ retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);890890+ if (retval) {891891+ dev_err(&hdev->dev, "hw start failed\n");892892+ goto exit;893893+ }894894+895895+ retval = kone_init_specials(hdev);896896+ if (retval) {897897+ dev_err(&hdev->dev, "couldn't install mouse\n");898898+ goto exit_stop;899899+ }900900+901901+ return 0;902902+903903+exit_stop:904904+ hid_hw_stop(hdev);905905+exit:906906+ return retval;907907+}908908+909909+static void kone_remove(struct hid_device *hdev)910910+{911911+ kone_remove_specials(hdev);912912+ hid_hw_stop(hdev);913913+}914914+915915+/* handle special events and keep actual profile and dpi values up to date */916916+static void kone_keep_values_up_to_date(struct kone_device *kone,917917+ struct kone_mouse_event const *event)918918+{919919+ switch (event->event) {920920+ case kone_mouse_event_switch_profile:921921+ case kone_mouse_event_osd_profile:922922+ kone->actual_profile = event->value;923923+ kone->actual_dpi = kone->profiles[kone->actual_profile - 1].924924+ startup_dpi;925925+ break;926926+ case kone_mouse_event_switch_dpi:927927+ case kone_mouse_event_osd_dpi:928928+ kone->actual_dpi = event->value;929929+ break;930930+ }931931+}932932+933933+/*934934+ * Is called for keyboard- and mousepart.935935+ * Only mousepart gets informations about special events in its extended event936936+ * structure.937937+ */938938+static int kone_raw_event(struct hid_device *hdev, struct hid_report *report,939939+ u8 *data, int size)940940+{941941+ struct kone_device *kone = hid_get_drvdata(hdev);942942+ struct kone_mouse_event *event = (struct kone_mouse_event *)data;943943+944944+ /* keyboard events are always processed by default handler */945945+ if (size != sizeof(struct kone_mouse_event))946946+ return 0;947947+948948+ /*949949+ * Firmware 1.38 introduced new behaviour for tilt and special buttons.950950+ * Pressed button is reported in each movement event.951951+ * Workaround sends only one event per press.952952+ */953953+ if (memcmp(&kone->last_mouse_event.tilt, &event->tilt, 5))954954+ memcpy(&kone->last_mouse_event, event,955955+ sizeof(struct kone_mouse_event));956956+ else957957+ memset(&event->tilt, 0, 5);958958+959959+ kone_keep_values_up_to_date(kone, event);960960+961961+ return 0; /* always do further processing */962962+}963963+964964+static const struct hid_device_id kone_devices[] = {965965+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },966966+ { }967967+};968968+969969+MODULE_DEVICE_TABLE(hid, kone_devices);970970+971971+static struct hid_driver kone_driver = {972972+ .name = "kone",973973+ .id_table = kone_devices,974974+ .probe = kone_probe,975975+ .remove = kone_remove,976976+ .raw_event = kone_raw_event977977+};978978+979979+static int __init kone_init(void)980980+{981981+ return hid_register_driver(&kone_driver);982982+}983983+984984+static void __exit kone_exit(void)985985+{986986+ hid_unregister_driver(&kone_driver);987987+}988988+989989+module_init(kone_init);990990+module_exit(kone_exit);991991+992992+MODULE_AUTHOR("Stefan Achatz");993993+MODULE_DESCRIPTION("USB Roccat Kone driver");994994+MODULE_LICENSE("GPL v2");
+224
drivers/hid/hid-roccat-kone.h
···11+#ifndef __HID_ROCCAT_KONE_H22+#define __HID_ROCCAT_KONE_H33+44+/*55+ * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>66+ */77+88+/*99+ * This program is free software; you can redistribute it and/or modify it1010+ * under the terms of the GNU General Public License as published by the Free1111+ * Software Foundation; either version 2 of the License, or (at your option)1212+ * any later version.1313+ */1414+1515+#include <linux/types.h>1616+1717+#define ROCCAT_KONE_DRIVER_VERSION "v0.3.1"1818+1919+#pragma pack(push)2020+#pragma pack(1)2121+2222+struct kone_keystroke {2323+ uint8_t key;2424+ uint8_t action;2525+ uint16_t period; /* in milliseconds */2626+};2727+2828+enum kone_keystroke_buttons {2929+ kone_keystroke_button_1 = 0xf0, /* left mouse button */3030+ kone_keystroke_button_2 = 0xf1, /* right mouse button */3131+ kone_keystroke_button_3 = 0xf2, /* wheel */3232+ kone_keystroke_button_9 = 0xf3, /* side button up */3333+ kone_keystroke_button_8 = 0xf4 /* side button down */3434+};3535+3636+enum kone_keystroke_actions {3737+ kone_keystroke_action_press = 0,3838+ kone_keystroke_action_release = 13939+};4040+4141+struct kone_button_info {4242+ uint8_t number; /* range 1-8 */4343+ uint8_t type;4444+ uint8_t macro_type; /* 0 = short, 1 = overlong */4545+ uint8_t macro_set_name[16]; /* can be max 15 chars long */4646+ uint8_t macro_name[16]; /* can be max 15 chars long */4747+ uint8_t count;4848+ struct kone_keystroke keystrokes[20];4949+};5050+5151+enum kone_button_info_types {5252+ /* valid button types until firmware 1.32 */5353+ kone_button_info_type_button_1 = 0x1, /* click (left mouse button) */5454+ kone_button_info_type_button_2 = 0x2, /* menu (right mouse button)*/5555+ kone_button_info_type_button_3 = 0x3, /* scroll (wheel) */5656+ kone_button_info_type_double_click = 0x4,5757+ kone_button_info_type_key = 0x5,5858+ kone_button_info_type_macro = 0x6,5959+ kone_button_info_type_off = 0x7,6060+ /* TODO clarify function and rename */6161+ kone_button_info_type_osd_xy_prescaling = 0x8,6262+ kone_button_info_type_osd_dpi = 0x9,6363+ kone_button_info_type_osd_profile = 0xa,6464+ kone_button_info_type_button_9 = 0xb, /* ie forward */6565+ kone_button_info_type_button_8 = 0xc, /* ie backward */6666+ kone_button_info_type_dpi_up = 0xd, /* internal */6767+ kone_button_info_type_dpi_down = 0xe, /* internal */6868+ kone_button_info_type_button_7 = 0xf, /* tilt left */6969+ kone_button_info_type_button_6 = 0x10, /* tilt right */7070+ kone_button_info_type_profile_up = 0x11, /* internal */7171+ kone_button_info_type_profile_down = 0x12, /* internal */7272+ /* additional valid button types since firmware 1.38 */7373+ kone_button_info_type_multimedia_open_player = 0x20,7474+ kone_button_info_type_multimedia_next_track = 0x21,7575+ kone_button_info_type_multimedia_prev_track = 0x22,7676+ kone_button_info_type_multimedia_play_pause = 0x23,7777+ kone_button_info_type_multimedia_stop = 0x24,7878+ kone_button_info_type_multimedia_mute = 0x25,7979+ kone_button_info_type_multimedia_volume_up = 0x26,8080+ kone_button_info_type_multimedia_volume_down = 0x278181+};8282+8383+enum kone_button_info_numbers {8484+ kone_button_top = 1,8585+ kone_button_wheel_tilt_left = 2,8686+ kone_button_wheel_tilt_right = 3,8787+ kone_button_forward = 4,8888+ kone_button_backward = 5,8989+ kone_button_middle = 6,9090+ kone_button_plus = 7,9191+ kone_button_minus = 8,9292+};9393+9494+struct kone_light_info {9595+ uint8_t number; /* number of light 1-5 */9696+ uint8_t mod; /* 1 = on, 2 = off */9797+ uint8_t red; /* range 0x00-0xff */9898+ uint8_t green; /* range 0x00-0xff */9999+ uint8_t blue; /* range 0x00-0xff */100100+};101101+102102+struct kone_profile {103103+ uint16_t size; /* always 975 */104104+ uint16_t unused; /* always 0 */105105+106106+ /*107107+ * range 1-5108108+ * This number does not need to correspond with location where profile109109+ * saved110110+ */111111+ uint8_t profile; /* range 1-5 */112112+113113+ uint16_t main_sensitivity; /* range 100-1000 */114114+ uint8_t xy_sensitivity_enabled; /* 1 = on, 2 = off */115115+ uint16_t x_sensitivity; /* range 100-1000 */116116+ uint16_t y_sensitivity; /* range 100-1000 */117117+ uint8_t dpi_rate; /* bit 1 = 800, ... */118118+ uint8_t startup_dpi; /* range 1-6 */119119+ uint8_t polling_rate; /* 1 = 125Hz, 2 = 500Hz, 3 = 1000Hz */120120+ /* kone has no dcu121121+ * value is always 2 in firmwares <= 1.32 and122122+ * 1 in firmwares > 1.32123123+ */124124+ uint8_t dcu_flag;125125+ uint8_t light_effect_1; /* range 1-3 */126126+ uint8_t light_effect_2; /* range 1-5 */127127+ uint8_t light_effect_3; /* range 1-4 */128128+ uint8_t light_effect_speed; /* range 0-255 */129129+130130+ struct kone_light_info light_infos[5];131131+ /* offset is kone_button_info_numbers - 1 */132132+ struct kone_button_info button_infos[8];133133+134134+ uint16_t checksum; /* \brief holds checksum of struct */135135+};136136+137137+enum kone_polling_rates {138138+ kone_polling_rate_125 = 1,139139+ kone_polling_rate_500 = 2,140140+ kone_polling_rate_1000 = 3141141+};142142+143143+struct kone_settings {144144+ uint16_t size; /* always 36 */145145+ uint8_t startup_profile; /* 1-5 */146146+ uint8_t unknown1;147147+ uint8_t tcu; /* 0 = off, 1 = on */148148+ uint8_t unknown2[23];149149+ uint8_t calibration_data[4];150150+ uint8_t unknown3[2];151151+ uint16_t checksum;152152+};153153+154154+/*155155+ * 12 byte mouse event read by interrupt_read156156+ */157157+struct kone_mouse_event {158158+ uint8_t report_number; /* always 1 */159159+ uint8_t button;160160+ uint16_t x;161161+ uint16_t y;162162+ uint8_t wheel; /* up = 1, down = -1 */163163+ uint8_t tilt; /* right = 1, left = -1 */164164+ uint8_t unknown;165165+ uint8_t event;166166+ uint8_t value; /* press = 0, release = 1 */167167+ uint8_t macro_key; /* 0 to 8 */168168+};169169+170170+enum kone_mouse_events {171171+ /* osd events are thought to be display on screen */172172+ kone_mouse_event_osd_dpi = 0xa0,173173+ kone_mouse_event_osd_profile = 0xb0,174174+ /* TODO clarify meaning and occurence of kone_mouse_event_calibration */175175+ kone_mouse_event_calibration = 0xc0,176176+ kone_mouse_event_call_overlong_macro = 0xe0,177177+ /* switch events notify if user changed values with mousebutton click */178178+ kone_mouse_event_switch_dpi = 0xf0,179179+ kone_mouse_event_switch_profile = 0xf1180180+};181181+182182+enum kone_commands {183183+ kone_command_profile = 0x5a,184184+ kone_command_settings = 0x15a,185185+ kone_command_firmware_version = 0x25a,186186+ kone_command_weight = 0x45a,187187+ kone_command_calibrate = 0x55a,188188+ kone_command_confirm_write = 0x65a,189189+ kone_command_firmware = 0xe5a190190+};191191+192192+#pragma pack(pop)193193+194194+struct kone_device {195195+ /*196196+ * Storing actual values when we get informed about changes since there197197+ * is no way of getting this information from the device on demand198198+ */199199+ int actual_profile, actual_dpi;200200+ /* Used for neutralizing abnormal button behaviour */201201+ struct kone_mouse_event last_mouse_event;202202+203203+ /*204204+ * It's unlikely that multiple sysfs attributes are accessed at a time,205205+ * so only one mutex is used to secure hardware access and profiles and206206+ * settings of this struct.207207+ */208208+ struct mutex kone_lock;209209+210210+ /*211211+ * Storing the data here reduces IO and ensures that data is available212212+ * when its needed (E.g. interrupt handler).213213+ */214214+ struct kone_profile profiles[5];215215+ struct kone_settings settings;216216+217217+ /*218218+ * firmware doesn't change unless firmware update is implemented,219219+ * so it's read only once220220+ */221221+ int firmware_version;222222+};223223+224224+#endif
+1
drivers/hid/usbhid/hid-core.c
···623623624624 return 0;625625}626626+EXPORT_SYMBOL_GPL(usbhid_wait_io);626627627628static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)628629{