1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Surface Book (gen. 2 and later) detachment system (DTX) driver. |
4 | * |
5 | * Provides a user-space interface to properly handle clipboard/tablet |
6 | * (containing screen and processor) detachment from the base of the device |
7 | * (containing the keyboard and optionally a discrete GPU). Allows to |
8 | * acknowledge (to speed things up), abort (e.g. in case the dGPU is still in |
9 | * use), or request detachment via user-space. |
10 | * |
11 | * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com> |
12 | */ |
13 | |
14 | #include <linux/fs.h> |
15 | #include <linux/input.h> |
16 | #include <linux/ioctl.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/kfifo.h> |
19 | #include <linux/kref.h> |
20 | #include <linux/miscdevice.h> |
21 | #include <linux/module.h> |
22 | #include <linux/mutex.h> |
23 | #include <linux/platform_device.h> |
24 | #include <linux/poll.h> |
25 | #include <linux/rwsem.h> |
26 | #include <linux/slab.h> |
27 | #include <linux/workqueue.h> |
28 | |
29 | #include <linux/surface_aggregator/controller.h> |
30 | #include <linux/surface_aggregator/device.h> |
31 | #include <linux/surface_aggregator/dtx.h> |
32 | |
33 | |
34 | /* -- SSAM interface. ------------------------------------------------------- */ |
35 | |
36 | enum sam_event_cid_bas { |
37 | SAM_EVENT_CID_DTX_CONNECTION = 0x0c, |
38 | SAM_EVENT_CID_DTX_REQUEST = 0x0e, |
39 | SAM_EVENT_CID_DTX_CANCEL = 0x0f, |
40 | SAM_EVENT_CID_DTX_LATCH_STATUS = 0x11, |
41 | }; |
42 | |
43 | enum ssam_bas_base_state { |
44 | SSAM_BAS_BASE_STATE_DETACH_SUCCESS = 0x00, |
45 | SSAM_BAS_BASE_STATE_ATTACHED = 0x01, |
46 | SSAM_BAS_BASE_STATE_NOT_FEASIBLE = 0x02, |
47 | }; |
48 | |
49 | enum ssam_bas_latch_status { |
50 | SSAM_BAS_LATCH_STATUS_CLOSED = 0x00, |
51 | SSAM_BAS_LATCH_STATUS_OPENED = 0x01, |
52 | SSAM_BAS_LATCH_STATUS_FAILED_TO_OPEN = 0x02, |
53 | SSAM_BAS_LATCH_STATUS_FAILED_TO_REMAIN_OPEN = 0x03, |
54 | SSAM_BAS_LATCH_STATUS_FAILED_TO_CLOSE = 0x04, |
55 | }; |
56 | |
57 | enum ssam_bas_cancel_reason { |
58 | SSAM_BAS_CANCEL_REASON_NOT_FEASIBLE = 0x00, /* Low battery. */ |
59 | SSAM_BAS_CANCEL_REASON_TIMEOUT = 0x02, |
60 | SSAM_BAS_CANCEL_REASON_FAILED_TO_OPEN = 0x03, |
61 | SSAM_BAS_CANCEL_REASON_FAILED_TO_REMAIN_OPEN = 0x04, |
62 | SSAM_BAS_CANCEL_REASON_FAILED_TO_CLOSE = 0x05, |
63 | }; |
64 | |
65 | struct ssam_bas_base_info { |
66 | u8 state; |
67 | u8 base_id; |
68 | } __packed; |
69 | |
70 | static_assert(sizeof(struct ssam_bas_base_info) == 2); |
71 | |
72 | SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, { |
73 | .target_category = SSAM_SSH_TC_BAS, |
74 | .target_id = SSAM_SSH_TID_SAM, |
75 | .command_id = 0x06, |
76 | .instance_id = 0x00, |
77 | }); |
78 | |
79 | SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, { |
80 | .target_category = SSAM_SSH_TC_BAS, |
81 | .target_id = SSAM_SSH_TID_SAM, |
82 | .command_id = 0x07, |
83 | .instance_id = 0x00, |
84 | }); |
85 | |
86 | SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_request, { |
87 | .target_category = SSAM_SSH_TC_BAS, |
88 | .target_id = SSAM_SSH_TID_SAM, |
89 | .command_id = 0x08, |
90 | .instance_id = 0x00, |
91 | }); |
92 | |
93 | SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_confirm, { |
94 | .target_category = SSAM_SSH_TC_BAS, |
95 | .target_id = SSAM_SSH_TID_SAM, |
96 | .command_id = 0x09, |
97 | .instance_id = 0x00, |
98 | }); |
99 | |
100 | SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_heartbeat, { |
101 | .target_category = SSAM_SSH_TC_BAS, |
102 | .target_id = SSAM_SSH_TID_SAM, |
103 | .command_id = 0x0a, |
104 | .instance_id = 0x00, |
105 | }); |
106 | |
107 | SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_cancel, { |
108 | .target_category = SSAM_SSH_TC_BAS, |
109 | .target_id = SSAM_SSH_TID_SAM, |
110 | .command_id = 0x0b, |
111 | .instance_id = 0x00, |
112 | }); |
113 | |
114 | SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_base, struct ssam_bas_base_info, { |
115 | .target_category = SSAM_SSH_TC_BAS, |
116 | .target_id = SSAM_SSH_TID_SAM, |
117 | .command_id = 0x0c, |
118 | .instance_id = 0x00, |
119 | }); |
120 | |
121 | SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_device_mode, u8, { |
122 | .target_category = SSAM_SSH_TC_BAS, |
123 | .target_id = SSAM_SSH_TID_SAM, |
124 | .command_id = 0x0d, |
125 | .instance_id = 0x00, |
126 | }); |
127 | |
128 | SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_latch_status, u8, { |
129 | .target_category = SSAM_SSH_TC_BAS, |
130 | .target_id = SSAM_SSH_TID_SAM, |
131 | .command_id = 0x11, |
132 | .instance_id = 0x00, |
133 | }); |
134 | |
135 | |
136 | /* -- Main structures. ------------------------------------------------------ */ |
137 | |
138 | enum sdtx_device_state { |
139 | SDTX_DEVICE_SHUTDOWN_BIT = BIT(0), |
140 | SDTX_DEVICE_DIRTY_BASE_BIT = BIT(1), |
141 | SDTX_DEVICE_DIRTY_MODE_BIT = BIT(2), |
142 | SDTX_DEVICE_DIRTY_LATCH_BIT = BIT(3), |
143 | }; |
144 | |
145 | struct sdtx_device { |
146 | struct kref kref; |
147 | struct rw_semaphore lock; /* Guards device and controller reference. */ |
148 | |
149 | struct device *dev; |
150 | struct ssam_controller *ctrl; |
151 | unsigned long flags; |
152 | |
153 | struct miscdevice mdev; |
154 | wait_queue_head_t waitq; |
155 | struct mutex write_lock; /* Guards order of events/notifications. */ |
156 | struct rw_semaphore client_lock; /* Guards client list. */ |
157 | struct list_head client_list; |
158 | |
159 | struct delayed_work state_work; |
160 | struct { |
161 | struct ssam_bas_base_info base; |
162 | u8 device_mode; |
163 | u8 latch_status; |
164 | } state; |
165 | |
166 | struct delayed_work mode_work; |
167 | struct input_dev *mode_switch; |
168 | |
169 | struct ssam_event_notifier notif; |
170 | }; |
171 | |
172 | enum sdtx_client_state { |
173 | SDTX_CLIENT_EVENTS_ENABLED_BIT = BIT(0), |
174 | }; |
175 | |
176 | struct sdtx_client { |
177 | struct sdtx_device *ddev; |
178 | struct list_head node; |
179 | unsigned long flags; |
180 | |
181 | struct fasync_struct *fasync; |
182 | |
183 | struct mutex read_lock; /* Guards FIFO buffer read access. */ |
184 | DECLARE_KFIFO(buffer, u8, 512); |
185 | }; |
186 | |
187 | static void __sdtx_device_release(struct kref *kref) |
188 | { |
189 | struct sdtx_device *ddev = container_of(kref, struct sdtx_device, kref); |
190 | |
191 | mutex_destroy(lock: &ddev->write_lock); |
192 | kfree(objp: ddev); |
193 | } |
194 | |
195 | static struct sdtx_device *sdtx_device_get(struct sdtx_device *ddev) |
196 | { |
197 | if (ddev) |
198 | kref_get(kref: &ddev->kref); |
199 | |
200 | return ddev; |
201 | } |
202 | |
203 | static void sdtx_device_put(struct sdtx_device *ddev) |
204 | { |
205 | if (ddev) |
206 | kref_put(kref: &ddev->kref, release: __sdtx_device_release); |
207 | } |
208 | |
209 | |
210 | /* -- Firmware value translations. ------------------------------------------ */ |
211 | |
212 | static u16 sdtx_translate_base_state(struct sdtx_device *ddev, u8 state) |
213 | { |
214 | switch (state) { |
215 | case SSAM_BAS_BASE_STATE_ATTACHED: |
216 | return SDTX_BASE_ATTACHED; |
217 | |
218 | case SSAM_BAS_BASE_STATE_DETACH_SUCCESS: |
219 | return SDTX_BASE_DETACHED; |
220 | |
221 | case SSAM_BAS_BASE_STATE_NOT_FEASIBLE: |
222 | return SDTX_DETACH_NOT_FEASIBLE; |
223 | |
224 | default: |
225 | dev_err(ddev->dev, "unknown base state: %#04x\n" , state); |
226 | return SDTX_UNKNOWN(state); |
227 | } |
228 | } |
229 | |
230 | static u16 sdtx_translate_latch_status(struct sdtx_device *ddev, u8 status) |
231 | { |
232 | switch (status) { |
233 | case SSAM_BAS_LATCH_STATUS_CLOSED: |
234 | return SDTX_LATCH_CLOSED; |
235 | |
236 | case SSAM_BAS_LATCH_STATUS_OPENED: |
237 | return SDTX_LATCH_OPENED; |
238 | |
239 | case SSAM_BAS_LATCH_STATUS_FAILED_TO_OPEN: |
240 | return SDTX_ERR_FAILED_TO_OPEN; |
241 | |
242 | case SSAM_BAS_LATCH_STATUS_FAILED_TO_REMAIN_OPEN: |
243 | return SDTX_ERR_FAILED_TO_REMAIN_OPEN; |
244 | |
245 | case SSAM_BAS_LATCH_STATUS_FAILED_TO_CLOSE: |
246 | return SDTX_ERR_FAILED_TO_CLOSE; |
247 | |
248 | default: |
249 | dev_err(ddev->dev, "unknown latch status: %#04x\n" , status); |
250 | return SDTX_UNKNOWN(status); |
251 | } |
252 | } |
253 | |
254 | static u16 sdtx_translate_cancel_reason(struct sdtx_device *ddev, u8 reason) |
255 | { |
256 | switch (reason) { |
257 | case SSAM_BAS_CANCEL_REASON_NOT_FEASIBLE: |
258 | return SDTX_DETACH_NOT_FEASIBLE; |
259 | |
260 | case SSAM_BAS_CANCEL_REASON_TIMEOUT: |
261 | return SDTX_DETACH_TIMEDOUT; |
262 | |
263 | case SSAM_BAS_CANCEL_REASON_FAILED_TO_OPEN: |
264 | return SDTX_ERR_FAILED_TO_OPEN; |
265 | |
266 | case SSAM_BAS_CANCEL_REASON_FAILED_TO_REMAIN_OPEN: |
267 | return SDTX_ERR_FAILED_TO_REMAIN_OPEN; |
268 | |
269 | case SSAM_BAS_CANCEL_REASON_FAILED_TO_CLOSE: |
270 | return SDTX_ERR_FAILED_TO_CLOSE; |
271 | |
272 | default: |
273 | dev_err(ddev->dev, "unknown cancel reason: %#04x\n" , reason); |
274 | return SDTX_UNKNOWN(reason); |
275 | } |
276 | } |
277 | |
278 | |
279 | /* -- IOCTLs. --------------------------------------------------------------- */ |
280 | |
281 | static int sdtx_ioctl_get_base_info(struct sdtx_device *ddev, |
282 | struct sdtx_base_info __user *buf) |
283 | { |
284 | struct ssam_bas_base_info raw; |
285 | struct sdtx_base_info info; |
286 | int status; |
287 | |
288 | lockdep_assert_held_read(&ddev->lock); |
289 | |
290 | status = ssam_retry(ssam_bas_get_base, ddev->ctrl, &raw); |
291 | if (status < 0) |
292 | return status; |
293 | |
294 | info.state = sdtx_translate_base_state(ddev, state: raw.state); |
295 | info.base_id = SDTX_BASE_TYPE_SSH(raw.base_id); |
296 | |
297 | if (copy_to_user(to: buf, from: &info, n: sizeof(info))) |
298 | return -EFAULT; |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | static int sdtx_ioctl_get_device_mode(struct sdtx_device *ddev, u16 __user *buf) |
304 | { |
305 | u8 mode; |
306 | int status; |
307 | |
308 | lockdep_assert_held_read(&ddev->lock); |
309 | |
310 | status = ssam_retry(ssam_bas_get_device_mode, ddev->ctrl, &mode); |
311 | if (status < 0) |
312 | return status; |
313 | |
314 | return put_user(mode, buf); |
315 | } |
316 | |
317 | static int sdtx_ioctl_get_latch_status(struct sdtx_device *ddev, u16 __user *buf) |
318 | { |
319 | u8 latch; |
320 | int status; |
321 | |
322 | lockdep_assert_held_read(&ddev->lock); |
323 | |
324 | status = ssam_retry(ssam_bas_get_latch_status, ddev->ctrl, &latch); |
325 | if (status < 0) |
326 | return status; |
327 | |
328 | return put_user(sdtx_translate_latch_status(ddev, latch), buf); |
329 | } |
330 | |
331 | static long __surface_dtx_ioctl(struct sdtx_client *client, unsigned int cmd, unsigned long arg) |
332 | { |
333 | struct sdtx_device *ddev = client->ddev; |
334 | |
335 | lockdep_assert_held_read(&ddev->lock); |
336 | |
337 | switch (cmd) { |
338 | case SDTX_IOCTL_EVENTS_ENABLE: |
339 | set_bit(nr: SDTX_CLIENT_EVENTS_ENABLED_BIT, addr: &client->flags); |
340 | return 0; |
341 | |
342 | case SDTX_IOCTL_EVENTS_DISABLE: |
343 | clear_bit(nr: SDTX_CLIENT_EVENTS_ENABLED_BIT, addr: &client->flags); |
344 | return 0; |
345 | |
346 | case SDTX_IOCTL_LATCH_LOCK: |
347 | return ssam_retry(ssam_bas_latch_lock, ddev->ctrl); |
348 | |
349 | case SDTX_IOCTL_LATCH_UNLOCK: |
350 | return ssam_retry(ssam_bas_latch_unlock, ddev->ctrl); |
351 | |
352 | case SDTX_IOCTL_LATCH_REQUEST: |
353 | return ssam_retry(ssam_bas_latch_request, ddev->ctrl); |
354 | |
355 | case SDTX_IOCTL_LATCH_CONFIRM: |
356 | return ssam_retry(ssam_bas_latch_confirm, ddev->ctrl); |
357 | |
358 | case SDTX_IOCTL_LATCH_HEARTBEAT: |
359 | return ssam_retry(ssam_bas_latch_heartbeat, ddev->ctrl); |
360 | |
361 | case SDTX_IOCTL_LATCH_CANCEL: |
362 | return ssam_retry(ssam_bas_latch_cancel, ddev->ctrl); |
363 | |
364 | case SDTX_IOCTL_GET_BASE_INFO: |
365 | return sdtx_ioctl_get_base_info(ddev, buf: (struct sdtx_base_info __user *)arg); |
366 | |
367 | case SDTX_IOCTL_GET_DEVICE_MODE: |
368 | return sdtx_ioctl_get_device_mode(ddev, buf: (u16 __user *)arg); |
369 | |
370 | case SDTX_IOCTL_GET_LATCH_STATUS: |
371 | return sdtx_ioctl_get_latch_status(ddev, buf: (u16 __user *)arg); |
372 | |
373 | default: |
374 | return -EINVAL; |
375 | } |
376 | } |
377 | |
378 | static long surface_dtx_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
379 | { |
380 | struct sdtx_client *client = file->private_data; |
381 | long status; |
382 | |
383 | if (down_read_killable(sem: &client->ddev->lock)) |
384 | return -ERESTARTSYS; |
385 | |
386 | if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &client->ddev->flags)) { |
387 | up_read(sem: &client->ddev->lock); |
388 | return -ENODEV; |
389 | } |
390 | |
391 | status = __surface_dtx_ioctl(client, cmd, arg); |
392 | |
393 | up_read(sem: &client->ddev->lock); |
394 | return status; |
395 | } |
396 | |
397 | |
398 | /* -- File operations. ------------------------------------------------------ */ |
399 | |
400 | static int surface_dtx_open(struct inode *inode, struct file *file) |
401 | { |
402 | struct sdtx_device *ddev = container_of(file->private_data, struct sdtx_device, mdev); |
403 | struct sdtx_client *client; |
404 | |
405 | /* Initialize client. */ |
406 | client = kzalloc(size: sizeof(*client), GFP_KERNEL); |
407 | if (!client) |
408 | return -ENOMEM; |
409 | |
410 | client->ddev = sdtx_device_get(ddev); |
411 | |
412 | INIT_LIST_HEAD(list: &client->node); |
413 | |
414 | mutex_init(&client->read_lock); |
415 | INIT_KFIFO(client->buffer); |
416 | |
417 | file->private_data = client; |
418 | |
419 | /* Attach client. */ |
420 | down_write(sem: &ddev->client_lock); |
421 | |
422 | /* |
423 | * Do not add a new client if the device has been shut down. Note that |
424 | * it's enough to hold the client_lock here as, during shutdown, we |
425 | * only acquire that lock and remove clients after marking the device |
426 | * as shut down. |
427 | */ |
428 | if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &ddev->flags)) { |
429 | up_write(sem: &ddev->client_lock); |
430 | mutex_destroy(lock: &client->read_lock); |
431 | sdtx_device_put(ddev: client->ddev); |
432 | kfree(objp: client); |
433 | return -ENODEV; |
434 | } |
435 | |
436 | list_add_tail(new: &client->node, head: &ddev->client_list); |
437 | up_write(sem: &ddev->client_lock); |
438 | |
439 | stream_open(inode, filp: file); |
440 | return 0; |
441 | } |
442 | |
443 | static int surface_dtx_release(struct inode *inode, struct file *file) |
444 | { |
445 | struct sdtx_client *client = file->private_data; |
446 | |
447 | /* Detach client. */ |
448 | down_write(sem: &client->ddev->client_lock); |
449 | list_del(entry: &client->node); |
450 | up_write(sem: &client->ddev->client_lock); |
451 | |
452 | /* Free client. */ |
453 | sdtx_device_put(ddev: client->ddev); |
454 | mutex_destroy(lock: &client->read_lock); |
455 | kfree(objp: client); |
456 | |
457 | return 0; |
458 | } |
459 | |
460 | static ssize_t surface_dtx_read(struct file *file, char __user *buf, size_t count, loff_t *offs) |
461 | { |
462 | struct sdtx_client *client = file->private_data; |
463 | struct sdtx_device *ddev = client->ddev; |
464 | unsigned int copied; |
465 | int status = 0; |
466 | |
467 | if (down_read_killable(sem: &ddev->lock)) |
468 | return -ERESTARTSYS; |
469 | |
470 | /* Make sure we're not shut down. */ |
471 | if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &ddev->flags)) { |
472 | up_read(sem: &ddev->lock); |
473 | return -ENODEV; |
474 | } |
475 | |
476 | do { |
477 | /* Check availability, wait if necessary. */ |
478 | if (kfifo_is_empty(&client->buffer)) { |
479 | up_read(sem: &ddev->lock); |
480 | |
481 | if (file->f_flags & O_NONBLOCK) |
482 | return -EAGAIN; |
483 | |
484 | status = wait_event_interruptible(ddev->waitq, |
485 | !kfifo_is_empty(&client->buffer) || |
486 | test_bit(SDTX_DEVICE_SHUTDOWN_BIT, |
487 | &ddev->flags)); |
488 | if (status < 0) |
489 | return status; |
490 | |
491 | if (down_read_killable(sem: &ddev->lock)) |
492 | return -ERESTARTSYS; |
493 | |
494 | /* Need to check that we're not shut down again. */ |
495 | if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &ddev->flags)) { |
496 | up_read(sem: &ddev->lock); |
497 | return -ENODEV; |
498 | } |
499 | } |
500 | |
501 | /* Try to read from FIFO. */ |
502 | if (mutex_lock_interruptible(&client->read_lock)) { |
503 | up_read(sem: &ddev->lock); |
504 | return -ERESTARTSYS; |
505 | } |
506 | |
507 | status = kfifo_to_user(&client->buffer, buf, count, &copied); |
508 | mutex_unlock(lock: &client->read_lock); |
509 | |
510 | if (status < 0) { |
511 | up_read(sem: &ddev->lock); |
512 | return status; |
513 | } |
514 | |
515 | /* We might not have gotten anything, check this here. */ |
516 | if (copied == 0 && (file->f_flags & O_NONBLOCK)) { |
517 | up_read(sem: &ddev->lock); |
518 | return -EAGAIN; |
519 | } |
520 | } while (copied == 0); |
521 | |
522 | up_read(sem: &ddev->lock); |
523 | return copied; |
524 | } |
525 | |
526 | static __poll_t surface_dtx_poll(struct file *file, struct poll_table_struct *pt) |
527 | { |
528 | struct sdtx_client *client = file->private_data; |
529 | __poll_t events = 0; |
530 | |
531 | if (test_bit(SDTX_DEVICE_SHUTDOWN_BIT, &client->ddev->flags)) |
532 | return EPOLLHUP | EPOLLERR; |
533 | |
534 | poll_wait(filp: file, wait_address: &client->ddev->waitq, p: pt); |
535 | |
536 | if (!kfifo_is_empty(&client->buffer)) |
537 | events |= EPOLLIN | EPOLLRDNORM; |
538 | |
539 | return events; |
540 | } |
541 | |
542 | static int surface_dtx_fasync(int fd, struct file *file, int on) |
543 | { |
544 | struct sdtx_client *client = file->private_data; |
545 | |
546 | return fasync_helper(fd, file, on, &client->fasync); |
547 | } |
548 | |
549 | static const struct file_operations surface_dtx_fops = { |
550 | .owner = THIS_MODULE, |
551 | .open = surface_dtx_open, |
552 | .release = surface_dtx_release, |
553 | .read = surface_dtx_read, |
554 | .poll = surface_dtx_poll, |
555 | .fasync = surface_dtx_fasync, |
556 | .unlocked_ioctl = surface_dtx_ioctl, |
557 | .compat_ioctl = surface_dtx_ioctl, |
558 | .llseek = no_llseek, |
559 | }; |
560 | |
561 | |
562 | /* -- Event handling/forwarding. -------------------------------------------- */ |
563 | |
564 | /* |
565 | * The device operation mode is not immediately updated on the EC when the |
566 | * base has been connected, i.e. querying the device mode inside the |
567 | * connection event callback yields an outdated value. Thus, we can only |
568 | * determine the new tablet-mode switch and device mode values after some |
569 | * time. |
570 | * |
571 | * These delays have been chosen by experimenting. We first delay on connect |
572 | * events, then check and validate the device mode against the base state and |
573 | * if invalid delay again by the "recheck" delay. |
574 | */ |
575 | #define SDTX_DEVICE_MODE_DELAY_CONNECT msecs_to_jiffies(100) |
576 | #define SDTX_DEVICE_MODE_DELAY_RECHECK msecs_to_jiffies(100) |
577 | |
578 | struct sdtx_status_event { |
579 | struct sdtx_event e; |
580 | __u16 v; |
581 | } __packed; |
582 | |
583 | struct sdtx_base_info_event { |
584 | struct sdtx_event e; |
585 | struct sdtx_base_info v; |
586 | } __packed; |
587 | |
588 | union sdtx_generic_event { |
589 | struct sdtx_event common; |
590 | struct sdtx_status_event status; |
591 | struct sdtx_base_info_event base; |
592 | }; |
593 | |
594 | static void sdtx_update_device_mode(struct sdtx_device *ddev, unsigned long delay); |
595 | |
596 | /* Must be executed with ddev->write_lock held. */ |
597 | static void sdtx_push_event(struct sdtx_device *ddev, struct sdtx_event *evt) |
598 | { |
599 | const size_t len = sizeof(struct sdtx_event) + evt->length; |
600 | struct sdtx_client *client; |
601 | |
602 | lockdep_assert_held(&ddev->write_lock); |
603 | |
604 | down_read(sem: &ddev->client_lock); |
605 | list_for_each_entry(client, &ddev->client_list, node) { |
606 | if (!test_bit(SDTX_CLIENT_EVENTS_ENABLED_BIT, &client->flags)) |
607 | continue; |
608 | |
609 | if (likely(kfifo_avail(&client->buffer) >= len)) |
610 | kfifo_in(&client->buffer, (const u8 *)evt, len); |
611 | else |
612 | dev_warn(ddev->dev, "event buffer overrun\n" ); |
613 | |
614 | kill_fasync(&client->fasync, SIGIO, POLL_IN); |
615 | } |
616 | up_read(sem: &ddev->client_lock); |
617 | |
618 | wake_up_interruptible(&ddev->waitq); |
619 | } |
620 | |
621 | static u32 sdtx_notifier(struct ssam_event_notifier *nf, const struct ssam_event *in) |
622 | { |
623 | struct sdtx_device *ddev = container_of(nf, struct sdtx_device, notif); |
624 | union sdtx_generic_event event; |
625 | size_t len; |
626 | |
627 | /* Validate event payload length. */ |
628 | switch (in->command_id) { |
629 | case SAM_EVENT_CID_DTX_CONNECTION: |
630 | len = 2 * sizeof(u8); |
631 | break; |
632 | |
633 | case SAM_EVENT_CID_DTX_REQUEST: |
634 | len = 0; |
635 | break; |
636 | |
637 | case SAM_EVENT_CID_DTX_CANCEL: |
638 | len = sizeof(u8); |
639 | break; |
640 | |
641 | case SAM_EVENT_CID_DTX_LATCH_STATUS: |
642 | len = sizeof(u8); |
643 | break; |
644 | |
645 | default: |
646 | return 0; |
647 | } |
648 | |
649 | if (in->length != len) { |
650 | dev_err(ddev->dev, |
651 | "unexpected payload size for event %#04x: got %u, expected %zu\n" , |
652 | in->command_id, in->length, len); |
653 | return 0; |
654 | } |
655 | |
656 | mutex_lock(&ddev->write_lock); |
657 | |
658 | /* Translate event. */ |
659 | switch (in->command_id) { |
660 | case SAM_EVENT_CID_DTX_CONNECTION: |
661 | clear_bit(nr: SDTX_DEVICE_DIRTY_BASE_BIT, addr: &ddev->flags); |
662 | |
663 | /* If state has not changed: do not send new event. */ |
664 | if (ddev->state.base.state == in->data[0] && |
665 | ddev->state.base.base_id == in->data[1]) |
666 | goto out; |
667 | |
668 | ddev->state.base.state = in->data[0]; |
669 | ddev->state.base.base_id = in->data[1]; |
670 | |
671 | event.base.e.length = sizeof(struct sdtx_base_info); |
672 | event.base.e.code = SDTX_EVENT_BASE_CONNECTION; |
673 | event.base.v.state = sdtx_translate_base_state(ddev, state: in->data[0]); |
674 | event.base.v.base_id = SDTX_BASE_TYPE_SSH(in->data[1]); |
675 | break; |
676 | |
677 | case SAM_EVENT_CID_DTX_REQUEST: |
678 | event.common.code = SDTX_EVENT_REQUEST; |
679 | event.common.length = 0; |
680 | break; |
681 | |
682 | case SAM_EVENT_CID_DTX_CANCEL: |
683 | event.status.e.length = sizeof(u16); |
684 | event.status.e.code = SDTX_EVENT_CANCEL; |
685 | event.status.v = sdtx_translate_cancel_reason(ddev, reason: in->data[0]); |
686 | break; |
687 | |
688 | case SAM_EVENT_CID_DTX_LATCH_STATUS: |
689 | clear_bit(nr: SDTX_DEVICE_DIRTY_LATCH_BIT, addr: &ddev->flags); |
690 | |
691 | /* If state has not changed: do not send new event. */ |
692 | if (ddev->state.latch_status == in->data[0]) |
693 | goto out; |
694 | |
695 | ddev->state.latch_status = in->data[0]; |
696 | |
697 | event.status.e.length = sizeof(u16); |
698 | event.status.e.code = SDTX_EVENT_LATCH_STATUS; |
699 | event.status.v = sdtx_translate_latch_status(ddev, status: in->data[0]); |
700 | break; |
701 | } |
702 | |
703 | sdtx_push_event(ddev, evt: &event.common); |
704 | |
705 | /* Update device mode on base connection change. */ |
706 | if (in->command_id == SAM_EVENT_CID_DTX_CONNECTION) { |
707 | unsigned long delay; |
708 | |
709 | delay = in->data[0] ? SDTX_DEVICE_MODE_DELAY_CONNECT : 0; |
710 | sdtx_update_device_mode(ddev, delay); |
711 | } |
712 | |
713 | out: |
714 | mutex_unlock(lock: &ddev->write_lock); |
715 | return SSAM_NOTIF_HANDLED; |
716 | } |
717 | |
718 | |
719 | /* -- State update functions. ----------------------------------------------- */ |
720 | |
721 | static bool sdtx_device_mode_invalid(u8 mode, u8 base_state) |
722 | { |
723 | return ((base_state == SSAM_BAS_BASE_STATE_ATTACHED) && |
724 | (mode == SDTX_DEVICE_MODE_TABLET)) || |
725 | ((base_state == SSAM_BAS_BASE_STATE_DETACH_SUCCESS) && |
726 | (mode != SDTX_DEVICE_MODE_TABLET)); |
727 | } |
728 | |
729 | static void sdtx_device_mode_workfn(struct work_struct *work) |
730 | { |
731 | struct sdtx_device *ddev = container_of(work, struct sdtx_device, mode_work.work); |
732 | struct sdtx_status_event event; |
733 | struct ssam_bas_base_info base; |
734 | int status, tablet; |
735 | u8 mode; |
736 | |
737 | /* Get operation mode. */ |
738 | status = ssam_retry(ssam_bas_get_device_mode, ddev->ctrl, &mode); |
739 | if (status) { |
740 | dev_err(ddev->dev, "failed to get device mode: %d\n" , status); |
741 | return; |
742 | } |
743 | |
744 | /* Get base info. */ |
745 | status = ssam_retry(ssam_bas_get_base, ddev->ctrl, &base); |
746 | if (status) { |
747 | dev_err(ddev->dev, "failed to get base info: %d\n" , status); |
748 | return; |
749 | } |
750 | |
751 | /* |
752 | * In some cases (specifically when attaching the base), the device |
753 | * mode isn't updated right away. Thus we check if the device mode |
754 | * makes sense for the given base state and try again later if it |
755 | * doesn't. |
756 | */ |
757 | if (sdtx_device_mode_invalid(mode, base_state: base.state)) { |
758 | dev_dbg(ddev->dev, "device mode is invalid, trying again\n" ); |
759 | sdtx_update_device_mode(ddev, SDTX_DEVICE_MODE_DELAY_RECHECK); |
760 | return; |
761 | } |
762 | |
763 | mutex_lock(&ddev->write_lock); |
764 | clear_bit(nr: SDTX_DEVICE_DIRTY_MODE_BIT, addr: &ddev->flags); |
765 | |
766 | /* Avoid sending duplicate device-mode events. */ |
767 | if (ddev->state.device_mode == mode) { |
768 | mutex_unlock(lock: &ddev->write_lock); |
769 | return; |
770 | } |
771 | |
772 | ddev->state.device_mode = mode; |
773 | |
774 | event.e.length = sizeof(u16); |
775 | event.e.code = SDTX_EVENT_DEVICE_MODE; |
776 | event.v = mode; |
777 | |
778 | sdtx_push_event(ddev, evt: &event.e); |
779 | |
780 | /* Send SW_TABLET_MODE event. */ |
781 | tablet = mode != SDTX_DEVICE_MODE_LAPTOP; |
782 | input_report_switch(dev: ddev->mode_switch, SW_TABLET_MODE, value: tablet); |
783 | input_sync(dev: ddev->mode_switch); |
784 | |
785 | mutex_unlock(lock: &ddev->write_lock); |
786 | } |
787 | |
788 | static void sdtx_update_device_mode(struct sdtx_device *ddev, unsigned long delay) |
789 | { |
790 | schedule_delayed_work(dwork: &ddev->mode_work, delay); |
791 | } |
792 | |
793 | /* Must be executed with ddev->write_lock held. */ |
794 | static void __sdtx_device_state_update_base(struct sdtx_device *ddev, |
795 | struct ssam_bas_base_info info) |
796 | { |
797 | struct sdtx_base_info_event event; |
798 | |
799 | lockdep_assert_held(&ddev->write_lock); |
800 | |
801 | /* Prevent duplicate events. */ |
802 | if (ddev->state.base.state == info.state && |
803 | ddev->state.base.base_id == info.base_id) |
804 | return; |
805 | |
806 | ddev->state.base = info; |
807 | |
808 | event.e.length = sizeof(struct sdtx_base_info); |
809 | event.e.code = SDTX_EVENT_BASE_CONNECTION; |
810 | event.v.state = sdtx_translate_base_state(ddev, state: info.state); |
811 | event.v.base_id = SDTX_BASE_TYPE_SSH(info.base_id); |
812 | |
813 | sdtx_push_event(ddev, evt: &event.e); |
814 | } |
815 | |
816 | /* Must be executed with ddev->write_lock held. */ |
817 | static void __sdtx_device_state_update_mode(struct sdtx_device *ddev, u8 mode) |
818 | { |
819 | struct sdtx_status_event event; |
820 | int tablet; |
821 | |
822 | /* |
823 | * Note: This function must be called after updating the base state |
824 | * via __sdtx_device_state_update_base(), as we rely on the updated |
825 | * base state value in the validity check below. |
826 | */ |
827 | |
828 | lockdep_assert_held(&ddev->write_lock); |
829 | |
830 | if (sdtx_device_mode_invalid(mode, base_state: ddev->state.base.state)) { |
831 | dev_dbg(ddev->dev, "device mode is invalid, trying again\n" ); |
832 | sdtx_update_device_mode(ddev, SDTX_DEVICE_MODE_DELAY_RECHECK); |
833 | return; |
834 | } |
835 | |
836 | /* Prevent duplicate events. */ |
837 | if (ddev->state.device_mode == mode) |
838 | return; |
839 | |
840 | ddev->state.device_mode = mode; |
841 | |
842 | /* Send event. */ |
843 | event.e.length = sizeof(u16); |
844 | event.e.code = SDTX_EVENT_DEVICE_MODE; |
845 | event.v = mode; |
846 | |
847 | sdtx_push_event(ddev, evt: &event.e); |
848 | |
849 | /* Send SW_TABLET_MODE event. */ |
850 | tablet = mode != SDTX_DEVICE_MODE_LAPTOP; |
851 | input_report_switch(dev: ddev->mode_switch, SW_TABLET_MODE, value: tablet); |
852 | input_sync(dev: ddev->mode_switch); |
853 | } |
854 | |
855 | /* Must be executed with ddev->write_lock held. */ |
856 | static void __sdtx_device_state_update_latch(struct sdtx_device *ddev, u8 status) |
857 | { |
858 | struct sdtx_status_event event; |
859 | |
860 | lockdep_assert_held(&ddev->write_lock); |
861 | |
862 | /* Prevent duplicate events. */ |
863 | if (ddev->state.latch_status == status) |
864 | return; |
865 | |
866 | ddev->state.latch_status = status; |
867 | |
868 | event.e.length = sizeof(struct sdtx_base_info); |
869 | event.e.code = SDTX_EVENT_BASE_CONNECTION; |
870 | event.v = sdtx_translate_latch_status(ddev, status); |
871 | |
872 | sdtx_push_event(ddev, evt: &event.e); |
873 | } |
874 | |
875 | static void sdtx_device_state_workfn(struct work_struct *work) |
876 | { |
877 | struct sdtx_device *ddev = container_of(work, struct sdtx_device, state_work.work); |
878 | struct ssam_bas_base_info base; |
879 | u8 mode, latch; |
880 | int status; |
881 | |
882 | /* Mark everything as dirty. */ |
883 | set_bit(nr: SDTX_DEVICE_DIRTY_BASE_BIT, addr: &ddev->flags); |
884 | set_bit(nr: SDTX_DEVICE_DIRTY_MODE_BIT, addr: &ddev->flags); |
885 | set_bit(nr: SDTX_DEVICE_DIRTY_LATCH_BIT, addr: &ddev->flags); |
886 | |
887 | /* |
888 | * Ensure that the state gets marked as dirty before continuing to |
889 | * query it. Necessary to ensure that clear_bit() calls in |
890 | * sdtx_notifier() and sdtx_device_mode_workfn() actually clear these |
891 | * bits if an event is received while updating the state here. |
892 | */ |
893 | smp_mb__after_atomic(); |
894 | |
895 | status = ssam_retry(ssam_bas_get_base, ddev->ctrl, &base); |
896 | if (status) { |
897 | dev_err(ddev->dev, "failed to get base state: %d\n" , status); |
898 | return; |
899 | } |
900 | |
901 | status = ssam_retry(ssam_bas_get_device_mode, ddev->ctrl, &mode); |
902 | if (status) { |
903 | dev_err(ddev->dev, "failed to get device mode: %d\n" , status); |
904 | return; |
905 | } |
906 | |
907 | status = ssam_retry(ssam_bas_get_latch_status, ddev->ctrl, &latch); |
908 | if (status) { |
909 | dev_err(ddev->dev, "failed to get latch status: %d\n" , status); |
910 | return; |
911 | } |
912 | |
913 | mutex_lock(&ddev->write_lock); |
914 | |
915 | /* |
916 | * If the respective dirty-bit has been cleared, an event has been |
917 | * received, updating this state. The queried state may thus be out of |
918 | * date. At this point, we can safely assume that the state provided |
919 | * by the event is either up to date, or we're about to receive |
920 | * another event updating it. |
921 | */ |
922 | |
923 | if (test_and_clear_bit(nr: SDTX_DEVICE_DIRTY_BASE_BIT, addr: &ddev->flags)) |
924 | __sdtx_device_state_update_base(ddev, info: base); |
925 | |
926 | if (test_and_clear_bit(nr: SDTX_DEVICE_DIRTY_MODE_BIT, addr: &ddev->flags)) |
927 | __sdtx_device_state_update_mode(ddev, mode); |
928 | |
929 | if (test_and_clear_bit(nr: SDTX_DEVICE_DIRTY_LATCH_BIT, addr: &ddev->flags)) |
930 | __sdtx_device_state_update_latch(ddev, status: latch); |
931 | |
932 | mutex_unlock(lock: &ddev->write_lock); |
933 | } |
934 | |
935 | static void sdtx_update_device_state(struct sdtx_device *ddev, unsigned long delay) |
936 | { |
937 | schedule_delayed_work(dwork: &ddev->state_work, delay); |
938 | } |
939 | |
940 | |
941 | /* -- Common device initialization. ----------------------------------------- */ |
942 | |
943 | static int sdtx_device_init(struct sdtx_device *ddev, struct device *dev, |
944 | struct ssam_controller *ctrl) |
945 | { |
946 | int status, tablet_mode; |
947 | |
948 | /* Basic initialization. */ |
949 | kref_init(kref: &ddev->kref); |
950 | init_rwsem(&ddev->lock); |
951 | ddev->dev = dev; |
952 | ddev->ctrl = ctrl; |
953 | |
954 | ddev->mdev.minor = MISC_DYNAMIC_MINOR; |
955 | ddev->mdev.name = "surface_dtx" ; |
956 | ddev->mdev.nodename = "surface/dtx" ; |
957 | ddev->mdev.fops = &surface_dtx_fops; |
958 | |
959 | ddev->notif.base.priority = 1; |
960 | ddev->notif.base.fn = sdtx_notifier; |
961 | ddev->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; |
962 | ddev->notif.event.id.target_category = SSAM_SSH_TC_BAS; |
963 | ddev->notif.event.id.instance = 0; |
964 | ddev->notif.event.mask = SSAM_EVENT_MASK_NONE; |
965 | ddev->notif.event.flags = SSAM_EVENT_SEQUENCED; |
966 | |
967 | init_waitqueue_head(&ddev->waitq); |
968 | mutex_init(&ddev->write_lock); |
969 | init_rwsem(&ddev->client_lock); |
970 | INIT_LIST_HEAD(list: &ddev->client_list); |
971 | |
972 | INIT_DELAYED_WORK(&ddev->mode_work, sdtx_device_mode_workfn); |
973 | INIT_DELAYED_WORK(&ddev->state_work, sdtx_device_state_workfn); |
974 | |
975 | /* |
976 | * Get current device state. We want to guarantee that events are only |
977 | * sent when state actually changes. Thus we cannot use special |
978 | * "uninitialized" values, as that would cause problems when manually |
979 | * querying the state in surface_dtx_pm_complete(). I.e. we would not |
980 | * be able to detect state changes there if no change event has been |
981 | * received between driver initialization and first device suspension. |
982 | * |
983 | * Note that we also need to do this before registering the event |
984 | * notifier, as that may access the state values. |
985 | */ |
986 | status = ssam_retry(ssam_bas_get_base, ddev->ctrl, &ddev->state.base); |
987 | if (status) |
988 | return status; |
989 | |
990 | status = ssam_retry(ssam_bas_get_device_mode, ddev->ctrl, &ddev->state.device_mode); |
991 | if (status) |
992 | return status; |
993 | |
994 | status = ssam_retry(ssam_bas_get_latch_status, ddev->ctrl, &ddev->state.latch_status); |
995 | if (status) |
996 | return status; |
997 | |
998 | /* Set up tablet mode switch. */ |
999 | ddev->mode_switch = input_allocate_device(); |
1000 | if (!ddev->mode_switch) |
1001 | return -ENOMEM; |
1002 | |
1003 | ddev->mode_switch->name = "Microsoft Surface DTX Device Mode Switch" ; |
1004 | ddev->mode_switch->phys = "ssam/01:11:01:00:00/input0" ; |
1005 | ddev->mode_switch->id.bustype = BUS_HOST; |
1006 | ddev->mode_switch->dev.parent = ddev->dev; |
1007 | |
1008 | tablet_mode = (ddev->state.device_mode != SDTX_DEVICE_MODE_LAPTOP); |
1009 | input_set_capability(dev: ddev->mode_switch, EV_SW, SW_TABLET_MODE); |
1010 | input_report_switch(dev: ddev->mode_switch, SW_TABLET_MODE, value: tablet_mode); |
1011 | |
1012 | status = input_register_device(ddev->mode_switch); |
1013 | if (status) { |
1014 | input_free_device(dev: ddev->mode_switch); |
1015 | return status; |
1016 | } |
1017 | |
1018 | /* Set up event notifier. */ |
1019 | status = ssam_notifier_register(ctrl: ddev->ctrl, n: &ddev->notif); |
1020 | if (status) |
1021 | goto err_notif; |
1022 | |
1023 | /* Register miscdevice. */ |
1024 | status = misc_register(misc: &ddev->mdev); |
1025 | if (status) |
1026 | goto err_mdev; |
1027 | |
1028 | /* |
1029 | * Update device state in case it has changed between getting the |
1030 | * initial mode and registering the event notifier. |
1031 | */ |
1032 | sdtx_update_device_state(ddev, delay: 0); |
1033 | return 0; |
1034 | |
1035 | err_notif: |
1036 | ssam_notifier_unregister(ctrl: ddev->ctrl, n: &ddev->notif); |
1037 | cancel_delayed_work_sync(dwork: &ddev->mode_work); |
1038 | err_mdev: |
1039 | input_unregister_device(ddev->mode_switch); |
1040 | return status; |
1041 | } |
1042 | |
1043 | static struct sdtx_device *sdtx_device_create(struct device *dev, struct ssam_controller *ctrl) |
1044 | { |
1045 | struct sdtx_device *ddev; |
1046 | int status; |
1047 | |
1048 | ddev = kzalloc(size: sizeof(*ddev), GFP_KERNEL); |
1049 | if (!ddev) |
1050 | return ERR_PTR(error: -ENOMEM); |
1051 | |
1052 | status = sdtx_device_init(ddev, dev, ctrl); |
1053 | if (status) { |
1054 | sdtx_device_put(ddev); |
1055 | return ERR_PTR(error: status); |
1056 | } |
1057 | |
1058 | return ddev; |
1059 | } |
1060 | |
1061 | static void sdtx_device_destroy(struct sdtx_device *ddev) |
1062 | { |
1063 | struct sdtx_client *client; |
1064 | |
1065 | /* |
1066 | * Mark device as shut-down. Prevent new clients from being added and |
1067 | * new operations from being executed. |
1068 | */ |
1069 | set_bit(nr: SDTX_DEVICE_SHUTDOWN_BIT, addr: &ddev->flags); |
1070 | |
1071 | /* Disable notifiers, prevent new events from arriving. */ |
1072 | ssam_notifier_unregister(ctrl: ddev->ctrl, n: &ddev->notif); |
1073 | |
1074 | /* Stop mode_work, prevent access to mode_switch. */ |
1075 | cancel_delayed_work_sync(dwork: &ddev->mode_work); |
1076 | |
1077 | /* Stop state_work. */ |
1078 | cancel_delayed_work_sync(dwork: &ddev->state_work); |
1079 | |
1080 | /* With mode_work canceled, we can unregister the mode_switch. */ |
1081 | input_unregister_device(ddev->mode_switch); |
1082 | |
1083 | /* Wake up async clients. */ |
1084 | down_write(sem: &ddev->client_lock); |
1085 | list_for_each_entry(client, &ddev->client_list, node) { |
1086 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); |
1087 | } |
1088 | up_write(sem: &ddev->client_lock); |
1089 | |
1090 | /* Wake up blocking clients. */ |
1091 | wake_up_interruptible(&ddev->waitq); |
1092 | |
1093 | /* |
1094 | * Wait for clients to finish their current operation. After this, the |
1095 | * controller and device references are guaranteed to be no longer in |
1096 | * use. |
1097 | */ |
1098 | down_write(sem: &ddev->lock); |
1099 | ddev->dev = NULL; |
1100 | ddev->ctrl = NULL; |
1101 | up_write(sem: &ddev->lock); |
1102 | |
1103 | /* Finally remove the misc-device. */ |
1104 | misc_deregister(misc: &ddev->mdev); |
1105 | |
1106 | /* |
1107 | * We're now guaranteed that sdtx_device_open() won't be called any |
1108 | * more, so we can now drop out reference. |
1109 | */ |
1110 | sdtx_device_put(ddev); |
1111 | } |
1112 | |
1113 | |
1114 | /* -- PM ops. --------------------------------------------------------------- */ |
1115 | |
1116 | #ifdef CONFIG_PM_SLEEP |
1117 | |
1118 | static void surface_dtx_pm_complete(struct device *dev) |
1119 | { |
1120 | struct sdtx_device *ddev = dev_get_drvdata(dev); |
1121 | |
1122 | /* |
1123 | * Normally, the EC will store events while suspended (i.e. in |
1124 | * display-off state) and release them when resumed (i.e. transitioned |
1125 | * to display-on state). During hibernation, however, the EC will be |
1126 | * shut down and does not store events. Furthermore, events might be |
1127 | * dropped during prolonged suspension (it is currently unknown how |
1128 | * big this event buffer is and how it behaves on overruns). |
1129 | * |
1130 | * To prevent any problems, we update the device state here. We do |
1131 | * this delayed to ensure that any events sent by the EC directly |
1132 | * after resuming will be handled first. The delay below has been |
1133 | * chosen (experimentally), so that there should be ample time for |
1134 | * these events to be handled, before we check and, if necessary, |
1135 | * update the state. |
1136 | */ |
1137 | sdtx_update_device_state(ddev, delay: msecs_to_jiffies(m: 1000)); |
1138 | } |
1139 | |
1140 | static const struct dev_pm_ops surface_dtx_pm_ops = { |
1141 | .complete = surface_dtx_pm_complete, |
1142 | }; |
1143 | |
1144 | #else /* CONFIG_PM_SLEEP */ |
1145 | |
1146 | static const struct dev_pm_ops surface_dtx_pm_ops = {}; |
1147 | |
1148 | #endif /* CONFIG_PM_SLEEP */ |
1149 | |
1150 | |
1151 | /* -- Platform driver. ------------------------------------------------------ */ |
1152 | |
1153 | static int surface_dtx_platform_probe(struct platform_device *pdev) |
1154 | { |
1155 | struct ssam_controller *ctrl; |
1156 | struct sdtx_device *ddev; |
1157 | |
1158 | /* Link to EC. */ |
1159 | ctrl = ssam_client_bind(client: &pdev->dev); |
1160 | if (IS_ERR(ptr: ctrl)) |
1161 | return PTR_ERR(ptr: ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ptr: ctrl); |
1162 | |
1163 | ddev = sdtx_device_create(dev: &pdev->dev, ctrl); |
1164 | if (IS_ERR(ptr: ddev)) |
1165 | return PTR_ERR(ptr: ddev); |
1166 | |
1167 | platform_set_drvdata(pdev, data: ddev); |
1168 | return 0; |
1169 | } |
1170 | |
1171 | static void surface_dtx_platform_remove(struct platform_device *pdev) |
1172 | { |
1173 | sdtx_device_destroy(ddev: platform_get_drvdata(pdev)); |
1174 | } |
1175 | |
1176 | static const struct acpi_device_id surface_dtx_acpi_match[] = { |
1177 | { "MSHW0133" , 0 }, |
1178 | { }, |
1179 | }; |
1180 | MODULE_DEVICE_TABLE(acpi, surface_dtx_acpi_match); |
1181 | |
1182 | static struct platform_driver surface_dtx_platform_driver = { |
1183 | .probe = surface_dtx_platform_probe, |
1184 | .remove_new = surface_dtx_platform_remove, |
1185 | .driver = { |
1186 | .name = "surface_dtx_pltf" , |
1187 | .acpi_match_table = surface_dtx_acpi_match, |
1188 | .pm = &surface_dtx_pm_ops, |
1189 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
1190 | }, |
1191 | }; |
1192 | |
1193 | |
1194 | /* -- SSAM device driver. --------------------------------------------------- */ |
1195 | |
1196 | #ifdef CONFIG_SURFACE_AGGREGATOR_BUS |
1197 | |
1198 | static int surface_dtx_ssam_probe(struct ssam_device *sdev) |
1199 | { |
1200 | struct sdtx_device *ddev; |
1201 | |
1202 | ddev = sdtx_device_create(dev: &sdev->dev, ctrl: sdev->ctrl); |
1203 | if (IS_ERR(ptr: ddev)) |
1204 | return PTR_ERR(ptr: ddev); |
1205 | |
1206 | ssam_device_set_drvdata(sdev, data: ddev); |
1207 | return 0; |
1208 | } |
1209 | |
1210 | static void surface_dtx_ssam_remove(struct ssam_device *sdev) |
1211 | { |
1212 | sdtx_device_destroy(ddev: ssam_device_get_drvdata(sdev)); |
1213 | } |
1214 | |
1215 | static const struct ssam_device_id surface_dtx_ssam_match[] = { |
1216 | { SSAM_SDEV(BAS, SAM, 0x00, 0x00) }, |
1217 | { }, |
1218 | }; |
1219 | MODULE_DEVICE_TABLE(ssam, surface_dtx_ssam_match); |
1220 | |
1221 | static struct ssam_device_driver surface_dtx_ssam_driver = { |
1222 | .probe = surface_dtx_ssam_probe, |
1223 | .remove = surface_dtx_ssam_remove, |
1224 | .match_table = surface_dtx_ssam_match, |
1225 | .driver = { |
1226 | .name = "surface_dtx" , |
1227 | .pm = &surface_dtx_pm_ops, |
1228 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
1229 | }, |
1230 | }; |
1231 | |
1232 | static int ssam_dtx_driver_register(void) |
1233 | { |
1234 | return ssam_device_driver_register(&surface_dtx_ssam_driver); |
1235 | } |
1236 | |
1237 | static void ssam_dtx_driver_unregister(void) |
1238 | { |
1239 | ssam_device_driver_unregister(d: &surface_dtx_ssam_driver); |
1240 | } |
1241 | |
1242 | #else /* CONFIG_SURFACE_AGGREGATOR_BUS */ |
1243 | |
1244 | static int ssam_dtx_driver_register(void) |
1245 | { |
1246 | return 0; |
1247 | } |
1248 | |
1249 | static void ssam_dtx_driver_unregister(void) |
1250 | { |
1251 | } |
1252 | |
1253 | #endif /* CONFIG_SURFACE_AGGREGATOR_BUS */ |
1254 | |
1255 | |
1256 | /* -- Module setup. --------------------------------------------------------- */ |
1257 | |
1258 | static int __init surface_dtx_init(void) |
1259 | { |
1260 | int status; |
1261 | |
1262 | status = ssam_dtx_driver_register(); |
1263 | if (status) |
1264 | return status; |
1265 | |
1266 | status = platform_driver_register(&surface_dtx_platform_driver); |
1267 | if (status) |
1268 | ssam_dtx_driver_unregister(); |
1269 | |
1270 | return status; |
1271 | } |
1272 | module_init(surface_dtx_init); |
1273 | |
1274 | static void __exit surface_dtx_exit(void) |
1275 | { |
1276 | platform_driver_unregister(&surface_dtx_platform_driver); |
1277 | ssam_dtx_driver_unregister(); |
1278 | } |
1279 | module_exit(surface_dtx_exit); |
1280 | |
1281 | MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>" ); |
1282 | MODULE_DESCRIPTION("Detachment-system driver for Surface System Aggregator Module" ); |
1283 | MODULE_LICENSE("GPL" ); |
1284 | |