Blame view

drivers/hid/hid-rmi.c 18.9 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
2
3
4
5
6
  /*
   *  Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com>
   *  Copyright (c) 2013 Synaptics Incorporated
   *  Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com>
   *  Copyright (c) 2014 Red Hat, Inc
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
7
8
9
10
11
12
   */
  
  #include <linux/kernel.h>
  #include <linux/hid.h>
  #include <linux/input.h>
  #include <linux/input/mt.h>
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
13
14
  #include <linux/irq.h>
  #include <linux/irqdomain.h>
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
15
16
17
18
19
  #include <linux/module.h>
  #include <linux/pm.h>
  #include <linux/slab.h>
  #include <linux/wait.h>
  #include <linux/sched.h>
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
20
  #include <linux/rmi.h>
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
21
22
23
24
25
26
27
28
29
30
  #include "hid-ids.h"
  
  #define RMI_MOUSE_REPORT_ID		0x01 /* Mouse emulation Report */
  #define RMI_WRITE_REPORT_ID		0x09 /* Output Report */
  #define RMI_READ_ADDR_REPORT_ID		0x0a /* Output Report */
  #define RMI_READ_DATA_REPORT_ID		0x0b /* Input Report */
  #define RMI_ATTN_REPORT_ID		0x0c /* Input Report */
  #define RMI_SET_RMI_MODE_REPORT_ID	0x0f /* Feature Report */
  
  /* flags */
af43c4086   Dan Carpenter   HID: rmi: fix som...
31
32
33
  #define RMI_READ_REQUEST_PENDING	0
  #define RMI_READ_DATA_PENDING		1
  #define RMI_STARTED			2
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
34

2f43de605   Andrew Duggan   HID: rmi: Support...
35
36
  /* device flags */
  #define RMI_DEVICE			BIT(0)
79364d87a   Andrew Duggan   HID: rmi: Support...
37
  #define RMI_DEVICE_HAS_PHYS_BUTTONS	BIT(1)
10235380d   Tobias Auerochs   HID: rmi: Use SET...
38
  #define RMI_DEVICE_OUTPUT_SET_REPORT	BIT(2)
2f43de605   Andrew Duggan   HID: rmi: Support...
39

