diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 14:58:38 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-07 14:58:38 -0400 |
| commit | bc75450cc3db3485db1e289fef8c1028ba38296a (patch) | |
| tree | cd7dca62f9f60524ed5241a9e2fbdce1c8ab0bb7 | |
| parent | e6e3d8f8f4f06caf25004c749bb2ba84f18c7d39 (diff) | |
| parent | 179023e6af0c608ffb505821223f5580853ef6b8 (diff) | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina:
- Integrated Sensor Hub support (Cherrytrail+) from Srinivas Pandruvada
- Big cleanup of Wacom driver; namely it's now using devres, and the
standardized LED API so that libinput doesn't need to have root
access any more, with substantial amount of other cleanups
piggy-backing on top. All this from Benjamin Tissoires
- Report descriptor parsing would now ignore and out-of-range System
controls in case of the application actually being System Control.
This fixes quite some issues with several devices, and allows us to
remove a few ->report_fixup callbacks. From Benjamin Tissoires
- ... a lot of other assorted small fixes and device ID additions
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (76 commits)
HID: add missing \n to end of dev_warn messages
HID: alps: fix multitouch cursor issue
HID: hid-logitech: Documentation updates/corrections
HID: hid-logitech: Improve Wingman Formula Force GP support
HID: hid-logitech: Rewrite of descriptor for all DF wheels
HID: hid-logitech: Compute combined pedals value
HID: hid-logitech: Add combined pedal support Logitech wheels
HID: hid-logitech: Introduce control for combined pedals feature
HID: sony: Update copyright and add Dualshock 4 rate control note
HID: sony: Defer the initial USB Sixaxis output report
HID: sony: Relax duplicate checking for USB-only devices
Revert "HID: microsoft: fix invalid rdesc for 3k kbd"
HID: alps: fix error return code in alps_input_configured()
HID: alps: fix stick device not working after resume
HID: support for keyboard - Corsair STRAFE
HID: alps: Fix memory leak
HID: uclogic: Add support for UC-Logic TWHA60 v3
HID: uclogic: Override constant descriptors
HID: uclogic: Support UGTizer GP0610 partially
HID: uclogic: Add support for several more tablets
...
48 files changed, 9645 insertions, 672 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff index db197a879580..305dffd229a8 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff +++ b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff | |||
| @@ -35,6 +35,12 @@ Description: Displays a set of alternate modes supported by a wheel. Each | |||
| 35 | DF-EX <*--------> G25 <-> G27 | 35 | DF-EX <*--------> G25 <-> G27 |
| 36 | DF-EX <*----------------> G27 | 36 | DF-EX <*----------------> G27 |
| 37 | 37 | ||
| 38 | G29: | ||
| 39 | DF-EX <*> DFP <-> G25 <-> G27 <-> G29 | ||
| 40 | DF-EX <*--------> G25 <-> G27 <-> G29 | ||
| 41 | DF-EX <*----------------> G27 <-> G29 | ||
| 42 | DF-EX <*------------------------> G29 | ||
| 43 | |||
| 38 | DFGT: | 44 | DFGT: |
| 39 | DF-EX <*> DFP <-> DFGT | 45 | DF-EX <*> DFP <-> DFGT |
| 40 | DF-EX <*--------> DFGT | 46 | DF-EX <*--------> DFGT |
| @@ -50,3 +56,12 @@ Description: Displays the real model of the wheel regardless of any | |||
| 50 | alternate mode the wheel might be switched to. | 56 | alternate mode the wheel might be switched to. |
| 51 | It is a read-only value. | 57 | It is a read-only value. |
| 52 | This entry is not created for devices that have only one mode. | 58 | This entry is not created for devices that have only one mode. |
| 59 | |||
| 60 | What: /sys/bus/hid/drivers/logitech/<dev>/combine_pedals | ||
| 61 | Date: Sep 2016 | ||
| 62 | KernelVersion: 4.9 | ||
| 63 | Contact: Simon Wood <simon@mungewell.org> | ||
| 64 | Description: Controls whether a combined value of accelerator and brake is | ||
| 65 | reported on the Y axis of the controller. Useful for older games | ||
| 66 | which can do not work with separate accelerator/brake axis. | ||
| 67 | Off ('0') by default, enabled by setting '1'. | ||
diff --git a/Documentation/ABI/testing/sysfs-driver-wacom b/Documentation/ABI/testing/sysfs-driver-wacom index dca429340772..2aa5503ee200 100644 --- a/Documentation/ABI/testing/sysfs-driver-wacom +++ b/Documentation/ABI/testing/sysfs-driver-wacom | |||
| @@ -24,6 +24,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status0_luminance | |||
| 24 | Date: August 2014 | 24 | Date: August 2014 |
| 25 | Contact: linux-input@vger.kernel.org | 25 | Contact: linux-input@vger.kernel.org |
| 26 | Description: | 26 | Description: |
| 27 | <obsoleted by the LED class API now exported by the driver> | ||
| 27 | Writing to this file sets the status LED luminance (1..127) | 28 | Writing to this file sets the status LED luminance (1..127) |
| 28 | when the stylus does not touch the tablet surface, and no | 29 | when the stylus does not touch the tablet surface, and no |
| 29 | button is pressed on the stylus. This luminance level is | 30 | button is pressed on the stylus. This luminance level is |
| @@ -33,6 +34,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status1_luminance | |||
| 33 | Date: August 2014 | 34 | Date: August 2014 |
| 34 | Contact: linux-input@vger.kernel.org | 35 | Contact: linux-input@vger.kernel.org |
| 35 | Description: | 36 | Description: |
| 37 | <obsoleted by the LED class API now exported by the driver> | ||
| 36 | Writing to this file sets the status LED luminance (1..127) | 38 | Writing to this file sets the status LED luminance (1..127) |
| 37 | when the stylus touches the tablet surface, or any button is | 39 | when the stylus touches the tablet surface, or any button is |
| 38 | pressed on the stylus. | 40 | pressed on the stylus. |
| @@ -41,6 +43,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led0_select | |||
| 41 | Date: August 2014 | 43 | Date: August 2014 |
| 42 | Contact: linux-input@vger.kernel.org | 44 | Contact: linux-input@vger.kernel.org |
| 43 | Description: | 45 | Description: |
| 46 | <obsoleted by the LED class API now exported by the driver> | ||
| 44 | Writing to this file sets which one of the four (for Intuos 4 | 47 | Writing to this file sets which one of the four (for Intuos 4 |
| 45 | and Intuos 5) or of the right four (for Cintiq 21UX2 and Cintiq | 48 | and Intuos 5) or of the right four (for Cintiq 21UX2 and Cintiq |
| 46 | 24HD) status LEDs is active (0..3). The other three LEDs on the | 49 | 24HD) status LEDs is active (0..3). The other three LEDs on the |
| @@ -50,6 +53,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led1_select | |||
| 50 | Date: August 2014 | 53 | Date: August 2014 |
| 51 | Contact: linux-input@vger.kernel.org | 54 | Contact: linux-input@vger.kernel.org |
| 52 | Description: | 55 | Description: |
| 56 | <obsoleted by the LED class API now exported by the driver> | ||
| 53 | Writing to this file sets which one of the left four (for Cintiq 21UX2 | 57 | Writing to this file sets which one of the left four (for Cintiq 21UX2 |
| 54 | and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on | 58 | and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on |
| 55 | the left are always inactive. | 59 | the left are always inactive. |
| @@ -91,6 +95,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_remote/<serial_number>/r | |||
| 91 | Date: July 2015 | 95 | Date: July 2015 |
| 92 | Contact: linux-input@vger.kernel.org | 96 | Contact: linux-input@vger.kernel.org |
| 93 | Description: | 97 | Description: |
| 98 | <obsoleted by the LED class API now exported by the driver> | ||
| 94 | Reading from this file reports the mode status of the | 99 | Reading from this file reports the mode status of the |
| 95 | remote as indicated by the LED lights on the device. If no | 100 | remote as indicated by the LED lights on the device. If no |
| 96 | reports have been received from the paired device, reading | 101 | reports have been received from the paired device, reading |
diff --git a/Documentation/hid/intel-ish-hid.txt b/Documentation/hid/intel-ish-hid.txt new file mode 100644 index 000000000000..d48b21c71ddd --- /dev/null +++ b/Documentation/hid/intel-ish-hid.txt | |||
| @@ -0,0 +1,454 @@ | |||
| 1 | Intel Integrated Sensor Hub (ISH) | ||
| 2 | =============================== | ||
| 3 | |||
| 4 | A sensor hub enables the ability to offload sensor polling and algorithm | ||
| 5 | processing to a dedicated low power co-processor. This allows the core | ||
| 6 | processor to go into low power modes more often, resulting in the increased | ||
| 7 | battery life. | ||
| 8 | |||
| 9 | There are many vendors providing external sensor hubs confirming to HID | ||
| 10 | Sensor usage tables, and used in several tablets, 2 in 1 convertible laptops | ||
| 11 | and embedded products. Linux had this support since Linux 3.9. | ||
| 12 | |||
| 13 | IntelĀ® introduced integrated sensor hubs as a part of the SoC starting from | ||
| 14 | Cherry Trail and now supported on multiple generations of CPU packages. There | ||
| 15 | are many commercial devices already shipped with Integrated Sensor Hubs (ISH). | ||
| 16 | These ISH also comply to HID sensor specification, but the difference is the | ||
| 17 | transport protocol used for communication. The current external sensor hubs | ||
| 18 | mainly use HID over i2C or USB. But ISH doesn't use either i2c or USB. | ||
| 19 | |||
| 20 | 1. Overview | ||
| 21 | |||
| 22 | Using a analogy with a usbhid implementation, the ISH follows a similar model | ||
| 23 | for a very high speed communication: | ||
| 24 | |||
| 25 | ----------------- ---------------------- | ||
| 26 | | USB HID | --> | ISH HID | | ||
| 27 | ----------------- ---------------------- | ||
| 28 | ----------------- ---------------------- | ||
| 29 | | USB protocol | --> | ISH Transport | | ||
| 30 | ----------------- ---------------------- | ||
| 31 | ----------------- ---------------------- | ||
| 32 | | EHCI/XHCI | --> | ISH IPC | | ||
| 33 | ----------------- ---------------------- | ||
| 34 | PCI PCI | ||
| 35 | ----------------- ---------------------- | ||
| 36 | |Host controller| --> | ISH processor | | ||
| 37 | ----------------- ---------------------- | ||
| 38 | USB Link | ||
| 39 | ----------------- ---------------------- | ||
| 40 | | USB End points| --> | ISH Clients | | ||
| 41 | ----------------- ---------------------- | ||
| 42 | |||
| 43 | Like USB protocol provides a method for device enumeration, link management | ||
| 44 | and user data encapsulation, the ISH also provides similar services. But it is | ||
| 45 | very light weight tailored to manage and communicate with ISH client | ||
| 46 | applications implemented in the firmware. | ||
| 47 | |||
| 48 | The ISH allows multiple sensor management applications executing in the | ||
| 49 | firmware. Like USB endpoints the messaging can be to/from a client. As part of | ||
| 50 | enumeration process, these clients are identified. These clients can be simple | ||
| 51 | HID sensor applications, sensor calibration application or senor firmware | ||
| 52 | update application. | ||
| 53 | |||
| 54 | The implementation model is similar, like USB bus, ISH transport is also | ||
| 55 | implemented as a bus. Each client application executing in the ISH processor | ||
| 56 | is registered as a device on this bus. The driver, which binds each device | ||
| 57 | (ISH HID driver) identifies the device type and registers with the hid core. | ||
| 58 | |||
| 59 | 2. ISH Implementation: Block Diagram | ||
| 60 | |||
| 61 | --------------------------- | ||
| 62 | | User Space Applications | | ||
| 63 | --------------------------- | ||
| 64 | |||
| 65 | ----------------IIO ABI---------------- | ||
| 66 | -------------------------- | ||
| 67 | | IIO Sensor Drivers | | ||
| 68 | -------------------------- | ||
| 69 | -------------------------- | ||
| 70 | | IIO core | | ||
| 71 | -------------------------- | ||
| 72 | -------------------------- | ||
| 73 | | HID Sensor Hub MFD | | ||
| 74 | -------------------------- | ||
| 75 | -------------------------- | ||
| 76 | | HID Core | | ||
| 77 | -------------------------- | ||
| 78 | -------------------------- | ||
| 79 | | HID over ISH Client | | ||
| 80 | -------------------------- | ||
| 81 | -------------------------- | ||
| 82 | | ISH Transport (ISHTP) | | ||
| 83 | -------------------------- | ||
| 84 | -------------------------- | ||
| 85 | | IPC Drivers | | ||
| 86 | -------------------------- | ||
| 87 | OS | ||
| 88 | ---------------- PCI ----------------- | ||
| 89 | Hardware + Firmware | ||
| 90 | ---------------------------- | ||
| 91 | | ISH Hardware/Firmware(FW) | | ||
| 92 | ---------------------------- | ||
| 93 | |||
| 94 | 3. High level processing in above blocks | ||
| 95 | |||
| 96 | 3.1 Hardware Interface | ||
| 97 | |||
| 98 | The ISH is exposed as "Non-VGA unclassified PCI device" to the host. The PCI | ||
| 99 | product and vendor IDs are changed from different generations of processors. So | ||
| 100 | the source code which enumerate drivers needs to update from generation to | ||
| 101 | generation. | ||
| 102 | |||
| 103 | 3.2 Inter Processor Communication (IPC) driver | ||
| 104 | Location: drivers/hid/intel-ish-hid/ipc | ||
| 105 | |||
| 106 | The IPC message used memory mapped I/O. The registers are defined in | ||
| 107 | hw-ish-regs.h. | ||
| 108 | |||
| 109 | 3.2.1 IPC/FW message types | ||
| 110 | |||
| 111 | There are two types of messages, one for management of link and other messages | ||
| 112 | are to and from transport layers. | ||
| 113 | |||
| 114 | TX and RX of Transport messages | ||
| 115 | |||
| 116 | A set of memory mapped register offers support of multi byte messages TX and | ||
| 117 | RX (E.g.IPC_REG_ISH2HOST_MSG, IPC_REG_HOST2ISH_MSG). The IPC layer maintains | ||
| 118 | internal queues to sequence messages and send them in order to the FW. | ||
| 119 | Optionally the caller can register handler to get notification of completion. | ||
| 120 | A door bell mechanism is used in messaging to trigger processing in host and | ||
| 121 | client firmware side. When ISH interrupt handler is called, the ISH2HOST | ||
| 122 | doorbell register is used by host drivers to determine that the interrupt | ||
| 123 | is for ISH. | ||
| 124 | |||
| 125 | Each side has 32 32-bit message registers and a 32-bit doorbell. Doorbell | ||
| 126 | register has the following format: | ||
| 127 | Bits 0..6: fragment length (7 bits are used) | ||
| 128 | Bits 10..13: encapsulated protocol | ||
| 129 | Bits 16..19: management command (for IPC management protocol) | ||
| 130 | Bit 31: doorbell trigger (signal H/W interrupt to the other side) | ||
| 131 | Other bits are reserved, should be 0. | ||
| 132 | |||
| 133 | 3.2.2 Transport layer interface | ||
| 134 | |||
| 135 | To abstract HW level IPC communication, a set of callbacks are registered. | ||
| 136 | The transport layer uses them to send and receive messages. | ||
| 137 | Refer to struct ishtp_hw_ops for callbacks. | ||
| 138 | |||
| 139 | 3.3 ISH Transport layer | ||
| 140 | Location: drivers/hid/intel-ish-hid/ishtp/ | ||
| 141 | |||
| 142 | 3.3.1 A Generic Transport Layer | ||
| 143 | |||
| 144 | The transport layer is a bi-directional protocol, which defines: | ||
| 145 | - Set of commands to start, stop, connect, disconnect and flow control | ||
| 146 | (ishtp/hbm.h) for details | ||
| 147 | - A flow control mechanism to avoid buffer overflows | ||
| 148 | |||
| 149 | This protocol resembles bus messages described in the following document: | ||
| 150 | http://www.intel.com/content/dam/www/public/us/en/documents/technical-\ | ||
| 151 | specifications/dcmi-hi-1-0-spec.pdf "Chapter 7: Bus Message Layer" | ||
| 152 | |||
| 153 | 3.3.2 Connection and Flow Control Mechanism | ||
| 154 | |||
| 155 | Each FW client and a protocol is identified by an UUID. In order to communicate | ||
| 156 | to a FW client, a connection must be established using connect request and | ||
| 157 | response bus messages. If successful, a pair (host_client_id and fw_client_id) | ||
| 158 | will identify the connection. | ||
| 159 | |||
| 160 | Once connection is established, peers send each other flow control bus messages | ||
| 161 | independently. Every peer may send a message only if it has received a | ||
| 162 | flow-control credit before. Once it sent a message, it may not send another one | ||
| 163 | before receiving the next flow control credit. | ||
| 164 | Either side can send disconnect request bus message to end communication. Also | ||
| 165 | the link will be dropped if major FW reset occurs. | ||
| 166 | |||
| 167 | 3.3.3 Peer to Peer data transfer | ||
| 168 | |||
| 169 | Peer to Peer data transfer can happen with or without using DMA. Depending on | ||
| 170 | the sensor bandwidth requirement DMA can be enabled by using module parameter | ||
| 171 | ishtp_use_dma under intel_ishtp. | ||
| 172 | |||
| 173 | Each side (host and FW) manages its DMA transfer memory independently. When an | ||
| 174 | ISHTP client from either host or FW side wants to send something, it decides | ||
| 175 | whether to send over IPC or over DMA; for each transfer the decision is | ||
| 176 | independent. The sending side sends DMA_XFER message when the message is in | ||
| 177 | the respective host buffer (TX when host client sends, RX when FW client | ||
| 178 | sends). The recipient of DMA message responds with DMA_XFER_ACK, indicating | ||
| 179 | the sender that the memory region for that message may be reused. | ||
| 180 | |||
| 181 | DMA initialization is started with host sending DMA_ALLOC_NOTIFY bus message | ||
| 182 | (that includes RX buffer) and FW responds with DMA_ALLOC_NOTIFY_ACK. | ||
| 183 | Additionally to DMA address communication, this sequence checks capabilities: | ||
| 184 | if thw host doesn't support DMA, then it won't send DMA allocation, so FW can't | ||
| 185 | send DMA; if FW doesn't support DMA then it won't respond with | ||
| 186 | DMA_ALLOC_NOTIFY_ACK, in which case host will not use DMA transfers. | ||
| 187 | Here ISH acts as busmaster DMA controller. Hence when host sends DMA_XFER, | ||
| 188 | it's request to do host->ISH DMA transfer; when FW sends DMA_XFER, it means | ||
| 189 | that it already did DMA and the message resides at host. Thus, DMA_XFER | ||
| 190 | and DMA_XFER_ACK act as ownership indicators. | ||
| 191 | |||
| 192 | At initial state all outgoing memory belongs to the sender (TX to host, RX to | ||
| 193 | FW), DMA_XFER transfers ownership on the region that contains ISHTP message to | ||
| 194 | the receiving side, DMA_XFER_ACK returns ownership to the sender. A sender | ||
| 195 | needs not wait for previous DMA_XFER to be ack'ed, and may send another message | ||
| 196 | as long as remaining continuous memory in its ownership is enough. | ||
| 197 | In principle, multiple DMA_XFER and DMA_XFER_ACK messages may be sent at once | ||
| 198 | (up to IPC MTU), thus allowing for interrupt throttling. | ||
| 199 | Currently, ISH FW decides to send over DMA if ISHTP message is more than 3 IPC | ||
| 200 | fragments and via IPC otherwise. | ||
| 201 | |||
| 202 | 3.3.4 Ring Buffers | ||
| 203 | |||
| 204 | When a client initiate a connection, a ring or RX and TX buffers are allocated. | ||
| 205 | The size of ring can be specified by the client. HID client set 16 and 32 for | ||
| 206 | TX and RX buffers respectively. On send request from client, the data to be | ||
| 207 | sent is copied to one of the send ring buffer and scheduled to be sent using | ||
| 208 | bus message protocol. These buffers are required because the FW may have not | ||
| 209 | have processed the last message and may not have enough flow control credits | ||
| 210 | to send. Same thing holds true on receive side and flow control is required. | ||
| 211 | |||
| 212 | 3.3.5 Host Enumeration | ||
| 213 | |||
| 214 | The host enumeration bus command allow discovery of clients present in the FW. | ||
| 215 | There can be multiple sensor clients and clients for calibration function. | ||
| 216 | |||
| 217 | To ease in implantation and allow independent driver handle each client | ||
| 218 | this transport layer takes advantage of Linux Bus driver model. Each | ||
| 219 | client is registered as device on the the transport bus (ishtp bus). | ||
| 220 | |||
| 221 | Enumeration sequence of messages: | ||
| 222 | - Host sends HOST_START_REQ_CMD, indicating that host ISHTP layer is up. | ||
| 223 | - FW responds with HOST_START_RES_CMD | ||
| 224 | - Host sends HOST_ENUM_REQ_CMD (enumerate FW clients) | ||
| 225 | - FW responds with HOST_ENUM_RES_CMD that includes bitmap of available FW | ||
| 226 | client IDs | ||
| 227 | - For each FW ID found in that bitmap host sends | ||
| 228 | HOST_CLIENT_PROPERTIES_REQ_CMD | ||
| 229 | - FW responds with HOST_CLIENT_PROPERTIES_RES_CMD. Properties include UUID, | ||
| 230 | max ISHTP message size, etc. | ||
| 231 | - Once host received properties for that last discovered client, it considers | ||
| 232 | ISHTP device fully functional (and allocates DMA buffers) | ||
| 233 | |||
| 234 | 3.4 HID over ISH Client | ||
| 235 | Location: drivers/hid/intel-ish-hid | ||
| 236 | |||
| 237 | The ISHTP client driver is responsible for: | ||
| 238 | - enumerate HID devices under FW ISH client | ||
| 239 | - Get Report descriptor | ||
| 240 | - Register with HID core as a LL driver | ||
| 241 | - Process Get/Set feature request | ||
| 242 | - Get input reports | ||
| 243 | |||
| 244 | 3.5 HID Sensor Hub MFD and IIO sensor drivers | ||
| 245 | |||
| 246 | The functionality in these drivers is the same as an external sensor hub. | ||
| 247 | Refer to | ||
| 248 | Documentation/hid/hid-sensor.txt for HID sensor | ||
| 249 | Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space | ||
| 250 | |||
| 251 | 3.6 End to End HID transport Sequence Diagram | ||
| 252 | |||
| 253 | HID-ISH-CLN ISHTP IPC HW | ||
| 254 | | | | | | ||
| 255 | | | |-----WAKE UP------------------>| | ||
| 256 | | | | | | ||
| 257 | | | |-----HOST READY--------------->| | ||
| 258 | | | | | | ||
| 259 | | | |<----MNG_RESET_NOTIFY_ACK----- | | ||
| 260 | | | | | | ||
| 261 | | |<----ISHTP_START------ | | | ||
| 262 | | | | | | ||
| 263 | | |<-----------------HOST_START_RES_CMD-------------------| | ||
| 264 | | | | | | ||
| 265 | | |------------------QUERY_SUBSCRIBER-------------------->| | ||
| 266 | | | | | | ||
| 267 | | |------------------HOST_ENUM_REQ_CMD------------------->| | ||
| 268 | | | | | | ||
| 269 | | |<-----------------HOST_ENUM_RES_CMD--------------------| | ||
| 270 | | | | | | ||
| 271 | | |------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>| | ||
| 272 | | | | | | ||
| 273 | | |<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------| | ||
| 274 | | Create new device on in ishtp bus | | | ||
| 275 | | | | | | ||
| 276 | | |------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>| | ||
| 277 | | | | | | ||
| 278 | | |<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------| | ||
| 279 | | Create new device on in ishtp bus | | | ||
| 280 | | | | | | ||
| 281 | | |--Repeat HOST_CLIENT_PROPERTIES_REQ_CMD-till last one--| | ||
| 282 | | | | | | ||
| 283 | probed() | ||
| 284 | |----ishtp_cl_connect-->|----------------- CLIENT_CONNECT_REQ_CMD-------------->| | ||
| 285 | | | | | | ||
| 286 | | |<----------------CLIENT_CONNECT_RES_CMD----------------| | ||
| 287 | | | | | | ||
| 288 | |register event callback| | | | ||
| 289 | | | | | | ||
| 290 | |ishtp_cl_send( | ||
| 291 | HOSTIF_DM_ENUM_DEVICES) |----------fill ishtp_msg_hdr struct write to HW----- >| | ||
| 292 | | | | | | ||
| 293 | | | |<-----IRQ(IPC_PROTOCOL_ISHTP---| | ||
| 294 | | | | | | ||
| 295 | |<--ENUM_DEVICE RSP-----| | | | ||
| 296 | | | | | | ||
| 297 | for each enumerated device | ||
| 298 | |ishtp_cl_send( | ||
| 299 | HOSTIF_GET_HID_DESCRIPTOR |----------fill ishtp_msg_hdr struct write to HW--- >| | ||
| 300 | | | | | | ||
| 301 | ...Response | ||
| 302 | | | | | | ||
| 303 | for each enumerated device | ||
| 304 | |ishtp_cl_send( | ||
| 305 | HOSTIF_GET_REPORT_DESCRIPTOR |----------fill ishtp_msg_hdr struct write to HW- >| | ||
| 306 | | | | | | ||
| 307 | | | | | | ||
| 308 | hid_allocate_device | ||
| 309 | | | | | | ||
| 310 | hid_add_device | | | | ||
| 311 | | | | | | ||
| 312 | |||
| 313 | |||
| 314 | 3.7 ISH Debugging | ||
| 315 | |||
| 316 | To debug ISH, event tracing mechanism is used. To enable debug logs | ||
| 317 | echo 1 > /sys/kernel/debug/tracing/events/intel_ish/enable | ||
| 318 | cat sys/kernel/debug/tracing/trace | ||
| 319 | |||
| 320 | 3.8 ISH IIO sysfs Example on Lenovo thinkpad Yoga 260 | ||
| 321 | |||
| 322 | root@otcpl-ThinkPad-Yoga-260:~# tree -l /sys/bus/iio/devices/ | ||
| 323 | /sys/bus/iio/devices/ | ||
| 324 | āāā iio:device0 -> ../../../devices/0044:8086:22D8.0001/HID-SENSOR-200073.9.auto/iio:device0 | ||
| 325 | āĀ Ā āāā buffer | ||
| 326 | āĀ Ā āĀ Ā āāā enable | ||
| 327 | āĀ Ā āĀ Ā āāā length | ||
| 328 | āĀ Ā āĀ Ā āāā watermark | ||
| 329 | ... | ||
| 330 | āĀ Ā āāā in_accel_hysteresis | ||
| 331 | āĀ Ā āāā in_accel_offset | ||
| 332 | āĀ Ā āāā in_accel_sampling_frequency | ||
| 333 | āĀ Ā āāā in_accel_scale | ||
| 334 | āĀ Ā āāā in_accel_x_raw | ||
| 335 | āĀ Ā āāā in_accel_y_raw | ||
| 336 | āĀ Ā āāā in_accel_z_raw | ||
| 337 | āĀ Ā āāā name | ||
| 338 | āĀ Ā āāā scan_elements | ||
| 339 | āĀ Ā āĀ Ā āāā in_accel_x_en | ||
| 340 | āĀ Ā āĀ Ā āāā in_accel_x_index | ||
| 341 | āĀ Ā āĀ Ā āāā in_accel_x_type | ||
| 342 | āĀ Ā āĀ Ā āāā in_accel_y_en | ||
| 343 | āĀ Ā āĀ Ā āāā in_accel_y_index | ||
| 344 | āĀ Ā āĀ Ā āāā in_accel_y_type | ||
| 345 | āĀ Ā āĀ Ā āāā in_accel_z_en | ||
| 346 | āĀ Ā āĀ Ā āāā in_accel_z_index | ||
| 347 | āĀ Ā āĀ Ā āāā in_accel_z_type | ||
| 348 | ... | ||
| 349 | āĀ Ā āĀ Ā āāā devices | ||
| 350 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā buffer | ||
| 351 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā enable | ||
| 352 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā length | ||
| 353 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā watermark | ||
| 354 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā dev | ||
| 355 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_both_raw | ||
| 356 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_hysteresis | ||
| 357 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_offset | ||
| 358 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_sampling_frequency | ||
| 359 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_scale | ||
| 360 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā name | ||
| 361 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā scan_elements | ||
| 362 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_both_en | ||
| 363 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_both_index | ||
| 364 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_intensity_both_type | ||
| 365 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā trigger | ||
| 366 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā current_trigger | ||
| 367 | ... | ||
| 368 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā buffer | ||
| 369 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā enable | ||
| 370 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā length | ||
| 371 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā watermark | ||
| 372 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā dev | ||
| 373 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_hysteresis | ||
| 374 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_offset | ||
| 375 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_sampling_frequency | ||
| 376 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_scale | ||
| 377 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_x_raw | ||
| 378 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_y_raw | ||
| 379 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_z_raw | ||
| 380 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_from_north_magnetic_tilt_comp_raw | ||
| 381 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_hysteresis | ||
| 382 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_offset | ||
| 383 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_sampling_frequency | ||
| 384 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_scale | ||
| 385 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā name | ||
| 386 | ... | ||
| 387 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā scan_elements | ||
| 388 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_x_en | ||
| 389 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_x_index | ||
| 390 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_x_type | ||
| 391 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_y_en | ||
| 392 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_y_index | ||
| 393 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_y_type | ||
| 394 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_z_en | ||
| 395 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_z_index | ||
| 396 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_magn_z_type | ||
| 397 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_from_north_magnetic_tilt_comp_en | ||
| 398 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_from_north_magnetic_tilt_comp_index | ||
| 399 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_rot_from_north_magnetic_tilt_comp_type | ||
| 400 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā trigger | ||
| 401 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā current_trigger | ||
| 402 | ... | ||
| 403 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā buffer | ||
| 404 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā enable | ||
| 405 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā length | ||
| 406 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā watermark | ||
| 407 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā dev | ||
| 408 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_hysteresis | ||
| 409 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_offset | ||
| 410 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_sampling_frequency | ||
| 411 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_scale | ||
| 412 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_raw | ||
| 413 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_raw | ||
| 414 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_raw | ||
| 415 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā name | ||
| 416 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā scan_elements | ||
| 417 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_en | ||
| 418 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_index | ||
| 419 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_type | ||
| 420 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_en | ||
| 421 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_index | ||
| 422 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_type | ||
| 423 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_en | ||
| 424 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_index | ||
| 425 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_type | ||
| 426 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā trigger | ||
| 427 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā current_trigger | ||
| 428 | ... | ||
| 429 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā buffer | ||
| 430 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā enable | ||
| 431 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā length | ||
| 432 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā watermark | ||
| 433 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā dev | ||
| 434 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_hysteresis | ||
| 435 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_offset | ||
| 436 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_sampling_frequency | ||
| 437 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_scale | ||
| 438 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_raw | ||
| 439 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_raw | ||
| 440 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_raw | ||
| 441 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā name | ||
| 442 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā scan_elements | ||
| 443 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_en | ||
| 444 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_index | ||
| 445 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_x_type | ||
| 446 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_en | ||
| 447 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_index | ||
| 448 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_y_type | ||
| 449 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_en | ||
| 450 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_index | ||
| 451 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā in_anglvel_z_type | ||
| 452 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā trigger | ||
| 453 | āĀ Ā āĀ Ā āĀ Ā āĀ Ā āĀ Ā āāā current_trigger | ||
| 454 | ... | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 4d3426434a98..33d77794a1f2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -6205,6 +6205,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git | |||
| 6205 | S: Supported | 6205 | S: Supported |
| 6206 | F: drivers/idle/intel_idle.c | 6206 | F: drivers/idle/intel_idle.c |
| 6207 | 6207 | ||
| 6208 | INTEL INTEGRATED SENSOR HUB DRIVER | ||
| 6209 | M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | ||
| 6210 | M: Jiri Kosina <jikos@kernel.org> | ||
| 6211 | L: linux-input@vger.kernel.org | ||
| 6212 | S: Maintained | ||
| 6213 | F: drivers/hid/intel-ish-hid/ | ||
| 6214 | |||
| 6208 | INTEL PSTATE DRIVER | 6215 | INTEL PSTATE DRIVER |
| 6209 | M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | 6216 | M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> |
| 6210 | M: Len Brown <lenb@kernel.org> | 6217 | M: Len Brown <lenb@kernel.org> |
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 78ac4811bd3c..cd4599c0523b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
| @@ -457,8 +457,6 @@ config LOGITECH_FF | |||
| 457 | - Logitech WingMan Cordless RumblePad | 457 | - Logitech WingMan Cordless RumblePad |
| 458 | - Logitech WingMan Cordless RumblePad 2 | 458 | - Logitech WingMan Cordless RumblePad 2 |
| 459 | - Logitech WingMan Force 3D | 459 | - Logitech WingMan Force 3D |
| 460 | - Logitech Formula Force EX | ||
| 461 | - Logitech WingMan Formula Force GP | ||
| 462 | 460 | ||
| 463 | and if you want to enable force feedback for them. | 461 | and if you want to enable force feedback for them. |
| 464 | Note: if you say N here, this device will still be supported, but without | 462 | Note: if you say N here, this device will still be supported, but without |
| @@ -488,15 +486,22 @@ config LOGIWHEELS_FF | |||
| 488 | select INPUT_FF_MEMLESS | 486 | select INPUT_FF_MEMLESS |
| 489 | default LOGITECH_FF | 487 | default LOGITECH_FF |
| 490 | help | 488 | help |
| 491 | Say Y here if you want to enable force feedback and range setting | 489 | Say Y here if you want to enable force feedback and range setting(*) |
| 492 | support for following Logitech wheels: | 490 | support for following Logitech wheels: |
| 491 | - Logitech G25 (*) | ||
| 492 | - Logitech G27 (*) | ||
| 493 | - Logitech G29 (*) | ||
| 493 | - Logitech Driving Force | 494 | - Logitech Driving Force |
| 494 | - Logitech Driving Force Pro | 495 | - Logitech Driving Force Pro (*) |
| 495 | - Logitech Driving Force GT | 496 | - Logitech Driving Force GT (*) |
| 496 | - Logitech G25 | 497 | - Logitech Driving Force EX/RX |
| 497 | - Logitech G27 | 498 | - Logitech Driving Force Wireless |
| 498 | - Logitech MOMO/MOMO 2 | 499 | - Logitech Speed Force Wireless |
| 499 | - Logitech Formula Force EX | 500 | - Logitech MOMO Force |
| 501 | - Logitech MOMO Racing Force | ||
| 502 | - Logitech Formula Force GP | ||
| 503 | - Logitech Formula Force EX/RX | ||
| 504 | - Logitech Wingman Formula Force GP | ||
| 500 | 505 | ||
| 501 | config HID_MAGICMOUSE | 506 | config HID_MAGICMOUSE |
| 502 | tristate "Apple Magic Mouse/Trackpad multi-touch support" | 507 | tristate "Apple Magic Mouse/Trackpad multi-touch support" |
| @@ -862,6 +867,7 @@ config HID_WACOM | |||
| 862 | select POWER_SUPPLY | 867 | select POWER_SUPPLY |
| 863 | select NEW_LEDS | 868 | select NEW_LEDS |
| 864 | select LEDS_CLASS | 869 | select LEDS_CLASS |
| 870 | select LEDS_TRIGGERS | ||
| 865 | help | 871 | help |
| 866 | Say Y here if you want to use the USB or BT version of the Wacom Intuos | 872 | Say Y here if you want to use the USB or BT version of the Wacom Intuos |
| 867 | or Graphire tablet. | 873 | or Graphire tablet. |
| @@ -967,4 +973,6 @@ source "drivers/hid/usbhid/Kconfig" | |||
| 967 | 973 | ||
| 968 | source "drivers/hid/i2c-hid/Kconfig" | 974 | source "drivers/hid/i2c-hid/Kconfig" |
| 969 | 975 | ||
| 976 | source "drivers/hid/intel-ish-hid/Kconfig" | ||
| 977 | |||
| 970 | endmenu | 978 | endmenu |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fc4b2aa47f2e..86b2b5785fd2 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
| @@ -113,3 +113,5 @@ obj-$(CONFIG_USB_MOUSE) += usbhid/ | |||
| 113 | obj-$(CONFIG_USB_KBD) += usbhid/ | 113 | obj-$(CONFIG_USB_KBD) += usbhid/ |
| 114 | 114 | ||
| 115 | obj-$(CONFIG_I2C_HID) += i2c-hid/ | 115 | obj-$(CONFIG_I2C_HID) += i2c-hid/ |
| 116 | |||
| 117 | obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/ | ||
diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 048befde295a..ed9c0ea5b026 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c | |||
| @@ -139,8 +139,8 @@ static int u1_read_write_register(struct hid_device *hdev, u32 address, | |||
| 139 | if (read_flag) { | 139 | if (read_flag) { |
| 140 | readbuf = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL); | 140 | readbuf = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL); |
| 141 | if (!readbuf) { | 141 | if (!readbuf) { |
| 142 | kfree(input); | 142 | ret = -ENOMEM; |
| 143 | return -ENOMEM; | 143 | goto exit; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, readbuf, | 146 | ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, readbuf, |
| @@ -149,6 +149,7 @@ static int u1_read_write_register(struct hid_device *hdev, u32 address, | |||
| 149 | 149 | ||
| 150 | if (ret < 0) { | 150 | if (ret < 0) { |
| 151 | dev_err(&hdev->dev, "failed read register (%d)\n", ret); | 151 | dev_err(&hdev->dev, "failed read register (%d)\n", ret); |
| 152 | kfree(readbuf); | ||
| 152 | goto exit; | 153 | goto exit; |
| 153 | } | 154 | } |
| 154 | 155 | ||
| @@ -190,16 +191,16 @@ static int alps_raw_event(struct hid_device *hdev, | |||
| 190 | if (z != 0) { | 191 | if (z != 0) { |
| 191 | input_mt_report_slot_state(hdata->input, | 192 | input_mt_report_slot_state(hdata->input, |
| 192 | MT_TOOL_FINGER, 1); | 193 | MT_TOOL_FINGER, 1); |
| 194 | input_report_abs(hdata->input, | ||
| 195 | ABS_MT_POSITION_X, x); | ||
| 196 | input_report_abs(hdata->input, | ||
| 197 | ABS_MT_POSITION_Y, y); | ||
| 198 | input_report_abs(hdata->input, | ||
| 199 | ABS_MT_PRESSURE, z); | ||
| 193 | } else { | 200 | } else { |
| 194 | input_mt_report_slot_state(hdata->input, | 201 | input_mt_report_slot_state(hdata->input, |
| 195 | MT_TOOL_FINGER, 0); | 202 | MT_TOOL_FINGER, 0); |
| 196 | break; | ||
| 197 | } | 203 | } |
| 198 | |||
| 199 | input_report_abs(hdata->input, ABS_MT_POSITION_X, x); | ||
| 200 | input_report_abs(hdata->input, ABS_MT_POSITION_Y, y); | ||
| 201 | input_report_abs(hdata->input, ABS_MT_PRESSURE, z); | ||
| 202 | |||
| 203 | } | 204 | } |
| 204 | 205 | ||
| 205 | input_mt_sync_frame(hdata->input); | 206 | input_mt_sync_frame(hdata->input); |
| @@ -244,13 +245,13 @@ static int alps_raw_event(struct hid_device *hdev, | |||
| 244 | static int alps_post_reset(struct hid_device *hdev) | 245 | static int alps_post_reset(struct hid_device *hdev) |
| 245 | { | 246 | { |
| 246 | return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, | 247 | return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, |
| 247 | NULL, U1_TP_ABS_MODE, false); | 248 | NULL, U1_TP_ABS_MODE | U1_SP_ABS_MODE, false); |
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | static int alps_post_resume(struct hid_device *hdev) | 251 | static int alps_post_resume(struct hid_device *hdev) |
| 251 | { | 252 | { |
| 252 | return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, | 253 | return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, |
| 253 | NULL, U1_TP_ABS_MODE, false); | 254 | NULL, U1_TP_ABS_MODE | U1_SP_ABS_MODE, false); |
| 254 | } | 255 | } |
| 255 | #endif /* CONFIG_PM */ | 256 | #endif /* CONFIG_PM */ |
| 256 | 257 | ||
| @@ -383,7 +384,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) | |||
| 383 | 384 | ||
| 384 | input2 = input_allocate_device(); | 385 | input2 = input_allocate_device(); |
| 385 | if (!input2) { | 386 | if (!input2) { |
| 386 | input_free_device(input2); | 387 | ret = -ENOMEM; |
| 387 | goto exit; | 388 | goto exit; |
| 388 | } | 389 | } |
| 389 | 390 | ||
| @@ -425,7 +426,8 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) | |||
| 425 | __set_bit(INPUT_PROP_POINTER, input2->propbit); | 426 | __set_bit(INPUT_PROP_POINTER, input2->propbit); |
| 426 | __set_bit(INPUT_PROP_POINTING_STICK, input2->propbit); | 427 | __set_bit(INPUT_PROP_POINTING_STICK, input2->propbit); |
| 427 | 428 | ||
| 428 | if (input_register_device(data->input2)) { | 429 | ret = input_register_device(data->input2); |
| 430 | if (ret) { | ||
| 429 | input_free_device(input2); | 431 | input_free_device(input2); |
| 430 | goto exit; | 432 | goto exit; |
| 431 | } | 433 | } |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 08f53c7fd513..2b89c701076f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
| @@ -727,6 +727,7 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) | |||
| 727 | (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 || | 727 | (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 || |
| 728 | hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 || | 728 | hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 || |
| 729 | hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP || | 729 | hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP || |
| 730 | hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP || | ||
| 730 | hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || | 731 | hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || |
| 731 | hid->product == USB_DEVICE_ID_MS_POWER_COVER) && | 732 | hid->product == USB_DEVICE_ID_MS_POWER_COVER) && |
| 732 | hid->group == HID_GROUP_MULTITOUCH) | 733 | hid->group == HID_GROUP_MULTITOUCH) |
| @@ -1916,7 +1917,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 1916 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, | 1917 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, |
| 1917 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, | 1918 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, |
| 1918 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, | 1919 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, |
| 1919 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, | 1920 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) }, |
| 1920 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, | 1921 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, |
| 1921 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) }, | 1922 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) }, |
| 1922 | { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, | 1923 | { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, |
| @@ -1982,6 +1983,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 1982 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, | 1983 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, |
| 1983 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, | 1984 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, |
| 1984 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, | 1985 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, |
| 1986 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) }, | ||
| 1985 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, | 1987 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, |
| 1986 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) }, | 1988 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) }, |
| 1987 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) }, | 1989 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) }, |
| @@ -2037,6 +2039,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 2037 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, | 2039 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, |
| 2038 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) }, | 2040 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) }, |
| 2039 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, | 2041 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, |
| 2042 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9) }, | ||
| 2040 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, | 2043 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, |
| 2041 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5) }, | 2044 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5) }, |
| 2042 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, | 2045 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, |
| @@ -2083,6 +2086,11 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
| 2083 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, | 2086 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, |
| 2084 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, | 2087 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, |
| 2085 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, | 2088 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, |
| 2089 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, | ||
| 2090 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, | ||
| 2091 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, | ||
| 2092 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, | ||
| 2093 | { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, | ||
| 2086 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, | 2094 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, |
| 2087 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, | 2095 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, |
| 2088 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, | 2096 | { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, |
| @@ -2480,7 +2488,7 @@ static const struct hid_device_id hid_ignore_list[] = { | |||
| 2480 | { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, | 2488 | { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, |
| 2481 | { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, | 2489 | { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, |
| 2482 | { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, | 2490 | { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, |
| 2483 | #if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE) | 2491 | #if IS_ENABLED(CONFIG_MOUSE_SYNAPTICS_USB) |
| 2484 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) }, | 2492 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) }, |
| 2485 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) }, | 2493 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) }, |
| 2486 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) }, | 2494 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) }, |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4ed9a4fdfea7..cd59c79eebdd 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
| @@ -268,6 +268,7 @@ | |||
| 268 | #define USB_DEVICE_ID_CORSAIR_K95RGB 0x1b11 | 268 | #define USB_DEVICE_ID_CORSAIR_K95RGB 0x1b11 |
| 269 | #define USB_DEVICE_ID_CORSAIR_M65RGB 0x1b12 | 269 | #define USB_DEVICE_ID_CORSAIR_M65RGB 0x1b12 |
| 270 | #define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13 | 270 | #define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13 |
| 271 | #define USB_DEVICE_ID_CORSAIR_STRAFE 0x1b15 | ||
| 271 | #define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17 | 272 | #define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17 |
| 272 | 273 | ||
| 273 | #define USB_VENDOR_ID_CREATIVELABS 0x041e | 274 | #define USB_VENDOR_ID_CREATIVELABS 0x041e |
| @@ -565,7 +566,7 @@ | |||
| 565 | #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 | 566 | #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 |
| 566 | #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 | 567 | #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 |
| 567 | #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 | 568 | #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 |
| 568 | #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a | 569 | #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a |
| 569 | #define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 | 570 | #define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 |
| 570 | #define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015 | 571 | #define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015 |
| 571 | 572 | ||
| @@ -713,6 +714,7 @@ | |||
| 713 | #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc | 714 | #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc |
| 714 | #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 | 715 | #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 |
| 715 | #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd | 716 | #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd |
| 717 | #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9 | ||
| 716 | #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de | 718 | #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de |
| 717 | #define USB_DEVICE_ID_MS_POWER_COVER 0x07da | 719 | #define USB_DEVICE_ID_MS_POWER_COVER 0x07da |
| 718 | 720 | ||
| @@ -859,6 +861,7 @@ | |||
| 859 | #define USB_DEVICE_ID_SAITEK_PS1000 0x0621 | 861 | #define USB_DEVICE_ID_SAITEK_PS1000 0x0621 |
| 860 | #define USB_DEVICE_ID_SAITEK_RAT7_OLD 0x0ccb | 862 | #define USB_DEVICE_ID_SAITEK_RAT7_OLD 0x0ccb |
| 861 | #define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7 | 863 | #define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7 |
| 864 | #define USB_DEVICE_ID_SAITEK_RAT9 0x0cfa | ||
| 862 | #define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0 | 865 | #define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0 |
| 863 | 866 | ||
| 864 | #define USB_VENDOR_ID_SAMSUNG 0x0419 | 867 | #define USB_VENDOR_ID_SAMSUNG 0x0419 |
| @@ -996,6 +999,10 @@ | |||
| 996 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 | 999 | #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 |
| 997 | #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 | 1000 | #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 |
| 998 | #define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 | 1001 | #define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 |
| 1002 | #define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031 | ||
| 1003 | #define USB_DEVICE_ID_UGEE_TABLET_81 0x0081 | ||
| 1004 | #define USB_DEVICE_ID_UGEE_TABLET_45 0x0045 | ||
| 1005 | #define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d | ||
| 999 | 1006 | ||
| 1000 | #define USB_VENDOR_ID_UNITEC 0x227d | 1007 | #define USB_VENDOR_ID_UNITEC 0x227d |
| 1001 | #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 | 1008 | #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 |
| @@ -1085,4 +1092,7 @@ | |||
| 1085 | #define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002 | 1092 | #define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002 |
| 1086 | #define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003 | 1093 | #define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003 |
| 1087 | 1094 | ||
| 1095 | #define USB_VENDOR_ID_UGTIZER 0x2179 | ||
| 1096 | #define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053 | ||
| 1097 | |||
| 1088 | #endif | 1098 | #endif |
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index bcfaf32d9e5e..fb9ace1cef8b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c | |||
| @@ -604,6 +604,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel | |||
| 604 | break; | 604 | break; |
| 605 | } | 605 | } |
| 606 | 606 | ||
| 607 | /* | ||
| 608 | * Some lazy vendors declare 255 usages for System Control, | ||
| 609 | * leading to the creation of ABS_X|Y axis and too many others. | ||
| 610 | * It wouldn't be a problem if joydev doesn't consider the | ||
| 611 | * device as a joystick then. | ||
| 612 | */ | ||
| 613 | if (field->application == HID_GD_SYSTEM_CONTROL) | ||
| 614 | goto ignore; | ||
| 615 | |||
| 607 | if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ | 616 | if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ |
| 608 | switch (usage->hid) { | 617 | switch (usage->hid) { |
| 609 | case HID_GD_UP: usage->hat_dir = 1; break; | 618 | case HID_GD_UP: usage->hat_dir = 1; break; |
| @@ -953,6 +962,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel | |||
| 953 | case HID_UP_HPVENDOR2: | 962 | case HID_UP_HPVENDOR2: |
| 954 | set_bit(EV_REP, input->evbit); | 963 | set_bit(EV_REP, input->evbit); |
| 955 | switch (usage->hid & HID_USAGE) { | 964 | switch (usage->hid & HID_USAGE) { |
| 965 | case 0x001: map_key_clear(KEY_MICMUTE); break; | ||
| 956 | case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break; | 966 | case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break; |
| 957 | case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break; | 967 | case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break; |
| 958 | default: goto ignore; | 968 | default: goto ignore; |
diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 32e6d8d9ded0..0dd1167b2c9b 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c | |||
| @@ -19,11 +19,6 @@ | |||
| 19 | 19 | ||
| 20 | #include "hid-ids.h" | 20 | #include "hid-ids.h" |
| 21 | 21 | ||
| 22 | /* | ||
| 23 | * See EasyPen i405X description, device and HID report descriptors at | ||
| 24 | * http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_i405X | ||
| 25 | */ | ||
| 26 | |||
| 27 | /* Original EasyPen i405X report descriptor size */ | 22 | /* Original EasyPen i405X report descriptor size */ |
| 28 | #define EASYPEN_I405X_RDESC_ORIG_SIZE 476 | 23 | #define EASYPEN_I405X_RDESC_ORIG_SIZE 476 |
| 29 | 24 | ||
| @@ -82,11 +77,6 @@ static __u8 easypen_i405x_rdesc_fixed[] = { | |||
| 82 | 0xC0 /* End Collection */ | 77 | 0xC0 /* End Collection */ |
| 83 | }; | 78 | }; |
| 84 | 79 | ||
| 85 | /* | ||
| 86 | * See MousePen i608X description, device and HID report descriptors at | ||
| 87 | * http://sf.net/apps/mediawiki/digimend/?title=KYE_MousePen_i608X | ||
| 88 | */ | ||
| 89 | |||
| 90 | /* Original MousePen i608X report descriptor size */ | 80 | /* Original MousePen i608X report descriptor size */ |
| 91 | #define MOUSEPEN_I608X_RDESC_ORIG_SIZE 476 | 81 | #define MOUSEPEN_I608X_RDESC_ORIG_SIZE 476 |
| 92 | 82 | ||
| @@ -186,10 +176,104 @@ static __u8 mousepen_i608x_rdesc_fixed[] = { | |||
| 186 | 0xC0 /* End Collection */ | 176 | 0xC0 /* End Collection */ |
| 187 | }; | 177 | }; |
| 188 | 178 | ||
| 189 | /* | 179 | /* Original MousePen i608X v2 report descriptor size */ |
| 190 | * See EasyPen M610X description, device and HID report descriptors at | 180 | #define MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE 482 |
| 191 | * http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_M610X | 181 | |
| 192 | */ | 182 | /* Fixed MousePen i608X v2 report descriptor */ |
| 183 | static __u8 mousepen_i608x_v2_rdesc_fixed[] = { | ||
| 184 | 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ | ||
| 185 | 0x09, 0x01, /* Usage (01h), */ | ||
| 186 | 0xA1, 0x01, /* Collection (Application), */ | ||
| 187 | 0x85, 0x05, /* Report ID (5), */ | ||
| 188 | 0x09, 0x01, /* Usage (01h), */ | ||
| 189 | 0x15, 0x80, /* Logical Minimum (-128), */ | ||
| 190 | 0x25, 0x7F, /* Logical Maximum (127), */ | ||
| 191 | 0x75, 0x08, /* Report Size (8), */ | ||
| 192 | 0x95, 0x07, /* Report Count (7), */ | ||
| 193 | 0xB1, 0x02, /* Feature (Variable), */ | ||
| 194 | 0xC0, /* End Collection, */ | ||
| 195 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | ||
| 196 | 0x09, 0x02, /* Usage (Pen), */ | ||
| 197 | 0xA1, 0x01, /* Collection (Application), */ | ||
| 198 | 0x85, 0x10, /* Report ID (16), */ | ||
| 199 | 0x09, 0x20, /* Usage (Stylus), */ | ||
| 200 | 0xA0, /* Collection (Physical), */ | ||
| 201 | 0x14, /* Logical Minimum (0), */ | ||
| 202 | 0x25, 0x01, /* Logical Maximum (1), */ | ||
| 203 | 0x75, 0x01, /* Report Size (1), */ | ||
| 204 | 0x09, 0x42, /* Usage (Tip Switch), */ | ||
| 205 | 0x09, 0x44, /* Usage (Barrel Switch), */ | ||
| 206 | 0x09, 0x46, /* Usage (Tablet Pick), */ | ||
| 207 | 0x95, 0x03, /* Report Count (3), */ | ||
| 208 | 0x81, 0x02, /* Input (Variable), */ | ||
| 209 | 0x95, 0x04, /* Report Count (4), */ | ||
| 210 | 0x81, 0x03, /* Input (Constant, Variable), */ | ||
| 211 | 0x09, 0x32, /* Usage (In Range), */ | ||
| 212 | 0x95, 0x01, /* Report Count (1), */ | ||
| 213 | 0x81, 0x02, /* Input (Variable), */ | ||
| 214 | 0x75, 0x10, /* Report Size (16), */ | ||
| 215 | 0x95, 0x01, /* Report Count (1), */ | ||
| 216 | 0xA4, /* Push, */ | ||
| 217 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
| 218 | 0x55, 0xFD, /* Unit Exponent (-3), */ | ||
| 219 | 0x65, 0x13, /* Unit (Inch), */ | ||
| 220 | 0x34, /* Physical Minimum (0), */ | ||
| 221 | 0x09, 0x30, /* Usage (X), */ | ||
| 222 | 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ | ||
| 223 | 0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */ | ||
| 224 | 0x81, 0x02, /* Input (Variable), */ | ||
| 225 | 0x09, 0x31, /* Usage (Y), */ | ||
| 226 | 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ | ||
| 227 | 0x26, 0x00, 0x78, /* Logical Maximum (30720), */ | ||
| 228 | 0x81, 0x02, /* Input (Variable), */ | ||
| 229 | 0xB4, /* Pop, */ | ||
| 230 | 0x09, 0x30, /* Usage (Tip Pressure), */ | ||
| 231 | 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ | ||
| 232 | 0x81, 0x02, /* Input (Variable), */ | ||
| 233 | 0xC0, /* End Collection, */ | ||
| 234 | 0xC0, /* End Collection, */ | ||
| 235 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
| 236 | 0x09, 0x02, /* Usage (Mouse), */ | ||
| 237 | 0xA1, 0x01, /* Collection (Application), */ | ||
| 238 | 0x85, 0x11, /* Report ID (17), */ | ||
| 239 | 0x09, 0x01, /* Usage (Pointer), */ | ||
| 240 | 0xA0, /* Collection (Physical), */ | ||
| 241 | 0x14, /* Logical Minimum (0), */ | ||
| 242 | 0xA4, /* Push, */ | ||
| 243 | 0x05, 0x09, /* Usage Page (Button), */ | ||
| 244 | 0x75, 0x01, /* Report Size (1), */ | ||
| 245 | 0x19, 0x01, /* Usage Minimum (01h), */ | ||
| 246 | 0x29, 0x03, /* Usage Maximum (03h), */ | ||
| 247 | 0x25, 0x01, /* Logical Maximum (1), */ | ||
| 248 | 0x95, 0x03, /* Report Count (3), */ | ||
| 249 | 0x81, 0x02, /* Input (Variable), */ | ||
| 250 | 0x95, 0x05, /* Report Count (5), */ | ||
| 251 | 0x81, 0x01, /* Input (Constant), */ | ||
| 252 | 0xB4, /* Pop, */ | ||
| 253 | 0x95, 0x01, /* Report Count (1), */ | ||
| 254 | 0xA4, /* Push, */ | ||
| 255 | 0x55, 0xFD, /* Unit Exponent (-3), */ | ||
| 256 | 0x65, 0x13, /* Unit (Inch), */ | ||
| 257 | 0x34, /* Physical Minimum (0), */ | ||
| 258 | 0x75, 0x10, /* Report Size (16), */ | ||
| 259 | 0x09, 0x30, /* Usage (X), */ | ||
| 260 | 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ | ||
| 261 | 0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */ | ||
| 262 | 0x81, 0x02, /* Input (Variable), */ | ||
| 263 | 0x09, 0x31, /* Usage (Y), */ | ||
| 264 | 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ | ||
| 265 | 0x26, 0x00, 0x78, /* Logical Maximum (30720), */ | ||
| 266 | 0x81, 0x02, /* Input (Variable), */ | ||
| 267 | 0xB4, /* Pop, */ | ||
| 268 | 0x75, 0x08, /* Report Size (8), */ | ||
| 269 | 0x09, 0x38, /* Usage (Wheel), */ | ||
| 270 | 0x15, 0xFF, /* Logical Minimum (-1), */ | ||
| 271 | 0x25, 0x01, /* Logical Maximum (1), */ | ||
| 272 | 0x81, 0x06, /* Input (Variable, Relative), */ | ||
| 273 | 0x81, 0x01, /* Input (Constant), */ | ||
| 274 | 0xC0, /* End Collection, */ | ||
| 275 | 0xC0 /* End Collection */ | ||
| 276 | }; | ||
| 193 | 277 | ||
| 194 | /* Original EasyPen M610X report descriptor size */ | 278 | /* Original EasyPen M610X report descriptor size */ |
| 195 | #define EASYPEN_M610X_RDESC_ORIG_SIZE 476 | 279 | #define EASYPEN_M610X_RDESC_ORIG_SIZE 476 |
| @@ -454,12 +538,17 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 454 | } | 538 | } |
| 455 | break; | 539 | break; |
| 456 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: | 540 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: |
| 457 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: | ||
| 458 | if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) { | 541 | if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) { |
| 459 | rdesc = mousepen_i608x_rdesc_fixed; | 542 | rdesc = mousepen_i608x_rdesc_fixed; |
| 460 | *rsize = sizeof(mousepen_i608x_rdesc_fixed); | 543 | *rsize = sizeof(mousepen_i608x_rdesc_fixed); |
| 461 | } | 544 | } |
| 462 | break; | 545 | break; |
| 546 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2: | ||
| 547 | if (*rsize == MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE) { | ||
| 548 | rdesc = mousepen_i608x_v2_rdesc_fixed; | ||
| 549 | *rsize = sizeof(mousepen_i608x_v2_rdesc_fixed); | ||
| 550 | } | ||
| 551 | break; | ||
| 463 | case USB_DEVICE_ID_KYE_EASYPEN_M610X: | 552 | case USB_DEVICE_ID_KYE_EASYPEN_M610X: |
| 464 | if (*rsize == EASYPEN_M610X_RDESC_ORIG_SIZE) { | 553 | if (*rsize == EASYPEN_M610X_RDESC_ORIG_SIZE) { |
| 465 | rdesc = easypen_m610x_rdesc_fixed; | 554 | rdesc = easypen_m610x_rdesc_fixed; |
| @@ -553,7 +642,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 553 | switch (id->product) { | 642 | switch (id->product) { |
| 554 | case USB_DEVICE_ID_KYE_EASYPEN_I405X: | 643 | case USB_DEVICE_ID_KYE_EASYPEN_I405X: |
| 555 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: | 644 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: |
| 556 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: | 645 | case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2: |
| 557 | case USB_DEVICE_ID_KYE_EASYPEN_M610X: | 646 | case USB_DEVICE_ID_KYE_EASYPEN_M610X: |
| 558 | case USB_DEVICE_ID_KYE_PENSKETCH_M912: | 647 | case USB_DEVICE_ID_KYE_PENSKETCH_M912: |
| 559 | ret = kye_tablet_enable(hdev); | 648 | ret = kye_tablet_enable(hdev); |
| @@ -586,7 +675,7 @@ static const struct hid_device_id kye_devices[] = { | |||
| 586 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, | 675 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, |
| 587 | USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, | 676 | USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, |
| 588 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, | 677 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, |
| 589 | USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, | 678 | USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) }, |
| 590 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, | 679 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, |
| 591 | USB_DEVICE_ID_KYE_EASYPEN_M610X) }, | 680 | USB_DEVICE_ID_KYE_EASYPEN_M610X) }, |
| 592 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, | 681 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, |
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index feb2be71f77c..76f644deb0a7 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c | |||
| @@ -49,6 +49,7 @@ | |||
| 49 | #define FV_RDESC_ORIG_SIZE 130 | 49 | #define FV_RDESC_ORIG_SIZE 130 |
| 50 | #define MOMO_RDESC_ORIG_SIZE 87 | 50 | #define MOMO_RDESC_ORIG_SIZE 87 |
| 51 | #define MOMO2_RDESC_ORIG_SIZE 87 | 51 | #define MOMO2_RDESC_ORIG_SIZE 87 |
| 52 | #define FFG_RDESC_ORIG_SIZE 85 | ||
| 52 | 53 | ||
| 53 | /* Fixed report descriptors for Logitech Driving Force (and Pro) | 54 | /* Fixed report descriptors for Logitech Driving Force (and Pro) |
| 54 | * wheel controllers | 55 | * wheel controllers |
| @@ -334,6 +335,52 @@ static __u8 momo2_rdesc_fixed[] = { | |||
| 334 | 0xC0 /* End Collection */ | 335 | 0xC0 /* End Collection */ |
| 335 | }; | 336 | }; |
| 336 | 337 | ||
| 338 | static __u8 ffg_rdesc_fixed[] = { | ||
| 339 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
| 340 | 0x09, 0x04, /* Usage (Joystik), */ | ||
| 341 | 0xA1, 0x01, /* Collection (Application), */ | ||
| 342 | 0xA1, 0x02, /* Collection (Logical), */ | ||
| 343 | 0x95, 0x01, /* Report Count (1), */ | ||
| 344 | 0x75, 0x0A, /* Report Size (10), */ | ||
| 345 | 0x15, 0x00, /* Logical Minimum (0), */ | ||
| 346 | 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ | ||
| 347 | 0x35, 0x00, /* Physical Minimum (0), */ | ||
| 348 | 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */ | ||
| 349 | 0x09, 0x30, /* Usage (X), */ | ||
| 350 | 0x81, 0x02, /* Input (Variable), */ | ||
| 351 | 0x95, 0x06, /* Report Count (6), */ | ||
| 352 | 0x75, 0x01, /* Report Size (1), */ | ||
| 353 | 0x25, 0x01, /* Logical Maximum (1), */ | ||
| 354 | 0x45, 0x01, /* Physical Maximum (1), */ | ||
| 355 | 0x05, 0x09, /* Usage Page (Button), */ | ||
| 356 | 0x19, 0x01, /* Usage Minimum (01h), */ | ||
| 357 | 0x29, 0x06, /* Usage Maximum (06h), */ | ||
| 358 | 0x81, 0x02, /* Input (Variable), */ | ||
| 359 | 0x95, 0x01, /* Report Count (1), */ | ||
| 360 | 0x75, 0x08, /* Report Size (8), */ | ||
| 361 | 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ | ||
| 362 | 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ | ||
| 363 | 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ | ||
| 364 | 0x09, 0x01, /* Usage (01h), */ | ||
| 365 | 0x81, 0x02, /* Input (Variable), */ | ||
| 366 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
| 367 | 0x81, 0x01, /* Input (Constant), */ | ||
| 368 | 0x09, 0x31, /* Usage (Y), */ | ||
| 369 | 0x81, 0x02, /* Input (Variable), */ | ||
| 370 | 0x09, 0x32, /* Usage (Z), */ | ||
| 371 | 0x81, 0x02, /* Input (Variable), */ | ||
| 372 | 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ | ||
| 373 | 0x09, 0x01, /* Usage (01h), */ | ||
| 374 | 0x81, 0x02, /* Input (Variable), */ | ||
| 375 | 0xC0, /* End Collection, */ | ||
| 376 | 0xA1, 0x02, /* Collection (Logical), */ | ||
| 377 | 0x09, 0x02, /* Usage (02h), */ | ||
| 378 | 0x95, 0x07, /* Report Count (7), */ | ||
| 379 | 0x91, 0x02, /* Output (Variable), */ | ||
| 380 | 0xC0, /* End Collection, */ | ||
| 381 | 0xC0 /* End Collection */ | ||
| 382 | }; | ||
| 383 | |||
| 337 | /* | 384 | /* |
| 338 | * Certain Logitech keyboards send in report #3 keys which are far | 385 | * Certain Logitech keyboards send in report #3 keys which are far |
| 339 | * above the logical maximum described in descriptor. This extends | 386 | * above the logical maximum described in descriptor. This extends |
| @@ -343,8 +390,6 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 343 | unsigned int *rsize) | 390 | unsigned int *rsize) |
| 344 | { | 391 | { |
| 345 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); | 392 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); |
| 346 | struct usb_device_descriptor *udesc; | ||
| 347 | __u16 bcdDevice, rev_maj, rev_min; | ||
| 348 | 393 | ||
| 349 | if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 && | 394 | if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 && |
| 350 | rdesc[84] == 0x8c && rdesc[85] == 0x02) { | 395 | rdesc[84] == 0x8c && rdesc[85] == 0x02) { |
| @@ -363,20 +408,18 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 363 | 408 | ||
| 364 | switch (hdev->product) { | 409 | switch (hdev->product) { |
| 365 | 410 | ||
| 366 | /* Several wheels report as this id when operating in emulation mode. */ | 411 | case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: |
| 367 | case USB_DEVICE_ID_LOGITECH_WHEEL: | 412 | if (*rsize == FFG_RDESC_ORIG_SIZE) { |
| 368 | udesc = &(hid_to_usb_dev(hdev)->descriptor); | 413 | hid_info(hdev, |
| 369 | if (!udesc) { | 414 | "fixing up Logitech Wingman Formula Force GP report descriptor\n"); |
| 370 | hid_err(hdev, "NULL USB device descriptor\n"); | 415 | rdesc = ffg_rdesc_fixed; |
| 371 | break; | 416 | *rsize = sizeof(ffg_rdesc_fixed); |
| 372 | } | 417 | } |
| 373 | bcdDevice = le16_to_cpu(udesc->bcdDevice); | 418 | break; |
| 374 | rev_maj = bcdDevice >> 8; | ||
| 375 | rev_min = bcdDevice & 0xff; | ||
| 376 | 419 | ||
| 377 | /* Update the report descriptor for only the Driving Force wheel */ | 420 | /* Several wheels report as this id when operating in emulation mode. */ |
| 378 | if (rev_maj == 1 && rev_min == 2 && | 421 | case USB_DEVICE_ID_LOGITECH_WHEEL: |
| 379 | *rsize == DF_RDESC_ORIG_SIZE) { | 422 | if (*rsize == DF_RDESC_ORIG_SIZE) { |
| 380 | hid_info(hdev, | 423 | hid_info(hdev, |
| 381 | "fixing up Logitech Driving Force report descriptor\n"); | 424 | "fixing up Logitech Driving Force report descriptor\n"); |
| 382 | rdesc = df_rdesc_fixed; | 425 | rdesc = df_rdesc_fixed; |
| @@ -621,6 +664,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, | |||
| 621 | usage->code == ABS_RZ)) { | 664 | usage->code == ABS_RZ)) { |
| 622 | switch (hdev->product) { | 665 | switch (hdev->product) { |
| 623 | case USB_DEVICE_ID_LOGITECH_G29_WHEEL: | 666 | case USB_DEVICE_ID_LOGITECH_G29_WHEEL: |
| 667 | case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: | ||
| 624 | case USB_DEVICE_ID_LOGITECH_WHEEL: | 668 | case USB_DEVICE_ID_LOGITECH_WHEEL: |
| 625 | case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: | 669 | case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: |
| 626 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | 670 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: |
| @@ -657,6 +701,17 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field, | |||
| 657 | return 0; | 701 | return 0; |
| 658 | } | 702 | } |
| 659 | 703 | ||
| 704 | static int lg_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
| 705 | u8 *rd, int size) | ||
| 706 | { | ||
| 707 | struct lg_drv_data *drv_data = hid_get_drvdata(hdev); | ||
| 708 | |||
| 709 | if (drv_data->quirks & LG_FF4) | ||
| 710 | return lg4ff_raw_event(hdev, report, rd, size, drv_data); | ||
| 711 | |||
| 712 | return 0; | ||
| 713 | } | ||
| 714 | |||
| 660 | static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) | 715 | static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) |
| 661 | { | 716 | { |
| 662 | struct usb_interface *iface = to_usb_interface(hdev->dev.parent); | 717 | struct usb_interface *iface = to_usb_interface(hdev->dev.parent); |
| @@ -809,7 +864,7 @@ static const struct hid_device_id lg_devices[] = { | |||
| 809 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), | 864 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), |
| 810 | .driver_data = LG_FF4 }, | 865 | .driver_data = LG_FF4 }, |
| 811 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG), | 866 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG), |
| 812 | .driver_data = LG_FF }, | 867 | .driver_data = LG_NOGET | LG_FF4 }, |
| 813 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), | 868 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), |
| 814 | .driver_data = LG_FF2 }, | 869 | .driver_data = LG_FF2 }, |
| 815 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940), | 870 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940), |
| @@ -830,6 +885,7 @@ static struct hid_driver lg_driver = { | |||
| 830 | .input_mapping = lg_input_mapping, | 885 | .input_mapping = lg_input_mapping, |
| 831 | .input_mapped = lg_input_mapped, | 886 | .input_mapped = lg_input_mapped, |
| 832 | .event = lg_event, | 887 | .event = lg_event, |
| 888 | .raw_event = lg_raw_event, | ||
| 833 | .probe = lg_probe, | 889 | .probe = lg_probe, |
| 834 | .remove = lg_remove, | 890 | .remove = lg_remove, |
| 835 | }; | 891 | }; |
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index af3a8ec8a746..1fc12e357035 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c | |||
| @@ -75,6 +75,7 @@ static void lg4ff_set_range_g25(struct hid_device *hid, u16 range); | |||
| 75 | 75 | ||
| 76 | struct lg4ff_wheel_data { | 76 | struct lg4ff_wheel_data { |
| 77 | const u32 product_id; | 77 | const u32 product_id; |
| 78 | u16 combine; | ||
| 78 | u16 range; | 79 | u16 range; |
| 79 | const u16 min_range; | 80 | const u16 min_range; |
| 80 | const u16 max_range; | 81 | const u16 max_range; |
| @@ -136,6 +137,7 @@ struct lg4ff_alternate_mode { | |||
| 136 | }; | 137 | }; |
| 137 | 138 | ||
| 138 | static const struct lg4ff_wheel lg4ff_devices[] = { | 139 | static const struct lg4ff_wheel lg4ff_devices[] = { |
| 140 | {USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL}, | ||
| 139 | {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, | 141 | {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, |
| 140 | {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, | 142 | {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, |
| 141 | {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp}, | 143 | {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp}, |
| @@ -328,6 +330,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, | |||
| 328 | } | 330 | } |
| 329 | } | 331 | } |
| 330 | 332 | ||
| 333 | int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
| 334 | u8 *rd, int size, struct lg_drv_data *drv_data) | ||
| 335 | { | ||
| 336 | int offset; | ||
| 337 | struct lg4ff_device_entry *entry = drv_data->device_props; | ||
| 338 | |||
| 339 | if (!entry) | ||
| 340 | return 0; | ||
| 341 | |||
| 342 | /* adjust HID report present combined pedals data */ | ||
| 343 | if (entry->wdata.combine) { | ||
| 344 | switch (entry->wdata.product_id) { | ||
| 345 | case USB_DEVICE_ID_LOGITECH_WHEEL: | ||
| 346 | rd[5] = rd[3]; | ||
| 347 | rd[6] = 0x7F; | ||
| 348 | return 1; | ||
| 349 | case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: | ||
| 350 | case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: | ||
| 351 | case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: | ||
| 352 | rd[4] = rd[3]; | ||
| 353 | rd[5] = 0x7F; | ||
| 354 | return 1; | ||
| 355 | case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: | ||
| 356 | rd[5] = rd[4]; | ||
| 357 | rd[6] = 0x7F; | ||
| 358 | return 1; | ||
| 359 | case USB_DEVICE_ID_LOGITECH_G25_WHEEL: | ||
| 360 | case USB_DEVICE_ID_LOGITECH_G27_WHEEL: | ||
| 361 | offset = 5; | ||
| 362 | break; | ||
| 363 | case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: | ||
| 364 | case USB_DEVICE_ID_LOGITECH_G29_WHEEL: | ||
| 365 | offset = 6; | ||
| 366 | break; | ||
| 367 | case USB_DEVICE_ID_LOGITECH_WII_WHEEL: | ||
| 368 | offset = 3; | ||
| 369 | break; | ||
| 370 | default: | ||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | /* Compute a combined axis when wheel does not supply it */ | ||
| 375 | rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1; | ||
| 376 | rd[offset+1] = 0x7F; | ||
| 377 | return 1; | ||
| 378 | } | ||
| 379 | |||
| 380 | return 0; | ||
| 381 | } | ||
| 382 | |||
| 331 | static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel, | 383 | static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel, |
| 332 | const struct lg4ff_multimode_wheel *mmode_wheel, | 384 | const struct lg4ff_multimode_wheel *mmode_wheel, |
| 333 | const u16 real_product_id) | 385 | const u16 real_product_id) |
| @@ -345,6 +397,7 @@ static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const s | |||
| 345 | { | 397 | { |
| 346 | struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id, | 398 | struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id, |
| 347 | .real_product_id = real_product_id, | 399 | .real_product_id = real_product_id, |
| 400 | .combine = 0, | ||
| 348 | .min_range = wheel->min_range, | 401 | .min_range = wheel->min_range, |
| 349 | .max_range = wheel->max_range, | 402 | .max_range = wheel->max_range, |
| 350 | .set_range = wheel->set_range, | 403 | .set_range = wheel->set_range, |
| @@ -885,6 +938,58 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att | |||
| 885 | } | 938 | } |
| 886 | static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store); | 939 | static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store); |
| 887 | 940 | ||
| 941 | static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *attr, | ||
| 942 | char *buf) | ||
| 943 | { | ||
| 944 | struct hid_device *hid = to_hid_device(dev); | ||
| 945 | struct lg4ff_device_entry *entry; | ||
| 946 | struct lg_drv_data *drv_data; | ||
| 947 | size_t count; | ||
| 948 | |||
| 949 | drv_data = hid_get_drvdata(hid); | ||
| 950 | if (!drv_data) { | ||
| 951 | hid_err(hid, "Private driver data not found!\n"); | ||
| 952 | return 0; | ||
| 953 | } | ||
| 954 | |||
| 955 | entry = drv_data->device_props; | ||
| 956 | if (!entry) { | ||
| 957 | hid_err(hid, "Device properties not found!\n"); | ||
| 958 | return 0; | ||
| 959 | } | ||
| 960 | |||
| 961 | count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine); | ||
| 962 | return count; | ||
| 963 | } | ||
| 964 | |||
| 965 | static ssize_t lg4ff_combine_store(struct device *dev, struct device_attribute *attr, | ||
| 966 | const char *buf, size_t count) | ||
| 967 | { | ||
| 968 | struct hid_device *hid = to_hid_device(dev); | ||
| 969 | struct lg4ff_device_entry *entry; | ||
| 970 | struct lg_drv_data *drv_data; | ||
| 971 | u16 combine = simple_strtoul(buf, NULL, 10); | ||
| 972 | |||
| 973 | drv_data = hid_get_drvdata(hid); | ||
| 974 | if (!drv_data) { | ||
| 975 | hid_err(hid, "Private driver data not found!\n"); | ||
| 976 | return -EINVAL; | ||
| 977 | } | ||
| 978 | |||
| 979 | entry = drv_data->device_props; | ||
| 980 | if (!entry) { | ||
| 981 | hid_err(hid, "Device properties not found!\n"); | ||
| 982 | return -EINVAL; | ||
| 983 | } | ||
| 984 | |||
| 985 | if (combine > 1) | ||
| 986 | combine = 1; | ||
| 987 | |||
| 988 | entry->wdata.combine = combine; | ||
| 989 | return count; | ||
| 990 | } | ||
| 991 | static DEVICE_ATTR(combine_pedals, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_combine_show, lg4ff_combine_store); | ||
| 992 | |||
| 888 | /* Export the currently set range of the wheel */ | 993 | /* Export the currently set range of the wheel */ |
| 889 | static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, | 994 | static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, |
| 890 | char *buf) | 995 | char *buf) |
| @@ -1259,6 +1364,9 @@ int lg4ff_init(struct hid_device *hid) | |||
| 1259 | } | 1364 | } |
| 1260 | 1365 | ||
| 1261 | /* Create sysfs interface */ | 1366 | /* Create sysfs interface */ |
| 1367 | error = device_create_file(&hid->dev, &dev_attr_combine_pedals); | ||
| 1368 | if (error) | ||
| 1369 | hid_warn(hid, "Unable to create sysfs interface for \"combine\", errno %d\n", error); | ||
| 1262 | error = device_create_file(&hid->dev, &dev_attr_range); | 1370 | error = device_create_file(&hid->dev, &dev_attr_range); |
| 1263 | if (error) | 1371 | if (error) |
| 1264 | hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error); | 1372 | hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error); |
| @@ -1358,6 +1466,7 @@ int lg4ff_deinit(struct hid_device *hid) | |||
| 1358 | device_remove_file(&hid->dev, &dev_attr_alternate_modes); | 1466 | device_remove_file(&hid->dev, &dev_attr_alternate_modes); |
| 1359 | } | 1467 | } |
| 1360 | 1468 | ||
| 1469 | device_remove_file(&hid->dev, &dev_attr_combine_pedals); | ||
| 1361 | device_remove_file(&hid->dev, &dev_attr_range); | 1470 | device_remove_file(&hid->dev, &dev_attr_range); |
| 1362 | #ifdef CONFIG_LEDS_CLASS | 1471 | #ifdef CONFIG_LEDS_CLASS |
| 1363 | { | 1472 | { |
diff --git a/drivers/hid/hid-lg4ff.h b/drivers/hid/hid-lg4ff.h index 66201af44da3..de1f350e0bd3 100644 --- a/drivers/hid/hid-lg4ff.h +++ b/drivers/hid/hid-lg4ff.h | |||
| @@ -6,11 +6,15 @@ extern int lg4ff_no_autoswitch; /* From hid-lg.c */ | |||
| 6 | 6 | ||
| 7 | int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, | 7 | int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, |
| 8 | struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data); | 8 | struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data); |
| 9 | int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
| 10 | u8 *rd, int size, struct lg_drv_data *drv_data); | ||
| 9 | int lg4ff_init(struct hid_device *hdev); | 11 | int lg4ff_init(struct hid_device *hdev); |
| 10 | int lg4ff_deinit(struct hid_device *hdev); | 12 | int lg4ff_deinit(struct hid_device *hdev); |
| 11 | #else | 13 | #else |
| 12 | static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, | 14 | static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, |
| 13 | struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; } | 15 | struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; } |
| 16 | static inline int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
| 17 | u8 *rd, int size, struct lg_drv_data *drv_data) { return 0; } | ||
| 14 | static inline int lg4ff_init(struct hid_device *hdev) { return -1; } | 18 | static inline int lg4ff_init(struct hid_device *hdev) { return -1; } |
| 15 | static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } | 19 | static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } |
| 16 | #endif | 20 | #endif |
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index e924d555536c..c6cd392e9f99 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c | |||
| @@ -28,7 +28,6 @@ | |||
| 28 | #define MS_RDESC 0x08 | 28 | #define MS_RDESC 0x08 |
| 29 | #define MS_NOGET 0x10 | 29 | #define MS_NOGET 0x10 |
| 30 | #define MS_DUPLICATE_USAGES 0x20 | 30 | #define MS_DUPLICATE_USAGES 0x20 |
| 31 | #define MS_RDESC_3K 0x40 | ||
| 32 | 31 | ||
| 33 | static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 32 | static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
| 34 | unsigned int *rsize) | 33 | unsigned int *rsize) |
| @@ -45,13 +44,6 @@ static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 45 | rdesc[557] = 0x35; | 44 | rdesc[557] = 0x35; |
| 46 | rdesc[559] = 0x45; | 45 | rdesc[559] = 0x45; |
| 47 | } | 46 | } |
| 48 | /* the same as above (s/usage/physical/) */ | ||
| 49 | if ((quirks & MS_RDESC_3K) && *rsize == 106 && rdesc[94] == 0x19 && | ||
| 50 | rdesc[95] == 0x00 && rdesc[96] == 0x29 && | ||
| 51 | rdesc[97] == 0xff) { | ||
| 52 | rdesc[94] = 0x35; | ||
| 53 | rdesc[96] = 0x45; | ||
| 54 | } | ||
| 55 | return rdesc; | 47 | return rdesc; |
| 56 | } | 48 | } |
| 57 | 49 | ||
| @@ -271,7 +263,7 @@ static const struct hid_device_id ms_devices[] = { | |||
| 271 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), | 263 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), |
| 272 | .driver_data = MS_PRESENTER }, | 264 | .driver_data = MS_PRESENTER }, |
| 273 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K), | 265 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K), |
| 274 | .driver_data = MS_ERGONOMY | MS_RDESC_3K }, | 266 | .driver_data = MS_ERGONOMY }, |
| 275 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K), | 267 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K), |
| 276 | .driver_data = MS_ERGONOMY }, | 268 | .driver_data = MS_ERGONOMY }, |
| 277 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600), | 269 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600), |
| @@ -288,6 +280,8 @@ static const struct hid_device_id ms_devices[] = { | |||
| 288 | .driver_data = MS_HIDINPUT }, | 280 | .driver_data = MS_HIDINPUT }, |
| 289 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), | 281 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), |
| 290 | .driver_data = MS_HIDINPUT }, | 282 | .driver_data = MS_HIDINPUT }, |
| 283 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP), | ||
| 284 | .driver_data = MS_HIDINPUT }, | ||
| 291 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), | 285 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), |
| 292 | .driver_data = MS_HIDINPUT }, | 286 | .driver_data = MS_HIDINPUT }, |
| 293 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), | 287 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), |
diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index 2f84b26f1167..39e642686ff0 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c | |||
| @@ -183,6 +183,8 @@ static const struct hid_device_id saitek_devices[] = { | |||
| 183 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | 183 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
| 184 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), | 184 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), |
| 185 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | 185 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
| 186 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9), | ||
| 187 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | ||
| 186 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9), | 188 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9), |
| 187 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | 189 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
| 188 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), | 190 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), |
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 3d5ba5b51af3..658a607dc6d9 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| 17 | * | 17 | * |
| 18 | */ | 18 | */ |
| 19 | |||
| 19 | #include <linux/device.h> | 20 | #include <linux/device.h> |
| 20 | #include <linux/hid.h> | 21 | #include <linux/hid.h> |
| 21 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| @@ -798,6 +799,9 @@ static const struct hid_device_id sensor_hub_devices[] = { | |||
| 798 | { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, | 799 | { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, |
| 799 | USB_DEVICE_ID_ITE_LENOVO_YOGA900), | 800 | USB_DEVICE_ID_ITE_LENOVO_YOGA900), |
| 800 | .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, | 801 | .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, |
| 802 | { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_0, | ||
| 803 | 0x22D8), | ||
| 804 | .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, | ||
| 801 | { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, | 805 | { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, |
| 802 | HID_ANY_ID) }, | 806 | HID_ANY_ID) }, |
| 803 | { } | 807 | { } |
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 310436a54a3f..b0bb99a821bd 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | * Copyright (c) 2012 David Dillow <dave@thedillows.org> | 8 | * Copyright (c) 2012 David Dillow <dave@thedillows.org> |
| 9 | * Copyright (c) 2006-2013 Jiri Kosina | 9 | * Copyright (c) 2006-2013 Jiri Kosina |
| 10 | * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> | 10 | * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> |
| 11 | * Copyright (c) 2014 Frank Praznik <frank.praznik@gmail.com> | 11 | * Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com> |
| 12 | */ | 12 | */ |
| 13 | 13 | ||
| 14 | /* | 14 | /* |
| @@ -51,6 +51,7 @@ | |||
| 51 | #define NAVIGATION_CONTROLLER_USB BIT(9) | 51 | #define NAVIGATION_CONTROLLER_USB BIT(9) |
| 52 | #define NAVIGATION_CONTROLLER_BT BIT(10) | 52 | #define NAVIGATION_CONTROLLER_BT BIT(10) |
| 53 | #define SINO_LITE_CONTROLLER BIT(11) | 53 | #define SINO_LITE_CONTROLLER BIT(11) |
| 54 | #define FUTUREMAX_DANCE_MAT BIT(12) | ||
| 54 | 55 | ||
| 55 | #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) | 56 | #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) |
| 56 | #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) | 57 | #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) |
| @@ -65,6 +66,8 @@ | |||
| 65 | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER) | 66 | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER) |
| 66 | #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\ | 67 | #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\ |
| 67 | MOTION_CONTROLLER) | 68 | MOTION_CONTROLLER) |
| 69 | #define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\ | ||
| 70 | MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT) | ||
| 68 | 71 | ||
| 69 | #define MAX_LEDS 4 | 72 | #define MAX_LEDS 4 |
| 70 | 73 | ||
| @@ -1048,6 +1051,7 @@ struct sony_sc { | |||
| 1048 | 1051 | ||
| 1049 | u8 mac_address[6]; | 1052 | u8 mac_address[6]; |
| 1050 | u8 worker_initialized; | 1053 | u8 worker_initialized; |
| 1054 | u8 defer_initialization; | ||
| 1051 | u8 cable_state; | 1055 | u8 cable_state; |
| 1052 | u8 battery_charging; | 1056 | u8 battery_charging; |
| 1053 | u8 battery_capacity; | 1057 | u8 battery_capacity; |
| @@ -1058,6 +1062,12 @@ struct sony_sc { | |||
| 1058 | u8 led_count; | 1062 | u8 led_count; |
| 1059 | }; | 1063 | }; |
| 1060 | 1064 | ||
| 1065 | static inline void sony_schedule_work(struct sony_sc *sc) | ||
| 1066 | { | ||
| 1067 | if (!sc->defer_initialization) | ||
| 1068 | schedule_work(&sc->state_worker); | ||
| 1069 | } | ||
| 1070 | |||
| 1061 | static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc, | 1071 | static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc, |
| 1062 | unsigned int *rsize) | 1072 | unsigned int *rsize) |
| 1063 | { | 1073 | { |
| @@ -1125,7 +1135,7 @@ static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, | |||
| 1125 | { | 1135 | { |
| 1126 | struct sony_sc *sc = hid_get_drvdata(hdev); | 1136 | struct sony_sc *sc = hid_get_drvdata(hdev); |
| 1127 | 1137 | ||
| 1128 | if (sc->quirks & SINO_LITE_CONTROLLER) | 1138 | if (sc->quirks & (SINO_LITE_CONTROLLER | FUTUREMAX_DANCE_MAT)) |
| 1129 | return rdesc; | 1139 | return rdesc; |
| 1130 | 1140 | ||
| 1131 | /* | 1141 | /* |
| @@ -1317,6 +1327,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, | |||
| 1317 | dualshock4_parse_report(sc, rd, size); | 1327 | dualshock4_parse_report(sc, rd, size); |
| 1318 | } | 1328 | } |
| 1319 | 1329 | ||
| 1330 | if (sc->defer_initialization) { | ||
| 1331 | sc->defer_initialization = 0; | ||
| 1332 | sony_schedule_work(sc); | ||
| 1333 | } | ||
| 1334 | |||
| 1320 | return 0; | 1335 | return 0; |
| 1321 | } | 1336 | } |
| 1322 | 1337 | ||
| @@ -1554,7 +1569,7 @@ static void buzz_set_leds(struct sony_sc *sc) | |||
| 1554 | static void sony_set_leds(struct sony_sc *sc) | 1569 | static void sony_set_leds(struct sony_sc *sc) |
| 1555 | { | 1570 | { |
| 1556 | if (!(sc->quirks & BUZZ_CONTROLLER)) | 1571 | if (!(sc->quirks & BUZZ_CONTROLLER)) |
| 1557 | schedule_work(&sc->state_worker); | 1572 | sony_schedule_work(sc); |
| 1558 | else | 1573 | else |
| 1559 | buzz_set_leds(sc); | 1574 | buzz_set_leds(sc); |
| 1560 | } | 1575 | } |
| @@ -1665,7 +1680,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, | |||
| 1665 | new_off != drv_data->led_delay_off[n]) { | 1680 | new_off != drv_data->led_delay_off[n]) { |
| 1666 | drv_data->led_delay_on[n] = new_on; | 1681 | drv_data->led_delay_on[n] = new_on; |
| 1667 | drv_data->led_delay_off[n] = new_off; | 1682 | drv_data->led_delay_off[n] = new_off; |
| 1668 | schedule_work(&drv_data->state_worker); | 1683 | sony_schedule_work(drv_data); |
| 1669 | } | 1684 | } |
| 1670 | 1685 | ||
| 1671 | return 0; | 1686 | return 0; |
| @@ -1865,6 +1880,17 @@ static void dualshock4_send_output_report(struct sony_sc *sc) | |||
| 1865 | u8 *buf = sc->output_report_dmabuf; | 1880 | u8 *buf = sc->output_report_dmabuf; |
| 1866 | int offset; | 1881 | int offset; |
| 1867 | 1882 | ||
| 1883 | /* | ||
| 1884 | * NOTE: The buf[1] field of the Bluetooth report controls | ||
| 1885 | * the Dualshock 4 reporting rate. | ||
| 1886 | * | ||
| 1887 | * Known values include: | ||
| 1888 | * | ||
| 1889 | * 0x80 - 1000hz (full speed) | ||
| 1890 | * 0xA0 - 31hz | ||
| 1891 | * 0xB0 - 20hz | ||
| 1892 | * 0xD0 - 66hz | ||
| 1893 | */ | ||
| 1868 | if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { | 1894 | if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { |
| 1869 | memset(buf, 0, DS4_REPORT_0x05_SIZE); | 1895 | memset(buf, 0, DS4_REPORT_0x05_SIZE); |
| 1870 | buf[0] = 0x05; | 1896 | buf[0] = 0x05; |
| @@ -1976,7 +2002,7 @@ static int sony_play_effect(struct input_dev *dev, void *data, | |||
| 1976 | sc->left = effect->u.rumble.strong_magnitude / 256; | 2002 | sc->left = effect->u.rumble.strong_magnitude / 256; |
| 1977 | sc->right = effect->u.rumble.weak_magnitude / 256; | 2003 | sc->right = effect->u.rumble.weak_magnitude / 256; |
| 1978 | 2004 | ||
| 1979 | schedule_work(&sc->state_worker); | 2005 | sony_schedule_work(sc); |
| 1980 | return 0; | 2006 | return 0; |
| 1981 | } | 2007 | } |
| 1982 | 2008 | ||
| @@ -2039,8 +2065,11 @@ static int sony_battery_get_property(struct power_supply *psy, | |||
| 2039 | return ret; | 2065 | return ret; |
| 2040 | } | 2066 | } |
| 2041 | 2067 | ||
| 2042 | static int sony_battery_probe(struct sony_sc *sc) | 2068 | static int sony_battery_probe(struct sony_sc *sc, int append_dev_id) |
| 2043 | { | 2069 | { |
| 2070 | const char *battery_str_fmt = append_dev_id ? | ||
| 2071 | "sony_controller_battery_%pMR_%i" : | ||
| 2072 | "sony_controller_battery_%pMR"; | ||
| 2044 | struct power_supply_config psy_cfg = { .drv_data = sc, }; | 2073 | struct power_supply_config psy_cfg = { .drv_data = sc, }; |
| 2045 | struct hid_device *hdev = sc->hdev; | 2074 | struct hid_device *hdev = sc->hdev; |
| 2046 | int ret; | 2075 | int ret; |
| @@ -2056,9 +2085,8 @@ static int sony_battery_probe(struct sony_sc *sc) | |||
| 2056 | sc->battery_desc.get_property = sony_battery_get_property; | 2085 | sc->battery_desc.get_property = sony_battery_get_property; |
| 2057 | sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; | 2086 | sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; |
| 2058 | sc->battery_desc.use_for_apm = 0; | 2087 | sc->battery_desc.use_for_apm = 0; |
| 2059 | sc->battery_desc.name = kasprintf(GFP_KERNEL, | 2088 | sc->battery_desc.name = kasprintf(GFP_KERNEL, battery_str_fmt, |
| 2060 | "sony_controller_battery_%pMR", | 2089 | sc->mac_address, sc->device_id); |
| 2061 | sc->mac_address); | ||
| 2062 | if (!sc->battery_desc.name) | 2090 | if (!sc->battery_desc.name) |
| 2063 | return -ENOMEM; | 2091 | return -ENOMEM; |
| 2064 | 2092 | ||
| @@ -2094,7 +2122,21 @@ static void sony_battery_remove(struct sony_sc *sc) | |||
| 2094 | * it will show up as two devices. A global list of connected controllers and | 2122 | * it will show up as two devices. A global list of connected controllers and |
| 2095 | * their MAC addresses is maintained to ensure that a device is only connected | 2123 | * their MAC addresses is maintained to ensure that a device is only connected |
| 2096 | * once. | 2124 | * once. |
| 2125 | * | ||
| 2126 | * Some USB-only devices masquerade as Sixaxis controllers and all have the | ||
| 2127 | * same dummy Bluetooth address, so a comparison of the connection type is | ||
| 2128 | * required. Devices are only rejected in the case where two devices have | ||
| 2129 | * matching Bluetooth addresses on different bus types. | ||
| 2097 | */ | 2130 | */ |
| 2131 | static inline int sony_compare_connection_type(struct sony_sc *sc0, | ||
| 2132 | struct sony_sc *sc1) | ||
| 2133 | { | ||
| 2134 | const int sc0_not_bt = !(sc0->quirks & SONY_BT_DEVICE); | ||
| 2135 | const int sc1_not_bt = !(sc1->quirks & SONY_BT_DEVICE); | ||
| 2136 | |||
| 2137 | return sc0_not_bt == sc1_not_bt; | ||
| 2138 | } | ||
| 2139 | |||
| 2098 | static int sony_check_add_dev_list(struct sony_sc *sc) | 2140 | static int sony_check_add_dev_list(struct sony_sc *sc) |
| 2099 | { | 2141 | { |
| 2100 | struct sony_sc *entry; | 2142 | struct sony_sc *entry; |
| @@ -2107,9 +2149,14 @@ static int sony_check_add_dev_list(struct sony_sc *sc) | |||
| 2107 | ret = memcmp(sc->mac_address, entry->mac_address, | 2149 | ret = memcmp(sc->mac_address, entry->mac_address, |
| 2108 | sizeof(sc->mac_address)); | 2150 | sizeof(sc->mac_address)); |
| 2109 | if (!ret) { | 2151 | if (!ret) { |
| 2110 | ret = -EEXIST; | 2152 | if (sony_compare_connection_type(sc, entry)) { |
| 2111 | hid_info(sc->hdev, "controller with MAC address %pMR already connected\n", | 2153 | ret = 1; |
| 2154 | } else { | ||
| 2155 | ret = -EEXIST; | ||
| 2156 | hid_info(sc->hdev, | ||
| 2157 | "controller with MAC address %pMR already connected\n", | ||
| 2112 | sc->mac_address); | 2158 | sc->mac_address); |
| 2159 | } | ||
| 2113 | goto unlock; | 2160 | goto unlock; |
| 2114 | } | 2161 | } |
| 2115 | } | 2162 | } |
| @@ -2285,10 +2332,14 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc) | |||
| 2285 | static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | 2332 | static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) |
| 2286 | { | 2333 | { |
| 2287 | int ret; | 2334 | int ret; |
| 2335 | int append_dev_id; | ||
| 2288 | unsigned long quirks = id->driver_data; | 2336 | unsigned long quirks = id->driver_data; |
| 2289 | struct sony_sc *sc; | 2337 | struct sony_sc *sc; |
| 2290 | unsigned int connect_mask = HID_CONNECT_DEFAULT; | 2338 | unsigned int connect_mask = HID_CONNECT_DEFAULT; |
| 2291 | 2339 | ||
| 2340 | if (!strcmp(hdev->name, "FutureMax Dance Mat")) | ||
| 2341 | quirks |= FUTUREMAX_DANCE_MAT; | ||
| 2342 | |||
| 2292 | sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); | 2343 | sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); |
| 2293 | if (sc == NULL) { | 2344 | if (sc == NULL) { |
| 2294 | hid_err(hdev, "can't alloc sony descriptor\n"); | 2345 | hid_err(hdev, "can't alloc sony descriptor\n"); |
| @@ -2341,9 +2392,16 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 2341 | * the Sixaxis does not want the report_id as part of the data | 2392 | * the Sixaxis does not want the report_id as part of the data |
| 2342 | * packet, so we have to discard buf[0] when sending the actual | 2393 | * packet, so we have to discard buf[0] when sending the actual |
| 2343 | * control message, even for numbered reports, humpf! | 2394 | * control message, even for numbered reports, humpf! |
| 2395 | * | ||
| 2396 | * Additionally, the Sixaxis on USB isn't properly initialized | ||
| 2397 | * until the PS logo button is pressed and as such won't retain | ||
| 2398 | * any state set by an output report, so the initial | ||
| 2399 | * configuration report is deferred until the first input | ||
| 2400 | * report arrives. | ||
| 2344 | */ | 2401 | */ |
| 2345 | hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; | 2402 | hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; |
| 2346 | hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; | 2403 | hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; |
| 2404 | sc->defer_initialization = 1; | ||
| 2347 | ret = sixaxis_set_operational_usb(hdev); | 2405 | ret = sixaxis_set_operational_usb(hdev); |
| 2348 | sony_init_output_report(sc, sixaxis_send_output_report); | 2406 | sony_init_output_report(sc, sixaxis_send_output_report); |
| 2349 | } else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) || | 2407 | } else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) || |
| @@ -2379,7 +2437,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 2379 | if (ret < 0) | 2437 | if (ret < 0) |
| 2380 | goto err_stop; | 2438 | goto err_stop; |
| 2381 | 2439 | ||
| 2382 | ret = sony_check_add(sc); | 2440 | ret = append_dev_id = sony_check_add(sc); |
| 2383 | if (ret < 0) | 2441 | if (ret < 0) |
| 2384 | goto err_stop; | 2442 | goto err_stop; |
| 2385 | 2443 | ||
| @@ -2390,7 +2448,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
| 2390 | } | 2448 | } |
| 2391 | 2449 | ||
| 2392 | if (sc->quirks & SONY_BATTERY_SUPPORT) { | 2450 | if (sc->quirks & SONY_BATTERY_SUPPORT) { |
| 2393 | ret = sony_battery_probe(sc); | 2451 | ret = sony_battery_probe(sc, append_dev_id); |
| 2394 | if (ret < 0) | 2452 | if (ret < 0) |
| 2395 | goto err_stop; | 2453 | goto err_stop; |
| 2396 | 2454 | ||
| @@ -2486,8 +2544,10 @@ static int sony_resume(struct hid_device *hdev) | |||
| 2486 | * reinitialized on resume or they won't behave properly. | 2544 | * reinitialized on resume or they won't behave properly. |
| 2487 | */ | 2545 | */ |
| 2488 | if ((sc->quirks & SIXAXIS_CONTROLLER_USB) || | 2546 | if ((sc->quirks & SIXAXIS_CONTROLLER_USB) || |
| 2489 | (sc->quirks & NAVIGATION_CONTROLLER_USB)) | 2547 | (sc->quirks & NAVIGATION_CONTROLLER_USB)) { |
| 2490 | sixaxis_set_operational_usb(sc->hdev); | 2548 | sixaxis_set_operational_usb(sc->hdev); |
| 2549 | sc->defer_initialization = 1; | ||
| 2550 | } | ||
| 2491 | 2551 | ||
| 2492 | sony_set_leds(sc); | 2552 | sony_set_leds(sc); |
| 2493 | } | 2553 | } |
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index 85ac43517e3f..1509d7287ff3 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c | |||
| @@ -21,13 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | #include "hid-ids.h" | 22 | #include "hid-ids.h" |
| 23 | 23 | ||
| 24 | /* | ||
| 25 | * See WPXXXXU model descriptions, device and HID report descriptors at | ||
| 26 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP4030U | ||
| 27 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP5540U | ||
| 28 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP8060U | ||
| 29 | */ | ||
| 30 | |||
| 31 | /* Size of the original descriptor of WPXXXXU tablets */ | 24 | /* Size of the original descriptor of WPXXXXU tablets */ |
| 32 | #define WPXXXXU_RDESC_ORIG_SIZE 212 | 25 | #define WPXXXXU_RDESC_ORIG_SIZE 212 |
| 33 | 26 | ||
| @@ -221,11 +214,6 @@ static __u8 wp8060u_rdesc_fixed[] = { | |||
| 221 | 0xC0 /* End Collection */ | 214 | 0xC0 /* End Collection */ |
| 222 | }; | 215 | }; |
| 223 | 216 | ||
| 224 | /* | ||
| 225 | * See WP1062 description, device and HID report descriptors at | ||
| 226 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP1062 | ||
| 227 | */ | ||
| 228 | |||
| 229 | /* Size of the original descriptor of WP1062 tablet */ | 217 | /* Size of the original descriptor of WP1062 tablet */ |
| 230 | #define WP1062_RDESC_ORIG_SIZE 254 | 218 | #define WP1062_RDESC_ORIG_SIZE 254 |
| 231 | 219 | ||
| @@ -274,11 +262,6 @@ static __u8 wp1062_rdesc_fixed[] = { | |||
| 274 | 0xC0 /* End Collection */ | 262 | 0xC0 /* End Collection */ |
| 275 | }; | 263 | }; |
| 276 | 264 | ||
| 277 | /* | ||
| 278 | * See PF1209 description, device and HID report descriptors at | ||
| 279 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_PF1209 | ||
| 280 | */ | ||
| 281 | |||
| 282 | /* Size of the original descriptor of PF1209 tablet */ | 265 | /* Size of the original descriptor of PF1209 tablet */ |
| 283 | #define PF1209_RDESC_ORIG_SIZE 234 | 266 | #define PF1209_RDESC_ORIG_SIZE 234 |
| 284 | 267 | ||
| @@ -356,11 +339,6 @@ static __u8 pf1209_rdesc_fixed[] = { | |||
| 356 | 0xC0 /* End Collection */ | 339 | 0xC0 /* End Collection */ |
| 357 | }; | 340 | }; |
| 358 | 341 | ||
| 359 | /* | ||
| 360 | * See TWHL850 description, device and HID report descriptors at | ||
| 361 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850 | ||
| 362 | */ | ||
| 363 | |||
| 364 | /* Size of the original descriptors of TWHL850 tablet */ | 342 | /* Size of the original descriptors of TWHL850 tablet */ |
| 365 | #define TWHL850_RDESC_ORIG_SIZE0 182 | 343 | #define TWHL850_RDESC_ORIG_SIZE0 182 |
| 366 | #define TWHL850_RDESC_ORIG_SIZE1 161 | 344 | #define TWHL850_RDESC_ORIG_SIZE1 161 |
| @@ -469,11 +447,6 @@ static __u8 twhl850_rdesc_fixed2[] = { | |||
| 469 | 0xC0 /* End Collection */ | 447 | 0xC0 /* End Collection */ |
| 470 | }; | 448 | }; |
| 471 | 449 | ||
| 472 | /* | ||
| 473 | * See TWHA60 description, device and HID report descriptors at | ||
| 474 | * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60 | ||
| 475 | */ | ||
| 476 | |||
| 477 | /* Size of the original descriptors of TWHA60 tablet */ | 450 | /* Size of the original descriptors of TWHA60 tablet */ |
| 478 | #define TWHA60_RDESC_ORIG_SIZE0 254 | 451 | #define TWHA60_RDESC_ORIG_SIZE0 254 |
| 479 | #define TWHA60_RDESC_ORIG_SIZE1 139 | 452 | #define TWHA60_RDESC_ORIG_SIZE1 139 |
| @@ -613,6 +586,27 @@ static const __u8 uclogic_tablet_rdesc_template[] = { | |||
| 613 | 0xC0 /* End Collection */ | 586 | 0xC0 /* End Collection */ |
| 614 | }; | 587 | }; |
| 615 | 588 | ||
| 589 | /* Fixed virtual pad report descriptor */ | ||
| 590 | static const __u8 uclogic_buttonpad_rdesc[] = { | ||
| 591 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
| 592 | 0x09, 0x07, /* Usage (Keypad), */ | ||
| 593 | 0xA1, 0x01, /* Collection (Application), */ | ||
| 594 | 0x85, 0xF7, /* Report ID (247), */ | ||
| 595 | 0x05, 0x0D, /* Usage Page (Digitizers), */ | ||
| 596 | 0x09, 0x39, /* Usage (Tablet Function Keys), */ | ||
| 597 | 0xA0, /* Collection (Physical), */ | ||
| 598 | 0x05, 0x09, /* Usage Page (Button), */ | ||
| 599 | 0x75, 0x01, /* Report Size (1), */ | ||
| 600 | 0x95, 0x18, /* Report Count (24), */ | ||
| 601 | 0x81, 0x03, /* Input (Constant, Variable), */ | ||
| 602 | 0x19, 0x01, /* Usage Minimum (01h), */ | ||
| 603 | 0x29, 0x08, /* Usage Maximum (08h), */ | ||
| 604 | 0x95, 0x08, /* Report Count (8), */ | ||
| 605 | 0x81, 0x02, /* Input (Variable), */ | ||
| 606 | 0xC0, /* End Collection */ | ||
| 607 | 0xC0 /* End Collection */ | ||
| 608 | }; | ||
| 609 | |||
| 616 | /* Parameter indices */ | 610 | /* Parameter indices */ |
| 617 | enum uclogic_prm { | 611 | enum uclogic_prm { |
| 618 | UCLOGIC_PRM_X_LM = 1, | 612 | UCLOGIC_PRM_X_LM = 1, |
| @@ -628,6 +622,7 @@ struct uclogic_drvdata { | |||
| 628 | unsigned int rsize; | 622 | unsigned int rsize; |
| 629 | bool invert_pen_inrange; | 623 | bool invert_pen_inrange; |
| 630 | bool ignore_pen_usage; | 624 | bool ignore_pen_usage; |
| 625 | bool has_virtual_pad_interface; | ||
| 631 | }; | 626 | }; |
| 632 | 627 | ||
| 633 | static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 628 | static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
| @@ -637,6 +632,12 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 637 | __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; | 632 | __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; |
| 638 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | 633 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); |
| 639 | 634 | ||
| 635 | if (drvdata->rdesc != NULL) { | ||
| 636 | rdesc = drvdata->rdesc; | ||
| 637 | *rsize = drvdata->rsize; | ||
| 638 | return rdesc; | ||
| 639 | } | ||
| 640 | |||
| 640 | switch (hdev->product) { | 641 | switch (hdev->product) { |
| 641 | case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: | 642 | case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: |
| 642 | if (*rsize == PF1209_RDESC_ORIG_SIZE) { | 643 | if (*rsize == PF1209_RDESC_ORIG_SIZE) { |
| @@ -706,11 +707,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 706 | break; | 707 | break; |
| 707 | } | 708 | } |
| 708 | break; | 709 | break; |
| 709 | default: | ||
| 710 | if (drvdata->rdesc != NULL) { | ||
| 711 | rdesc = drvdata->rdesc; | ||
| 712 | *rsize = drvdata->rsize; | ||
| 713 | } | ||
| 714 | } | 710 | } |
| 715 | 711 | ||
| 716 | return rdesc; | 712 | return rdesc; |
| @@ -804,7 +800,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev) | |||
| 804 | len = UCLOGIC_PRM_NUM * sizeof(*buf); | 800 | len = UCLOGIC_PRM_NUM * sizeof(*buf); |
| 805 | buf = kmalloc(len, GFP_KERNEL); | 801 | buf = kmalloc(len, GFP_KERNEL); |
| 806 | if (buf == NULL) { | 802 | if (buf == NULL) { |
| 807 | hid_err(hdev, "failed to allocate parameter buffer\n"); | ||
| 808 | rc = -ENOMEM; | 803 | rc = -ENOMEM; |
| 809 | goto cleanup; | 804 | goto cleanup; |
| 810 | } | 805 | } |
| @@ -848,7 +843,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev) | |||
| 848 | sizeof(uclogic_tablet_rdesc_template), | 843 | sizeof(uclogic_tablet_rdesc_template), |
| 849 | GFP_KERNEL); | 844 | GFP_KERNEL); |
| 850 | if (drvdata->rdesc == NULL) { | 845 | if (drvdata->rdesc == NULL) { |
| 851 | hid_err(hdev, "failed to allocate fixed rdesc\n"); | ||
| 852 | rc = -ENOMEM; | 846 | rc = -ENOMEM; |
| 853 | goto cleanup; | 847 | goto cleanup; |
| 854 | } | 848 | } |
| @@ -876,11 +870,75 @@ cleanup: | |||
| 876 | return rc; | 870 | return rc; |
| 877 | } | 871 | } |
| 878 | 872 | ||
| 873 | /** | ||
| 874 | * Enable actual button mode. | ||
| 875 | * | ||
| 876 | * @hdev: HID device | ||
| 877 | */ | ||
| 878 | static int uclogic_button_enable(struct hid_device *hdev) | ||
| 879 | { | ||
| 880 | int rc; | ||
| 881 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); | ||
| 882 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
| 883 | char *str_buf; | ||
| 884 | size_t str_len = 16; | ||
| 885 | unsigned char *rdesc; | ||
| 886 | size_t rdesc_len; | ||
| 887 | |||
| 888 | str_buf = kzalloc(str_len, GFP_KERNEL); | ||
| 889 | if (str_buf == NULL) { | ||
| 890 | rc = -ENOMEM; | ||
| 891 | goto cleanup; | ||
| 892 | } | ||
| 893 | |||
| 894 | /* Enable abstract keyboard mode */ | ||
| 895 | rc = usb_string(usb_dev, 0x7b, str_buf, str_len); | ||
| 896 | if (rc == -EPIPE) { | ||
| 897 | hid_info(hdev, "button mode setting not found\n"); | ||
| 898 | rc = 0; | ||
| 899 | goto cleanup; | ||
| 900 | } else if (rc < 0) { | ||
| 901 | hid_err(hdev, "failed to enable abstract keyboard\n"); | ||
| 902 | goto cleanup; | ||
| 903 | } else if (strncmp(str_buf, "HK On", rc)) { | ||
| 904 | hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", | ||
| 905 | str_buf); | ||
| 906 | rc = -EINVAL; | ||
| 907 | goto cleanup; | ||
| 908 | } | ||
| 909 | |||
| 910 | /* Re-allocate fixed report descriptor */ | ||
| 911 | rdesc_len = drvdata->rsize + sizeof(uclogic_buttonpad_rdesc); | ||
| 912 | rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); | ||
| 913 | if (!rdesc) { | ||
| 914 | rc = -ENOMEM; | ||
| 915 | goto cleanup; | ||
| 916 | } | ||
| 917 | |||
| 918 | memcpy(rdesc, drvdata->rdesc, drvdata->rsize); | ||
| 919 | |||
| 920 | /* Append the buttonpad descriptor */ | ||
| 921 | memcpy(rdesc + drvdata->rsize, uclogic_buttonpad_rdesc, | ||
| 922 | sizeof(uclogic_buttonpad_rdesc)); | ||
| 923 | |||
| 924 | /* clean up old rdesc and use the new one */ | ||
| 925 | drvdata->rsize = rdesc_len; | ||
| 926 | devm_kfree(&hdev->dev, drvdata->rdesc); | ||
| 927 | drvdata->rdesc = rdesc; | ||
| 928 | |||
| 929 | rc = 0; | ||
| 930 | |||
| 931 | cleanup: | ||
| 932 | kfree(str_buf); | ||
| 933 | return rc; | ||
| 934 | } | ||
| 935 | |||
| 879 | static int uclogic_probe(struct hid_device *hdev, | 936 | static int uclogic_probe(struct hid_device *hdev, |
| 880 | const struct hid_device_id *id) | 937 | const struct hid_device_id *id) |
| 881 | { | 938 | { |
| 882 | int rc; | 939 | int rc; |
| 883 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | 940 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
| 941 | struct usb_device *udev = hid_to_usb_dev(hdev); | ||
| 884 | struct uclogic_drvdata *drvdata; | 942 | struct uclogic_drvdata *drvdata; |
| 885 | 943 | ||
| 886 | /* | 944 | /* |
| @@ -899,6 +957,10 @@ static int uclogic_probe(struct hid_device *hdev, | |||
| 899 | 957 | ||
| 900 | switch (id->product) { | 958 | switch (id->product) { |
| 901 | case USB_DEVICE_ID_HUION_TABLET: | 959 | case USB_DEVICE_ID_HUION_TABLET: |
| 960 | case USB_DEVICE_ID_YIYNOVA_TABLET: | ||
| 961 | case USB_DEVICE_ID_UGEE_TABLET_81: | ||
| 962 | case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: | ||
| 963 | case USB_DEVICE_ID_UGEE_TABLET_45: | ||
| 902 | /* If this is the pen interface */ | 964 | /* If this is the pen interface */ |
| 903 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { | 965 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { |
| 904 | rc = uclogic_tablet_enable(hdev); | 966 | rc = uclogic_tablet_enable(hdev); |
| @@ -907,10 +969,48 @@ static int uclogic_probe(struct hid_device *hdev, | |||
| 907 | return rc; | 969 | return rc; |
| 908 | } | 970 | } |
| 909 | drvdata->invert_pen_inrange = true; | 971 | drvdata->invert_pen_inrange = true; |
| 972 | |||
| 973 | rc = uclogic_button_enable(hdev); | ||
| 974 | drvdata->has_virtual_pad_interface = !rc; | ||
| 910 | } else { | 975 | } else { |
| 911 | drvdata->ignore_pen_usage = true; | 976 | drvdata->ignore_pen_usage = true; |
| 912 | } | 977 | } |
| 913 | break; | 978 | break; |
| 979 | case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: | ||
| 980 | /* If this is the pen interface */ | ||
| 981 | if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { | ||
| 982 | rc = uclogic_tablet_enable(hdev); | ||
| 983 | if (rc) { | ||
| 984 | hid_err(hdev, "tablet enabling failed\n"); | ||
| 985 | return rc; | ||
| 986 | } | ||
| 987 | drvdata->invert_pen_inrange = true; | ||
| 988 | } else { | ||
| 989 | drvdata->ignore_pen_usage = true; | ||
| 990 | } | ||
| 991 | break; | ||
| 992 | case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: | ||
| 993 | /* | ||
| 994 | * If it is the three-interface version, which is known to | ||
| 995 | * respond to initialization. | ||
| 996 | */ | ||
| 997 | if (udev->config->desc.bNumInterfaces == 3) { | ||
| 998 | /* If it is the pen interface */ | ||
| 999 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { | ||
| 1000 | rc = uclogic_tablet_enable(hdev); | ||
| 1001 | if (rc) { | ||
| 1002 | hid_err(hdev, "tablet enabling failed\n"); | ||
| 1003 | return rc; | ||
| 1004 | } | ||
| 1005 | drvdata->invert_pen_inrange = true; | ||
| 1006 | |||
| 1007 | rc = uclogic_button_enable(hdev); | ||
| 1008 | drvdata->has_virtual_pad_interface = !rc; | ||
| 1009 | } else { | ||
| 1010 | drvdata->ignore_pen_usage = true; | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | break; | ||
| 914 | } | 1014 | } |
| 915 | 1015 | ||
| 916 | rc = hid_parse(hdev); | 1016 | rc = hid_parse(hdev); |
| @@ -933,12 +1033,16 @@ static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, | |||
| 933 | { | 1033 | { |
| 934 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | 1034 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); |
| 935 | 1035 | ||
| 936 | if ((drvdata->invert_pen_inrange) && | 1036 | if ((report->type == HID_INPUT_REPORT) && |
| 937 | (report->type == HID_INPUT_REPORT) && | ||
| 938 | (report->id == UCLOGIC_PEN_REPORT_ID) && | 1037 | (report->id == UCLOGIC_PEN_REPORT_ID) && |
| 939 | (size >= 2)) | 1038 | (size >= 2)) { |
| 940 | /* Invert the in-range bit */ | 1039 | if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) |
| 941 | data[1] ^= 0x40; | 1040 | /* Change to virtual frame button report ID */ |
| 1041 | data[0] = 0xf7; | ||
| 1042 | else if (drvdata->invert_pen_inrange) | ||
| 1043 | /* Invert the in-range bit */ | ||
| 1044 | data[1] ^= 0x40; | ||
| 1045 | } | ||
| 942 | 1046 | ||
| 943 | return 0; | 1047 | return 0; |
| 944 | } | 1048 | } |
| @@ -960,6 +1064,11 @@ static const struct hid_device_id uclogic_devices[] = { | |||
| 960 | USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, | 1064 | USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, |
| 961 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, | 1065 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, |
| 962 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, | 1066 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, |
| 1067 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, | ||
| 1068 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, | ||
| 1069 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, | ||
| 1070 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, | ||
| 1071 | { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, | ||
| 963 | { } | 1072 | { } |
| 964 | }; | 1073 | }; |
| 965 | MODULE_DEVICE_TABLE(hid, uclogic_devices); | 1074 | MODULE_DEVICE_TABLE(hid, uclogic_devices); |
diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c index 059931d7b392..a91aabe4a70a 100644 --- a/drivers/hid/hid-waltop.c +++ b/drivers/hid/hid-waltop.c | |||
| @@ -42,11 +42,6 @@ | |||
| 42 | * 02 16 02 ink | 42 | * 02 16 02 ink |
| 43 | */ | 43 | */ |
| 44 | 44 | ||
| 45 | /* | ||
| 46 | * See Slim Tablet 5.8 inch description, device and HID report descriptors at | ||
| 47 | * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_5.8%22 | ||
| 48 | */ | ||
| 49 | |||
| 50 | /* Size of the original report descriptor of Slim Tablet 5.8 inch */ | 45 | /* Size of the original report descriptor of Slim Tablet 5.8 inch */ |
| 51 | #define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222 | 46 | #define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222 |
| 52 | 47 | ||
| @@ -98,11 +93,6 @@ static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = { | |||
| 98 | 0xC0 /* End Collection */ | 93 | 0xC0 /* End Collection */ |
| 99 | }; | 94 | }; |
| 100 | 95 | ||
| 101 | /* | ||
| 102 | * See Slim Tablet 12.1 inch description, device and HID report descriptors at | ||
| 103 | * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_12.1%22 | ||
| 104 | */ | ||
| 105 | |||
| 106 | /* Size of the original report descriptor of Slim Tablet 12.1 inch */ | 96 | /* Size of the original report descriptor of Slim Tablet 12.1 inch */ |
| 107 | #define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269 | 97 | #define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269 |
| 108 | 98 | ||
| @@ -154,11 +144,6 @@ static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = { | |||
| 154 | 0xC0 /* End Collection */ | 144 | 0xC0 /* End Collection */ |
| 155 | }; | 145 | }; |
| 156 | 146 | ||
| 157 | /* | ||
| 158 | * See Q Pad description, device and HID report descriptors at | ||
| 159 | * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Q_Pad | ||
| 160 | */ | ||
| 161 | |||
| 162 | /* Size of the original report descriptor of Q Pad */ | 147 | /* Size of the original report descriptor of Q Pad */ |
| 163 | #define Q_PAD_RDESC_ORIG_SIZE 241 | 148 | #define Q_PAD_RDESC_ORIG_SIZE 241 |
| 164 | 149 | ||
| @@ -210,11 +195,6 @@ static __u8 q_pad_rdesc_fixed[] = { | |||
| 210 | 0xC0 /* End Collection */ | 195 | 0xC0 /* End Collection */ |
| 211 | }; | 196 | }; |
| 212 | 197 | ||
| 213 | /* | ||
| 214 | * See description, device and HID report descriptors of tablet with PID 0038 at | ||
| 215 | * http://sf.net/apps/mediawiki/digimend/?title=Waltop_PID_0038 | ||
| 216 | */ | ||
| 217 | |||
| 218 | /* Size of the original report descriptor of tablet with PID 0038 */ | 198 | /* Size of the original report descriptor of tablet with PID 0038 */ |
| 219 | #define PID_0038_RDESC_ORIG_SIZE 241 | 199 | #define PID_0038_RDESC_ORIG_SIZE 241 |
| 220 | 200 | ||
| @@ -268,11 +248,6 @@ static __u8 pid_0038_rdesc_fixed[] = { | |||
| 268 | 0xC0 /* End Collection */ | 248 | 0xC0 /* End Collection */ |
| 269 | }; | 249 | }; |
| 270 | 250 | ||
| 271 | /* | ||
| 272 | * See Media Tablet 10.6 inch description, device and HID report descriptors at | ||
| 273 | * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_10.6%22 | ||
| 274 | */ | ||
| 275 | |||
| 276 | /* Size of the original report descriptor of Media Tablet 10.6 inch */ | 251 | /* Size of the original report descriptor of Media Tablet 10.6 inch */ |
| 277 | #define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300 | 252 | #define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300 |
| 278 | 253 | ||
| @@ -386,11 +361,6 @@ static __u8 media_tablet_10_6_inch_rdesc_fixed[] = { | |||
| 386 | 0xC0 /* End Collection */ | 361 | 0xC0 /* End Collection */ |
| 387 | }; | 362 | }; |
| 388 | 363 | ||
| 389 | /* | ||
| 390 | * See Media Tablet 14.1 inch description, device and HID report descriptors at | ||
| 391 | * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_14.1%22 | ||
| 392 | */ | ||
| 393 | |||
| 394 | /* Size of the original report descriptor of Media Tablet 14.1 inch */ | 364 | /* Size of the original report descriptor of Media Tablet 14.1 inch */ |
| 395 | #define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309 | 365 | #define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309 |
| 396 | 366 | ||
| @@ -502,12 +472,6 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = { | |||
| 502 | 0xC0 /* End Collection */ | 472 | 0xC0 /* End Collection */ |
| 503 | }; | 473 | }; |
| 504 | 474 | ||
| 505 | /* | ||
| 506 | * See Sirius Battery Free Tablet description, device and HID report descriptors | ||
| 507 | * at | ||
| 508 | * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Sirius_Battery_Free_Tablet | ||
| 509 | */ | ||
| 510 | |||
| 511 | /* Size of the original report descriptor of Sirius Battery Free Tablet */ | 475 | /* Size of the original report descriptor of Sirius Battery Free Tablet */ |
| 512 | #define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335 | 476 | #define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335 |
| 513 | 477 | ||
diff --git a/drivers/hid/intel-ish-hid/Kconfig b/drivers/hid/intel-ish-hid/Kconfig new file mode 100644 index 000000000000..ea065b3684a2 --- /dev/null +++ b/drivers/hid/intel-ish-hid/Kconfig | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | menu "Intel ISH HID support" | ||
| 2 | depends on X86_64 && PCI | ||
| 3 | |||
| 4 | config INTEL_ISH_HID | ||
| 5 | tristate "Intel Integrated Sensor Hub" | ||
| 6 | default n | ||
| 7 | select HID | ||
| 8 | help | ||
| 9 | The Integrated Sensor Hub (ISH) enables the ability to offload | ||
| 10 | sensor polling and algorithm processing to a dedicated low power | ||
| 11 | processor in the chipset. This allows the core processor to go into | ||
| 12 | low power modes more often, resulting in the increased battery life. | ||
| 13 | The current processors that support ISH are: Cherrytrail, Skylake, | ||
| 14 | Broxton and Kaby Lake. | ||
| 15 | |||
| 16 | Say Y here if you want to support Intel ISH. If unsure, say N. | ||
| 17 | endmenu | ||
diff --git a/drivers/hid/intel-ish-hid/Makefile b/drivers/hid/intel-ish-hid/Makefile new file mode 100644 index 000000000000..8c08b0b358b1 --- /dev/null +++ b/drivers/hid/intel-ish-hid/Makefile | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | # | ||
| 2 | # Makefile - Intel ISH HID drivers | ||
| 3 | # Copyright (c) 2014-2016, Intel Corporation. | ||
| 4 | # | ||
| 5 | # | ||
| 6 | obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp.o | ||
| 7 | intel-ishtp-objs := ishtp/init.o | ||
| 8 | intel-ishtp-objs += ishtp/hbm.o | ||
| 9 | intel-ishtp-objs += ishtp/client.o | ||
| 10 | intel-ishtp-objs += ishtp/bus.o | ||
| 11 | intel-ishtp-objs += ishtp/dma-if.o | ||
| 12 | intel-ishtp-objs += ishtp/client-buffers.o | ||
| 13 | |||
| 14 | obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-ipc.o | ||
| 15 | intel-ish-ipc-objs := ipc/ipc.o | ||
| 16 | intel-ish-ipc-objs += ipc/pci-ish.o | ||
| 17 | |||
| 18 | obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp-hid.o | ||
| 19 | intel-ishtp-hid-objs := ishtp-hid.o | ||
| 20 | intel-ishtp-hid-objs += ishtp-hid-client.o | ||
| 21 | |||
| 22 | ccflags-y += -Idrivers/hid/intel-ish-hid/ishtp | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h new file mode 100644 index 000000000000..ab68afcba2a2 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | /* | ||
| 2 | * ISH registers definitions | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef _ISHTP_ISH_REGS_H_ | ||
| 17 | #define _ISHTP_ISH_REGS_H_ | ||
| 18 | |||
| 19 | |||
| 20 | /*** IPC PCI Offsets and sizes ***/ | ||
| 21 | /* ISH IPC Base Address */ | ||
| 22 | #define IPC_REG_BASE 0x0000 | ||
| 23 | /* Peripheral Interrupt Status Register */ | ||
| 24 | #define IPC_REG_PISR_CHV_AB (IPC_REG_BASE + 0x00) | ||
| 25 | /* Peripheral Interrupt Mask Register */ | ||
| 26 | #define IPC_REG_PIMR_CHV_AB (IPC_REG_BASE + 0x04) | ||
| 27 | /*BXT, CHV_K0*/ | ||
| 28 | /*Peripheral Interrupt Status Register */ | ||
| 29 | #define IPC_REG_PISR_BXT (IPC_REG_BASE + 0x0C) | ||
| 30 | /*Peripheral Interrupt Mask Register */ | ||
| 31 | #define IPC_REG_PIMR_BXT (IPC_REG_BASE + 0x08) | ||
| 32 | /***********************************/ | ||
| 33 | /* ISH Host Firmware status Register */ | ||
| 34 | #define IPC_REG_ISH_HOST_FWSTS (IPC_REG_BASE + 0x34) | ||
| 35 | /* Host Communication Register */ | ||
| 36 | #define IPC_REG_HOST_COMM (IPC_REG_BASE + 0x38) | ||
| 37 | /* Reset register */ | ||
| 38 | #define IPC_REG_ISH_RST (IPC_REG_BASE + 0x44) | ||
| 39 | |||
| 40 | /* Inbound doorbell register Host to ISH */ | ||
| 41 | #define IPC_REG_HOST2ISH_DRBL (IPC_REG_BASE + 0x48) | ||
| 42 | /* Outbound doorbell register ISH to Host */ | ||
| 43 | #define IPC_REG_ISH2HOST_DRBL (IPC_REG_BASE + 0x54) | ||
| 44 | /* ISH to HOST message registers */ | ||
| 45 | #define IPC_REG_ISH2HOST_MSG (IPC_REG_BASE + 0x60) | ||
| 46 | /* HOST to ISH message registers */ | ||
| 47 | #define IPC_REG_HOST2ISH_MSG (IPC_REG_BASE + 0xE0) | ||
| 48 | /* REMAP2 to enable DMA (D3 RCR) */ | ||
| 49 | #define IPC_REG_ISH_RMP2 (IPC_REG_BASE + 0x368) | ||
| 50 | |||
| 51 | #define IPC_REG_MAX (IPC_REG_BASE + 0x400) | ||
| 52 | |||
| 53 | /*** register bits - HISR ***/ | ||
| 54 | /* bit corresponds HOST2ISH interrupt in PISR and PIMR registers */ | ||
| 55 | #define IPC_INT_HOST2ISH_BIT (1<<0) | ||
| 56 | /***********************************/ | ||
| 57 | /*CHV_A0, CHV_B0*/ | ||
| 58 | /* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */ | ||
| 59 | #define IPC_INT_ISH2HOST_BIT_CHV_AB (1<<3) | ||
| 60 | /*BXT, CHV_K0*/ | ||
| 61 | /* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */ | ||
| 62 | #define IPC_INT_ISH2HOST_BIT_BXT (1<<0) | ||
| 63 | /***********************************/ | ||
| 64 | |||
| 65 | /* bit corresponds ISH2HOST busy clear interrupt in PIMR register */ | ||
| 66 | #define IPC_INT_ISH2HOST_CLR_MASK_BIT (1<<11) | ||
| 67 | |||
| 68 | /* offset of ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */ | ||
| 69 | #define IPC_INT_ISH2HOST_CLR_OFFS (0) | ||
| 70 | |||
| 71 | /* bit corresponds ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */ | ||
| 72 | #define IPC_INT_ISH2HOST_CLR_BIT (1<<IPC_INT_ISH2HOST_CLR_OFFS) | ||
| 73 | |||
| 74 | /* bit corresponds busy bit in doorbell registers */ | ||
| 75 | #define IPC_DRBL_BUSY_OFFS (31) | ||
| 76 | #define IPC_DRBL_BUSY_BIT (1<<IPC_DRBL_BUSY_OFFS) | ||
| 77 | |||
| 78 | #define IPC_HOST_OWNS_MSG_OFFS (30) | ||
| 79 | |||
| 80 | /* | ||
| 81 | * A0: bit means that host owns MSGnn registers and is reading them. | ||
| 82 | * ISH FW may not write to them | ||
| 83 | */ | ||
| 84 | #define IPC_HOST_OWNS_MSG_BIT (1<<IPC_HOST_OWNS_MSG_OFFS) | ||
| 85 | |||
| 86 | /* | ||
| 87 | * Host status bits (HOSTCOMM) | ||
| 88 | */ | ||
| 89 | /* bit corresponds host ready bit in Host Status Register (HOST_COMM) */ | ||
| 90 | #define IPC_HOSTCOMM_READY_OFFS (7) | ||
| 91 | #define IPC_HOSTCOMM_READY_BIT (1<<IPC_HOSTCOMM_READY_OFFS) | ||
| 92 | |||
| 93 | /***********************************/ | ||
| 94 | /*CHV_A0, CHV_B0*/ | ||
| 95 | #define IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB (31) | ||
| 96 | #define IPC_HOSTCOMM_INT_EN_BIT_CHV_AB \ | ||
| 97 | (1<<IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB) | ||
| 98 | /*BXT, CHV_K0*/ | ||
| 99 | #define IPC_PIMR_INT_EN_OFFS_BXT (0) | ||
| 100 | #define IPC_PIMR_INT_EN_BIT_BXT (1<<IPC_PIMR_INT_EN_OFFS_BXT) | ||
| 101 | |||
| 102 | #define IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT (8) | ||
| 103 | #define IPC_HOST2ISH_BUSYCLEAR_MASK_BIT \ | ||
| 104 | (1<<IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT) | ||
| 105 | /***********************************/ | ||
| 106 | /* | ||
| 107 | * both Host and ISH have ILUP at bit 0 | ||
| 108 | * bit corresponds host ready bit in both status registers | ||
| 109 | */ | ||
| 110 | #define IPC_ILUP_OFFS (0) | ||
| 111 | #define IPC_ILUP_BIT (1<<IPC_ILUP_OFFS) | ||
| 112 | |||
| 113 | /* | ||
| 114 | * FW status bits (relevant) | ||
| 115 | */ | ||
| 116 | #define IPC_FWSTS_ILUP 0x1 | ||
| 117 | #define IPC_FWSTS_ISHTP_UP (1<<1) | ||
| 118 | #define IPC_FWSTS_DMA0 (1<<16) | ||
| 119 | #define IPC_FWSTS_DMA1 (1<<17) | ||
| 120 | #define IPC_FWSTS_DMA2 (1<<18) | ||
| 121 | #define IPC_FWSTS_DMA3 (1<<19) | ||
| 122 | |||
| 123 | #define IPC_ISH_IN_DMA \ | ||
| 124 | (IPC_FWSTS_DMA0 | IPC_FWSTS_DMA1 | IPC_FWSTS_DMA2 | IPC_FWSTS_DMA3) | ||
| 125 | |||
| 126 | /* bit corresponds host ready bit in ISH FW Status Register */ | ||
| 127 | #define IPC_ISH_ISHTP_READY_OFFS (1) | ||
| 128 | #define IPC_ISH_ISHTP_READY_BIT (1<<IPC_ISH_ISHTP_READY_OFFS) | ||
| 129 | |||
| 130 | #define IPC_RMP2_DMA_ENABLED 0x1 /* Value to enable DMA, per D3 RCR */ | ||
| 131 | |||
| 132 | #define IPC_MSG_MAX_SIZE 0x80 | ||
| 133 | |||
| 134 | |||
| 135 | #define IPC_HEADER_LENGTH_MASK 0x03FF | ||
| 136 | #define IPC_HEADER_PROTOCOL_MASK 0x0F | ||
| 137 | #define IPC_HEADER_MNG_CMD_MASK 0x0F | ||
| 138 | |||
| 139 | #define IPC_HEADER_LENGTH_OFFSET 0 | ||
| 140 | #define IPC_HEADER_PROTOCOL_OFFSET 10 | ||
| 141 | #define IPC_HEADER_MNG_CMD_OFFSET 16 | ||
| 142 | |||
| 143 | #define IPC_HEADER_GET_LENGTH(drbl_reg) \ | ||
| 144 | (((drbl_reg) >> IPC_HEADER_LENGTH_OFFSET)&IPC_HEADER_LENGTH_MASK) | ||
| 145 | #define IPC_HEADER_GET_PROTOCOL(drbl_reg) \ | ||
| 146 | (((drbl_reg) >> IPC_HEADER_PROTOCOL_OFFSET)&IPC_HEADER_PROTOCOL_MASK) | ||
| 147 | #define IPC_HEADER_GET_MNG_CMD(drbl_reg) \ | ||
| 148 | (((drbl_reg) >> IPC_HEADER_MNG_CMD_OFFSET)&IPC_HEADER_MNG_CMD_MASK) | ||
| 149 | |||
| 150 | #define IPC_IS_BUSY(drbl_reg) \ | ||
| 151 | (((drbl_reg)&IPC_DRBL_BUSY_BIT) == ((uint32_t)IPC_DRBL_BUSY_BIT)) | ||
| 152 | |||
| 153 | /***********************************/ | ||
| 154 | /*CHV_A0, CHV_B0*/ | ||
| 155 | #define IPC_INT_FROM_ISH_TO_HOST_CHV_AB(drbl_reg) \ | ||
| 156 | (((drbl_reg)&IPC_INT_ISH2HOST_BIT_CHV_AB) == \ | ||
| 157 | ((u32)IPC_INT_ISH2HOST_BIT_CHV_AB)) | ||
| 158 | /*BXT, CHV_K0*/ | ||
| 159 | #define IPC_INT_FROM_ISH_TO_HOST_BXT(drbl_reg) \ | ||
| 160 | (((drbl_reg)&IPC_INT_ISH2HOST_BIT_BXT) == \ | ||
| 161 | ((u32)IPC_INT_ISH2HOST_BIT_BXT)) | ||
| 162 | /***********************************/ | ||
| 163 | |||
| 164 | #define IPC_BUILD_HEADER(length, protocol, busy) \ | ||
| 165 | (((busy)<<IPC_DRBL_BUSY_OFFS) | \ | ||
| 166 | ((protocol) << IPC_HEADER_PROTOCOL_OFFSET) | \ | ||
| 167 | ((length)<<IPC_HEADER_LENGTH_OFFSET)) | ||
| 168 | |||
| 169 | #define IPC_BUILD_MNG_MSG(cmd, length) \ | ||
| 170 | (((1)<<IPC_DRBL_BUSY_OFFS)| \ | ||
| 171 | ((IPC_PROTOCOL_MNG)<<IPC_HEADER_PROTOCOL_OFFSET)| \ | ||
| 172 | ((cmd)<<IPC_HEADER_MNG_CMD_OFFSET)| \ | ||
| 173 | ((length)<<IPC_HEADER_LENGTH_OFFSET)) | ||
| 174 | |||
| 175 | |||
| 176 | #define IPC_SET_HOST_READY(host_status) \ | ||
| 177 | ((host_status) |= (IPC_HOSTCOMM_READY_BIT)) | ||
| 178 | |||
| 179 | #define IPC_SET_HOST_ILUP(host_status) \ | ||
| 180 | ((host_status) |= (IPC_ILUP_BIT)) | ||
| 181 | |||
| 182 | #define IPC_CLEAR_HOST_READY(host_status) \ | ||
| 183 | ((host_status) ^= (IPC_HOSTCOMM_READY_BIT)) | ||
| 184 | |||
| 185 | #define IPC_CLEAR_HOST_ILUP(host_status) \ | ||
| 186 | ((host_status) ^= (IPC_ILUP_BIT)) | ||
| 187 | |||
| 188 | /* todo - temp until PIMR HW ready */ | ||
| 189 | #define IPC_HOST_BUSY_READING_OFFS 6 | ||
| 190 | |||
| 191 | /* bit corresponds host ready bit in Host Status Register (HOST_COMM) */ | ||
| 192 | #define IPC_HOST_BUSY_READING_BIT (1<<IPC_HOST_BUSY_READING_OFFS) | ||
| 193 | |||
| 194 | #define IPC_SET_HOST_BUSY_READING(host_status) \ | ||
| 195 | ((host_status) |= (IPC_HOST_BUSY_READING_BIT)) | ||
| 196 | |||
| 197 | #define IPC_CLEAR_HOST_BUSY_READING(host_status)\ | ||
| 198 | ((host_status) ^= (IPC_HOST_BUSY_READING_BIT)) | ||
| 199 | |||
| 200 | |||
| 201 | #define IPC_IS_ISH_ISHTP_READY(ish_status) \ | ||
| 202 | (((ish_status) & IPC_ISH_ISHTP_READY_BIT) == \ | ||
| 203 | ((uint32_t)IPC_ISH_ISHTP_READY_BIT)) | ||
| 204 | |||
| 205 | #define IPC_IS_ISH_ILUP(ish_status) \ | ||
| 206 | (((ish_status) & IPC_ILUP_BIT) == ((uint32_t)IPC_ILUP_BIT)) | ||
| 207 | |||
| 208 | |||
| 209 | #define IPC_PROTOCOL_ISHTP 1 | ||
| 210 | #define IPC_PROTOCOL_MNG 3 | ||
| 211 | |||
| 212 | #define MNG_RX_CMPL_ENABLE 0 | ||
| 213 | #define MNG_RX_CMPL_DISABLE 1 | ||
| 214 | #define MNG_RX_CMPL_INDICATION 2 | ||
| 215 | #define MNG_RESET_NOTIFY 3 | ||
| 216 | #define MNG_RESET_NOTIFY_ACK 4 | ||
| 217 | #define MNG_SYNC_FW_CLOCK 5 | ||
| 218 | #define MNG_ILLEGAL_CMD 0xFF | ||
| 219 | |||
| 220 | #endif /* _ISHTP_ISH_REGS_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h new file mode 100644 index 000000000000..46615a03e78f --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | /* | ||
| 2 | * H/W layer of ISHTP provider device (ISH) | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef _ISHTP_HW_ISH_H_ | ||
| 17 | #define _ISHTP_HW_ISH_H_ | ||
| 18 | |||
| 19 | #include <linux/pci.h> | ||
| 20 | #include <linux/interrupt.h> | ||
| 21 | #include "hw-ish-regs.h" | ||
| 22 | #include "ishtp-dev.h" | ||
| 23 | |||
| 24 | #define CHV_DEVICE_ID 0x22D8 | ||
| 25 | #define BXT_Ax_DEVICE_ID 0x0AA2 | ||
| 26 | #define BXT_Bx_DEVICE_ID 0x1AA2 | ||
| 27 | #define APL_Ax_DEVICE_ID 0x5AA2 | ||
| 28 | #define SPT_Ax_DEVICE_ID 0x9D35 | ||
| 29 | |||
| 30 | #define REVISION_ID_CHT_A0 0x6 | ||
| 31 | #define REVISION_ID_CHT_Ax_SI 0x0 | ||
| 32 | #define REVISION_ID_CHT_Bx_SI 0x10 | ||
| 33 | #define REVISION_ID_CHT_Kx_SI 0x20 | ||
| 34 | #define REVISION_ID_CHT_Dx_SI 0x30 | ||
| 35 | #define REVISION_ID_CHT_B0 0xB0 | ||
| 36 | #define REVISION_ID_SI_MASK 0x70 | ||
| 37 | |||
| 38 | struct ipc_rst_payload_type { | ||
| 39 | uint16_t reset_id; | ||
| 40 | uint16_t reserved; | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct time_sync_format { | ||
| 44 | uint8_t ts1_source; | ||
| 45 | uint8_t ts2_source; | ||
| 46 | uint16_t reserved; | ||
| 47 | } __packed; | ||
| 48 | |||
| 49 | struct ipc_time_update_msg { | ||
| 50 | uint64_t primary_host_time; | ||
| 51 | struct time_sync_format sync_info; | ||
| 52 | uint64_t secondary_host_time; | ||
| 53 | } __packed; | ||
| 54 | |||
| 55 | enum { | ||
| 56 | HOST_UTC_TIME_USEC = 0, | ||
| 57 | HOST_SYSTEM_TIME_USEC = 1 | ||
| 58 | }; | ||
| 59 | |||
| 60 | struct ish_hw { | ||
| 61 | void __iomem *mem_addr; | ||
| 62 | }; | ||
| 63 | |||
| 64 | #define to_ish_hw(dev) (struct ish_hw *)((dev)->hw) | ||
| 65 | |||
| 66 | irqreturn_t ish_irq_handler(int irq, void *dev_id); | ||
| 67 | struct ishtp_device *ish_dev_init(struct pci_dev *pdev); | ||
| 68 | int ish_hw_start(struct ishtp_device *dev); | ||
| 69 | void ish_device_disable(struct ishtp_device *dev); | ||
| 70 | |||
| 71 | #endif /* _ISHTP_HW_ISH_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c new file mode 100644 index 000000000000..e2517c11e0ee --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c | |||
| @@ -0,0 +1,881 @@ | |||
| 1 | /* | ||
| 2 | * H/W layer of ISHTP provider device (ISH) | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/sched.h> | ||
| 17 | #include <linux/spinlock.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/jiffies.h> | ||
| 20 | #include "client.h" | ||
| 21 | #include "hw-ish.h" | ||
| 22 | #include "utils.h" | ||
| 23 | #include "hbm.h" | ||
| 24 | |||
| 25 | /* For FW reset flow */ | ||
| 26 | static struct work_struct fw_reset_work; | ||
| 27 | static struct ishtp_device *ishtp_dev; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * ish_reg_read() - Read register | ||
| 31 | * @dev: ISHTP device pointer | ||
| 32 | * @offset: Register offset | ||
| 33 | * | ||
| 34 | * Read 32 bit register at a given offset | ||
| 35 | * | ||
| 36 | * Return: Read register value | ||
| 37 | */ | ||
| 38 | static inline uint32_t ish_reg_read(const struct ishtp_device *dev, | ||
| 39 | unsigned long offset) | ||
| 40 | { | ||
| 41 | struct ish_hw *hw = to_ish_hw(dev); | ||
| 42 | |||
| 43 | return readl(hw->mem_addr + offset); | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * ish_reg_write() - Write register | ||
| 48 | * @dev: ISHTP device pointer | ||
| 49 | * @offset: Register offset | ||
| 50 | * @value: Value to write | ||
| 51 | * | ||
| 52 | * Writes 32 bit register at a give offset | ||
| 53 | */ | ||
| 54 | static inline void ish_reg_write(struct ishtp_device *dev, | ||
| 55 | unsigned long offset, | ||
| 56 | uint32_t value) | ||
| 57 | { | ||
| 58 | struct ish_hw *hw = to_ish_hw(dev); | ||
| 59 | |||
| 60 | writel(value, hw->mem_addr + offset); | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * _ish_read_fw_sts_reg() - Read FW status register | ||
| 65 | * @dev: ISHTP device pointer | ||
| 66 | * | ||
| 67 | * Read FW status register | ||
| 68 | * | ||
| 69 | * Return: Read register value | ||
| 70 | */ | ||
| 71 | static inline uint32_t _ish_read_fw_sts_reg(struct ishtp_device *dev) | ||
| 72 | { | ||
| 73 | return ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * check_generated_interrupt() - Check if ISH interrupt | ||
| 78 | * @dev: ISHTP device pointer | ||
| 79 | * | ||
| 80 | * Check if an interrupt was generated for ISH | ||
| 81 | * | ||
| 82 | * Return: Read true or false | ||
| 83 | */ | ||
| 84 | static bool check_generated_interrupt(struct ishtp_device *dev) | ||
| 85 | { | ||
| 86 | bool interrupt_generated = true; | ||
| 87 | uint32_t pisr_val = 0; | ||
| 88 | |||
| 89 | if (dev->pdev->device == CHV_DEVICE_ID) { | ||
| 90 | pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB); | ||
| 91 | interrupt_generated = | ||
| 92 | IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val); | ||
| 93 | } else { | ||
| 94 | pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT); | ||
| 95 | interrupt_generated = IPC_INT_FROM_ISH_TO_HOST_BXT(pisr_val); | ||
| 96 | } | ||
| 97 | |||
| 98 | return interrupt_generated; | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * ish_is_input_ready() - Check if FW ready for RX | ||
| 103 | * @dev: ISHTP device pointer | ||
| 104 | * | ||
| 105 | * Check if ISH FW is ready for receiving data | ||
| 106 | * | ||
| 107 | * Return: Read true or false | ||
| 108 | */ | ||
| 109 | static bool ish_is_input_ready(struct ishtp_device *dev) | ||
| 110 | { | ||
| 111 | uint32_t doorbell_val; | ||
| 112 | |||
| 113 | doorbell_val = ish_reg_read(dev, IPC_REG_HOST2ISH_DRBL); | ||
| 114 | return !IPC_IS_BUSY(doorbell_val); | ||
| 115 | } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * set_host_ready() - Indicate host ready | ||
| 119 | * @dev: ISHTP device pointer | ||
| 120 | * | ||
| 121 | * Set host ready indication to FW | ||
| 122 | */ | ||
| 123 | static void set_host_ready(struct ishtp_device *dev) | ||
| 124 | { | ||
| 125 | if (dev->pdev->device == CHV_DEVICE_ID) { | ||
| 126 | if (dev->pdev->revision == REVISION_ID_CHT_A0 || | ||
| 127 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
| 128 | REVISION_ID_CHT_Ax_SI) | ||
| 129 | ish_reg_write(dev, IPC_REG_HOST_COMM, 0x81); | ||
| 130 | else if (dev->pdev->revision == REVISION_ID_CHT_B0 || | ||
| 131 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
| 132 | REVISION_ID_CHT_Bx_SI || | ||
| 133 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
| 134 | REVISION_ID_CHT_Kx_SI || | ||
| 135 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
| 136 | REVISION_ID_CHT_Dx_SI) { | ||
| 137 | uint32_t host_comm_val; | ||
| 138 | |||
| 139 | host_comm_val = ish_reg_read(dev, IPC_REG_HOST_COMM); | ||
| 140 | host_comm_val |= IPC_HOSTCOMM_INT_EN_BIT_CHV_AB | 0x81; | ||
| 141 | ish_reg_write(dev, IPC_REG_HOST_COMM, host_comm_val); | ||
| 142 | } | ||
| 143 | } else { | ||
| 144 | uint32_t host_pimr_val; | ||
| 145 | |||
| 146 | host_pimr_val = ish_reg_read(dev, IPC_REG_PIMR_BXT); | ||
| 147 | host_pimr_val |= IPC_PIMR_INT_EN_BIT_BXT; | ||
| 148 | /* | ||
| 149 | * disable interrupt generated instead of | ||
| 150 | * RX_complete_msg | ||
| 151 | */ | ||
| 152 | host_pimr_val &= ~IPC_HOST2ISH_BUSYCLEAR_MASK_BIT; | ||
| 153 | |||
| 154 | ish_reg_write(dev, IPC_REG_PIMR_BXT, host_pimr_val); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /** | ||
| 159 | * ishtp_fw_is_ready() - Check if FW ready | ||
| 160 | * @dev: ISHTP device pointer | ||
| 161 | * | ||
| 162 | * Check if ISH FW is ready | ||
| 163 | * | ||
| 164 | * Return: Read true or false | ||
| 165 | */ | ||
| 166 | static bool ishtp_fw_is_ready(struct ishtp_device *dev) | ||
| 167 | { | ||
| 168 | uint32_t ish_status = _ish_read_fw_sts_reg(dev); | ||
| 169 | |||
| 170 | return IPC_IS_ISH_ILUP(ish_status) && | ||
| 171 | IPC_IS_ISH_ISHTP_READY(ish_status); | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * ish_set_host_rdy() - Indicate host ready | ||
| 176 | * @dev: ISHTP device pointer | ||
| 177 | * | ||
| 178 | * Set host ready indication to FW | ||
| 179 | */ | ||
| 180 | static void ish_set_host_rdy(struct ishtp_device *dev) | ||
| 181 | { | ||
| 182 | uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM); | ||
| 183 | |||
| 184 | IPC_SET_HOST_READY(host_status); | ||
| 185 | ish_reg_write(dev, IPC_REG_HOST_COMM, host_status); | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * ish_clr_host_rdy() - Indicate host not ready | ||
| 190 | * @dev: ISHTP device pointer | ||
| 191 | * | ||
| 192 | * Send host not ready indication to FW | ||
| 193 | */ | ||
| 194 | static void ish_clr_host_rdy(struct ishtp_device *dev) | ||
| 195 | { | ||
| 196 | uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM); | ||
| 197 | |||
| 198 | IPC_CLEAR_HOST_READY(host_status); | ||
| 199 | ish_reg_write(dev, IPC_REG_HOST_COMM, host_status); | ||
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * _ishtp_read_hdr() - Read message header | ||
| 204 | * @dev: ISHTP device pointer | ||
| 205 | * | ||
| 206 | * Read header of 32bit length | ||
| 207 | * | ||
| 208 | * Return: Read register value | ||
| 209 | */ | ||
| 210 | static uint32_t _ishtp_read_hdr(const struct ishtp_device *dev) | ||
| 211 | { | ||
| 212 | return ish_reg_read(dev, IPC_REG_ISH2HOST_MSG); | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * _ishtp_read - Read message | ||
| 217 | * @dev: ISHTP device pointer | ||
| 218 | * @buffer: message buffer | ||
| 219 | * @buffer_length: length of message buffer | ||
| 220 | * | ||
| 221 | * Read message from FW | ||
| 222 | * | ||
| 223 | * Return: Always 0 | ||
| 224 | */ | ||
| 225 | static int _ishtp_read(struct ishtp_device *dev, unsigned char *buffer, | ||
| 226 | unsigned long buffer_length) | ||
| 227 | { | ||
| 228 | uint32_t i; | ||
| 229 | uint32_t *r_buf = (uint32_t *)buffer; | ||
| 230 | uint32_t msg_offs; | ||
| 231 | |||
| 232 | msg_offs = IPC_REG_ISH2HOST_MSG + sizeof(struct ishtp_msg_hdr); | ||
| 233 | for (i = 0; i < buffer_length; i += sizeof(uint32_t)) | ||
| 234 | *r_buf++ = ish_reg_read(dev, msg_offs + i); | ||
| 235 | |||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | /** | ||
| 240 | * write_ipc_from_queue() - try to write ipc msg from Tx queue to device | ||
| 241 | * @dev: ishtp device pointer | ||
| 242 | * | ||
| 243 | * Check if DRBL is cleared. if it is - write the first IPC msg, then call | ||
| 244 | * the callback function (unless it's NULL) | ||
| 245 | * | ||
| 246 | * Return: 0 for success else failure code | ||
| 247 | */ | ||
| 248 | static int write_ipc_from_queue(struct ishtp_device *dev) | ||
| 249 | { | ||
| 250 | struct wr_msg_ctl_info *ipc_link; | ||
| 251 | unsigned long length; | ||
| 252 | unsigned long rem; | ||
| 253 | unsigned long flags; | ||
| 254 | uint32_t doorbell_val; | ||
| 255 | uint32_t *r_buf; | ||
| 256 | uint32_t reg_addr; | ||
| 257 | int i; | ||
| 258 | void (*ipc_send_compl)(void *); | ||
| 259 | void *ipc_send_compl_prm; | ||
| 260 | static int out_ipc_locked; | ||
| 261 | unsigned long out_ipc_flags; | ||
| 262 | |||
| 263 | if (dev->dev_state == ISHTP_DEV_DISABLED) | ||
| 264 | return -EINVAL; | ||
| 265 | |||
| 266 | spin_lock_irqsave(&dev->out_ipc_spinlock, out_ipc_flags); | ||
| 267 | if (out_ipc_locked) { | ||
| 268 | spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); | ||
| 269 | return -EBUSY; | ||
| 270 | } | ||
| 271 | out_ipc_locked = 1; | ||
| 272 | if (!ish_is_input_ready(dev)) { | ||
| 273 | out_ipc_locked = 0; | ||
| 274 | spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); | ||
| 275 | return -EBUSY; | ||
| 276 | } | ||
| 277 | spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); | ||
| 278 | |||
| 279 | spin_lock_irqsave(&dev->wr_processing_spinlock, flags); | ||
| 280 | /* | ||
| 281 | * if tx send list is empty - return 0; | ||
| 282 | * may happen, as RX_COMPLETE handler doesn't check list emptiness. | ||
| 283 | */ | ||
| 284 | if (list_empty(&dev->wr_processing_list_head.link)) { | ||
| 285 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
| 286 | out_ipc_locked = 0; | ||
| 287 | return 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | ipc_link = list_entry(dev->wr_processing_list_head.link.next, | ||
| 291 | struct wr_msg_ctl_info, link); | ||
| 292 | /* first 4 bytes of the data is the doorbell value (IPC header) */ | ||
| 293 | length = ipc_link->length - sizeof(uint32_t); | ||
| 294 | doorbell_val = *(uint32_t *)ipc_link->inline_data; | ||
| 295 | r_buf = (uint32_t *)(ipc_link->inline_data + sizeof(uint32_t)); | ||
| 296 | |||
| 297 | /* If sending MNG_SYNC_FW_CLOCK, update clock again */ | ||
| 298 | if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG && | ||
| 299 | IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) { | ||
| 300 | struct timespec ts_system; | ||
| 301 | struct timeval tv_utc; | ||
| 302 | uint64_t usec_system, usec_utc; | ||
| 303 | struct ipc_time_update_msg time_update; | ||
| 304 | struct time_sync_format ts_format; | ||
| 305 | |||
| 306 | get_monotonic_boottime(&ts_system); | ||
| 307 | do_gettimeofday(&tv_utc); | ||
| 308 | usec_system = (timespec_to_ns(&ts_system)) / NSEC_PER_USEC; | ||
| 309 | usec_utc = (uint64_t)tv_utc.tv_sec * 1000000 + | ||
| 310 | ((uint32_t)tv_utc.tv_usec); | ||
| 311 | ts_format.ts1_source = HOST_SYSTEM_TIME_USEC; | ||
| 312 | ts_format.ts2_source = HOST_UTC_TIME_USEC; | ||
| 313 | |||
| 314 | time_update.primary_host_time = usec_system; | ||
| 315 | time_update.secondary_host_time = usec_utc; | ||
| 316 | time_update.sync_info = ts_format; | ||
| 317 | |||
| 318 | memcpy(r_buf, &time_update, | ||
| 319 | sizeof(struct ipc_time_update_msg)); | ||
| 320 | } | ||
| 321 | |||
| 322 | for (i = 0, reg_addr = IPC_REG_HOST2ISH_MSG; i < length >> 2; i++, | ||
| 323 | reg_addr += 4) | ||
| 324 | ish_reg_write(dev, reg_addr, r_buf[i]); | ||
| 325 | |||
| 326 | rem = length & 0x3; | ||
| 327 | if (rem > 0) { | ||
| 328 | uint32_t reg = 0; | ||
| 329 | |||
| 330 | memcpy(®, &r_buf[length >> 2], rem); | ||
| 331 | ish_reg_write(dev, reg_addr, reg); | ||
| 332 | } | ||
| 333 | /* Flush writes to msg registers and doorbell */ | ||
| 334 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
| 335 | |||
| 336 | /* Update IPC counters */ | ||
| 337 | ++dev->ipc_tx_cnt; | ||
| 338 | dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); | ||
| 339 | |||
| 340 | ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val); | ||
| 341 | out_ipc_locked = 0; | ||
| 342 | |||
| 343 | ipc_send_compl = ipc_link->ipc_send_compl; | ||
| 344 | ipc_send_compl_prm = ipc_link->ipc_send_compl_prm; | ||
| 345 | list_del_init(&ipc_link->link); | ||
| 346 | list_add_tail(&ipc_link->link, &dev->wr_free_list_head.link); | ||
| 347 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
| 348 | |||
| 349 | /* | ||
| 350 | * callback will be called out of spinlock, | ||
| 351 | * after ipc_link returned to free list | ||
| 352 | */ | ||
| 353 | if (ipc_send_compl) | ||
| 354 | ipc_send_compl(ipc_send_compl_prm); | ||
| 355 | |||
| 356 | return 0; | ||
| 357 | } | ||
| 358 | |||
| 359 | /** | ||
| 360 | * write_ipc_to_queue() - write ipc msg to Tx queue | ||
| 361 | * @dev: ishtp device instance | ||
| 362 | * @ipc_send_compl: Send complete callback | ||
| 363 | * @ipc_send_compl_prm: Parameter to send in complete callback | ||
| 364 | * @msg: Pointer to message | ||
| 365 | * @length: Length of message | ||
| 366 | * | ||
| 367 | * Recived msg with IPC (and upper protocol) header and add it to the device | ||
| 368 | * Tx-to-write list then try to send the first IPC waiting msg | ||
| 369 | * (if DRBL is cleared) | ||
| 370 | * This function returns negative value for failure (means free list | ||
| 371 | * is empty, or msg too long) and 0 for success. | ||
| 372 | * | ||
| 373 | * Return: 0 for success else failure code | ||
| 374 | */ | ||
| 375 | static int write_ipc_to_queue(struct ishtp_device *dev, | ||
| 376 | void (*ipc_send_compl)(void *), void *ipc_send_compl_prm, | ||
| 377 | unsigned char *msg, int length) | ||
| 378 | { | ||
| 379 | struct wr_msg_ctl_info *ipc_link; | ||
| 380 | unsigned long flags; | ||
| 381 | |||
| 382 | if (length > IPC_FULL_MSG_SIZE) | ||
| 383 | return -EMSGSIZE; | ||
| 384 | |||
| 385 | spin_lock_irqsave(&dev->wr_processing_spinlock, flags); | ||
| 386 | if (list_empty(&dev->wr_free_list_head.link)) { | ||
| 387 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
| 388 | return -ENOMEM; | ||
| 389 | } | ||
| 390 | ipc_link = list_entry(dev->wr_free_list_head.link.next, | ||
| 391 | struct wr_msg_ctl_info, link); | ||
| 392 | list_del_init(&ipc_link->link); | ||
| 393 | |||
| 394 | ipc_link->ipc_send_compl = ipc_send_compl; | ||
| 395 | ipc_link->ipc_send_compl_prm = ipc_send_compl_prm; | ||
| 396 | ipc_link->length = length; | ||
| 397 | memcpy(ipc_link->inline_data, msg, length); | ||
| 398 | |||
| 399 | list_add_tail(&ipc_link->link, &dev->wr_processing_list_head.link); | ||
| 400 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
| 401 | |||
| 402 | write_ipc_from_queue(dev); | ||
| 403 | |||
| 404 | return 0; | ||
| 405 | } | ||
| 406 | |||
| 407 | /** | ||
| 408 | * ipc_send_mng_msg() - Send management message | ||
| 409 | * @dev: ishtp device instance | ||
| 410 | * @msg_code: Message code | ||
| 411 | * @msg: Pointer to message | ||
| 412 | * @size: Length of message | ||
| 413 | * | ||
| 414 | * Send management message to FW | ||
| 415 | * | ||
| 416 | * Return: 0 for success else failure code | ||
| 417 | */ | ||
| 418 | static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code, | ||
| 419 | void *msg, size_t size) | ||
| 420 | { | ||
| 421 | unsigned char ipc_msg[IPC_FULL_MSG_SIZE]; | ||
| 422 | uint32_t drbl_val = IPC_BUILD_MNG_MSG(msg_code, size); | ||
| 423 | |||
| 424 | memcpy(ipc_msg, &drbl_val, sizeof(uint32_t)); | ||
| 425 | memcpy(ipc_msg + sizeof(uint32_t), msg, size); | ||
| 426 | return write_ipc_to_queue(dev, NULL, NULL, ipc_msg, | ||
| 427 | sizeof(uint32_t) + size); | ||
| 428 | } | ||
| 429 | |||
| 430 | /** | ||
| 431 | * ish_fw_reset_handler() - FW reset handler | ||
| 432 | * @dev: ishtp device pointer | ||
| 433 | * | ||
| 434 | * Handle FW reset | ||
| 435 | * | ||
| 436 | * Return: 0 for success else failure code | ||
| 437 | */ | ||
| 438 | static int ish_fw_reset_handler(struct ishtp_device *dev) | ||
| 439 | { | ||
| 440 | uint32_t reset_id; | ||
| 441 | unsigned long flags; | ||
| 442 | struct wr_msg_ctl_info *processing, *next; | ||
| 443 | |||
| 444 | /* Read reset ID */ | ||
| 445 | reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF; | ||
| 446 | |||
| 447 | /* Clear IPC output queue */ | ||
| 448 | spin_lock_irqsave(&dev->wr_processing_spinlock, flags); | ||
| 449 | list_for_each_entry_safe(processing, next, | ||
| 450 | &dev->wr_processing_list_head.link, link) { | ||
| 451 | list_move_tail(&processing->link, &dev->wr_free_list_head.link); | ||
| 452 | } | ||
| 453 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
| 454 | |||
| 455 | /* ISHTP notification in IPC_RESET */ | ||
| 456 | ishtp_reset_handler(dev); | ||
| 457 | |||
| 458 | if (!ish_is_input_ready(dev)) | ||
| 459 | timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, | ||
| 460 | ish_is_input_ready(dev), (2 * HZ)); | ||
| 461 | |||
| 462 | /* ISH FW is dead */ | ||
| 463 | if (!ish_is_input_ready(dev)) | ||
| 464 | return -EPIPE; | ||
| 465 | /* | ||
| 466 | * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending | ||
| 467 | * RESET_NOTIFY_ACK - FW will be checking for it | ||
| 468 | */ | ||
| 469 | ish_set_host_rdy(dev); | ||
| 470 | /* Send RESET_NOTIFY_ACK (with reset_id) */ | ||
| 471 | ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id, | ||
| 472 | sizeof(uint32_t)); | ||
| 473 | |||
| 474 | /* Wait for ISH FW'es ILUP and ISHTP_READY */ | ||
| 475 | timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, ishtp_fw_is_ready(dev), | ||
| 476 | (2 * HZ)); | ||
| 477 | if (!ishtp_fw_is_ready(dev)) { | ||
| 478 | /* ISH FW is dead */ | ||
| 479 | uint32_t ish_status; | ||
| 480 | |||
| 481 | ish_status = _ish_read_fw_sts_reg(dev); | ||
| 482 | dev_err(dev->devc, | ||
| 483 | "[ishtp-ish]: completed reset, ISH is dead (FWSTS = %08X)\n", | ||
| 484 | ish_status); | ||
| 485 | return -ENODEV; | ||
| 486 | } | ||
| 487 | return 0; | ||
| 488 | } | ||
| 489 | |||
| 490 | /** | ||
| 491 | * ish_fw_reset_work_fn() - FW reset worker function | ||
| 492 | * @unused: not used | ||
| 493 | * | ||
| 494 | * Call ish_fw_reset_handler to complete FW reset | ||
| 495 | */ | ||
| 496 | static void fw_reset_work_fn(struct work_struct *unused) | ||
| 497 | { | ||
| 498 | int rv; | ||
| 499 | |||
| 500 | rv = ish_fw_reset_handler(ishtp_dev); | ||
| 501 | if (!rv) { | ||
| 502 | /* ISH is ILUP & ISHTP-ready. Restart ISHTP */ | ||
| 503 | schedule_timeout(HZ / 3); | ||
| 504 | ishtp_dev->recvd_hw_ready = 1; | ||
| 505 | wake_up_interruptible(&ishtp_dev->wait_hw_ready); | ||
| 506 | |||
| 507 | /* ISHTP notification in IPC_RESET sequence completion */ | ||
| 508 | ishtp_reset_compl_handler(ishtp_dev); | ||
| 509 | } else | ||
| 510 | dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n", | ||
| 511 | rv); | ||
| 512 | } | ||
| 513 | |||
| 514 | /** | ||
| 515 | * _ish_sync_fw_clock() -Sync FW clock with the OS clock | ||
| 516 | * @dev: ishtp device pointer | ||
| 517 | * | ||
| 518 | * Sync FW and OS time | ||
| 519 | */ | ||
| 520 | static void _ish_sync_fw_clock(struct ishtp_device *dev) | ||
| 521 | { | ||
| 522 | static unsigned long prev_sync; | ||
| 523 | struct timespec ts; | ||
| 524 | uint64_t usec; | ||
| 525 | |||
| 526 | if (prev_sync && jiffies - prev_sync < 20 * HZ) | ||
| 527 | return; | ||
| 528 | |||
| 529 | prev_sync = jiffies; | ||
| 530 | get_monotonic_boottime(&ts); | ||
| 531 | usec = (timespec_to_ns(&ts)) / NSEC_PER_USEC; | ||
| 532 | ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t)); | ||
| 533 | } | ||
| 534 | |||
| 535 | /** | ||
| 536 | * recv_ipc() - Receive and process IPC management messages | ||
| 537 | * @dev: ishtp device instance | ||
| 538 | * @doorbell_val: doorbell value | ||
| 539 | * | ||
| 540 | * This function runs in ISR context. | ||
| 541 | * NOTE: Any other mng command than reset_notify and reset_notify_ack | ||
| 542 | * won't wake BH handler | ||
| 543 | */ | ||
| 544 | static void recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val) | ||
| 545 | { | ||
| 546 | uint32_t mng_cmd; | ||
| 547 | |||
| 548 | mng_cmd = IPC_HEADER_GET_MNG_CMD(doorbell_val); | ||
| 549 | |||
| 550 | switch (mng_cmd) { | ||
| 551 | default: | ||
| 552 | break; | ||
| 553 | |||
| 554 | case MNG_RX_CMPL_INDICATION: | ||
| 555 | if (dev->suspend_flag) { | ||
| 556 | dev->suspend_flag = 0; | ||
| 557 | wake_up_interruptible(&dev->suspend_wait); | ||
| 558 | } | ||
| 559 | if (dev->resume_flag) { | ||
| 560 | dev->resume_flag = 0; | ||
| 561 | wake_up_interruptible(&dev->resume_wait); | ||
| 562 | } | ||
| 563 | |||
| 564 | write_ipc_from_queue(dev); | ||
| 565 | break; | ||
| 566 | |||
| 567 | case MNG_RESET_NOTIFY: | ||
| 568 | if (!ishtp_dev) { | ||
| 569 | ishtp_dev = dev; | ||
| 570 | INIT_WORK(&fw_reset_work, fw_reset_work_fn); | ||
| 571 | } | ||
| 572 | schedule_work(&fw_reset_work); | ||
| 573 | break; | ||
| 574 | |||
| 575 | case MNG_RESET_NOTIFY_ACK: | ||
| 576 | dev->recvd_hw_ready = 1; | ||
| 577 | wake_up_interruptible(&dev->wait_hw_ready); | ||
| 578 | break; | ||
| 579 | } | ||
| 580 | } | ||
| 581 | |||
| 582 | /** | ||
| 583 | * ish_irq_handler() - ISH IRQ handler | ||
| 584 | * @irq: irq number | ||
| 585 | * @dev_id: ishtp device pointer | ||
| 586 | * | ||
| 587 | * ISH IRQ handler. If interrupt is generated and is for ISH it will process | ||
| 588 | * the interrupt. | ||
| 589 | */ | ||
| 590 | irqreturn_t ish_irq_handler(int irq, void *dev_id) | ||
| 591 | { | ||
| 592 | struct ishtp_device *dev = dev_id; | ||
| 593 | uint32_t doorbell_val; | ||
| 594 | bool interrupt_generated; | ||
| 595 | |||
| 596 | /* Check that it's interrupt from ISH (may be shared) */ | ||
| 597 | interrupt_generated = check_generated_interrupt(dev); | ||
| 598 | |||
| 599 | if (!interrupt_generated) | ||
| 600 | return IRQ_NONE; | ||
| 601 | |||
| 602 | doorbell_val = ish_reg_read(dev, IPC_REG_ISH2HOST_DRBL); | ||
| 603 | if (!IPC_IS_BUSY(doorbell_val)) | ||
| 604 | return IRQ_HANDLED; | ||
| 605 | |||
| 606 | if (dev->dev_state == ISHTP_DEV_DISABLED) | ||
| 607 | return IRQ_HANDLED; | ||
| 608 | |||
| 609 | /* Sanity check: IPC dgram length in header */ | ||
| 610 | if (IPC_HEADER_GET_LENGTH(doorbell_val) > IPC_PAYLOAD_SIZE) { | ||
| 611 | dev_err(dev->devc, | ||
| 612 | "IPC hdr - bad length: %u; dropped\n", | ||
| 613 | (unsigned int)IPC_HEADER_GET_LENGTH(doorbell_val)); | ||
| 614 | goto eoi; | ||
| 615 | } | ||
| 616 | |||
| 617 | switch (IPC_HEADER_GET_PROTOCOL(doorbell_val)) { | ||
| 618 | default: | ||
| 619 | break; | ||
| 620 | case IPC_PROTOCOL_MNG: | ||
| 621 | recv_ipc(dev, doorbell_val); | ||
| 622 | break; | ||
| 623 | case IPC_PROTOCOL_ISHTP: | ||
| 624 | ishtp_recv(dev); | ||
| 625 | break; | ||
| 626 | } | ||
| 627 | |||
| 628 | eoi: | ||
| 629 | /* Update IPC counters */ | ||
| 630 | ++dev->ipc_rx_cnt; | ||
| 631 | dev->ipc_rx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); | ||
| 632 | |||
| 633 | ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0); | ||
| 634 | /* Flush write to doorbell */ | ||
| 635 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
| 636 | |||
| 637 | return IRQ_HANDLED; | ||
| 638 | } | ||
| 639 | |||
| 640 | /** | ||
| 641 | * _ish_hw_reset() - HW reset | ||
| 642 | * @dev: ishtp device pointer | ||
| 643 | * | ||
| 644 | * Reset ISH HW to recover if any error | ||
| 645 | * | ||
| 646 | * Return: 0 for success else error fault code | ||
| 647 | */ | ||
| 648 | static int _ish_hw_reset(struct ishtp_device *dev) | ||
| 649 | { | ||
| 650 | struct pci_dev *pdev = dev->pdev; | ||
| 651 | int rv; | ||
| 652 | unsigned int dma_delay; | ||
| 653 | uint16_t csr; | ||
| 654 | |||
| 655 | if (!pdev) | ||
| 656 | return -ENODEV; | ||
| 657 | |||
| 658 | rv = pci_reset_function(pdev); | ||
| 659 | if (!rv) | ||
| 660 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
| 661 | |||
| 662 | if (!pdev->pm_cap) { | ||
| 663 | dev_err(&pdev->dev, "Can't reset - no PM caps\n"); | ||
| 664 | return -EINVAL; | ||
| 665 | } | ||
| 666 | |||
| 667 | /* Now trigger reset to FW */ | ||
| 668 | ish_reg_write(dev, IPC_REG_ISH_RMP2, 0); | ||
| 669 | |||
| 670 | for (dma_delay = 0; dma_delay < MAX_DMA_DELAY && | ||
| 671 | _ish_read_fw_sts_reg(dev) & (IPC_ISH_IN_DMA); | ||
| 672 | dma_delay += 5) | ||
| 673 | mdelay(5); | ||
| 674 | |||
| 675 | if (dma_delay >= MAX_DMA_DELAY) { | ||
| 676 | dev_err(&pdev->dev, | ||
| 677 | "Can't reset - stuck with DMA in-progress\n"); | ||
| 678 | return -EBUSY; | ||
| 679 | } | ||
| 680 | |||
| 681 | pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &csr); | ||
| 682 | |||
| 683 | csr &= ~PCI_PM_CTRL_STATE_MASK; | ||
| 684 | csr |= PCI_D3hot; | ||
| 685 | pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr); | ||
| 686 | |||
| 687 | mdelay(pdev->d3_delay); | ||
| 688 | |||
| 689 | csr &= ~PCI_PM_CTRL_STATE_MASK; | ||
| 690 | csr |= PCI_D0; | ||
| 691 | pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr); | ||
| 692 | |||
| 693 | ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); | ||
| 694 | |||
| 695 | /* | ||
| 696 | * Send 0 IPC message so that ISH FW wakes up if it was already | ||
| 697 | * asleep | ||
| 698 | */ | ||
| 699 | ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); | ||
| 700 | |||
| 701 | /* Flush writes to doorbell and REMAP2 */ | ||
| 702 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
| 703 | |||
| 704 | return 0; | ||
| 705 | } | ||
| 706 | |||
| 707 | /** | ||
| 708 | * _ish_ipc_reset() - IPC reset | ||
| 709 | * @dev: ishtp device pointer | ||
| 710 | * | ||
| 711 | * Resets host and fw IPC and upper layers | ||
| 712 | * | ||
| 713 | * Return: 0 for success else error fault code | ||
| 714 | */ | ||
| 715 | static int _ish_ipc_reset(struct ishtp_device *dev) | ||
| 716 | { | ||
| 717 | struct ipc_rst_payload_type ipc_mng_msg; | ||
| 718 | int rv = 0; | ||
| 719 | |||
| 720 | ipc_mng_msg.reset_id = 1; | ||
| 721 | ipc_mng_msg.reserved = 0; | ||
| 722 | |||
| 723 | set_host_ready(dev); | ||
| 724 | |||
| 725 | /* Clear the incoming doorbell */ | ||
| 726 | ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0); | ||
| 727 | /* Flush write to doorbell */ | ||
| 728 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
| 729 | |||
| 730 | dev->recvd_hw_ready = 0; | ||
| 731 | |||
| 732 | /* send message */ | ||
| 733 | rv = ipc_send_mng_msg(dev, MNG_RESET_NOTIFY, &ipc_mng_msg, | ||
| 734 | sizeof(struct ipc_rst_payload_type)); | ||
| 735 | if (rv) { | ||
| 736 | dev_err(dev->devc, "Failed to send IPC MNG_RESET_NOTIFY\n"); | ||
| 737 | return rv; | ||
| 738 | } | ||
| 739 | |||
| 740 | wait_event_interruptible_timeout(dev->wait_hw_ready, | ||
| 741 | dev->recvd_hw_ready, 2 * HZ); | ||
| 742 | if (!dev->recvd_hw_ready) { | ||
| 743 | dev_err(dev->devc, "Timed out waiting for HW ready\n"); | ||
| 744 | rv = -ENODEV; | ||
| 745 | } | ||
| 746 | |||
| 747 | return rv; | ||
| 748 | } | ||
| 749 | |||
| 750 | /** | ||
| 751 | * ish_hw_start() -Start ISH HW | ||
| 752 | * @dev: ishtp device pointer | ||
| 753 | * | ||
| 754 | * Set host to ready state and wait for FW reset | ||
| 755 | * | ||
| 756 | * Return: 0 for success else error fault code | ||
| 757 | */ | ||
| 758 | int ish_hw_start(struct ishtp_device *dev) | ||
| 759 | { | ||
| 760 | ish_set_host_rdy(dev); | ||
| 761 | /* After that we can enable ISH DMA operation */ | ||
| 762 | ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); | ||
| 763 | |||
| 764 | /* | ||
| 765 | * Send 0 IPC message so that ISH FW wakes up if it was already | ||
| 766 | * asleep | ||
| 767 | */ | ||
| 768 | ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); | ||
| 769 | /* Flush write to doorbell */ | ||
| 770 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
| 771 | |||
| 772 | set_host_ready(dev); | ||
| 773 | |||
| 774 | /* wait for FW-initiated reset flow */ | ||
| 775 | if (!dev->recvd_hw_ready) | ||
| 776 | wait_event_interruptible_timeout(dev->wait_hw_ready, | ||
| 777 | dev->recvd_hw_ready, | ||
| 778 | 10 * HZ); | ||
| 779 | |||
| 780 | if (!dev->recvd_hw_ready) { | ||
| 781 | dev_err(dev->devc, | ||
| 782 | "[ishtp-ish]: Timed out waiting for FW-initiated reset\n"); | ||
| 783 | return -ENODEV; | ||
| 784 | } | ||
| 785 | |||
| 786 | return 0; | ||
| 787 | } | ||
| 788 | |||
| 789 | /** | ||
| 790 | * ish_ipc_get_header() -Get doorbell value | ||
| 791 | * @dev: ishtp device pointer | ||
| 792 | * @length: length of message | ||
| 793 | * @busy: busy status | ||
| 794 | * | ||
| 795 | * Get door bell value from message header | ||
| 796 | * | ||
| 797 | * Return: door bell value | ||
| 798 | */ | ||
| 799 | static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length, | ||
| 800 | int busy) | ||
| 801 | { | ||
| 802 | uint32_t drbl_val; | ||
| 803 | |||
| 804 | drbl_val = IPC_BUILD_HEADER(length, IPC_PROTOCOL_ISHTP, busy); | ||
| 805 | |||
| 806 | return drbl_val; | ||
| 807 | } | ||
| 808 | |||
| 809 | static const struct ishtp_hw_ops ish_hw_ops = { | ||
| 810 | .hw_reset = _ish_hw_reset, | ||
| 811 | .ipc_reset = _ish_ipc_reset, | ||
| 812 | .ipc_get_header = ish_ipc_get_header, | ||
| 813 | .ishtp_read = _ishtp_read, | ||
| 814 | .write = write_ipc_to_queue, | ||
| 815 | .get_fw_status = _ish_read_fw_sts_reg, | ||
| 816 | .sync_fw_clock = _ish_sync_fw_clock, | ||
| 817 | .ishtp_read_hdr = _ishtp_read_hdr | ||
| 818 | }; | ||
| 819 | |||
| 820 | /** | ||
| 821 | * ish_dev_init() -Initialize ISH devoce | ||
| 822 | * @pdev: PCI device | ||
| 823 | * | ||
| 824 | * Allocate ISHTP device and initialize IPC processing | ||
| 825 | * | ||
| 826 | * Return: ISHTP device instance on success else NULL | ||
| 827 | */ | ||
| 828 | struct ishtp_device *ish_dev_init(struct pci_dev *pdev) | ||
| 829 | { | ||
| 830 | struct ishtp_device *dev; | ||
| 831 | int i; | ||
| 832 | |||
| 833 | dev = kzalloc(sizeof(struct ishtp_device) + sizeof(struct ish_hw), | ||
| 834 | GFP_KERNEL); | ||
| 835 | if (!dev) | ||
| 836 | return NULL; | ||
| 837 | |||
| 838 | ishtp_device_init(dev); | ||
| 839 | |||
| 840 | init_waitqueue_head(&dev->wait_hw_ready); | ||
| 841 | |||
| 842 | spin_lock_init(&dev->wr_processing_spinlock); | ||
| 843 | spin_lock_init(&dev->out_ipc_spinlock); | ||
| 844 | |||
| 845 | /* Init IPC processing and free lists */ | ||
| 846 | INIT_LIST_HEAD(&dev->wr_processing_list_head.link); | ||
| 847 | INIT_LIST_HEAD(&dev->wr_free_list_head.link); | ||
| 848 | for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) { | ||
| 849 | struct wr_msg_ctl_info *tx_buf; | ||
| 850 | |||
| 851 | tx_buf = kzalloc(sizeof(struct wr_msg_ctl_info), GFP_KERNEL); | ||
| 852 | if (!tx_buf) { | ||
| 853 | /* | ||
| 854 | * IPC buffers may be limited or not available | ||
| 855 | * at all - although this shouldn't happen | ||
| 856 | */ | ||
| 857 | dev_err(dev->devc, | ||
| 858 | "[ishtp-ish]: failure in Tx FIFO allocations (%d)\n", | ||
| 859 | i); | ||
| 860 | break; | ||
| 861 | } | ||
| 862 | list_add_tail(&tx_buf->link, &dev->wr_free_list_head.link); | ||
| 863 | } | ||
| 864 | |||
| 865 | dev->ops = &ish_hw_ops; | ||
| 866 | dev->devc = &pdev->dev; | ||
| 867 | dev->mtu = IPC_PAYLOAD_SIZE - sizeof(struct ishtp_msg_hdr); | ||
| 868 | return dev; | ||
| 869 | } | ||
| 870 | |||
| 871 | /** | ||
| 872 | * ish_device_disable() - Disable ISH device | ||
| 873 | * @dev: ISHTP device pointer | ||
| 874 | * | ||
| 875 | * Disable ISH by clearing host ready to inform firmware. | ||
| 876 | */ | ||
| 877 | void ish_device_disable(struct ishtp_device *dev) | ||
| 878 | { | ||
| 879 | dev->dev_state = ISHTP_DEV_DISABLED; | ||
| 880 | ish_clr_host_rdy(dev); | ||
| 881 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c new file mode 100644 index 000000000000..42f0beeb09fd --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c | |||
| @@ -0,0 +1,322 @@ | |||
| 1 | /* | ||
| 2 | * PCI glue for ISHTP provider device (ISH) driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/moduleparam.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/fs.h> | ||
| 21 | #include <linux/errno.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/pci.h> | ||
| 24 | #include <linux/sched.h> | ||
| 25 | #include <linux/interrupt.h> | ||
| 26 | #include <linux/workqueue.h> | ||
| 27 | #include <linux/miscdevice.h> | ||
| 28 | #define CREATE_TRACE_POINTS | ||
| 29 | #include <trace/events/intel_ish.h> | ||
| 30 | #include "ishtp-dev.h" | ||
| 31 | #include "hw-ish.h" | ||
| 32 | |||
| 33 | static const struct pci_device_id ish_pci_tbl[] = { | ||
| 34 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)}, | ||
| 35 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)}, | ||
| 36 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, | ||
| 37 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, | ||
| 38 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, | ||
| 39 | {0, } | ||
| 40 | }; | ||
| 41 | MODULE_DEVICE_TABLE(pci, ish_pci_tbl); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * ish_event_tracer() - Callback function to dump trace messages | ||
| 45 | * @dev: ishtp device | ||
| 46 | * @format: printf style format | ||
| 47 | * | ||
| 48 | * Callback to direct log messages to Linux trace buffers | ||
| 49 | */ | ||
| 50 | static void ish_event_tracer(struct ishtp_device *dev, char *format, ...) | ||
| 51 | { | ||
| 52 | if (trace_ishtp_dump_enabled()) { | ||
| 53 | va_list args; | ||
| 54 | char tmp_buf[100]; | ||
| 55 | |||
| 56 | va_start(args, format); | ||
| 57 | vsnprintf(tmp_buf, sizeof(tmp_buf), format, args); | ||
| 58 | va_end(args); | ||
| 59 | |||
| 60 | trace_ishtp_dump(tmp_buf); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * ish_init() - Init function | ||
| 66 | * @dev: ishtp device | ||
| 67 | * | ||
| 68 | * This function initialize wait queues for suspend/resume and call | ||
| 69 | * calls hadware initialization function. This will initiate | ||
| 70 | * startup sequence | ||
| 71 | * | ||
| 72 | * Return: 0 for success or error code for failure | ||
| 73 | */ | ||
| 74 | static int ish_init(struct ishtp_device *dev) | ||
| 75 | { | ||
| 76 | int ret; | ||
| 77 | |||
| 78 | /* Set the state of ISH HW to start */ | ||
| 79 | ret = ish_hw_start(dev); | ||
| 80 | if (ret) { | ||
| 81 | dev_err(dev->devc, "ISH: hw start failed.\n"); | ||
| 82 | return ret; | ||
| 83 | } | ||
| 84 | |||
| 85 | /* Start the inter process communication to ISH processor */ | ||
| 86 | ret = ishtp_start(dev); | ||
| 87 | if (ret) { | ||
| 88 | dev_err(dev->devc, "ISHTP: Protocol init failed.\n"); | ||
| 89 | return ret; | ||
| 90 | } | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * ish_probe() - PCI driver probe callback | ||
| 97 | * @pdev: pci device | ||
| 98 | * @ent: pci device id | ||
| 99 | * | ||
| 100 | * Initialize PCI function, setup interrupt and call for ISH initialization | ||
| 101 | * | ||
| 102 | * Return: 0 for success or error code for failure | ||
| 103 | */ | ||
| 104 | static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
| 105 | { | ||
| 106 | struct ishtp_device *dev; | ||
| 107 | struct ish_hw *hw; | ||
| 108 | int ret; | ||
| 109 | |||
| 110 | /* enable pci dev */ | ||
| 111 | ret = pci_enable_device(pdev); | ||
| 112 | if (ret) { | ||
| 113 | dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n"); | ||
| 114 | return ret; | ||
| 115 | } | ||
| 116 | |||
| 117 | /* set PCI host mastering */ | ||
| 118 | pci_set_master(pdev); | ||
| 119 | |||
| 120 | /* pci request regions for ISH driver */ | ||
| 121 | ret = pci_request_regions(pdev, KBUILD_MODNAME); | ||
| 122 | if (ret) { | ||
| 123 | dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n"); | ||
| 124 | goto disable_device; | ||
| 125 | } | ||
| 126 | |||
| 127 | /* allocates and initializes the ISH dev structure */ | ||
| 128 | dev = ish_dev_init(pdev); | ||
| 129 | if (!dev) { | ||
| 130 | ret = -ENOMEM; | ||
| 131 | goto release_regions; | ||
| 132 | } | ||
| 133 | hw = to_ish_hw(dev); | ||
| 134 | dev->print_log = ish_event_tracer; | ||
| 135 | |||
| 136 | /* mapping IO device memory */ | ||
| 137 | hw->mem_addr = pci_iomap(pdev, 0, 0); | ||
| 138 | if (!hw->mem_addr) { | ||
| 139 | dev_err(&pdev->dev, "ISH: mapping I/O range failure\n"); | ||
| 140 | ret = -ENOMEM; | ||
| 141 | goto free_device; | ||
| 142 | } | ||
| 143 | |||
| 144 | dev->pdev = pdev; | ||
| 145 | |||
| 146 | pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; | ||
| 147 | |||
| 148 | /* request and enable interrupt */ | ||
| 149 | ret = request_irq(pdev->irq, ish_irq_handler, IRQF_NO_SUSPEND, | ||
| 150 | KBUILD_MODNAME, dev); | ||
| 151 | if (ret) { | ||
| 152 | dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n", | ||
| 153 | pdev->irq); | ||
| 154 | goto free_device; | ||
| 155 | } | ||
| 156 | |||
| 157 | dev_set_drvdata(dev->devc, dev); | ||
| 158 | |||
| 159 | init_waitqueue_head(&dev->suspend_wait); | ||
| 160 | init_waitqueue_head(&dev->resume_wait); | ||
| 161 | |||
| 162 | ret = ish_init(dev); | ||
| 163 | if (ret) | ||
| 164 | goto free_irq; | ||
| 165 | |||
| 166 | return 0; | ||
| 167 | |||
| 168 | free_irq: | ||
| 169 | free_irq(pdev->irq, dev); | ||
| 170 | free_device: | ||
| 171 | pci_iounmap(pdev, hw->mem_addr); | ||
| 172 | kfree(dev); | ||
| 173 | release_regions: | ||
| 174 | pci_release_regions(pdev); | ||
| 175 | disable_device: | ||
| 176 | pci_clear_master(pdev); | ||
| 177 | pci_disable_device(pdev); | ||
| 178 | dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n"); | ||
| 179 | |||
| 180 | return ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | /** | ||
| 184 | * ish_remove() - PCI driver remove callback | ||
| 185 | * @pdev: pci device | ||
| 186 | * | ||
| 187 | * This function does cleanup of ISH on pci remove callback | ||
| 188 | */ | ||
| 189 | static void ish_remove(struct pci_dev *pdev) | ||
| 190 | { | ||
| 191 | struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev); | ||
| 192 | struct ish_hw *hw = to_ish_hw(ishtp_dev); | ||
| 193 | |||
| 194 | ishtp_bus_remove_all_clients(ishtp_dev, false); | ||
| 195 | ish_device_disable(ishtp_dev); | ||
| 196 | |||
| 197 | free_irq(pdev->irq, ishtp_dev); | ||
| 198 | pci_iounmap(pdev, hw->mem_addr); | ||
| 199 | pci_release_regions(pdev); | ||
| 200 | pci_clear_master(pdev); | ||
| 201 | pci_disable_device(pdev); | ||
| 202 | kfree(ishtp_dev); | ||
| 203 | } | ||
| 204 | |||
| 205 | static struct device *ish_resume_device; | ||
| 206 | |||
| 207 | /** | ||
| 208 | * ish_resume_handler() - Work function to complete resume | ||
| 209 | * @work: work struct | ||
| 210 | * | ||
| 211 | * The resume work function to complete resume function asynchronously. | ||
| 212 | * There are two types of platforms, one where ISH is not powered off, | ||
| 213 | * in that case a simple resume message is enough, others we need | ||
| 214 | * a reset sequence. | ||
| 215 | */ | ||
| 216 | static void ish_resume_handler(struct work_struct *work) | ||
| 217 | { | ||
| 218 | struct pci_dev *pdev = to_pci_dev(ish_resume_device); | ||
| 219 | struct ishtp_device *dev = pci_get_drvdata(pdev); | ||
| 220 | int ret; | ||
| 221 | |||
| 222 | ishtp_send_resume(dev); | ||
| 223 | |||
| 224 | /* 50 ms to get resume response */ | ||
| 225 | if (dev->resume_flag) | ||
| 226 | ret = wait_event_interruptible_timeout(dev->resume_wait, | ||
| 227 | !dev->resume_flag, | ||
| 228 | msecs_to_jiffies(50)); | ||
| 229 | |||
| 230 | /* | ||
| 231 | * If no resume response. This platform is not S0ix compatible | ||
| 232 | * So on resume full reboot of ISH processor will happen, so | ||
| 233 | * need to go through init sequence again | ||
| 234 | */ | ||
| 235 | if (dev->resume_flag) | ||
| 236 | ish_init(dev); | ||
| 237 | } | ||
| 238 | |||
| 239 | /** | ||
| 240 | * ish_suspend() - ISH suspend callback | ||
| 241 | * @device: device pointer | ||
| 242 | * | ||
| 243 | * ISH suspend callback | ||
| 244 | * | ||
| 245 | * Return: 0 to the pm core | ||
| 246 | */ | ||
| 247 | static int ish_suspend(struct device *device) | ||
| 248 | { | ||
| 249 | struct pci_dev *pdev = to_pci_dev(device); | ||
| 250 | struct ishtp_device *dev = pci_get_drvdata(pdev); | ||
| 251 | |||
| 252 | enable_irq_wake(pdev->irq); | ||
| 253 | /* | ||
| 254 | * If previous suspend hasn't been asnwered then ISH is likely dead, | ||
| 255 | * don't attempt nested notification | ||
| 256 | */ | ||
| 257 | if (dev->suspend_flag) | ||
| 258 | return 0; | ||
| 259 | |||
| 260 | dev->resume_flag = 0; | ||
| 261 | dev->suspend_flag = 1; | ||
| 262 | ishtp_send_suspend(dev); | ||
| 263 | |||
| 264 | /* 25 ms should be enough for live ISH to flush all IPC buf */ | ||
| 265 | if (dev->suspend_flag) | ||
| 266 | wait_event_interruptible_timeout(dev->suspend_wait, | ||
| 267 | !dev->suspend_flag, | ||
| 268 | msecs_to_jiffies(25)); | ||
| 269 | |||
| 270 | return 0; | ||
| 271 | } | ||
| 272 | |||
| 273 | static DECLARE_WORK(resume_work, ish_resume_handler); | ||
| 274 | /** | ||
| 275 | * ish_resume() - ISH resume callback | ||
| 276 | * @device: device pointer | ||
| 277 | * | ||
| 278 | * ISH resume callback | ||
| 279 | * | ||
| 280 | * Return: 0 to the pm core | ||
| 281 | */ | ||
| 282 | static int ish_resume(struct device *device) | ||
| 283 | { | ||
| 284 | struct pci_dev *pdev = to_pci_dev(device); | ||
| 285 | struct ishtp_device *dev = pci_get_drvdata(pdev); | ||
| 286 | |||
| 287 | ish_resume_device = device; | ||
| 288 | dev->resume_flag = 1; | ||
| 289 | |||
| 290 | disable_irq_wake(pdev->irq); | ||
| 291 | schedule_work(&resume_work); | ||
| 292 | |||
| 293 | return 0; | ||
| 294 | } | ||
| 295 | |||
| 296 | #ifdef CONFIG_PM | ||
| 297 | static const struct dev_pm_ops ish_pm_ops = { | ||
| 298 | .suspend = ish_suspend, | ||
| 299 | .resume = ish_resume, | ||
| 300 | }; | ||
| 301 | #define ISHTP_ISH_PM_OPS (&ish_pm_ops) | ||
| 302 | #else | ||
| 303 | #define ISHTP_ISH_PM_OPS NULL | ||
| 304 | #endif | ||
| 305 | |||
| 306 | static struct pci_driver ish_driver = { | ||
| 307 | .name = KBUILD_MODNAME, | ||
| 308 | .id_table = ish_pci_tbl, | ||
| 309 | .probe = ish_probe, | ||
| 310 | .remove = ish_remove, | ||
| 311 | .driver.pm = ISHTP_ISH_PM_OPS, | ||
| 312 | }; | ||
| 313 | |||
| 314 | module_pci_driver(ish_driver); | ||
| 315 | |||
| 316 | /* Original author */ | ||
| 317 | MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>"); | ||
| 318 | /* Adoption to upstream Linux kernel */ | ||
| 319 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
| 320 | |||
| 321 | MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver"); | ||
| 322 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/utils.h b/drivers/hid/intel-ish-hid/ipc/utils.h new file mode 100644 index 000000000000..5a82123dc7b4 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/utils.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * Utility macros of ISH | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | #ifndef UTILS__H | ||
| 16 | #define UTILS__H | ||
| 17 | |||
| 18 | #define WAIT_FOR_SEND_SLICE (HZ / 10) | ||
| 19 | #define WAIT_FOR_CONNECT_SLICE (HZ / 10) | ||
| 20 | |||
| 21 | /* | ||
| 22 | * Waits for specified event when a thread that triggers event can't signal | ||
| 23 | * Also, waits *at_least* `timeinc` after condition is satisfied | ||
| 24 | */ | ||
| 25 | #define timed_wait_for(timeinc, condition) \ | ||
| 26 | do { \ | ||
| 27 | int completed = 0; \ | ||
| 28 | do { \ | ||
| 29 | unsigned long j; \ | ||
| 30 | int done = 0; \ | ||
| 31 | \ | ||
| 32 | completed = (condition); \ | ||
| 33 | for (j = jiffies, done = 0; !done; ) { \ | ||
| 34 | schedule_timeout(timeinc); \ | ||
| 35 | if (time_is_before_eq_jiffies(j + timeinc)) \ | ||
| 36 | done = 1; \ | ||
| 37 | } \ | ||
| 38 | } while (!(completed)); \ | ||
| 39 | } while (0) | ||
| 40 | |||
| 41 | |||
| 42 | /* | ||
| 43 | * Waits for specified event when a thread that triggers event | ||
| 44 | * can't signal with timeout (use whenever we may hang) | ||
| 45 | */ | ||
| 46 | #define timed_wait_for_timeout(timeinc, condition, timeout) \ | ||
| 47 | do { \ | ||
| 48 | int t = timeout; \ | ||
| 49 | do { \ | ||
| 50 | unsigned long j; \ | ||
| 51 | int done = 0; \ | ||
| 52 | \ | ||
| 53 | for (j = jiffies, done = 0; !done; ) { \ | ||
| 54 | schedule_timeout(timeinc); \ | ||
| 55 | if (time_is_before_eq_jiffies(j + timeinc)) \ | ||
| 56 | done = 1; \ | ||
| 57 | } \ | ||
| 58 | t -= timeinc; \ | ||
| 59 | if (t <= 0) \ | ||
| 60 | break; \ | ||
| 61 | } while (!(condition)); \ | ||
| 62 | } while (0) | ||
| 63 | |||
| 64 | #endif /* UTILS__H */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c new file mode 100644 index 000000000000..5c643d7a07b2 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c | |||
| @@ -0,0 +1,978 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP client driver for HID (ISH) | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/hid.h> | ||
| 18 | #include <linux/sched.h> | ||
| 19 | #include "ishtp/ishtp-dev.h" | ||
| 20 | #include "ishtp/client.h" | ||
| 21 | #include "ishtp-hid.h" | ||
| 22 | |||
| 23 | /* Rx ring buffer pool size */ | ||
| 24 | #define HID_CL_RX_RING_SIZE 32 | ||
| 25 | #define HID_CL_TX_RING_SIZE 16 | ||
| 26 | |||
| 27 | /** | ||
| 28 | * report_bad_packets() - Report bad packets | ||
| 29 | * @hid_ishtp_cl: Client instance to get stats | ||
| 30 | * @recv_buf: Raw received host interface message | ||
| 31 | * @cur_pos: Current position index in payload | ||
| 32 | * @payload_len: Length of payload expected | ||
| 33 | * | ||
| 34 | * Dumps error in case bad packet is received | ||
| 35 | */ | ||
| 36 | static void report_bad_packet(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, | ||
| 37 | size_t cur_pos, size_t payload_len) | ||
| 38 | { | ||
| 39 | struct hostif_msg *recv_msg = recv_buf; | ||
| 40 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 41 | |||
| 42 | dev_err(&client_data->cl_device->dev, "[hid-ish]: BAD packet %02X\n" | ||
| 43 | "total_bad=%u cur_pos=%u\n" | ||
| 44 | "[%02X %02X %02X %02X]\n" | ||
| 45 | "payload_len=%u\n" | ||
| 46 | "multi_packet_cnt=%u\n" | ||
| 47 | "is_response=%02X\n", | ||
| 48 | recv_msg->hdr.command, client_data->bad_recv_cnt, | ||
| 49 | (unsigned int)cur_pos, | ||
| 50 | ((unsigned char *)recv_msg)[0], ((unsigned char *)recv_msg)[1], | ||
| 51 | ((unsigned char *)recv_msg)[2], ((unsigned char *)recv_msg)[3], | ||
| 52 | (unsigned int)payload_len, client_data->multi_packet_cnt, | ||
| 53 | recv_msg->hdr.command & ~CMD_MASK); | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * process_recv() - Received and parse incoming packet | ||
| 58 | * @hid_ishtp_cl: Client instance to get stats | ||
| 59 | * @recv_buf: Raw received host interface message | ||
| 60 | * @data_len: length of the message | ||
| 61 | * | ||
| 62 | * Parse the incoming packet. If it is a response packet then it will update | ||
| 63 | * per instance flags and wake up the caller waiting to for the response. | ||
| 64 | */ | ||
| 65 | static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, | ||
| 66 | size_t data_len) | ||
| 67 | { | ||
| 68 | struct hostif_msg *recv_msg; | ||
| 69 | unsigned char *payload; | ||
| 70 | struct device_info *dev_info; | ||
| 71 | int i, j; | ||
| 72 | size_t payload_len, total_len, cur_pos; | ||
| 73 | int report_type; | ||
| 74 | struct report_list *reports_list; | ||
| 75 | char *reports; | ||
| 76 | size_t report_len; | ||
| 77 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 78 | int curr_hid_dev = client_data->cur_hid_dev; | ||
| 79 | |||
| 80 | if (data_len < sizeof(struct hostif_msg_hdr)) { | ||
| 81 | dev_err(&client_data->cl_device->dev, | ||
| 82 | "[hid-ish]: error, received %u which is less than data header %u\n", | ||
| 83 | (unsigned int)data_len, | ||
| 84 | (unsigned int)sizeof(struct hostif_msg_hdr)); | ||
| 85 | ++client_data->bad_recv_cnt; | ||
| 86 | ish_hw_reset(hid_ishtp_cl->dev); | ||
| 87 | return; | ||
| 88 | } | ||
| 89 | |||
| 90 | payload = recv_buf + sizeof(struct hostif_msg_hdr); | ||
| 91 | total_len = data_len; | ||
| 92 | cur_pos = 0; | ||
| 93 | |||
| 94 | do { | ||
| 95 | recv_msg = (struct hostif_msg *)(recv_buf + cur_pos); | ||
| 96 | payload_len = recv_msg->hdr.size; | ||
| 97 | |||
| 98 | /* Sanity checks */ | ||
| 99 | if (cur_pos + payload_len + sizeof(struct hostif_msg) > | ||
| 100 | total_len) { | ||
| 101 | ++client_data->bad_recv_cnt; | ||
| 102 | report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos, | ||
| 103 | payload_len); | ||
| 104 | ish_hw_reset(hid_ishtp_cl->dev); | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | |||
| 108 | hid_ishtp_trace(client_data, "%s %d\n", | ||
| 109 | __func__, recv_msg->hdr.command & CMD_MASK); | ||
| 110 | |||
| 111 | switch (recv_msg->hdr.command & CMD_MASK) { | ||
| 112 | case HOSTIF_DM_ENUM_DEVICES: | ||
| 113 | if ((!(recv_msg->hdr.command & ~CMD_MASK) || | ||
| 114 | client_data->init_done)) { | ||
| 115 | ++client_data->bad_recv_cnt; | ||
| 116 | report_bad_packet(hid_ishtp_cl, recv_msg, | ||
| 117 | cur_pos, | ||
| 118 | payload_len); | ||
| 119 | ish_hw_reset(hid_ishtp_cl->dev); | ||
| 120 | break; | ||
| 121 | } | ||
| 122 | client_data->hid_dev_count = (unsigned int)*payload; | ||
| 123 | if (!client_data->hid_devices) | ||
| 124 | client_data->hid_devices = devm_kzalloc( | ||
| 125 | &client_data->cl_device->dev, | ||
| 126 | client_data->hid_dev_count * | ||
| 127 | sizeof(struct device_info), | ||
| 128 | GFP_KERNEL); | ||
| 129 | if (!client_data->hid_devices) { | ||
| 130 | dev_err(&client_data->cl_device->dev, | ||
| 131 | "Mem alloc failed for hid device info\n"); | ||
| 132 | wake_up_interruptible(&client_data->init_wait); | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | for (i = 0; i < client_data->hid_dev_count; ++i) { | ||
| 136 | if (1 + sizeof(struct device_info) * i >= | ||
| 137 | payload_len) { | ||
| 138 | dev_err(&client_data->cl_device->dev, | ||
| 139 | "[hid-ish]: [ENUM_DEVICES]: content size %lu is bigger than payload_len %u\n", | ||
| 140 | 1 + sizeof(struct device_info) | ||
| 141 | * i, | ||
| 142 | (unsigned int)payload_len); | ||
| 143 | } | ||
| 144 | |||
| 145 | if (1 + sizeof(struct device_info) * i >= | ||
| 146 | data_len) | ||
| 147 | break; | ||
| 148 | |||
| 149 | dev_info = (struct device_info *)(payload + 1 + | ||
| 150 | sizeof(struct device_info) * i); | ||
| 151 | if (client_data->hid_devices) | ||
| 152 | memcpy(client_data->hid_devices + i, | ||
| 153 | dev_info, | ||
| 154 | sizeof(struct device_info)); | ||
| 155 | } | ||
| 156 | |||
| 157 | client_data->enum_devices_done = true; | ||
| 158 | wake_up_interruptible(&client_data->init_wait); | ||
| 159 | |||
| 160 | break; | ||
| 161 | |||
| 162 | case HOSTIF_GET_HID_DESCRIPTOR: | ||
| 163 | if ((!(recv_msg->hdr.command & ~CMD_MASK) || | ||
| 164 | client_data->init_done)) { | ||
| 165 | ++client_data->bad_recv_cnt; | ||
| 166 | report_bad_packet(hid_ishtp_cl, recv_msg, | ||
| 167 | cur_pos, | ||
| 168 | payload_len); | ||
| 169 | ish_hw_reset(hid_ishtp_cl->dev); | ||
| 170 | break; | ||
| 171 | } | ||
| 172 | if (!client_data->hid_descr[curr_hid_dev]) | ||
| 173 | client_data->hid_descr[curr_hid_dev] = | ||
| 174 | devm_kmalloc(&client_data->cl_device->dev, | ||
| 175 | payload_len, GFP_KERNEL); | ||
| 176 | if (client_data->hid_descr[curr_hid_dev]) { | ||
| 177 | memcpy(client_data->hid_descr[curr_hid_dev], | ||
| 178 | payload, payload_len); | ||
| 179 | client_data->hid_descr_size[curr_hid_dev] = | ||
| 180 | payload_len; | ||
| 181 | client_data->hid_descr_done = true; | ||
| 182 | } | ||
| 183 | wake_up_interruptible(&client_data->init_wait); | ||
| 184 | |||
| 185 | break; | ||
| 186 | |||
| 187 | case HOSTIF_GET_REPORT_DESCRIPTOR: | ||
| 188 | if ((!(recv_msg->hdr.command & ~CMD_MASK) || | ||
| 189 | client_data->init_done)) { | ||
| 190 | ++client_data->bad_recv_cnt; | ||
| 191 | report_bad_packet(hid_ishtp_cl, recv_msg, | ||
| 192 | cur_pos, | ||
| 193 | payload_len); | ||
| 194 | ish_hw_reset(hid_ishtp_cl->dev); | ||
| 195 | break; | ||
| 196 | } | ||
| 197 | if (!client_data->report_descr[curr_hid_dev]) | ||
| 198 | client_data->report_descr[curr_hid_dev] = | ||
| 199 | devm_kmalloc(&client_data->cl_device->dev, | ||
| 200 | payload_len, GFP_KERNEL); | ||
| 201 | if (client_data->report_descr[curr_hid_dev]) { | ||
| 202 | memcpy(client_data->report_descr[curr_hid_dev], | ||
| 203 | payload, | ||
| 204 | payload_len); | ||
| 205 | client_data->report_descr_size[curr_hid_dev] = | ||
| 206 | payload_len; | ||
| 207 | client_data->report_descr_done = true; | ||
| 208 | } | ||
| 209 | wake_up_interruptible(&client_data->init_wait); | ||
| 210 | |||
| 211 | break; | ||
| 212 | |||
| 213 | case HOSTIF_GET_FEATURE_REPORT: | ||
| 214 | report_type = HID_FEATURE_REPORT; | ||
| 215 | goto do_get_report; | ||
| 216 | |||
| 217 | case HOSTIF_GET_INPUT_REPORT: | ||
| 218 | report_type = HID_INPUT_REPORT; | ||
| 219 | do_get_report: | ||
| 220 | /* Get index of device that matches this id */ | ||
| 221 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
| 222 | if (recv_msg->hdr.device_id == | ||
| 223 | client_data->hid_devices[i].dev_id) | ||
| 224 | if (client_data->hid_sensor_hubs[i]) { | ||
| 225 | hid_input_report( | ||
| 226 | client_data->hid_sensor_hubs[ | ||
| 227 | i], | ||
| 228 | report_type, payload, | ||
| 229 | payload_len, 0); | ||
| 230 | ishtp_hid_wakeup( | ||
| 231 | client_data->hid_sensor_hubs[ | ||
| 232 | i]); | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | break; | ||
| 237 | |||
| 238 | case HOSTIF_SET_FEATURE_REPORT: | ||
| 239 | /* Get index of device that matches this id */ | ||
| 240 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
| 241 | if (recv_msg->hdr.device_id == | ||
| 242 | client_data->hid_devices[i].dev_id) | ||
| 243 | if (client_data->hid_sensor_hubs[i]) { | ||
| 244 | ishtp_hid_wakeup( | ||
| 245 | client_data->hid_sensor_hubs[ | ||
| 246 | i]); | ||
| 247 | break; | ||
| 248 | } | ||
| 249 | } | ||
| 250 | break; | ||
| 251 | |||
| 252 | case HOSTIF_PUBLISH_INPUT_REPORT: | ||
| 253 | report_type = HID_INPUT_REPORT; | ||
| 254 | for (i = 0; i < client_data->num_hid_devices; ++i) | ||
| 255 | if (recv_msg->hdr.device_id == | ||
| 256 | client_data->hid_devices[i].dev_id) | ||
| 257 | if (client_data->hid_sensor_hubs[i]) | ||
| 258 | hid_input_report( | ||
| 259 | client_data->hid_sensor_hubs[ | ||
| 260 | i], | ||
| 261 | report_type, payload, | ||
| 262 | payload_len, 0); | ||
| 263 | break; | ||
| 264 | |||
| 265 | case HOSTIF_PUBLISH_INPUT_REPORT_LIST: | ||
| 266 | report_type = HID_INPUT_REPORT; | ||
| 267 | reports_list = (struct report_list *)payload; | ||
| 268 | reports = (char *)reports_list->reports; | ||
| 269 | |||
| 270 | for (j = 0; j < reports_list->num_of_reports; j++) { | ||
| 271 | recv_msg = (struct hostif_msg *)(reports + | ||
| 272 | sizeof(uint16_t)); | ||
| 273 | report_len = *(uint16_t *)reports; | ||
| 274 | payload = reports + sizeof(uint16_t) + | ||
| 275 | sizeof(struct hostif_msg_hdr); | ||
| 276 | payload_len = report_len - | ||
| 277 | sizeof(struct hostif_msg_hdr); | ||
| 278 | |||
| 279 | for (i = 0; i < client_data->num_hid_devices; | ||
| 280 | ++i) | ||
| 281 | if (recv_msg->hdr.device_id == | ||
| 282 | client_data->hid_devices[i].dev_id && | ||
| 283 | client_data->hid_sensor_hubs[i]) { | ||
| 284 | hid_input_report( | ||
| 285 | client_data->hid_sensor_hubs[ | ||
| 286 | i], | ||
| 287 | report_type, | ||
| 288 | payload, payload_len, | ||
| 289 | 0); | ||
| 290 | } | ||
| 291 | |||
| 292 | reports += sizeof(uint16_t) + report_len; | ||
| 293 | } | ||
| 294 | break; | ||
| 295 | default: | ||
| 296 | ++client_data->bad_recv_cnt; | ||
| 297 | report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos, | ||
| 298 | payload_len); | ||
| 299 | ish_hw_reset(hid_ishtp_cl->dev); | ||
| 300 | break; | ||
| 301 | |||
| 302 | } | ||
| 303 | |||
| 304 | if (!cur_pos && cur_pos + payload_len + | ||
| 305 | sizeof(struct hostif_msg) < total_len) | ||
| 306 | ++client_data->multi_packet_cnt; | ||
| 307 | |||
| 308 | cur_pos += payload_len + sizeof(struct hostif_msg); | ||
| 309 | payload += payload_len + sizeof(struct hostif_msg); | ||
| 310 | |||
| 311 | } while (cur_pos < total_len); | ||
| 312 | } | ||
| 313 | |||
| 314 | /** | ||
| 315 | * ish_cl_event_cb() - bus driver callback for incoming message/packet | ||
| 316 | * @device: Pointer to the the ishtp client device for which this message | ||
| 317 | * is targeted | ||
| 318 | * | ||
| 319 | * Remove the packet from the list and process the message by calling | ||
| 320 | * process_recv | ||
| 321 | */ | ||
| 322 | static void ish_cl_event_cb(struct ishtp_cl_device *device) | ||
| 323 | { | ||
| 324 | struct ishtp_cl *hid_ishtp_cl = device->driver_data; | ||
| 325 | struct ishtp_cl_rb *rb_in_proc; | ||
| 326 | size_t r_length; | ||
| 327 | unsigned long flags; | ||
| 328 | |||
| 329 | if (!hid_ishtp_cl) | ||
| 330 | return; | ||
| 331 | |||
| 332 | spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags); | ||
| 333 | while (!list_empty(&hid_ishtp_cl->in_process_list.list)) { | ||
| 334 | rb_in_proc = list_entry( | ||
| 335 | hid_ishtp_cl->in_process_list.list.next, | ||
| 336 | struct ishtp_cl_rb, list); | ||
| 337 | list_del_init(&rb_in_proc->list); | ||
| 338 | spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock, | ||
| 339 | flags); | ||
| 340 | |||
| 341 | if (!rb_in_proc->buffer.data) | ||
| 342 | return; | ||
| 343 | |||
| 344 | r_length = rb_in_proc->buf_idx; | ||
| 345 | |||
| 346 | /* decide what to do with received data */ | ||
| 347 | process_recv(hid_ishtp_cl, rb_in_proc->buffer.data, r_length); | ||
| 348 | |||
| 349 | ishtp_cl_io_rb_recycle(rb_in_proc); | ||
| 350 | spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags); | ||
| 351 | } | ||
| 352 | spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock, flags); | ||
| 353 | } | ||
| 354 | |||
| 355 | /** | ||
| 356 | * hid_ishtp_set_feature() - send request to ISH FW to set a feature request | ||
| 357 | * @hid: hid device instance for this request | ||
| 358 | * @buf: feature buffer | ||
| 359 | * @len: Length of feature buffer | ||
| 360 | * @report_id: Report id for the feature set request | ||
| 361 | * | ||
| 362 | * This is called from hid core .request() callback. This function doesn't wait | ||
| 363 | * for response. | ||
| 364 | */ | ||
| 365 | void hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len, | ||
| 366 | int report_id) | ||
| 367 | { | ||
| 368 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
| 369 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
| 370 | struct hostif_msg *msg = (struct hostif_msg *)buf; | ||
| 371 | int rv; | ||
| 372 | int i; | ||
| 373 | |||
| 374 | hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid); | ||
| 375 | |||
| 376 | rv = ishtp_hid_link_ready_wait(client_data); | ||
| 377 | if (rv) { | ||
| 378 | hid_ishtp_trace(client_data, "%s hid %p link not ready\n", | ||
| 379 | __func__, hid); | ||
| 380 | return; | ||
| 381 | } | ||
| 382 | |||
| 383 | memset(msg, 0, sizeof(struct hostif_msg)); | ||
| 384 | msg->hdr.command = HOSTIF_SET_FEATURE_REPORT; | ||
| 385 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
| 386 | if (hid == client_data->hid_sensor_hubs[i]) { | ||
| 387 | msg->hdr.device_id = | ||
| 388 | client_data->hid_devices[i].dev_id; | ||
| 389 | break; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | if (i == client_data->num_hid_devices) | ||
| 394 | return; | ||
| 395 | |||
| 396 | rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len); | ||
| 397 | if (rv) | ||
| 398 | hid_ishtp_trace(client_data, "%s hid %p send failed\n", | ||
| 399 | __func__, hid); | ||
| 400 | } | ||
| 401 | |||
| 402 | /** | ||
| 403 | * hid_ishtp_get_report() - request to get feature/input report | ||
| 404 | * @hid: hid device instance for this request | ||
| 405 | * @report_id: Report id for the get request | ||
| 406 | * @report_type: Report type for the this request | ||
| 407 | * | ||
| 408 | * This is called from hid core .request() callback. This function will send | ||
| 409 | * request to FW and return without waiting for response. | ||
| 410 | */ | ||
| 411 | void hid_ishtp_get_report(struct hid_device *hid, int report_id, | ||
| 412 | int report_type) | ||
| 413 | { | ||
| 414 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
| 415 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
| 416 | static unsigned char buf[10]; | ||
| 417 | unsigned int len; | ||
| 418 | struct hostif_msg_to_sensor *msg = (struct hostif_msg_to_sensor *)buf; | ||
| 419 | int rv; | ||
| 420 | int i; | ||
| 421 | |||
| 422 | hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid); | ||
| 423 | rv = ishtp_hid_link_ready_wait(client_data); | ||
| 424 | if (rv) { | ||
| 425 | hid_ishtp_trace(client_data, "%s hid %p link not ready\n", | ||
| 426 | __func__, hid); | ||
| 427 | return; | ||
| 428 | } | ||
| 429 | |||
| 430 | len = sizeof(struct hostif_msg_to_sensor); | ||
| 431 | |||
| 432 | memset(msg, 0, sizeof(struct hostif_msg_to_sensor)); | ||
| 433 | msg->hdr.command = (report_type == HID_FEATURE_REPORT) ? | ||
| 434 | HOSTIF_GET_FEATURE_REPORT : HOSTIF_GET_INPUT_REPORT; | ||
| 435 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
| 436 | if (hid == client_data->hid_sensor_hubs[i]) { | ||
| 437 | msg->hdr.device_id = | ||
| 438 | client_data->hid_devices[i].dev_id; | ||
| 439 | break; | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | if (i == client_data->num_hid_devices) | ||
| 444 | return; | ||
| 445 | |||
| 446 | msg->report_id = report_id; | ||
| 447 | rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len); | ||
| 448 | if (rv) | ||
| 449 | hid_ishtp_trace(client_data, "%s hid %p send failed\n", | ||
| 450 | __func__, hid); | ||
| 451 | } | ||
| 452 | |||
| 453 | /** | ||
| 454 | * ishtp_hid_link_ready_wait() - Wait for link ready | ||
| 455 | * @client_data: client data instance | ||
| 456 | * | ||
| 457 | * If the transport link started suspend process, then wait, till either | ||
| 458 | * resumed or timeout | ||
| 459 | * | ||
| 460 | * Return: 0 on success, non zero on error | ||
| 461 | */ | ||
| 462 | int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data) | ||
| 463 | { | ||
| 464 | int rc; | ||
| 465 | |||
| 466 | if (client_data->suspended) { | ||
| 467 | hid_ishtp_trace(client_data, "wait for link ready\n"); | ||
| 468 | rc = wait_event_interruptible_timeout( | ||
| 469 | client_data->ishtp_resume_wait, | ||
| 470 | !client_data->suspended, | ||
| 471 | 5 * HZ); | ||
| 472 | |||
| 473 | if (rc == 0) { | ||
| 474 | hid_ishtp_trace(client_data, "link not ready\n"); | ||
| 475 | return -EIO; | ||
| 476 | } | ||
| 477 | hid_ishtp_trace(client_data, "link ready\n"); | ||
| 478 | } | ||
| 479 | |||
| 480 | return 0; | ||
| 481 | } | ||
| 482 | |||
| 483 | /** | ||
| 484 | * ishtp_enum_enum_devices() - Enumerate hid devices | ||
| 485 | * @hid_ishtp_cl: client instance | ||
| 486 | * | ||
| 487 | * Helper function to send request to firmware to enumerate HID devices | ||
| 488 | * | ||
| 489 | * Return: 0 on success, non zero on error | ||
| 490 | */ | ||
| 491 | static int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl) | ||
| 492 | { | ||
| 493 | struct hostif_msg msg; | ||
| 494 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 495 | int retry_count; | ||
| 496 | int rv; | ||
| 497 | |||
| 498 | /* Send HOSTIF_DM_ENUM_DEVICES */ | ||
| 499 | memset(&msg, 0, sizeof(struct hostif_msg)); | ||
| 500 | msg.hdr.command = HOSTIF_DM_ENUM_DEVICES; | ||
| 501 | rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *)&msg, | ||
| 502 | sizeof(struct hostif_msg)); | ||
| 503 | if (rv) | ||
| 504 | return rv; | ||
| 505 | |||
| 506 | retry_count = 0; | ||
| 507 | while (!client_data->enum_devices_done && | ||
| 508 | retry_count < 10) { | ||
| 509 | wait_event_interruptible_timeout(client_data->init_wait, | ||
| 510 | client_data->enum_devices_done, | ||
| 511 | 3 * HZ); | ||
| 512 | ++retry_count; | ||
| 513 | if (!client_data->enum_devices_done) | ||
| 514 | /* Send HOSTIF_DM_ENUM_DEVICES */ | ||
| 515 | rv = ishtp_cl_send(hid_ishtp_cl, | ||
| 516 | (unsigned char *) &msg, | ||
| 517 | sizeof(struct hostif_msg)); | ||
| 518 | } | ||
| 519 | if (!client_data->enum_devices_done) { | ||
| 520 | dev_err(&client_data->cl_device->dev, | ||
| 521 | "[hid-ish]: timed out waiting for enum_devices\n"); | ||
| 522 | return -ETIMEDOUT; | ||
| 523 | } | ||
| 524 | if (!client_data->hid_devices) { | ||
| 525 | dev_err(&client_data->cl_device->dev, | ||
| 526 | "[hid-ish]: failed to allocate HID dev structures\n"); | ||
| 527 | return -ENOMEM; | ||
| 528 | } | ||
| 529 | |||
| 530 | client_data->num_hid_devices = client_data->hid_dev_count; | ||
| 531 | dev_info(&hid_ishtp_cl->device->dev, | ||
| 532 | "[hid-ish]: enum_devices_done OK, num_hid_devices=%d\n", | ||
| 533 | client_data->num_hid_devices); | ||
| 534 | |||
| 535 | return 0; | ||
| 536 | } | ||
| 537 | |||
| 538 | /** | ||
| 539 | * ishtp_get_hid_descriptor() - Get hid descriptor | ||
| 540 | * @hid_ishtp_cl: client instance | ||
| 541 | * @index: Index into the hid_descr array | ||
| 542 | * | ||
| 543 | * Helper function to send request to firmware get HID descriptor of a device | ||
| 544 | * | ||
| 545 | * Return: 0 on success, non zero on error | ||
| 546 | */ | ||
| 547 | static int ishtp_get_hid_descriptor(struct ishtp_cl *hid_ishtp_cl, int index) | ||
| 548 | { | ||
| 549 | struct hostif_msg msg; | ||
| 550 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 551 | int rv; | ||
| 552 | |||
| 553 | /* Get HID descriptor */ | ||
| 554 | client_data->hid_descr_done = false; | ||
| 555 | memset(&msg, 0, sizeof(struct hostif_msg)); | ||
| 556 | msg.hdr.command = HOSTIF_GET_HID_DESCRIPTOR; | ||
| 557 | msg.hdr.device_id = client_data->hid_devices[index].dev_id; | ||
| 558 | rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg, | ||
| 559 | sizeof(struct hostif_msg)); | ||
| 560 | if (rv) | ||
| 561 | return rv; | ||
| 562 | |||
| 563 | if (!client_data->hid_descr_done) { | ||
| 564 | wait_event_interruptible_timeout(client_data->init_wait, | ||
| 565 | client_data->hid_descr_done, | ||
| 566 | 3 * HZ); | ||
| 567 | if (!client_data->hid_descr_done) { | ||
| 568 | dev_err(&client_data->cl_device->dev, | ||
| 569 | "[hid-ish]: timed out for hid_descr_done\n"); | ||
| 570 | return -EIO; | ||
| 571 | } | ||
| 572 | |||
| 573 | if (!client_data->hid_descr[index]) { | ||
| 574 | dev_err(&client_data->cl_device->dev, | ||
| 575 | "[hid-ish]: allocation HID desc fail\n"); | ||
| 576 | return -ENOMEM; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | return 0; | ||
| 581 | } | ||
| 582 | |||
| 583 | /** | ||
| 584 | * ishtp_get_report_descriptor() - Get report descriptor | ||
| 585 | * @hid_ishtp_cl: client instance | ||
| 586 | * @index: Index into the hid_descr array | ||
| 587 | * | ||
| 588 | * Helper function to send request to firmware get HID report descriptor of | ||
| 589 | * a device | ||
| 590 | * | ||
| 591 | * Return: 0 on success, non zero on error | ||
| 592 | */ | ||
| 593 | static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl, | ||
| 594 | int index) | ||
| 595 | { | ||
| 596 | struct hostif_msg msg; | ||
| 597 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 598 | int rv; | ||
| 599 | |||
| 600 | /* Get report descriptor */ | ||
| 601 | client_data->report_descr_done = false; | ||
| 602 | memset(&msg, 0, sizeof(struct hostif_msg)); | ||
| 603 | msg.hdr.command = HOSTIF_GET_REPORT_DESCRIPTOR; | ||
| 604 | msg.hdr.device_id = client_data->hid_devices[index].dev_id; | ||
| 605 | rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg, | ||
| 606 | sizeof(struct hostif_msg)); | ||
| 607 | if (rv) | ||
| 608 | return rv; | ||
| 609 | |||
| 610 | if (!client_data->report_descr_done) | ||
| 611 | wait_event_interruptible_timeout(client_data->init_wait, | ||
| 612 | client_data->report_descr_done, | ||
| 613 | 3 * HZ); | ||
| 614 | if (!client_data->report_descr_done) { | ||
| 615 | dev_err(&client_data->cl_device->dev, | ||
| 616 | "[hid-ish]: timed out for report descr\n"); | ||
| 617 | return -EIO; | ||
| 618 | } | ||
| 619 | if (!client_data->report_descr[index]) { | ||
| 620 | dev_err(&client_data->cl_device->dev, | ||
| 621 | "[hid-ish]: failed to alloc report descr\n"); | ||
| 622 | return -ENOMEM; | ||
| 623 | } | ||
| 624 | |||
| 625 | return 0; | ||
| 626 | } | ||
| 627 | |||
| 628 | /** | ||
| 629 | * hid_ishtp_cl_init() - Init function for ISHTP client | ||
| 630 | * @hid_ishtp_cl: ISHTP client instance | ||
| 631 | * @reset: true if called for init after reset | ||
| 632 | * | ||
| 633 | * This function complete the initializtion of the client. The summary of | ||
| 634 | * processing: | ||
| 635 | * - Send request to enumerate the hid clients | ||
| 636 | * Get the HID descriptor for each enumearated device | ||
| 637 | * Get report description of each device | ||
| 638 | * Register each device wik hid core by calling ishtp_hid_probe | ||
| 639 | * | ||
| 640 | * Return: 0 on success, non zero on error | ||
| 641 | */ | ||
| 642 | static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset) | ||
| 643 | { | ||
| 644 | struct ishtp_device *dev; | ||
| 645 | unsigned long flags; | ||
| 646 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 647 | int i; | ||
| 648 | int rv; | ||
| 649 | |||
| 650 | dev_dbg(&client_data->cl_device->dev, "%s\n", __func__); | ||
| 651 | hid_ishtp_trace(client_data, "%s reset flag: %d\n", __func__, reset); | ||
| 652 | |||
| 653 | rv = ishtp_cl_link(hid_ishtp_cl, ISHTP_HOST_CLIENT_ID_ANY); | ||
| 654 | if (rv) { | ||
| 655 | dev_err(&client_data->cl_device->dev, | ||
| 656 | "ishtp_cl_link failed\n"); | ||
| 657 | return -ENOMEM; | ||
| 658 | } | ||
| 659 | |||
| 660 | client_data->init_done = 0; | ||
| 661 | |||
| 662 | dev = hid_ishtp_cl->dev; | ||
| 663 | |||
| 664 | /* Connect to FW client */ | ||
| 665 | hid_ishtp_cl->rx_ring_size = HID_CL_RX_RING_SIZE; | ||
| 666 | hid_ishtp_cl->tx_ring_size = HID_CL_TX_RING_SIZE; | ||
| 667 | |||
| 668 | spin_lock_irqsave(&dev->fw_clients_lock, flags); | ||
| 669 | i = ishtp_fw_cl_by_uuid(dev, &hid_ishtp_guid); | ||
| 670 | if (i < 0) { | ||
| 671 | spin_unlock_irqrestore(&dev->fw_clients_lock, flags); | ||
| 672 | dev_err(&client_data->cl_device->dev, | ||
| 673 | "ish client uuid not found\n"); | ||
| 674 | return i; | ||
| 675 | } | ||
| 676 | hid_ishtp_cl->fw_client_id = dev->fw_clients[i].client_id; | ||
| 677 | spin_unlock_irqrestore(&dev->fw_clients_lock, flags); | ||
| 678 | hid_ishtp_cl->state = ISHTP_CL_CONNECTING; | ||
| 679 | |||
| 680 | rv = ishtp_cl_connect(hid_ishtp_cl); | ||
| 681 | if (rv) { | ||
| 682 | dev_err(&client_data->cl_device->dev, | ||
| 683 | "client connect fail\n"); | ||
| 684 | goto err_cl_unlink; | ||
| 685 | } | ||
| 686 | |||
| 687 | hid_ishtp_trace(client_data, "%s client connected\n", __func__); | ||
| 688 | |||
| 689 | /* Register read callback */ | ||
| 690 | ishtp_register_event_cb(hid_ishtp_cl->device, ish_cl_event_cb); | ||
| 691 | |||
| 692 | rv = ishtp_enum_enum_devices(hid_ishtp_cl); | ||
| 693 | if (rv) | ||
| 694 | goto err_cl_disconnect; | ||
| 695 | |||
| 696 | hid_ishtp_trace(client_data, "%s enumerated device count %d\n", | ||
| 697 | __func__, client_data->num_hid_devices); | ||
| 698 | |||
| 699 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
| 700 | client_data->cur_hid_dev = i; | ||
| 701 | |||
| 702 | rv = ishtp_get_hid_descriptor(hid_ishtp_cl, i); | ||
| 703 | if (rv) | ||
| 704 | goto err_cl_disconnect; | ||
| 705 | |||
| 706 | rv = ishtp_get_report_descriptor(hid_ishtp_cl, i); | ||
| 707 | if (rv) | ||
| 708 | goto err_cl_disconnect; | ||
| 709 | |||
| 710 | if (!reset) { | ||
| 711 | rv = ishtp_hid_probe(i, client_data); | ||
| 712 | if (rv) { | ||
| 713 | dev_err(&client_data->cl_device->dev, | ||
| 714 | "[hid-ish]: HID probe for #%u failed: %d\n", | ||
| 715 | i, rv); | ||
| 716 | goto err_cl_disconnect; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | } /* for() on all hid devices */ | ||
| 720 | |||
| 721 | client_data->init_done = 1; | ||
| 722 | client_data->suspended = false; | ||
| 723 | wake_up_interruptible(&client_data->ishtp_resume_wait); | ||
| 724 | hid_ishtp_trace(client_data, "%s successful init\n", __func__); | ||
| 725 | return 0; | ||
| 726 | |||
| 727 | err_cl_disconnect: | ||
| 728 | hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING; | ||
| 729 | ishtp_cl_disconnect(hid_ishtp_cl); | ||
| 730 | err_cl_unlink: | ||
| 731 | ishtp_cl_unlink(hid_ishtp_cl); | ||
| 732 | return rv; | ||
| 733 | } | ||
| 734 | |||
| 735 | /** | ||
| 736 | * hid_ishtp_cl_deinit() - Deinit function for ISHTP client | ||
| 737 | * @hid_ishtp_cl: ISHTP client instance | ||
| 738 | * | ||
| 739 | * Unlink and free hid client | ||
| 740 | */ | ||
| 741 | static void hid_ishtp_cl_deinit(struct ishtp_cl *hid_ishtp_cl) | ||
| 742 | { | ||
| 743 | ishtp_cl_unlink(hid_ishtp_cl); | ||
| 744 | ishtp_cl_flush_queues(hid_ishtp_cl); | ||
| 745 | |||
| 746 | /* disband and free all Tx and Rx client-level rings */ | ||
| 747 | ishtp_cl_free(hid_ishtp_cl); | ||
| 748 | } | ||
| 749 | |||
| 750 | static void hid_ishtp_cl_reset_handler(struct work_struct *work) | ||
| 751 | { | ||
| 752 | struct ishtp_cl_data *client_data; | ||
| 753 | struct ishtp_cl *hid_ishtp_cl; | ||
| 754 | struct ishtp_cl_device *cl_device; | ||
| 755 | int retry; | ||
| 756 | int rv; | ||
| 757 | |||
| 758 | client_data = container_of(work, struct ishtp_cl_data, work); | ||
| 759 | |||
| 760 | hid_ishtp_cl = client_data->hid_ishtp_cl; | ||
| 761 | cl_device = client_data->cl_device; | ||
| 762 | |||
| 763 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
| 764 | hid_ishtp_cl); | ||
| 765 | dev_dbg(&cl_device->dev, "%s\n", __func__); | ||
| 766 | |||
| 767 | hid_ishtp_cl_deinit(hid_ishtp_cl); | ||
| 768 | |||
| 769 | hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev); | ||
| 770 | if (!hid_ishtp_cl) | ||
| 771 | return; | ||
| 772 | |||
| 773 | cl_device->driver_data = hid_ishtp_cl; | ||
| 774 | hid_ishtp_cl->client_data = client_data; | ||
| 775 | client_data->hid_ishtp_cl = hid_ishtp_cl; | ||
| 776 | |||
| 777 | client_data->num_hid_devices = 0; | ||
| 778 | |||
| 779 | for (retry = 0; retry < 3; ++retry) { | ||
| 780 | rv = hid_ishtp_cl_init(hid_ishtp_cl, 1); | ||
| 781 | if (!rv) | ||
| 782 | break; | ||
| 783 | dev_err(&client_data->cl_device->dev, "Retry reset init\n"); | ||
| 784 | } | ||
| 785 | if (rv) { | ||
| 786 | dev_err(&client_data->cl_device->dev, "Reset Failed\n"); | ||
| 787 | hid_ishtp_trace(client_data, "%s Failed hid_ishtp_cl %p\n", | ||
| 788 | __func__, hid_ishtp_cl); | ||
| 789 | } | ||
| 790 | } | ||
| 791 | |||
| 792 | /** | ||
| 793 | * hid_ishtp_cl_probe() - ISHTP client driver probe | ||
| 794 | * @cl_device: ISHTP client device instance | ||
| 795 | * | ||
| 796 | * This function gets called on device create on ISHTP bus | ||
| 797 | * | ||
| 798 | * Return: 0 on success, non zero on error | ||
| 799 | */ | ||
| 800 | static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device) | ||
| 801 | { | ||
| 802 | struct ishtp_cl *hid_ishtp_cl; | ||
| 803 | struct ishtp_cl_data *client_data; | ||
| 804 | int rv; | ||
| 805 | |||
| 806 | if (!cl_device) | ||
| 807 | return -ENODEV; | ||
| 808 | |||
| 809 | if (uuid_le_cmp(hid_ishtp_guid, | ||
| 810 | cl_device->fw_client->props.protocol_name) != 0) | ||
| 811 | return -ENODEV; | ||
| 812 | |||
| 813 | client_data = devm_kzalloc(&cl_device->dev, sizeof(*client_data), | ||
| 814 | GFP_KERNEL); | ||
| 815 | if (!client_data) | ||
| 816 | return -ENOMEM; | ||
| 817 | |||
| 818 | hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev); | ||
| 819 | if (!hid_ishtp_cl) | ||
| 820 | return -ENOMEM; | ||
| 821 | |||
| 822 | cl_device->driver_data = hid_ishtp_cl; | ||
| 823 | hid_ishtp_cl->client_data = client_data; | ||
| 824 | client_data->hid_ishtp_cl = hid_ishtp_cl; | ||
| 825 | client_data->cl_device = cl_device; | ||
| 826 | |||
| 827 | init_waitqueue_head(&client_data->init_wait); | ||
| 828 | init_waitqueue_head(&client_data->ishtp_resume_wait); | ||
| 829 | |||
| 830 | INIT_WORK(&client_data->work, hid_ishtp_cl_reset_handler); | ||
| 831 | |||
| 832 | rv = hid_ishtp_cl_init(hid_ishtp_cl, 0); | ||
| 833 | if (rv) { | ||
| 834 | ishtp_cl_free(hid_ishtp_cl); | ||
| 835 | return rv; | ||
| 836 | } | ||
| 837 | ishtp_get_device(cl_device); | ||
| 838 | |||
| 839 | return 0; | ||
| 840 | } | ||
| 841 | |||
| 842 | /** | ||
| 843 | * hid_ishtp_cl_remove() - ISHTP client driver remove | ||
| 844 | * @cl_device: ISHTP client device instance | ||
| 845 | * | ||
| 846 | * This function gets called on device remove on ISHTP bus | ||
| 847 | * | ||
| 848 | * Return: 0 | ||
| 849 | */ | ||
| 850 | static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device) | ||
| 851 | { | ||
| 852 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
| 853 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 854 | |||
| 855 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
| 856 | hid_ishtp_cl); | ||
| 857 | |||
| 858 | dev_dbg(&cl_device->dev, "%s\n", __func__); | ||
| 859 | hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING; | ||
| 860 | ishtp_cl_disconnect(hid_ishtp_cl); | ||
| 861 | ishtp_put_device(cl_device); | ||
| 862 | ishtp_hid_remove(client_data); | ||
| 863 | hid_ishtp_cl_deinit(hid_ishtp_cl); | ||
| 864 | |||
| 865 | hid_ishtp_cl = NULL; | ||
| 866 | |||
| 867 | client_data->num_hid_devices = 0; | ||
| 868 | |||
| 869 | return 0; | ||
| 870 | } | ||
| 871 | |||
| 872 | /** | ||
| 873 | * hid_ishtp_cl_reset() - ISHTP client driver reset | ||
| 874 | * @cl_device: ISHTP client device instance | ||
| 875 | * | ||
| 876 | * This function gets called on device reset on ISHTP bus | ||
| 877 | * | ||
| 878 | * Return: 0 | ||
| 879 | */ | ||
| 880 | static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device) | ||
| 881 | { | ||
| 882 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
| 883 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 884 | |||
| 885 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
| 886 | hid_ishtp_cl); | ||
| 887 | |||
| 888 | schedule_work(&client_data->work); | ||
| 889 | |||
| 890 | return 0; | ||
| 891 | } | ||
| 892 | |||
| 893 | #define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev) | ||
| 894 | |||
| 895 | /** | ||
| 896 | * hid_ishtp_cl_suspend() - ISHTP client driver suspend | ||
| 897 | * @device: device instance | ||
| 898 | * | ||
| 899 | * This function gets called on system suspend | ||
| 900 | * | ||
| 901 | * Return: 0 | ||
| 902 | */ | ||
| 903 | static int hid_ishtp_cl_suspend(struct device *device) | ||
| 904 | { | ||
| 905 | struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device); | ||
| 906 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
| 907 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 908 | |||
| 909 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
| 910 | hid_ishtp_cl); | ||
| 911 | client_data->suspended = true; | ||
| 912 | |||
| 913 | return 0; | ||
| 914 | } | ||
| 915 | |||
| 916 | /** | ||
| 917 | * hid_ishtp_cl_resume() - ISHTP client driver resume | ||
| 918 | * @device: device instance | ||
| 919 | * | ||
| 920 | * This function gets called on system resume | ||
| 921 | * | ||
| 922 | * Return: 0 | ||
| 923 | */ | ||
| 924 | static int hid_ishtp_cl_resume(struct device *device) | ||
| 925 | { | ||
| 926 | struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device); | ||
| 927 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
| 928 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
| 929 | |||
| 930 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
| 931 | hid_ishtp_cl); | ||
| 932 | client_data->suspended = false; | ||
| 933 | return 0; | ||
| 934 | } | ||
| 935 | |||
| 936 | static const struct dev_pm_ops hid_ishtp_pm_ops = { | ||
| 937 | .suspend = hid_ishtp_cl_suspend, | ||
| 938 | .resume = hid_ishtp_cl_resume, | ||
| 939 | }; | ||
| 940 | |||
| 941 | static struct ishtp_cl_driver hid_ishtp_cl_driver = { | ||
| 942 | .name = "ish-hid", | ||
| 943 | .probe = hid_ishtp_cl_probe, | ||
| 944 | .remove = hid_ishtp_cl_remove, | ||
| 945 | .reset = hid_ishtp_cl_reset, | ||
| 946 | .driver.pm = &hid_ishtp_pm_ops, | ||
| 947 | }; | ||
| 948 | |||
| 949 | static int __init ish_hid_init(void) | ||
| 950 | { | ||
| 951 | int rv; | ||
| 952 | |||
| 953 | /* Register ISHTP client device driver with ISHTP Bus */ | ||
| 954 | rv = ishtp_cl_driver_register(&hid_ishtp_cl_driver); | ||
| 955 | |||
| 956 | return rv; | ||
| 957 | |||
| 958 | } | ||
| 959 | |||
| 960 | static void __exit ish_hid_exit(void) | ||
| 961 | { | ||
| 962 | ishtp_cl_driver_unregister(&hid_ishtp_cl_driver); | ||
| 963 | } | ||
| 964 | |||
| 965 | late_initcall(ish_hid_init); | ||
| 966 | module_exit(ish_hid_exit); | ||
| 967 | |||
| 968 | MODULE_DESCRIPTION("ISH ISHTP HID client driver"); | ||
| 969 | /* Primary author */ | ||
| 970 | MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>"); | ||
| 971 | /* | ||
| 972 | * Several modification for multi instance support | ||
| 973 | * suspend/resume and clean up | ||
| 974 | */ | ||
| 975 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
| 976 | |||
| 977 | MODULE_LICENSE("GPL"); | ||
| 978 | MODULE_ALIAS("ishtp:*"); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c new file mode 100644 index 000000000000..277983aa1d90 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp-hid.c | |||
| @@ -0,0 +1,246 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP-HID glue driver. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/hid.h> | ||
| 17 | #include <uapi/linux/input.h> | ||
| 18 | #include "ishtp/client.h" | ||
| 19 | #include "ishtp-hid.h" | ||
| 20 | |||
| 21 | /** | ||
| 22 | * ishtp_hid_parse() - hid-core .parse() callback | ||
| 23 | * @hid: hid device instance | ||
| 24 | * | ||
| 25 | * This function gets called during call to hid_add_device | ||
| 26 | * | ||
| 27 | * Return: 0 on success and non zero on error | ||
| 28 | */ | ||
| 29 | static int ishtp_hid_parse(struct hid_device *hid) | ||
| 30 | { | ||
| 31 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
| 32 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
| 33 | int rv; | ||
| 34 | |||
| 35 | rv = hid_parse_report(hid, client_data->report_descr[hid_data->index], | ||
| 36 | client_data->report_descr_size[hid_data->index]); | ||
| 37 | if (rv) | ||
| 38 | return rv; | ||
| 39 | |||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Empty callbacks with success return code */ | ||
| 44 | static int ishtp_hid_start(struct hid_device *hid) | ||
| 45 | { | ||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | static void ishtp_hid_stop(struct hid_device *hid) | ||
| 50 | { | ||
| 51 | } | ||
| 52 | |||
| 53 | static int ishtp_hid_open(struct hid_device *hid) | ||
| 54 | { | ||
| 55 | return 0; | ||
| 56 | } | ||
| 57 | |||
| 58 | static void ishtp_hid_close(struct hid_device *hid) | ||
| 59 | { | ||
| 60 | } | ||
| 61 | |||
| 62 | static int ishtp_raw_request(struct hid_device *hdev, unsigned char reportnum, | ||
| 63 | __u8 *buf, size_t len, unsigned char rtype, int reqtype) | ||
| 64 | { | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * ishtp_hid_request() - hid-core .request() callback | ||
| 70 | * @hid: hid device instance | ||
| 71 | * @rep: pointer to hid_report | ||
| 72 | * @reqtype: type of req. [GET|SET]_REPORT | ||
| 73 | * | ||
| 74 | * This function is used to set/get feaure/input report. | ||
| 75 | */ | ||
| 76 | static void ishtp_hid_request(struct hid_device *hid, struct hid_report *rep, | ||
| 77 | int reqtype) | ||
| 78 | { | ||
| 79 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
| 80 | /* the specific report length, just HID part of it */ | ||
| 81 | unsigned int len = ((rep->size - 1) >> 3) + 1 + (rep->id > 0); | ||
| 82 | char *buf; | ||
| 83 | unsigned int header_size = sizeof(struct hostif_msg); | ||
| 84 | |||
| 85 | len += header_size; | ||
| 86 | |||
| 87 | hid_data->request_done = false; | ||
| 88 | switch (reqtype) { | ||
| 89 | case HID_REQ_GET_REPORT: | ||
| 90 | hid_ishtp_get_report(hid, rep->id, rep->type); | ||
| 91 | break; | ||
| 92 | case HID_REQ_SET_REPORT: | ||
| 93 | /* | ||
| 94 | * Spare 7 bytes for 64b accesses through | ||
| 95 | * get/put_unaligned_le64() | ||
| 96 | */ | ||
| 97 | buf = kzalloc(len + 7, GFP_KERNEL); | ||
| 98 | if (!buf) | ||
| 99 | return; | ||
| 100 | |||
| 101 | hid_output_report(rep, buf + header_size); | ||
| 102 | hid_ishtp_set_feature(hid, buf, len, rep->id); | ||
| 103 | kfree(buf); | ||
| 104 | break; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | /** | ||
| 109 | * ishtp_wait_for_response() - hid-core .wait() callback | ||
| 110 | * @hid: hid device instance | ||
| 111 | * | ||
| 112 | * This function is used to wait after get feaure/input report. | ||
| 113 | * | ||
| 114 | * Return: 0 on success and non zero on error | ||
| 115 | */ | ||
| 116 | static int ishtp_wait_for_response(struct hid_device *hid) | ||
| 117 | { | ||
| 118 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
| 119 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
| 120 | int rv; | ||
| 121 | |||
| 122 | hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid); | ||
| 123 | |||
| 124 | rv = ishtp_hid_link_ready_wait(hid_data->client_data); | ||
| 125 | if (rv) | ||
| 126 | return rv; | ||
| 127 | |||
| 128 | if (!hid_data->request_done) | ||
| 129 | wait_event_interruptible_timeout(hid_data->hid_wait, | ||
| 130 | hid_data->request_done, 3 * HZ); | ||
| 131 | |||
| 132 | if (!hid_data->request_done) { | ||
| 133 | hid_err(hid, | ||
| 134 | "timeout waiting for response from ISHTP device\n"); | ||
| 135 | return -ETIMEDOUT; | ||
| 136 | } | ||
| 137 | hid_ishtp_trace(client_data, "%s hid %p done\n", __func__, hid); | ||
| 138 | |||
| 139 | hid_data->request_done = false; | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * ishtp_hid_wakeup() - Wakeup caller | ||
| 146 | * @hid: hid device instance | ||
| 147 | * | ||
| 148 | * This function will wakeup caller waiting for Get/Set feature report | ||
| 149 | */ | ||
| 150 | void ishtp_hid_wakeup(struct hid_device *hid) | ||
| 151 | { | ||
| 152 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
| 153 | |||
| 154 | hid_data->request_done = true; | ||
| 155 | wake_up_interruptible(&hid_data->hid_wait); | ||
| 156 | } | ||
| 157 | |||
| 158 | static struct hid_ll_driver ishtp_hid_ll_driver = { | ||
| 159 | .parse = ishtp_hid_parse, | ||
| 160 | .start = ishtp_hid_start, | ||
| 161 | .stop = ishtp_hid_stop, | ||
| 162 | .open = ishtp_hid_open, | ||
| 163 | .close = ishtp_hid_close, | ||
| 164 | .request = ishtp_hid_request, | ||
| 165 | .wait = ishtp_wait_for_response, | ||
| 166 | .raw_request = ishtp_raw_request | ||
| 167 | }; | ||
| 168 | |||
| 169 | /** | ||
| 170 | * ishtp_hid_probe() - hid register ll driver | ||
| 171 | * @cur_hid_dev: Index of hid device calling to register | ||
| 172 | * @client_data: Client data pointer | ||
| 173 | * | ||
| 174 | * This function is used to allocate and add HID device. | ||
| 175 | * | ||
| 176 | * Return: 0 on success, non zero on error | ||
| 177 | */ | ||
| 178 | int ishtp_hid_probe(unsigned int cur_hid_dev, | ||
| 179 | struct ishtp_cl_data *client_data) | ||
| 180 | { | ||
| 181 | int rv; | ||
| 182 | struct hid_device *hid; | ||
| 183 | struct ishtp_hid_data *hid_data; | ||
| 184 | |||
| 185 | hid = hid_allocate_device(); | ||
| 186 | if (IS_ERR(hid)) { | ||
| 187 | rv = PTR_ERR(hid); | ||
| 188 | return -ENOMEM; | ||
| 189 | } | ||
| 190 | |||
| 191 | hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL); | ||
| 192 | if (!hid_data) { | ||
| 193 | rv = -ENOMEM; | ||
| 194 | goto err_hid_data; | ||
| 195 | } | ||
| 196 | |||
| 197 | hid_data->index = cur_hid_dev; | ||
| 198 | hid_data->client_data = client_data; | ||
| 199 | init_waitqueue_head(&hid_data->hid_wait); | ||
| 200 | |||
| 201 | hid->driver_data = hid_data; | ||
| 202 | |||
| 203 | client_data->hid_sensor_hubs[cur_hid_dev] = hid; | ||
| 204 | |||
| 205 | hid->ll_driver = &ishtp_hid_ll_driver; | ||
| 206 | hid->bus = BUS_INTEL_ISHTP; | ||
| 207 | hid->dev.parent = &client_data->cl_device->dev; | ||
| 208 | hid->version = le16_to_cpu(ISH_HID_VERSION); | ||
| 209 | hid->vendor = le16_to_cpu(ISH_HID_VENDOR); | ||
| 210 | hid->product = le16_to_cpu(ISH_HID_PRODUCT); | ||
| 211 | snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", "hid-ishtp", | ||
| 212 | hid->vendor, hid->product); | ||
| 213 | |||
| 214 | rv = hid_add_device(hid); | ||
| 215 | if (rv) | ||
| 216 | goto err_hid_device; | ||
| 217 | |||
| 218 | hid_ishtp_trace(client_data, "%s allocated hid %p\n", __func__, hid); | ||
| 219 | |||
| 220 | return 0; | ||
| 221 | |||
| 222 | err_hid_device: | ||
| 223 | kfree(hid_data); | ||
| 224 | err_hid_data: | ||
| 225 | kfree(hid); | ||
| 226 | return rv; | ||
| 227 | } | ||
| 228 | |||
| 229 | /** | ||
| 230 | * ishtp_hid_probe() - Remove registered hid device | ||
| 231 | * @client_data: client data pointer | ||
| 232 | * | ||
| 233 | * This function is used to destroy allocatd HID device. | ||
| 234 | */ | ||
| 235 | void ishtp_hid_remove(struct ishtp_cl_data *client_data) | ||
| 236 | { | ||
| 237 | int i; | ||
| 238 | |||
| 239 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
| 240 | if (client_data->hid_sensor_hubs[i]) { | ||
| 241 | kfree(client_data->hid_sensor_hubs[i]->driver_data); | ||
| 242 | hid_destroy_device(client_data->hid_sensor_hubs[i]); | ||
| 243 | client_data->hid_sensor_hubs[i] = NULL; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.h b/drivers/hid/intel-ish-hid/ishtp-hid.h new file mode 100644 index 000000000000..f5c7eb79b7b5 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp-hid.h | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP-HID glue driver's definitions. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | #ifndef ISHTP_HID__H | ||
| 16 | #define ISHTP_HID__H | ||
| 17 | |||
| 18 | /* The fixed ISH product and vendor id */ | ||
| 19 | #define ISH_HID_VENDOR 0x8086 | ||
| 20 | #define ISH_HID_PRODUCT 0x22D8 | ||
| 21 | #define ISH_HID_VERSION 0x0200 | ||
| 22 | |||
| 23 | #define CMD_MASK 0x7F | ||
| 24 | #define IS_RESPONSE 0x80 | ||
| 25 | |||
| 26 | /* Used to dump to Linux trace buffer, if enabled */ | ||
| 27 | #define hid_ishtp_trace(client, ...) \ | ||
| 28 | client->cl_device->ishtp_dev->print_log(\ | ||
| 29 | client->cl_device->ishtp_dev, __VA_ARGS__) | ||
| 30 | |||
| 31 | /* ISH Transport protocol (ISHTP in short) GUID */ | ||
| 32 | static const uuid_le hid_ishtp_guid = UUID_LE(0x33AECD58, 0xB679, 0x4E54, | ||
| 33 | 0x9B, 0xD9, 0xA0, 0x4D, 0x34, | ||
| 34 | 0xF0, 0xC2, 0x26); | ||
| 35 | |||
| 36 | /* ISH HID message structure */ | ||
| 37 | struct hostif_msg_hdr { | ||
| 38 | uint8_t command; /* Bit 7: is_response */ | ||
| 39 | uint8_t device_id; | ||
| 40 | uint8_t status; | ||
| 41 | uint8_t flags; | ||
| 42 | uint16_t size; | ||
| 43 | } __packed; | ||
| 44 | |||
| 45 | struct hostif_msg { | ||
| 46 | struct hostif_msg_hdr hdr; | ||
| 47 | } __packed; | ||
| 48 | |||
| 49 | struct hostif_msg_to_sensor { | ||
| 50 | struct hostif_msg_hdr hdr; | ||
| 51 | uint8_t report_id; | ||
| 52 | } __packed; | ||
| 53 | |||
| 54 | struct device_info { | ||
| 55 | uint32_t dev_id; | ||
| 56 | uint8_t dev_class; | ||
| 57 | uint16_t pid; | ||
| 58 | uint16_t vid; | ||
| 59 | } __packed; | ||
| 60 | |||
| 61 | struct ishtp_version { | ||
| 62 | uint8_t major; | ||
| 63 | uint8_t minor; | ||
| 64 | uint8_t hotfix; | ||
| 65 | uint16_t build; | ||
| 66 | } __packed; | ||
| 67 | |||
| 68 | /* struct for ISHTP aggregated input data */ | ||
| 69 | struct report_list { | ||
| 70 | uint16_t total_size; | ||
| 71 | uint8_t num_of_reports; | ||
| 72 | uint8_t flags; | ||
| 73 | struct { | ||
| 74 | uint16_t size_of_report; | ||
| 75 | uint8_t report[1]; | ||
| 76 | } __packed reports[1]; | ||
| 77 | } __packed; | ||
| 78 | |||
| 79 | /* HOSTIF commands */ | ||
| 80 | #define HOSTIF_HID_COMMAND_BASE 0 | ||
| 81 | #define HOSTIF_GET_HID_DESCRIPTOR 0 | ||
| 82 | #define HOSTIF_GET_REPORT_DESCRIPTOR 1 | ||
| 83 | #define HOSTIF_GET_FEATURE_REPORT 2 | ||
| 84 | #define HOSTIF_SET_FEATURE_REPORT 3 | ||
| 85 | #define HOSTIF_GET_INPUT_REPORT 4 | ||
| 86 | #define HOSTIF_PUBLISH_INPUT_REPORT 5 | ||
| 87 | #define HOSTIF_PUBLISH_INPUT_REPORT_LIST 6 | ||
| 88 | #define HOSTIF_DM_COMMAND_BASE 32 | ||
| 89 | #define HOSTIF_DM_ENUM_DEVICES 33 | ||
| 90 | #define HOSTIF_DM_ADD_DEVICE 34 | ||
| 91 | |||
| 92 | #define MAX_HID_DEVICES 32 | ||
| 93 | |||
| 94 | /** | ||
| 95 | * struct ishtp_cl_data - Encapsulate per ISH TP HID Client | ||
| 96 | * @enum_device_done: Enum devices response complete flag | ||
| 97 | * @hid_descr_done: HID descriptor complete flag | ||
| 98 | * @report_descr_done: Get report descriptor complete flag | ||
| 99 | * @init_done: Init process completed successfully | ||
| 100 | * @suspended: System is under suspend state or in progress | ||
| 101 | * @num_hid_devices: Number of HID devices enumerated in this client | ||
| 102 | * @cur_hid_dev: This keeps track of the device index for which | ||
| 103 | * initialization and registration with HID core | ||
| 104 | * in progress. | ||
| 105 | * @hid_devices: Store vid/pid/devid for each enumerated HID device | ||
| 106 | * @report_descr: Stores the raw report descriptors for each HID device | ||
| 107 | * @report_descr_size: Report description of size of above repo_descr[] | ||
| 108 | * @hid_sensor_hubs: Pointer to hid_device for all HID device, so that | ||
| 109 | * when clients are removed, they can be freed | ||
| 110 | * @hid_descr: Pointer to hid descriptor for each enumerated hid | ||
| 111 | * device | ||
| 112 | * @hid_descr_size: Size of each above report descriptor | ||
| 113 | * @init_wait: Wait queue to wait during initialization, where the | ||
| 114 | * client send message to ISH FW and wait for response | ||
| 115 | * @ishtp_hid_wait: The wait for get report during wait callback from hid | ||
| 116 | * core | ||
| 117 | * @bad_recv_cnt: Running count of packets received with error | ||
| 118 | * @multi_packet_cnt: Count of fragmented packet count | ||
| 119 | * | ||
| 120 | * This structure is used to store completion flags and per client data like | ||
| 121 | * like report description, number of HID devices etc. | ||
| 122 | */ | ||
| 123 | struct ishtp_cl_data { | ||
| 124 | /* completion flags */ | ||
| 125 | bool enum_devices_done; | ||
| 126 | bool hid_descr_done; | ||
| 127 | bool report_descr_done; | ||
| 128 | bool init_done; | ||
| 129 | bool suspended; | ||
| 130 | |||
| 131 | unsigned int num_hid_devices; | ||
| 132 | unsigned int cur_hid_dev; | ||
| 133 | unsigned int hid_dev_count; | ||
| 134 | |||
| 135 | struct device_info *hid_devices; | ||
| 136 | unsigned char *report_descr[MAX_HID_DEVICES]; | ||
| 137 | int report_descr_size[MAX_HID_DEVICES]; | ||
| 138 | struct hid_device *hid_sensor_hubs[MAX_HID_DEVICES]; | ||
| 139 | unsigned char *hid_descr[MAX_HID_DEVICES]; | ||
| 140 | int hid_descr_size[MAX_HID_DEVICES]; | ||
| 141 | |||
| 142 | wait_queue_head_t init_wait; | ||
| 143 | wait_queue_head_t ishtp_resume_wait; | ||
| 144 | struct ishtp_cl *hid_ishtp_cl; | ||
| 145 | |||
| 146 | /* Statistics */ | ||
| 147 | unsigned int bad_recv_cnt; | ||
| 148 | int multi_packet_cnt; | ||
| 149 | |||
| 150 | struct work_struct work; | ||
| 151 | struct ishtp_cl_device *cl_device; | ||
| 152 | }; | ||
| 153 | |||
| 154 | /** | ||
| 155 | * struct ishtp_hid_data - Per instance HID data | ||
| 156 | * @index: Device index in the order of enumeration | ||
| 157 | * @request_done: Get Feature/Input report complete flag | ||
| 158 | * used during get/set request from hid core | ||
| 159 | * @client_data: Link to the client instance | ||
| 160 | * @hid_wait: Completion waitq | ||
| 161 | * | ||
| 162 | * Used to tie hid hid->driver data to driver client instance | ||
| 163 | */ | ||
| 164 | struct ishtp_hid_data { | ||
| 165 | int index; | ||
| 166 | bool request_done; | ||
| 167 | struct ishtp_cl_data *client_data; | ||
| 168 | wait_queue_head_t hid_wait; | ||
| 169 | }; | ||
| 170 | |||
| 171 | /* Interface functions between HID LL driver and ISH TP client */ | ||
| 172 | void hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len, | ||
| 173 | int report_id); | ||
| 174 | void hid_ishtp_get_report(struct hid_device *hid, int report_id, | ||
| 175 | int report_type); | ||
| 176 | int ishtp_hid_probe(unsigned int cur_hid_dev, | ||
| 177 | struct ishtp_cl_data *client_data); | ||
| 178 | void ishtp_hid_remove(struct ishtp_cl_data *client_data); | ||
| 179 | int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data); | ||
| 180 | void ishtp_hid_wakeup(struct hid_device *hid); | ||
| 181 | |||
| 182 | #endif /* ISHTP_HID__H */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c new file mode 100644 index 000000000000..256521509d20 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/bus.c | |||
| @@ -0,0 +1,788 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP bus driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/init.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/sched.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include "bus.h" | ||
| 23 | #include "ishtp-dev.h" | ||
| 24 | #include "client.h" | ||
| 25 | #include "hbm.h" | ||
| 26 | |||
| 27 | static int ishtp_use_dma; | ||
| 28 | module_param_named(ishtp_use_dma, ishtp_use_dma, int, 0600); | ||
| 29 | MODULE_PARM_DESC(ishtp_use_dma, "Use DMA to send messages"); | ||
| 30 | |||
| 31 | #define to_ishtp_cl_driver(d) container_of(d, struct ishtp_cl_driver, driver) | ||
| 32 | #define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev) | ||
| 33 | static bool ishtp_device_ready; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * ishtp_recv() - process ishtp message | ||
| 37 | * @dev: ishtp device | ||
| 38 | * | ||
| 39 | * If a message with valid header and size is received, then | ||
| 40 | * this function calls appropriate handler. The host or firmware | ||
| 41 | * address is zero, then they are host bus management message, | ||
| 42 | * otherwise they are message fo clients. | ||
| 43 | */ | ||
| 44 | void ishtp_recv(struct ishtp_device *dev) | ||
| 45 | { | ||
| 46 | uint32_t msg_hdr; | ||
| 47 | struct ishtp_msg_hdr *ishtp_hdr; | ||
| 48 | |||
| 49 | /* Read ISHTP header dword */ | ||
| 50 | msg_hdr = dev->ops->ishtp_read_hdr(dev); | ||
| 51 | if (!msg_hdr) | ||
| 52 | return; | ||
| 53 | |||
| 54 | dev->ops->sync_fw_clock(dev); | ||
| 55 | |||
| 56 | ishtp_hdr = (struct ishtp_msg_hdr *)&msg_hdr; | ||
| 57 | dev->ishtp_msg_hdr = msg_hdr; | ||
| 58 | |||
| 59 | /* Sanity check: ISHTP frag. length in header */ | ||
| 60 | if (ishtp_hdr->length > dev->mtu) { | ||
| 61 | dev_err(dev->devc, | ||
| 62 | "ISHTP hdr - bad length: %u; dropped [%08X]\n", | ||
| 63 | (unsigned int)ishtp_hdr->length, msg_hdr); | ||
| 64 | return; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* ISHTP bus message */ | ||
| 68 | if (!ishtp_hdr->host_addr && !ishtp_hdr->fw_addr) | ||
| 69 | recv_hbm(dev, ishtp_hdr); | ||
| 70 | /* ISHTP fixed-client message */ | ||
| 71 | else if (!ishtp_hdr->host_addr) | ||
| 72 | recv_fixed_cl_msg(dev, ishtp_hdr); | ||
| 73 | else | ||
| 74 | /* ISHTP client message */ | ||
| 75 | recv_ishtp_cl_msg(dev, ishtp_hdr); | ||
| 76 | } | ||
| 77 | EXPORT_SYMBOL(ishtp_recv); | ||
| 78 | |||
| 79 | /** | ||
| 80 | * ishtp_send_msg() - Send ishtp message | ||
| 81 | * @dev: ishtp device | ||
| 82 | * @hdr: Message header | ||
| 83 | * @msg: Message contents | ||
| 84 | * @ipc_send_compl: completion callback | ||
| 85 | * @ipc_send_compl_prm: completion callback parameter | ||
| 86 | * | ||
| 87 | * Send a multi fragment message via IPC. After sending the first fragment | ||
| 88 | * the completion callback is called to schedule transmit of next fragment. | ||
| 89 | * | ||
| 90 | * Return: This returns IPC send message status. | ||
| 91 | */ | ||
| 92 | int ishtp_send_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, | ||
| 93 | void *msg, void(*ipc_send_compl)(void *), | ||
| 94 | void *ipc_send_compl_prm) | ||
| 95 | { | ||
| 96 | unsigned char ipc_msg[IPC_FULL_MSG_SIZE]; | ||
| 97 | uint32_t drbl_val; | ||
| 98 | |||
| 99 | drbl_val = dev->ops->ipc_get_header(dev, hdr->length + | ||
| 100 | sizeof(struct ishtp_msg_hdr), | ||
| 101 | 1); | ||
| 102 | |||
| 103 | memcpy(ipc_msg, &drbl_val, sizeof(uint32_t)); | ||
| 104 | memcpy(ipc_msg + sizeof(uint32_t), hdr, sizeof(uint32_t)); | ||
| 105 | memcpy(ipc_msg + 2 * sizeof(uint32_t), msg, hdr->length); | ||
| 106 | return dev->ops->write(dev, ipc_send_compl, ipc_send_compl_prm, | ||
| 107 | ipc_msg, 2 * sizeof(uint32_t) + hdr->length); | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * ishtp_write_message() - Send ishtp single fragment message | ||
| 112 | * @dev: ishtp device | ||
| 113 | * @hdr: Message header | ||
| 114 | * @buf: message data | ||
| 115 | * | ||
| 116 | * Send a single fragment message via IPC. This returns IPC send message | ||
| 117 | * status. | ||
| 118 | * | ||
| 119 | * Return: This returns IPC send message status. | ||
| 120 | */ | ||
| 121 | int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, | ||
| 122 | unsigned char *buf) | ||
| 123 | { | ||
| 124 | return ishtp_send_msg(dev, hdr, buf, NULL, NULL); | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * ishtp_fw_cl_by_uuid() - locate index of fw client | ||
| 129 | * @dev: ishtp device | ||
| 130 | * @uuid: uuid of the client to search | ||
| 131 | * | ||
| 132 | * Search firmware client using UUID. | ||
| 133 | * | ||
| 134 | * Return: fw client index or -ENOENT if not found | ||
| 135 | */ | ||
| 136 | int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *uuid) | ||
| 137 | { | ||
| 138 | int i, res = -ENOENT; | ||
| 139 | |||
| 140 | for (i = 0; i < dev->fw_clients_num; ++i) { | ||
| 141 | if (uuid_le_cmp(*uuid, dev->fw_clients[i].props.protocol_name) | ||
| 142 | == 0) { | ||
| 143 | res = i; | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | return res; | ||
| 148 | } | ||
| 149 | EXPORT_SYMBOL(ishtp_fw_cl_by_uuid); | ||
| 150 | |||
| 151 | /** | ||
| 152 | * ishtp_fw_cl_by_id() - return index to fw_clients for client_id | ||
| 153 | * @dev: the ishtp device structure | ||
| 154 | * @client_id: fw client id to search | ||
| 155 | * | ||
| 156 | * Search firmware client using client id. | ||
| 157 | * | ||
| 158 | * Return: index on success, -ENOENT on failure. | ||
| 159 | */ | ||
| 160 | int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id) | ||
| 161 | { | ||
| 162 | int i, res = -ENOENT; | ||
| 163 | unsigned long flags; | ||
| 164 | |||
| 165 | spin_lock_irqsave(&dev->fw_clients_lock, flags); | ||
| 166 | for (i = 0; i < dev->fw_clients_num; i++) { | ||
| 167 | if (dev->fw_clients[i].client_id == client_id) { | ||
| 168 | res = i; | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | spin_unlock_irqrestore(&dev->fw_clients_lock, flags); | ||
| 173 | |||
| 174 | return res; | ||
| 175 | } | ||
| 176 | |||
| 177 | /** | ||
| 178 | * ishtp_cl_device_probe() - Bus probe() callback | ||
| 179 | * @dev: the device structure | ||
| 180 | * | ||
| 181 | * This is a bus probe callback and calls the drive probe function. | ||
| 182 | * | ||
| 183 | * Return: Return value from driver probe() call. | ||
| 184 | */ | ||
| 185 | static int ishtp_cl_device_probe(struct device *dev) | ||
| 186 | { | ||
| 187 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
| 188 | struct ishtp_cl_driver *driver; | ||
| 189 | |||
| 190 | if (!device) | ||
| 191 | return 0; | ||
| 192 | |||
| 193 | driver = to_ishtp_cl_driver(dev->driver); | ||
| 194 | if (!driver || !driver->probe) | ||
| 195 | return -ENODEV; | ||
| 196 | |||
| 197 | return driver->probe(device); | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * ishtp_cl_device_remove() - Bus remove() callback | ||
| 202 | * @dev: the device structure | ||
| 203 | * | ||
| 204 | * This is a bus remove callback and calls the drive remove function. | ||
| 205 | * Since the ISH driver model supports only built in, this is | ||
| 206 | * primarily can be called during pci driver init failure. | ||
| 207 | * | ||
| 208 | * Return: Return value from driver remove() call. | ||
| 209 | */ | ||
| 210 | static int ishtp_cl_device_remove(struct device *dev) | ||
| 211 | { | ||
| 212 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
| 213 | struct ishtp_cl_driver *driver; | ||
| 214 | |||
| 215 | if (!device || !dev->driver) | ||
| 216 | return 0; | ||
| 217 | |||
| 218 | if (device->event_cb) { | ||
| 219 | device->event_cb = NULL; | ||
| 220 | cancel_work_sync(&device->event_work); | ||
| 221 | } | ||
| 222 | |||
| 223 | driver = to_ishtp_cl_driver(dev->driver); | ||
| 224 | if (!driver->remove) { | ||
| 225 | dev->driver = NULL; | ||
| 226 | |||
| 227 | return 0; | ||
| 228 | } | ||
| 229 | |||
| 230 | return driver->remove(device); | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * ishtp_cl_device_suspend() - Bus suspend callback | ||
| 235 | * @dev: device | ||
| 236 | * | ||
| 237 | * Called during device suspend process. | ||
| 238 | * | ||
| 239 | * Return: Return value from driver suspend() call. | ||
| 240 | */ | ||
| 241 | static int ishtp_cl_device_suspend(struct device *dev) | ||
| 242 | { | ||
| 243 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
| 244 | struct ishtp_cl_driver *driver; | ||
| 245 | int ret = 0; | ||
| 246 | |||
| 247 | if (!device) | ||
| 248 | return 0; | ||
| 249 | |||
| 250 | driver = to_ishtp_cl_driver(dev->driver); | ||
| 251 | if (driver && driver->driver.pm) { | ||
| 252 | if (driver->driver.pm->suspend) | ||
| 253 | ret = driver->driver.pm->suspend(dev); | ||
| 254 | } | ||
| 255 | |||
| 256 | return ret; | ||
| 257 | } | ||
| 258 | |||
| 259 | /** | ||
| 260 | * ishtp_cl_device_resume() - Bus resume callback | ||
| 261 | * @dev: device | ||
| 262 | * | ||
| 263 | * Called during device resume process. | ||
| 264 | * | ||
| 265 | * Return: Return value from driver resume() call. | ||
| 266 | */ | ||
| 267 | static int ishtp_cl_device_resume(struct device *dev) | ||
| 268 | { | ||
| 269 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
| 270 | struct ishtp_cl_driver *driver; | ||
| 271 | int ret = 0; | ||
| 272 | |||
| 273 | if (!device) | ||
| 274 | return 0; | ||
| 275 | |||
| 276 | /* | ||
| 277 | * When ISH needs hard reset, it is done asynchrnously, hence bus | ||
| 278 | * resume will be called before full ISH resume | ||
| 279 | */ | ||
| 280 | if (device->ishtp_dev->resume_flag) | ||
| 281 | return 0; | ||
| 282 | |||
| 283 | driver = to_ishtp_cl_driver(dev->driver); | ||
| 284 | if (driver && driver->driver.pm) { | ||
| 285 | if (driver->driver.pm->resume) | ||
| 286 | ret = driver->driver.pm->resume(dev); | ||
| 287 | } | ||
| 288 | |||
| 289 | return ret; | ||
| 290 | } | ||
| 291 | |||
| 292 | /** | ||
| 293 | * ishtp_cl_device_reset() - Reset callback | ||
| 294 | * @device: ishtp client device instance | ||
| 295 | * | ||
| 296 | * This is a callback when HW reset is done and the device need | ||
| 297 | * reinit. | ||
| 298 | * | ||
| 299 | * Return: Return value from driver reset() call. | ||
| 300 | */ | ||
| 301 | static int ishtp_cl_device_reset(struct ishtp_cl_device *device) | ||
| 302 | { | ||
| 303 | struct ishtp_cl_driver *driver; | ||
| 304 | int ret = 0; | ||
| 305 | |||
| 306 | device->event_cb = NULL; | ||
| 307 | cancel_work_sync(&device->event_work); | ||
| 308 | |||
| 309 | driver = to_ishtp_cl_driver(device->dev.driver); | ||
| 310 | if (driver && driver->reset) | ||
| 311 | ret = driver->reset(device); | ||
| 312 | |||
| 313 | return ret; | ||
| 314 | } | ||
| 315 | |||
| 316 | static ssize_t modalias_show(struct device *dev, struct device_attribute *a, | ||
| 317 | char *buf) | ||
| 318 | { | ||
| 319 | int len; | ||
| 320 | |||
| 321 | len = snprintf(buf, PAGE_SIZE, "ishtp:%s\n", dev_name(dev)); | ||
| 322 | return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; | ||
| 323 | } | ||
| 324 | |||
| 325 | static struct device_attribute ishtp_cl_dev_attrs[] = { | ||
| 326 | __ATTR_RO(modalias), | ||
| 327 | __ATTR_NULL, | ||
| 328 | }; | ||
| 329 | |||
| 330 | static int ishtp_cl_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
| 331 | { | ||
| 332 | if (add_uevent_var(env, "MODALIAS=ishtp:%s", dev_name(dev))) | ||
| 333 | return -ENOMEM; | ||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | static const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = { | ||
| 338 | /* Suspend callbacks */ | ||
| 339 | .suspend = ishtp_cl_device_suspend, | ||
| 340 | .resume = ishtp_cl_device_resume, | ||
| 341 | /* Hibernate callbacks */ | ||
| 342 | .freeze = ishtp_cl_device_suspend, | ||
| 343 | .thaw = ishtp_cl_device_resume, | ||
| 344 | .restore = ishtp_cl_device_resume, | ||
| 345 | }; | ||
| 346 | |||
| 347 | static struct bus_type ishtp_cl_bus_type = { | ||
| 348 | .name = "ishtp", | ||
| 349 | .dev_attrs = ishtp_cl_dev_attrs, | ||
| 350 | .probe = ishtp_cl_device_probe, | ||
| 351 | .remove = ishtp_cl_device_remove, | ||
| 352 | .pm = &ishtp_cl_bus_dev_pm_ops, | ||
| 353 | .uevent = ishtp_cl_uevent, | ||
| 354 | }; | ||
| 355 | |||
| 356 | static void ishtp_cl_dev_release(struct device *dev) | ||
| 357 | { | ||
| 358 | kfree(to_ishtp_cl_device(dev)); | ||
| 359 | } | ||
| 360 | |||
| 361 | static struct device_type ishtp_cl_device_type = { | ||
| 362 | .release = ishtp_cl_dev_release, | ||
| 363 | }; | ||
| 364 | |||
| 365 | /** | ||
| 366 | * ishtp_bus_add_device() - Function to create device on bus | ||
| 367 | * @dev: ishtp device | ||
| 368 | * @uuid: uuid of the client | ||
| 369 | * @name: Name of the client | ||
| 370 | * | ||
| 371 | * Allocate ISHTP bus client device, attach it to uuid | ||
| 372 | * and register with ISHTP bus. | ||
| 373 | * | ||
| 374 | * Return: ishtp_cl_device pointer or NULL on failure | ||
| 375 | */ | ||
| 376 | static struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev, | ||
| 377 | uuid_le uuid, char *name) | ||
| 378 | { | ||
| 379 | struct ishtp_cl_device *device; | ||
| 380 | int status; | ||
| 381 | unsigned long flags; | ||
| 382 | |||
| 383 | spin_lock_irqsave(&dev->device_list_lock, flags); | ||
| 384 | list_for_each_entry(device, &dev->device_list, device_link) { | ||
| 385 | if (!strcmp(name, dev_name(&device->dev))) { | ||
| 386 | device->fw_client = &dev->fw_clients[ | ||
| 387 | dev->fw_client_presentation_num - 1]; | ||
| 388 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
| 389 | ishtp_cl_device_reset(device); | ||
| 390 | return device; | ||
| 391 | } | ||
| 392 | } | ||
| 393 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
| 394 | |||
| 395 | device = kzalloc(sizeof(struct ishtp_cl_device), GFP_KERNEL); | ||
| 396 | if (!device) | ||
| 397 | return NULL; | ||
| 398 | |||
| 399 | device->dev.parent = dev->devc; | ||
| 400 | device->dev.bus = &ishtp_cl_bus_type; | ||
| 401 | device->dev.type = &ishtp_cl_device_type; | ||
| 402 | device->ishtp_dev = dev; | ||
| 403 | |||
| 404 | device->fw_client = | ||
| 405 | &dev->fw_clients[dev->fw_client_presentation_num - 1]; | ||
| 406 | |||
| 407 | dev_set_name(&device->dev, "%s", name); | ||
| 408 | |||
| 409 | spin_lock_irqsave(&dev->device_list_lock, flags); | ||
| 410 | list_add_tail(&device->device_link, &dev->device_list); | ||
| 411 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
| 412 | |||
| 413 | status = device_register(&device->dev); | ||
| 414 | if (status) { | ||
| 415 | spin_lock_irqsave(&dev->device_list_lock, flags); | ||
| 416 | list_del(&device->device_link); | ||
| 417 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
| 418 | dev_err(dev->devc, "Failed to register ISHTP client device\n"); | ||
| 419 | kfree(device); | ||
| 420 | return NULL; | ||
| 421 | } | ||
| 422 | |||
| 423 | ishtp_device_ready = true; | ||
| 424 | |||
| 425 | return device; | ||
| 426 | } | ||
| 427 | |||
| 428 | /** | ||
| 429 | * ishtp_bus_remove_device() - Function to relase device on bus | ||
| 430 | * @device: client device instance | ||
| 431 | * | ||
| 432 | * This is a counterpart of ishtp_bus_add_device. | ||
| 433 | * Device is unregistered. | ||
| 434 | * the device structure is freed in 'ishtp_cl_dev_release' function | ||
| 435 | * Called only during error in pci driver init path. | ||
| 436 | */ | ||
| 437 | static void ishtp_bus_remove_device(struct ishtp_cl_device *device) | ||
| 438 | { | ||
| 439 | device_unregister(&device->dev); | ||
| 440 | } | ||
| 441 | |||
| 442 | /** | ||
| 443 | * __ishtp_cl_driver_register() - Client driver register | ||
| 444 | * @driver: the client driver instance | ||
| 445 | * @owner: Owner of this driver module | ||
| 446 | * | ||
| 447 | * Once a client driver is probed, it created a client | ||
| 448 | * instance and registers with the bus. | ||
| 449 | * | ||
| 450 | * Return: Return value of driver_register or -ENODEV if not ready | ||
| 451 | */ | ||
| 452 | int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver, | ||
| 453 | struct module *owner) | ||
| 454 | { | ||
| 455 | int err; | ||
| 456 | |||
| 457 | if (!ishtp_device_ready) | ||
| 458 | return -ENODEV; | ||
| 459 | |||
| 460 | driver->driver.name = driver->name; | ||
| 461 | driver->driver.owner = owner; | ||
| 462 | driver->driver.bus = &ishtp_cl_bus_type; | ||
| 463 | |||
| 464 | err = driver_register(&driver->driver); | ||
| 465 | if (err) | ||
| 466 | return err; | ||
| 467 | |||
| 468 | return 0; | ||
| 469 | } | ||
| 470 | EXPORT_SYMBOL(__ishtp_cl_driver_register); | ||
| 471 | |||
| 472 | /** | ||
| 473 | * ishtp_cl_driver_unregister() - Client driver unregister | ||
| 474 | * @driver: the client driver instance | ||
| 475 | * | ||
| 476 | * Unregister client during device removal process. | ||
| 477 | */ | ||
| 478 | void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver) | ||
| 479 | { | ||
| 480 | driver_unregister(&driver->driver); | ||
| 481 | } | ||
| 482 | EXPORT_SYMBOL(ishtp_cl_driver_unregister); | ||
| 483 | |||
| 484 | /** | ||
| 485 | * ishtp_bus_event_work() - event work function | ||
| 486 | * @work: work struct pointer | ||
| 487 | * | ||
| 488 | * Once an event is received for a client this work | ||
| 489 | * function is called. If the device has registered a | ||
| 490 | * callback then the callback is called. | ||
| 491 | */ | ||
| 492 | static void ishtp_bus_event_work(struct work_struct *work) | ||
| 493 | { | ||
| 494 | struct ishtp_cl_device *device; | ||
| 495 | |||
| 496 | device = container_of(work, struct ishtp_cl_device, event_work); | ||
| 497 | |||
| 498 | if (device->event_cb) | ||
| 499 | device->event_cb(device); | ||
| 500 | } | ||
| 501 | |||
| 502 | /** | ||
| 503 | * ishtp_cl_bus_rx_event() - schedule event work | ||
| 504 | * @device: client device instance | ||
| 505 | * | ||
| 506 | * Once an event is received for a client this schedules | ||
| 507 | * a work function to process. | ||
| 508 | */ | ||
| 509 | void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device) | ||
| 510 | { | ||
| 511 | if (!device || !device->event_cb) | ||
| 512 | return; | ||
| 513 | |||
| 514 | if (device->event_cb) | ||
| 515 | schedule_work(&device->event_work); | ||
| 516 | } | ||
| 517 | |||
| 518 | /** | ||
| 519 | * ishtp_register_event_cb() - Register callback | ||
| 520 | * @device: client device instance | ||
| 521 | * @event_cb: Event processor for an client | ||
| 522 | * | ||
| 523 | * Register a callback for events, called from client driver | ||
| 524 | * | ||
| 525 | * Return: Return 0 or -EALREADY if already registered | ||
| 526 | */ | ||
| 527 | int ishtp_register_event_cb(struct ishtp_cl_device *device, | ||
| 528 | void (*event_cb)(struct ishtp_cl_device *)) | ||
| 529 | { | ||
| 530 | if (device->event_cb) | ||
| 531 | return -EALREADY; | ||
| 532 | |||
| 533 | device->event_cb = event_cb; | ||
| 534 | INIT_WORK(&device->event_work, ishtp_bus_event_work); | ||
| 535 | |||
| 536 | return 0; | ||
| 537 | } | ||
| 538 | EXPORT_SYMBOL(ishtp_register_event_cb); | ||
| 539 | |||
| 540 | /** | ||
| 541 | * ishtp_get_device() - update usage count for the device | ||
| 542 | * @cl_device: client device instance | ||
| 543 | * | ||
| 544 | * Increment the usage count. The device can't be deleted | ||
| 545 | */ | ||
| 546 | void ishtp_get_device(struct ishtp_cl_device *cl_device) | ||
| 547 | { | ||
| 548 | cl_device->reference_count++; | ||
| 549 | } | ||
| 550 | EXPORT_SYMBOL(ishtp_get_device); | ||
| 551 | |||
| 552 | /** | ||
| 553 | * ishtp_put_device() - decrement usage count for the device | ||
| 554 | * @cl_device: client device instance | ||
| 555 | * | ||
| 556 | * Decrement the usage count. The device can be deleted is count = 0 | ||
| 557 | */ | ||
| 558 | void ishtp_put_device(struct ishtp_cl_device *cl_device) | ||
| 559 | { | ||
| 560 | cl_device->reference_count--; | ||
| 561 | } | ||
| 562 | EXPORT_SYMBOL(ishtp_put_device); | ||
| 563 | |||
| 564 | /** | ||
| 565 | * ishtp_bus_new_client() - Create a new client | ||
| 566 | * @dev: ISHTP device instance | ||
| 567 | * | ||
| 568 | * Once bus protocol enumerates a client, this is called | ||
| 569 | * to add a device for the client. | ||
| 570 | * | ||
| 571 | * Return: 0 on success or error code on failure | ||
| 572 | */ | ||
| 573 | int ishtp_bus_new_client(struct ishtp_device *dev) | ||
| 574 | { | ||
| 575 | int i; | ||
| 576 | char *dev_name; | ||
| 577 | struct ishtp_cl_device *cl_device; | ||
| 578 | uuid_le device_uuid; | ||
| 579 | |||
| 580 | /* | ||
| 581 | * For all reported clients, create an unconnected client and add its | ||
| 582 | * device to ISHTP bus. | ||
| 583 | * If appropriate driver has loaded, this will trigger its probe(). | ||
| 584 | * Otherwise, probe() will be called when driver is loaded | ||
| 585 | */ | ||
| 586 | i = dev->fw_client_presentation_num - 1; | ||
| 587 | device_uuid = dev->fw_clients[i].props.protocol_name; | ||
| 588 | dev_name = kasprintf(GFP_KERNEL, | ||
| 589 | "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", | ||
| 590 | device_uuid.b[3], device_uuid.b[2], device_uuid.b[1], | ||
| 591 | device_uuid.b[0], device_uuid.b[5], device_uuid.b[4], | ||
| 592 | device_uuid.b[7], device_uuid.b[6], device_uuid.b[8], | ||
| 593 | device_uuid.b[9], device_uuid.b[10], device_uuid.b[11], | ||
| 594 | device_uuid.b[12], device_uuid.b[13], device_uuid.b[14], | ||
| 595 | device_uuid.b[15]); | ||
| 596 | if (!dev_name) | ||
| 597 | return -ENOMEM; | ||
| 598 | |||
| 599 | cl_device = ishtp_bus_add_device(dev, device_uuid, dev_name); | ||
| 600 | if (!cl_device) { | ||
| 601 | kfree(dev_name); | ||
| 602 | return -ENOENT; | ||
| 603 | } | ||
| 604 | |||
| 605 | kfree(dev_name); | ||
| 606 | |||
| 607 | return 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | /** | ||
| 611 | * ishtp_cl_device_bind() - bind a device | ||
| 612 | * @cl: ishtp client device | ||
| 613 | * | ||
| 614 | * Binds connected ishtp_cl to ISHTP bus device | ||
| 615 | * | ||
| 616 | * Return: 0 on success or fault code | ||
| 617 | */ | ||
| 618 | int ishtp_cl_device_bind(struct ishtp_cl *cl) | ||
| 619 | { | ||
| 620 | struct ishtp_cl_device *cl_device; | ||
| 621 | unsigned long flags; | ||
| 622 | int rv; | ||
| 623 | |||
| 624 | if (!cl->fw_client_id || cl->state != ISHTP_CL_CONNECTED) | ||
| 625 | return -EFAULT; | ||
| 626 | |||
| 627 | rv = -ENOENT; | ||
| 628 | spin_lock_irqsave(&cl->dev->device_list_lock, flags); | ||
| 629 | list_for_each_entry(cl_device, &cl->dev->device_list, | ||
| 630 | device_link) { | ||
| 631 | if (cl_device->fw_client->client_id == cl->fw_client_id) { | ||
| 632 | cl->device = cl_device; | ||
| 633 | rv = 0; | ||
| 634 | break; | ||
| 635 | } | ||
| 636 | } | ||
| 637 | spin_unlock_irqrestore(&cl->dev->device_list_lock, flags); | ||
| 638 | return rv; | ||
| 639 | } | ||
| 640 | |||
| 641 | /** | ||
| 642 | * ishtp_bus_remove_all_clients() - Remove all clients | ||
| 643 | * @ishtp_dev: ishtp device | ||
| 644 | * @warm_reset: Reset due to FW reset dure to errors or S3 suspend | ||
| 645 | * | ||
| 646 | * This is part of reset/remove flow. This function the main processing | ||
| 647 | * only targets error processing, if the FW has forced reset or | ||
| 648 | * error to remove connected clients. When warm reset the client devices are | ||
| 649 | * not removed. | ||
| 650 | */ | ||
| 651 | void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev, | ||
| 652 | bool warm_reset) | ||
| 653 | { | ||
| 654 | struct ishtp_cl_device *cl_device, *n; | ||
| 655 | struct ishtp_cl *cl; | ||
| 656 | unsigned long flags; | ||
| 657 | |||
| 658 | spin_lock_irqsave(&ishtp_dev->cl_list_lock, flags); | ||
| 659 | list_for_each_entry(cl, &ishtp_dev->cl_list, link) { | ||
| 660 | cl->state = ISHTP_CL_DISCONNECTED; | ||
| 661 | |||
| 662 | /* | ||
| 663 | * Wake any pending process. The waiter would check dev->state | ||
| 664 | * and determine that it's not enabled already, | ||
| 665 | * and will return error to its caller | ||
| 666 | */ | ||
| 667 | wake_up_interruptible(&cl->wait_ctrl_res); | ||
| 668 | |||
| 669 | /* Disband any pending read/write requests and free rb */ | ||
| 670 | ishtp_cl_flush_queues(cl); | ||
| 671 | |||
| 672 | /* Remove all free and in_process rings, both Rx and Tx */ | ||
| 673 | ishtp_cl_free_rx_ring(cl); | ||
| 674 | ishtp_cl_free_tx_ring(cl); | ||
| 675 | |||
| 676 | /* | ||
| 677 | * Free client and ISHTP bus client device structures | ||
| 678 | * don't free host client because it is part of the OS fd | ||
| 679 | * structure | ||
| 680 | */ | ||
| 681 | } | ||
| 682 | spin_unlock_irqrestore(&ishtp_dev->cl_list_lock, flags); | ||
| 683 | |||
| 684 | /* Release DMA buffers for client messages */ | ||
| 685 | ishtp_cl_free_dma_buf(ishtp_dev); | ||
| 686 | |||
| 687 | /* remove bus clients */ | ||
| 688 | spin_lock_irqsave(&ishtp_dev->device_list_lock, flags); | ||
| 689 | list_for_each_entry_safe(cl_device, n, &ishtp_dev->device_list, | ||
| 690 | device_link) { | ||
| 691 | if (warm_reset && cl_device->reference_count) | ||
| 692 | continue; | ||
| 693 | |||
| 694 | list_del(&cl_device->device_link); | ||
| 695 | spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags); | ||
| 696 | ishtp_bus_remove_device(cl_device); | ||
| 697 | spin_lock_irqsave(&ishtp_dev->device_list_lock, flags); | ||
| 698 | } | ||
| 699 | spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags); | ||
| 700 | |||
| 701 | /* Free all client structures */ | ||
| 702 | spin_lock_irqsave(&ishtp_dev->fw_clients_lock, flags); | ||
| 703 | kfree(ishtp_dev->fw_clients); | ||
| 704 | ishtp_dev->fw_clients = NULL; | ||
| 705 | ishtp_dev->fw_clients_num = 0; | ||
| 706 | ishtp_dev->fw_client_presentation_num = 0; | ||
| 707 | ishtp_dev->fw_client_index = 0; | ||
| 708 | bitmap_zero(ishtp_dev->fw_clients_map, ISHTP_CLIENTS_MAX); | ||
| 709 | spin_unlock_irqrestore(&ishtp_dev->fw_clients_lock, flags); | ||
| 710 | } | ||
| 711 | EXPORT_SYMBOL(ishtp_bus_remove_all_clients); | ||
| 712 | |||
| 713 | /** | ||
| 714 | * ishtp_reset_handler() - IPC reset handler | ||
| 715 | * @dev: ishtp device | ||
| 716 | * | ||
| 717 | * ISHTP Handler for IPC_RESET notification | ||
| 718 | */ | ||
| 719 | void ishtp_reset_handler(struct ishtp_device *dev) | ||
| 720 | { | ||
| 721 | unsigned long flags; | ||
| 722 | |||
| 723 | /* Handle FW-initiated reset */ | ||
| 724 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
| 725 | |||
| 726 | /* Clear BH processing queue - no further HBMs */ | ||
| 727 | spin_lock_irqsave(&dev->rd_msg_spinlock, flags); | ||
| 728 | dev->rd_msg_fifo_head = dev->rd_msg_fifo_tail = 0; | ||
| 729 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
| 730 | |||
| 731 | /* Handle ISH FW reset against upper layers */ | ||
| 732 | ishtp_bus_remove_all_clients(dev, true); | ||
| 733 | } | ||
| 734 | EXPORT_SYMBOL(ishtp_reset_handler); | ||
| 735 | |||
| 736 | /** | ||
| 737 | * ishtp_reset_compl_handler() - Reset completion handler | ||
| 738 | * @dev: ishtp device | ||
| 739 | * | ||
| 740 | * ISHTP handler for IPC_RESET sequence completion to start | ||
| 741 | * host message bus start protocol sequence. | ||
| 742 | */ | ||
| 743 | void ishtp_reset_compl_handler(struct ishtp_device *dev) | ||
| 744 | { | ||
| 745 | dev->dev_state = ISHTP_DEV_INIT_CLIENTS; | ||
| 746 | dev->hbm_state = ISHTP_HBM_START; | ||
| 747 | ishtp_hbm_start_req(dev); | ||
| 748 | } | ||
| 749 | EXPORT_SYMBOL(ishtp_reset_compl_handler); | ||
| 750 | |||
| 751 | /** | ||
| 752 | * ishtp_use_dma_transfer() - Function to use DMA | ||
| 753 | * | ||
| 754 | * This interface is used to enable usage of DMA | ||
| 755 | * | ||
| 756 | * Return non zero if DMA can be enabled | ||
| 757 | */ | ||
| 758 | int ishtp_use_dma_transfer(void) | ||
| 759 | { | ||
| 760 | return ishtp_use_dma; | ||
| 761 | } | ||
| 762 | |||
| 763 | /** | ||
| 764 | * ishtp_bus_register() - Function to register bus | ||
| 765 | * | ||
| 766 | * This register ishtp bus | ||
| 767 | * | ||
| 768 | * Return: Return output of bus_register | ||
| 769 | */ | ||
| 770 | static int __init ishtp_bus_register(void) | ||
| 771 | { | ||
| 772 | return bus_register(&ishtp_cl_bus_type); | ||
| 773 | } | ||
| 774 | |||
| 775 | /** | ||
| 776 | * ishtp_bus_unregister() - Function to unregister bus | ||
| 777 | * | ||
| 778 | * This unregister ishtp bus | ||
| 779 | */ | ||
| 780 | static void __exit ishtp_bus_unregister(void) | ||
| 781 | { | ||
| 782 | bus_unregister(&ishtp_cl_bus_type); | ||
| 783 | } | ||
| 784 | |||
| 785 | module_init(ishtp_bus_register); | ||
| 786 | module_exit(ishtp_bus_unregister); | ||
| 787 | |||
| 788 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.h b/drivers/hid/intel-ish-hid/ishtp/bus.h new file mode 100644 index 000000000000..a1ffae7f26ad --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/bus.h | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP bus definitions | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | #ifndef _LINUX_ISHTP_CL_BUS_H | ||
| 16 | #define _LINUX_ISHTP_CL_BUS_H | ||
| 17 | |||
| 18 | #include <linux/device.h> | ||
| 19 | #include <linux/mod_devicetable.h> | ||
| 20 | |||
| 21 | struct ishtp_cl; | ||
| 22 | struct ishtp_cl_device; | ||
| 23 | struct ishtp_device; | ||
| 24 | struct ishtp_msg_hdr; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * struct ishtp_cl_device - ISHTP device handle | ||
| 28 | * @dev: device pointer | ||
| 29 | * @ishtp_dev: pointer to ishtp device structure to primarily to access | ||
| 30 | * hw device operation callbacks and properties | ||
| 31 | * @fw_client: fw_client pointer to get fw information like protocol name | ||
| 32 | * max message length etc. | ||
| 33 | * @device_link: Link to next client in the list on a bus | ||
| 34 | * @event_work: Used to schedule rx event for client | ||
| 35 | * @driver_data: Storage driver private data | ||
| 36 | * @reference_count: Used for get/put device | ||
| 37 | * @event_cb: Callback to driver to send events | ||
| 38 | * | ||
| 39 | * An ishtp_cl_device pointer is returned from ishtp_add_device() | ||
| 40 | * and links ISHTP bus clients to their actual host client pointer. | ||
| 41 | * Drivers for ISHTP devices will get an ishtp_cl_device pointer | ||
| 42 | * when being probed and shall use it for doing bus I/O. | ||
| 43 | */ | ||
| 44 | struct ishtp_cl_device { | ||
| 45 | struct device dev; | ||
| 46 | struct ishtp_device *ishtp_dev; | ||
| 47 | struct ishtp_fw_client *fw_client; | ||
| 48 | struct list_head device_link; | ||
| 49 | struct work_struct event_work; | ||
| 50 | void *driver_data; | ||
| 51 | int reference_count; | ||
| 52 | void (*event_cb)(struct ishtp_cl_device *device); | ||
| 53 | }; | ||
| 54 | |||
| 55 | /** | ||
| 56 | * struct ishtp_cl_device - ISHTP device handle | ||
| 57 | * @driver: driver instance on a bus | ||
| 58 | * @name: Name of the device for probe | ||
| 59 | * @probe: driver callback for device probe | ||
| 60 | * @remove: driver callback on device removal | ||
| 61 | * | ||
| 62 | * Client drivers defines to get probed/removed for ISHTP client device. | ||
| 63 | */ | ||
| 64 | struct ishtp_cl_driver { | ||
| 65 | struct device_driver driver; | ||
| 66 | const char *name; | ||
| 67 | int (*probe)(struct ishtp_cl_device *dev); | ||
| 68 | int (*remove)(struct ishtp_cl_device *dev); | ||
| 69 | int (*reset)(struct ishtp_cl_device *dev); | ||
| 70 | const struct dev_pm_ops *pm; | ||
| 71 | }; | ||
| 72 | |||
| 73 | |||
| 74 | int ishtp_bus_new_client(struct ishtp_device *dev); | ||
| 75 | void ishtp_remove_all_clients(struct ishtp_device *dev); | ||
| 76 | int ishtp_cl_device_bind(struct ishtp_cl *cl); | ||
| 77 | void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device); | ||
| 78 | |||
| 79 | /* Write a multi-fragment message */ | ||
| 80 | int ishtp_send_msg(struct ishtp_device *dev, | ||
| 81 | struct ishtp_msg_hdr *hdr, void *msg, | ||
| 82 | void (*ipc_send_compl)(void *), | ||
| 83 | void *ipc_send_compl_prm); | ||
| 84 | |||
| 85 | /* Write a single-fragment message */ | ||
| 86 | int ishtp_write_message(struct ishtp_device *dev, | ||
| 87 | struct ishtp_msg_hdr *hdr, | ||
| 88 | unsigned char *buf); | ||
| 89 | |||
| 90 | /* Use DMA to send/receive messages */ | ||
| 91 | int ishtp_use_dma_transfer(void); | ||
| 92 | |||
| 93 | /* Exported functions */ | ||
| 94 | void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev, | ||
| 95 | bool warm_reset); | ||
| 96 | |||
| 97 | void ishtp_recv(struct ishtp_device *dev); | ||
| 98 | void ishtp_reset_handler(struct ishtp_device *dev); | ||
| 99 | void ishtp_reset_compl_handler(struct ishtp_device *dev); | ||
| 100 | |||
| 101 | void ishtp_put_device(struct ishtp_cl_device *); | ||
| 102 | void ishtp_get_device(struct ishtp_cl_device *); | ||
| 103 | |||
| 104 | int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver, | ||
| 105 | struct module *owner); | ||
| 106 | #define ishtp_cl_driver_register(driver) \ | ||
| 107 | __ishtp_cl_driver_register(driver, THIS_MODULE) | ||
| 108 | void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver); | ||
| 109 | |||
| 110 | int ishtp_register_event_cb(struct ishtp_cl_device *device, | ||
| 111 | void (*read_cb)(struct ishtp_cl_device *)); | ||
| 112 | int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid); | ||
| 113 | |||
| 114 | #endif /* _LINUX_ISHTP_CL_BUS_H */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/client-buffers.c b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c new file mode 100644 index 000000000000..b9b917d2d50d --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c | |||
| @@ -0,0 +1,257 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP Ring Buffers | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/slab.h> | ||
| 18 | #include "client.h" | ||
| 19 | |||
| 20 | /** | ||
| 21 | * ishtp_cl_alloc_rx_ring() - Allocate RX ring buffers | ||
| 22 | * @cl: client device instance | ||
| 23 | * | ||
| 24 | * Allocate and initialize RX ring buffers | ||
| 25 | * | ||
| 26 | * Return: 0 on success else -ENOMEM | ||
| 27 | */ | ||
| 28 | int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl) | ||
| 29 | { | ||
| 30 | size_t len = cl->device->fw_client->props.max_msg_length; | ||
| 31 | int j; | ||
| 32 | struct ishtp_cl_rb *rb; | ||
| 33 | int ret = 0; | ||
| 34 | unsigned long flags; | ||
| 35 | |||
| 36 | for (j = 0; j < cl->rx_ring_size; ++j) { | ||
| 37 | rb = ishtp_io_rb_init(cl); | ||
| 38 | if (!rb) { | ||
| 39 | ret = -ENOMEM; | ||
| 40 | goto out; | ||
| 41 | } | ||
| 42 | ret = ishtp_io_rb_alloc_buf(rb, len); | ||
| 43 | if (ret) | ||
| 44 | goto out; | ||
| 45 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
| 46 | list_add_tail(&rb->list, &cl->free_rb_list.list); | ||
| 47 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
| 48 | } | ||
| 49 | |||
| 50 | return 0; | ||
| 51 | |||
| 52 | out: | ||
| 53 | dev_err(&cl->device->dev, "error in allocating Rx buffers\n"); | ||
| 54 | ishtp_cl_free_rx_ring(cl); | ||
| 55 | return ret; | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * ishtp_cl_alloc_tx_ring() - Allocate TX ring buffers | ||
| 60 | * @cl: client device instance | ||
| 61 | * | ||
| 62 | * Allocate and initialize TX ring buffers | ||
| 63 | * | ||
| 64 | * Return: 0 on success else -ENOMEM | ||
| 65 | */ | ||
| 66 | int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl) | ||
| 67 | { | ||
| 68 | size_t len = cl->device->fw_client->props.max_msg_length; | ||
| 69 | int j; | ||
| 70 | unsigned long flags; | ||
| 71 | |||
| 72 | /* Allocate pool to free Tx bufs */ | ||
| 73 | for (j = 0; j < cl->tx_ring_size; ++j) { | ||
| 74 | struct ishtp_cl_tx_ring *tx_buf; | ||
| 75 | |||
| 76 | tx_buf = kzalloc(sizeof(struct ishtp_cl_tx_ring), GFP_KERNEL); | ||
| 77 | if (!tx_buf) | ||
| 78 | goto out; | ||
| 79 | |||
| 80 | tx_buf->send_buf.data = kmalloc(len, GFP_KERNEL); | ||
| 81 | if (!tx_buf->send_buf.data) { | ||
| 82 | kfree(tx_buf); | ||
| 83 | goto out; | ||
| 84 | } | ||
| 85 | |||
| 86 | spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); | ||
| 87 | list_add_tail(&tx_buf->list, &cl->tx_free_list.list); | ||
| 88 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); | ||
| 89 | } | ||
| 90 | return 0; | ||
| 91 | out: | ||
| 92 | dev_err(&cl->device->dev, "error in allocating Tx pool\n"); | ||
| 93 | ishtp_cl_free_rx_ring(cl); | ||
| 94 | return -ENOMEM; | ||
| 95 | } | ||
| 96 | |||
| 97 | /** | ||
| 98 | * ishtp_cl_free_rx_ring() - Free RX ring buffers | ||
| 99 | * @cl: client device instance | ||
| 100 | * | ||
| 101 | * Free RX ring buffers | ||
| 102 | */ | ||
| 103 | void ishtp_cl_free_rx_ring(struct ishtp_cl *cl) | ||
| 104 | { | ||
| 105 | struct ishtp_cl_rb *rb; | ||
| 106 | unsigned long flags; | ||
| 107 | |||
| 108 | /* release allocated memory - pass over free_rb_list */ | ||
| 109 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
| 110 | while (!list_empty(&cl->free_rb_list.list)) { | ||
| 111 | rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, | ||
| 112 | list); | ||
| 113 | list_del(&rb->list); | ||
| 114 | kfree(rb->buffer.data); | ||
| 115 | kfree(rb); | ||
| 116 | } | ||
| 117 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
| 118 | /* release allocated memory - pass over in_process_list */ | ||
| 119 | spin_lock_irqsave(&cl->in_process_spinlock, flags); | ||
| 120 | while (!list_empty(&cl->in_process_list.list)) { | ||
| 121 | rb = list_entry(cl->in_process_list.list.next, | ||
| 122 | struct ishtp_cl_rb, list); | ||
| 123 | list_del(&rb->list); | ||
| 124 | kfree(rb->buffer.data); | ||
| 125 | kfree(rb); | ||
| 126 | } | ||
| 127 | spin_unlock_irqrestore(&cl->in_process_spinlock, flags); | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * ishtp_cl_free_tx_ring() - Free TX ring buffers | ||
| 132 | * @cl: client device instance | ||
| 133 | * | ||
| 134 | * Free TX ring buffers | ||
| 135 | */ | ||
| 136 | void ishtp_cl_free_tx_ring(struct ishtp_cl *cl) | ||
| 137 | { | ||
| 138 | struct ishtp_cl_tx_ring *tx_buf; | ||
| 139 | unsigned long flags; | ||
| 140 | |||
| 141 | spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); | ||
| 142 | /* release allocated memory - pass over tx_free_list */ | ||
| 143 | while (!list_empty(&cl->tx_free_list.list)) { | ||
| 144 | tx_buf = list_entry(cl->tx_free_list.list.next, | ||
| 145 | struct ishtp_cl_tx_ring, list); | ||
| 146 | list_del(&tx_buf->list); | ||
| 147 | kfree(tx_buf->send_buf.data); | ||
| 148 | kfree(tx_buf); | ||
| 149 | } | ||
| 150 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); | ||
| 151 | |||
| 152 | spin_lock_irqsave(&cl->tx_list_spinlock, flags); | ||
| 153 | /* release allocated memory - pass over tx_list */ | ||
| 154 | while (!list_empty(&cl->tx_list.list)) { | ||
| 155 | tx_buf = list_entry(cl->tx_list.list.next, | ||
| 156 | struct ishtp_cl_tx_ring, list); | ||
| 157 | list_del(&tx_buf->list); | ||
| 158 | kfree(tx_buf->send_buf.data); | ||
| 159 | kfree(tx_buf); | ||
| 160 | } | ||
| 161 | spin_unlock_irqrestore(&cl->tx_list_spinlock, flags); | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * ishtp_io_rb_free() - Free IO request block | ||
| 166 | * @rb: IO request block | ||
| 167 | * | ||
| 168 | * Free io request block memory | ||
| 169 | */ | ||
| 170 | void ishtp_io_rb_free(struct ishtp_cl_rb *rb) | ||
| 171 | { | ||
| 172 | if (rb == NULL) | ||
| 173 | return; | ||
| 174 | |||
| 175 | kfree(rb->buffer.data); | ||
| 176 | kfree(rb); | ||
| 177 | } | ||
| 178 | |||
| 179 | /** | ||
| 180 | * ishtp_io_rb_init() - Allocate and init IO request block | ||
| 181 | * @cl: client device instance | ||
| 182 | * | ||
| 183 | * Allocate and initialize request block | ||
| 184 | * | ||
| 185 | * Return: Allocted IO request block pointer | ||
| 186 | */ | ||
| 187 | struct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl) | ||
| 188 | { | ||
| 189 | struct ishtp_cl_rb *rb; | ||
| 190 | |||
| 191 | rb = kzalloc(sizeof(struct ishtp_cl_rb), GFP_KERNEL); | ||
| 192 | if (!rb) | ||
| 193 | return NULL; | ||
| 194 | |||
| 195 | INIT_LIST_HEAD(&rb->list); | ||
| 196 | rb->cl = cl; | ||
| 197 | rb->buf_idx = 0; | ||
| 198 | return rb; | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * ishtp_io_rb_alloc_buf() - Allocate and init response buffer | ||
| 203 | * @rb: IO request block | ||
| 204 | * @length: length of response buffer | ||
| 205 | * | ||
| 206 | * Allocate respose buffer | ||
| 207 | * | ||
| 208 | * Return: 0 on success else -ENOMEM | ||
| 209 | */ | ||
| 210 | int ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length) | ||
| 211 | { | ||
| 212 | if (!rb) | ||
| 213 | return -EINVAL; | ||
| 214 | |||
| 215 | if (length == 0) | ||
| 216 | return 0; | ||
| 217 | |||
| 218 | rb->buffer.data = kmalloc(length, GFP_KERNEL); | ||
| 219 | if (!rb->buffer.data) | ||
| 220 | return -ENOMEM; | ||
| 221 | |||
| 222 | rb->buffer.size = length; | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | /** | ||
| 227 | * ishtp_cl_io_rb_recycle() - Recycle IO request blocks | ||
| 228 | * @rb: IO request block | ||
| 229 | * | ||
| 230 | * Re-append rb to its client's free list and send flow control if needed | ||
| 231 | * | ||
| 232 | * Return: 0 on success else -EFAULT | ||
| 233 | */ | ||
| 234 | int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb) | ||
| 235 | { | ||
| 236 | struct ishtp_cl *cl; | ||
| 237 | int rets = 0; | ||
| 238 | unsigned long flags; | ||
| 239 | |||
| 240 | if (!rb || !rb->cl) | ||
| 241 | return -EFAULT; | ||
| 242 | |||
| 243 | cl = rb->cl; | ||
| 244 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
| 245 | list_add_tail(&rb->list, &cl->free_rb_list.list); | ||
| 246 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
| 247 | |||
| 248 | /* | ||
| 249 | * If we returned the first buffer to empty 'free' list, | ||
| 250 | * send flow control | ||
| 251 | */ | ||
| 252 | if (!cl->out_flow_ctrl_creds) | ||
| 253 | rets = ishtp_cl_read_start(cl); | ||
| 254 | |||
| 255 | return rets; | ||
| 256 | } | ||
| 257 | EXPORT_SYMBOL(ishtp_cl_io_rb_recycle); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c new file mode 100644 index 000000000000..aad61328f282 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/client.c | |||
| @@ -0,0 +1,1054 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP client logic | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/sched.h> | ||
| 19 | #include <linux/wait.h> | ||
| 20 | #include <linux/delay.h> | ||
| 21 | #include <linux/dma-mapping.h> | ||
| 22 | #include "hbm.h" | ||
| 23 | #include "client.h" | ||
| 24 | |||
| 25 | /** | ||
| 26 | * ishtp_read_list_flush() - Flush read queue | ||
| 27 | * @cl: ishtp client instance | ||
| 28 | * | ||
| 29 | * Used to remove all entries from read queue for a client | ||
| 30 | */ | ||
| 31 | static void ishtp_read_list_flush(struct ishtp_cl *cl) | ||
| 32 | { | ||
| 33 | struct ishtp_cl_rb *rb; | ||
| 34 | struct ishtp_cl_rb *next; | ||
| 35 | unsigned long flags; | ||
| 36 | |||
| 37 | spin_lock_irqsave(&cl->dev->read_list_spinlock, flags); | ||
| 38 | list_for_each_entry_safe(rb, next, &cl->dev->read_list.list, list) | ||
| 39 | if (rb->cl && ishtp_cl_cmp_id(cl, rb->cl)) { | ||
| 40 | list_del(&rb->list); | ||
| 41 | ishtp_io_rb_free(rb); | ||
| 42 | } | ||
| 43 | spin_unlock_irqrestore(&cl->dev->read_list_spinlock, flags); | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * ishtp_cl_flush_queues() - Flush all queues for a client | ||
| 48 | * @cl: ishtp client instance | ||
| 49 | * | ||
| 50 | * Used to remove all queues for a client. This is called when a client device | ||
| 51 | * needs reset due to error, S3 resume or during module removal | ||
| 52 | * | ||
| 53 | * Return: 0 on success else -EINVAL if device is NULL | ||
| 54 | */ | ||
| 55 | int ishtp_cl_flush_queues(struct ishtp_cl *cl) | ||
| 56 | { | ||
| 57 | if (WARN_ON(!cl || !cl->dev)) | ||
| 58 | return -EINVAL; | ||
| 59 | |||
| 60 | ishtp_read_list_flush(cl); | ||
| 61 | |||
| 62 | return 0; | ||
| 63 | } | ||
| 64 | EXPORT_SYMBOL(ishtp_cl_flush_queues); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * ishtp_cl_init() - Initialize all fields of a client device | ||
| 68 | * @cl: ishtp client instance | ||
| 69 | * @dev: ishtp device | ||
| 70 | * | ||
| 71 | * Initializes a client device fields: Init spinlocks, init queues etc. | ||
| 72 | * This function is called during new client creation | ||
| 73 | */ | ||
| 74 | static void ishtp_cl_init(struct ishtp_cl *cl, struct ishtp_device *dev) | ||
| 75 | { | ||
| 76 | memset(cl, 0, sizeof(struct ishtp_cl)); | ||
| 77 | init_waitqueue_head(&cl->wait_ctrl_res); | ||
| 78 | spin_lock_init(&cl->free_list_spinlock); | ||
| 79 | spin_lock_init(&cl->in_process_spinlock); | ||
| 80 | spin_lock_init(&cl->tx_list_spinlock); | ||
| 81 | spin_lock_init(&cl->tx_free_list_spinlock); | ||
| 82 | spin_lock_init(&cl->fc_spinlock); | ||
| 83 | INIT_LIST_HEAD(&cl->link); | ||
| 84 | cl->dev = dev; | ||
| 85 | |||
| 86 | INIT_LIST_HEAD(&cl->free_rb_list.list); | ||
| 87 | INIT_LIST_HEAD(&cl->tx_list.list); | ||
| 88 | INIT_LIST_HEAD(&cl->tx_free_list.list); | ||
| 89 | INIT_LIST_HEAD(&cl->in_process_list.list); | ||
| 90 | |||
| 91 | cl->rx_ring_size = CL_DEF_RX_RING_SIZE; | ||
| 92 | cl->tx_ring_size = CL_DEF_TX_RING_SIZE; | ||
| 93 | |||
| 94 | /* dma */ | ||
| 95 | cl->last_tx_path = CL_TX_PATH_IPC; | ||
| 96 | cl->last_dma_acked = 1; | ||
| 97 | cl->last_dma_addr = NULL; | ||
| 98 | cl->last_ipc_acked = 1; | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * ishtp_cl_allocate() - allocates client structure and sets it up. | ||
| 103 | * @dev: ishtp device | ||
| 104 | * | ||
| 105 | * Allocate memory for new client device and call to initialize each field. | ||
| 106 | * | ||
| 107 | * Return: The allocated client instance or NULL on failure | ||
| 108 | */ | ||
| 109 | struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev) | ||
| 110 | { | ||
| 111 | struct ishtp_cl *cl; | ||
| 112 | |||
| 113 | cl = kmalloc(sizeof(struct ishtp_cl), GFP_KERNEL); | ||
| 114 | if (!cl) | ||
| 115 | return NULL; | ||
| 116 | |||
| 117 | ishtp_cl_init(cl, dev); | ||
| 118 | return cl; | ||
| 119 | } | ||
| 120 | EXPORT_SYMBOL(ishtp_cl_allocate); | ||
| 121 | |||
| 122 | /** | ||
| 123 | * ishtp_cl_free() - Frees a client device | ||
| 124 | * @cl: client device instance | ||
| 125 | * | ||
| 126 | * Frees a client device | ||
| 127 | */ | ||
| 128 | void ishtp_cl_free(struct ishtp_cl *cl) | ||
| 129 | { | ||
| 130 | struct ishtp_device *dev; | ||
| 131 | unsigned long flags; | ||
| 132 | |||
| 133 | if (!cl) | ||
| 134 | return; | ||
| 135 | |||
| 136 | dev = cl->dev; | ||
| 137 | if (!dev) | ||
| 138 | return; | ||
| 139 | |||
| 140 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
| 141 | ishtp_cl_free_rx_ring(cl); | ||
| 142 | ishtp_cl_free_tx_ring(cl); | ||
| 143 | kfree(cl); | ||
| 144 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 145 | } | ||
| 146 | EXPORT_SYMBOL(ishtp_cl_free); | ||
| 147 | |||
| 148 | /** | ||
| 149 | * ishtp_cl_link() - Reserve a host id and link the client instance | ||
| 150 | * @cl: client device instance | ||
| 151 | * @id: host client id to use. It can be ISHTP_HOST_CLIENT_ID_ANY if any | ||
| 152 | * id from the available can be used | ||
| 153 | * | ||
| 154 | * | ||
| 155 | * This allocates a single bit in the hostmap. This function will make sure | ||
| 156 | * that not many client sessions are opened at the same time. Once allocated | ||
| 157 | * the client device instance is added to the ishtp device in the current | ||
| 158 | * client list | ||
| 159 | * | ||
| 160 | * Return: 0 or error code on failure | ||
| 161 | */ | ||
| 162 | int ishtp_cl_link(struct ishtp_cl *cl, int id) | ||
| 163 | { | ||
| 164 | struct ishtp_device *dev; | ||
| 165 | unsigned long flags, flags_cl; | ||
| 166 | int ret = 0; | ||
| 167 | |||
| 168 | if (WARN_ON(!cl || !cl->dev)) | ||
| 169 | return -EINVAL; | ||
| 170 | |||
| 171 | dev = cl->dev; | ||
| 172 | |||
| 173 | spin_lock_irqsave(&dev->device_lock, flags); | ||
| 174 | |||
| 175 | if (dev->open_handle_count >= ISHTP_MAX_OPEN_HANDLE_COUNT) { | ||
| 176 | ret = -EMFILE; | ||
| 177 | goto unlock_dev; | ||
| 178 | } | ||
| 179 | |||
| 180 | /* If Id is not assigned get one*/ | ||
| 181 | if (id == ISHTP_HOST_CLIENT_ID_ANY) | ||
| 182 | id = find_first_zero_bit(dev->host_clients_map, | ||
| 183 | ISHTP_CLIENTS_MAX); | ||
| 184 | |||
| 185 | if (id >= ISHTP_CLIENTS_MAX) { | ||
| 186 | spin_unlock_irqrestore(&dev->device_lock, flags); | ||
| 187 | dev_err(&cl->device->dev, "id exceeded %d", ISHTP_CLIENTS_MAX); | ||
| 188 | return -ENOENT; | ||
| 189 | } | ||
| 190 | |||
| 191 | dev->open_handle_count++; | ||
| 192 | cl->host_client_id = id; | ||
| 193 | spin_lock_irqsave(&dev->cl_list_lock, flags_cl); | ||
| 194 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
| 195 | ret = -ENODEV; | ||
| 196 | goto unlock_cl; | ||
| 197 | } | ||
| 198 | list_add_tail(&cl->link, &dev->cl_list); | ||
| 199 | set_bit(id, dev->host_clients_map); | ||
| 200 | cl->state = ISHTP_CL_INITIALIZING; | ||
| 201 | |||
| 202 | unlock_cl: | ||
| 203 | spin_unlock_irqrestore(&dev->cl_list_lock, flags_cl); | ||
| 204 | unlock_dev: | ||
| 205 | spin_unlock_irqrestore(&dev->device_lock, flags); | ||
| 206 | return ret; | ||
| 207 | } | ||
| 208 | EXPORT_SYMBOL(ishtp_cl_link); | ||
| 209 | |||
| 210 | /** | ||
| 211 | * ishtp_cl_unlink() - remove fw_cl from the client device list | ||
| 212 | * @cl: client device instance | ||
| 213 | * | ||
| 214 | * Remove a previously linked device to a ishtp device | ||
| 215 | */ | ||
| 216 | void ishtp_cl_unlink(struct ishtp_cl *cl) | ||
| 217 | { | ||
| 218 | struct ishtp_device *dev; | ||
| 219 | struct ishtp_cl *pos; | ||
| 220 | unsigned long flags; | ||
| 221 | |||
| 222 | /* don't shout on error exit path */ | ||
| 223 | if (!cl || !cl->dev) | ||
| 224 | return; | ||
| 225 | |||
| 226 | dev = cl->dev; | ||
| 227 | |||
| 228 | spin_lock_irqsave(&dev->device_lock, flags); | ||
| 229 | if (dev->open_handle_count > 0) { | ||
| 230 | clear_bit(cl->host_client_id, dev->host_clients_map); | ||
| 231 | dev->open_handle_count--; | ||
| 232 | } | ||
| 233 | spin_unlock_irqrestore(&dev->device_lock, flags); | ||
| 234 | |||
| 235 | /* | ||
| 236 | * This checks that 'cl' is actually linked into device's structure, | ||
| 237 | * before attempting 'list_del' | ||
| 238 | */ | ||
| 239 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
| 240 | list_for_each_entry(pos, &dev->cl_list, link) | ||
| 241 | if (cl->host_client_id == pos->host_client_id) { | ||
| 242 | list_del_init(&pos->link); | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 246 | } | ||
| 247 | EXPORT_SYMBOL(ishtp_cl_unlink); | ||
| 248 | |||
| 249 | /** | ||
| 250 | * ishtp_cl_disconnect() - Send disconnect request to firmware | ||
| 251 | * @cl: client device instance | ||
| 252 | * | ||
| 253 | * Send a disconnect request for a client to firmware. | ||
| 254 | * | ||
| 255 | * Return: 0 if successful disconnect response from the firmware or error | ||
| 256 | * code on failure | ||
| 257 | */ | ||
| 258 | int ishtp_cl_disconnect(struct ishtp_cl *cl) | ||
| 259 | { | ||
| 260 | struct ishtp_device *dev; | ||
| 261 | int err; | ||
| 262 | |||
| 263 | if (WARN_ON(!cl || !cl->dev)) | ||
| 264 | return -ENODEV; | ||
| 265 | |||
| 266 | dev = cl->dev; | ||
| 267 | |||
| 268 | dev->print_log(dev, "%s() state %d\n", __func__, cl->state); | ||
| 269 | |||
| 270 | if (cl->state != ISHTP_CL_DISCONNECTING) { | ||
| 271 | dev->print_log(dev, "%s() Disconnect in progress\n", __func__); | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | if (ishtp_hbm_cl_disconnect_req(dev, cl)) { | ||
| 276 | dev->print_log(dev, "%s() Failed to disconnect\n", __func__); | ||
| 277 | dev_err(&cl->device->dev, "failed to disconnect.\n"); | ||
| 278 | return -ENODEV; | ||
| 279 | } | ||
| 280 | |||
| 281 | err = wait_event_interruptible_timeout(cl->wait_ctrl_res, | ||
| 282 | (dev->dev_state != ISHTP_DEV_ENABLED || | ||
| 283 | cl->state == ISHTP_CL_DISCONNECTED), | ||
| 284 | ishtp_secs_to_jiffies(ISHTP_CL_CONNECT_TIMEOUT)); | ||
| 285 | |||
| 286 | /* | ||
| 287 | * If FW reset arrived, this will happen. Don't check cl->, | ||
| 288 | * as 'cl' may be freed already | ||
| 289 | */ | ||
| 290 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
| 291 | dev->print_log(dev, "%s() dev_state != ISHTP_DEV_ENABLED\n", | ||
| 292 | __func__); | ||
| 293 | return -ENODEV; | ||
| 294 | } | ||
| 295 | |||
| 296 | if (cl->state == ISHTP_CL_DISCONNECTED) { | ||
| 297 | dev->print_log(dev, "%s() successful\n", __func__); | ||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | return -ENODEV; | ||
| 302 | } | ||
| 303 | EXPORT_SYMBOL(ishtp_cl_disconnect); | ||
| 304 | |||
| 305 | /** | ||
| 306 | * ishtp_cl_is_other_connecting() - Check other client is connecting | ||
| 307 | * @cl: client device instance | ||
| 308 | * | ||
| 309 | * Checks if other client with the same fw client id is connecting | ||
| 310 | * | ||
| 311 | * Return: true if other client is connected else false | ||
| 312 | */ | ||
| 313 | static bool ishtp_cl_is_other_connecting(struct ishtp_cl *cl) | ||
| 314 | { | ||
| 315 | struct ishtp_device *dev; | ||
| 316 | struct ishtp_cl *pos; | ||
| 317 | unsigned long flags; | ||
| 318 | |||
| 319 | if (WARN_ON(!cl || !cl->dev)) | ||
| 320 | return false; | ||
| 321 | |||
| 322 | dev = cl->dev; | ||
| 323 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
| 324 | list_for_each_entry(pos, &dev->cl_list, link) { | ||
| 325 | if ((pos->state == ISHTP_CL_CONNECTING) && (pos != cl) && | ||
| 326 | cl->fw_client_id == pos->fw_client_id) { | ||
| 327 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 328 | return true; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 332 | |||
| 333 | return false; | ||
| 334 | } | ||
| 335 | |||
| 336 | /** | ||
| 337 | * ishtp_cl_connect() - Send connect request to firmware | ||
| 338 | * @cl: client device instance | ||
| 339 | * | ||
| 340 | * Send a connect request for a client to firmware. If successful it will | ||
| 341 | * RX and TX ring buffers | ||
| 342 | * | ||
| 343 | * Return: 0 if successful connect response from the firmware and able | ||
| 344 | * to bind and allocate ring buffers or error code on failure | ||
| 345 | */ | ||
| 346 | int ishtp_cl_connect(struct ishtp_cl *cl) | ||
| 347 | { | ||
| 348 | struct ishtp_device *dev; | ||
| 349 | int rets; | ||
| 350 | |||
| 351 | if (WARN_ON(!cl || !cl->dev)) | ||
| 352 | return -ENODEV; | ||
| 353 | |||
| 354 | dev = cl->dev; | ||
| 355 | |||
| 356 | dev->print_log(dev, "%s() current_state = %d\n", __func__, cl->state); | ||
| 357 | |||
| 358 | if (ishtp_cl_is_other_connecting(cl)) { | ||
| 359 | dev->print_log(dev, "%s() Busy\n", __func__); | ||
| 360 | return -EBUSY; | ||
| 361 | } | ||
| 362 | |||
| 363 | if (ishtp_hbm_cl_connect_req(dev, cl)) { | ||
| 364 | dev->print_log(dev, "%s() HBM connect req fail\n", __func__); | ||
| 365 | return -ENODEV; | ||
| 366 | } | ||
| 367 | |||
| 368 | rets = wait_event_interruptible_timeout(cl->wait_ctrl_res, | ||
| 369 | (dev->dev_state == ISHTP_DEV_ENABLED && | ||
| 370 | (cl->state == ISHTP_CL_CONNECTED || | ||
| 371 | cl->state == ISHTP_CL_DISCONNECTED)), | ||
| 372 | ishtp_secs_to_jiffies( | ||
| 373 | ISHTP_CL_CONNECT_TIMEOUT)); | ||
| 374 | /* | ||
| 375 | * If FW reset arrived, this will happen. Don't check cl->, | ||
| 376 | * as 'cl' may be freed already | ||
| 377 | */ | ||
| 378 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
| 379 | dev->print_log(dev, "%s() dev_state != ISHTP_DEV_ENABLED\n", | ||
| 380 | __func__); | ||
| 381 | return -EFAULT; | ||
| 382 | } | ||
| 383 | |||
| 384 | if (cl->state != ISHTP_CL_CONNECTED) { | ||
| 385 | dev->print_log(dev, "%s() state != ISHTP_CL_CONNECTED\n", | ||
| 386 | __func__); | ||
| 387 | return -EFAULT; | ||
| 388 | } | ||
| 389 | |||
| 390 | rets = cl->status; | ||
| 391 | if (rets) { | ||
| 392 | dev->print_log(dev, "%s() Invalid status\n", __func__); | ||
| 393 | return rets; | ||
| 394 | } | ||
| 395 | |||
| 396 | rets = ishtp_cl_device_bind(cl); | ||
| 397 | if (rets) { | ||
| 398 | dev->print_log(dev, "%s() Bind error\n", __func__); | ||
| 399 | ishtp_cl_disconnect(cl); | ||
| 400 | return rets; | ||
| 401 | } | ||
| 402 | |||
| 403 | rets = ishtp_cl_alloc_rx_ring(cl); | ||
| 404 | if (rets) { | ||
| 405 | dev->print_log(dev, "%s() Alloc RX ring failed\n", __func__); | ||
| 406 | /* if failed allocation, disconnect */ | ||
| 407 | ishtp_cl_disconnect(cl); | ||
| 408 | return rets; | ||
| 409 | } | ||
| 410 | |||
| 411 | rets = ishtp_cl_alloc_tx_ring(cl); | ||
| 412 | if (rets) { | ||
| 413 | dev->print_log(dev, "%s() Alloc TX ring failed\n", __func__); | ||
| 414 | /* if failed allocation, disconnect */ | ||
| 415 | ishtp_cl_free_rx_ring(cl); | ||
| 416 | ishtp_cl_disconnect(cl); | ||
| 417 | return rets; | ||
| 418 | } | ||
| 419 | |||
| 420 | /* Upon successful connection and allocation, emit flow-control */ | ||
| 421 | rets = ishtp_cl_read_start(cl); | ||
| 422 | |||
| 423 | dev->print_log(dev, "%s() successful\n", __func__); | ||
| 424 | |||
| 425 | return rets; | ||
| 426 | } | ||
| 427 | EXPORT_SYMBOL(ishtp_cl_connect); | ||
| 428 | |||
| 429 | /** | ||
| 430 | * ishtp_cl_read_start() - Prepare to read client message | ||
| 431 | * @cl: client device instance | ||
| 432 | * | ||
| 433 | * Get a free buffer from pool of free read buffers and add to read buffer | ||
| 434 | * pool to add contents. Send a flow control request to firmware to be able | ||
| 435 | * send next message. | ||
| 436 | * | ||
| 437 | * Return: 0 if successful or error code on failure | ||
| 438 | */ | ||
| 439 | int ishtp_cl_read_start(struct ishtp_cl *cl) | ||
| 440 | { | ||
| 441 | struct ishtp_device *dev; | ||
| 442 | struct ishtp_cl_rb *rb; | ||
| 443 | int rets; | ||
| 444 | int i; | ||
| 445 | unsigned long flags; | ||
| 446 | unsigned long dev_flags; | ||
| 447 | |||
| 448 | if (WARN_ON(!cl || !cl->dev)) | ||
| 449 | return -ENODEV; | ||
| 450 | |||
| 451 | dev = cl->dev; | ||
| 452 | |||
| 453 | if (cl->state != ISHTP_CL_CONNECTED) | ||
| 454 | return -ENODEV; | ||
| 455 | |||
| 456 | if (dev->dev_state != ISHTP_DEV_ENABLED) | ||
| 457 | return -ENODEV; | ||
| 458 | |||
| 459 | i = ishtp_fw_cl_by_id(dev, cl->fw_client_id); | ||
| 460 | if (i < 0) { | ||
| 461 | dev_err(&cl->device->dev, "no such fw client %d\n", | ||
| 462 | cl->fw_client_id); | ||
| 463 | return -ENODEV; | ||
| 464 | } | ||
| 465 | |||
| 466 | /* The current rb is the head of the free rb list */ | ||
| 467 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
| 468 | if (list_empty(&cl->free_rb_list.list)) { | ||
| 469 | dev_warn(&cl->device->dev, | ||
| 470 | "[ishtp-ish] Rx buffers pool is empty\n"); | ||
| 471 | rets = -ENOMEM; | ||
| 472 | rb = NULL; | ||
| 473 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
| 474 | goto out; | ||
| 475 | } | ||
| 476 | rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, list); | ||
| 477 | list_del_init(&rb->list); | ||
| 478 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
| 479 | |||
| 480 | rb->cl = cl; | ||
| 481 | rb->buf_idx = 0; | ||
| 482 | |||
| 483 | INIT_LIST_HEAD(&rb->list); | ||
| 484 | rets = 0; | ||
| 485 | |||
| 486 | /* | ||
| 487 | * This must be BEFORE sending flow control - | ||
| 488 | * response in ISR may come too fast... | ||
| 489 | */ | ||
| 490 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
| 491 | list_add_tail(&rb->list, &dev->read_list.list); | ||
| 492 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
| 493 | if (ishtp_hbm_cl_flow_control_req(dev, cl)) { | ||
| 494 | rets = -ENODEV; | ||
| 495 | goto out; | ||
| 496 | } | ||
| 497 | out: | ||
| 498 | /* if ishtp_hbm_cl_flow_control_req failed, return rb to free list */ | ||
| 499 | if (rets && rb) { | ||
| 500 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
| 501 | list_del(&rb->list); | ||
| 502 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
| 503 | |||
| 504 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
| 505 | list_add_tail(&rb->list, &cl->free_rb_list.list); | ||
| 506 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
| 507 | } | ||
| 508 | return rets; | ||
| 509 | } | ||
| 510 | |||
| 511 | /** | ||
| 512 | * ishtp_cl_send() - Send a message to firmware | ||
| 513 | * @cl: client device instance | ||
| 514 | * @buf: message buffer | ||
| 515 | * @length: length of message | ||
| 516 | * | ||
| 517 | * If the client is correct state to send message, this function gets a buffer | ||
| 518 | * from tx ring buffers, copy the message data and call to send the message | ||
| 519 | * using ishtp_cl_send_msg() | ||
| 520 | * | ||
| 521 | * Return: 0 if successful or error code on failure | ||
| 522 | */ | ||
| 523 | int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length) | ||
| 524 | { | ||
| 525 | struct ishtp_device *dev; | ||
| 526 | int id; | ||
| 527 | struct ishtp_cl_tx_ring *cl_msg; | ||
| 528 | int have_msg_to_send = 0; | ||
| 529 | unsigned long tx_flags, tx_free_flags; | ||
| 530 | |||
| 531 | if (WARN_ON(!cl || !cl->dev)) | ||
| 532 | return -ENODEV; | ||
| 533 | |||
| 534 | dev = cl->dev; | ||
| 535 | |||
| 536 | if (cl->state != ISHTP_CL_CONNECTED) { | ||
| 537 | ++cl->err_send_msg; | ||
| 538 | return -EPIPE; | ||
| 539 | } | ||
| 540 | |||
| 541 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
| 542 | ++cl->err_send_msg; | ||
| 543 | return -ENODEV; | ||
| 544 | } | ||
| 545 | |||
| 546 | /* Check if we have fw client device */ | ||
| 547 | id = ishtp_fw_cl_by_id(dev, cl->fw_client_id); | ||
| 548 | if (id < 0) { | ||
| 549 | ++cl->err_send_msg; | ||
| 550 | return -ENOENT; | ||
| 551 | } | ||
| 552 | |||
| 553 | if (length > dev->fw_clients[id].props.max_msg_length) { | ||
| 554 | ++cl->err_send_msg; | ||
| 555 | return -EMSGSIZE; | ||
| 556 | } | ||
| 557 | |||
| 558 | /* No free bufs */ | ||
| 559 | spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags); | ||
| 560 | if (list_empty(&cl->tx_free_list.list)) { | ||
| 561 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, | ||
| 562 | tx_free_flags); | ||
| 563 | ++cl->err_send_msg; | ||
| 564 | return -ENOMEM; | ||
| 565 | } | ||
| 566 | |||
| 567 | cl_msg = list_first_entry(&cl->tx_free_list.list, | ||
| 568 | struct ishtp_cl_tx_ring, list); | ||
| 569 | if (!cl_msg->send_buf.data) { | ||
| 570 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, | ||
| 571 | tx_free_flags); | ||
| 572 | return -EIO; | ||
| 573 | /* Should not happen, as free list is pre-allocated */ | ||
| 574 | } | ||
| 575 | /* | ||
| 576 | * This is safe, as 'length' is already checked for not exceeding | ||
| 577 | * max ISHTP message size per client | ||
| 578 | */ | ||
| 579 | list_del_init(&cl_msg->list); | ||
| 580 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags); | ||
| 581 | memcpy(cl_msg->send_buf.data, buf, length); | ||
| 582 | cl_msg->send_buf.size = length; | ||
| 583 | spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); | ||
| 584 | have_msg_to_send = !list_empty(&cl->tx_list.list); | ||
| 585 | list_add_tail(&cl_msg->list, &cl->tx_list.list); | ||
| 586 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 587 | |||
| 588 | if (!have_msg_to_send && cl->ishtp_flow_ctrl_creds > 0) | ||
| 589 | ishtp_cl_send_msg(dev, cl); | ||
| 590 | |||
| 591 | return 0; | ||
| 592 | } | ||
| 593 | EXPORT_SYMBOL(ishtp_cl_send); | ||
| 594 | |||
| 595 | /** | ||
| 596 | * ishtp_cl_read_complete() - read complete | ||
| 597 | * @rb: Pointer to client request block | ||
| 598 | * | ||
| 599 | * If the message is completely received call ishtp_cl_bus_rx_event() | ||
| 600 | * to process message | ||
| 601 | */ | ||
| 602 | static void ishtp_cl_read_complete(struct ishtp_cl_rb *rb) | ||
| 603 | { | ||
| 604 | unsigned long flags; | ||
| 605 | int schedule_work_flag = 0; | ||
| 606 | struct ishtp_cl *cl = rb->cl; | ||
| 607 | |||
| 608 | spin_lock_irqsave(&cl->in_process_spinlock, flags); | ||
| 609 | /* | ||
| 610 | * if in-process list is empty, then need to schedule | ||
| 611 | * the processing thread | ||
| 612 | */ | ||
| 613 | schedule_work_flag = list_empty(&cl->in_process_list.list); | ||
| 614 | list_add_tail(&rb->list, &cl->in_process_list.list); | ||
| 615 | spin_unlock_irqrestore(&cl->in_process_spinlock, flags); | ||
| 616 | |||
| 617 | if (schedule_work_flag) | ||
| 618 | ishtp_cl_bus_rx_event(cl->device); | ||
| 619 | } | ||
| 620 | |||
| 621 | /** | ||
| 622 | * ipc_tx_callback() - IPC tx callback function | ||
| 623 | * @prm: Pointer to client device instance | ||
| 624 | * | ||
| 625 | * Send message over IPC either first time or on callback on previous message | ||
| 626 | * completion | ||
| 627 | */ | ||
| 628 | static void ipc_tx_callback(void *prm) | ||
| 629 | { | ||
| 630 | struct ishtp_cl *cl = prm; | ||
| 631 | struct ishtp_cl_tx_ring *cl_msg; | ||
| 632 | size_t rem; | ||
| 633 | struct ishtp_device *dev = (cl ? cl->dev : NULL); | ||
| 634 | struct ishtp_msg_hdr ishtp_hdr; | ||
| 635 | unsigned long tx_flags, tx_free_flags; | ||
| 636 | unsigned char *pmsg; | ||
| 637 | |||
| 638 | if (!dev) | ||
| 639 | return; | ||
| 640 | |||
| 641 | /* | ||
| 642 | * Other conditions if some critical error has | ||
| 643 | * occurred before this callback is called | ||
| 644 | */ | ||
| 645 | if (dev->dev_state != ISHTP_DEV_ENABLED) | ||
| 646 | return; | ||
| 647 | |||
| 648 | if (cl->state != ISHTP_CL_CONNECTED) | ||
| 649 | return; | ||
| 650 | |||
| 651 | spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); | ||
| 652 | if (list_empty(&cl->tx_list.list)) { | ||
| 653 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 654 | return; | ||
| 655 | } | ||
| 656 | |||
| 657 | if (cl->ishtp_flow_ctrl_creds != 1 && !cl->sending) { | ||
| 658 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 659 | return; | ||
| 660 | } | ||
| 661 | |||
| 662 | if (!cl->sending) { | ||
| 663 | --cl->ishtp_flow_ctrl_creds; | ||
| 664 | cl->last_ipc_acked = 0; | ||
| 665 | cl->last_tx_path = CL_TX_PATH_IPC; | ||
| 666 | cl->sending = 1; | ||
| 667 | } | ||
| 668 | |||
| 669 | cl_msg = list_entry(cl->tx_list.list.next, struct ishtp_cl_tx_ring, | ||
| 670 | list); | ||
| 671 | rem = cl_msg->send_buf.size - cl->tx_offs; | ||
| 672 | |||
| 673 | ishtp_hdr.host_addr = cl->host_client_id; | ||
| 674 | ishtp_hdr.fw_addr = cl->fw_client_id; | ||
| 675 | ishtp_hdr.reserved = 0; | ||
| 676 | pmsg = cl_msg->send_buf.data + cl->tx_offs; | ||
| 677 | |||
| 678 | if (rem <= dev->mtu) { | ||
| 679 | ishtp_hdr.length = rem; | ||
| 680 | ishtp_hdr.msg_complete = 1; | ||
| 681 | cl->sending = 0; | ||
| 682 | list_del_init(&cl_msg->list); /* Must be before write */ | ||
| 683 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 684 | /* Submit to IPC queue with no callback */ | ||
| 685 | ishtp_write_message(dev, &ishtp_hdr, pmsg); | ||
| 686 | spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags); | ||
| 687 | list_add_tail(&cl_msg->list, &cl->tx_free_list.list); | ||
| 688 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, | ||
| 689 | tx_free_flags); | ||
| 690 | } else { | ||
| 691 | /* Send IPC fragment */ | ||
| 692 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 693 | cl->tx_offs += dev->mtu; | ||
| 694 | ishtp_hdr.length = dev->mtu; | ||
| 695 | ishtp_hdr.msg_complete = 0; | ||
| 696 | ishtp_send_msg(dev, &ishtp_hdr, pmsg, ipc_tx_callback, cl); | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | /** | ||
| 701 | * ishtp_cl_send_msg_ipc() -Send message using IPC | ||
| 702 | * @dev: ISHTP device instance | ||
| 703 | * @cl: Pointer to client device instance | ||
| 704 | * | ||
| 705 | * Send message over IPC not using DMA | ||
| 706 | */ | ||
| 707 | static void ishtp_cl_send_msg_ipc(struct ishtp_device *dev, | ||
| 708 | struct ishtp_cl *cl) | ||
| 709 | { | ||
| 710 | /* If last DMA message wasn't acked yet, leave this one in Tx queue */ | ||
| 711 | if (cl->last_tx_path == CL_TX_PATH_DMA && cl->last_dma_acked == 0) | ||
| 712 | return; | ||
| 713 | |||
| 714 | cl->tx_offs = 0; | ||
| 715 | ipc_tx_callback(cl); | ||
| 716 | ++cl->send_msg_cnt_ipc; | ||
| 717 | } | ||
| 718 | |||
| 719 | /** | ||
| 720 | * ishtp_cl_send_msg_dma() -Send message using DMA | ||
| 721 | * @dev: ISHTP device instance | ||
| 722 | * @cl: Pointer to client device instance | ||
| 723 | * | ||
| 724 | * Send message using DMA | ||
| 725 | */ | ||
| 726 | static void ishtp_cl_send_msg_dma(struct ishtp_device *dev, | ||
| 727 | struct ishtp_cl *cl) | ||
| 728 | { | ||
| 729 | struct ishtp_msg_hdr hdr; | ||
| 730 | struct dma_xfer_hbm dma_xfer; | ||
| 731 | unsigned char *msg_addr; | ||
| 732 | int off; | ||
| 733 | struct ishtp_cl_tx_ring *cl_msg; | ||
| 734 | unsigned long tx_flags, tx_free_flags; | ||
| 735 | |||
| 736 | /* If last IPC message wasn't acked yet, leave this one in Tx queue */ | ||
| 737 | if (cl->last_tx_path == CL_TX_PATH_IPC && cl->last_ipc_acked == 0) | ||
| 738 | return; | ||
| 739 | |||
| 740 | spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); | ||
| 741 | if (list_empty(&cl->tx_list.list)) { | ||
| 742 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 743 | return; | ||
| 744 | } | ||
| 745 | |||
| 746 | cl_msg = list_entry(cl->tx_list.list.next, struct ishtp_cl_tx_ring, | ||
| 747 | list); | ||
| 748 | |||
| 749 | msg_addr = ishtp_cl_get_dma_send_buf(dev, cl_msg->send_buf.size); | ||
| 750 | if (!msg_addr) { | ||
| 751 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 752 | if (dev->transfer_path == CL_TX_PATH_DEFAULT) | ||
| 753 | ishtp_cl_send_msg_ipc(dev, cl); | ||
| 754 | return; | ||
| 755 | } | ||
| 756 | |||
| 757 | list_del_init(&cl_msg->list); /* Must be before write */ | ||
| 758 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
| 759 | |||
| 760 | --cl->ishtp_flow_ctrl_creds; | ||
| 761 | cl->last_dma_acked = 0; | ||
| 762 | cl->last_dma_addr = msg_addr; | ||
| 763 | cl->last_tx_path = CL_TX_PATH_DMA; | ||
| 764 | |||
| 765 | /* write msg to dma buf */ | ||
| 766 | memcpy(msg_addr, cl_msg->send_buf.data, cl_msg->send_buf.size); | ||
| 767 | |||
| 768 | /* send dma_xfer hbm msg */ | ||
| 769 | off = msg_addr - (unsigned char *)dev->ishtp_host_dma_tx_buf; | ||
| 770 | ishtp_hbm_hdr(&hdr, sizeof(struct dma_xfer_hbm)); | ||
| 771 | dma_xfer.hbm = DMA_XFER; | ||
| 772 | dma_xfer.fw_client_id = cl->fw_client_id; | ||
| 773 | dma_xfer.host_client_id = cl->host_client_id; | ||
| 774 | dma_xfer.reserved = 0; | ||
| 775 | dma_xfer.msg_addr = dev->ishtp_host_dma_tx_buf_phys + off; | ||
| 776 | dma_xfer.msg_length = cl_msg->send_buf.size; | ||
| 777 | dma_xfer.reserved2 = 0; | ||
| 778 | ishtp_write_message(dev, &hdr, (unsigned char *)&dma_xfer); | ||
| 779 | spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags); | ||
| 780 | list_add_tail(&cl_msg->list, &cl->tx_free_list.list); | ||
| 781 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags); | ||
| 782 | ++cl->send_msg_cnt_dma; | ||
| 783 | } | ||
| 784 | |||
| 785 | /** | ||
| 786 | * ishtp_cl_send_msg() -Send message using DMA or IPC | ||
| 787 | * @dev: ISHTP device instance | ||
| 788 | * @cl: Pointer to client device instance | ||
| 789 | * | ||
| 790 | * Send message using DMA or IPC based on transfer_path | ||
| 791 | */ | ||
| 792 | void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl) | ||
| 793 | { | ||
| 794 | if (dev->transfer_path == CL_TX_PATH_DMA) | ||
| 795 | ishtp_cl_send_msg_dma(dev, cl); | ||
| 796 | else | ||
| 797 | ishtp_cl_send_msg_ipc(dev, cl); | ||
| 798 | } | ||
| 799 | |||
| 800 | /** | ||
| 801 | * recv_ishtp_cl_msg() -Receive client message | ||
| 802 | * @dev: ISHTP device instance | ||
| 803 | * @ishtp_hdr: Pointer to message header | ||
| 804 | * | ||
| 805 | * Receive and dispatch ISHTP client messages. This function executes in ISR | ||
| 806 | * context | ||
| 807 | */ | ||
| 808 | void recv_ishtp_cl_msg(struct ishtp_device *dev, | ||
| 809 | struct ishtp_msg_hdr *ishtp_hdr) | ||
| 810 | { | ||
| 811 | struct ishtp_cl *cl; | ||
| 812 | struct ishtp_cl_rb *rb; | ||
| 813 | struct ishtp_cl_rb *new_rb; | ||
| 814 | unsigned char *buffer = NULL; | ||
| 815 | struct ishtp_cl_rb *complete_rb = NULL; | ||
| 816 | unsigned long dev_flags; | ||
| 817 | unsigned long flags; | ||
| 818 | int rb_count; | ||
| 819 | |||
| 820 | if (ishtp_hdr->reserved) { | ||
| 821 | dev_err(dev->devc, "corrupted message header.\n"); | ||
| 822 | goto eoi; | ||
| 823 | } | ||
| 824 | |||
| 825 | if (ishtp_hdr->length > IPC_PAYLOAD_SIZE) { | ||
| 826 | dev_err(dev->devc, | ||
| 827 | "ISHTP message length in hdr exceeds IPC MTU\n"); | ||
| 828 | goto eoi; | ||
| 829 | } | ||
| 830 | |||
| 831 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
| 832 | rb_count = -1; | ||
| 833 | list_for_each_entry(rb, &dev->read_list.list, list) { | ||
| 834 | ++rb_count; | ||
| 835 | cl = rb->cl; | ||
| 836 | if (!cl || !(cl->host_client_id == ishtp_hdr->host_addr && | ||
| 837 | cl->fw_client_id == ishtp_hdr->fw_addr) || | ||
| 838 | !(cl->state == ISHTP_CL_CONNECTED)) | ||
| 839 | continue; | ||
| 840 | |||
| 841 | /* If no Rx buffer is allocated, disband the rb */ | ||
| 842 | if (rb->buffer.size == 0 || rb->buffer.data == NULL) { | ||
| 843 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
| 844 | dev_flags); | ||
| 845 | dev_err(&cl->device->dev, | ||
| 846 | "Rx buffer is not allocated.\n"); | ||
| 847 | list_del(&rb->list); | ||
| 848 | ishtp_io_rb_free(rb); | ||
| 849 | cl->status = -ENOMEM; | ||
| 850 | goto eoi; | ||
| 851 | } | ||
| 852 | |||
| 853 | /* | ||
| 854 | * If message buffer overflown (exceeds max. client msg | ||
| 855 | * size, drop message and return to free buffer. | ||
| 856 | * Do we need to disconnect such a client? (We don't send | ||
| 857 | * back FC, so communication will be stuck anyway) | ||
| 858 | */ | ||
| 859 | if (rb->buffer.size < ishtp_hdr->length + rb->buf_idx) { | ||
| 860 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
| 861 | dev_flags); | ||
| 862 | dev_err(&cl->device->dev, | ||
| 863 | "message overflow. size %d len %d idx %ld\n", | ||
| 864 | rb->buffer.size, ishtp_hdr->length, | ||
| 865 | rb->buf_idx); | ||
| 866 | list_del(&rb->list); | ||
| 867 | ishtp_cl_io_rb_recycle(rb); | ||
| 868 | cl->status = -EIO; | ||
| 869 | goto eoi; | ||
| 870 | } | ||
| 871 | |||
| 872 | buffer = rb->buffer.data + rb->buf_idx; | ||
| 873 | dev->ops->ishtp_read(dev, buffer, ishtp_hdr->length); | ||
| 874 | |||
| 875 | rb->buf_idx += ishtp_hdr->length; | ||
| 876 | if (ishtp_hdr->msg_complete) { | ||
| 877 | /* Last fragment in message - it's complete */ | ||
| 878 | cl->status = 0; | ||
| 879 | list_del(&rb->list); | ||
| 880 | complete_rb = rb; | ||
| 881 | |||
| 882 | --cl->out_flow_ctrl_creds; | ||
| 883 | /* | ||
| 884 | * the whole msg arrived, send a new FC, and add a new | ||
| 885 | * rb buffer for the next coming msg | ||
| 886 | */ | ||
| 887 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
| 888 | |||
| 889 | if (!list_empty(&cl->free_rb_list.list)) { | ||
| 890 | new_rb = list_entry(cl->free_rb_list.list.next, | ||
| 891 | struct ishtp_cl_rb, list); | ||
| 892 | list_del_init(&new_rb->list); | ||
| 893 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
| 894 | flags); | ||
| 895 | new_rb->cl = cl; | ||
| 896 | new_rb->buf_idx = 0; | ||
| 897 | INIT_LIST_HEAD(&new_rb->list); | ||
| 898 | list_add_tail(&new_rb->list, | ||
| 899 | &dev->read_list.list); | ||
| 900 | |||
| 901 | ishtp_hbm_cl_flow_control_req(dev, cl); | ||
| 902 | } else { | ||
| 903 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
| 904 | flags); | ||
| 905 | } | ||
| 906 | } | ||
| 907 | /* One more fragment in message (even if this was last) */ | ||
| 908 | ++cl->recv_msg_num_frags; | ||
| 909 | |||
| 910 | /* | ||
| 911 | * We can safely break here (and in BH too), | ||
| 912 | * a single input message can go only to a single request! | ||
| 913 | */ | ||
| 914 | break; | ||
| 915 | } | ||
| 916 | |||
| 917 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
| 918 | /* If it's nobody's message, just read and discard it */ | ||
| 919 | if (!buffer) { | ||
| 920 | uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; | ||
| 921 | |||
| 922 | dev_err(dev->devc, "Dropped Rx msg - no request\n"); | ||
| 923 | dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length); | ||
| 924 | goto eoi; | ||
| 925 | } | ||
| 926 | |||
| 927 | if (complete_rb) { | ||
| 928 | getnstimeofday(&cl->ts_rx); | ||
| 929 | ++cl->recv_msg_cnt_ipc; | ||
| 930 | ishtp_cl_read_complete(complete_rb); | ||
| 931 | } | ||
| 932 | eoi: | ||
| 933 | return; | ||
| 934 | } | ||
| 935 | |||
| 936 | /** | ||
| 937 | * recv_ishtp_cl_msg_dma() -Receive client message | ||
| 938 | * @dev: ISHTP device instance | ||
| 939 | * @msg: message pointer | ||
| 940 | * @hbm: hbm buffer | ||
| 941 | * | ||
| 942 | * Receive and dispatch ISHTP client messages using DMA. This function executes | ||
| 943 | * in ISR context | ||
| 944 | */ | ||
| 945 | void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, | ||
| 946 | struct dma_xfer_hbm *hbm) | ||
| 947 | { | ||
| 948 | struct ishtp_cl *cl; | ||
| 949 | struct ishtp_cl_rb *rb; | ||
| 950 | struct ishtp_cl_rb *new_rb; | ||
| 951 | unsigned char *buffer = NULL; | ||
| 952 | struct ishtp_cl_rb *complete_rb = NULL; | ||
| 953 | unsigned long dev_flags; | ||
| 954 | unsigned long flags; | ||
| 955 | |||
| 956 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
| 957 | list_for_each_entry(rb, &dev->read_list.list, list) { | ||
| 958 | cl = rb->cl; | ||
| 959 | if (!cl || !(cl->host_client_id == hbm->host_client_id && | ||
| 960 | cl->fw_client_id == hbm->fw_client_id) || | ||
| 961 | !(cl->state == ISHTP_CL_CONNECTED)) | ||
| 962 | continue; | ||
| 963 | |||
| 964 | /* | ||
| 965 | * If no Rx buffer is allocated, disband the rb | ||
| 966 | */ | ||
| 967 | if (rb->buffer.size == 0 || rb->buffer.data == NULL) { | ||
| 968 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
| 969 | dev_flags); | ||
| 970 | dev_err(&cl->device->dev, | ||
| 971 | "response buffer is not allocated.\n"); | ||
| 972 | list_del(&rb->list); | ||
| 973 | ishtp_io_rb_free(rb); | ||
| 974 | cl->status = -ENOMEM; | ||
| 975 | goto eoi; | ||
| 976 | } | ||
| 977 | |||
| 978 | /* | ||
| 979 | * If message buffer overflown (exceeds max. client msg | ||
| 980 | * size, drop message and return to free buffer. | ||
| 981 | * Do we need to disconnect such a client? (We don't send | ||
| 982 | * back FC, so communication will be stuck anyway) | ||
| 983 | */ | ||
| 984 | if (rb->buffer.size < hbm->msg_length) { | ||
| 985 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
| 986 | dev_flags); | ||
| 987 | dev_err(&cl->device->dev, | ||
| 988 | "message overflow. size %d len %d idx %ld\n", | ||
| 989 | rb->buffer.size, hbm->msg_length, rb->buf_idx); | ||
| 990 | list_del(&rb->list); | ||
| 991 | ishtp_cl_io_rb_recycle(rb); | ||
| 992 | cl->status = -EIO; | ||
| 993 | goto eoi; | ||
| 994 | } | ||
| 995 | |||
| 996 | buffer = rb->buffer.data; | ||
| 997 | memcpy(buffer, msg, hbm->msg_length); | ||
| 998 | rb->buf_idx = hbm->msg_length; | ||
| 999 | |||
| 1000 | /* Last fragment in message - it's complete */ | ||
| 1001 | cl->status = 0; | ||
| 1002 | list_del(&rb->list); | ||
| 1003 | complete_rb = rb; | ||
| 1004 | |||
| 1005 | --cl->out_flow_ctrl_creds; | ||
| 1006 | /* | ||
| 1007 | * the whole msg arrived, send a new FC, and add a new | ||
| 1008 | * rb buffer for the next coming msg | ||
| 1009 | */ | ||
| 1010 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
| 1011 | |||
| 1012 | if (!list_empty(&cl->free_rb_list.list)) { | ||
| 1013 | new_rb = list_entry(cl->free_rb_list.list.next, | ||
| 1014 | struct ishtp_cl_rb, list); | ||
| 1015 | list_del_init(&new_rb->list); | ||
| 1016 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
| 1017 | flags); | ||
| 1018 | new_rb->cl = cl; | ||
| 1019 | new_rb->buf_idx = 0; | ||
| 1020 | INIT_LIST_HEAD(&new_rb->list); | ||
| 1021 | list_add_tail(&new_rb->list, | ||
| 1022 | &dev->read_list.list); | ||
| 1023 | |||
| 1024 | ishtp_hbm_cl_flow_control_req(dev, cl); | ||
| 1025 | } else { | ||
| 1026 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
| 1027 | flags); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | /* One more fragment in message (this is always last) */ | ||
| 1031 | ++cl->recv_msg_num_frags; | ||
| 1032 | |||
| 1033 | /* | ||
| 1034 | * We can safely break here (and in BH too), | ||
| 1035 | * a single input message can go only to a single request! | ||
| 1036 | */ | ||
| 1037 | break; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
| 1041 | /* If it's nobody's message, just read and discard it */ | ||
| 1042 | if (!buffer) { | ||
| 1043 | dev_err(dev->devc, "Dropped Rx (DMA) msg - no request\n"); | ||
| 1044 | goto eoi; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | if (complete_rb) { | ||
| 1048 | getnstimeofday(&cl->ts_rx); | ||
| 1049 | ++cl->recv_msg_cnt_dma; | ||
| 1050 | ishtp_cl_read_complete(complete_rb); | ||
| 1051 | } | ||
| 1052 | eoi: | ||
| 1053 | return; | ||
| 1054 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.h b/drivers/hid/intel-ish-hid/ishtp/client.h new file mode 100644 index 000000000000..444d069c2ed4 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/client.h | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP client logic | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef _ISHTP_CLIENT_H_ | ||
| 17 | #define _ISHTP_CLIENT_H_ | ||
| 18 | |||
| 19 | #include <linux/types.h> | ||
| 20 | #include "ishtp-dev.h" | ||
| 21 | |||
| 22 | /* Client state */ | ||
| 23 | enum cl_state { | ||
| 24 | ISHTP_CL_INITIALIZING = 0, | ||
| 25 | ISHTP_CL_CONNECTING, | ||
| 26 | ISHTP_CL_CONNECTED, | ||
| 27 | ISHTP_CL_DISCONNECTING, | ||
| 28 | ISHTP_CL_DISCONNECTED | ||
| 29 | }; | ||
| 30 | |||
| 31 | /* Tx and Rx ring size */ | ||
| 32 | #define CL_DEF_RX_RING_SIZE 2 | ||
| 33 | #define CL_DEF_TX_RING_SIZE 2 | ||
| 34 | #define CL_MAX_RX_RING_SIZE 32 | ||
| 35 | #define CL_MAX_TX_RING_SIZE 32 | ||
| 36 | |||
| 37 | #define DMA_SLOT_SIZE 4096 | ||
| 38 | /* Number of IPC fragments after which it's worth sending via DMA */ | ||
| 39 | #define DMA_WORTH_THRESHOLD 3 | ||
| 40 | |||
| 41 | /* DMA/IPC Tx paths. Other the default means enforcement */ | ||
| 42 | #define CL_TX_PATH_DEFAULT 0 | ||
| 43 | #define CL_TX_PATH_IPC 1 | ||
| 44 | #define CL_TX_PATH_DMA 2 | ||
| 45 | |||
| 46 | /* Client Tx buffer list entry */ | ||
| 47 | struct ishtp_cl_tx_ring { | ||
| 48 | struct list_head list; | ||
| 49 | struct ishtp_msg_data send_buf; | ||
| 50 | }; | ||
| 51 | |||
| 52 | /* ISHTP client instance */ | ||
| 53 | struct ishtp_cl { | ||
| 54 | struct list_head link; | ||
| 55 | struct ishtp_device *dev; | ||
| 56 | enum cl_state state; | ||
| 57 | int status; | ||
| 58 | |||
| 59 | /* Link to ISHTP bus device */ | ||
| 60 | struct ishtp_cl_device *device; | ||
| 61 | |||
| 62 | /* ID of client connected */ | ||
| 63 | uint8_t host_client_id; | ||
| 64 | uint8_t fw_client_id; | ||
| 65 | uint8_t ishtp_flow_ctrl_creds; | ||
| 66 | uint8_t out_flow_ctrl_creds; | ||
| 67 | |||
| 68 | /* dma */ | ||
| 69 | int last_tx_path; | ||
| 70 | /* 0: ack wasn't received,1:ack was received */ | ||
| 71 | int last_dma_acked; | ||
| 72 | unsigned char *last_dma_addr; | ||
| 73 | /* 0: ack wasn't received,1:ack was received */ | ||
| 74 | int last_ipc_acked; | ||
| 75 | |||
| 76 | /* Rx ring buffer pool */ | ||
| 77 | unsigned int rx_ring_size; | ||
| 78 | struct ishtp_cl_rb free_rb_list; | ||
| 79 | spinlock_t free_list_spinlock; | ||
| 80 | /* Rx in-process list */ | ||
| 81 | struct ishtp_cl_rb in_process_list; | ||
| 82 | spinlock_t in_process_spinlock; | ||
| 83 | |||
| 84 | /* Client Tx buffers list */ | ||
| 85 | unsigned int tx_ring_size; | ||
| 86 | struct ishtp_cl_tx_ring tx_list, tx_free_list; | ||
| 87 | spinlock_t tx_list_spinlock; | ||
| 88 | spinlock_t tx_free_list_spinlock; | ||
| 89 | size_t tx_offs; /* Offset in buffer at head of 'tx_list' */ | ||
| 90 | |||
| 91 | /** | ||
| 92 | * if we get a FC, and the list is not empty, we must know whether we | ||
| 93 | * are at the middle of sending. | ||
| 94 | * if so -need to increase FC counter, otherwise, need to start sending | ||
| 95 | * the first msg in list | ||
| 96 | * (!)This is for counting-FC implementation only. Within single-FC the | ||
| 97 | * other party may NOT send FC until it receives complete message | ||
| 98 | */ | ||
| 99 | int sending; | ||
| 100 | |||
| 101 | /* Send FC spinlock */ | ||
| 102 | spinlock_t fc_spinlock; | ||
| 103 | |||
| 104 | /* wait queue for connect and disconnect response from FW */ | ||
| 105 | wait_queue_head_t wait_ctrl_res; | ||
| 106 | |||
| 107 | /* Error stats */ | ||
| 108 | unsigned int err_send_msg; | ||
| 109 | unsigned int err_send_fc; | ||
| 110 | |||
| 111 | /* Send/recv stats */ | ||
| 112 | unsigned int send_msg_cnt_ipc; | ||
| 113 | unsigned int send_msg_cnt_dma; | ||
| 114 | unsigned int recv_msg_cnt_ipc; | ||
| 115 | unsigned int recv_msg_cnt_dma; | ||
| 116 | unsigned int recv_msg_num_frags; | ||
| 117 | unsigned int ishtp_flow_ctrl_cnt; | ||
| 118 | unsigned int out_flow_ctrl_cnt; | ||
| 119 | |||
| 120 | /* Rx msg ... out FC timing */ | ||
| 121 | struct timespec ts_rx; | ||
| 122 | struct timespec ts_out_fc; | ||
| 123 | struct timespec ts_max_fc_delay; | ||
| 124 | void *client_data; | ||
| 125 | }; | ||
| 126 | |||
| 127 | /* Client connection managenment internal functions */ | ||
| 128 | int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, uuid_le *uuid); | ||
| 129 | int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id); | ||
| 130 | void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl); | ||
| 131 | void recv_ishtp_cl_msg(struct ishtp_device *dev, | ||
| 132 | struct ishtp_msg_hdr *ishtp_hdr); | ||
| 133 | int ishtp_cl_read_start(struct ishtp_cl *cl); | ||
| 134 | |||
| 135 | /* Ring Buffer I/F */ | ||
| 136 | int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl); | ||
| 137 | int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl); | ||
| 138 | void ishtp_cl_free_rx_ring(struct ishtp_cl *cl); | ||
| 139 | void ishtp_cl_free_tx_ring(struct ishtp_cl *cl); | ||
| 140 | |||
| 141 | /* DMA I/F functions */ | ||
| 142 | void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, | ||
| 143 | struct dma_xfer_hbm *hbm); | ||
| 144 | void ishtp_cl_alloc_dma_buf(struct ishtp_device *dev); | ||
| 145 | void ishtp_cl_free_dma_buf(struct ishtp_device *dev); | ||
| 146 | void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev, | ||
| 147 | uint32_t size); | ||
| 148 | void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev, | ||
| 149 | void *msg_addr, | ||
| 150 | uint8_t size); | ||
| 151 | |||
| 152 | /* Request blocks alloc/free I/F */ | ||
| 153 | struct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl); | ||
| 154 | void ishtp_io_rb_free(struct ishtp_cl_rb *priv_rb); | ||
| 155 | int ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length); | ||
| 156 | |||
| 157 | /** | ||
| 158 | * ishtp_cl_cmp_id - tells if file private data have same id | ||
| 159 | * returns true - if ids are the same and not NULL | ||
| 160 | */ | ||
| 161 | static inline bool ishtp_cl_cmp_id(const struct ishtp_cl *cl1, | ||
| 162 | const struct ishtp_cl *cl2) | ||
| 163 | { | ||
| 164 | return cl1 && cl2 && | ||
| 165 | (cl1->host_client_id == cl2->host_client_id) && | ||
| 166 | (cl1->fw_client_id == cl2->fw_client_id); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* exported functions from ISHTP under client management scope */ | ||
| 170 | struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev); | ||
| 171 | void ishtp_cl_free(struct ishtp_cl *cl); | ||
| 172 | int ishtp_cl_link(struct ishtp_cl *cl, int id); | ||
| 173 | void ishtp_cl_unlink(struct ishtp_cl *cl); | ||
| 174 | int ishtp_cl_disconnect(struct ishtp_cl *cl); | ||
| 175 | int ishtp_cl_connect(struct ishtp_cl *cl); | ||
| 176 | int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length); | ||
| 177 | int ishtp_cl_flush_queues(struct ishtp_cl *cl); | ||
| 178 | |||
| 179 | /* exported functions from ISHTP client buffer management scope */ | ||
| 180 | int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb); | ||
| 181 | |||
| 182 | #endif /* _ISHTP_CLIENT_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/dma-if.c b/drivers/hid/intel-ish-hid/ishtp/dma-if.c new file mode 100644 index 000000000000..2783f3666114 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/dma-if.c | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP DMA I/F functions | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/sched.h> | ||
| 19 | #include <linux/wait.h> | ||
| 20 | #include <linux/delay.h> | ||
| 21 | #include <linux/dma-mapping.h> | ||
| 22 | #include "ishtp-dev.h" | ||
| 23 | #include "client.h" | ||
| 24 | |||
| 25 | /** | ||
| 26 | * ishtp_cl_alloc_dma_buf() - Allocate DMA RX and TX buffer | ||
| 27 | * @dev: ishtp device | ||
| 28 | * | ||
| 29 | * Allocate RX and TX DMA buffer once during bus setup. | ||
| 30 | * It allocates 1MB, RX and TX DMA buffer, which are divided | ||
| 31 | * into slots. | ||
| 32 | */ | ||
| 33 | void ishtp_cl_alloc_dma_buf(struct ishtp_device *dev) | ||
| 34 | { | ||
| 35 | dma_addr_t h; | ||
| 36 | |||
| 37 | if (dev->ishtp_host_dma_tx_buf) | ||
| 38 | return; | ||
| 39 | |||
| 40 | dev->ishtp_host_dma_tx_buf_size = 1024*1024; | ||
| 41 | dev->ishtp_host_dma_rx_buf_size = 1024*1024; | ||
| 42 | |||
| 43 | /* Allocate Tx buffer and init usage bitmap */ | ||
| 44 | dev->ishtp_host_dma_tx_buf = dma_alloc_coherent(dev->devc, | ||
| 45 | dev->ishtp_host_dma_tx_buf_size, | ||
| 46 | &h, GFP_KERNEL); | ||
| 47 | if (dev->ishtp_host_dma_tx_buf) | ||
| 48 | dev->ishtp_host_dma_tx_buf_phys = h; | ||
| 49 | |||
| 50 | dev->ishtp_dma_num_slots = dev->ishtp_host_dma_tx_buf_size / | ||
| 51 | DMA_SLOT_SIZE; | ||
| 52 | |||
| 53 | dev->ishtp_dma_tx_map = kcalloc(dev->ishtp_dma_num_slots, | ||
| 54 | sizeof(uint8_t), | ||
| 55 | GFP_KERNEL); | ||
| 56 | spin_lock_init(&dev->ishtp_dma_tx_lock); | ||
| 57 | |||
| 58 | /* Allocate Rx buffer */ | ||
| 59 | dev->ishtp_host_dma_rx_buf = dma_alloc_coherent(dev->devc, | ||
| 60 | dev->ishtp_host_dma_rx_buf_size, | ||
| 61 | &h, GFP_KERNEL); | ||
| 62 | |||
| 63 | if (dev->ishtp_host_dma_rx_buf) | ||
| 64 | dev->ishtp_host_dma_rx_buf_phys = h; | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * ishtp_cl_free_dma_buf() - Free DMA RX and TX buffer | ||
| 69 | * @dev: ishtp device | ||
| 70 | * | ||
| 71 | * Free DMA buffer when all clients are released. This is | ||
| 72 | * only happens during error path in ISH built in driver | ||
| 73 | * model | ||
| 74 | */ | ||
| 75 | void ishtp_cl_free_dma_buf(struct ishtp_device *dev) | ||
| 76 | { | ||
| 77 | dma_addr_t h; | ||
| 78 | |||
| 79 | if (dev->ishtp_host_dma_tx_buf) { | ||
| 80 | h = dev->ishtp_host_dma_tx_buf_phys; | ||
| 81 | dma_free_coherent(dev->devc, dev->ishtp_host_dma_tx_buf_size, | ||
| 82 | dev->ishtp_host_dma_tx_buf, h); | ||
| 83 | } | ||
| 84 | |||
| 85 | if (dev->ishtp_host_dma_rx_buf) { | ||
| 86 | h = dev->ishtp_host_dma_rx_buf_phys; | ||
| 87 | dma_free_coherent(dev->devc, dev->ishtp_host_dma_rx_buf_size, | ||
| 88 | dev->ishtp_host_dma_rx_buf, h); | ||
| 89 | } | ||
| 90 | |||
| 91 | kfree(dev->ishtp_dma_tx_map); | ||
| 92 | dev->ishtp_host_dma_tx_buf = NULL; | ||
| 93 | dev->ishtp_host_dma_rx_buf = NULL; | ||
| 94 | dev->ishtp_dma_tx_map = NULL; | ||
| 95 | } | ||
| 96 | |||
| 97 | /* | ||
| 98 | * ishtp_cl_get_dma_send_buf() - Get a DMA memory slot | ||
| 99 | * @dev: ishtp device | ||
| 100 | * @size: Size of memory to get | ||
| 101 | * | ||
| 102 | * Find and return free address of "size" bytes in dma tx buffer. | ||
| 103 | * the function will mark this address as "in-used" memory. | ||
| 104 | * | ||
| 105 | * Return: NULL when no free buffer else a buffer to copy | ||
| 106 | */ | ||
| 107 | void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev, | ||
| 108 | uint32_t size) | ||
| 109 | { | ||
| 110 | unsigned long flags; | ||
| 111 | int i, j, free; | ||
| 112 | /* additional slot is needed if there is rem */ | ||
| 113 | int required_slots = (size / DMA_SLOT_SIZE) | ||
| 114 | + 1 * (size % DMA_SLOT_SIZE != 0); | ||
| 115 | |||
| 116 | spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); | ||
| 117 | for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) { | ||
| 118 | free = 1; | ||
| 119 | for (j = 0; j < required_slots; j++) | ||
| 120 | if (dev->ishtp_dma_tx_map[i+j]) { | ||
| 121 | free = 0; | ||
| 122 | i += j; | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | if (free) { | ||
| 126 | /* mark memory as "caught" */ | ||
| 127 | for (j = 0; j < required_slots; j++) | ||
| 128 | dev->ishtp_dma_tx_map[i+j] = 1; | ||
| 129 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
| 130 | return (i * DMA_SLOT_SIZE) + | ||
| 131 | (unsigned char *)dev->ishtp_host_dma_tx_buf; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
| 135 | dev_err(dev->devc, "No free DMA buffer to send msg\n"); | ||
| 136 | return NULL; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* | ||
| 140 | * ishtp_cl_release_dma_acked_mem() - Release DMA memory slot | ||
| 141 | * @dev: ishtp device | ||
| 142 | * @msg_addr: message address of slot | ||
| 143 | * @size: Size of memory to get | ||
| 144 | * | ||
| 145 | * Release_dma_acked_mem - returnes the acked memory to free list. | ||
| 146 | * (from msg_addr, size bytes long) | ||
| 147 | */ | ||
| 148 | void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev, | ||
| 149 | void *msg_addr, | ||
| 150 | uint8_t size) | ||
| 151 | { | ||
| 152 | unsigned long flags; | ||
| 153 | int acked_slots = (size / DMA_SLOT_SIZE) | ||
| 154 | + 1 * (size % DMA_SLOT_SIZE != 0); | ||
| 155 | int i, j; | ||
| 156 | |||
| 157 | if ((msg_addr - dev->ishtp_host_dma_tx_buf) % DMA_SLOT_SIZE) { | ||
| 158 | dev_err(dev->devc, "Bad DMA Tx ack address\n"); | ||
| 159 | return; | ||
| 160 | } | ||
| 161 | |||
| 162 | i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE; | ||
| 163 | spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); | ||
| 164 | for (j = 0; j < acked_slots; j++) { | ||
| 165 | if ((i + j) >= dev->ishtp_dma_num_slots || | ||
| 166 | !dev->ishtp_dma_tx_map[i+j]) { | ||
| 167 | /* no such slot, or memory is already free */ | ||
| 168 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
| 169 | dev_err(dev->devc, "Bad DMA Tx ack address\n"); | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | dev->ishtp_dma_tx_map[i+j] = 0; | ||
| 173 | } | ||
| 174 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
| 175 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c new file mode 100644 index 000000000000..74bffee60774 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c | |||
| @@ -0,0 +1,1032 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP bus layer messages handling | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/export.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/sched.h> | ||
| 20 | #include <linux/wait.h> | ||
| 21 | #include <linux/spinlock.h> | ||
| 22 | #include <linux/miscdevice.h> | ||
| 23 | #include "ishtp-dev.h" | ||
| 24 | #include "hbm.h" | ||
| 25 | #include "client.h" | ||
| 26 | |||
| 27 | /** | ||
| 28 | * ishtp_hbm_fw_cl_allocate() - Allocate FW clients | ||
| 29 | * @dev: ISHTP device instance | ||
| 30 | * | ||
| 31 | * Allocates storage for fw clients | ||
| 32 | */ | ||
| 33 | static void ishtp_hbm_fw_cl_allocate(struct ishtp_device *dev) | ||
| 34 | { | ||
| 35 | struct ishtp_fw_client *clients; | ||
| 36 | int b; | ||
| 37 | |||
| 38 | /* count how many ISH clients we have */ | ||
| 39 | for_each_set_bit(b, dev->fw_clients_map, ISHTP_CLIENTS_MAX) | ||
| 40 | dev->fw_clients_num++; | ||
| 41 | |||
| 42 | if (dev->fw_clients_num <= 0) | ||
| 43 | return; | ||
| 44 | |||
| 45 | /* allocate storage for fw clients representation */ | ||
| 46 | clients = kcalloc(dev->fw_clients_num, sizeof(struct ishtp_fw_client), | ||
| 47 | GFP_KERNEL); | ||
| 48 | if (!clients) { | ||
| 49 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
| 50 | ish_hw_reset(dev); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | dev->fw_clients = clients; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * ishtp_hbm_cl_hdr() - construct client hbm header | ||
| 58 | * @cl: client | ||
| 59 | * @hbm_cmd: host bus message command | ||
| 60 | * @buf: buffer for cl header | ||
| 61 | * @len: buffer length | ||
| 62 | * | ||
| 63 | * Initialize HBM buffer | ||
| 64 | */ | ||
| 65 | static inline void ishtp_hbm_cl_hdr(struct ishtp_cl *cl, uint8_t hbm_cmd, | ||
| 66 | void *buf, size_t len) | ||
| 67 | { | ||
| 68 | struct ishtp_hbm_cl_cmd *cmd = buf; | ||
| 69 | |||
| 70 | memset(cmd, 0, len); | ||
| 71 | |||
| 72 | cmd->hbm_cmd = hbm_cmd; | ||
| 73 | cmd->host_addr = cl->host_client_id; | ||
| 74 | cmd->fw_addr = cl->fw_client_id; | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * ishtp_hbm_cl_addr_equal() - Compare client address | ||
| 79 | * @cl: client | ||
| 80 | * @buf: Client command buffer | ||
| 81 | * | ||
| 82 | * Compare client address with the address in command buffer | ||
| 83 | * | ||
| 84 | * Return: True if they have the same address | ||
| 85 | */ | ||
| 86 | static inline bool ishtp_hbm_cl_addr_equal(struct ishtp_cl *cl, void *buf) | ||
| 87 | { | ||
| 88 | struct ishtp_hbm_cl_cmd *cmd = buf; | ||
| 89 | |||
| 90 | return cl->host_client_id == cmd->host_addr && | ||
| 91 | cl->fw_client_id == cmd->fw_addr; | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * ishtp_hbm_start_wait() - Wait for HBM start message | ||
| 96 | * @dev: ISHTP device instance | ||
| 97 | * | ||
| 98 | * Wait for HBM start message from firmware | ||
| 99 | * | ||
| 100 | * Return: 0 if HBM start is/was received else timeout error | ||
| 101 | */ | ||
| 102 | int ishtp_hbm_start_wait(struct ishtp_device *dev) | ||
| 103 | { | ||
| 104 | int ret; | ||
| 105 | |||
| 106 | if (dev->hbm_state > ISHTP_HBM_START) | ||
| 107 | return 0; | ||
| 108 | |||
| 109 | dev_dbg(dev->devc, "Going to wait for ishtp start. hbm_state=%08X\n", | ||
| 110 | dev->hbm_state); | ||
| 111 | ret = wait_event_interruptible_timeout(dev->wait_hbm_recvd_msg, | ||
| 112 | dev->hbm_state >= ISHTP_HBM_STARTED, | ||
| 113 | (ISHTP_INTEROP_TIMEOUT * HZ)); | ||
| 114 | |||
| 115 | dev_dbg(dev->devc, | ||
| 116 | "Woke up from waiting for ishtp start. hbm_state=%08X\n", | ||
| 117 | dev->hbm_state); | ||
| 118 | |||
| 119 | if (ret <= 0 && (dev->hbm_state <= ISHTP_HBM_START)) { | ||
| 120 | dev->hbm_state = ISHTP_HBM_IDLE; | ||
| 121 | dev_err(dev->devc, | ||
| 122 | "waiting for ishtp start failed. ret=%d hbm_state=%08X\n", | ||
| 123 | ret, dev->hbm_state); | ||
| 124 | return -ETIMEDOUT; | ||
| 125 | } | ||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * ishtp_hbm_start_req() - Send HBM start message | ||
| 131 | * @dev: ISHTP device instance | ||
| 132 | * | ||
| 133 | * Send HBM start message to firmware | ||
| 134 | * | ||
| 135 | * Return: 0 if success else error code | ||
| 136 | */ | ||
| 137 | int ishtp_hbm_start_req(struct ishtp_device *dev) | ||
| 138 | { | ||
| 139 | struct ishtp_msg_hdr hdr; | ||
| 140 | unsigned char data[128]; | ||
| 141 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
| 142 | struct hbm_host_version_request *start_req; | ||
| 143 | const size_t len = sizeof(struct hbm_host_version_request); | ||
| 144 | |||
| 145 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
| 146 | |||
| 147 | /* host start message */ | ||
| 148 | start_req = (struct hbm_host_version_request *)data; | ||
| 149 | memset(start_req, 0, len); | ||
| 150 | start_req->hbm_cmd = HOST_START_REQ_CMD; | ||
| 151 | start_req->host_version.major_version = HBM_MAJOR_VERSION; | ||
| 152 | start_req->host_version.minor_version = HBM_MINOR_VERSION; | ||
| 153 | |||
| 154 | /* | ||
| 155 | * (!) Response to HBM start may be so quick that this thread would get | ||
| 156 | * preempted BEFORE managing to set hbm_state = ISHTP_HBM_START. | ||
| 157 | * So set it at first, change back to ISHTP_HBM_IDLE upon failure | ||
| 158 | */ | ||
| 159 | dev->hbm_state = ISHTP_HBM_START; | ||
| 160 | if (ishtp_write_message(dev, ishtp_hdr, data)) { | ||
| 161 | dev_err(dev->devc, "version message send failed\n"); | ||
| 162 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
| 163 | dev->hbm_state = ISHTP_HBM_IDLE; | ||
| 164 | ish_hw_reset(dev); | ||
| 165 | return -ENODEV; | ||
| 166 | } | ||
| 167 | |||
| 168 | return 0; | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 172 | * ishtp_hbm_enum_clients_req() - Send client enum req | ||
| 173 | * @dev: ISHTP device instance | ||
| 174 | * | ||
| 175 | * Send enumeration client request message | ||
| 176 | * | ||
| 177 | * Return: 0 if success else error code | ||
| 178 | */ | ||
| 179 | void ishtp_hbm_enum_clients_req(struct ishtp_device *dev) | ||
| 180 | { | ||
| 181 | struct ishtp_msg_hdr hdr; | ||
| 182 | unsigned char data[128]; | ||
| 183 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
| 184 | struct hbm_host_enum_request *enum_req; | ||
| 185 | const size_t len = sizeof(struct hbm_host_enum_request); | ||
| 186 | |||
| 187 | /* enumerate clients */ | ||
| 188 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
| 189 | |||
| 190 | enum_req = (struct hbm_host_enum_request *)data; | ||
| 191 | memset(enum_req, 0, len); | ||
| 192 | enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; | ||
| 193 | |||
| 194 | if (ishtp_write_message(dev, ishtp_hdr, data)) { | ||
| 195 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
| 196 | dev_err(dev->devc, "enumeration request send failed\n"); | ||
| 197 | ish_hw_reset(dev); | ||
| 198 | } | ||
| 199 | dev->hbm_state = ISHTP_HBM_ENUM_CLIENTS; | ||
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * ishtp_hbm_prop_req() - Request property | ||
| 204 | * @dev: ISHTP device instance | ||
| 205 | * | ||
| 206 | * Request property for a single client | ||
| 207 | * | ||
| 208 | * Return: 0 if success else error code | ||
| 209 | */ | ||
| 210 | static int ishtp_hbm_prop_req(struct ishtp_device *dev) | ||
| 211 | { | ||
| 212 | |||
| 213 | struct ishtp_msg_hdr hdr; | ||
| 214 | unsigned char data[128]; | ||
| 215 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
| 216 | struct hbm_props_request *prop_req; | ||
| 217 | const size_t len = sizeof(struct hbm_props_request); | ||
| 218 | unsigned long next_client_index; | ||
| 219 | uint8_t client_num; | ||
| 220 | |||
| 221 | client_num = dev->fw_client_presentation_num; | ||
| 222 | |||
| 223 | next_client_index = find_next_bit(dev->fw_clients_map, | ||
| 224 | ISHTP_CLIENTS_MAX, dev->fw_client_index); | ||
| 225 | |||
| 226 | /* We got all client properties */ | ||
| 227 | if (next_client_index == ISHTP_CLIENTS_MAX) { | ||
| 228 | dev->hbm_state = ISHTP_HBM_WORKING; | ||
| 229 | dev->dev_state = ISHTP_DEV_ENABLED; | ||
| 230 | |||
| 231 | for (dev->fw_client_presentation_num = 1; | ||
| 232 | dev->fw_client_presentation_num < client_num + 1; | ||
| 233 | ++dev->fw_client_presentation_num) | ||
| 234 | /* Add new client device */ | ||
| 235 | ishtp_bus_new_client(dev); | ||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | dev->fw_clients[client_num].client_id = next_client_index; | ||
| 240 | |||
| 241 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
| 242 | prop_req = (struct hbm_props_request *)data; | ||
| 243 | |||
| 244 | memset(prop_req, 0, sizeof(struct hbm_props_request)); | ||
| 245 | |||
| 246 | prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; | ||
| 247 | prop_req->address = next_client_index; | ||
| 248 | |||
| 249 | if (ishtp_write_message(dev, ishtp_hdr, data)) { | ||
| 250 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
| 251 | dev_err(dev->devc, "properties request send failed\n"); | ||
| 252 | ish_hw_reset(dev); | ||
| 253 | return -EIO; | ||
| 254 | } | ||
| 255 | |||
| 256 | dev->fw_client_index = next_client_index; | ||
| 257 | |||
| 258 | return 0; | ||
| 259 | } | ||
| 260 | |||
| 261 | /** | ||
| 262 | * ishtp_hbm_stop_req() - Send HBM stop | ||
| 263 | * @dev: ISHTP device instance | ||
| 264 | * | ||
| 265 | * Send stop request message | ||
| 266 | */ | ||
| 267 | static void ishtp_hbm_stop_req(struct ishtp_device *dev) | ||
| 268 | { | ||
| 269 | struct ishtp_msg_hdr hdr; | ||
| 270 | unsigned char data[128]; | ||
| 271 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
| 272 | struct hbm_host_stop_request *req; | ||
| 273 | const size_t len = sizeof(struct hbm_host_stop_request); | ||
| 274 | |||
| 275 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
| 276 | req = (struct hbm_host_stop_request *)data; | ||
| 277 | |||
| 278 | memset(req, 0, sizeof(struct hbm_host_stop_request)); | ||
| 279 | req->hbm_cmd = HOST_STOP_REQ_CMD; | ||
| 280 | req->reason = DRIVER_STOP_REQUEST; | ||
| 281 | |||
| 282 | ishtp_write_message(dev, ishtp_hdr, data); | ||
| 283 | } | ||
| 284 | |||
| 285 | /** | ||
| 286 | * ishtp_hbm_cl_flow_control_req() - Send flow control request | ||
| 287 | * @dev: ISHTP device instance | ||
| 288 | * @cl: ISHTP client instance | ||
| 289 | * | ||
| 290 | * Send flow control request | ||
| 291 | * | ||
| 292 | * Return: 0 if success else error code | ||
| 293 | */ | ||
| 294 | int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, | ||
| 295 | struct ishtp_cl *cl) | ||
| 296 | { | ||
| 297 | struct ishtp_msg_hdr hdr; | ||
| 298 | unsigned char data[128]; | ||
| 299 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
| 300 | const size_t len = sizeof(struct hbm_flow_control); | ||
| 301 | int rv; | ||
| 302 | unsigned int num_frags; | ||
| 303 | unsigned long flags; | ||
| 304 | |||
| 305 | spin_lock_irqsave(&cl->fc_spinlock, flags); | ||
| 306 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
| 307 | ishtp_hbm_cl_hdr(cl, ISHTP_FLOW_CONTROL_CMD, data, len); | ||
| 308 | |||
| 309 | /* | ||
| 310 | * Sync possible race when RB recycle and packet receive paths | ||
| 311 | * both try to send an out FC | ||
| 312 | */ | ||
| 313 | if (cl->out_flow_ctrl_creds) { | ||
| 314 | spin_unlock_irqrestore(&cl->fc_spinlock, flags); | ||
| 315 | return 0; | ||
| 316 | } | ||
| 317 | |||
| 318 | num_frags = cl->recv_msg_num_frags; | ||
| 319 | cl->recv_msg_num_frags = 0; | ||
| 320 | |||
| 321 | rv = ishtp_write_message(dev, ishtp_hdr, data); | ||
| 322 | if (!rv) { | ||
| 323 | ++cl->out_flow_ctrl_creds; | ||
| 324 | ++cl->out_flow_ctrl_cnt; | ||
| 325 | getnstimeofday(&cl->ts_out_fc); | ||
| 326 | if (cl->ts_rx.tv_sec && cl->ts_rx.tv_nsec) { | ||
| 327 | struct timespec ts_diff; | ||
| 328 | |||
| 329 | ts_diff = timespec_sub(cl->ts_out_fc, cl->ts_rx); | ||
| 330 | if (timespec_compare(&ts_diff, &cl->ts_max_fc_delay) | ||
| 331 | > 0) | ||
| 332 | cl->ts_max_fc_delay = ts_diff; | ||
| 333 | } | ||
| 334 | } else { | ||
| 335 | ++cl->err_send_fc; | ||
| 336 | } | ||
| 337 | |||
| 338 | spin_unlock_irqrestore(&cl->fc_spinlock, flags); | ||
| 339 | return rv; | ||
| 340 | } | ||
| 341 | |||
| 342 | /** | ||
| 343 | * ishtp_hbm_cl_disconnect_req() - Send disconnect request | ||
| 344 | * @dev: ISHTP device instance | ||
| 345 | * @cl: ISHTP client instance | ||
| 346 | * | ||
| 347 | * Send disconnect message to fw | ||
| 348 | * | ||
| 349 | * Return: 0 if success else error code | ||
| 350 | */ | ||
| 351 | int ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl) | ||
| 352 | { | ||
| 353 | struct ishtp_msg_hdr hdr; | ||
| 354 | unsigned char data[128]; | ||
| 355 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
| 356 | const size_t len = sizeof(struct hbm_client_connect_request); | ||
| 357 | |||
| 358 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
| 359 | ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, data, len); | ||
| 360 | |||
| 361 | return ishtp_write_message(dev, ishtp_hdr, data); | ||
| 362 | } | ||
| 363 | |||
| 364 | /** | ||
| 365 | * ishtp_hbm_cl_disconnect_res() - Get disconnect response | ||
| 366 | * @dev: ISHTP device instance | ||
| 367 | * @rs: Response message | ||
| 368 | * | ||
| 369 | * Received disconnect response from fw | ||
| 370 | */ | ||
| 371 | static void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev, | ||
| 372 | struct hbm_client_connect_response *rs) | ||
| 373 | { | ||
| 374 | struct ishtp_cl *cl = NULL; | ||
| 375 | unsigned long flags; | ||
| 376 | |||
| 377 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
| 378 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
| 379 | if (!rs->status && ishtp_hbm_cl_addr_equal(cl, rs)) { | ||
| 380 | cl->state = ISHTP_CL_DISCONNECTED; | ||
| 381 | break; | ||
| 382 | } | ||
| 383 | } | ||
| 384 | if (cl) | ||
| 385 | wake_up_interruptible(&cl->wait_ctrl_res); | ||
| 386 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 387 | } | ||
| 388 | |||
| 389 | /** | ||
| 390 | * ishtp_hbm_cl_connect_req() - Send connect request | ||
| 391 | * @dev: ISHTP device instance | ||
| 392 | * @cl: client device instance | ||
| 393 | * | ||
| 394 | * Send connection request to specific fw client | ||
| 395 | * | ||
| 396 | * Return: 0 if success else error code | ||
| 397 | */ | ||
| 398 | int ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl) | ||
| 399 | { | ||
| 400 | struct ishtp_msg_hdr hdr; | ||
| 401 | unsigned char data[128]; | ||
| 402 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
| 403 | const size_t len = sizeof(struct hbm_client_connect_request); | ||
| 404 | |||
| 405 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
| 406 | ishtp_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, data, len); | ||
| 407 | |||
| 408 | return ishtp_write_message(dev, ishtp_hdr, data); | ||
| 409 | } | ||
| 410 | |||
| 411 | /** | ||
| 412 | * ishtp_hbm_cl_connect_res() - Get connect response | ||
| 413 | * @dev: ISHTP device instance | ||
| 414 | * @rs: Response message | ||
| 415 | * | ||
| 416 | * Received connect response from fw | ||
| 417 | */ | ||
| 418 | static void ishtp_hbm_cl_connect_res(struct ishtp_device *dev, | ||
| 419 | struct hbm_client_connect_response *rs) | ||
| 420 | { | ||
| 421 | struct ishtp_cl *cl = NULL; | ||
| 422 | unsigned long flags; | ||
| 423 | |||
| 424 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
| 425 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
| 426 | if (ishtp_hbm_cl_addr_equal(cl, rs)) { | ||
| 427 | if (!rs->status) { | ||
| 428 | cl->state = ISHTP_CL_CONNECTED; | ||
| 429 | cl->status = 0; | ||
| 430 | } else { | ||
| 431 | cl->state = ISHTP_CL_DISCONNECTED; | ||
| 432 | cl->status = -ENODEV; | ||
| 433 | } | ||
| 434 | break; | ||
| 435 | } | ||
| 436 | } | ||
| 437 | if (cl) | ||
| 438 | wake_up_interruptible(&cl->wait_ctrl_res); | ||
| 439 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 440 | } | ||
| 441 | |||
| 442 | /** | ||
| 443 | * ishtp_client_disconnect_request() - Receive disconnect request | ||
| 444 | * @dev: ISHTP device instance | ||
| 445 | * @disconnect_req: disconnect request structure | ||
| 446 | * | ||
| 447 | * Disconnect request bus message from the fw. Send diconnect response. | ||
| 448 | */ | ||
| 449 | static void ishtp_hbm_fw_disconnect_req(struct ishtp_device *dev, | ||
| 450 | struct hbm_client_connect_request *disconnect_req) | ||
| 451 | { | ||
| 452 | struct ishtp_cl *cl; | ||
| 453 | const size_t len = sizeof(struct hbm_client_connect_response); | ||
| 454 | unsigned long flags; | ||
| 455 | struct ishtp_msg_hdr hdr; | ||
| 456 | unsigned char data[4]; /* All HBM messages are 4 bytes */ | ||
| 457 | |||
| 458 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
| 459 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
| 460 | if (ishtp_hbm_cl_addr_equal(cl, disconnect_req)) { | ||
| 461 | cl->state = ISHTP_CL_DISCONNECTED; | ||
| 462 | |||
| 463 | /* send disconnect response */ | ||
| 464 | ishtp_hbm_hdr(&hdr, len); | ||
| 465 | ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, data, | ||
| 466 | len); | ||
| 467 | ishtp_write_message(dev, &hdr, data); | ||
| 468 | break; | ||
| 469 | } | ||
| 470 | } | ||
| 471 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 472 | } | ||
| 473 | |||
| 474 | /** | ||
| 475 | * ishtp_hbm_dma_xfer_ack(() - Receive transfer ACK | ||
| 476 | * @dev: ISHTP device instance | ||
| 477 | * @dma_xfer: HBM transfer message | ||
| 478 | * | ||
| 479 | * Receive ack for ISHTP-over-DMA client message | ||
| 480 | */ | ||
| 481 | static void ishtp_hbm_dma_xfer_ack(struct ishtp_device *dev, | ||
| 482 | struct dma_xfer_hbm *dma_xfer) | ||
| 483 | { | ||
| 484 | void *msg; | ||
| 485 | uint64_t offs; | ||
| 486 | struct ishtp_msg_hdr *ishtp_hdr = | ||
| 487 | (struct ishtp_msg_hdr *)&dev->ishtp_msg_hdr; | ||
| 488 | unsigned int msg_offs; | ||
| 489 | struct ishtp_cl *cl; | ||
| 490 | |||
| 491 | for (msg_offs = 0; msg_offs < ishtp_hdr->length; | ||
| 492 | msg_offs += sizeof(struct dma_xfer_hbm)) { | ||
| 493 | offs = dma_xfer->msg_addr - dev->ishtp_host_dma_tx_buf_phys; | ||
| 494 | if (offs > dev->ishtp_host_dma_tx_buf_size) { | ||
| 495 | dev_err(dev->devc, "Bad DMA Tx ack message address\n"); | ||
| 496 | return; | ||
| 497 | } | ||
| 498 | if (dma_xfer->msg_length > | ||
| 499 | dev->ishtp_host_dma_tx_buf_size - offs) { | ||
| 500 | dev_err(dev->devc, "Bad DMA Tx ack message size\n"); | ||
| 501 | return; | ||
| 502 | } | ||
| 503 | |||
| 504 | /* logical address of the acked mem */ | ||
| 505 | msg = (unsigned char *)dev->ishtp_host_dma_tx_buf + offs; | ||
| 506 | ishtp_cl_release_dma_acked_mem(dev, msg, dma_xfer->msg_length); | ||
| 507 | |||
| 508 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
| 509 | if (cl->fw_client_id == dma_xfer->fw_client_id && | ||
| 510 | cl->host_client_id == dma_xfer->host_client_id) | ||
| 511 | /* | ||
| 512 | * in case that a single ack may be sent | ||
| 513 | * over several dma transfers, and the last msg | ||
| 514 | * addr was inside the acked memory, but not in | ||
| 515 | * its start | ||
| 516 | */ | ||
| 517 | if (cl->last_dma_addr >= | ||
| 518 | (unsigned char *)msg && | ||
| 519 | cl->last_dma_addr < | ||
| 520 | (unsigned char *)msg + | ||
| 521 | dma_xfer->msg_length) { | ||
| 522 | cl->last_dma_acked = 1; | ||
| 523 | |||
| 524 | if (!list_empty(&cl->tx_list.list) && | ||
| 525 | cl->ishtp_flow_ctrl_creds) { | ||
| 526 | /* | ||
| 527 | * start sending the first msg | ||
| 528 | */ | ||
| 529 | ishtp_cl_send_msg(dev, cl); | ||
| 530 | } | ||
| 531 | } | ||
| 532 | } | ||
| 533 | ++dma_xfer; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | /** | ||
| 538 | * ishtp_hbm_dma_xfer() - Receive DMA transfer message | ||
| 539 | * @dev: ISHTP device instance | ||
| 540 | * @dma_xfer: HBM transfer message | ||
| 541 | * | ||
| 542 | * Receive ISHTP-over-DMA client message | ||
| 543 | */ | ||
| 544 | static void ishtp_hbm_dma_xfer(struct ishtp_device *dev, | ||
| 545 | struct dma_xfer_hbm *dma_xfer) | ||
| 546 | { | ||
| 547 | void *msg; | ||
| 548 | uint64_t offs; | ||
| 549 | struct ishtp_msg_hdr hdr; | ||
| 550 | struct ishtp_msg_hdr *ishtp_hdr = | ||
| 551 | (struct ishtp_msg_hdr *) &dev->ishtp_msg_hdr; | ||
| 552 | struct dma_xfer_hbm *prm = dma_xfer; | ||
| 553 | unsigned int msg_offs; | ||
| 554 | |||
| 555 | for (msg_offs = 0; msg_offs < ishtp_hdr->length; | ||
| 556 | msg_offs += sizeof(struct dma_xfer_hbm)) { | ||
| 557 | |||
| 558 | offs = dma_xfer->msg_addr - dev->ishtp_host_dma_rx_buf_phys; | ||
| 559 | if (offs > dev->ishtp_host_dma_rx_buf_size) { | ||
| 560 | dev_err(dev->devc, "Bad DMA Rx message address\n"); | ||
| 561 | return; | ||
| 562 | } | ||
| 563 | if (dma_xfer->msg_length > | ||
| 564 | dev->ishtp_host_dma_rx_buf_size - offs) { | ||
| 565 | dev_err(dev->devc, "Bad DMA Rx message size\n"); | ||
| 566 | return; | ||
| 567 | } | ||
| 568 | msg = dev->ishtp_host_dma_rx_buf + offs; | ||
| 569 | recv_ishtp_cl_msg_dma(dev, msg, dma_xfer); | ||
| 570 | dma_xfer->hbm = DMA_XFER_ACK; /* Prepare for response */ | ||
| 571 | ++dma_xfer; | ||
| 572 | } | ||
| 573 | |||
| 574 | /* Send DMA_XFER_ACK [...] */ | ||
| 575 | ishtp_hbm_hdr(&hdr, ishtp_hdr->length); | ||
| 576 | ishtp_write_message(dev, &hdr, (unsigned char *)prm); | ||
| 577 | } | ||
| 578 | |||
| 579 | /** | ||
| 580 | * ishtp_hbm_dispatch() - HBM dispatch function | ||
| 581 | * @dev: ISHTP device instance | ||
| 582 | * @hdr: bus message | ||
| 583 | * | ||
| 584 | * Bottom half read routine after ISR to handle the read bus message cmd | ||
| 585 | * processing | ||
| 586 | */ | ||
| 587 | void ishtp_hbm_dispatch(struct ishtp_device *dev, | ||
| 588 | struct ishtp_bus_message *hdr) | ||
| 589 | { | ||
| 590 | struct ishtp_bus_message *ishtp_msg; | ||
| 591 | struct ishtp_fw_client *fw_client; | ||
| 592 | struct hbm_host_version_response *version_res; | ||
| 593 | struct hbm_client_connect_response *connect_res; | ||
| 594 | struct hbm_client_connect_response *disconnect_res; | ||
| 595 | struct hbm_client_connect_request *disconnect_req; | ||
| 596 | struct hbm_props_response *props_res; | ||
| 597 | struct hbm_host_enum_response *enum_res; | ||
| 598 | struct ishtp_msg_hdr ishtp_hdr; | ||
| 599 | struct dma_alloc_notify dma_alloc_notify; | ||
| 600 | struct dma_xfer_hbm *dma_xfer; | ||
| 601 | |||
| 602 | ishtp_msg = hdr; | ||
| 603 | |||
| 604 | switch (ishtp_msg->hbm_cmd) { | ||
| 605 | case HOST_START_RES_CMD: | ||
| 606 | version_res = (struct hbm_host_version_response *)ishtp_msg; | ||
| 607 | if (!version_res->host_version_supported) { | ||
| 608 | dev->version = version_res->fw_max_version; | ||
| 609 | |||
| 610 | dev->hbm_state = ISHTP_HBM_STOPPED; | ||
| 611 | ishtp_hbm_stop_req(dev); | ||
| 612 | return; | ||
| 613 | } | ||
| 614 | |||
| 615 | dev->version.major_version = HBM_MAJOR_VERSION; | ||
| 616 | dev->version.minor_version = HBM_MINOR_VERSION; | ||
| 617 | if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS && | ||
| 618 | dev->hbm_state == ISHTP_HBM_START) { | ||
| 619 | dev->hbm_state = ISHTP_HBM_STARTED; | ||
| 620 | ishtp_hbm_enum_clients_req(dev); | ||
| 621 | } else { | ||
| 622 | dev_err(dev->devc, | ||
| 623 | "reset: wrong host start response\n"); | ||
| 624 | /* BUG: why do we arrive here? */ | ||
| 625 | ish_hw_reset(dev); | ||
| 626 | return; | ||
| 627 | } | ||
| 628 | |||
| 629 | wake_up_interruptible(&dev->wait_hbm_recvd_msg); | ||
| 630 | break; | ||
| 631 | |||
| 632 | case CLIENT_CONNECT_RES_CMD: | ||
| 633 | connect_res = (struct hbm_client_connect_response *)ishtp_msg; | ||
| 634 | ishtp_hbm_cl_connect_res(dev, connect_res); | ||
| 635 | break; | ||
| 636 | |||
| 637 | case CLIENT_DISCONNECT_RES_CMD: | ||
| 638 | disconnect_res = | ||
| 639 | (struct hbm_client_connect_response *)ishtp_msg; | ||
| 640 | ishtp_hbm_cl_disconnect_res(dev, disconnect_res); | ||
| 641 | break; | ||
| 642 | |||
| 643 | case HOST_CLIENT_PROPERTIES_RES_CMD: | ||
| 644 | props_res = (struct hbm_props_response *)ishtp_msg; | ||
| 645 | fw_client = &dev->fw_clients[dev->fw_client_presentation_num]; | ||
| 646 | |||
| 647 | if (props_res->status || !dev->fw_clients) { | ||
| 648 | dev_err(dev->devc, | ||
| 649 | "reset: properties response hbm wrong status\n"); | ||
| 650 | ish_hw_reset(dev); | ||
| 651 | return; | ||
| 652 | } | ||
| 653 | |||
| 654 | if (fw_client->client_id != props_res->address) { | ||
| 655 | dev_err(dev->devc, | ||
| 656 | "reset: host properties response address mismatch [%02X %02X]\n", | ||
| 657 | fw_client->client_id, props_res->address); | ||
| 658 | ish_hw_reset(dev); | ||
| 659 | return; | ||
| 660 | } | ||
| 661 | |||
| 662 | if (dev->dev_state != ISHTP_DEV_INIT_CLIENTS || | ||
| 663 | dev->hbm_state != ISHTP_HBM_CLIENT_PROPERTIES) { | ||
| 664 | dev_err(dev->devc, | ||
| 665 | "reset: unexpected properties response\n"); | ||
| 666 | ish_hw_reset(dev); | ||
| 667 | return; | ||
| 668 | } | ||
| 669 | |||
| 670 | fw_client->props = props_res->client_properties; | ||
| 671 | dev->fw_client_index++; | ||
| 672 | dev->fw_client_presentation_num++; | ||
| 673 | |||
| 674 | /* request property for the next client */ | ||
| 675 | ishtp_hbm_prop_req(dev); | ||
| 676 | |||
| 677 | if (dev->dev_state != ISHTP_DEV_ENABLED) | ||
| 678 | break; | ||
| 679 | |||
| 680 | if (!ishtp_use_dma_transfer()) | ||
| 681 | break; | ||
| 682 | |||
| 683 | dev_dbg(dev->devc, "Requesting to use DMA\n"); | ||
| 684 | ishtp_cl_alloc_dma_buf(dev); | ||
| 685 | if (dev->ishtp_host_dma_rx_buf) { | ||
| 686 | const size_t len = sizeof(dma_alloc_notify); | ||
| 687 | |||
| 688 | memset(&dma_alloc_notify, 0, sizeof(dma_alloc_notify)); | ||
| 689 | dma_alloc_notify.hbm = DMA_BUFFER_ALLOC_NOTIFY; | ||
| 690 | dma_alloc_notify.buf_size = | ||
| 691 | dev->ishtp_host_dma_rx_buf_size; | ||
| 692 | dma_alloc_notify.buf_address = | ||
| 693 | dev->ishtp_host_dma_rx_buf_phys; | ||
| 694 | ishtp_hbm_hdr(&ishtp_hdr, len); | ||
| 695 | ishtp_write_message(dev, &ishtp_hdr, | ||
| 696 | (unsigned char *)&dma_alloc_notify); | ||
| 697 | } | ||
| 698 | |||
| 699 | break; | ||
| 700 | |||
| 701 | case HOST_ENUM_RES_CMD: | ||
| 702 | enum_res = (struct hbm_host_enum_response *) ishtp_msg; | ||
| 703 | memcpy(dev->fw_clients_map, enum_res->valid_addresses, 32); | ||
| 704 | if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS && | ||
| 705 | dev->hbm_state == ISHTP_HBM_ENUM_CLIENTS) { | ||
| 706 | dev->fw_client_presentation_num = 0; | ||
| 707 | dev->fw_client_index = 0; | ||
| 708 | |||
| 709 | ishtp_hbm_fw_cl_allocate(dev); | ||
| 710 | dev->hbm_state = ISHTP_HBM_CLIENT_PROPERTIES; | ||
| 711 | |||
| 712 | /* first property request */ | ||
| 713 | ishtp_hbm_prop_req(dev); | ||
| 714 | } else { | ||
| 715 | dev_err(dev->devc, | ||
| 716 | "reset: unexpected enumeration response hbm\n"); | ||
| 717 | ish_hw_reset(dev); | ||
| 718 | return; | ||
| 719 | } | ||
| 720 | break; | ||
| 721 | |||
| 722 | case HOST_STOP_RES_CMD: | ||
| 723 | if (dev->hbm_state != ISHTP_HBM_STOPPED) | ||
| 724 | dev_err(dev->devc, "unexpected stop response\n"); | ||
| 725 | |||
| 726 | dev->dev_state = ISHTP_DEV_DISABLED; | ||
| 727 | dev_info(dev->devc, "reset: FW stop response\n"); | ||
| 728 | ish_hw_reset(dev); | ||
| 729 | break; | ||
| 730 | |||
| 731 | case CLIENT_DISCONNECT_REQ_CMD: | ||
| 732 | /* search for client */ | ||
| 733 | disconnect_req = | ||
| 734 | (struct hbm_client_connect_request *)ishtp_msg; | ||
| 735 | ishtp_hbm_fw_disconnect_req(dev, disconnect_req); | ||
| 736 | break; | ||
| 737 | |||
| 738 | case FW_STOP_REQ_CMD: | ||
| 739 | dev->hbm_state = ISHTP_HBM_STOPPED; | ||
| 740 | break; | ||
| 741 | |||
| 742 | case DMA_BUFFER_ALLOC_RESPONSE: | ||
| 743 | dev->ishtp_host_dma_enabled = 1; | ||
| 744 | break; | ||
| 745 | |||
| 746 | case DMA_XFER: | ||
| 747 | dma_xfer = (struct dma_xfer_hbm *)ishtp_msg; | ||
| 748 | if (!dev->ishtp_host_dma_enabled) { | ||
| 749 | dev_err(dev->devc, | ||
| 750 | "DMA XFER requested but DMA is not enabled\n"); | ||
| 751 | break; | ||
| 752 | } | ||
| 753 | ishtp_hbm_dma_xfer(dev, dma_xfer); | ||
| 754 | break; | ||
| 755 | |||
| 756 | case DMA_XFER_ACK: | ||
| 757 | dma_xfer = (struct dma_xfer_hbm *)ishtp_msg; | ||
| 758 | if (!dev->ishtp_host_dma_enabled || | ||
| 759 | !dev->ishtp_host_dma_tx_buf) { | ||
| 760 | dev_err(dev->devc, | ||
| 761 | "DMA XFER acked but DMA Tx is not enabled\n"); | ||
| 762 | break; | ||
| 763 | } | ||
| 764 | ishtp_hbm_dma_xfer_ack(dev, dma_xfer); | ||
| 765 | break; | ||
| 766 | |||
| 767 | default: | ||
| 768 | dev_err(dev->devc, "unknown HBM: %u\n", | ||
| 769 | (unsigned int)ishtp_msg->hbm_cmd); | ||
| 770 | |||
| 771 | break; | ||
| 772 | } | ||
| 773 | } | ||
| 774 | |||
| 775 | /** | ||
| 776 | * bh_hbm_work_fn() - HBM work function | ||
| 777 | * @work: work struct | ||
| 778 | * | ||
| 779 | * Bottom half processing work function (instead of thread handler) | ||
| 780 | * for processing hbm messages | ||
| 781 | */ | ||
| 782 | void bh_hbm_work_fn(struct work_struct *work) | ||
| 783 | { | ||
| 784 | unsigned long flags; | ||
| 785 | struct ishtp_device *dev; | ||
| 786 | unsigned char hbm[IPC_PAYLOAD_SIZE]; | ||
| 787 | |||
| 788 | dev = container_of(work, struct ishtp_device, bh_hbm_work); | ||
| 789 | spin_lock_irqsave(&dev->rd_msg_spinlock, flags); | ||
| 790 | if (dev->rd_msg_fifo_head != dev->rd_msg_fifo_tail) { | ||
| 791 | memcpy(hbm, dev->rd_msg_fifo + dev->rd_msg_fifo_head, | ||
| 792 | IPC_PAYLOAD_SIZE); | ||
| 793 | dev->rd_msg_fifo_head = | ||
| 794 | (dev->rd_msg_fifo_head + IPC_PAYLOAD_SIZE) % | ||
| 795 | (RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE); | ||
| 796 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
| 797 | ishtp_hbm_dispatch(dev, (struct ishtp_bus_message *)hbm); | ||
| 798 | } else { | ||
| 799 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
| 800 | } | ||
| 801 | } | ||
| 802 | |||
| 803 | /** | ||
| 804 | * recv_hbm() - Receive HBM message | ||
| 805 | * @dev: ISHTP device instance | ||
| 806 | * @ishtp_hdr: received bus message | ||
| 807 | * | ||
| 808 | * Receive and process ISHTP bus messages in ISR context. This will schedule | ||
| 809 | * work function to process message | ||
| 810 | */ | ||
| 811 | void recv_hbm(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr) | ||
| 812 | { | ||
| 813 | uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; | ||
| 814 | struct ishtp_bus_message *ishtp_msg = | ||
| 815 | (struct ishtp_bus_message *)rd_msg_buf; | ||
| 816 | unsigned long flags; | ||
| 817 | |||
| 818 | dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length); | ||
| 819 | |||
| 820 | /* Flow control - handle in place */ | ||
| 821 | if (ishtp_msg->hbm_cmd == ISHTP_FLOW_CONTROL_CMD) { | ||
| 822 | struct hbm_flow_control *flow_control = | ||
| 823 | (struct hbm_flow_control *)ishtp_msg; | ||
| 824 | struct ishtp_cl *cl = NULL; | ||
| 825 | unsigned long flags, tx_flags; | ||
| 826 | |||
| 827 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
| 828 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
| 829 | if (cl->host_client_id == flow_control->host_addr && | ||
| 830 | cl->fw_client_id == | ||
| 831 | flow_control->fw_addr) { | ||
| 832 | /* | ||
| 833 | * NOTE: It's valid only for counting | ||
| 834 | * flow-control implementation to receive a | ||
| 835 | * FC in the middle of sending. Meanwhile not | ||
| 836 | * supported | ||
| 837 | */ | ||
| 838 | if (cl->ishtp_flow_ctrl_creds) | ||
| 839 | dev_err(dev->devc, | ||
| 840 | "recv extra FC from FW client %u (host client %u) (FC count was %d)\n", | ||
| 841 | (unsigned int)cl->fw_client_id, | ||
| 842 | (unsigned int)cl->host_client_id, | ||
| 843 | cl->ishtp_flow_ctrl_creds); | ||
| 844 | else { | ||
| 845 | ++cl->ishtp_flow_ctrl_creds; | ||
| 846 | ++cl->ishtp_flow_ctrl_cnt; | ||
| 847 | cl->last_ipc_acked = 1; | ||
| 848 | spin_lock_irqsave( | ||
| 849 | &cl->tx_list_spinlock, | ||
| 850 | tx_flags); | ||
| 851 | if (!list_empty(&cl->tx_list.list)) { | ||
| 852 | /* | ||
| 853 | * start sending the first msg | ||
| 854 | * = the callback function | ||
| 855 | */ | ||
| 856 | spin_unlock_irqrestore( | ||
| 857 | &cl->tx_list_spinlock, | ||
| 858 | tx_flags); | ||
| 859 | ishtp_cl_send_msg(dev, cl); | ||
| 860 | } else { | ||
| 861 | spin_unlock_irqrestore( | ||
| 862 | &cl->tx_list_spinlock, | ||
| 863 | tx_flags); | ||
| 864 | } | ||
| 865 | } | ||
| 866 | break; | ||
| 867 | } | ||
| 868 | } | ||
| 869 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
| 870 | goto eoi; | ||
| 871 | } | ||
| 872 | |||
| 873 | /* | ||
| 874 | * Some messages that are safe for ISR processing and important | ||
| 875 | * to be done "quickly" and in-order, go here | ||
| 876 | */ | ||
| 877 | if (ishtp_msg->hbm_cmd == CLIENT_CONNECT_RES_CMD || | ||
| 878 | ishtp_msg->hbm_cmd == CLIENT_DISCONNECT_RES_CMD || | ||
| 879 | ishtp_msg->hbm_cmd == CLIENT_DISCONNECT_REQ_CMD || | ||
| 880 | ishtp_msg->hbm_cmd == DMA_XFER) { | ||
| 881 | ishtp_hbm_dispatch(dev, ishtp_msg); | ||
| 882 | goto eoi; | ||
| 883 | } | ||
| 884 | |||
| 885 | /* | ||
| 886 | * All other HBMs go here. | ||
| 887 | * We schedule HBMs for processing serially by using system wq, | ||
| 888 | * possibly there will be multiple HBMs scheduled at the same time. | ||
| 889 | */ | ||
| 890 | spin_lock_irqsave(&dev->rd_msg_spinlock, flags); | ||
| 891 | if ((dev->rd_msg_fifo_tail + IPC_PAYLOAD_SIZE) % | ||
| 892 | (RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE) == | ||
| 893 | dev->rd_msg_fifo_head) { | ||
| 894 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
| 895 | dev_err(dev->devc, "BH buffer overflow, dropping HBM %u\n", | ||
| 896 | (unsigned int)ishtp_msg->hbm_cmd); | ||
| 897 | goto eoi; | ||
| 898 | } | ||
| 899 | memcpy(dev->rd_msg_fifo + dev->rd_msg_fifo_tail, ishtp_msg, | ||
| 900 | ishtp_hdr->length); | ||
| 901 | dev->rd_msg_fifo_tail = (dev->rd_msg_fifo_tail + IPC_PAYLOAD_SIZE) % | ||
| 902 | (RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE); | ||
| 903 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
| 904 | schedule_work(&dev->bh_hbm_work); | ||
| 905 | eoi: | ||
| 906 | return; | ||
| 907 | } | ||
| 908 | |||
| 909 | /** | ||
| 910 | * recv_fixed_cl_msg() - Receive fixed client message | ||
| 911 | * @dev: ISHTP device instance | ||
| 912 | * @ishtp_hdr: received bus message | ||
| 913 | * | ||
| 914 | * Receive and process ISHTP fixed client messages (address == 0) | ||
| 915 | * in ISR context | ||
| 916 | */ | ||
| 917 | void recv_fixed_cl_msg(struct ishtp_device *dev, | ||
| 918 | struct ishtp_msg_hdr *ishtp_hdr) | ||
| 919 | { | ||
| 920 | uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; | ||
| 921 | |||
| 922 | dev->print_log(dev, | ||
| 923 | "%s() got fixed client msg from client #%d\n", | ||
| 924 | __func__, ishtp_hdr->fw_addr); | ||
| 925 | dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length); | ||
| 926 | if (ishtp_hdr->fw_addr == ISHTP_SYSTEM_STATE_CLIENT_ADDR) { | ||
| 927 | struct ish_system_states_header *msg_hdr = | ||
| 928 | (struct ish_system_states_header *)rd_msg_buf; | ||
| 929 | if (msg_hdr->cmd == SYSTEM_STATE_SUBSCRIBE) | ||
| 930 | ishtp_send_resume(dev); | ||
| 931 | /* if FW request arrived here, the system is not suspended */ | ||
| 932 | else | ||
| 933 | dev_err(dev->devc, "unknown fixed client msg [%02X]\n", | ||
| 934 | msg_hdr->cmd); | ||
| 935 | } | ||
| 936 | } | ||
| 937 | |||
| 938 | /** | ||
| 939 | * fix_cl_hdr() - Initialize fixed client header | ||
| 940 | * @hdr: message header | ||
| 941 | * @length: length of message | ||
| 942 | * @cl_addr: Client address | ||
| 943 | * | ||
| 944 | * Initialize message header for fixed client | ||
| 945 | */ | ||
| 946 | static inline void fix_cl_hdr(struct ishtp_msg_hdr *hdr, size_t length, | ||
| 947 | uint8_t cl_addr) | ||
| 948 | { | ||
| 949 | hdr->host_addr = 0; | ||
| 950 | hdr->fw_addr = cl_addr; | ||
| 951 | hdr->length = length; | ||
| 952 | hdr->msg_complete = 1; | ||
| 953 | hdr->reserved = 0; | ||
| 954 | } | ||
| 955 | |||
| 956 | /*** Suspend and resume notification ***/ | ||
| 957 | |||
| 958 | static uint32_t current_state; | ||
| 959 | static uint32_t supported_states = 0 | SUSPEND_STATE_BIT; | ||
| 960 | |||
| 961 | /** | ||
| 962 | * ishtp_send_suspend() - Send suspend message to FW | ||
| 963 | * @dev: ISHTP device instance | ||
| 964 | * | ||
| 965 | * Send suspend message to FW. This is useful for system freeze (non S3) case | ||
| 966 | */ | ||
| 967 | void ishtp_send_suspend(struct ishtp_device *dev) | ||
| 968 | { | ||
| 969 | struct ishtp_msg_hdr ishtp_hdr; | ||
| 970 | struct ish_system_states_status state_status_msg; | ||
| 971 | const size_t len = sizeof(struct ish_system_states_status); | ||
| 972 | |||
| 973 | fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR); | ||
| 974 | |||
| 975 | memset(&state_status_msg, 0, len); | ||
| 976 | state_status_msg.hdr.cmd = SYSTEM_STATE_STATUS; | ||
| 977 | state_status_msg.supported_states = supported_states; | ||
| 978 | current_state |= SUSPEND_STATE_BIT; | ||
| 979 | dev->print_log(dev, "%s() sends SUSPEND notification\n", __func__); | ||
| 980 | state_status_msg.states_status = current_state; | ||
| 981 | |||
| 982 | ishtp_write_message(dev, &ishtp_hdr, | ||
| 983 | (unsigned char *)&state_status_msg); | ||
| 984 | } | ||
| 985 | EXPORT_SYMBOL(ishtp_send_suspend); | ||
| 986 | |||
| 987 | /** | ||
| 988 | * ishtp_send_resume() - Send resume message to FW | ||
| 989 | * @dev: ISHTP device instance | ||
| 990 | * | ||
| 991 | * Send resume message to FW. This is useful for system freeze (non S3) case | ||
| 992 | */ | ||
| 993 | void ishtp_send_resume(struct ishtp_device *dev) | ||
| 994 | { | ||
| 995 | struct ishtp_msg_hdr ishtp_hdr; | ||
| 996 | struct ish_system_states_status state_status_msg; | ||
| 997 | const size_t len = sizeof(struct ish_system_states_status); | ||
| 998 | |||
| 999 | fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR); | ||
| 1000 | |||
| 1001 | memset(&state_status_msg, 0, len); | ||
| 1002 | state_status_msg.hdr.cmd = SYSTEM_STATE_STATUS; | ||
| 1003 | state_status_msg.supported_states = supported_states; | ||
| 1004 | current_state &= ~SUSPEND_STATE_BIT; | ||
| 1005 | dev->print_log(dev, "%s() sends RESUME notification\n", __func__); | ||
| 1006 | state_status_msg.states_status = current_state; | ||
| 1007 | |||
| 1008 | ishtp_write_message(dev, &ishtp_hdr, | ||
| 1009 | (unsigned char *)&state_status_msg); | ||
| 1010 | } | ||
| 1011 | EXPORT_SYMBOL(ishtp_send_resume); | ||
| 1012 | |||
| 1013 | /** | ||
| 1014 | * ishtp_query_subscribers() - Send query subscribers message | ||
| 1015 | * @dev: ISHTP device instance | ||
| 1016 | * | ||
| 1017 | * Send message to query subscribers | ||
| 1018 | */ | ||
| 1019 | void ishtp_query_subscribers(struct ishtp_device *dev) | ||
| 1020 | { | ||
| 1021 | struct ishtp_msg_hdr ishtp_hdr; | ||
| 1022 | struct ish_system_states_query_subscribers query_subscribers_msg; | ||
| 1023 | const size_t len = sizeof(struct ish_system_states_query_subscribers); | ||
| 1024 | |||
| 1025 | fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR); | ||
| 1026 | |||
| 1027 | memset(&query_subscribers_msg, 0, len); | ||
| 1028 | query_subscribers_msg.hdr.cmd = SYSTEM_STATE_QUERY_SUBSCRIBERS; | ||
| 1029 | |||
| 1030 | ishtp_write_message(dev, &ishtp_hdr, | ||
| 1031 | (unsigned char *)&query_subscribers_msg); | ||
| 1032 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.h b/drivers/hid/intel-ish-hid/ishtp/hbm.h new file mode 100644 index 000000000000..d96111cef7f8 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.h | |||
| @@ -0,0 +1,321 @@ | |||
| 1 | /* | ||
| 2 | * ISHTP bus layer messages handling | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef _ISHTP_HBM_H_ | ||
| 17 | #define _ISHTP_HBM_H_ | ||
| 18 | |||
| 19 | #include <linux/uuid.h> | ||
| 20 | |||
| 21 | struct ishtp_device; | ||
| 22 | struct ishtp_msg_hdr; | ||
| 23 | struct ishtp_cl; | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Timeouts in Seconds | ||
| 27 | */ | ||
| 28 | #define ISHTP_INTEROP_TIMEOUT 7 /* Timeout on ready message */ | ||
| 29 | |||
| 30 | #define ISHTP_CL_CONNECT_TIMEOUT 15 /* HPS: Client Connect Timeout */ | ||
| 31 | |||
| 32 | /* | ||
| 33 | * ISHTP Version | ||
| 34 | */ | ||
| 35 | #define HBM_MINOR_VERSION 0 | ||
| 36 | #define HBM_MAJOR_VERSION 1 | ||
| 37 | |||
| 38 | /* Host bus message command opcode */ | ||
| 39 | #define ISHTP_HBM_CMD_OP_MSK 0x7f | ||
| 40 | /* Host bus message command RESPONSE */ | ||
| 41 | #define ISHTP_HBM_CMD_RES_MSK 0x80 | ||
| 42 | |||
| 43 | /* | ||
| 44 | * ISHTP Bus Message Command IDs | ||
| 45 | */ | ||
| 46 | #define HOST_START_REQ_CMD 0x01 | ||
| 47 | #define HOST_START_RES_CMD 0x81 | ||
| 48 | |||
| 49 | #define HOST_STOP_REQ_CMD 0x02 | ||
| 50 | #define HOST_STOP_RES_CMD 0x82 | ||
| 51 | |||
| 52 | #define FW_STOP_REQ_CMD 0x03 | ||
| 53 | |||
| 54 | #define HOST_ENUM_REQ_CMD 0x04 | ||
| 55 | #define HOST_ENUM_RES_CMD 0x84 | ||
| 56 | |||
| 57 | #define HOST_CLIENT_PROPERTIES_REQ_CMD 0x05 | ||
| 58 | #define HOST_CLIENT_PROPERTIES_RES_CMD 0x85 | ||
| 59 | |||
| 60 | #define CLIENT_CONNECT_REQ_CMD 0x06 | ||
| 61 | #define CLIENT_CONNECT_RES_CMD 0x86 | ||
| 62 | |||
| 63 | #define CLIENT_DISCONNECT_REQ_CMD 0x07 | ||
| 64 | #define CLIENT_DISCONNECT_RES_CMD 0x87 | ||
| 65 | |||
| 66 | #define ISHTP_FLOW_CONTROL_CMD 0x08 | ||
| 67 | |||
| 68 | #define DMA_BUFFER_ALLOC_NOTIFY 0x11 | ||
| 69 | #define DMA_BUFFER_ALLOC_RESPONSE 0x91 | ||
| 70 | |||
| 71 | #define DMA_XFER 0x12 | ||
| 72 | #define DMA_XFER_ACK 0x92 | ||
| 73 | |||
| 74 | /* | ||
| 75 | * ISHTP Stop Reason | ||
| 76 | * used by hbm_host_stop_request.reason | ||
| 77 | */ | ||
| 78 | #define DRIVER_STOP_REQUEST 0x00 | ||
| 79 | |||
| 80 | /* | ||
| 81 | * ISHTP BUS Interface Section | ||
| 82 | */ | ||
| 83 | struct ishtp_msg_hdr { | ||
| 84 | uint32_t fw_addr:8; | ||
| 85 | uint32_t host_addr:8; | ||
| 86 | uint32_t length:9; | ||
| 87 | uint32_t reserved:6; | ||
| 88 | uint32_t msg_complete:1; | ||
| 89 | } __packed; | ||
| 90 | |||
| 91 | struct ishtp_bus_message { | ||
| 92 | uint8_t hbm_cmd; | ||
| 93 | uint8_t data[0]; | ||
| 94 | } __packed; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * struct hbm_cl_cmd - client specific host bus command | ||
| 98 | * CONNECT, DISCONNECT, and FlOW CONTROL | ||
| 99 | * | ||
| 100 | * @hbm_cmd - bus message command header | ||
| 101 | * @fw_addr - address of the fw client | ||
| 102 | * @host_addr - address of the client in the driver | ||
| 103 | * @data | ||
| 104 | */ | ||
| 105 | struct ishtp_hbm_cl_cmd { | ||
| 106 | uint8_t hbm_cmd; | ||
| 107 | uint8_t fw_addr; | ||
| 108 | uint8_t host_addr; | ||
| 109 | uint8_t data; | ||
| 110 | }; | ||
| 111 | |||
| 112 | struct hbm_version { | ||
| 113 | uint8_t minor_version; | ||
| 114 | uint8_t major_version; | ||
| 115 | } __packed; | ||
| 116 | |||
| 117 | struct hbm_host_version_request { | ||
| 118 | uint8_t hbm_cmd; | ||
| 119 | uint8_t reserved; | ||
| 120 | struct hbm_version host_version; | ||
| 121 | } __packed; | ||
| 122 | |||
| 123 | struct hbm_host_version_response { | ||
| 124 | uint8_t hbm_cmd; | ||
| 125 | uint8_t host_version_supported; | ||
| 126 | struct hbm_version fw_max_version; | ||
| 127 | } __packed; | ||
| 128 | |||
| 129 | struct hbm_host_stop_request { | ||
| 130 | uint8_t hbm_cmd; | ||
| 131 | uint8_t reason; | ||
| 132 | uint8_t reserved[2]; | ||
| 133 | } __packed; | ||
| 134 | |||
| 135 | struct hbm_host_stop_response { | ||
| 136 | uint8_t hbm_cmd; | ||
| 137 | uint8_t reserved[3]; | ||
| 138 | } __packed; | ||
| 139 | |||
| 140 | struct hbm_host_enum_request { | ||
| 141 | uint8_t hbm_cmd; | ||
| 142 | uint8_t reserved[3]; | ||
| 143 | } __packed; | ||
| 144 | |||
| 145 | struct hbm_host_enum_response { | ||
| 146 | uint8_t hbm_cmd; | ||
| 147 | uint8_t reserved[3]; | ||
| 148 | uint8_t valid_addresses[32]; | ||
| 149 | } __packed; | ||
| 150 | |||
| 151 | struct ishtp_client_properties { | ||
| 152 | uuid_le protocol_name; | ||
| 153 | uint8_t protocol_version; | ||
| 154 | uint8_t max_number_of_connections; | ||
| 155 | uint8_t fixed_address; | ||
| 156 | uint8_t single_recv_buf; | ||
| 157 | uint32_t max_msg_length; | ||
| 158 | uint8_t dma_hdr_len; | ||
| 159 | #define ISHTP_CLIENT_DMA_ENABLED 0x80 | ||
| 160 | uint8_t reserved4; | ||
| 161 | uint8_t reserved5; | ||
| 162 | uint8_t reserved6; | ||
| 163 | } __packed; | ||
| 164 | |||
| 165 | struct hbm_props_request { | ||
| 166 | uint8_t hbm_cmd; | ||
| 167 | uint8_t address; | ||
| 168 | uint8_t reserved[2]; | ||
| 169 | } __packed; | ||
| 170 | |||
| 171 | struct hbm_props_response { | ||
| 172 | uint8_t hbm_cmd; | ||
| 173 | uint8_t address; | ||
| 174 | uint8_t status; | ||
| 175 | uint8_t reserved[1]; | ||
| 176 | struct ishtp_client_properties client_properties; | ||
| 177 | } __packed; | ||
| 178 | |||
| 179 | /** | ||
| 180 | * struct hbm_client_connect_request - connect/disconnect request | ||
| 181 | * | ||
| 182 | * @hbm_cmd - bus message command header | ||
| 183 | * @fw_addr - address of the fw client | ||
| 184 | * @host_addr - address of the client in the driver | ||
| 185 | * @reserved | ||
| 186 | */ | ||
| 187 | struct hbm_client_connect_request { | ||
| 188 | uint8_t hbm_cmd; | ||
| 189 | uint8_t fw_addr; | ||
| 190 | uint8_t host_addr; | ||
| 191 | uint8_t reserved; | ||
| 192 | } __packed; | ||
| 193 | |||
| 194 | /** | ||
| 195 | * struct hbm_client_connect_response - connect/disconnect response | ||
| 196 | * | ||
| 197 | * @hbm_cmd - bus message command header | ||
| 198 | * @fw_addr - address of the fw client | ||
| 199 | * @host_addr - address of the client in the driver | ||
| 200 | * @status - status of the request | ||
| 201 | */ | ||
| 202 | struct hbm_client_connect_response { | ||
| 203 | uint8_t hbm_cmd; | ||
| 204 | uint8_t fw_addr; | ||
| 205 | uint8_t host_addr; | ||
| 206 | uint8_t status; | ||
| 207 | } __packed; | ||
| 208 | |||
| 209 | |||
| 210 | #define ISHTP_FC_MESSAGE_RESERVED_LENGTH 5 | ||
| 211 | |||
| 212 | struct hbm_flow_control { | ||
| 213 | uint8_t hbm_cmd; | ||
| 214 | uint8_t fw_addr; | ||
| 215 | uint8_t host_addr; | ||
| 216 | uint8_t reserved[ISHTP_FC_MESSAGE_RESERVED_LENGTH]; | ||
| 217 | } __packed; | ||
| 218 | |||
| 219 | struct dma_alloc_notify { | ||
| 220 | uint8_t hbm; | ||
| 221 | uint8_t status; | ||
| 222 | uint8_t reserved[2]; | ||
| 223 | uint32_t buf_size; | ||
| 224 | uint64_t buf_address; | ||
| 225 | /* [...] May come more size/address pairs */ | ||
| 226 | } __packed; | ||
| 227 | |||
| 228 | struct dma_xfer_hbm { | ||
| 229 | uint8_t hbm; | ||
| 230 | uint8_t fw_client_id; | ||
| 231 | uint8_t host_client_id; | ||
| 232 | uint8_t reserved; | ||
| 233 | uint64_t msg_addr; | ||
| 234 | uint32_t msg_length; | ||
| 235 | uint32_t reserved2; | ||
| 236 | } __packed; | ||
| 237 | |||
| 238 | /* System state */ | ||
| 239 | #define ISHTP_SYSTEM_STATE_CLIENT_ADDR 13 | ||
| 240 | |||
| 241 | #define SYSTEM_STATE_SUBSCRIBE 0x1 | ||
| 242 | #define SYSTEM_STATE_STATUS 0x2 | ||
| 243 | #define SYSTEM_STATE_QUERY_SUBSCRIBERS 0x3 | ||
| 244 | #define SYSTEM_STATE_STATE_CHANGE_REQ 0x4 | ||
| 245 | /*indicates suspend and resume states*/ | ||
| 246 | #define SUSPEND_STATE_BIT (1<<1) | ||
| 247 | |||
| 248 | struct ish_system_states_header { | ||
| 249 | uint32_t cmd; | ||
| 250 | uint32_t cmd_status; /*responses will have this set*/ | ||
| 251 | } __packed; | ||
| 252 | |||
| 253 | struct ish_system_states_subscribe { | ||
| 254 | struct ish_system_states_header hdr; | ||
| 255 | uint32_t states; | ||
| 256 | } __packed; | ||
| 257 | |||
| 258 | struct ish_system_states_status { | ||
| 259 | struct ish_system_states_header hdr; | ||
| 260 | uint32_t supported_states; | ||
| 261 | uint32_t states_status; | ||
| 262 | } __packed; | ||
| 263 | |||
| 264 | struct ish_system_states_query_subscribers { | ||
| 265 | struct ish_system_states_header hdr; | ||
| 266 | } __packed; | ||
| 267 | |||
| 268 | struct ish_system_states_state_change_req { | ||
| 269 | struct ish_system_states_header hdr; | ||
| 270 | uint32_t requested_states; | ||
| 271 | uint32_t states_status; | ||
| 272 | } __packed; | ||
| 273 | |||
| 274 | /** | ||
| 275 | * enum ishtp_hbm_state - host bus message protocol state | ||
| 276 | * | ||
| 277 | * @ISHTP_HBM_IDLE : protocol not started | ||
| 278 | * @ISHTP_HBM_START : start request message was sent | ||
| 279 | * @ISHTP_HBM_ENUM_CLIENTS : enumeration request was sent | ||
| 280 | * @ISHTP_HBM_CLIENT_PROPERTIES : acquiring clients properties | ||
| 281 | */ | ||
| 282 | enum ishtp_hbm_state { | ||
| 283 | ISHTP_HBM_IDLE = 0, | ||
| 284 | ISHTP_HBM_START, | ||
| 285 | ISHTP_HBM_STARTED, | ||
| 286 | ISHTP_HBM_ENUM_CLIENTS, | ||
| 287 | ISHTP_HBM_CLIENT_PROPERTIES, | ||
| 288 | ISHTP_HBM_WORKING, | ||
| 289 | ISHTP_HBM_STOPPED, | ||
| 290 | }; | ||
| 291 | |||
| 292 | static inline void ishtp_hbm_hdr(struct ishtp_msg_hdr *hdr, size_t length) | ||
| 293 | { | ||
| 294 | hdr->host_addr = 0; | ||
| 295 | hdr->fw_addr = 0; | ||
| 296 | hdr->length = length; | ||
| 297 | hdr->msg_complete = 1; | ||
| 298 | hdr->reserved = 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | int ishtp_hbm_start_req(struct ishtp_device *dev); | ||
| 302 | int ishtp_hbm_start_wait(struct ishtp_device *dev); | ||
| 303 | int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, | ||
| 304 | struct ishtp_cl *cl); | ||
| 305 | int ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl); | ||
| 306 | int ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl); | ||
| 307 | void ishtp_hbm_enum_clients_req(struct ishtp_device *dev); | ||
| 308 | void bh_hbm_work_fn(struct work_struct *work); | ||
| 309 | void recv_hbm(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr); | ||
| 310 | void recv_fixed_cl_msg(struct ishtp_device *dev, | ||
| 311 | struct ishtp_msg_hdr *ishtp_hdr); | ||
| 312 | void ishtp_hbm_dispatch(struct ishtp_device *dev, | ||
| 313 | struct ishtp_bus_message *hdr); | ||
| 314 | |||
| 315 | void ishtp_query_subscribers(struct ishtp_device *dev); | ||
| 316 | |||
| 317 | /* Exported I/F */ | ||
| 318 | void ishtp_send_suspend(struct ishtp_device *dev); | ||
| 319 | void ishtp_send_resume(struct ishtp_device *dev); | ||
| 320 | |||
| 321 | #endif /* _ISHTP_HBM_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/init.c b/drivers/hid/intel-ish-hid/ishtp/init.c new file mode 100644 index 000000000000..ac364418e17c --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/init.c | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | /* | ||
| 2 | * Initialization protocol for ISHTP driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/export.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/sched.h> | ||
| 19 | #include <linux/miscdevice.h> | ||
| 20 | #include "ishtp-dev.h" | ||
| 21 | #include "hbm.h" | ||
| 22 | #include "client.h" | ||
| 23 | |||
| 24 | /** | ||
| 25 | * ishtp_dev_state_str() -Convert to string format | ||
| 26 | * @state: state to convert | ||
| 27 | * | ||
| 28 | * Convert state to string for prints | ||
| 29 | * | ||
| 30 | * Return: character pointer to converted string | ||
| 31 | */ | ||
| 32 | const char *ishtp_dev_state_str(int state) | ||
| 33 | { | ||
| 34 | switch (state) { | ||
| 35 | case ISHTP_DEV_INITIALIZING: | ||
| 36 | return "INITIALIZING"; | ||
| 37 | case ISHTP_DEV_INIT_CLIENTS: | ||
| 38 | return "INIT_CLIENTS"; | ||
| 39 | case ISHTP_DEV_ENABLED: | ||
| 40 | return "ENABLED"; | ||
| 41 | case ISHTP_DEV_RESETTING: | ||
| 42 | return "RESETTING"; | ||
| 43 | case ISHTP_DEV_DISABLED: | ||
| 44 | return "DISABLED"; | ||
| 45 | case ISHTP_DEV_POWER_DOWN: | ||
| 46 | return "POWER_DOWN"; | ||
| 47 | case ISHTP_DEV_POWER_UP: | ||
| 48 | return "POWER_UP"; | ||
| 49 | default: | ||
| 50 | return "unknown"; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * ishtp_device_init() - ishtp device init | ||
| 56 | * @dev: ISHTP device instance | ||
| 57 | * | ||
| 58 | * After ISHTP device is alloacted, this function is used to initialize | ||
| 59 | * each field which includes spin lock, work struct and lists | ||
| 60 | */ | ||
| 61 | void ishtp_device_init(struct ishtp_device *dev) | ||
| 62 | { | ||
| 63 | dev->dev_state = ISHTP_DEV_INITIALIZING; | ||
| 64 | INIT_LIST_HEAD(&dev->cl_list); | ||
| 65 | INIT_LIST_HEAD(&dev->device_list); | ||
| 66 | dev->rd_msg_fifo_head = 0; | ||
| 67 | dev->rd_msg_fifo_tail = 0; | ||
| 68 | spin_lock_init(&dev->rd_msg_spinlock); | ||
| 69 | |||
| 70 | init_waitqueue_head(&dev->wait_hbm_recvd_msg); | ||
| 71 | spin_lock_init(&dev->read_list_spinlock); | ||
| 72 | spin_lock_init(&dev->device_lock); | ||
| 73 | spin_lock_init(&dev->device_list_lock); | ||
| 74 | spin_lock_init(&dev->cl_list_lock); | ||
| 75 | spin_lock_init(&dev->fw_clients_lock); | ||
| 76 | INIT_WORK(&dev->bh_hbm_work, bh_hbm_work_fn); | ||
| 77 | |||
| 78 | bitmap_zero(dev->host_clients_map, ISHTP_CLIENTS_MAX); | ||
| 79 | dev->open_handle_count = 0; | ||
| 80 | |||
| 81 | /* | ||
| 82 | * Reserving client ID 0 for ISHTP Bus Message communications | ||
| 83 | */ | ||
| 84 | bitmap_set(dev->host_clients_map, 0, 1); | ||
| 85 | |||
| 86 | INIT_LIST_HEAD(&dev->read_list.list); | ||
| 87 | |||
| 88 | } | ||
| 89 | EXPORT_SYMBOL(ishtp_device_init); | ||
| 90 | |||
| 91 | /** | ||
| 92 | * ishtp_start() - Start ISH processing | ||
| 93 | * @dev: ISHTP device instance | ||
| 94 | * | ||
| 95 | * Start ISHTP processing by sending query subscriber message | ||
| 96 | * | ||
| 97 | * Return: 0 on success else -ENODEV | ||
| 98 | */ | ||
| 99 | int ishtp_start(struct ishtp_device *dev) | ||
| 100 | { | ||
| 101 | if (ishtp_hbm_start_wait(dev)) { | ||
| 102 | dev_err(dev->devc, "HBM haven't started"); | ||
| 103 | goto err; | ||
| 104 | } | ||
| 105 | |||
| 106 | /* suspend & resume notification - send QUERY_SUBSCRIBERS msg */ | ||
| 107 | ishtp_query_subscribers(dev); | ||
| 108 | |||
| 109 | return 0; | ||
| 110 | err: | ||
| 111 | dev_err(dev->devc, "link layer initialization failed.\n"); | ||
| 112 | dev->dev_state = ISHTP_DEV_DISABLED; | ||
| 113 | return -ENODEV; | ||
| 114 | } | ||
| 115 | EXPORT_SYMBOL(ishtp_start); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h new file mode 100644 index 000000000000..a94f9a8a96a0 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h | |||
| @@ -0,0 +1,277 @@ | |||
| 1 | /* | ||
| 2 | * Most ISHTP provider device and ISHTP logic declarations | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef _ISHTP_DEV_H_ | ||
| 17 | #define _ISHTP_DEV_H_ | ||
| 18 | |||
| 19 | #include <linux/types.h> | ||
| 20 | #include <linux/spinlock.h> | ||
| 21 | #include "bus.h" | ||
| 22 | #include "hbm.h" | ||
| 23 | |||
| 24 | #define IPC_PAYLOAD_SIZE 128 | ||
| 25 | #define ISHTP_RD_MSG_BUF_SIZE IPC_PAYLOAD_SIZE | ||
| 26 | #define IPC_FULL_MSG_SIZE 132 | ||
| 27 | |||
| 28 | /* Number of messages to be held in ISR->BH FIFO */ | ||
| 29 | #define RD_INT_FIFO_SIZE 64 | ||
| 30 | |||
| 31 | /* | ||
| 32 | * Number of IPC messages to be held in Tx FIFO, to be sent by ISR - | ||
| 33 | * Tx complete interrupt or RX_COMPLETE handler | ||
| 34 | */ | ||
| 35 | #define IPC_TX_FIFO_SIZE 512 | ||
| 36 | |||
| 37 | /* | ||
| 38 | * Number of Maximum ISHTP Clients | ||
| 39 | */ | ||
| 40 | #define ISHTP_CLIENTS_MAX 256 | ||
| 41 | |||
| 42 | /* | ||
| 43 | * Number of File descriptors/handles | ||
| 44 | * that can be opened to the driver. | ||
| 45 | * | ||
| 46 | * Limit to 255: 256 Total Clients | ||
| 47 | * minus internal client for ISHTP Bus Messages | ||
| 48 | */ | ||
| 49 | #define ISHTP_MAX_OPEN_HANDLE_COUNT (ISHTP_CLIENTS_MAX - 1) | ||
| 50 | |||
| 51 | /* Internal Clients Number */ | ||
| 52 | #define ISHTP_HOST_CLIENT_ID_ANY (-1) | ||
| 53 | #define ISHTP_HBM_HOST_CLIENT_ID 0 | ||
| 54 | |||
| 55 | #define MAX_DMA_DELAY 20 | ||
| 56 | |||
| 57 | /* ISHTP device states */ | ||
| 58 | enum ishtp_dev_state { | ||
| 59 | ISHTP_DEV_INITIALIZING = 0, | ||
| 60 | ISHTP_DEV_INIT_CLIENTS, | ||
| 61 | ISHTP_DEV_ENABLED, | ||
| 62 | ISHTP_DEV_RESETTING, | ||
| 63 | ISHTP_DEV_DISABLED, | ||
| 64 | ISHTP_DEV_POWER_DOWN, | ||
| 65 | ISHTP_DEV_POWER_UP | ||
| 66 | }; | ||
| 67 | const char *ishtp_dev_state_str(int state); | ||
| 68 | |||
| 69 | struct ishtp_cl; | ||
| 70 | |||
| 71 | /** | ||
| 72 | * struct ishtp_fw_client - representation of fw client | ||
| 73 | * | ||
| 74 | * @props - client properties | ||
| 75 | * @client_id - fw client id | ||
| 76 | */ | ||
| 77 | struct ishtp_fw_client { | ||
| 78 | struct ishtp_client_properties props; | ||
| 79 | uint8_t client_id; | ||
| 80 | }; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * struct ishtp_msg_data - ISHTP message data struct | ||
| 84 | * @size: Size of data in the *data | ||
| 85 | * @data: Pointer to data | ||
| 86 | */ | ||
| 87 | struct ishtp_msg_data { | ||
| 88 | uint32_t size; | ||
| 89 | unsigned char *data; | ||
| 90 | }; | ||
| 91 | |||
| 92 | /* | ||
| 93 | * struct ishtp_cl_rb - request block structure | ||
| 94 | * @list: Link to list members | ||
| 95 | * @cl: ISHTP client instance | ||
| 96 | * @buffer: message header | ||
| 97 | * @buf_idx: Index into buffer | ||
| 98 | * @read_time: unused at this time | ||
| 99 | */ | ||
| 100 | struct ishtp_cl_rb { | ||
| 101 | struct list_head list; | ||
| 102 | struct ishtp_cl *cl; | ||
| 103 | struct ishtp_msg_data buffer; | ||
| 104 | unsigned long buf_idx; | ||
| 105 | unsigned long read_time; | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* | ||
| 109 | * Control info for IPC messages ISHTP/IPC sending FIFO - | ||
| 110 | * list with inline data buffer | ||
| 111 | * This structure will be filled with parameters submitted | ||
| 112 | * by the caller glue layer | ||
| 113 | * 'buf' may be pointing to the external buffer or to 'inline_data' | ||
| 114 | * 'offset' will be initialized to 0 by submitting | ||
| 115 | * | ||
| 116 | * 'ipc_send_compl' is intended for use by clients that send fragmented | ||
| 117 | * messages. When a fragment is sent down to IPC msg regs, | ||
| 118 | * it will be called. | ||
| 119 | * If it has more fragments to send, it will do it. With last fragment | ||
| 120 | * it will send appropriate ISHTP "message-complete" flag. | ||
| 121 | * It will remove the outstanding message | ||
| 122 | * (mark outstanding buffer as available). | ||
| 123 | * If counting flow control is in work and there are more flow control | ||
| 124 | * credits, it can put the next client message queued in cl. | ||
| 125 | * structure for IPC processing. | ||
| 126 | * | ||
| 127 | */ | ||
| 128 | struct wr_msg_ctl_info { | ||
| 129 | /* Will be called with 'ipc_send_compl_prm' as parameter */ | ||
| 130 | void (*ipc_send_compl)(void *); | ||
| 131 | |||
| 132 | void *ipc_send_compl_prm; | ||
| 133 | size_t length; | ||
| 134 | struct list_head link; | ||
| 135 | unsigned char inline_data[IPC_FULL_MSG_SIZE]; | ||
| 136 | }; | ||
| 137 | |||
| 138 | /* | ||
| 139 | * The ISHTP layer talks to hardware IPC message using the following | ||
| 140 | * callbacks | ||
| 141 | */ | ||
| 142 | struct ishtp_hw_ops { | ||
| 143 | int (*hw_reset)(struct ishtp_device *dev); | ||
| 144 | int (*ipc_reset)(struct ishtp_device *dev); | ||
| 145 | uint32_t (*ipc_get_header)(struct ishtp_device *dev, int length, | ||
| 146 | int busy); | ||
| 147 | int (*write)(struct ishtp_device *dev, | ||
| 148 | void (*ipc_send_compl)(void *), void *ipc_send_compl_prm, | ||
| 149 | unsigned char *msg, int length); | ||
| 150 | uint32_t (*ishtp_read_hdr)(const struct ishtp_device *dev); | ||
| 151 | int (*ishtp_read)(struct ishtp_device *dev, unsigned char *buffer, | ||
| 152 | unsigned long buffer_length); | ||
| 153 | uint32_t (*get_fw_status)(struct ishtp_device *dev); | ||
| 154 | void (*sync_fw_clock)(struct ishtp_device *dev); | ||
| 155 | }; | ||
| 156 | |||
| 157 | /** | ||
| 158 | * struct ishtp_device - ISHTP private device struct | ||
| 159 | */ | ||
| 160 | struct ishtp_device { | ||
| 161 | struct device *devc; /* pointer to lowest device */ | ||
| 162 | struct pci_dev *pdev; /* PCI device to get device ids */ | ||
| 163 | |||
| 164 | /* waitq for waiting for suspend response */ | ||
| 165 | wait_queue_head_t suspend_wait; | ||
| 166 | bool suspend_flag; /* Suspend is active */ | ||
| 167 | |||
| 168 | /* waitq for waiting for resume response */ | ||
| 169 | wait_queue_head_t resume_wait; | ||
| 170 | bool resume_flag; /*Resume is active */ | ||
| 171 | |||
| 172 | /* | ||
| 173 | * lock for the device, for everything that doesn't have | ||
| 174 | * a dedicated spinlock | ||
| 175 | */ | ||
| 176 | spinlock_t device_lock; | ||
| 177 | |||
| 178 | bool recvd_hw_ready; | ||
| 179 | struct hbm_version version; | ||
| 180 | int transfer_path; /* Choice of transfer path: IPC or DMA */ | ||
| 181 | |||
| 182 | /* ishtp device states */ | ||
| 183 | enum ishtp_dev_state dev_state; | ||
| 184 | enum ishtp_hbm_state hbm_state; | ||
| 185 | |||
| 186 | /* driver read queue */ | ||
| 187 | struct ishtp_cl_rb read_list; | ||
| 188 | spinlock_t read_list_spinlock; | ||
| 189 | |||
| 190 | /* list of ishtp_cl's */ | ||
| 191 | struct list_head cl_list; | ||
| 192 | spinlock_t cl_list_lock; | ||
| 193 | long open_handle_count; | ||
| 194 | |||
| 195 | /* List of bus devices */ | ||
| 196 | struct list_head device_list; | ||
| 197 | spinlock_t device_list_lock; | ||
| 198 | |||
| 199 | /* waiting queues for receive message from FW */ | ||
| 200 | wait_queue_head_t wait_hw_ready; | ||
| 201 | wait_queue_head_t wait_hbm_recvd_msg; | ||
| 202 | |||
| 203 | /* FIFO for input messages for BH processing */ | ||
| 204 | unsigned char rd_msg_fifo[RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE]; | ||
| 205 | unsigned int rd_msg_fifo_head, rd_msg_fifo_tail; | ||
| 206 | spinlock_t rd_msg_spinlock; | ||
| 207 | struct work_struct bh_hbm_work; | ||
| 208 | |||
| 209 | /* IPC write queue */ | ||
| 210 | struct wr_msg_ctl_info wr_processing_list_head, wr_free_list_head; | ||
| 211 | /* For both processing list and free list */ | ||
| 212 | spinlock_t wr_processing_spinlock; | ||
| 213 | |||
| 214 | spinlock_t out_ipc_spinlock; | ||
| 215 | |||
| 216 | struct ishtp_fw_client *fw_clients; /*Note:memory has to be allocated*/ | ||
| 217 | DECLARE_BITMAP(fw_clients_map, ISHTP_CLIENTS_MAX); | ||
| 218 | DECLARE_BITMAP(host_clients_map, ISHTP_CLIENTS_MAX); | ||
| 219 | uint8_t fw_clients_num; | ||
| 220 | uint8_t fw_client_presentation_num; | ||
| 221 | uint8_t fw_client_index; | ||
| 222 | spinlock_t fw_clients_lock; | ||
| 223 | |||
| 224 | /* TX DMA buffers and slots */ | ||
| 225 | int ishtp_host_dma_enabled; | ||
| 226 | void *ishtp_host_dma_tx_buf; | ||
| 227 | unsigned int ishtp_host_dma_tx_buf_size; | ||
| 228 | uint64_t ishtp_host_dma_tx_buf_phys; | ||
| 229 | int ishtp_dma_num_slots; | ||
| 230 | |||
| 231 | /* map of 4k blocks in Tx dma buf: 0-free, 1-used */ | ||
| 232 | uint8_t *ishtp_dma_tx_map; | ||
| 233 | spinlock_t ishtp_dma_tx_lock; | ||
| 234 | |||
| 235 | /* RX DMA buffers and slots */ | ||
| 236 | void *ishtp_host_dma_rx_buf; | ||
| 237 | unsigned int ishtp_host_dma_rx_buf_size; | ||
| 238 | uint64_t ishtp_host_dma_rx_buf_phys; | ||
| 239 | |||
| 240 | /* Dump to trace buffers if enabled*/ | ||
| 241 | void (*print_log)(struct ishtp_device *dev, char *format, ...); | ||
| 242 | |||
| 243 | /* Debug stats */ | ||
| 244 | unsigned int ipc_rx_cnt; | ||
| 245 | unsigned long long ipc_rx_bytes_cnt; | ||
| 246 | unsigned int ipc_tx_cnt; | ||
| 247 | unsigned long long ipc_tx_bytes_cnt; | ||
| 248 | |||
| 249 | const struct ishtp_hw_ops *ops; | ||
| 250 | size_t mtu; | ||
| 251 | uint32_t ishtp_msg_hdr; | ||
| 252 | char hw[0] __aligned(sizeof(void *)); | ||
| 253 | }; | ||
| 254 | |||
| 255 | static inline unsigned long ishtp_secs_to_jiffies(unsigned long sec) | ||
| 256 | { | ||
| 257 | return msecs_to_jiffies(sec * MSEC_PER_SEC); | ||
| 258 | } | ||
| 259 | |||
| 260 | /* | ||
| 261 | * Register Access Function | ||
| 262 | */ | ||
| 263 | static inline int ish_ipc_reset(struct ishtp_device *dev) | ||
| 264 | { | ||
| 265 | return dev->ops->ipc_reset(dev); | ||
| 266 | } | ||
| 267 | |||
| 268 | static inline int ish_hw_reset(struct ishtp_device *dev) | ||
| 269 | { | ||
| 270 | return dev->ops->hw_reset(dev); | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Exported function */ | ||
| 274 | void ishtp_device_init(struct ishtp_device *dev); | ||
| 275 | int ishtp_start(struct ishtp_device *dev); | ||
| 276 | |||
| 277 | #endif /*_ISHTP_DEV_H_*/ | ||
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index b4b8c6abb03e..0a0eca5da47d 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c | |||
| @@ -76,6 +76,7 @@ static const struct hid_blacklist { | |||
| 76 | { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL }, | 76 | { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL }, |
| 77 | { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS }, | 77 | { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS }, |
| 78 | { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS }, | 78 | { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS }, |
| 79 | { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL }, | ||
| 79 | { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET }, | 80 | { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET }, |
| 80 | { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, | 81 | { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, |
| 81 | { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, | 82 | { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, |
| @@ -98,6 +99,7 @@ static const struct hid_blacklist { | |||
| 98 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS }, | 99 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS }, |
| 99 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS }, | 100 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS }, |
| 100 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS }, | 101 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS }, |
| 102 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS }, | ||
| 101 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, | 103 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, |
| 102 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS }, | 104 | { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS }, |
| 103 | { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, | 105 | { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, |
| @@ -143,7 +145,7 @@ static const struct hid_blacklist { | |||
| 143 | { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT }, | 145 | { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT }, |
| 144 | { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, | 146 | { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, |
| 145 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, | 147 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, |
| 146 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT }, | 148 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2, HID_QUIRK_MULTI_INPUT }, |
| 147 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, | 149 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, |
| 148 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT }, | 150 | { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT }, |
| 149 | { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, | 151 | { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, |
diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 4681a65a4579..b4800ea891cb 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h | |||
| @@ -90,6 +90,8 @@ | |||
| 90 | #include <linux/module.h> | 90 | #include <linux/module.h> |
| 91 | #include <linux/mod_devicetable.h> | 91 | #include <linux/mod_devicetable.h> |
| 92 | #include <linux/hid.h> | 92 | #include <linux/hid.h> |
| 93 | #include <linux/kfifo.h> | ||
| 94 | #include <linux/leds.h> | ||
| 93 | #include <linux/usb/input.h> | 95 | #include <linux/usb/input.h> |
| 94 | #include <linux/power_supply.h> | 96 | #include <linux/power_supply.h> |
| 95 | #include <asm/unaligned.h> | 97 | #include <asm/unaligned.h> |
| @@ -105,32 +107,95 @@ | |||
| 105 | #define USB_VENDOR_ID_WACOM 0x056a | 107 | #define USB_VENDOR_ID_WACOM 0x056a |
| 106 | #define USB_VENDOR_ID_LENOVO 0x17ef | 108 | #define USB_VENDOR_ID_LENOVO 0x17ef |
| 107 | 109 | ||
| 110 | enum wacom_worker { | ||
| 111 | WACOM_WORKER_WIRELESS, | ||
| 112 | WACOM_WORKER_BATTERY, | ||
| 113 | WACOM_WORKER_REMOTE, | ||
| 114 | }; | ||
| 115 | |||
| 116 | struct wacom; | ||
| 117 | |||
| 118 | struct wacom_led { | ||
| 119 | struct led_classdev cdev; | ||
| 120 | struct led_trigger trigger; | ||
| 121 | struct wacom *wacom; | ||
| 122 | unsigned int group; | ||
| 123 | unsigned int id; | ||
| 124 | u8 llv; | ||
| 125 | u8 hlv; | ||
| 126 | bool held; | ||
| 127 | }; | ||
| 128 | |||
| 129 | struct wacom_group_leds { | ||
| 130 | u8 select; /* status led selector (0..3) */ | ||
| 131 | struct wacom_led *leds; | ||
| 132 | unsigned int count; | ||
| 133 | struct device *dev; | ||
| 134 | }; | ||
| 135 | |||
| 136 | struct wacom_battery { | ||
| 137 | struct wacom *wacom; | ||
| 138 | struct power_supply_desc bat_desc; | ||
| 139 | struct power_supply *battery; | ||
| 140 | char bat_name[WACOM_NAME_MAX]; | ||
| 141 | int battery_capacity; | ||
| 142 | int bat_charging; | ||
| 143 | int bat_connected; | ||
| 144 | int ps_connected; | ||
| 145 | }; | ||
| 146 | |||
| 147 | struct wacom_remote { | ||
| 148 | spinlock_t remote_lock; | ||
| 149 | struct kfifo remote_fifo; | ||
| 150 | struct kobject *remote_dir; | ||
| 151 | struct { | ||
| 152 | struct attribute_group group; | ||
| 153 | u32 serial; | ||
| 154 | struct input_dev *input; | ||
| 155 | bool registered; | ||
| 156 | struct wacom_battery battery; | ||
| 157 | } remotes[WACOM_MAX_REMOTES]; | ||
| 158 | }; | ||
| 159 | |||
| 108 | struct wacom { | 160 | struct wacom { |
| 109 | struct usb_device *usbdev; | 161 | struct usb_device *usbdev; |
| 110 | struct usb_interface *intf; | 162 | struct usb_interface *intf; |
| 111 | struct wacom_wac wacom_wac; | 163 | struct wacom_wac wacom_wac; |
| 112 | struct hid_device *hdev; | 164 | struct hid_device *hdev; |
| 113 | struct mutex lock; | 165 | struct mutex lock; |
| 114 | struct work_struct work; | 166 | struct work_struct wireless_work; |
| 115 | struct wacom_led { | 167 | struct work_struct battery_work; |
| 116 | u8 select[5]; /* status led selector (0..3) */ | 168 | struct work_struct remote_work; |
| 169 | struct wacom_remote *remote; | ||
| 170 | struct wacom_leds { | ||
| 171 | struct wacom_group_leds *groups; | ||
| 172 | unsigned int count; | ||
| 117 | u8 llv; /* status led brightness no button (1..127) */ | 173 | u8 llv; /* status led brightness no button (1..127) */ |
| 118 | u8 hlv; /* status led brightness button pressed (1..127) */ | 174 | u8 hlv; /* status led brightness button pressed (1..127) */ |
| 119 | u8 img_lum; /* OLED matrix display brightness */ | 175 | u8 img_lum; /* OLED matrix display brightness */ |
| 176 | u8 max_llv; /* maximum brightness of LED (llv) */ | ||
| 177 | u8 max_hlv; /* maximum brightness of LED (hlv) */ | ||
| 120 | } led; | 178 | } led; |
| 121 | bool led_initialized; | 179 | struct wacom_battery battery; |
| 122 | struct power_supply *battery; | 180 | bool resources; |
| 123 | struct power_supply *ac; | ||
| 124 | struct power_supply_desc battery_desc; | ||
| 125 | struct power_supply_desc ac_desc; | ||
| 126 | struct kobject *remote_dir; | ||
| 127 | struct attribute_group remote_group[5]; | ||
| 128 | }; | 181 | }; |
| 129 | 182 | ||
| 130 | static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) | 183 | static inline void wacom_schedule_work(struct wacom_wac *wacom_wac, |
| 184 | enum wacom_worker which) | ||
| 131 | { | 185 | { |
| 132 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); | 186 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); |
| 133 | schedule_work(&wacom->work); | 187 | |
| 188 | switch (which) { | ||
| 189 | case WACOM_WORKER_WIRELESS: | ||
| 190 | schedule_work(&wacom->wireless_work); | ||
| 191 | break; | ||
| 192 | case WACOM_WORKER_BATTERY: | ||
| 193 | schedule_work(&wacom->battery_work); | ||
| 194 | break; | ||
| 195 | case WACOM_WORKER_REMOTE: | ||
| 196 | schedule_work(&wacom->remote_work); | ||
| 197 | break; | ||
| 198 | } | ||
| 134 | } | 199 | } |
| 135 | 200 | ||
| 136 | extern const struct hid_device_id wacom_ids[]; | 201 | extern const struct hid_device_id wacom_ids[]; |
| @@ -149,7 +214,8 @@ int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, | |||
| 149 | struct hid_usage *usage, __s32 value); | 214 | struct hid_usage *usage, __s32 value); |
| 150 | void wacom_wac_report(struct hid_device *hdev, struct hid_report *report); | 215 | void wacom_wac_report(struct hid_device *hdev, struct hid_report *report); |
| 151 | void wacom_battery_work(struct work_struct *work); | 216 | void wacom_battery_work(struct work_struct *work); |
| 152 | int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, | 217 | enum led_brightness wacom_leds_brightness_get(struct wacom_led *led); |
| 153 | int index); | 218 | struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group, |
| 154 | void wacom_remote_destroy_attr_group(struct wacom *wacom, __u32 serial); | 219 | unsigned int id); |
| 220 | struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur); | ||
| 155 | #endif | 221 | #endif |
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 499cc8213cfe..5e7a5648e708 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c | |||
| @@ -25,7 +25,6 @@ | |||
| 25 | #define WAC_CMD_RETRIES 10 | 25 | #define WAC_CMD_RETRIES 10 |
| 26 | #define WAC_CMD_DELETE_PAIRING 0x20 | 26 | #define WAC_CMD_DELETE_PAIRING 0x20 |
| 27 | #define WAC_CMD_UNPAIR_ALL 0xFF | 27 | #define WAC_CMD_UNPAIR_ALL 0xFF |
| 28 | #define WAC_REMOTE_SERIAL_MAX_STRLEN 9 | ||
| 29 | 28 | ||
| 30 | #define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP) | 29 | #define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP) |
| 31 | #define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP) | 30 | #define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP) |
| @@ -91,7 +90,12 @@ static void wacom_close(struct input_dev *dev) | |||
| 91 | { | 90 | { |
| 92 | struct wacom *wacom = input_get_drvdata(dev); | 91 | struct wacom *wacom = input_get_drvdata(dev); |
| 93 | 92 | ||
| 94 | hid_hw_close(wacom->hdev); | 93 | /* |
| 94 | * wacom->hdev should never be null, but surprisingly, I had the case | ||
| 95 | * once while unplugging the Wacom Wireless Receiver. | ||
| 96 | */ | ||
| 97 | if (wacom->hdev) | ||
| 98 | hid_hw_close(wacom->hdev); | ||
| 95 | } | 99 | } |
| 96 | 100 | ||
| 97 | /* | 101 | /* |
| @@ -523,36 +527,95 @@ struct wacom_hdev_data { | |||
| 523 | static LIST_HEAD(wacom_udev_list); | 527 | static LIST_HEAD(wacom_udev_list); |
| 524 | static DEFINE_MUTEX(wacom_udev_list_lock); | 528 | static DEFINE_MUTEX(wacom_udev_list_lock); |
| 525 | 529 | ||
| 530 | static bool compare_device_paths(struct hid_device *hdev_a, | ||
| 531 | struct hid_device *hdev_b, char separator) | ||
| 532 | { | ||
| 533 | int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys; | ||
| 534 | int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys; | ||
| 535 | |||
| 536 | if (n1 != n2 || n1 <= 0 || n2 <= 0) | ||
| 537 | return false; | ||
| 538 | |||
| 539 | return !strncmp(hdev_a->phys, hdev_b->phys, n1); | ||
| 540 | } | ||
| 541 | |||
| 526 | static bool wacom_are_sibling(struct hid_device *hdev, | 542 | static bool wacom_are_sibling(struct hid_device *hdev, |
| 527 | struct hid_device *sibling) | 543 | struct hid_device *sibling) |
| 528 | { | 544 | { |
| 529 | struct wacom *wacom = hid_get_drvdata(hdev); | 545 | struct wacom *wacom = hid_get_drvdata(hdev); |
| 530 | struct wacom_features *features = &wacom->wacom_wac.features; | 546 | struct wacom_features *features = &wacom->wacom_wac.features; |
| 531 | int vid = features->oVid; | 547 | struct wacom *sibling_wacom = hid_get_drvdata(sibling); |
| 532 | int pid = features->oPid; | 548 | struct wacom_features *sibling_features = &sibling_wacom->wacom_wac.features; |
| 533 | int n1,n2; | 549 | __u32 oVid = features->oVid ? features->oVid : hdev->vendor; |
| 550 | __u32 oPid = features->oPid ? features->oPid : hdev->product; | ||
| 551 | |||
| 552 | /* The defined oVid/oPid must match that of the sibling */ | ||
| 553 | if (features->oVid != HID_ANY_ID && sibling->vendor != oVid) | ||
| 554 | return false; | ||
| 555 | if (features->oPid != HID_ANY_ID && sibling->product != oPid) | ||
| 556 | return false; | ||
| 534 | 557 | ||
| 535 | if (vid == 0 && pid == 0) { | 558 | /* |
| 536 | vid = hdev->vendor; | 559 | * Devices with the same VID/PID must share the same physical |
| 537 | pid = hdev->product; | 560 | * device path, while those with different VID/PID must share |
| 561 | * the same physical parent device path. | ||
| 562 | */ | ||
| 563 | if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) { | ||
| 564 | if (!compare_device_paths(hdev, sibling, '/')) | ||
| 565 | return false; | ||
| 566 | } else { | ||
| 567 | if (!compare_device_paths(hdev, sibling, '.')) | ||
| 568 | return false; | ||
| 538 | } | 569 | } |
| 539 | 570 | ||
| 540 | if (vid != sibling->vendor || pid != sibling->product) | 571 | /* Skip the remaining heuristics unless you are a HID_GENERIC device */ |
| 572 | if (features->type != HID_GENERIC) | ||
| 573 | return true; | ||
| 574 | |||
| 575 | /* | ||
| 576 | * Direct-input devices may not be siblings of indirect-input | ||
| 577 | * devices. | ||
| 578 | */ | ||
| 579 | if ((features->device_type & WACOM_DEVICETYPE_DIRECT) && | ||
| 580 | !(sibling_features->device_type & WACOM_DEVICETYPE_DIRECT)) | ||
| 541 | return false; | 581 | return false; |
| 542 | 582 | ||
| 543 | /* Compare the physical path. */ | 583 | /* |
| 544 | n1 = strrchr(hdev->phys, '.') - hdev->phys; | 584 | * Indirect-input devices may not be siblings of direct-input |
| 545 | n2 = strrchr(sibling->phys, '.') - sibling->phys; | 585 | * devices. |
| 546 | if (n1 != n2 || n1 <= 0 || n2 <= 0) | 586 | */ |
| 587 | if (!(features->device_type & WACOM_DEVICETYPE_DIRECT) && | ||
| 588 | (sibling_features->device_type & WACOM_DEVICETYPE_DIRECT)) | ||
| 589 | return false; | ||
| 590 | |||
| 591 | /* Pen devices may only be siblings of touch devices */ | ||
| 592 | if ((features->device_type & WACOM_DEVICETYPE_PEN) && | ||
| 593 | !(sibling_features->device_type & WACOM_DEVICETYPE_TOUCH)) | ||
| 547 | return false; | 594 | return false; |
| 548 | 595 | ||
| 549 | return !strncmp(hdev->phys, sibling->phys, n1); | 596 | /* Touch devices may only be siblings of pen devices */ |
| 597 | if ((features->device_type & WACOM_DEVICETYPE_TOUCH) && | ||
| 598 | !(sibling_features->device_type & WACOM_DEVICETYPE_PEN)) | ||
| 599 | return false; | ||
| 600 | |||
| 601 | /* | ||
| 602 | * No reason could be found for these two devices to NOT be | ||
| 603 | * siblings, so there's a good chance they ARE siblings | ||
| 604 | */ | ||
| 605 | return true; | ||
| 550 | } | 606 | } |
| 551 | 607 | ||
| 552 | static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) | 608 | static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) |
| 553 | { | 609 | { |
| 554 | struct wacom_hdev_data *data; | 610 | struct wacom_hdev_data *data; |
| 555 | 611 | ||
| 612 | /* Try to find an already-probed interface from the same device */ | ||
| 613 | list_for_each_entry(data, &wacom_udev_list, list) { | ||
| 614 | if (compare_device_paths(hdev, data->dev, '/')) | ||
| 615 | return data; | ||
| 616 | } | ||
| 617 | |||
| 618 | /* Fallback to finding devices that appear to be "siblings" */ | ||
| 556 | list_for_each_entry(data, &wacom_udev_list, list) { | 619 | list_for_each_entry(data, &wacom_udev_list, list) { |
| 557 | if (wacom_are_sibling(hdev, data->dev)) { | 620 | if (wacom_are_sibling(hdev, data->dev)) { |
| 558 | kref_get(&data->kref); | 621 | kref_get(&data->kref); |
| @@ -563,6 +626,38 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) | |||
| 563 | return NULL; | 626 | return NULL; |
| 564 | } | 627 | } |
| 565 | 628 | ||
| 629 | static void wacom_release_shared_data(struct kref *kref) | ||
| 630 | { | ||
| 631 | struct wacom_hdev_data *data = | ||
| 632 | container_of(kref, struct wacom_hdev_data, kref); | ||
| 633 | |||
| 634 | mutex_lock(&wacom_udev_list_lock); | ||
| 635 | list_del(&data->list); | ||
| 636 | mutex_unlock(&wacom_udev_list_lock); | ||
| 637 | |||
| 638 | kfree(data); | ||
| 639 | } | ||
| 640 | |||
| 641 | static void wacom_remove_shared_data(void *res) | ||
| 642 | { | ||
| 643 | struct wacom *wacom = res; | ||
| 644 | struct wacom_hdev_data *data; | ||
| 645 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | ||
| 646 | |||
| 647 | if (wacom_wac->shared) { | ||
| 648 | data = container_of(wacom_wac->shared, struct wacom_hdev_data, | ||
| 649 | shared); | ||
| 650 | |||
| 651 | if (wacom_wac->shared->touch == wacom->hdev) | ||
| 652 | wacom_wac->shared->touch = NULL; | ||
| 653 | else if (wacom_wac->shared->pen == wacom->hdev) | ||
| 654 | wacom_wac->shared->pen = NULL; | ||
| 655 | |||
| 656 | kref_put(&data->kref, wacom_release_shared_data); | ||
| 657 | wacom_wac->shared = NULL; | ||
| 658 | } | ||
| 659 | } | ||
| 660 | |||
| 566 | static int wacom_add_shared_data(struct hid_device *hdev) | 661 | static int wacom_add_shared_data(struct hid_device *hdev) |
| 567 | { | 662 | { |
| 568 | struct wacom *wacom = hid_get_drvdata(hdev); | 663 | struct wacom *wacom = hid_get_drvdata(hdev); |
| @@ -587,6 +682,13 @@ static int wacom_add_shared_data(struct hid_device *hdev) | |||
| 587 | 682 | ||
| 588 | wacom_wac->shared = &data->shared; | 683 | wacom_wac->shared = &data->shared; |
| 589 | 684 | ||
| 685 | retval = devm_add_action(&hdev->dev, wacom_remove_shared_data, wacom); | ||
| 686 | if (retval) { | ||
| 687 | mutex_unlock(&wacom_udev_list_lock); | ||
| 688 | wacom_remove_shared_data(wacom); | ||
| 689 | return retval; | ||
| 690 | } | ||
| 691 | |||
| 590 | if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) | 692 | if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) |
| 591 | wacom_wac->shared->touch = hdev; | 693 | wacom_wac->shared->touch = hdev; |
| 592 | else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN) | 694 | else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN) |
| @@ -597,37 +699,6 @@ out: | |||
| 597 | return retval; | 699 | return retval; |
| 598 | } | 700 | } |
| 599 | 701 | ||
| 600 | static void wacom_release_shared_data(struct kref *kref) | ||
| 601 | { | ||
| 602 | struct wacom_hdev_data *data = | ||
| 603 | container_of(kref, struct wacom_hdev_data, kref); | ||
| 604 | |||
| 605 | mutex_lock(&wacom_udev_list_lock); | ||
| 606 | list_del(&data->list); | ||
| 607 | mutex_unlock(&wacom_udev_list_lock); | ||
| 608 | |||
| 609 | kfree(data); | ||
| 610 | } | ||
| 611 | |||
| 612 | static void wacom_remove_shared_data(struct wacom *wacom) | ||
| 613 | { | ||
| 614 | struct wacom_hdev_data *data; | ||
| 615 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | ||
| 616 | |||
| 617 | if (wacom_wac->shared) { | ||
| 618 | data = container_of(wacom_wac->shared, struct wacom_hdev_data, | ||
| 619 | shared); | ||
| 620 | |||
| 621 | if (wacom_wac->shared->touch == wacom->hdev) | ||
| 622 | wacom_wac->shared->touch = NULL; | ||
| 623 | else if (wacom_wac->shared->pen == wacom->hdev) | ||
| 624 | wacom_wac->shared->pen = NULL; | ||
| 625 | |||
| 626 | kref_put(&data->kref, wacom_release_shared_data); | ||
| 627 | wacom_wac->shared = NULL; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | static int wacom_led_control(struct wacom *wacom) | 702 | static int wacom_led_control(struct wacom *wacom) |
| 632 | { | 703 | { |
| 633 | unsigned char *buf; | 704 | unsigned char *buf; |
| @@ -635,6 +706,12 @@ static int wacom_led_control(struct wacom *wacom) | |||
| 635 | unsigned char report_id = WAC_CMD_LED_CONTROL; | 706 | unsigned char report_id = WAC_CMD_LED_CONTROL; |
| 636 | int buf_size = 9; | 707 | int buf_size = 9; |
| 637 | 708 | ||
| 709 | if (!hid_get_drvdata(wacom->hdev)) | ||
| 710 | return -ENODEV; | ||
| 711 | |||
| 712 | if (!wacom->led.groups) | ||
| 713 | return -ENOTSUPP; | ||
| 714 | |||
| 638 | if (wacom->wacom_wac.pid) { /* wireless connected */ | 715 | if (wacom->wacom_wac.pid) { /* wireless connected */ |
| 639 | report_id = WAC_CMD_WL_LED_CONTROL; | 716 | report_id = WAC_CMD_WL_LED_CONTROL; |
| 640 | buf_size = 13; | 717 | buf_size = 13; |
| @@ -650,7 +727,7 @@ static int wacom_led_control(struct wacom *wacom) | |||
| 650 | * one of four values: | 727 | * one of four values: |
| 651 | * 0 = Low; 1 = Medium; 2 = High; 3 = Off | 728 | * 0 = Low; 1 = Medium; 2 = High; 3 = Off |
| 652 | */ | 729 | */ |
| 653 | int ring_led = wacom->led.select[0] & 0x03; | 730 | int ring_led = wacom->led.groups[0].select & 0x03; |
| 654 | int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03; | 731 | int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03; |
| 655 | int crop_lum = 0; | 732 | int crop_lum = 0; |
| 656 | unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led); | 733 | unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led); |
| @@ -665,11 +742,11 @@ static int wacom_led_control(struct wacom *wacom) | |||
| 665 | buf[1] = led_bits; | 742 | buf[1] = led_bits; |
| 666 | } | 743 | } |
| 667 | else { | 744 | else { |
| 668 | int led = wacom->led.select[0] | 0x4; | 745 | int led = wacom->led.groups[0].select | 0x4; |
| 669 | 746 | ||
| 670 | if (wacom->wacom_wac.features.type == WACOM_21UX2 || | 747 | if (wacom->wacom_wac.features.type == WACOM_21UX2 || |
| 671 | wacom->wacom_wac.features.type == WACOM_24HD) | 748 | wacom->wacom_wac.features.type == WACOM_24HD) |
| 672 | led |= (wacom->led.select[1] << 4) | 0x40; | 749 | led |= (wacom->led.groups[1].select << 4) | 0x40; |
| 673 | 750 | ||
| 674 | buf[0] = report_id; | 751 | buf[0] = report_id; |
| 675 | buf[1] = led; | 752 | buf[1] = led; |
| @@ -741,7 +818,7 @@ static ssize_t wacom_led_select_store(struct device *dev, int set_id, | |||
| 741 | 818 | ||
| 742 | mutex_lock(&wacom->lock); | 819 | mutex_lock(&wacom->lock); |
| 743 | 820 | ||
| 744 | wacom->led.select[set_id] = id & 0x3; | 821 | wacom->led.groups[set_id].select = id & 0x3; |
| 745 | err = wacom_led_control(wacom); | 822 | err = wacom_led_control(wacom); |
| 746 | 823 | ||
| 747 | mutex_unlock(&wacom->lock); | 824 | mutex_unlock(&wacom->lock); |
| @@ -761,7 +838,7 @@ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ | |||
| 761 | struct hid_device *hdev = to_hid_device(dev);\ | 838 | struct hid_device *hdev = to_hid_device(dev);\ |
| 762 | struct wacom *wacom = hid_get_drvdata(hdev); \ | 839 | struct wacom *wacom = hid_get_drvdata(hdev); \ |
| 763 | return scnprintf(buf, PAGE_SIZE, "%d\n", \ | 840 | return scnprintf(buf, PAGE_SIZE, "%d\n", \ |
| 764 | wacom->led.select[SET_ID]); \ | 841 | wacom->led.groups[SET_ID].select); \ |
| 765 | } \ | 842 | } \ |
| 766 | static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM, \ | 843 | static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM, \ |
| 767 | wacom_led##SET_ID##_select_show, \ | 844 | wacom_led##SET_ID##_select_show, \ |
| @@ -904,6 +981,327 @@ static struct attribute_group intuos5_led_attr_group = { | |||
| 904 | .attrs = intuos5_led_attrs, | 981 | .attrs = intuos5_led_attrs, |
| 905 | }; | 982 | }; |
| 906 | 983 | ||
| 984 | struct wacom_sysfs_group_devres { | ||
| 985 | struct attribute_group *group; | ||
| 986 | struct kobject *root; | ||
| 987 | }; | ||
| 988 | |||
| 989 | static void wacom_devm_sysfs_group_release(struct device *dev, void *res) | ||
| 990 | { | ||
| 991 | struct wacom_sysfs_group_devres *devres = res; | ||
| 992 | struct kobject *kobj = devres->root; | ||
| 993 | |||
| 994 | dev_dbg(dev, "%s: dropping reference to %s\n", | ||
| 995 | __func__, devres->group->name); | ||
| 996 | sysfs_remove_group(kobj, devres->group); | ||
| 997 | } | ||
| 998 | |||
| 999 | static int __wacom_devm_sysfs_create_group(struct wacom *wacom, | ||
| 1000 | struct kobject *root, | ||
| 1001 | struct attribute_group *group) | ||
| 1002 | { | ||
| 1003 | struct wacom_sysfs_group_devres *devres; | ||
| 1004 | int error; | ||
| 1005 | |||
| 1006 | devres = devres_alloc(wacom_devm_sysfs_group_release, | ||
| 1007 | sizeof(struct wacom_sysfs_group_devres), | ||
| 1008 | GFP_KERNEL); | ||
| 1009 | if (!devres) | ||
| 1010 | return -ENOMEM; | ||
| 1011 | |||
| 1012 | devres->group = group; | ||
| 1013 | devres->root = root; | ||
| 1014 | |||
| 1015 | error = sysfs_create_group(devres->root, group); | ||
| 1016 | if (error) | ||
| 1017 | return error; | ||
| 1018 | |||
| 1019 | devres_add(&wacom->hdev->dev, devres); | ||
| 1020 | |||
| 1021 | return 0; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | static int wacom_devm_sysfs_create_group(struct wacom *wacom, | ||
| 1025 | struct attribute_group *group) | ||
| 1026 | { | ||
| 1027 | return __wacom_devm_sysfs_create_group(wacom, &wacom->hdev->dev.kobj, | ||
| 1028 | group); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | enum led_brightness wacom_leds_brightness_get(struct wacom_led *led) | ||
| 1032 | { | ||
| 1033 | struct wacom *wacom = led->wacom; | ||
| 1034 | |||
| 1035 | if (wacom->led.max_hlv) | ||
| 1036 | return led->hlv * LED_FULL / wacom->led.max_hlv; | ||
| 1037 | |||
| 1038 | if (wacom->led.max_llv) | ||
| 1039 | return led->llv * LED_FULL / wacom->led.max_llv; | ||
| 1040 | |||
| 1041 | /* device doesn't support brightness tuning */ | ||
| 1042 | return LED_FULL; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | static enum led_brightness __wacom_led_brightness_get(struct led_classdev *cdev) | ||
| 1046 | { | ||
| 1047 | struct wacom_led *led = container_of(cdev, struct wacom_led, cdev); | ||
| 1048 | struct wacom *wacom = led->wacom; | ||
| 1049 | |||
| 1050 | if (wacom->led.groups[led->group].select != led->id) | ||
| 1051 | return LED_OFF; | ||
| 1052 | |||
| 1053 | return wacom_leds_brightness_get(led); | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | static int wacom_led_brightness_set(struct led_classdev *cdev, | ||
| 1057 | enum led_brightness brightness) | ||
| 1058 | { | ||
| 1059 | struct wacom_led *led = container_of(cdev, struct wacom_led, cdev); | ||
| 1060 | struct wacom *wacom = led->wacom; | ||
| 1061 | int error; | ||
| 1062 | |||
| 1063 | mutex_lock(&wacom->lock); | ||
| 1064 | |||
| 1065 | if (!wacom->led.groups || (brightness == LED_OFF && | ||
| 1066 | wacom->led.groups[led->group].select != led->id)) { | ||
| 1067 | error = 0; | ||
| 1068 | goto out; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL; | ||
| 1072 | led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL; | ||
| 1073 | |||
| 1074 | wacom->led.groups[led->group].select = led->id; | ||
| 1075 | |||
| 1076 | error = wacom_led_control(wacom); | ||
| 1077 | |||
| 1078 | out: | ||
| 1079 | mutex_unlock(&wacom->lock); | ||
| 1080 | |||
| 1081 | return error; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | static void wacom_led_readonly_brightness_set(struct led_classdev *cdev, | ||
| 1085 | enum led_brightness brightness) | ||
| 1086 | { | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | static int wacom_led_register_one(struct device *dev, struct wacom *wacom, | ||
| 1090 | struct wacom_led *led, unsigned int group, | ||
| 1091 | unsigned int id, bool read_only) | ||
| 1092 | { | ||
| 1093 | int error; | ||
| 1094 | char *name; | ||
| 1095 | |||
| 1096 | name = devm_kasprintf(dev, GFP_KERNEL, | ||
| 1097 | "%s::wacom-%d.%d", | ||
| 1098 | dev_name(dev), | ||
| 1099 | group, | ||
| 1100 | id); | ||
| 1101 | if (!name) | ||
| 1102 | return -ENOMEM; | ||
| 1103 | |||
| 1104 | if (!read_only) { | ||
| 1105 | led->trigger.name = name; | ||
| 1106 | error = devm_led_trigger_register(dev, &led->trigger); | ||
| 1107 | if (error) { | ||
| 1108 | hid_err(wacom->hdev, | ||
| 1109 | "failed to register LED trigger %s: %d\n", | ||
| 1110 | led->cdev.name, error); | ||
| 1111 | return error; | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | led->group = group; | ||
| 1116 | led->id = id; | ||
| 1117 | led->wacom = wacom; | ||
| 1118 | led->llv = wacom->led.llv; | ||
| 1119 | led->hlv = wacom->led.hlv; | ||
| 1120 | led->cdev.name = name; | ||
| 1121 | led->cdev.max_brightness = LED_FULL; | ||
| 1122 | led->cdev.flags = LED_HW_PLUGGABLE; | ||
| 1123 | led->cdev.brightness_get = __wacom_led_brightness_get; | ||
| 1124 | if (!read_only) { | ||
| 1125 | led->cdev.brightness_set_blocking = wacom_led_brightness_set; | ||
| 1126 | led->cdev.default_trigger = led->cdev.name; | ||
| 1127 | } else { | ||
| 1128 | led->cdev.brightness_set = wacom_led_readonly_brightness_set; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | error = devm_led_classdev_register(dev, &led->cdev); | ||
| 1132 | if (error) { | ||
| 1133 | hid_err(wacom->hdev, | ||
| 1134 | "failed to register LED %s: %d\n", | ||
| 1135 | led->cdev.name, error); | ||
| 1136 | led->cdev.name = NULL; | ||
| 1137 | return error; | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | return 0; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | static void wacom_led_groups_release_one(void *data) | ||
| 1144 | { | ||
| 1145 | struct wacom_group_leds *group = data; | ||
| 1146 | |||
| 1147 | devres_release_group(group->dev, group); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | static int wacom_led_groups_alloc_and_register_one(struct device *dev, | ||
| 1151 | struct wacom *wacom, | ||
| 1152 | int group_id, int count, | ||
| 1153 | bool read_only) | ||
| 1154 | { | ||
| 1155 | struct wacom_led *leds; | ||
| 1156 | int i, error; | ||
| 1157 | |||
| 1158 | if (group_id >= wacom->led.count || count <= 0) | ||
| 1159 | return -EINVAL; | ||
| 1160 | |||
| 1161 | if (!devres_open_group(dev, &wacom->led.groups[group_id], GFP_KERNEL)) | ||
| 1162 | return -ENOMEM; | ||
| 1163 | |||
| 1164 | leds = devm_kzalloc(dev, sizeof(struct wacom_led) * count, GFP_KERNEL); | ||
| 1165 | if (!leds) { | ||
| 1166 | error = -ENOMEM; | ||
| 1167 | goto err; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | wacom->led.groups[group_id].leds = leds; | ||
| 1171 | wacom->led.groups[group_id].count = count; | ||
| 1172 | |||
| 1173 | for (i = 0; i < count; i++) { | ||
| 1174 | error = wacom_led_register_one(dev, wacom, &leds[i], | ||
| 1175 | group_id, i, read_only); | ||
| 1176 | if (error) | ||
| 1177 | goto err; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | wacom->led.groups[group_id].dev = dev; | ||
| 1181 | |||
| 1182 | devres_close_group(dev, &wacom->led.groups[group_id]); | ||
| 1183 | |||
| 1184 | /* | ||
| 1185 | * There is a bug (?) in devm_led_classdev_register() in which its | ||
| 1186 | * increments the refcount of the parent. If the parent is an input | ||
| 1187 | * device, that means the ref count never reaches 0 when | ||
| 1188 | * devm_input_device_release() gets called. | ||
| 1189 | * This means that the LEDs are still there after disconnect. | ||
| 1190 | * Manually force the release of the group so that the leds are released | ||
| 1191 | * once we are done using them. | ||
| 1192 | */ | ||
| 1193 | error = devm_add_action_or_reset(&wacom->hdev->dev, | ||
| 1194 | wacom_led_groups_release_one, | ||
| 1195 | &wacom->led.groups[group_id]); | ||
| 1196 | if (error) | ||
| 1197 | return error; | ||
| 1198 | |||
| 1199 | return 0; | ||
| 1200 | |||
| 1201 | err: | ||
| 1202 | devres_release_group(dev, &wacom->led.groups[group_id]); | ||
| 1203 | return error; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group_id, | ||
| 1207 | unsigned int id) | ||
| 1208 | { | ||
| 1209 | struct wacom_group_leds *group; | ||
| 1210 | |||
| 1211 | if (group_id >= wacom->led.count) | ||
| 1212 | return NULL; | ||
| 1213 | |||
| 1214 | group = &wacom->led.groups[group_id]; | ||
| 1215 | |||
| 1216 | if (!group->leds) | ||
| 1217 | return NULL; | ||
| 1218 | |||
| 1219 | id %= group->count; | ||
| 1220 | |||
| 1221 | return &group->leds[id]; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | /** | ||
| 1225 | * wacom_led_next: gives the next available led with a wacom trigger. | ||
| 1226 | * | ||
| 1227 | * returns the next available struct wacom_led which has its default trigger | ||
| 1228 | * or the current one if none is available. | ||
| 1229 | */ | ||
| 1230 | struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur) | ||
| 1231 | { | ||
| 1232 | struct wacom_led *next_led; | ||
| 1233 | int group, next; | ||
| 1234 | |||
| 1235 | if (!wacom || !cur) | ||
| 1236 | return NULL; | ||
| 1237 | |||
| 1238 | group = cur->group; | ||
| 1239 | next = cur->id; | ||
| 1240 | |||
| 1241 | do { | ||
| 1242 | next_led = wacom_led_find(wacom, group, ++next); | ||
| 1243 | if (!next_led || next_led == cur) | ||
| 1244 | return next_led; | ||
| 1245 | } while (next_led->cdev.trigger != &next_led->trigger); | ||
| 1246 | |||
| 1247 | return next_led; | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | static void wacom_led_groups_release(void *data) | ||
| 1251 | { | ||
| 1252 | struct wacom *wacom = data; | ||
| 1253 | |||
| 1254 | wacom->led.groups = NULL; | ||
| 1255 | wacom->led.count = 0; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | static int wacom_led_groups_allocate(struct wacom *wacom, int count) | ||
| 1259 | { | ||
| 1260 | struct device *dev = &wacom->hdev->dev; | ||
| 1261 | struct wacom_group_leds *groups; | ||
| 1262 | int error; | ||
| 1263 | |||
| 1264 | groups = devm_kzalloc(dev, sizeof(struct wacom_group_leds) * count, | ||
| 1265 | GFP_KERNEL); | ||
| 1266 | if (!groups) | ||
| 1267 | return -ENOMEM; | ||
| 1268 | |||
| 1269 | error = devm_add_action_or_reset(dev, wacom_led_groups_release, wacom); | ||
| 1270 | if (error) | ||
| 1271 | return error; | ||
| 1272 | |||
| 1273 | wacom->led.groups = groups; | ||
| 1274 | wacom->led.count = count; | ||
| 1275 | |||
| 1276 | return 0; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | static int wacom_leds_alloc_and_register(struct wacom *wacom, int group_count, | ||
| 1280 | int led_per_group, bool read_only) | ||
| 1281 | { | ||
| 1282 | struct device *dev; | ||
| 1283 | int i, error; | ||
| 1284 | |||
| 1285 | if (!wacom->wacom_wac.pad_input) | ||
| 1286 | return -EINVAL; | ||
| 1287 | |||
| 1288 | dev = &wacom->wacom_wac.pad_input->dev; | ||
| 1289 | |||
| 1290 | error = wacom_led_groups_allocate(wacom, group_count); | ||
| 1291 | if (error) | ||
| 1292 | return error; | ||
| 1293 | |||
| 1294 | for (i = 0; i < group_count; i++) { | ||
| 1295 | error = wacom_led_groups_alloc_and_register_one(dev, wacom, i, | ||
| 1296 | led_per_group, | ||
| 1297 | read_only); | ||
| 1298 | if (error) | ||
| 1299 | return error; | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | return 0; | ||
| 1303 | } | ||
| 1304 | |||
| 907 | static int wacom_initialize_leds(struct wacom *wacom) | 1305 | static int wacom_initialize_leds(struct wacom *wacom) |
| 908 | { | 1306 | { |
| 909 | int error; | 1307 | int error; |
| @@ -917,25 +1315,38 @@ static int wacom_initialize_leds(struct wacom *wacom) | |||
| 917 | case INTUOS4: | 1315 | case INTUOS4: |
| 918 | case INTUOS4WL: | 1316 | case INTUOS4WL: |
| 919 | case INTUOS4L: | 1317 | case INTUOS4L: |
| 920 | wacom->led.select[0] = 0; | ||
| 921 | wacom->led.select[1] = 0; | ||
| 922 | wacom->led.llv = 10; | 1318 | wacom->led.llv = 10; |
| 923 | wacom->led.hlv = 20; | 1319 | wacom->led.hlv = 20; |
| 1320 | wacom->led.max_llv = 127; | ||
| 1321 | wacom->led.max_hlv = 127; | ||
| 924 | wacom->led.img_lum = 10; | 1322 | wacom->led.img_lum = 10; |
| 925 | error = sysfs_create_group(&wacom->hdev->dev.kobj, | 1323 | |
| 926 | &intuos4_led_attr_group); | 1324 | error = wacom_leds_alloc_and_register(wacom, 1, 4, false); |
| 1325 | if (error) { | ||
| 1326 | hid_err(wacom->hdev, | ||
| 1327 | "cannot create leds err: %d\n", error); | ||
| 1328 | return error; | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | error = wacom_devm_sysfs_create_group(wacom, | ||
| 1332 | &intuos4_led_attr_group); | ||
| 927 | break; | 1333 | break; |
| 928 | 1334 | ||
| 929 | case WACOM_24HD: | 1335 | case WACOM_24HD: |
| 930 | case WACOM_21UX2: | 1336 | case WACOM_21UX2: |
| 931 | wacom->led.select[0] = 0; | ||
| 932 | wacom->led.select[1] = 0; | ||
| 933 | wacom->led.llv = 0; | 1337 | wacom->led.llv = 0; |
| 934 | wacom->led.hlv = 0; | 1338 | wacom->led.hlv = 0; |
| 935 | wacom->led.img_lum = 0; | 1339 | wacom->led.img_lum = 0; |
| 936 | 1340 | ||
| 937 | error = sysfs_create_group(&wacom->hdev->dev.kobj, | 1341 | error = wacom_leds_alloc_and_register(wacom, 2, 4, false); |
| 938 | &cintiq_led_attr_group); | 1342 | if (error) { |
| 1343 | hid_err(wacom->hdev, | ||
| 1344 | "cannot create leds err: %d\n", error); | ||
| 1345 | return error; | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | error = wacom_devm_sysfs_create_group(wacom, | ||
| 1349 | &cintiq_led_attr_group); | ||
| 939 | break; | 1350 | break; |
| 940 | 1351 | ||
| 941 | case INTUOS5S: | 1352 | case INTUOS5S: |
| @@ -944,16 +1355,31 @@ static int wacom_initialize_leds(struct wacom *wacom) | |||
| 944 | case INTUOSPS: | 1355 | case INTUOSPS: |
| 945 | case INTUOSPM: | 1356 | case INTUOSPM: |
| 946 | case INTUOSPL: | 1357 | case INTUOSPL: |
| 947 | wacom->led.select[0] = 0; | ||
| 948 | wacom->led.select[1] = 0; | ||
| 949 | wacom->led.llv = 32; | 1358 | wacom->led.llv = 32; |
| 950 | wacom->led.hlv = 0; | 1359 | wacom->led.max_llv = 96; |
| 951 | wacom->led.img_lum = 0; | 1360 | |
| 1361 | error = wacom_leds_alloc_and_register(wacom, 1, 4, false); | ||
| 1362 | if (error) { | ||
| 1363 | hid_err(wacom->hdev, | ||
| 1364 | "cannot create leds err: %d\n", error); | ||
| 1365 | return error; | ||
| 1366 | } | ||
| 952 | 1367 | ||
| 953 | error = sysfs_create_group(&wacom->hdev->dev.kobj, | 1368 | error = wacom_devm_sysfs_create_group(wacom, |
| 954 | &intuos5_led_attr_group); | 1369 | &intuos5_led_attr_group); |
| 955 | break; | 1370 | break; |
| 956 | 1371 | ||
| 1372 | case REMOTE: | ||
| 1373 | wacom->led.llv = 255; | ||
| 1374 | wacom->led.max_llv = 255; | ||
| 1375 | error = wacom_led_groups_allocate(wacom, 5); | ||
| 1376 | if (error) { | ||
| 1377 | hid_err(wacom->hdev, | ||
| 1378 | "cannot create leds err: %d\n", error); | ||
| 1379 | return error; | ||
| 1380 | } | ||
| 1381 | return 0; | ||
| 1382 | |||
| 957 | default: | 1383 | default: |
| 958 | return 0; | 1384 | return 0; |
| 959 | } | 1385 | } |
| @@ -964,86 +1390,45 @@ static int wacom_initialize_leds(struct wacom *wacom) | |||
| 964 | return error; | 1390 | return error; |
| 965 | } | 1391 | } |
| 966 | wacom_led_control(wacom); | 1392 | wacom_led_control(wacom); |
| 967 | wacom->led_initialized = true; | ||
| 968 | 1393 | ||
| 969 | return 0; | 1394 | return 0; |
| 970 | } | 1395 | } |
| 971 | 1396 | ||
| 972 | static void wacom_destroy_leds(struct wacom *wacom) | ||
| 973 | { | ||
| 974 | if (!wacom->led_initialized) | ||
| 975 | return; | ||
| 976 | |||
| 977 | if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD)) | ||
| 978 | return; | ||
| 979 | |||
| 980 | wacom->led_initialized = false; | ||
| 981 | |||
| 982 | switch (wacom->wacom_wac.features.type) { | ||
| 983 | case INTUOS4S: | ||
| 984 | case INTUOS4: | ||
| 985 | case INTUOS4WL: | ||
| 986 | case INTUOS4L: | ||
| 987 | sysfs_remove_group(&wacom->hdev->dev.kobj, | ||
| 988 | &intuos4_led_attr_group); | ||
| 989 | break; | ||
| 990 | |||
| 991 | case WACOM_24HD: | ||
| 992 | case WACOM_21UX2: | ||
| 993 | sysfs_remove_group(&wacom->hdev->dev.kobj, | ||
| 994 | &cintiq_led_attr_group); | ||
| 995 | break; | ||
| 996 | |||
| 997 | case INTUOS5S: | ||
| 998 | case INTUOS5: | ||
| 999 | case INTUOS5L: | ||
| 1000 | case INTUOSPS: | ||
| 1001 | case INTUOSPM: | ||
| 1002 | case INTUOSPL: | ||
| 1003 | sysfs_remove_group(&wacom->hdev->dev.kobj, | ||
| 1004 | &intuos5_led_attr_group); | ||
| 1005 | break; | ||
| 1006 | } | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | static enum power_supply_property wacom_battery_props[] = { | 1397 | static enum power_supply_property wacom_battery_props[] = { |
| 1398 | POWER_SUPPLY_PROP_MODEL_NAME, | ||
| 1010 | POWER_SUPPLY_PROP_PRESENT, | 1399 | POWER_SUPPLY_PROP_PRESENT, |
| 1011 | POWER_SUPPLY_PROP_STATUS, | 1400 | POWER_SUPPLY_PROP_STATUS, |
| 1012 | POWER_SUPPLY_PROP_SCOPE, | 1401 | POWER_SUPPLY_PROP_SCOPE, |
| 1013 | POWER_SUPPLY_PROP_CAPACITY | 1402 | POWER_SUPPLY_PROP_CAPACITY |
| 1014 | }; | 1403 | }; |
| 1015 | 1404 | ||
| 1016 | static enum power_supply_property wacom_ac_props[] = { | ||
| 1017 | POWER_SUPPLY_PROP_PRESENT, | ||
| 1018 | POWER_SUPPLY_PROP_ONLINE, | ||
| 1019 | POWER_SUPPLY_PROP_SCOPE, | ||
| 1020 | }; | ||
| 1021 | |||
| 1022 | static int wacom_battery_get_property(struct power_supply *psy, | 1405 | static int wacom_battery_get_property(struct power_supply *psy, |
| 1023 | enum power_supply_property psp, | 1406 | enum power_supply_property psp, |
| 1024 | union power_supply_propval *val) | 1407 | union power_supply_propval *val) |
| 1025 | { | 1408 | { |
| 1026 | struct wacom *wacom = power_supply_get_drvdata(psy); | 1409 | struct wacom_battery *battery = power_supply_get_drvdata(psy); |
| 1027 | int ret = 0; | 1410 | int ret = 0; |
| 1028 | 1411 | ||
| 1029 | switch (psp) { | 1412 | switch (psp) { |
| 1413 | case POWER_SUPPLY_PROP_MODEL_NAME: | ||
| 1414 | val->strval = battery->wacom->wacom_wac.name; | ||
| 1415 | break; | ||
| 1030 | case POWER_SUPPLY_PROP_PRESENT: | 1416 | case POWER_SUPPLY_PROP_PRESENT: |
| 1031 | val->intval = wacom->wacom_wac.bat_connected; | 1417 | val->intval = battery->bat_connected; |
| 1032 | break; | 1418 | break; |
| 1033 | case POWER_SUPPLY_PROP_SCOPE: | 1419 | case POWER_SUPPLY_PROP_SCOPE: |
| 1034 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; | 1420 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; |
| 1035 | break; | 1421 | break; |
| 1036 | case POWER_SUPPLY_PROP_CAPACITY: | 1422 | case POWER_SUPPLY_PROP_CAPACITY: |
| 1037 | val->intval = | 1423 | val->intval = battery->battery_capacity; |
| 1038 | wacom->wacom_wac.battery_capacity; | ||
| 1039 | break; | 1424 | break; |
| 1040 | case POWER_SUPPLY_PROP_STATUS: | 1425 | case POWER_SUPPLY_PROP_STATUS: |
| 1041 | if (wacom->wacom_wac.bat_charging) | 1426 | if (battery->bat_charging) |
| 1042 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | 1427 | val->intval = POWER_SUPPLY_STATUS_CHARGING; |
| 1043 | else if (wacom->wacom_wac.battery_capacity == 100 && | 1428 | else if (battery->battery_capacity == 100 && |
| 1044 | wacom->wacom_wac.ps_connected) | 1429 | battery->ps_connected) |
| 1045 | val->intval = POWER_SUPPLY_STATUS_FULL; | 1430 | val->intval = POWER_SUPPLY_STATUS_FULL; |
| 1046 | else if (wacom->wacom_wac.ps_connected) | 1431 | else if (battery->ps_connected) |
| 1047 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | 1432 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
| 1048 | else | 1433 | else |
| 1049 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 1434 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
| @@ -1056,84 +1441,64 @@ static int wacom_battery_get_property(struct power_supply *psy, | |||
| 1056 | return ret; | 1441 | return ret; |
| 1057 | } | 1442 | } |
| 1058 | 1443 | ||
| 1059 | static int wacom_ac_get_property(struct power_supply *psy, | 1444 | static int __wacom_initialize_battery(struct wacom *wacom, |
| 1060 | enum power_supply_property psp, | 1445 | struct wacom_battery *battery) |
| 1061 | union power_supply_propval *val) | ||
| 1062 | { | 1446 | { |
| 1063 | struct wacom *wacom = power_supply_get_drvdata(psy); | 1447 | static atomic_t battery_no = ATOMIC_INIT(0); |
| 1064 | int ret = 0; | 1448 | struct device *dev = &wacom->hdev->dev; |
| 1449 | struct power_supply_config psy_cfg = { .drv_data = battery, }; | ||
| 1450 | struct power_supply *ps_bat; | ||
| 1451 | struct power_supply_desc *bat_desc = &battery->bat_desc; | ||
| 1452 | unsigned long n; | ||
| 1453 | int error; | ||
| 1065 | 1454 | ||
| 1066 | switch (psp) { | 1455 | if (!devres_open_group(dev, bat_desc, GFP_KERNEL)) |
| 1067 | case POWER_SUPPLY_PROP_PRESENT: | 1456 | return -ENOMEM; |
| 1068 | /* fall through */ | 1457 | |
| 1069 | case POWER_SUPPLY_PROP_ONLINE: | 1458 | battery->wacom = wacom; |
| 1070 | val->intval = wacom->wacom_wac.ps_connected; | 1459 | |
| 1071 | break; | 1460 | n = atomic_inc_return(&battery_no) - 1; |
| 1072 | case POWER_SUPPLY_PROP_SCOPE: | 1461 | |
| 1073 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; | 1462 | bat_desc->properties = wacom_battery_props; |
| 1074 | break; | 1463 | bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props); |
| 1075 | default: | 1464 | bat_desc->get_property = wacom_battery_get_property; |
| 1076 | ret = -EINVAL; | 1465 | sprintf(battery->bat_name, "wacom_battery_%ld", n); |
| 1077 | break; | 1466 | bat_desc->name = battery->bat_name; |
| 1467 | bat_desc->type = POWER_SUPPLY_TYPE_USB; | ||
| 1468 | bat_desc->use_for_apm = 0; | ||
| 1469 | |||
| 1470 | ps_bat = devm_power_supply_register(dev, bat_desc, &psy_cfg); | ||
| 1471 | if (IS_ERR(ps_bat)) { | ||
| 1472 | error = PTR_ERR(ps_bat); | ||
| 1473 | goto err; | ||
| 1078 | } | 1474 | } |
| 1079 | return ret; | 1475 | |
| 1476 | power_supply_powers(ps_bat, &wacom->hdev->dev); | ||
| 1477 | |||
| 1478 | battery->battery = ps_bat; | ||
| 1479 | |||
| 1480 | devres_close_group(dev, bat_desc); | ||
| 1481 | return 0; | ||
| 1482 | |||
| 1483 | err: | ||
| 1484 | devres_release_group(dev, bat_desc); | ||
| 1485 | return error; | ||
| 1080 | } | 1486 | } |
| 1081 | 1487 | ||
| 1082 | static int wacom_initialize_battery(struct wacom *wacom) | 1488 | static int wacom_initialize_battery(struct wacom *wacom) |
| 1083 | { | 1489 | { |
| 1084 | static atomic_t battery_no = ATOMIC_INIT(0); | 1490 | if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) |
| 1085 | struct power_supply_config psy_cfg = { .drv_data = wacom, }; | 1491 | return __wacom_initialize_battery(wacom, &wacom->battery); |
| 1086 | unsigned long n; | ||
| 1087 | |||
| 1088 | if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) { | ||
| 1089 | struct power_supply_desc *bat_desc = &wacom->battery_desc; | ||
| 1090 | struct power_supply_desc *ac_desc = &wacom->ac_desc; | ||
| 1091 | n = atomic_inc_return(&battery_no) - 1; | ||
| 1092 | |||
| 1093 | bat_desc->properties = wacom_battery_props; | ||
| 1094 | bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props); | ||
| 1095 | bat_desc->get_property = wacom_battery_get_property; | ||
| 1096 | sprintf(wacom->wacom_wac.bat_name, "wacom_battery_%ld", n); | ||
| 1097 | bat_desc->name = wacom->wacom_wac.bat_name; | ||
| 1098 | bat_desc->type = POWER_SUPPLY_TYPE_BATTERY; | ||
| 1099 | bat_desc->use_for_apm = 0; | ||
| 1100 | |||
| 1101 | ac_desc->properties = wacom_ac_props; | ||
| 1102 | ac_desc->num_properties = ARRAY_SIZE(wacom_ac_props); | ||
| 1103 | ac_desc->get_property = wacom_ac_get_property; | ||
| 1104 | sprintf(wacom->wacom_wac.ac_name, "wacom_ac_%ld", n); | ||
| 1105 | ac_desc->name = wacom->wacom_wac.ac_name; | ||
| 1106 | ac_desc->type = POWER_SUPPLY_TYPE_MAINS; | ||
| 1107 | ac_desc->use_for_apm = 0; | ||
| 1108 | |||
| 1109 | wacom->battery = power_supply_register(&wacom->hdev->dev, | ||
| 1110 | &wacom->battery_desc, &psy_cfg); | ||
| 1111 | if (IS_ERR(wacom->battery)) | ||
| 1112 | return PTR_ERR(wacom->battery); | ||
| 1113 | |||
| 1114 | power_supply_powers(wacom->battery, &wacom->hdev->dev); | ||
| 1115 | |||
| 1116 | wacom->ac = power_supply_register(&wacom->hdev->dev, | ||
| 1117 | &wacom->ac_desc, | ||
| 1118 | &psy_cfg); | ||
| 1119 | if (IS_ERR(wacom->ac)) { | ||
| 1120 | power_supply_unregister(wacom->battery); | ||
| 1121 | return PTR_ERR(wacom->ac); | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | power_supply_powers(wacom->ac, &wacom->hdev->dev); | ||
| 1125 | } | ||
| 1126 | 1492 | ||
| 1127 | return 0; | 1493 | return 0; |
| 1128 | } | 1494 | } |
| 1129 | 1495 | ||
| 1130 | static void wacom_destroy_battery(struct wacom *wacom) | 1496 | static void wacom_destroy_battery(struct wacom *wacom) |
| 1131 | { | 1497 | { |
| 1132 | if (wacom->battery) { | 1498 | if (wacom->battery.battery) { |
| 1133 | power_supply_unregister(wacom->battery); | 1499 | devres_release_group(&wacom->hdev->dev, |
| 1134 | wacom->battery = NULL; | 1500 | &wacom->battery.bat_desc); |
| 1135 | power_supply_unregister(wacom->ac); | 1501 | wacom->battery.battery = NULL; |
| 1136 | wacom->ac = NULL; | ||
| 1137 | } | 1502 | } |
| 1138 | } | 1503 | } |
| 1139 | 1504 | ||
| @@ -1179,7 +1544,7 @@ static ssize_t wacom_show_remote_mode(struct kobject *kobj, | |||
| 1179 | struct wacom *wacom = hid_get_drvdata(hdev); | 1544 | struct wacom *wacom = hid_get_drvdata(hdev); |
| 1180 | u8 mode; | 1545 | u8 mode; |
| 1181 | 1546 | ||
| 1182 | mode = wacom->led.select[index]; | 1547 | mode = wacom->led.groups[index].select; |
| 1183 | if (mode >= 0 && mode < 3) | 1548 | if (mode >= 0 && mode < 3) |
| 1184 | return snprintf(buf, PAGE_SIZE, "%d\n", mode); | 1549 | return snprintf(buf, PAGE_SIZE, "%d\n", mode); |
| 1185 | else | 1550 | else |
| @@ -1212,54 +1577,30 @@ DEVICE_EKR_ATTR_GROUP(2); | |||
| 1212 | DEVICE_EKR_ATTR_GROUP(3); | 1577 | DEVICE_EKR_ATTR_GROUP(3); |
| 1213 | DEVICE_EKR_ATTR_GROUP(4); | 1578 | DEVICE_EKR_ATTR_GROUP(4); |
| 1214 | 1579 | ||
| 1215 | int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, int index) | 1580 | static int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, |
| 1581 | int index) | ||
| 1216 | { | 1582 | { |
| 1217 | int error = 0; | 1583 | int error = 0; |
| 1218 | char *buf; | 1584 | struct wacom_remote *remote = wacom->remote; |
| 1219 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | ||
| 1220 | 1585 | ||
| 1221 | wacom_wac->serial[index] = serial; | 1586 | remote->remotes[index].group.name = devm_kasprintf(&wacom->hdev->dev, |
| 1222 | 1587 | GFP_KERNEL, | |
| 1223 | buf = kzalloc(WAC_REMOTE_SERIAL_MAX_STRLEN, GFP_KERNEL); | 1588 | "%d", serial); |
| 1224 | if (!buf) | 1589 | if (!remote->remotes[index].group.name) |
| 1225 | return -ENOMEM; | 1590 | return -ENOMEM; |
| 1226 | snprintf(buf, WAC_REMOTE_SERIAL_MAX_STRLEN, "%d", serial); | ||
| 1227 | wacom->remote_group[index].name = buf; | ||
| 1228 | 1591 | ||
| 1229 | error = sysfs_create_group(wacom->remote_dir, | 1592 | error = __wacom_devm_sysfs_create_group(wacom, remote->remote_dir, |
| 1230 | &wacom->remote_group[index]); | 1593 | &remote->remotes[index].group); |
| 1231 | if (error) { | 1594 | if (error) { |
| 1595 | remote->remotes[index].group.name = NULL; | ||
| 1232 | hid_err(wacom->hdev, | 1596 | hid_err(wacom->hdev, |
| 1233 | "cannot create sysfs group err: %d\n", error); | 1597 | "cannot create sysfs group err: %d\n", error); |
| 1234 | kobject_put(wacom->remote_dir); | ||
| 1235 | return error; | 1598 | return error; |
| 1236 | } | 1599 | } |
| 1237 | 1600 | ||
| 1238 | return 0; | 1601 | return 0; |
| 1239 | } | 1602 | } |
| 1240 | 1603 | ||
| 1241 | void wacom_remote_destroy_attr_group(struct wacom *wacom, __u32 serial) | ||
| 1242 | { | ||
| 1243 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | ||
| 1244 | int i; | ||
| 1245 | |||
| 1246 | if (!serial) | ||
| 1247 | return; | ||
| 1248 | |||
| 1249 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { | ||
| 1250 | if (wacom_wac->serial[i] == serial) { | ||
| 1251 | wacom_wac->serial[i] = 0; | ||
| 1252 | wacom->led.select[i] = WACOM_STATUS_UNKNOWN; | ||
| 1253 | if (wacom->remote_group[i].name) { | ||
| 1254 | sysfs_remove_group(wacom->remote_dir, | ||
| 1255 | &wacom->remote_group[i]); | ||
| 1256 | kfree(wacom->remote_group[i].name); | ||
| 1257 | wacom->remote_group[i].name = NULL; | ||
| 1258 | } | ||
| 1259 | } | ||
| 1260 | } | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | static int wacom_cmd_unpair_remote(struct wacom *wacom, unsigned char selector) | 1604 | static int wacom_cmd_unpair_remote(struct wacom *wacom, unsigned char selector) |
| 1264 | { | 1605 | { |
| 1265 | const size_t buf_size = 2; | 1606 | const size_t buf_size = 2; |
| @@ -1316,27 +1657,57 @@ static const struct attribute *remote_unpair_attrs[] = { | |||
| 1316 | NULL | 1657 | NULL |
| 1317 | }; | 1658 | }; |
| 1318 | 1659 | ||
| 1319 | static int wacom_initialize_remote(struct wacom *wacom) | 1660 | static void wacom_remotes_destroy(void *data) |
| 1661 | { | ||
| 1662 | struct wacom *wacom = data; | ||
| 1663 | struct wacom_remote *remote = wacom->remote; | ||
| 1664 | |||
| 1665 | if (!remote) | ||
| 1666 | return; | ||
| 1667 | |||
| 1668 | kobject_put(remote->remote_dir); | ||
| 1669 | kfifo_free(&remote->remote_fifo); | ||
| 1670 | wacom->remote = NULL; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | static int wacom_initialize_remotes(struct wacom *wacom) | ||
| 1320 | { | 1674 | { |
| 1321 | int error = 0; | 1675 | int error = 0; |
| 1322 | struct wacom_wac *wacom_wac = &(wacom->wacom_wac); | 1676 | struct wacom_remote *remote; |
| 1323 | int i; | 1677 | int i; |
| 1324 | 1678 | ||
| 1325 | if (wacom->wacom_wac.features.type != REMOTE) | 1679 | if (wacom->wacom_wac.features.type != REMOTE) |
| 1326 | return 0; | 1680 | return 0; |
| 1327 | 1681 | ||
| 1328 | wacom->remote_group[0] = remote0_serial_group; | 1682 | remote = devm_kzalloc(&wacom->hdev->dev, sizeof(*wacom->remote), |
| 1329 | wacom->remote_group[1] = remote1_serial_group; | 1683 | GFP_KERNEL); |
| 1330 | wacom->remote_group[2] = remote2_serial_group; | 1684 | if (!remote) |
| 1331 | wacom->remote_group[3] = remote3_serial_group; | 1685 | return -ENOMEM; |
| 1332 | wacom->remote_group[4] = remote4_serial_group; | 1686 | |
| 1687 | wacom->remote = remote; | ||
| 1333 | 1688 | ||
| 1334 | wacom->remote_dir = kobject_create_and_add("wacom_remote", | 1689 | spin_lock_init(&remote->remote_lock); |
| 1335 | &wacom->hdev->dev.kobj); | 1690 | |
| 1336 | if (!wacom->remote_dir) | 1691 | error = kfifo_alloc(&remote->remote_fifo, |
| 1692 | 5 * sizeof(struct wacom_remote_data), | ||
| 1693 | GFP_KERNEL); | ||
| 1694 | if (error) { | ||
| 1695 | hid_err(wacom->hdev, "failed allocating remote_fifo\n"); | ||
| 1337 | return -ENOMEM; | 1696 | return -ENOMEM; |
| 1697 | } | ||
| 1338 | 1698 | ||
| 1339 | error = sysfs_create_files(wacom->remote_dir, remote_unpair_attrs); | 1699 | remote->remotes[0].group = remote0_serial_group; |
| 1700 | remote->remotes[1].group = remote1_serial_group; | ||
| 1701 | remote->remotes[2].group = remote2_serial_group; | ||
| 1702 | remote->remotes[3].group = remote3_serial_group; | ||
| 1703 | remote->remotes[4].group = remote4_serial_group; | ||
| 1704 | |||
| 1705 | remote->remote_dir = kobject_create_and_add("wacom_remote", | ||
| 1706 | &wacom->hdev->dev.kobj); | ||
| 1707 | if (!remote->remote_dir) | ||
| 1708 | return -ENOMEM; | ||
| 1709 | |||
| 1710 | error = sysfs_create_files(remote->remote_dir, remote_unpair_attrs); | ||
| 1340 | 1711 | ||
| 1341 | if (error) { | 1712 | if (error) { |
| 1342 | hid_err(wacom->hdev, | 1713 | hid_err(wacom->hdev, |
| @@ -1345,10 +1716,15 @@ static int wacom_initialize_remote(struct wacom *wacom) | |||
| 1345 | } | 1716 | } |
| 1346 | 1717 | ||
| 1347 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { | 1718 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { |
| 1348 | wacom->led.select[i] = WACOM_STATUS_UNKNOWN; | 1719 | wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN; |
| 1349 | wacom_wac->serial[i] = 0; | 1720 | remote->remotes[i].serial = 0; |
| 1350 | } | 1721 | } |
| 1351 | 1722 | ||
| 1723 | error = devm_add_action_or_reset(&wacom->hdev->dev, | ||
| 1724 | wacom_remotes_destroy, wacom); | ||
| 1725 | if (error) | ||
| 1726 | return error; | ||
| 1727 | |||
| 1352 | return 0; | 1728 | return 0; |
| 1353 | } | 1729 | } |
| 1354 | 1730 | ||
| @@ -1358,7 +1734,7 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom) | |||
| 1358 | struct hid_device *hdev = wacom->hdev; | 1734 | struct hid_device *hdev = wacom->hdev; |
| 1359 | struct wacom_wac *wacom_wac = &(wacom->wacom_wac); | 1735 | struct wacom_wac *wacom_wac = &(wacom->wacom_wac); |
| 1360 | 1736 | ||
| 1361 | input_dev = input_allocate_device(); | 1737 | input_dev = devm_input_allocate_device(&hdev->dev); |
| 1362 | if (!input_dev) | 1738 | if (!input_dev) |
| 1363 | return NULL; | 1739 | return NULL; |
| 1364 | 1740 | ||
| @@ -1377,36 +1753,6 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom) | |||
| 1377 | return input_dev; | 1753 | return input_dev; |
| 1378 | } | 1754 | } |
| 1379 | 1755 | ||
| 1380 | static void wacom_clean_inputs(struct wacom *wacom) | ||
| 1381 | { | ||
| 1382 | if (wacom->wacom_wac.pen_input) { | ||
| 1383 | if (wacom->wacom_wac.pen_registered) | ||
| 1384 | input_unregister_device(wacom->wacom_wac.pen_input); | ||
| 1385 | else | ||
| 1386 | input_free_device(wacom->wacom_wac.pen_input); | ||
| 1387 | } | ||
| 1388 | if (wacom->wacom_wac.touch_input) { | ||
| 1389 | if (wacom->wacom_wac.touch_registered) | ||
| 1390 | input_unregister_device(wacom->wacom_wac.touch_input); | ||
| 1391 | else | ||
| 1392 | input_free_device(wacom->wacom_wac.touch_input); | ||
| 1393 | } | ||
| 1394 | if (wacom->wacom_wac.pad_input) { | ||
| 1395 | if (wacom->wacom_wac.pad_registered) | ||
| 1396 | input_unregister_device(wacom->wacom_wac.pad_input); | ||
| 1397 | else | ||
| 1398 | input_free_device(wacom->wacom_wac.pad_input); | ||
| 1399 | } | ||
| 1400 | kobject_put(wacom->remote_dir); | ||
| 1401 | wacom->wacom_wac.pen_input = NULL; | ||
| 1402 | wacom->wacom_wac.touch_input = NULL; | ||
| 1403 | wacom->wacom_wac.pad_input = NULL; | ||
| 1404 | wacom->wacom_wac.pen_registered = false; | ||
| 1405 | wacom->wacom_wac.touch_registered = false; | ||
| 1406 | wacom->wacom_wac.pad_registered = false; | ||
| 1407 | wacom_destroy_leds(wacom); | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | static int wacom_allocate_inputs(struct wacom *wacom) | 1756 | static int wacom_allocate_inputs(struct wacom *wacom) |
| 1411 | { | 1757 | { |
| 1412 | struct wacom_wac *wacom_wac = &(wacom->wacom_wac); | 1758 | struct wacom_wac *wacom_wac = &(wacom->wacom_wac); |
| @@ -1414,10 +1760,10 @@ static int wacom_allocate_inputs(struct wacom *wacom) | |||
| 1414 | wacom_wac->pen_input = wacom_allocate_input(wacom); | 1760 | wacom_wac->pen_input = wacom_allocate_input(wacom); |
| 1415 | wacom_wac->touch_input = wacom_allocate_input(wacom); | 1761 | wacom_wac->touch_input = wacom_allocate_input(wacom); |
| 1416 | wacom_wac->pad_input = wacom_allocate_input(wacom); | 1762 | wacom_wac->pad_input = wacom_allocate_input(wacom); |
| 1417 | if (!wacom_wac->pen_input || !wacom_wac->touch_input || !wacom_wac->pad_input) { | 1763 | if (!wacom_wac->pen_input || |
| 1418 | wacom_clean_inputs(wacom); | 1764 | !wacom_wac->touch_input || |
| 1765 | !wacom_wac->pad_input) | ||
| 1419 | return -ENOMEM; | 1766 | return -ENOMEM; |
| 1420 | } | ||
| 1421 | 1767 | ||
| 1422 | wacom_wac->pen_input->name = wacom_wac->pen_name; | 1768 | wacom_wac->pen_input->name = wacom_wac->pen_name; |
| 1423 | wacom_wac->touch_input->name = wacom_wac->touch_name; | 1769 | wacom_wac->touch_input->name = wacom_wac->touch_name; |
| @@ -1448,8 +1794,7 @@ static int wacom_register_inputs(struct wacom *wacom) | |||
| 1448 | } else { | 1794 | } else { |
| 1449 | error = input_register_device(pen_input_dev); | 1795 | error = input_register_device(pen_input_dev); |
| 1450 | if (error) | 1796 | if (error) |
| 1451 | goto fail_register_pen_input; | 1797 | goto fail; |
| 1452 | wacom_wac->pen_registered = true; | ||
| 1453 | } | 1798 | } |
| 1454 | 1799 | ||
| 1455 | error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac); | 1800 | error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac); |
| @@ -1461,8 +1806,7 @@ static int wacom_register_inputs(struct wacom *wacom) | |||
| 1461 | } else { | 1806 | } else { |
| 1462 | error = input_register_device(touch_input_dev); | 1807 | error = input_register_device(touch_input_dev); |
| 1463 | if (error) | 1808 | if (error) |
| 1464 | goto fail_register_touch_input; | 1809 | goto fail; |
| 1465 | wacom_wac->touch_registered = true; | ||
| 1466 | } | 1810 | } |
| 1467 | 1811 | ||
| 1468 | error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); | 1812 | error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); |
| @@ -1474,37 +1818,15 @@ static int wacom_register_inputs(struct wacom *wacom) | |||
| 1474 | } else { | 1818 | } else { |
| 1475 | error = input_register_device(pad_input_dev); | 1819 | error = input_register_device(pad_input_dev); |
| 1476 | if (error) | 1820 | if (error) |
| 1477 | goto fail_register_pad_input; | 1821 | goto fail; |
| 1478 | wacom_wac->pad_registered = true; | ||
| 1479 | |||
| 1480 | error = wacom_initialize_leds(wacom); | ||
| 1481 | if (error) | ||
| 1482 | goto fail_leds; | ||
| 1483 | |||
| 1484 | error = wacom_initialize_remote(wacom); | ||
| 1485 | if (error) | ||
| 1486 | goto fail_remote; | ||
| 1487 | } | 1822 | } |
| 1488 | 1823 | ||
| 1489 | return 0; | 1824 | return 0; |
| 1490 | 1825 | ||
| 1491 | fail_remote: | 1826 | fail: |
| 1492 | wacom_destroy_leds(wacom); | 1827 | wacom_wac->pad_input = NULL; |
| 1493 | fail_leds: | ||
| 1494 | input_unregister_device(pad_input_dev); | ||
| 1495 | pad_input_dev = NULL; | ||
| 1496 | wacom_wac->pad_registered = false; | ||
| 1497 | fail_register_pad_input: | ||
| 1498 | if (touch_input_dev) | ||
| 1499 | input_unregister_device(touch_input_dev); | ||
| 1500 | wacom_wac->touch_input = NULL; | 1828 | wacom_wac->touch_input = NULL; |
| 1501 | wacom_wac->touch_registered = false; | ||
| 1502 | fail_register_touch_input: | ||
| 1503 | if (pen_input_dev) | ||
| 1504 | input_unregister_device(pen_input_dev); | ||
| 1505 | wacom_wac->pen_input = NULL; | 1829 | wacom_wac->pen_input = NULL; |
| 1506 | wacom_wac->pen_registered = false; | ||
| 1507 | fail_register_pen_input: | ||
| 1508 | return error; | 1830 | return error; |
| 1509 | } | 1831 | } |
| 1510 | 1832 | ||
| @@ -1543,14 +1865,14 @@ static void wacom_calculate_res(struct wacom_features *features) | |||
| 1543 | 1865 | ||
| 1544 | void wacom_battery_work(struct work_struct *work) | 1866 | void wacom_battery_work(struct work_struct *work) |
| 1545 | { | 1867 | { |
| 1546 | struct wacom *wacom = container_of(work, struct wacom, work); | 1868 | struct wacom *wacom = container_of(work, struct wacom, battery_work); |
| 1547 | 1869 | ||
| 1548 | if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && | 1870 | if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && |
| 1549 | !wacom->battery) { | 1871 | !wacom->battery.battery) { |
| 1550 | wacom_initialize_battery(wacom); | 1872 | wacom_initialize_battery(wacom); |
| 1551 | } | 1873 | } |
| 1552 | else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && | 1874 | else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && |
| 1553 | wacom->battery) { | 1875 | wacom->battery.battery) { |
| 1554 | wacom_destroy_battery(wacom); | 1876 | wacom_destroy_battery(wacom); |
| 1555 | } | 1877 | } |
| 1556 | } | 1878 | } |
| @@ -1606,6 +1928,9 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix) | |||
| 1606 | strlcpy(name, features->name, sizeof(name)); | 1928 | strlcpy(name, features->name, sizeof(name)); |
| 1607 | } | 1929 | } |
| 1608 | 1930 | ||
| 1931 | snprintf(wacom_wac->name, sizeof(wacom_wac->name), "%s%s", | ||
| 1932 | name, suffix); | ||
| 1933 | |||
| 1609 | /* Append the device type to the name */ | 1934 | /* Append the device type to the name */ |
| 1610 | snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name), | 1935 | snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name), |
| 1611 | "%s%s Pen", name, suffix); | 1936 | "%s%s Pen", name, suffix); |
| @@ -1615,6 +1940,22 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix) | |||
| 1615 | "%s%s Pad", name, suffix); | 1940 | "%s%s Pad", name, suffix); |
| 1616 | } | 1941 | } |
| 1617 | 1942 | ||
| 1943 | static void wacom_release_resources(struct wacom *wacom) | ||
| 1944 | { | ||
| 1945 | struct hid_device *hdev = wacom->hdev; | ||
| 1946 | |||
| 1947 | if (!wacom->resources) | ||
| 1948 | return; | ||
| 1949 | |||
| 1950 | devres_release_group(&hdev->dev, wacom); | ||
| 1951 | |||
| 1952 | wacom->resources = false; | ||
| 1953 | |||
| 1954 | wacom->wacom_wac.pen_input = NULL; | ||
| 1955 | wacom->wacom_wac.touch_input = NULL; | ||
| 1956 | wacom->wacom_wac.pad_input = NULL; | ||
| 1957 | } | ||
| 1958 | |||
| 1618 | static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | 1959 | static int wacom_parse_and_register(struct wacom *wacom, bool wireless) |
| 1619 | { | 1960 | { |
| 1620 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | 1961 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; |
| @@ -1627,9 +1968,14 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | |||
| 1627 | if (features->pktlen > WACOM_PKGLEN_MAX) | 1968 | if (features->pktlen > WACOM_PKGLEN_MAX) |
| 1628 | return -EINVAL; | 1969 | return -EINVAL; |
| 1629 | 1970 | ||
| 1971 | if (!devres_open_group(&hdev->dev, wacom, GFP_KERNEL)) | ||
| 1972 | return -ENOMEM; | ||
| 1973 | |||
| 1974 | wacom->resources = true; | ||
| 1975 | |||
| 1630 | error = wacom_allocate_inputs(wacom); | 1976 | error = wacom_allocate_inputs(wacom); |
| 1631 | if (error) | 1977 | if (error) |
| 1632 | return error; | 1978 | goto fail; |
| 1633 | 1979 | ||
| 1634 | /* | 1980 | /* |
| 1635 | * Bamboo Pad has a generic hid handling for the Pen, and we switch it | 1981 | * Bamboo Pad has a generic hid handling for the Pen, and we switch it |
| @@ -1642,7 +1988,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | |||
| 1642 | } else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) && | 1988 | } else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) && |
| 1643 | (features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) { | 1989 | (features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) { |
| 1644 | error = -ENODEV; | 1990 | error = -ENODEV; |
| 1645 | goto fail_allocate_inputs; | 1991 | goto fail; |
| 1646 | } | 1992 | } |
| 1647 | } | 1993 | } |
| 1648 | 1994 | ||
| @@ -1662,7 +2008,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | |||
| 1662 | error ? "Ignoring" : "Assuming pen"); | 2008 | error ? "Ignoring" : "Assuming pen"); |
| 1663 | 2009 | ||
| 1664 | if (error) | 2010 | if (error) |
| 1665 | goto fail_parsed; | 2011 | goto fail; |
| 1666 | 2012 | ||
| 1667 | features->device_type |= WACOM_DEVICETYPE_PEN; | 2013 | features->device_type |= WACOM_DEVICETYPE_PEN; |
| 1668 | } | 2014 | } |
| @@ -1673,18 +2019,28 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | |||
| 1673 | 2019 | ||
| 1674 | error = wacom_add_shared_data(hdev); | 2020 | error = wacom_add_shared_data(hdev); |
| 1675 | if (error) | 2021 | if (error) |
| 1676 | goto fail_shared_data; | 2022 | goto fail; |
| 1677 | 2023 | ||
| 1678 | if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) && | 2024 | if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) && |
| 1679 | (features->quirks & WACOM_QUIRK_BATTERY)) { | 2025 | (features->quirks & WACOM_QUIRK_BATTERY)) { |
| 1680 | error = wacom_initialize_battery(wacom); | 2026 | error = wacom_initialize_battery(wacom); |
| 1681 | if (error) | 2027 | if (error) |
| 1682 | goto fail_battery; | 2028 | goto fail; |
| 1683 | } | 2029 | } |
| 1684 | 2030 | ||
| 1685 | error = wacom_register_inputs(wacom); | 2031 | error = wacom_register_inputs(wacom); |
| 1686 | if (error) | 2032 | if (error) |
| 1687 | goto fail_register_inputs; | 2033 | goto fail; |
| 2034 | |||
| 2035 | if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) { | ||
| 2036 | error = wacom_initialize_leds(wacom); | ||
| 2037 | if (error) | ||
| 2038 | goto fail; | ||
| 2039 | |||
| 2040 | error = wacom_initialize_remotes(wacom); | ||
| 2041 | if (error) | ||
| 2042 | goto fail; | ||
| 2043 | } | ||
| 1688 | 2044 | ||
| 1689 | if (features->type == HID_GENERIC) | 2045 | if (features->type == HID_GENERIC) |
| 1690 | connect_mask |= HID_CONNECT_DRIVER; | 2046 | connect_mask |= HID_CONNECT_DRIVER; |
| @@ -1693,7 +2049,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | |||
| 1693 | error = hid_hw_start(hdev, connect_mask); | 2049 | error = hid_hw_start(hdev, connect_mask); |
| 1694 | if (error) { | 2050 | if (error) { |
| 1695 | hid_err(hdev, "hw start failed\n"); | 2051 | hid_err(hdev, "hw start failed\n"); |
| 1696 | goto fail_hw_start; | 2052 | goto fail; |
| 1697 | } | 2053 | } |
| 1698 | 2054 | ||
| 1699 | if (!wireless) { | 2055 | if (!wireless) { |
| @@ -1705,7 +2061,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | |||
| 1705 | if ((features->type == BAMBOO_TOUCH) && | 2061 | if ((features->type == BAMBOO_TOUCH) && |
| 1706 | (features->device_type & WACOM_DEVICETYPE_PEN)) { | 2062 | (features->device_type & WACOM_DEVICETYPE_PEN)) { |
| 1707 | error = -ENODEV; | 2063 | error = -ENODEV; |
| 1708 | goto fail_hw_start; | 2064 | goto fail_quirks; |
| 1709 | } | 2065 | } |
| 1710 | 2066 | ||
| 1711 | /* pen only Bamboo neither support touch nor pad */ | 2067 | /* pen only Bamboo neither support touch nor pad */ |
| @@ -1713,37 +2069,33 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) | |||
| 1713 | ((features->device_type & WACOM_DEVICETYPE_TOUCH) || | 2069 | ((features->device_type & WACOM_DEVICETYPE_TOUCH) || |
| 1714 | (features->device_type & WACOM_DEVICETYPE_PAD))) { | 2070 | (features->device_type & WACOM_DEVICETYPE_PAD))) { |
| 1715 | error = -ENODEV; | 2071 | error = -ENODEV; |
| 1716 | goto fail_hw_start; | 2072 | goto fail_quirks; |
| 1717 | } | 2073 | } |
| 1718 | 2074 | ||
| 1719 | if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) | 2075 | if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) |
| 1720 | error = hid_hw_open(hdev); | 2076 | error = hid_hw_open(hdev); |
| 1721 | 2077 | ||
| 1722 | if ((wacom_wac->features.type == INTUOSHT || | 2078 | if ((wacom_wac->features.type == INTUOSHT || |
| 1723 | wacom_wac->features.type == INTUOSHT2) && | 2079 | wacom_wac->features.type == INTUOSHT2) && |
| 1724 | (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) { | 2080 | (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) { |
| 1725 | wacom_wac->shared->touch_input = wacom_wac->touch_input; | 2081 | wacom_wac->shared->type = wacom_wac->features.type; |
| 2082 | wacom_wac->shared->touch_input = wacom_wac->touch_input; | ||
| 1726 | } | 2083 | } |
| 1727 | 2084 | ||
| 2085 | devres_close_group(&hdev->dev, wacom); | ||
| 2086 | |||
| 1728 | return 0; | 2087 | return 0; |
| 1729 | 2088 | ||
| 1730 | fail_hw_start: | 2089 | fail_quirks: |
| 1731 | hid_hw_stop(hdev); | 2090 | hid_hw_stop(hdev); |
| 1732 | fail_register_inputs: | 2091 | fail: |
| 1733 | wacom_clean_inputs(wacom); | 2092 | wacom_release_resources(wacom); |
| 1734 | wacom_destroy_battery(wacom); | ||
| 1735 | fail_battery: | ||
| 1736 | wacom_remove_shared_data(wacom); | ||
| 1737 | fail_shared_data: | ||
| 1738 | fail_parsed: | ||
| 1739 | fail_allocate_inputs: | ||
| 1740 | wacom_clean_inputs(wacom); | ||
| 1741 | return error; | 2093 | return error; |
| 1742 | } | 2094 | } |
| 1743 | 2095 | ||
| 1744 | static void wacom_wireless_work(struct work_struct *work) | 2096 | static void wacom_wireless_work(struct work_struct *work) |
| 1745 | { | 2097 | { |
| 1746 | struct wacom *wacom = container_of(work, struct wacom, work); | 2098 | struct wacom *wacom = container_of(work, struct wacom, wireless_work); |
| 1747 | struct usb_device *usbdev = wacom->usbdev; | 2099 | struct usb_device *usbdev = wacom->usbdev; |
| 1748 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | 2100 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; |
| 1749 | struct hid_device *hdev1, *hdev2; | 2101 | struct hid_device *hdev1, *hdev2; |
| @@ -1762,17 +2114,16 @@ static void wacom_wireless_work(struct work_struct *work) | |||
| 1762 | hdev1 = usb_get_intfdata(usbdev->config->interface[1]); | 2114 | hdev1 = usb_get_intfdata(usbdev->config->interface[1]); |
| 1763 | wacom1 = hid_get_drvdata(hdev1); | 2115 | wacom1 = hid_get_drvdata(hdev1); |
| 1764 | wacom_wac1 = &(wacom1->wacom_wac); | 2116 | wacom_wac1 = &(wacom1->wacom_wac); |
| 1765 | wacom_clean_inputs(wacom1); | 2117 | wacom_release_resources(wacom1); |
| 1766 | 2118 | ||
| 1767 | /* Touch interface */ | 2119 | /* Touch interface */ |
| 1768 | hdev2 = usb_get_intfdata(usbdev->config->interface[2]); | 2120 | hdev2 = usb_get_intfdata(usbdev->config->interface[2]); |
| 1769 | wacom2 = hid_get_drvdata(hdev2); | 2121 | wacom2 = hid_get_drvdata(hdev2); |
| 1770 | wacom_wac2 = &(wacom2->wacom_wac); | 2122 | wacom_wac2 = &(wacom2->wacom_wac); |
| 1771 | wacom_clean_inputs(wacom2); | 2123 | wacom_release_resources(wacom2); |
| 1772 | 2124 | ||
| 1773 | if (wacom_wac->pid == 0) { | 2125 | if (wacom_wac->pid == 0) { |
| 1774 | hid_info(wacom->hdev, "wireless tablet disconnected\n"); | 2126 | hid_info(wacom->hdev, "wireless tablet disconnected\n"); |
| 1775 | wacom_wac1->shared->type = 0; | ||
| 1776 | } else { | 2127 | } else { |
| 1777 | const struct hid_device_id *id = wacom_ids; | 2128 | const struct hid_device_id *id = wacom_ids; |
| 1778 | 2129 | ||
| @@ -1814,6 +2165,8 @@ static void wacom_wireless_work(struct work_struct *work) | |||
| 1814 | goto fail; | 2165 | goto fail; |
| 1815 | } | 2166 | } |
| 1816 | 2167 | ||
| 2168 | strlcpy(wacom_wac->name, wacom_wac1->name, | ||
| 2169 | sizeof(wacom_wac->name)); | ||
| 1817 | error = wacom_initialize_battery(wacom); | 2170 | error = wacom_initialize_battery(wacom); |
| 1818 | if (error) | 2171 | if (error) |
| 1819 | goto fail; | 2172 | goto fail; |
| @@ -1822,11 +2175,177 @@ static void wacom_wireless_work(struct work_struct *work) | |||
| 1822 | return; | 2175 | return; |
| 1823 | 2176 | ||
| 1824 | fail: | 2177 | fail: |
| 1825 | wacom_clean_inputs(wacom1); | 2178 | wacom_release_resources(wacom1); |
| 1826 | wacom_clean_inputs(wacom2); | 2179 | wacom_release_resources(wacom2); |
| 1827 | return; | 2180 | return; |
| 1828 | } | 2181 | } |
| 1829 | 2182 | ||
| 2183 | static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index) | ||
| 2184 | { | ||
| 2185 | struct wacom_remote *remote = wacom->remote; | ||
| 2186 | u32 serial = remote->remotes[index].serial; | ||
| 2187 | int i; | ||
| 2188 | unsigned long flags; | ||
| 2189 | |||
| 2190 | spin_lock_irqsave(&remote->remote_lock, flags); | ||
| 2191 | remote->remotes[index].registered = false; | ||
| 2192 | spin_unlock_irqrestore(&remote->remote_lock, flags); | ||
| 2193 | |||
| 2194 | if (remote->remotes[index].battery.battery) | ||
| 2195 | devres_release_group(&wacom->hdev->dev, | ||
| 2196 | &remote->remotes[index].battery.bat_desc); | ||
| 2197 | |||
| 2198 | if (remote->remotes[index].group.name) | ||
| 2199 | devres_release_group(&wacom->hdev->dev, | ||
| 2200 | &remote->remotes[index]); | ||
| 2201 | |||
| 2202 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { | ||
| 2203 | if (remote->remotes[i].serial == serial) { | ||
| 2204 | remote->remotes[i].serial = 0; | ||
| 2205 | remote->remotes[i].group.name = NULL; | ||
| 2206 | remote->remotes[i].registered = false; | ||
| 2207 | remote->remotes[i].battery.battery = NULL; | ||
| 2208 | wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN; | ||
| 2209 | } | ||
| 2210 | } | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | static int wacom_remote_create_one(struct wacom *wacom, u32 serial, | ||
| 2214 | unsigned int index) | ||
| 2215 | { | ||
| 2216 | struct wacom_remote *remote = wacom->remote; | ||
| 2217 | struct device *dev = &wacom->hdev->dev; | ||
| 2218 | int error, k; | ||
| 2219 | |||
| 2220 | /* A remote can pair more than once with an EKR, | ||
| 2221 | * check to make sure this serial isn't already paired. | ||
| 2222 | */ | ||
| 2223 | for (k = 0; k < WACOM_MAX_REMOTES; k++) { | ||
| 2224 | if (remote->remotes[k].serial == serial) | ||
| 2225 | break; | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | if (k < WACOM_MAX_REMOTES) { | ||
| 2229 | remote->remotes[index].serial = serial; | ||
| 2230 | return 0; | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | if (!devres_open_group(dev, &remote->remotes[index], GFP_KERNEL)) | ||
| 2234 | return -ENOMEM; | ||
| 2235 | |||
| 2236 | error = wacom_remote_create_attr_group(wacom, serial, index); | ||
| 2237 | if (error) | ||
| 2238 | goto fail; | ||
| 2239 | |||
| 2240 | remote->remotes[index].input = wacom_allocate_input(wacom); | ||
| 2241 | if (!remote->remotes[index].input) { | ||
| 2242 | error = -ENOMEM; | ||
| 2243 | goto fail; | ||
| 2244 | } | ||
| 2245 | remote->remotes[index].input->uniq = remote->remotes[index].group.name; | ||
| 2246 | remote->remotes[index].input->name = wacom->wacom_wac.pad_name; | ||
| 2247 | |||
| 2248 | if (!remote->remotes[index].input->name) { | ||
| 2249 | error = -EINVAL; | ||
| 2250 | goto fail; | ||
| 2251 | } | ||
| 2252 | |||
| 2253 | error = wacom_setup_pad_input_capabilities(remote->remotes[index].input, | ||
| 2254 | &wacom->wacom_wac); | ||
| 2255 | if (error) | ||
| 2256 | goto fail; | ||
| 2257 | |||
| 2258 | remote->remotes[index].serial = serial; | ||
| 2259 | |||
| 2260 | error = input_register_device(remote->remotes[index].input); | ||
| 2261 | if (error) | ||
| 2262 | goto fail; | ||
| 2263 | |||
| 2264 | error = wacom_led_groups_alloc_and_register_one( | ||
| 2265 | &remote->remotes[index].input->dev, | ||
| 2266 | wacom, index, 3, true); | ||
| 2267 | if (error) | ||
| 2268 | goto fail; | ||
| 2269 | |||
| 2270 | remote->remotes[index].registered = true; | ||
| 2271 | |||
| 2272 | devres_close_group(dev, &remote->remotes[index]); | ||
| 2273 | return 0; | ||
| 2274 | |||
| 2275 | fail: | ||
| 2276 | devres_release_group(dev, &remote->remotes[index]); | ||
| 2277 | remote->remotes[index].serial = 0; | ||
| 2278 | return error; | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | static int wacom_remote_attach_battery(struct wacom *wacom, int index) | ||
| 2282 | { | ||
| 2283 | struct wacom_remote *remote = wacom->remote; | ||
| 2284 | int error; | ||
| 2285 | |||
| 2286 | if (!remote->remotes[index].registered) | ||
| 2287 | return 0; | ||
| 2288 | |||
| 2289 | if (remote->remotes[index].battery.battery) | ||
| 2290 | return 0; | ||
| 2291 | |||
| 2292 | if (wacom->led.groups[index].select == WACOM_STATUS_UNKNOWN) | ||
| 2293 | return 0; | ||
| 2294 | |||
| 2295 | error = __wacom_initialize_battery(wacom, | ||
| 2296 | &wacom->remote->remotes[index].battery); | ||
| 2297 | if (error) | ||
| 2298 | return error; | ||
| 2299 | |||
| 2300 | return 0; | ||
| 2301 | } | ||
| 2302 | |||
| 2303 | static void wacom_remote_work(struct work_struct *work) | ||
| 2304 | { | ||
| 2305 | struct wacom *wacom = container_of(work, struct wacom, remote_work); | ||
| 2306 | struct wacom_remote *remote = wacom->remote; | ||
| 2307 | struct wacom_remote_data data; | ||
| 2308 | unsigned long flags; | ||
| 2309 | unsigned int count; | ||
| 2310 | u32 serial; | ||
| 2311 | int i; | ||
| 2312 | |||
| 2313 | spin_lock_irqsave(&remote->remote_lock, flags); | ||
| 2314 | |||
| 2315 | count = kfifo_out(&remote->remote_fifo, &data, sizeof(data)); | ||
| 2316 | |||
| 2317 | if (count != sizeof(data)) { | ||
| 2318 | hid_err(wacom->hdev, | ||
| 2319 | "workitem triggered without status available\n"); | ||
| 2320 | spin_unlock_irqrestore(&remote->remote_lock, flags); | ||
| 2321 | return; | ||
| 2322 | } | ||
| 2323 | |||
| 2324 | if (!kfifo_is_empty(&remote->remote_fifo)) | ||
| 2325 | wacom_schedule_work(&wacom->wacom_wac, WACOM_WORKER_REMOTE); | ||
| 2326 | |||
| 2327 | spin_unlock_irqrestore(&remote->remote_lock, flags); | ||
| 2328 | |||
| 2329 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { | ||
| 2330 | serial = data.remote[i].serial; | ||
| 2331 | if (data.remote[i].connected) { | ||
| 2332 | |||
| 2333 | if (remote->remotes[i].serial == serial) { | ||
| 2334 | wacom_remote_attach_battery(wacom, i); | ||
| 2335 | continue; | ||
| 2336 | } | ||
| 2337 | |||
| 2338 | if (remote->remotes[i].serial) | ||
| 2339 | wacom_remote_destroy_one(wacom, i); | ||
| 2340 | |||
| 2341 | wacom_remote_create_one(wacom, serial, i); | ||
| 2342 | |||
| 2343 | } else if (remote->remotes[i].serial) { | ||
| 2344 | wacom_remote_destroy_one(wacom, i); | ||
| 2345 | } | ||
| 2346 | } | ||
| 2347 | } | ||
| 2348 | |||
| 1830 | static int wacom_probe(struct hid_device *hdev, | 2349 | static int wacom_probe(struct hid_device *hdev, |
| 1831 | const struct hid_device_id *id) | 2350 | const struct hid_device_id *id) |
| 1832 | { | 2351 | { |
| @@ -1845,7 +2364,7 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 1845 | /* hid-core sets this quirk for the boot interface */ | 2364 | /* hid-core sets this quirk for the boot interface */ |
| 1846 | hdev->quirks &= ~HID_QUIRK_NOGET; | 2365 | hdev->quirks &= ~HID_QUIRK_NOGET; |
| 1847 | 2366 | ||
| 1848 | wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); | 2367 | wacom = devm_kzalloc(&hdev->dev, sizeof(struct wacom), GFP_KERNEL); |
| 1849 | if (!wacom) | 2368 | if (!wacom) |
| 1850 | return -ENOMEM; | 2369 | return -ENOMEM; |
| 1851 | 2370 | ||
| @@ -1858,7 +2377,7 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 1858 | 2377 | ||
| 1859 | if (features->check_for_hid_type && features->hid_type != hdev->type) { | 2378 | if (features->check_for_hid_type && features->hid_type != hdev->type) { |
| 1860 | error = -ENODEV; | 2379 | error = -ENODEV; |
| 1861 | goto fail_type; | 2380 | goto fail; |
| 1862 | } | 2381 | } |
| 1863 | 2382 | ||
| 1864 | wacom_wac->hid_data.inputmode = -1; | 2383 | wacom_wac->hid_data.inputmode = -1; |
| @@ -1867,18 +2386,20 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 1867 | wacom->usbdev = dev; | 2386 | wacom->usbdev = dev; |
| 1868 | wacom->intf = intf; | 2387 | wacom->intf = intf; |
| 1869 | mutex_init(&wacom->lock); | 2388 | mutex_init(&wacom->lock); |
| 1870 | INIT_WORK(&wacom->work, wacom_wireless_work); | 2389 | INIT_WORK(&wacom->wireless_work, wacom_wireless_work); |
| 2390 | INIT_WORK(&wacom->battery_work, wacom_battery_work); | ||
| 2391 | INIT_WORK(&wacom->remote_work, wacom_remote_work); | ||
| 1871 | 2392 | ||
| 1872 | /* ask for the report descriptor to be loaded by HID */ | 2393 | /* ask for the report descriptor to be loaded by HID */ |
| 1873 | error = hid_parse(hdev); | 2394 | error = hid_parse(hdev); |
| 1874 | if (error) { | 2395 | if (error) { |
| 1875 | hid_err(hdev, "parse failed\n"); | 2396 | hid_err(hdev, "parse failed\n"); |
| 1876 | goto fail_parse; | 2397 | goto fail; |
| 1877 | } | 2398 | } |
| 1878 | 2399 | ||
| 1879 | error = wacom_parse_and_register(wacom, false); | 2400 | error = wacom_parse_and_register(wacom, false); |
| 1880 | if (error) | 2401 | if (error) |
| 1881 | goto fail_parse; | 2402 | goto fail; |
| 1882 | 2403 | ||
| 1883 | if (hdev->bus == BUS_BLUETOOTH) { | 2404 | if (hdev->bus == BUS_BLUETOOTH) { |
| 1884 | error = device_create_file(&hdev->dev, &dev_attr_speed); | 2405 | error = device_create_file(&hdev->dev, &dev_attr_speed); |
| @@ -1890,9 +2411,7 @@ static int wacom_probe(struct hid_device *hdev, | |||
| 1890 | 2411 | ||
| 1891 | return 0; | 2412 | return 0; |
| 1892 | 2413 | ||
| 1893 | fail_type: | 2414 | fail: |
| 1894 | fail_parse: | ||
| 1895 | kfree(wacom); | ||
| 1896 | hid_set_drvdata(hdev, NULL); | 2415 | hid_set_drvdata(hdev, NULL); |
| 1897 | return error; | 2416 | return error; |
| 1898 | } | 2417 | } |
| @@ -1908,15 +2427,13 @@ static void wacom_remove(struct hid_device *hdev) | |||
| 1908 | 2427 | ||
| 1909 | hid_hw_stop(hdev); | 2428 | hid_hw_stop(hdev); |
| 1910 | 2429 | ||
| 1911 | cancel_work_sync(&wacom->work); | 2430 | cancel_work_sync(&wacom->wireless_work); |
| 1912 | wacom_clean_inputs(wacom); | 2431 | cancel_work_sync(&wacom->battery_work); |
| 2432 | cancel_work_sync(&wacom->remote_work); | ||
| 1913 | if (hdev->bus == BUS_BLUETOOTH) | 2433 | if (hdev->bus == BUS_BLUETOOTH) |
| 1914 | device_remove_file(&hdev->dev, &dev_attr_speed); | 2434 | device_remove_file(&hdev->dev, &dev_attr_speed); |
| 1915 | wacom_destroy_battery(wacom); | ||
| 1916 | wacom_remove_shared_data(wacom); | ||
| 1917 | 2435 | ||
| 1918 | hid_set_drvdata(hdev, NULL); | 2436 | hid_set_drvdata(hdev, NULL); |
| 1919 | kfree(wacom); | ||
| 1920 | } | 2437 | } |
| 1921 | 2438 | ||
| 1922 | #ifdef CONFIG_PM | 2439 | #ifdef CONFIG_PM |
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 1eae13cdc502..1cb79925730d 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c | |||
| @@ -34,6 +34,10 @@ | |||
| 34 | */ | 34 | */ |
| 35 | #define WACOM_CONTACT_AREA_SCALE 2607 | 35 | #define WACOM_CONTACT_AREA_SCALE 2607 |
| 36 | 36 | ||
| 37 | static bool touch_arbitration = 1; | ||
| 38 | module_param(touch_arbitration, bool, 0644); | ||
| 39 | MODULE_PARM_DESC(touch_arbitration, " on (Y) off (N)"); | ||
| 40 | |||
| 37 | static void wacom_report_numbered_buttons(struct input_dev *input_dev, | 41 | static void wacom_report_numbered_buttons(struct input_dev *input_dev, |
| 38 | int button_count, int mask); | 42 | int button_count, int mask); |
| 39 | 43 | ||
| @@ -48,25 +52,34 @@ static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 }; | |||
| 48 | */ | 52 | */ |
| 49 | static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; | 53 | static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; |
| 50 | 54 | ||
| 55 | static void __wacom_notify_battery(struct wacom_battery *battery, | ||
| 56 | int bat_capacity, bool bat_charging, | ||
| 57 | bool bat_connected, bool ps_connected) | ||
| 58 | { | ||
| 59 | bool changed = battery->battery_capacity != bat_capacity || | ||
| 60 | battery->bat_charging != bat_charging || | ||
| 61 | battery->bat_connected != bat_connected || | ||
| 62 | battery->ps_connected != ps_connected; | ||
| 63 | |||
| 64 | if (changed) { | ||
| 65 | battery->battery_capacity = bat_capacity; | ||
| 66 | battery->bat_charging = bat_charging; | ||
| 67 | battery->bat_connected = bat_connected; | ||
| 68 | battery->ps_connected = ps_connected; | ||
| 69 | |||
| 70 | if (battery->battery) | ||
| 71 | power_supply_changed(battery->battery); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 51 | static void wacom_notify_battery(struct wacom_wac *wacom_wac, | 75 | static void wacom_notify_battery(struct wacom_wac *wacom_wac, |
| 52 | int bat_capacity, bool bat_charging, bool bat_connected, | 76 | int bat_capacity, bool bat_charging, bool bat_connected, |
| 53 | bool ps_connected) | 77 | bool ps_connected) |
| 54 | { | 78 | { |
| 55 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); | 79 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); |
| 56 | bool changed = wacom_wac->battery_capacity != bat_capacity || | ||
| 57 | wacom_wac->bat_charging != bat_charging || | ||
| 58 | wacom_wac->bat_connected != bat_connected || | ||
| 59 | wacom_wac->ps_connected != ps_connected; | ||
| 60 | 80 | ||
| 61 | if (changed) { | 81 | __wacom_notify_battery(&wacom->battery, bat_capacity, bat_charging, |
| 62 | wacom_wac->battery_capacity = bat_capacity; | 82 | bat_connected, ps_connected); |
| 63 | wacom_wac->bat_charging = bat_charging; | ||
| 64 | wacom_wac->bat_connected = bat_connected; | ||
| 65 | wacom_wac->ps_connected = ps_connected; | ||
| 66 | |||
| 67 | if (wacom->battery) | ||
| 68 | power_supply_changed(wacom->battery); | ||
| 69 | } | ||
| 70 | } | 83 | } |
| 71 | 84 | ||
| 72 | static int wacom_penpartner_irq(struct wacom_wac *wacom) | 85 | static int wacom_penpartner_irq(struct wacom_wac *wacom) |
| @@ -751,22 +764,37 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) | |||
| 751 | static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) | 764 | static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) |
| 752 | { | 765 | { |
| 753 | unsigned char *data = wacom_wac->data; | 766 | unsigned char *data = wacom_wac->data; |
| 754 | struct input_dev *input = wacom_wac->pad_input; | 767 | struct input_dev *input; |
| 755 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); | 768 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); |
| 756 | struct wacom_features *features = &wacom_wac->features; | 769 | struct wacom_remote *remote = wacom->remote; |
| 757 | int bat_charging, bat_percent, touch_ring_mode; | 770 | int bat_charging, bat_percent, touch_ring_mode; |
| 758 | __u32 serial; | 771 | __u32 serial; |
| 759 | int i; | 772 | int i, index = -1; |
| 773 | unsigned long flags; | ||
| 760 | 774 | ||
| 761 | if (data[0] != WACOM_REPORT_REMOTE) { | 775 | if (data[0] != WACOM_REPORT_REMOTE) { |
| 762 | dev_dbg(input->dev.parent, | 776 | hid_dbg(wacom->hdev, "%s: received unknown report #%d", |
| 763 | "%s: received unknown report #%d", __func__, data[0]); | 777 | __func__, data[0]); |
| 764 | return 0; | 778 | return 0; |
| 765 | } | 779 | } |
| 766 | 780 | ||
| 767 | serial = data[3] + (data[4] << 8) + (data[5] << 16); | 781 | serial = data[3] + (data[4] << 8) + (data[5] << 16); |
| 768 | wacom_wac->id[0] = PAD_DEVICE_ID; | 782 | wacom_wac->id[0] = PAD_DEVICE_ID; |
| 769 | 783 | ||
| 784 | spin_lock_irqsave(&remote->remote_lock, flags); | ||
| 785 | |||
| 786 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { | ||
| 787 | if (remote->remotes[i].serial == serial) { | ||
| 788 | index = i; | ||
| 789 | break; | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | if (index < 0 || !remote->remotes[index].registered) | ||
| 794 | goto out; | ||
| 795 | |||
| 796 | input = remote->remotes[index].input; | ||
| 797 | |||
| 770 | input_report_key(input, BTN_0, (data[9] & 0x01)); | 798 | input_report_key(input, BTN_0, (data[9] & 0x01)); |
| 771 | input_report_key(input, BTN_1, (data[9] & 0x02)); | 799 | input_report_key(input, BTN_1, (data[9] & 0x02)); |
| 772 | input_report_key(input, BTN_2, (data[9] & 0x04)); | 800 | input_report_key(input, BTN_2, (data[9] & 0x04)); |
| @@ -803,73 +831,69 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) | |||
| 803 | 831 | ||
| 804 | input_event(input, EV_MSC, MSC_SERIAL, serial); | 832 | input_event(input, EV_MSC, MSC_SERIAL, serial); |
| 805 | 833 | ||
| 834 | input_sync(input); | ||
| 835 | |||
| 806 | /*Which mode select (LED light) is currently on?*/ | 836 | /*Which mode select (LED light) is currently on?*/ |
| 807 | touch_ring_mode = (data[11] & 0xC0) >> 6; | 837 | touch_ring_mode = (data[11] & 0xC0) >> 6; |
| 808 | 838 | ||
| 809 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { | 839 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { |
| 810 | if (wacom_wac->serial[i] == serial) | 840 | if (remote->remotes[i].serial == serial) |
| 811 | wacom->led.select[i] = touch_ring_mode; | 841 | wacom->led.groups[i].select = touch_ring_mode; |
| 812 | } | ||
| 813 | |||
| 814 | if (!wacom->battery && | ||
| 815 | !(features->quirks & WACOM_QUIRK_BATTERY)) { | ||
| 816 | features->quirks |= WACOM_QUIRK_BATTERY; | ||
| 817 | INIT_WORK(&wacom->work, wacom_battery_work); | ||
| 818 | wacom_schedule_work(wacom_wac); | ||
| 819 | } | 842 | } |
| 820 | 843 | ||
| 821 | wacom_notify_battery(wacom_wac, bat_percent, bat_charging, 1, | 844 | __wacom_notify_battery(&remote->remotes[index].battery, bat_percent, |
| 822 | bat_charging); | 845 | bat_charging, 1, bat_charging); |
| 823 | 846 | ||
| 824 | return 1; | 847 | out: |
| 848 | spin_unlock_irqrestore(&remote->remote_lock, flags); | ||
| 849 | return 0; | ||
| 825 | } | 850 | } |
| 826 | 851 | ||
| 827 | static int wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len) | 852 | static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len) |
| 828 | { | 853 | { |
| 829 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); | 854 | struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); |
| 830 | unsigned char *data = wacom_wac->data; | 855 | unsigned char *data = wacom_wac->data; |
| 831 | int i; | 856 | struct wacom_remote *remote = wacom->remote; |
| 857 | struct wacom_remote_data remote_data; | ||
| 858 | unsigned long flags; | ||
| 859 | int i, ret; | ||
| 832 | 860 | ||
| 833 | if (data[0] != WACOM_REPORT_DEVICE_LIST) | 861 | if (data[0] != WACOM_REPORT_DEVICE_LIST) |
| 834 | return 0; | 862 | return; |
| 863 | |||
| 864 | memset(&remote_data, 0, sizeof(struct wacom_remote_data)); | ||
| 835 | 865 | ||
| 836 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { | 866 | for (i = 0; i < WACOM_MAX_REMOTES; i++) { |
| 837 | int j = i * 6; | 867 | int j = i * 6; |
| 838 | int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4]; | 868 | int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4]; |
| 839 | bool connected = data[j+2]; | 869 | bool connected = data[j+2]; |
| 840 | 870 | ||
| 841 | if (connected) { | 871 | remote_data.remote[i].serial = serial; |
| 842 | int k; | 872 | remote_data.remote[i].connected = connected; |
| 873 | } | ||
| 843 | 874 | ||
| 844 | if (wacom_wac->serial[i] == serial) | 875 | spin_lock_irqsave(&remote->remote_lock, flags); |
| 845 | continue; | ||
| 846 | 876 | ||
| 847 | if (wacom_wac->serial[i]) { | 877 | ret = kfifo_in(&remote->remote_fifo, &remote_data, sizeof(remote_data)); |
| 848 | wacom_remote_destroy_attr_group(wacom, | 878 | if (ret != sizeof(remote_data)) { |
| 849 | wacom_wac->serial[i]); | 879 | spin_unlock_irqrestore(&remote->remote_lock, flags); |
| 850 | } | 880 | hid_err(wacom->hdev, "Can't queue Remote status event.\n"); |
| 881 | return; | ||
| 882 | } | ||
| 851 | 883 | ||
| 852 | /* A remote can pair more than once with an EKR, | 884 | spin_unlock_irqrestore(&remote->remote_lock, flags); |
| 853 | * check to make sure this serial isn't already paired. | ||
| 854 | */ | ||
| 855 | for (k = 0; k < WACOM_MAX_REMOTES; k++) { | ||
| 856 | if (wacom_wac->serial[k] == serial) | ||
| 857 | break; | ||
| 858 | } | ||
| 859 | 885 | ||
| 860 | if (k < WACOM_MAX_REMOTES) { | 886 | wacom_schedule_work(wacom_wac, WACOM_WORKER_REMOTE); |
| 861 | wacom_wac->serial[i] = serial; | 887 | } |
| 862 | continue; | ||
| 863 | } | ||
| 864 | wacom_remote_create_attr_group(wacom, serial, i); | ||
| 865 | 888 | ||
| 866 | } else if (wacom_wac->serial[i]) { | 889 | static inline bool report_touch_events(struct wacom_wac *wacom) |
| 867 | wacom_remote_destroy_attr_group(wacom, | 890 | { |
| 868 | wacom_wac->serial[i]); | 891 | return (touch_arbitration ? !wacom->shared->stylus_in_proximity : 1); |
| 869 | } | 892 | } |
| 870 | } | ||
| 871 | 893 | ||
| 872 | return 0; | 894 | static inline bool delay_pen_events(struct wacom_wac *wacom) |
| 895 | { | ||
| 896 | return (wacom->shared->touch_down && touch_arbitration); | ||
| 873 | } | 897 | } |
| 874 | 898 | ||
| 875 | static int wacom_intuos_general(struct wacom_wac *wacom) | 899 | static int wacom_intuos_general(struct wacom_wac *wacom) |
| @@ -885,7 +909,7 @@ static int wacom_intuos_general(struct wacom_wac *wacom) | |||
| 885 | data[0] != WACOM_REPORT_INTUOS_PEN) | 909 | data[0] != WACOM_REPORT_INTUOS_PEN) |
| 886 | return 0; | 910 | return 0; |
| 887 | 911 | ||
| 888 | if (wacom->shared->touch_down) | 912 | if (delay_pen_events(wacom)) |
| 889 | return 1; | 913 | return 1; |
| 890 | 914 | ||
| 891 | /* don't report events if we don't know the tool ID */ | 915 | /* don't report events if we don't know the tool ID */ |
| @@ -1145,7 +1169,7 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom) | |||
| 1145 | 1169 | ||
| 1146 | if (touch_max == 1) | 1170 | if (touch_max == 1) |
| 1147 | return test_bit(BTN_TOUCH, input->key) && | 1171 | return test_bit(BTN_TOUCH, input->key) && |
| 1148 | !wacom->shared->stylus_in_proximity; | 1172 | report_touch_events(wacom); |
| 1149 | 1173 | ||
| 1150 | for (i = 0; i < input->mt->num_slots; i++) { | 1174 | for (i = 0; i < input->mt->num_slots; i++) { |
| 1151 | struct input_mt_slot *ps = &input->mt->slots[i]; | 1175 | struct input_mt_slot *ps = &input->mt->slots[i]; |
| @@ -1186,7 +1210,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) | |||
| 1186 | 1210 | ||
| 1187 | for (i = 0; i < contacts_to_send; i++) { | 1211 | for (i = 0; i < contacts_to_send; i++) { |
| 1188 | int offset = (byte_per_packet * i) + 1; | 1212 | int offset = (byte_per_packet * i) + 1; |
| 1189 | bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity; | 1213 | bool touch = (data[offset] & 0x1) && report_touch_events(wacom); |
| 1190 | int slot = input_mt_get_slot_by_key(input, data[offset + 1]); | 1214 | int slot = input_mt_get_slot_by_key(input, data[offset + 1]); |
| 1191 | 1215 | ||
| 1192 | if (slot < 0) | 1216 | if (slot < 0) |
| @@ -1250,7 +1274,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) | |||
| 1250 | 1274 | ||
| 1251 | for (i = 0; i < contacts_to_send; i++) { | 1275 | for (i = 0; i < contacts_to_send; i++) { |
| 1252 | int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; | 1276 | int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; |
| 1253 | bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity; | 1277 | bool touch = (data[offset] & 0x1) && report_touch_events(wacom); |
| 1254 | int id = get_unaligned_le16(&data[offset + 1]); | 1278 | int id = get_unaligned_le16(&data[offset + 1]); |
| 1255 | int slot = input_mt_get_slot_by_key(input, id); | 1279 | int slot = input_mt_get_slot_by_key(input, id); |
| 1256 | 1280 | ||
| @@ -1284,7 +1308,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom) | |||
| 1284 | 1308 | ||
| 1285 | for (i = 0; i < 2; i++) { | 1309 | for (i = 0; i < 2; i++) { |
| 1286 | int p = data[1] & (1 << i); | 1310 | int p = data[1] & (1 << i); |
| 1287 | bool touch = p && !wacom->shared->stylus_in_proximity; | 1311 | bool touch = p && report_touch_events(wacom); |
| 1288 | 1312 | ||
| 1289 | input_mt_slot(input, i); | 1313 | input_mt_slot(input, i); |
| 1290 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); | 1314 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); |
| @@ -1308,7 +1332,7 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) | |||
| 1308 | { | 1332 | { |
| 1309 | unsigned char *data = wacom->data; | 1333 | unsigned char *data = wacom->data; |
| 1310 | struct input_dev *input = wacom->touch_input; | 1334 | struct input_dev *input = wacom->touch_input; |
| 1311 | bool prox = !wacom->shared->stylus_in_proximity; | 1335 | bool prox = report_touch_events(wacom); |
| 1312 | int x = 0, y = 0; | 1336 | int x = 0, y = 0; |
| 1313 | 1337 | ||
| 1314 | if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG) | 1338 | if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG) |
| @@ -1353,8 +1377,10 @@ static int wacom_tpc_pen(struct wacom_wac *wacom) | |||
| 1353 | /* keep pen state for touch events */ | 1377 | /* keep pen state for touch events */ |
| 1354 | wacom->shared->stylus_in_proximity = prox; | 1378 | wacom->shared->stylus_in_proximity = prox; |
| 1355 | 1379 | ||
| 1356 | /* send pen events only when touch is up or forced out */ | 1380 | /* send pen events only when touch is up or forced out |
| 1357 | if (!wacom->shared->touch_down) { | 1381 | * or touch arbitration is off |
| 1382 | */ | ||
| 1383 | if (!delay_pen_events(wacom)) { | ||
| 1358 | input_report_key(input, BTN_STYLUS, data[1] & 0x02); | 1384 | input_report_key(input, BTN_STYLUS, data[1] & 0x02); |
| 1359 | input_report_key(input, BTN_STYLUS2, data[1] & 0x10); | 1385 | input_report_key(input, BTN_STYLUS2, data[1] & 0x10); |
| 1360 | input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); | 1386 | input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); |
| @@ -1496,8 +1522,10 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, | |||
| 1496 | return 0; | 1522 | return 0; |
| 1497 | } | 1523 | } |
| 1498 | 1524 | ||
| 1499 | /* send pen events only when touch is up or forced out */ | 1525 | /* send pen events only when touch is up or forced out |
| 1500 | if (!usage->type || wacom_wac->shared->touch_down) | 1526 | * or touch arbitration is off |
| 1527 | */ | ||
| 1528 | if (!usage->type || delay_pen_events(wacom_wac)) | ||
| 1501 | return 0; | 1529 | return 0; |
| 1502 | 1530 | ||
| 1503 | input_event(input, usage->type, usage->code, value); | 1531 | input_event(input, usage->type, usage->code, value); |
| @@ -1527,8 +1555,7 @@ static void wacom_wac_pen_report(struct hid_device *hdev, | |||
| 1527 | /* keep pen state for touch events */ | 1555 | /* keep pen state for touch events */ |
| 1528 | wacom_wac->shared->stylus_in_proximity = prox; | 1556 | wacom_wac->shared->stylus_in_proximity = prox; |
| 1529 | 1557 | ||
| 1530 | /* send pen events only when touch is up or forced out */ | 1558 | if (!delay_pen_events(wacom_wac)) { |
| 1531 | if (!wacom_wac->shared->touch_down) { | ||
| 1532 | input_report_key(input, BTN_TOUCH, | 1559 | input_report_key(input, BTN_TOUCH, |
| 1533 | wacom_wac->hid_data.tipswitch); | 1560 | wacom_wac->hid_data.tipswitch); |
| 1534 | input_report_key(input, wacom_wac->tool[0], prox); | 1561 | input_report_key(input, wacom_wac->tool[0], prox); |
| @@ -1544,13 +1571,11 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, | |||
| 1544 | { | 1571 | { |
| 1545 | struct wacom *wacom = hid_get_drvdata(hdev); | 1572 | struct wacom *wacom = hid_get_drvdata(hdev); |
| 1546 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | 1573 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; |
| 1547 | struct wacom_features *features = &wacom_wac->features; | ||
| 1548 | struct input_dev *input = wacom_wac->touch_input; | 1574 | struct input_dev *input = wacom_wac->touch_input; |
| 1549 | unsigned touch_max = wacom_wac->features.touch_max; | 1575 | unsigned touch_max = wacom_wac->features.touch_max; |
| 1550 | 1576 | ||
| 1551 | switch (usage->hid) { | 1577 | switch (usage->hid) { |
| 1552 | case HID_GD_X: | 1578 | case HID_GD_X: |
| 1553 | features->last_slot_field = usage->hid; | ||
| 1554 | if (touch_max == 1) | 1579 | if (touch_max == 1) |
| 1555 | wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4); | 1580 | wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4); |
| 1556 | else | 1581 | else |
| @@ -1558,7 +1583,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, | |||
| 1558 | ABS_MT_POSITION_X, 4); | 1583 | ABS_MT_POSITION_X, 4); |
| 1559 | break; | 1584 | break; |
| 1560 | case HID_GD_Y: | 1585 | case HID_GD_Y: |
| 1561 | features->last_slot_field = usage->hid; | ||
| 1562 | if (touch_max == 1) | 1586 | if (touch_max == 1) |
| 1563 | wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4); | 1587 | wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4); |
| 1564 | else | 1588 | else |
| @@ -1567,22 +1591,11 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, | |||
| 1567 | break; | 1591 | break; |
| 1568 | case HID_DG_WIDTH: | 1592 | case HID_DG_WIDTH: |
| 1569 | case HID_DG_HEIGHT: | 1593 | case HID_DG_HEIGHT: |
| 1570 | features->last_slot_field = usage->hid; | ||
| 1571 | wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); | 1594 | wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); |
| 1572 | wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MINOR, 0); | 1595 | wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MINOR, 0); |
| 1573 | input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); | 1596 | input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); |
| 1574 | break; | 1597 | break; |
| 1575 | case HID_DG_CONTACTID: | ||
| 1576 | features->last_slot_field = usage->hid; | ||
| 1577 | break; | ||
| 1578 | case HID_DG_INRANGE: | ||
| 1579 | features->last_slot_field = usage->hid; | ||
| 1580 | break; | ||
| 1581 | case HID_DG_INVERT: | ||
| 1582 | features->last_slot_field = usage->hid; | ||
| 1583 | break; | ||
| 1584 | case HID_DG_TIPSWITCH: | 1598 | case HID_DG_TIPSWITCH: |
| 1585 | features->last_slot_field = usage->hid; | ||
| 1586 | wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0); | 1599 | wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0); |
| 1587 | break; | 1600 | break; |
| 1588 | case HID_DG_CONTACTCOUNT: | 1601 | case HID_DG_CONTACTCOUNT: |
| @@ -1599,7 +1612,7 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac, | |||
| 1599 | struct hid_data *hid_data = &wacom_wac->hid_data; | 1612 | struct hid_data *hid_data = &wacom_wac->hid_data; |
| 1600 | bool mt = wacom_wac->features.touch_max > 1; | 1613 | bool mt = wacom_wac->features.touch_max > 1; |
| 1601 | bool prox = hid_data->tipswitch && | 1614 | bool prox = hid_data->tipswitch && |
| 1602 | !wacom_wac->shared->stylus_in_proximity; | 1615 | report_touch_events(wacom_wac); |
| 1603 | 1616 | ||
| 1604 | wacom_wac->hid_data.num_received++; | 1617 | wacom_wac->hid_data.num_received++; |
| 1605 | if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected) | 1618 | if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected) |
| @@ -1660,7 +1673,7 @@ static int wacom_wac_finger_event(struct hid_device *hdev, | |||
| 1660 | 1673 | ||
| 1661 | 1674 | ||
| 1662 | if (usage->usage_index + 1 == field->report_count) { | 1675 | if (usage->usage_index + 1 == field->report_count) { |
| 1663 | if (usage->hid == wacom_wac->features.last_slot_field) | 1676 | if (usage->hid == wacom_wac->hid_data.last_slot_field) |
| 1664 | wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input); | 1677 | wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input); |
| 1665 | } | 1678 | } |
| 1666 | 1679 | ||
| @@ -1673,31 +1686,35 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev, | |||
| 1673 | struct wacom *wacom = hid_get_drvdata(hdev); | 1686 | struct wacom *wacom = hid_get_drvdata(hdev); |
| 1674 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | 1687 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; |
| 1675 | struct hid_data* hid_data = &wacom_wac->hid_data; | 1688 | struct hid_data* hid_data = &wacom_wac->hid_data; |
| 1689 | int i; | ||
| 1676 | 1690 | ||
| 1677 | if (hid_data->cc_report != 0 && | 1691 | for (i = 0; i < report->maxfield; i++) { |
| 1678 | hid_data->cc_report != report->id) { | 1692 | struct hid_field *field = report->field[i]; |
| 1679 | int i; | 1693 | int j; |
| 1680 | 1694 | ||
| 1681 | hid_data->cc_report = report->id; | 1695 | for (j = 0; j < field->maxusage; j++) { |
| 1682 | hid_data->cc_index = -1; | 1696 | struct hid_usage *usage = &field->usage[j]; |
| 1683 | hid_data->cc_value_index = -1; | 1697 | |
| 1684 | 1698 | switch (usage->hid) { | |
| 1685 | for (i = 0; i < report->maxfield; i++) { | 1699 | case HID_GD_X: |
| 1686 | struct hid_field *field = report->field[i]; | 1700 | case HID_GD_Y: |
| 1687 | int j; | 1701 | case HID_DG_WIDTH: |
| 1688 | 1702 | case HID_DG_HEIGHT: | |
| 1689 | for (j = 0; j < field->maxusage; j++) { | 1703 | case HID_DG_CONTACTID: |
| 1690 | if (field->usage[j].hid == HID_DG_CONTACTCOUNT) { | 1704 | case HID_DG_INRANGE: |
| 1691 | hid_data->cc_index = i; | 1705 | case HID_DG_INVERT: |
| 1692 | hid_data->cc_value_index = j; | 1706 | case HID_DG_TIPSWITCH: |
| 1693 | 1707 | hid_data->last_slot_field = usage->hid; | |
| 1694 | /* break */ | 1708 | break; |
| 1695 | i = report->maxfield; | 1709 | case HID_DG_CONTACTCOUNT: |
| 1696 | j = field->maxusage; | 1710 | hid_data->cc_report = report->id; |
| 1697 | } | 1711 | hid_data->cc_index = i; |
| 1712 | hid_data->cc_value_index = j; | ||
| 1713 | break; | ||
| 1698 | } | 1714 | } |
| 1699 | } | 1715 | } |
| 1700 | } | 1716 | } |
| 1717 | |||
| 1701 | if (hid_data->cc_report != 0 && | 1718 | if (hid_data->cc_report != 0 && |
| 1702 | hid_data->cc_index >= 0) { | 1719 | hid_data->cc_index >= 0) { |
| 1703 | struct hid_field *field = report->field[hid_data->cc_index]; | 1720 | struct hid_field *field = report->field[hid_data->cc_index]; |
| @@ -1740,10 +1757,10 @@ void wacom_wac_usage_mapping(struct hid_device *hdev, | |||
| 1740 | { | 1757 | { |
| 1741 | struct wacom *wacom = hid_get_drvdata(hdev); | 1758 | struct wacom *wacom = hid_get_drvdata(hdev); |
| 1742 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; | 1759 | struct wacom_wac *wacom_wac = &wacom->wacom_wac; |
| 1760 | struct wacom_features *features = &wacom_wac->features; | ||
| 1743 | 1761 | ||
| 1744 | /* currently, only direct devices have proper hid report descriptors */ | 1762 | /* currently, only direct devices have proper hid report descriptors */ |
| 1745 | __set_bit(INPUT_PROP_DIRECT, wacom_wac->pen_input->propbit); | 1763 | features->device_type |= WACOM_DEVICETYPE_DIRECT; |
| 1746 | __set_bit(INPUT_PROP_DIRECT, wacom_wac->touch_input->propbit); | ||
| 1747 | 1764 | ||
| 1748 | if (WACOM_PEN_FIELD(field)) | 1765 | if (WACOM_PEN_FIELD(field)) |
| 1749 | return wacom_wac_pen_usage_mapping(hdev, field, usage); | 1766 | return wacom_wac_pen_usage_mapping(hdev, field, usage); |
| @@ -1825,15 +1842,8 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) | |||
| 1825 | 1842 | ||
| 1826 | for (i = 0; i < 2; i++) { | 1843 | for (i = 0; i < 2; i++) { |
| 1827 | int offset = (data[1] & 0x80) ? (8 * i) : (9 * i); | 1844 | int offset = (data[1] & 0x80) ? (8 * i) : (9 * i); |
| 1828 | bool touch = data[offset + 3] & 0x80; | 1845 | bool touch = report_touch_events(wacom) |
| 1829 | 1846 | && (data[offset + 3] & 0x80); | |
| 1830 | /* | ||
| 1831 | * Touch events need to be disabled while stylus is | ||
| 1832 | * in proximity because user's hand is resting on touchpad | ||
| 1833 | * and sending unwanted events. User expects tablet buttons | ||
| 1834 | * to continue working though. | ||
| 1835 | */ | ||
| 1836 | touch = touch && !wacom->shared->stylus_in_proximity; | ||
| 1837 | 1847 | ||
| 1838 | input_mt_slot(input, i); | 1848 | input_mt_slot(input, i); |
| 1839 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); | 1849 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); |
| @@ -1870,7 +1880,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) | |||
| 1870 | if (slot < 0) | 1880 | if (slot < 0) |
| 1871 | return; | 1881 | return; |
| 1872 | 1882 | ||
| 1873 | touch = touch && !wacom->shared->stylus_in_proximity; | 1883 | touch = touch && report_touch_events(wacom); |
| 1874 | 1884 | ||
| 1875 | input_mt_slot(input, slot); | 1885 | input_mt_slot(input, slot); |
| 1876 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); | 1886 | input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); |
| @@ -1942,7 +1952,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) | |||
| 1942 | } | 1952 | } |
| 1943 | 1953 | ||
| 1944 | /* only update touch if we actually have a touchpad and touch data changed */ | 1954 | /* only update touch if we actually have a touchpad and touch data changed */ |
| 1945 | if (wacom->touch_registered && touch_changed) { | 1955 | if (wacom->touch_input && touch_changed) { |
| 1946 | input_mt_sync_frame(wacom->touch_input); | 1956 | input_mt_sync_frame(wacom->touch_input); |
| 1947 | wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); | 1957 | wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); |
| 1948 | } | 1958 | } |
| @@ -1983,7 +1993,7 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) | |||
| 1983 | } | 1993 | } |
| 1984 | 1994 | ||
| 1985 | wacom->shared->stylus_in_proximity = prox; | 1995 | wacom->shared->stylus_in_proximity = prox; |
| 1986 | if (wacom->shared->touch_down) | 1996 | if (delay_pen_events(wacom)) |
| 1987 | return 0; | 1997 | return 0; |
| 1988 | 1998 | ||
| 1989 | if (prox) { | 1999 | if (prox) { |
| @@ -2077,7 +2087,7 @@ static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom, | |||
| 2077 | 2087 | ||
| 2078 | for (id = 0; id < wacom->features.touch_max; id++) { | 2088 | for (id = 0; id < wacom->features.touch_max; id++) { |
| 2079 | valid = !!(prefix & BIT(id)) && | 2089 | valid = !!(prefix & BIT(id)) && |
| 2080 | !wacom->shared->stylus_in_proximity; | 2090 | report_touch_events(wacom); |
| 2081 | 2091 | ||
| 2082 | input_mt_slot(input, id); | 2092 | input_mt_slot(input, id); |
| 2083 | input_mt_report_slot_state(input, MT_TOOL_FINGER, valid); | 2093 | input_mt_report_slot_state(input, MT_TOOL_FINGER, valid); |
| @@ -2099,8 +2109,7 @@ static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom, | |||
| 2099 | input_report_key(input, BTN_RIGHT, prefix & 0x80); | 2109 | input_report_key(input, BTN_RIGHT, prefix & 0x80); |
| 2100 | 2110 | ||
| 2101 | /* keep touch state for pen event */ | 2111 | /* keep touch state for pen event */ |
| 2102 | wacom->shared->touch_down = !!prefix && | 2112 | wacom->shared->touch_down = !!prefix && report_touch_events(wacom); |
| 2103 | !wacom->shared->stylus_in_proximity; | ||
| 2104 | 2113 | ||
| 2105 | return 1; | 2114 | return 1; |
| 2106 | } | 2115 | } |
| @@ -2149,16 +2158,15 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) | |||
| 2149 | charging = !!(data[5] & 0x80); | 2158 | charging = !!(data[5] & 0x80); |
| 2150 | if (wacom->pid != pid) { | 2159 | if (wacom->pid != pid) { |
| 2151 | wacom->pid = pid; | 2160 | wacom->pid = pid; |
| 2152 | wacom_schedule_work(wacom); | 2161 | wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS); |
| 2153 | } | 2162 | } |
| 2154 | 2163 | ||
| 2155 | if (wacom->shared->type) | 2164 | wacom_notify_battery(wacom, battery, charging, 1, 0); |
| 2156 | wacom_notify_battery(wacom, battery, charging, 1, 0); | ||
| 2157 | 2165 | ||
| 2158 | } else if (wacom->pid != 0) { | 2166 | } else if (wacom->pid != 0) { |
| 2159 | /* disconnected while previously connected */ | 2167 | /* disconnected while previously connected */ |
| 2160 | wacom->pid = 0; | 2168 | wacom->pid = 0; |
| 2161 | wacom_schedule_work(wacom); | 2169 | wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS); |
| 2162 | wacom_notify_battery(wacom, 0, 0, 0, 0); | 2170 | wacom_notify_battery(wacom, 0, 0, 0, 0); |
| 2163 | } | 2171 | } |
| 2164 | 2172 | ||
| @@ -2190,18 +2198,16 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len) | |||
| 2190 | wacom_notify_battery(wacom_wac, battery, charging, | 2198 | wacom_notify_battery(wacom_wac, battery, charging, |
| 2191 | battery || charging, 1); | 2199 | battery || charging, 1); |
| 2192 | 2200 | ||
| 2193 | if (!wacom->battery && | 2201 | if (!wacom->battery.battery && |
| 2194 | !(features->quirks & WACOM_QUIRK_BATTERY)) { | 2202 | !(features->quirks & WACOM_QUIRK_BATTERY)) { |
| 2195 | features->quirks |= WACOM_QUIRK_BATTERY; | 2203 | features->quirks |= WACOM_QUIRK_BATTERY; |
| 2196 | INIT_WORK(&wacom->work, wacom_battery_work); | 2204 | wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY); |
| 2197 | wacom_schedule_work(wacom_wac); | ||
| 2198 | } | 2205 | } |
| 2199 | } | 2206 | } |
| 2200 | else if ((features->quirks & WACOM_QUIRK_BATTERY) && | 2207 | else if ((features->quirks & WACOM_QUIRK_BATTERY) && |
| 2201 | wacom->battery) { | 2208 | wacom->battery.battery) { |
| 2202 | features->quirks &= ~WACOM_QUIRK_BATTERY; | 2209 | features->quirks &= ~WACOM_QUIRK_BATTERY; |
| 2203 | INIT_WORK(&wacom->work, wacom_battery_work); | 2210 | wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY); |
| 2204 | wacom_schedule_work(wacom_wac); | ||
| 2205 | wacom_notify_battery(wacom_wac, 0, 0, 0, 0); | 2211 | wacom_notify_battery(wacom_wac, 0, 0, 0, 0); |
| 2206 | } | 2212 | } |
| 2207 | return 0; | 2213 | return 0; |
| @@ -2312,8 +2318,9 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) | |||
| 2312 | break; | 2318 | break; |
| 2313 | 2319 | ||
| 2314 | case REMOTE: | 2320 | case REMOTE: |
| 2321 | sync = false; | ||
| 2315 | if (wacom_wac->data[0] == WACOM_REPORT_DEVICE_LIST) | 2322 | if (wacom_wac->data[0] == WACOM_REPORT_DEVICE_LIST) |
| 2316 | sync = wacom_remote_status_irq(wacom_wac, len); | 2323 | wacom_remote_status_irq(wacom_wac, len); |
| 2317 | else | 2324 | else |
| 2318 | sync = wacom_remote_irq(wacom_wac, len); | 2325 | sync = wacom_remote_irq(wacom_wac, len); |
| 2319 | break; | 2326 | break; |
| @@ -2451,6 +2458,33 @@ void wacom_setup_device_quirks(struct wacom *wacom) | |||
| 2451 | if (features->type == REMOTE) | 2458 | if (features->type == REMOTE) |
| 2452 | features->device_type = WACOM_DEVICETYPE_PAD; | 2459 | features->device_type = WACOM_DEVICETYPE_PAD; |
| 2453 | 2460 | ||
| 2461 | switch (features->type) { | ||
| 2462 | case PL: | ||
| 2463 | case DTU: | ||
| 2464 | case DTUS: | ||
| 2465 | case DTUSX: | ||
| 2466 | case WACOM_21UX2: | ||
| 2467 | case WACOM_22HD: | ||
| 2468 | case DTK: | ||
| 2469 | case WACOM_24HD: | ||
| 2470 | case WACOM_27QHD: | ||
| 2471 | case CINTIQ_HYBRID: | ||
| 2472 | case CINTIQ_COMPANION_2: | ||
| 2473 | case CINTIQ: | ||
| 2474 | case WACOM_BEE: | ||
| 2475 | case WACOM_13HD: | ||
| 2476 | case WACOM_24HDT: | ||
| 2477 | case WACOM_27QHDT: | ||
| 2478 | case TABLETPC: | ||
| 2479 | case TABLETPCE: | ||
| 2480 | case TABLETPC2FG: | ||
| 2481 | case MTSCREEN: | ||
| 2482 | case MTTPC: | ||
| 2483 | case MTTPC_B: | ||
| 2484 | features->device_type |= WACOM_DEVICETYPE_DIRECT; | ||
| 2485 | break; | ||
| 2486 | } | ||
| 2487 | |||
| 2454 | if (wacom->hdev->bus == BUS_BLUETOOTH) | 2488 | if (wacom->hdev->bus == BUS_BLUETOOTH) |
| 2455 | features->quirks |= WACOM_QUIRK_BATTERY; | 2489 | features->quirks |= WACOM_QUIRK_BATTERY; |
| 2456 | 2490 | ||
| @@ -2469,6 +2503,9 @@ void wacom_setup_device_quirks(struct wacom *wacom) | |||
| 2469 | features->quirks |= WACOM_QUIRK_BATTERY; | 2503 | features->quirks |= WACOM_QUIRK_BATTERY; |
| 2470 | } | 2504 | } |
| 2471 | } | 2505 | } |
| 2506 | |||
| 2507 | if (features->type == REMOTE) | ||
| 2508 | features->device_type |= WACOM_DEVICETYPE_WL_MONITOR; | ||
| 2472 | } | 2509 | } |
| 2473 | 2510 | ||
| 2474 | int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | 2511 | int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, |
| @@ -2481,6 +2518,11 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2481 | if (!(features->device_type & WACOM_DEVICETYPE_PEN)) | 2518 | if (!(features->device_type & WACOM_DEVICETYPE_PEN)) |
| 2482 | return -ENODEV; | 2519 | return -ENODEV; |
| 2483 | 2520 | ||
| 2521 | if (features->device_type & WACOM_DEVICETYPE_DIRECT) | ||
| 2522 | __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); | ||
| 2523 | else | ||
| 2524 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2525 | |||
| 2484 | if (features->type == HID_GENERIC) | 2526 | if (features->type == HID_GENERIC) |
| 2485 | /* setup has already been done */ | 2527 | /* setup has already been done */ |
| 2486 | return 0; | 2528 | return 0; |
| @@ -2499,7 +2541,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2499 | input_abs_set_res(input_dev, ABS_X, features->x_resolution); | 2541 | input_abs_set_res(input_dev, ABS_X, features->x_resolution); |
| 2500 | input_abs_set_res(input_dev, ABS_Y, features->y_resolution); | 2542 | input_abs_set_res(input_dev, ABS_Y, features->y_resolution); |
| 2501 | 2543 | ||
| 2502 | |||
| 2503 | switch (features->type) { | 2544 | switch (features->type) { |
| 2504 | case GRAPHIRE_BT: | 2545 | case GRAPHIRE_BT: |
| 2505 | __clear_bit(ABS_MISC, input_dev->absbit); | 2546 | __clear_bit(ABS_MISC, input_dev->absbit); |
| @@ -2523,8 +2564,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2523 | __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); | 2564 | __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); |
| 2524 | __set_bit(BTN_STYLUS, input_dev->keybit); | 2565 | __set_bit(BTN_STYLUS, input_dev->keybit); |
| 2525 | __set_bit(BTN_STYLUS2, input_dev->keybit); | 2566 | __set_bit(BTN_STYLUS2, input_dev->keybit); |
| 2526 | |||
| 2527 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2528 | break; | 2567 | break; |
| 2529 | 2568 | ||
| 2530 | case WACOM_27QHD: | 2569 | case WACOM_27QHD: |
| @@ -2539,7 +2578,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2539 | case CINTIQ_COMPANION_2: | 2578 | case CINTIQ_COMPANION_2: |
| 2540 | input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); | 2579 | input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); |
| 2541 | input_abs_set_res(input_dev, ABS_Z, 287); | 2580 | input_abs_set_res(input_dev, ABS_Z, 287); |
| 2542 | __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); | ||
| 2543 | wacom_setup_cintiq(wacom_wac); | 2581 | wacom_setup_cintiq(wacom_wac); |
| 2544 | break; | 2582 | break; |
| 2545 | 2583 | ||
| @@ -2555,8 +2593,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2555 | /* fall through */ | 2593 | /* fall through */ |
| 2556 | 2594 | ||
| 2557 | case INTUOS: | 2595 | case INTUOS: |
| 2558 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2559 | |||
| 2560 | wacom_setup_intuos(wacom_wac); | 2596 | wacom_setup_intuos(wacom_wac); |
| 2561 | break; | 2597 | break; |
| 2562 | 2598 | ||
| @@ -2566,8 +2602,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2566 | case INTUOSPL: | 2602 | case INTUOSPL: |
| 2567 | case INTUOS5S: | 2603 | case INTUOS5S: |
| 2568 | case INTUOSPS: | 2604 | case INTUOSPS: |
| 2569 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2570 | |||
| 2571 | input_set_abs_params(input_dev, ABS_DISTANCE, 0, | 2605 | input_set_abs_params(input_dev, ABS_DISTANCE, 0, |
| 2572 | features->distance_max, | 2606 | features->distance_max, |
| 2573 | features->distance_fuzz, 0); | 2607 | features->distance_fuzz, 0); |
| @@ -2597,8 +2631,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2597 | __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); | 2631 | __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); |
| 2598 | __set_bit(BTN_STYLUS, input_dev->keybit); | 2632 | __set_bit(BTN_STYLUS, input_dev->keybit); |
| 2599 | __set_bit(BTN_STYLUS2, input_dev->keybit); | 2633 | __set_bit(BTN_STYLUS2, input_dev->keybit); |
| 2600 | |||
| 2601 | __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); | ||
| 2602 | break; | 2634 | break; |
| 2603 | 2635 | ||
| 2604 | case PTU: | 2636 | case PTU: |
| @@ -2609,16 +2641,12 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, | |||
| 2609 | __set_bit(BTN_TOOL_PEN, input_dev->keybit); | 2641 | __set_bit(BTN_TOOL_PEN, input_dev->keybit); |
| 2610 | __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); | 2642 | __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); |
| 2611 | __set_bit(BTN_STYLUS, input_dev->keybit); | 2643 | __set_bit(BTN_STYLUS, input_dev->keybit); |
| 2612 | |||
| 2613 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2614 | break; | 2644 | break; |
| 2615 | 2645 | ||
| 2616 | case INTUOSHT: | 2646 | case INTUOSHT: |
| 2617 | case BAMBOO_PT: | 2647 | case BAMBOO_PT: |
| 2618 | case BAMBOO_PEN: | 2648 | case BAMBOO_PEN: |
| 2619 | case INTUOSHT2: | 2649 | case INTUOSHT2: |
| 2620 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2621 | |||
| 2622 | if (features->type == INTUOSHT2) { | 2650 | if (features->type == INTUOSHT2) { |
| 2623 | wacom_setup_basic_pro_pen(wacom_wac); | 2651 | wacom_setup_basic_pro_pen(wacom_wac); |
| 2624 | } else { | 2652 | } else { |
| @@ -2649,6 +2677,11 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, | |||
| 2649 | if (!(features->device_type & WACOM_DEVICETYPE_TOUCH)) | 2677 | if (!(features->device_type & WACOM_DEVICETYPE_TOUCH)) |
| 2650 | return -ENODEV; | 2678 | return -ENODEV; |
| 2651 | 2679 | ||
| 2680 | if (features->device_type & WACOM_DEVICETYPE_DIRECT) | ||
| 2681 | __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); | ||
| 2682 | else | ||
| 2683 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2684 | |||
| 2652 | if (features->type == HID_GENERIC) | 2685 | if (features->type == HID_GENERIC) |
| 2653 | /* setup has already been done */ | 2686 | /* setup has already been done */ |
| 2654 | return 0; | 2687 | return 0; |
| @@ -2683,8 +2716,6 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, | |||
| 2683 | case INTUOSPL: | 2716 | case INTUOSPL: |
| 2684 | case INTUOS5S: | 2717 | case INTUOS5S: |
| 2685 | case INTUOSPS: | 2718 | case INTUOSPS: |
| 2686 | __set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
| 2687 | |||
| 2688 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); | 2719 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); |
| 2689 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0); | 2720 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0); |
| 2690 | input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); | 2721 | input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); |
| @@ -2707,7 +2738,6 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, | |||
| 2707 | 2738 | ||
| 2708 | case TABLETPC: | 2739 | case TABLETPC: |
| 2709 | case TABLETPCE: | 2740 | case TABLETPCE: |
| 2710 | __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); | ||
| 2711 | break; | 2741 | break; |
| 2712 | 2742 | ||
| 2713 | case INTUOSHT: | 2743 | case INTUOSHT: |
| @@ -2752,11 +2782,105 @@ static void wacom_setup_numbered_buttons(struct input_dev *input_dev, | |||
| 2752 | __set_bit(BTN_BASE + (i-16), input_dev->keybit); | 2782 | __set_bit(BTN_BASE + (i-16), input_dev->keybit); |
| 2753 | } | 2783 | } |
| 2754 | 2784 | ||
| 2785 | static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group) | ||
| 2786 | { | ||
| 2787 | struct wacom_led *led; | ||
| 2788 | int i; | ||
| 2789 | bool updated = false; | ||
| 2790 | |||
| 2791 | /* | ||
| 2792 | * 24HD has LED group 1 to the left and LED group 0 to the right. | ||
| 2793 | * So group 0 matches the second half of the buttons and thus the mask | ||
| 2794 | * needs to be shifted. | ||
| 2795 | */ | ||
| 2796 | if (group == 0) | ||
| 2797 | mask >>= 8; | ||
| 2798 | |||
| 2799 | for (i = 0; i < 3; i++) { | ||
| 2800 | led = wacom_led_find(wacom, group, i); | ||
| 2801 | if (!led) { | ||
| 2802 | hid_err(wacom->hdev, "can't find LED %d in group %d\n", | ||
| 2803 | i, group); | ||
| 2804 | continue; | ||
| 2805 | } | ||
| 2806 | if (!updated && mask & BIT(i)) { | ||
| 2807 | led->held = true; | ||
| 2808 | led_trigger_event(&led->trigger, LED_FULL); | ||
| 2809 | } else { | ||
| 2810 | led->held = false; | ||
| 2811 | } | ||
| 2812 | } | ||
| 2813 | } | ||
| 2814 | |||
| 2815 | static bool wacom_is_led_toggled(struct wacom *wacom, int button_count, | ||
| 2816 | int mask, int group) | ||
| 2817 | { | ||
| 2818 | int button_per_group; | ||
| 2819 | |||
| 2820 | /* | ||
| 2821 | * 21UX2 has LED group 1 to the left and LED group 0 | ||
| 2822 | * to the right. We need to reverse the group to match this | ||
| 2823 | * historical behavior. | ||
| 2824 | */ | ||
| 2825 | if (wacom->wacom_wac.features.type == WACOM_21UX2) | ||
| 2826 | group = 1 - group; | ||
| 2827 | |||
| 2828 | button_per_group = button_count/wacom->led.count; | ||
| 2829 | |||
| 2830 | return mask & (1 << (group * button_per_group)); | ||
| 2831 | } | ||
| 2832 | |||
| 2833 | static void wacom_update_led(struct wacom *wacom, int button_count, int mask, | ||
| 2834 | int group) | ||
| 2835 | { | ||
| 2836 | struct wacom_led *led, *next_led; | ||
| 2837 | int cur; | ||
| 2838 | bool pressed; | ||
| 2839 | |||
| 2840 | if (wacom->wacom_wac.features.type == WACOM_24HD) | ||
| 2841 | return wacom_24hd_update_leds(wacom, mask, group); | ||
| 2842 | |||
| 2843 | pressed = wacom_is_led_toggled(wacom, button_count, mask, group); | ||
| 2844 | cur = wacom->led.groups[group].select; | ||
| 2845 | |||
| 2846 | led = wacom_led_find(wacom, group, cur); | ||
| 2847 | if (!led) { | ||
| 2848 | hid_err(wacom->hdev, "can't find current LED %d in group %d\n", | ||
| 2849 | cur, group); | ||
| 2850 | return; | ||
| 2851 | } | ||
| 2852 | |||
| 2853 | if (!pressed) { | ||
| 2854 | led->held = false; | ||
| 2855 | return; | ||
| 2856 | } | ||
| 2857 | |||
| 2858 | if (led->held && pressed) | ||
| 2859 | return; | ||
| 2860 | |||
| 2861 | next_led = wacom_led_next(wacom, led); | ||
| 2862 | if (!next_led) { | ||
| 2863 | hid_err(wacom->hdev, "can't find next LED in group %d\n", | ||
| 2864 | group); | ||
| 2865 | return; | ||
| 2866 | } | ||
| 2867 | if (next_led == led) | ||
| 2868 | return; | ||
| 2869 | |||
| 2870 | next_led->held = true; | ||
| 2871 | led_trigger_event(&next_led->trigger, | ||
| 2872 | wacom_leds_brightness_get(next_led)); | ||
| 2873 | } | ||
| 2874 | |||
| 2755 | static void wacom_report_numbered_buttons(struct input_dev *input_dev, | 2875 | static void wacom_report_numbered_buttons(struct input_dev *input_dev, |
| 2756 | int button_count, int mask) | 2876 | int button_count, int mask) |
| 2757 | { | 2877 | { |
| 2878 | struct wacom *wacom = input_get_drvdata(input_dev); | ||
| 2758 | int i; | 2879 | int i; |
| 2759 | 2880 | ||
| 2881 | for (i = 0; i < wacom->led.count; i++) | ||
| 2882 | wacom_update_led(wacom, button_count, mask, i); | ||
| 2883 | |||
| 2760 | for (i = 0; i < button_count && i < 10; i++) | 2884 | for (i = 0; i < button_count && i < 10; i++) |
| 2761 | input_report_key(input_dev, BTN_0 + i, mask & (1 << i)); | 2885 | input_report_key(input_dev, BTN_0 + i, mask & (1 << i)); |
| 2762 | for (i = 10; i < button_count && i < 16; i++) | 2886 | for (i = 10; i < button_count && i < 16; i++) |
| @@ -2773,6 +2897,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, | |||
| 2773 | if (!(features->device_type & WACOM_DEVICETYPE_PAD)) | 2897 | if (!(features->device_type & WACOM_DEVICETYPE_PAD)) |
| 2774 | return -ENODEV; | 2898 | return -ENODEV; |
| 2775 | 2899 | ||
| 2900 | if (features->type == REMOTE && input_dev == wacom_wac->pad_input) | ||
| 2901 | return -ENODEV; | ||
| 2902 | |||
| 2776 | input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | 2903 | input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
| 2777 | 2904 | ||
| 2778 | /* kept for making legacy xf86-input-wacom working with the wheels */ | 2905 | /* kept for making legacy xf86-input-wacom working with the wheels */ |
| @@ -3403,7 +3530,7 @@ static const struct wacom_features wacom_features_0x343 = | |||
| 3403 | WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; | 3530 | WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; |
| 3404 | 3531 | ||
| 3405 | static const struct wacom_features wacom_features_HID_ANY_ID = | 3532 | static const struct wacom_features wacom_features_HID_ANY_ID = |
| 3406 | { "Wacom HID", .type = HID_GENERIC }; | 3533 | { "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID }; |
| 3407 | 3534 | ||
| 3408 | #define USB_DEVICE_WACOM(prod) \ | 3535 | #define USB_DEVICE_WACOM(prod) \ |
| 3409 | HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ | 3536 | HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ |
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 53d16537fd2a..324c40b0c119 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h | |||
| @@ -82,6 +82,7 @@ | |||
| 82 | #define WACOM_DEVICETYPE_TOUCH 0x0002 | 82 | #define WACOM_DEVICETYPE_TOUCH 0x0002 |
| 83 | #define WACOM_DEVICETYPE_PAD 0x0004 | 83 | #define WACOM_DEVICETYPE_PAD 0x0004 |
| 84 | #define WACOM_DEVICETYPE_WL_MONITOR 0x0008 | 84 | #define WACOM_DEVICETYPE_WL_MONITOR 0x0008 |
| 85 | #define WACOM_DEVICETYPE_DIRECT 0x0010 | ||
| 85 | 86 | ||
| 86 | #define WACOM_VENDORDEFINED_PEN 0xff0d0001 | 87 | #define WACOM_VENDORDEFINED_PEN 0xff0d0001 |
| 87 | #define WACOM_G9_PAGE 0xff090000 | 88 | #define WACOM_G9_PAGE 0xff090000 |
| @@ -185,7 +186,6 @@ struct wacom_features { | |||
| 185 | int pktlen; | 186 | int pktlen; |
| 186 | bool check_for_hid_type; | 187 | bool check_for_hid_type; |
| 187 | int hid_type; | 188 | int hid_type; |
| 188 | int last_slot_field; | ||
| 189 | }; | 189 | }; |
| 190 | 190 | ||
| 191 | struct wacom_shared { | 191 | struct wacom_shared { |
| @@ -214,35 +214,35 @@ struct hid_data { | |||
| 214 | int cc_report; | 214 | int cc_report; |
| 215 | int cc_index; | 215 | int cc_index; |
| 216 | int cc_value_index; | 216 | int cc_value_index; |
| 217 | int last_slot_field; | ||
| 217 | int num_expected; | 218 | int num_expected; |
| 218 | int num_received; | 219 | int num_received; |
| 219 | }; | 220 | }; |
| 220 | 221 | ||
| 222 | struct wacom_remote_data { | ||
| 223 | struct { | ||
| 224 | u32 serial; | ||
| 225 | bool connected; | ||
| 226 | } remote[WACOM_MAX_REMOTES]; | ||
| 227 | }; | ||
| 228 | |||
| 221 | struct wacom_wac { | 229 | struct wacom_wac { |
| 230 | char name[WACOM_NAME_MAX]; | ||
| 222 | char pen_name[WACOM_NAME_MAX]; | 231 | char pen_name[WACOM_NAME_MAX]; |
| 223 | char touch_name[WACOM_NAME_MAX]; | 232 | char touch_name[WACOM_NAME_MAX]; |
| 224 | char pad_name[WACOM_NAME_MAX]; | 233 | char pad_name[WACOM_NAME_MAX]; |
| 225 | char bat_name[WACOM_NAME_MAX]; | ||
| 226 | char ac_name[WACOM_NAME_MAX]; | ||
| 227 | unsigned char data[WACOM_PKGLEN_MAX]; | 234 | unsigned char data[WACOM_PKGLEN_MAX]; |
| 228 | int tool[2]; | 235 | int tool[2]; |
| 229 | int id[2]; | 236 | int id[2]; |
| 230 | __u32 serial[5]; | 237 | __u32 serial[2]; |
| 231 | bool reporting_data; | 238 | bool reporting_data; |
| 232 | struct wacom_features features; | 239 | struct wacom_features features; |
| 233 | struct wacom_shared *shared; | 240 | struct wacom_shared *shared; |
| 234 | struct input_dev *pen_input; | 241 | struct input_dev *pen_input; |
| 235 | struct input_dev *touch_input; | 242 | struct input_dev *touch_input; |
| 236 | struct input_dev *pad_input; | 243 | struct input_dev *pad_input; |
| 237 | bool pen_registered; | ||
| 238 | bool touch_registered; | ||
| 239 | bool pad_registered; | ||
| 240 | int pid; | 244 | int pid; |
| 241 | int battery_capacity; | ||
| 242 | int num_contacts_left; | 245 | int num_contacts_left; |
| 243 | int bat_charging; | ||
| 244 | int bat_connected; | ||
| 245 | int ps_connected; | ||
| 246 | u8 bt_features; | 246 | u8 bt_features; |
| 247 | u8 bt_high_speed; | 247 | u8 bt_high_speed; |
| 248 | int mode_report; | 248 | int mode_report; |
diff --git a/include/linux/hid.h b/include/linux/hid.h index 75b66eccc692..b2ec82712baa 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
| @@ -837,7 +837,7 @@ __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, | |||
| 837 | */ | 837 | */ |
| 838 | static inline void hid_device_io_start(struct hid_device *hid) { | 838 | static inline void hid_device_io_start(struct hid_device *hid) { |
| 839 | if (hid->io_started) { | 839 | if (hid->io_started) { |
| 840 | dev_warn(&hid->dev, "io already started"); | 840 | dev_warn(&hid->dev, "io already started\n"); |
| 841 | return; | 841 | return; |
| 842 | } | 842 | } |
| 843 | hid->io_started = true; | 843 | hid->io_started = true; |
| @@ -857,7 +857,7 @@ static inline void hid_device_io_start(struct hid_device *hid) { | |||
| 857 | */ | 857 | */ |
| 858 | static inline void hid_device_io_stop(struct hid_device *hid) { | 858 | static inline void hid_device_io_stop(struct hid_device *hid) { |
| 859 | if (!hid->io_started) { | 859 | if (!hid->io_started) { |
| 860 | dev_warn(&hid->dev, "io already stopped"); | 860 | dev_warn(&hid->dev, "io already stopped\n"); |
| 861 | return; | 861 | return; |
| 862 | } | 862 | } |
| 863 | hid->io_started = false; | 863 | hid->io_started = false; |
diff --git a/include/trace/events/intel_ish.h b/include/trace/events/intel_ish.h new file mode 100644 index 000000000000..92f7d5b23177 --- /dev/null +++ b/include/trace/events/intel_ish.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #undef TRACE_SYSTEM | ||
| 2 | #define TRACE_SYSTEM intel_ish | ||
| 3 | |||
| 4 | #if !defined(_TRACE_INTEL_ISH_H) || defined(TRACE_HEADER_MULTI_READ) | ||
| 5 | #define _TRACE_INTEL_ISH_H | ||
| 6 | |||
| 7 | #include <linux/tracepoint.h> | ||
| 8 | |||
| 9 | TRACE_EVENT(ishtp_dump, | ||
| 10 | |||
| 11 | TP_PROTO(const char *message), | ||
| 12 | |||
| 13 | TP_ARGS(message), | ||
| 14 | |||
| 15 | TP_STRUCT__entry( | ||
| 16 | __string(message, message) | ||
| 17 | ), | ||
| 18 | |||
| 19 | TP_fast_assign( | ||
| 20 | __assign_str(message, message); | ||
| 21 | ), | ||
| 22 | |||
| 23 | TP_printk("%s", __get_str(message)) | ||
| 24 | ); | ||
| 25 | |||
| 26 | |||
| 27 | #endif /* _TRACE_INTEL_ISH_H */ | ||
| 28 | |||
| 29 | /* This part must be outside protection */ | ||
| 30 | #include <trace/define_trace.h> | ||
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index c51494119817..e794f7bee22f 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h | |||
| @@ -248,6 +248,7 @@ struct input_mask { | |||
| 248 | #define BUS_SPI 0x1C | 248 | #define BUS_SPI 0x1C |
| 249 | #define BUS_RMI 0x1D | 249 | #define BUS_RMI 0x1D |
| 250 | #define BUS_CEC 0x1E | 250 | #define BUS_CEC 0x1E |
| 251 | #define BUS_INTEL_ISHTP 0x1F | ||
| 251 | 252 | ||
| 252 | /* | 253 | /* |
| 253 | * MT_TOOL types | 254 | * MT_TOOL types |