7035f3a4e   Andrew Duggan   HID: rmi: Write u...
40
41
42
43
44
45
46
  /*
   * retrieve the ctrl registers
   * the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
   * and there is no way to know if the first 20 bytes are here or not.
   * We use only the first 12 bytes, so get only them.
   */
  #define RMI_F11_CTRL_REG_COUNT		12
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
47
48
49
50
51
  enum rmi_mode_type {
  	RMI_MODE_OFF			= 0,
  	RMI_MODE_ATTN_REPORTS		= 1,
  	RMI_MODE_NO_PACKED_ATTN_REPORTS	= 2,
  };
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
52
53
54
55
56
  /**
   * struct rmi_data - stores information for hid communication
   *
   * @page_mutex: Locks current page to avoid changing pages in unexpected ways.
   * @page: Keeps track of the current virtual page
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
57
   * @xport: transport device to be registered with the RMI4 core.
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
58
59
60
61
62
63
64
65
66
67
68
   *
   * @wait: Used for waiting for read data
   *
   * @writeReport: output buffer when writing RMI registers
   * @readReport: input buffer when reading RMI registers
   *
   * @input_report_size: size of an input report (advertised by HID)
   * @output_report_size: size of an output report (advertised by HID)
   *
   * @flags: flags for the current device (started, reading, etc...)
   *
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
69
70
   * @reset_work: worker which will be called in case of a mouse report
   * @hdev: pointer to the struct hid_device
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
71
72
73
74
75
   *
   * @device_flags: flags which describe the device
   *
   * @domain: the IRQ domain allocated for this RMI4 device
   * @rmi_irq: the irq that will be used to generate events to rmi-core
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
76
77
78
79
   */
  struct rmi_data {
  	struct mutex page_mutex;
  	int page;
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
80
  	struct rmi_transport_dev xport;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
81
82
83
84
85
  
  	wait_queue_head_t wait;
  
  	u8 *writeReport;
  	u8 *readReport;
3064a03b9   Aaron Ma   HID: Fix hid_repo...
86
87
  	u32 input_report_size;
  	u32 output_report_size;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
88
89
  
  	unsigned long flags;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
90
91
  	struct work_struct reset_work;
  	struct hid_device *hdev;
2f43de605   Andrew Duggan   HID: rmi: Support...
92
93
  
  	unsigned long device_flags;
092563604   Andrew Duggan   HID: rmi: Disable...
94

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
95
96
  	struct irq_domain *domain;
  	int rmi_irq;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
  };
  
  #define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
  
  static int rmi_write_report(struct hid_device *hdev, u8 *report, int len);
  
  /**
   * rmi_set_page - Set RMI page
   * @hdev: The pointer to the hid_device struct
   * @page: The new page address.
   *
   * RMI devices have 16-bit addressing, but some of the physical
   * implementations (like SMBus) only have 8-bit addressing. So RMI implements
   * a page address at 0xff of every page so we can reliable page addresses
   * every 256 registers.
   *
   * The page_mutex lock must be held when this function is entered.
   *
   * Returns zero on success, non-zero on failure.
   */
  static int rmi_set_page(struct hid_device *hdev, u8 page)
  {
  	struct rmi_data *data = hid_get_drvdata(hdev);
  	int retval;
  
  	data->writeReport[0] = RMI_WRITE_REPORT_ID;
  	data->writeReport[1] = 1;
  	data->writeReport[2] = 0xFF;
  	data->writeReport[4] = page;
  
  	retval = rmi_write_report(hdev, data->writeReport,
  			data->output_report_size);
  	if (retval != data->output_report_size) {
  		dev_err(&hdev->dev,
  			"%s: set page failed: %d.", __func__, retval);
  		return retval;
  	}
  
  	data->page = page;
  	return 0;
  }
  
  static int rmi_set_mode(struct hid_device *hdev, u8 mode)
  {
  	int ret;
6dab07df5   Benjamin Tissoires   HID: rmi: make tr...
142
143
  	const u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode};
  	u8 *buf;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
144

6dab07df5   Benjamin Tissoires   HID: rmi: make tr...
145
146
147
148
149
  	buf = kmemdup(txbuf, sizeof(txbuf), GFP_KERNEL);
  	if (!buf)
  		return -ENOMEM;
  
  	ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, buf,
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
150
  			sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
6dab07df5   Benjamin Tissoires   HID: rmi: make tr...
151
  	kfree(buf);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
152
153
154
155
156
157
158
159
160
161
162
163
  	if (ret < 0) {
  		dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)
  ", mode,
  			ret);
  		return ret;
  	}
  
  	return 0;
  }
  
  static int rmi_write_report(struct hid_device *hdev, u8 *report, int len)
  {
10235380d   Tobias Auerochs   HID: rmi: Use SET...
164
  	struct rmi_data *data = hid_get_drvdata(hdev);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
165
  	int ret;
10235380d   Tobias Auerochs   HID: rmi: Use SET...
166
167
168
169
170
171
172
173
174
  	if (data->device_flags & RMI_DEVICE_OUTPUT_SET_REPORT) {
  		/*
  		 * Talk to device by using SET_REPORT requests instead.
  		 */
  		ret = hid_hw_raw_request(hdev, report[0], report,
  				len, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
  	} else {
  		ret = hid_hw_output_report(hdev, (void *)report, len);
  	}
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
175
176
177
178
179
180
181
182
  	if (ret < 0) {
  		dev_err(&hdev->dev, "failed to write hid report (%d)
  ", ret);
  		return ret;
  	}
  
  	return ret;
  }
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
183
184
  static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr,
  		void *buf, size_t len)
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
185
  {
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
186
187
  	struct rmi_data *data = container_of(xport, struct rmi_data, xport);
  	struct hid_device *hdev = data->hdev;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  	int ret;
  	int bytes_read;
  	int bytes_needed;
  	int retries;
  	int read_input_count;
  
  	mutex_lock(&data->page_mutex);
  
  	if (RMI_PAGE(addr) != data->page) {
  		ret = rmi_set_page(hdev, RMI_PAGE(addr));
  		if (ret < 0)
  			goto exit;
  	}
  
  	for (retries = 5; retries > 0; retries--) {
  		data->writeReport[0] = RMI_READ_ADDR_REPORT_ID;
  		data->writeReport[1] = 0; /* old 1 byte read count */
  		data->writeReport[2] = addr & 0xFF;
  		data->writeReport[3] = (addr >> 8) & 0xFF;
  		data->writeReport[4] = len  & 0xFF;
  		data->writeReport[5] = (len >> 8) & 0xFF;
  
  		set_bit(RMI_READ_REQUEST_PENDING, &data->flags);
  
  		ret = rmi_write_report(hdev, data->writeReport,
  						data->output_report_size);
  		if (ret != data->output_report_size) {
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  			dev_err(&hdev->dev,
  				"failed to write request output report (%d)
  ",
  				ret);
  			goto exit;
  		}
  
  		bytes_read = 0;
  		bytes_needed = len;
  		while (bytes_read < len) {
  			if (!wait_event_timeout(data->wait,
  				test_bit(RMI_READ_DATA_PENDING, &data->flags),
  					msecs_to_jiffies(1000))) {
  				hid_warn(hdev, "%s: timeout elapsed
  ",
  					 __func__);
  				ret = -EAGAIN;
  				break;
  			}
  
  			read_input_count = data->readReport[1];
  			memcpy(buf + bytes_read, &data->readReport[2],
  				read_input_count < bytes_needed ?
  					read_input_count : bytes_needed);
  
  			bytes_read += read_input_count;
  			bytes_needed -= read_input_count;
  			clear_bit(RMI_READ_DATA_PENDING, &data->flags);
  		}
  
  		if (ret >= 0) {
  			ret = 0;
  			break;
  		}
  	}
  
  exit:
  	clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
  	mutex_unlock(&data->page_mutex);
  	return ret;
  }
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
256
257
  static int rmi_hid_write_block(struct rmi_transport_dev *xport, u16 addr,
  		const void *buf, size_t len)
dd8df2845   Andrew Duggan   HID: rmi: Add fun...
258
  {
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
259
260
  	struct rmi_data *data = container_of(xport, struct rmi_data, xport);
  	struct hid_device *hdev = data->hdev;
dd8df2845   Andrew Duggan   HID: rmi: Add fun...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  	int ret;
  
  	mutex_lock(&data->page_mutex);
  
  	if (RMI_PAGE(addr) != data->page) {
  		ret = rmi_set_page(hdev, RMI_PAGE(addr));
  		if (ret < 0)
  			goto exit;
  	}
  
  	data->writeReport[0] = RMI_WRITE_REPORT_ID;
  	data->writeReport[1] = len;
  	data->writeReport[2] = addr & 0xFF;
  	data->writeReport[3] = (addr >> 8) & 0xFF;
  	memcpy(&data->writeReport[4], buf, len);
  
  	ret = rmi_write_report(hdev, data->writeReport,
  					data->output_report_size);
  	if (ret < 0) {
  		dev_err(&hdev->dev,
  			"failed to write request output report (%d)
  ",
  			ret);
  		goto exit;
  	}
  	ret = 0;
  
  exit:
  	mutex_unlock(&data->page_mutex);
  	return ret;
  }
9a98b3387   Andrew Duggan   HID: rmi: Set F01...
292
293
294
  static int rmi_reset_attn_mode(struct hid_device *hdev)
  {
  	struct rmi_data *data = hid_get_drvdata(hdev);
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
295
  	struct rmi_device *rmi_dev = data->xport.rmi_dev;
9a98b3387   Andrew Duggan   HID: rmi: Set F01...
296
297
298
299
300
  	int ret;
  
  	ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
  	if (ret)
  		return ret;
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
301
302
  	if (test_bit(RMI_STARTED, &data->flags))
  		ret = rmi_dev->driver->reset_handler(rmi_dev);
9a98b3387   Andrew Duggan   HID: rmi: Set F01...
303

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
304
  	return ret;
9a98b3387   Andrew Duggan   HID: rmi: Set F01...
305
  }
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
306
307
308
309
310
311
  static void rmi_reset_work(struct work_struct *work)
  {
  	struct rmi_data *hdata = container_of(work, struct rmi_data,
  						reset_work);
  
  	/* switch the device to RMI if we receive a generic mouse report */
9a98b3387   Andrew Duggan   HID: rmi: Set F01...
312
  	rmi_reset_attn_mode(hdata->hdev);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
313
  }
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
314
  static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
315
316
  {
  	struct rmi_data *hdata = hid_get_drvdata(hdev);
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
317
318
  	struct rmi_device *rmi_dev = hdata->xport.rmi_dev;
  	unsigned long flags;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
319

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
320
  	if (!(test_bit(RMI_STARTED, &hdata->flags)))
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
321
  		return 0;
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
322
  	local_irq_save(flags);
5b65c2a02   Benjamin Tissoires   HID: rmi: check s...
323

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
324
  	rmi_set_attn_data(rmi_dev, data[1], &data[2], size - 2);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
325

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
326
  	generic_handle_irq(hdata->rmi_irq);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
327

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
328
  	local_irq_restore(flags);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
329
330
331
332
333
334
335
336
337
  
  	return 1;
  }
  
  static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
  {
  	struct rmi_data *hdata = hid_get_drvdata(hdev);
  
  	if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) {
01a5f8a40   Andrew Duggan   HID: rmi: change ...
338
339
  		hid_dbg(hdev, "no read request pending
  ");
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
340
341
342
343
344
345
346
347
348
349
  		return 0;
  	}
  
  	memcpy(hdata->readReport, data, size < hdata->input_report_size ?
  			size : hdata->input_report_size);
  	set_bit(RMI_READ_DATA_PENDING, &hdata->flags);
  	wake_up(&hdata->wait);
  
  	return 1;
  }
5b65c2a02   Benjamin Tissoires   HID: rmi: check s...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  static int rmi_check_sanity(struct hid_device *hdev, u8 *data, int size)
  {
  	int valid_size = size;
  	/*
  	 * On the Dell XPS 13 9333, the bus sometimes get confused and fills
  	 * the report with a sentinel value "ff". Synaptics told us that such
  	 * behavior does not comes from the touchpad itself, so we filter out
  	 * such reports here.
  	 */
  
  	while ((data[valid_size - 1] == 0xff) && valid_size > 0)
  		valid_size--;
  
  	return valid_size;
  }
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
365
366
367
  static int rmi_raw_event(struct hid_device *hdev,
  		struct hid_report *report, u8 *data, int size)
  {
ef14a4bf0   Andrew Duggan   HID: rmi: Check t...
368
369
370
371
  	struct rmi_data *hdata = hid_get_drvdata(hdev);
  
  	if (!(hdata->device_flags & RMI_DEVICE))
  		return 0;
5b65c2a02   Benjamin Tissoires   HID: rmi: check s...
372
373
374
  	size = rmi_check_sanity(hdev, data, size);
  	if (size < 2)
  		return 0;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
375
376
377
378
379
  	switch (data[0]) {
  	case RMI_READ_DATA_REPORT_ID:
  		return rmi_read_data_event(hdev, data, size);
  	case RMI_ATTN_REPORT_ID:
  		return rmi_input_event(hdev, data, size);
2f43de605   Andrew Duggan   HID: rmi: Support...
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  	default:
  		return 1;
  	}
  
  	return 0;
  }
  
  static int rmi_event(struct hid_device *hdev, struct hid_field *field,
  			struct hid_usage *usage, __s32 value)
  {
  	struct rmi_data *data = hid_get_drvdata(hdev);
  
  	if ((data->device_flags & RMI_DEVICE) &&
  	    (field->application == HID_GD_POINTER ||
  	    field->application == HID_GD_MOUSE)) {
79364d87a   Andrew Duggan   HID: rmi: Support...
395
396
397
398
399
400
401
402
  		if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS) {
  			if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
  				return 0;
  
  			if ((usage->hid == HID_GD_X || usage->hid == HID_GD_Y)
  			    && !value)
  				return 1;
  		}
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
403
  		schedule_work(&data->reset_work);
2f43de605   Andrew Duggan   HID: rmi: Support...
404
  		return 1;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
405
406
407
408
  	}
  
  	return 0;
  }
c94ba0601   Benjamin Tissoires   HID: rmi: use HID...
409
410
411
412
413
414
415
416
417
  static void rmi_report(struct hid_device *hid, struct hid_report *report)
  {
  	struct hid_field *field = report->field[0];
  
  	if (!(hid->claimed & HID_CLAIMED_INPUT))
  		return;
  
  	switch (report->id) {
  	case RMI_READ_DATA_REPORT_ID:
c94ba0601   Benjamin Tissoires   HID: rmi: use HID...
418
419
420
421
422
423
424
  	case RMI_ATTN_REPORT_ID:
  		return;
  	}
  
  	if (field && field->hidinput && field->hidinput->input)
  		input_sync(field->hidinput->input);
  }
a278e2683   Geert Uytterhoeven   HID: rmi: Protect...
425
  #ifdef CONFIG_PM
092563604   Andrew Duggan   HID: rmi: Disable...
426
427
  static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
  {
7035f3a4e   Andrew Duggan   HID: rmi: Write u...
428
  	struct rmi_data *data = hid_get_drvdata(hdev);
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
429
  	struct rmi_device *rmi_dev = data->xport.rmi_dev;
092563604   Andrew Duggan   HID: rmi: Disable...
430
  	int ret;
b786ae8e2   Andrew Duggan   HID: rmi: Check t...
431
432
  	if (!(data->device_flags & RMI_DEVICE))
  		return 0;
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
433
  	ret = rmi_driver_suspend(rmi_dev, false);
092563604   Andrew Duggan   HID: rmi: Disable...
434
  	if (ret) {
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
435
436
  		hid_warn(hdev, "Failed to suspend device: %d
  ", ret);
092563604   Andrew Duggan   HID: rmi: Disable...
437
438
  		return ret;
  	}
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
439
  	return 0;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
440
441
442
443
  }
  
  static int rmi_post_resume(struct hid_device *hdev)
  {
b786ae8e2   Andrew Duggan   HID: rmi: Check t...
444
  	struct rmi_data *data = hid_get_drvdata(hdev);
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
445
446
  	struct rmi_device *rmi_dev = data->xport.rmi_dev;
  	int ret;
b786ae8e2   Andrew Duggan   HID: rmi: Check t...
447
448
449
  
  	if (!(data->device_flags & RMI_DEVICE))
  		return 0;
cac72b990   Lyude   HID: rmi: Make su...
450
451
  	/* Make sure the HID device is ready to receive events */
  	ret = hid_hw_open(hdev);
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
452
  	if (ret)
05ba999fc   Andrew Duggan   HID: rmi: disable...
453
  		return ret;
8414947a2   Andrew Duggan   HID: rmi: Check f...
454

cac72b990   Lyude   HID: rmi: Make su...
455
456
457
  	ret = rmi_reset_attn_mode(hdev);
  	if (ret)
  		goto out;
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
458
  	ret = rmi_driver_resume(rmi_dev, false);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
459
  	if (ret) {
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
460
461
  		hid_warn(hdev, "Failed to resume device: %d
  ", ret);
cac72b990   Lyude   HID: rmi: Make su...
462
  		goto out;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
463
  	}
cac72b990   Lyude   HID: rmi: Make su...
464
465
466
  out:
  	hid_hw_close(hdev);
  	return ret;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
467
  }
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
468
  #endif /* CONFIG_PM */
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
469

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
470
  static int rmi_hid_reset(struct rmi_transport_dev *xport, u16 reset_addr)
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
471
  {
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
472
473
  	struct rmi_data *data = container_of(xport, struct rmi_data, xport);
  	struct hid_device *hdev = data->hdev;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
474

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
475
  	return rmi_reset_attn_mode(hdev);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
476
  }
9154301a4   Dmitry Torokhov   HID: hid-input: a...
477
  static int rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
478
479
480
  {
  	struct rmi_data *data = hid_get_drvdata(hdev);
  	struct input_dev *input = hi->input;
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
481
482
483
484
  	int ret = 0;
  
  	if (!(data->device_flags & RMI_DEVICE))
  		return 0;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
485

0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
486
  	data->xport.input = input;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
487
488
489
490
491
  
  	hid_dbg(hdev, "Opening low level driver
  ");
  	ret = hid_hw_open(hdev);
  	if (ret)
9154301a4   Dmitry Torokhov   HID: hid-input: a...
492
  		return ret;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
  
  	/* Allow incoming hid reports */
  	hid_device_io_start(hdev);
  
  	ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
  	if (ret < 0) {
  		dev_err(&hdev->dev, "failed to set rmi mode
  ");
  		goto exit;
  	}
  
  	ret = rmi_set_page(hdev, 0);
  	if (ret < 0) {
  		dev_err(&hdev->dev, "failed to set page select to 0.
  ");
  		goto exit;
  	}
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
510
511
512
513
  	ret = rmi_register_transport_device(&data->xport);
  	if (ret < 0) {
  		dev_err(&hdev->dev, "failed to register transport driver
  ");
9154301a4   Dmitry Torokhov   HID: hid-input: a...
514
  		goto exit;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
515
516
517
518
519
520
521
  	}
  
  	set_bit(RMI_STARTED, &data->flags);
  
  exit:
  	hid_device_io_stop(hdev);
  	hid_hw_close(hdev);
9154301a4   Dmitry Torokhov   HID: hid-input: a...
522
  	return ret;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
523
524
525
526
527
528
  }
  
  static int rmi_input_mapping(struct hid_device *hdev,
  		struct hid_input *hi, struct hid_field *field,
  		struct hid_usage *usage, unsigned long **bit, int *max)
  {
2f43de605   Andrew Duggan   HID: rmi: Support...
529
530
531
532
533
534
  	struct rmi_data *data = hid_get_drvdata(hdev);
  
  	/*
  	 * we want to make HID ignore the advertised HID collection
  	 * for RMI deivces
  	 */
79364d87a   Andrew Duggan   HID: rmi: Support...
535
536
537
538
  	if (data->device_flags & RMI_DEVICE) {
  		if ((data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS) &&
  		    ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON))
  			return 0;
2f43de605   Andrew Duggan   HID: rmi: Support...
539
  		return -1;
79364d87a   Andrew Duggan   HID: rmi: Support...
540
  	}
2f43de605   Andrew Duggan   HID: rmi: Support...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
  
  	return 0;
  }
  
  static int rmi_check_valid_report_id(struct hid_device *hdev, unsigned type,
  		unsigned id, struct hid_report **report)
  {
  	int i;
  
  	*report = hdev->report_enum[type].report_id_hash[id];
  	if (*report) {
  		for (i = 0; i < (*report)->maxfield; i++) {
  			unsigned app = (*report)->field[i]->application;
  			if ((app & HID_USAGE_PAGE) >= HID_UP_MSVENDOR)
  				return 1;
  		}
  	}
  
  	return 0;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
560
  }
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
  static struct rmi_device_platform_data rmi_hid_pdata = {
  	.sensor_pdata = {
  		.sensor_type = rmi_sensor_touchpad,
  		.axis_align.flip_y = true,
  		.dribble = RMI_REG_STATE_ON,
  		.palm_detect = RMI_REG_STATE_OFF,
  	},
  };
  
  static const struct rmi_transport_ops hid_rmi_ops = {
  	.write_block	= rmi_hid_write_block,
  	.read_block	= rmi_hid_read_block,
  	.reset		= rmi_hid_reset,
  };
  
  static void rmi_irq_teardown(void *data)
  {
  	struct rmi_data *hdata = data;
  	struct irq_domain *domain = hdata->domain;
  
  	if (!domain)
  		return;
  
  	irq_dispose_mapping(irq_find_mapping(domain, 0));
  
  	irq_domain_remove(domain);
  	hdata->domain = NULL;
  	hdata->rmi_irq = 0;
  }
  
  static int rmi_irq_map(struct irq_domain *h, unsigned int virq,
  		       irq_hw_number_t hw_irq_num)
  {
  	irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
  
  	return 0;
  }
  
  static const struct irq_domain_ops rmi_irq_ops = {
  	.map = rmi_irq_map,
  };
  
  static int rmi_setup_irq_domain(struct hid_device *hdev)
  {
  	struct rmi_data *hdata = hid_get_drvdata(hdev);
  	int ret;
  
  	hdata->domain = irq_domain_create_linear(hdev->dev.fwnode, 1,
  						 &rmi_irq_ops, hdata);
  	if (!hdata->domain)
  		return -ENOMEM;
  
  	ret = devm_add_action_or_reset(&hdev->dev, &rmi_irq_teardown, hdata);
  	if (ret)
  		return ret;
  
  	hdata->rmi_irq = irq_create_mapping(hdata->domain, 0);
  	if (hdata->rmi_irq <= 0) {
  		hid_err(hdev, "Can't allocate an IRQ
  ");
  		return hdata->rmi_irq < 0 ? hdata->rmi_irq : -ENXIO;
  	}
  
  	return 0;
  }
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
626
627
628
629
630
  static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  	struct rmi_data *data = NULL;
  	int ret;
  	size_t alloc_size;
dd3edeb6a   Andrew Duggan   HID: rmi: check t...
631
632
  	struct hid_report *input_report;
  	struct hid_report *output_report;
2f43de605   Andrew Duggan   HID: rmi: Support...
633
  	struct hid_report *feature_report;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
634
635
636
637
638
639
640
641
642
643
644
  
  	data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL);
  	if (!data)
  		return -ENOMEM;
  
  	INIT_WORK(&data->reset_work, rmi_reset_work);
  	data->hdev = hdev;
  
  	hid_set_drvdata(hdev, data);
  
  	hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
c94ba0601   Benjamin Tissoires   HID: rmi: use HID...
645
  	hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
646
647
648
649
650
651
652
  
  	ret = hid_parse(hdev);
  	if (ret) {
  		hid_err(hdev, "parse failed
  ");
  		return ret;
  	}
79364d87a   Andrew Duggan   HID: rmi: Support...
653
654
  	if (id->driver_data)
  		data->device_flags = id->driver_data;
2f43de605   Andrew Duggan   HID: rmi: Support...
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
  	/*
  	 * Check for the RMI specific report ids. If they are misisng
  	 * simply return and let the events be processed by hid-input
  	 */
  	if (!rmi_check_valid_report_id(hdev, HID_FEATURE_REPORT,
  	    RMI_SET_RMI_MODE_REPORT_ID, &feature_report)) {
  		hid_dbg(hdev, "device does not have set mode feature report
  ");
  		goto start;
  	}
  
  	if (!rmi_check_valid_report_id(hdev, HID_INPUT_REPORT,
  	    RMI_ATTN_REPORT_ID, &input_report)) {
  		hid_dbg(hdev, "device does not have attention input report
  ");
  		goto start;
dd3edeb6a   Andrew Duggan   HID: rmi: check t...
671
  	}
b8aed6ea3   Andrew Duggan   HID: rmi: Use hid...
672
  	data->input_report_size = hid_report_len(input_report);
dd3edeb6a   Andrew Duggan   HID: rmi: check t...
673

2f43de605   Andrew Duggan   HID: rmi: Support...
674
675
676
677
678
679
  	if (!rmi_check_valid_report_id(hdev, HID_OUTPUT_REPORT,
  	    RMI_WRITE_REPORT_ID, &output_report)) {
  		hid_dbg(hdev,
  			"device does not have rmi write output report
  ");
  		goto start;
dd3edeb6a   Andrew Duggan   HID: rmi: check t...
680
  	}
b8aed6ea3   Andrew Duggan   HID: rmi: Use hid...
681
  	data->output_report_size = hid_report_len(output_report);
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
682

2f43de605   Andrew Duggan   HID: rmi: Support...
683
  	data->device_flags |= RMI_DEVICE;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
684
685
686
687
  	alloc_size = data->output_report_size + data->input_report_size;
  
  	data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL);
  	if (!data->writeReport) {
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
688
689
690
  		hid_err(hdev, "failed to allocate buffer for HID reports
  ");
  		return -ENOMEM;
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
691
692
693
694
695
696
697
  	}
  
  	data->readReport = data->writeReport + data->output_report_size;
  
  	init_waitqueue_head(&data->wait);
  
  	mutex_init(&data->page_mutex);
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
698
699
700
701
702
703
704
705
  	ret = rmi_setup_irq_domain(hdev);
  	if (ret) {
  		hid_err(hdev, "failed to allocate IRQ domain
  ");
  		return ret;
  	}
  
  	if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)
261bfb332   Vincent Huang   Input: synaptics-...
706
  		rmi_hid_pdata.gpio_data.disable = true;
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
707
708
709
710
711
712
  
  	data->xport.dev = hdev->dev.parent;
  	data->xport.pdata = rmi_hid_pdata;
  	data->xport.pdata.irq = data->rmi_irq;
  	data->xport.proto_name = "hid";
  	data->xport.ops = &hid_rmi_ops;
2f43de605   Andrew Duggan   HID: rmi: Support...
713
  start:
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
714
715
716
717
718
719
  	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
  	if (ret) {
  		hid_err(hdev, "hw start failed
  ");
  		return ret;
  	}
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
720
721
722
723
724
725
  	return 0;
  }
  
  static void rmi_remove(struct hid_device *hdev)
  {
  	struct rmi_data *hdata = hid_get_drvdata(hdev);
8725aa4fa   Andrew Duggan   HID: rmi: Check t...
726
727
  	if ((hdata->device_flags & RMI_DEVICE)
  	    && test_bit(RMI_STARTED, &hdata->flags)) {
ef14a4bf0   Andrew Duggan   HID: rmi: Check t...
728
729
730
731
  		clear_bit(RMI_STARTED, &hdata->flags);
  		cancel_work_sync(&hdata->reset_work);
  		rmi_unregister_transport_device(&hdata->xport);
  	}
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
732
733
734
735
736
  
  	hid_hw_stop(hdev);
  }
  
  static const struct hid_device_id rmi_id[] = {
e9287099b   Andrew Duggan   HID: rmi: Add sup...
737
738
  	{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14),
  		.driver_data = RMI_DEVICE_HAS_PHYS_BUTTONS },
c7821d0f3   Andrew Duggan   HID: rmi: Support...
739
  	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_COVER) },
c5293409e   Andrew Duggan   HID: rmi: Support...
740
  	{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_REZEL) },
10235380d   Tobias Auerochs   HID: rmi: Use SET...
741
742
  	{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5),
  		.driver_data = RMI_DEVICE_OUTPUT_SET_REPORT },
ba391e5a5   Benjamin Tissoires   HID: rmi: do not ...
743
  	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) },
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
744
745
746
747
748
749
750
751
752
  	{ }
  };
  MODULE_DEVICE_TABLE(hid, rmi_id);
  
  static struct hid_driver rmi_driver = {
  	.name = "hid-rmi",
  	.id_table		= rmi_id,
  	.probe			= rmi_probe,
  	.remove			= rmi_remove,
2f43de605   Andrew Duggan   HID: rmi: Support...
753
  	.event			= rmi_event,
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
754
  	.raw_event		= rmi_raw_event,
c94ba0601   Benjamin Tissoires   HID: rmi: use HID...
755
  	.report			= rmi_report,
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
756
757
758
  	.input_mapping		= rmi_input_mapping,
  	.input_configured	= rmi_input_configured,
  #ifdef CONFIG_PM
092563604   Andrew Duggan   HID: rmi: Disable...
759
  	.suspend		= rmi_suspend,
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
760
  	.resume			= rmi_post_resume,
0b2c7a897   Andrew Duggan   HID: rmi: Make hi...
761
  	.reset_resume		= rmi_post_resume,
9fb6bf02e   Benjamin Tissoires   HID: rmi: introdu...
762
763
764
765
766
767
768
769
  #endif
  };
  
  module_hid_driver(rmi_driver);
  
  MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
  MODULE_DESCRIPTION("RMI HID driver");
  MODULE_LICENSE("GPL");