]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Raw serio device providing access to a raw byte stream from underlying | |
3 | * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device | |
4 | * | |
5 | * Copyright (c) 2004 Dmitry Torokhov | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published by | |
9 | * the Free Software Foundation. | |
10 | */ | |
11 | ||
ba538cd2 | 12 | #include <linux/kref.h> |
d43c36dc | 13 | #include <linux/sched.h> |
1da177e4 LT |
14 | #include <linux/slab.h> |
15 | #include <linux/poll.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/serio.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/major.h> | |
20 | #include <linux/device.h> | |
1da177e4 LT |
21 | #include <linux/miscdevice.h> |
22 | #include <linux/wait.h> | |
c4e32e9f | 23 | #include <linux/mutex.h> |
1da177e4 LT |
24 | |
25 | #define DRIVER_DESC "Raw serio driver" | |
26 | ||
27 | MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); | |
28 | MODULE_DESCRIPTION(DRIVER_DESC); | |
29 | MODULE_LICENSE("GPL"); | |
30 | ||
31 | #define SERIO_RAW_QUEUE_LEN 64 | |
32 | struct serio_raw { | |
33 | unsigned char queue[SERIO_RAW_QUEUE_LEN]; | |
34 | unsigned int tail, head; | |
35 | ||
36 | char name[16]; | |
ba538cd2 | 37 | struct kref kref; |
1da177e4 LT |
38 | struct serio *serio; |
39 | struct miscdevice dev; | |
40 | wait_queue_head_t wait; | |
7c5bbb2e | 41 | struct list_head client_list; |
1da177e4 | 42 | struct list_head node; |
85f5b35d | 43 | bool dead; |
1da177e4 LT |
44 | }; |
45 | ||
7c5bbb2e | 46 | struct serio_raw_client { |
1da177e4 LT |
47 | struct fasync_struct *fasync; |
48 | struct serio_raw *serio_raw; | |
49 | struct list_head node; | |
50 | }; | |
51 | ||
c4e32e9f | 52 | static DEFINE_MUTEX(serio_raw_mutex); |
1da177e4 | 53 | static LIST_HEAD(serio_raw_list); |
1da177e4 LT |
54 | |
55 | /********************************************************************* | |
56 | * Interface with userspace (file operations) * | |
57 | *********************************************************************/ | |
58 | ||
59 | static int serio_raw_fasync(int fd, struct file *file, int on) | |
60 | { | |
7c5bbb2e | 61 | struct serio_raw_client *client = file->private_data; |
1da177e4 | 62 | |
7c5bbb2e | 63 | return fasync_helper(fd, file, on, &client->fasync); |
1da177e4 LT |
64 | } |
65 | ||
66 | static struct serio_raw *serio_raw_locate(int minor) | |
67 | { | |
68 | struct serio_raw *serio_raw; | |
69 | ||
70 | list_for_each_entry(serio_raw, &serio_raw_list, node) { | |
71 | if (serio_raw->dev.minor == minor) | |
72 | return serio_raw; | |
73 | } | |
74 | ||
75 | return NULL; | |
76 | } | |
77 | ||
78 | static int serio_raw_open(struct inode *inode, struct file *file) | |
79 | { | |
80 | struct serio_raw *serio_raw; | |
7c5bbb2e DT |
81 | struct serio_raw_client *client; |
82 | int retval; | |
1da177e4 | 83 | |
c4e32e9f | 84 | retval = mutex_lock_interruptible(&serio_raw_mutex); |
1da177e4 | 85 | if (retval) |
77554b4d | 86 | return retval; |
1da177e4 | 87 | |
77554b4d TLSC |
88 | serio_raw = serio_raw_locate(iminor(inode)); |
89 | if (!serio_raw) { | |
1da177e4 LT |
90 | retval = -ENODEV; |
91 | goto out; | |
92 | } | |
93 | ||
85f5b35d | 94 | if (serio_raw->dead) { |
1da177e4 LT |
95 | retval = -ENODEV; |
96 | goto out; | |
97 | } | |
98 | ||
7c5bbb2e DT |
99 | client = kzalloc(sizeof(struct serio_raw_client), GFP_KERNEL); |
100 | if (!client) { | |
1da177e4 LT |
101 | retval = -ENOMEM; |
102 | goto out; | |
103 | } | |
104 | ||
7c5bbb2e DT |
105 | client->serio_raw = serio_raw; |
106 | file->private_data = client; | |
1da177e4 | 107 | |
ba538cd2 | 108 | kref_get(&serio_raw->kref); |
843e784a DT |
109 | |
110 | serio_pause_rx(serio_raw->serio); | |
7c5bbb2e | 111 | list_add_tail(&client->node, &serio_raw->client_list); |
843e784a | 112 | serio_continue_rx(serio_raw->serio); |
1da177e4 LT |
113 | |
114 | out: | |
c4e32e9f | 115 | mutex_unlock(&serio_raw_mutex); |
1da177e4 LT |
116 | return retval; |
117 | } | |
118 | ||
550eca7c | 119 | static void serio_raw_free(struct kref *kref) |
1da177e4 | 120 | { |
ba538cd2 DT |
121 | struct serio_raw *serio_raw = |
122 | container_of(kref, struct serio_raw, kref); | |
1da177e4 | 123 | |
85f5b35d | 124 | put_device(&serio_raw->serio->dev); |
ba538cd2 | 125 | kfree(serio_raw); |
1da177e4 LT |
126 | } |
127 | ||
128 | static int serio_raw_release(struct inode *inode, struct file *file) | |
129 | { | |
7c5bbb2e DT |
130 | struct serio_raw_client *client = file->private_data; |
131 | struct serio_raw *serio_raw = client->serio_raw; | |
1da177e4 | 132 | |
550eca7c DT |
133 | serio_pause_rx(serio_raw->serio); |
134 | list_del(&client->node); | |
135 | serio_continue_rx(serio_raw->serio); | |
1da177e4 | 136 | |
550eca7c DT |
137 | kfree(client); |
138 | ||
139 | kref_put(&serio_raw->kref, serio_raw_free); | |
1da177e4 | 140 | |
1da177e4 LT |
141 | return 0; |
142 | } | |
143 | ||
8c31eb01 | 144 | static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) |
1da177e4 | 145 | { |
8c31eb01 | 146 | bool empty; |
1da177e4 | 147 | |
843e784a | 148 | serio_pause_rx(serio_raw->serio); |
1da177e4 LT |
149 | |
150 | empty = serio_raw->head == serio_raw->tail; | |
151 | if (!empty) { | |
152 | *c = serio_raw->queue[serio_raw->tail]; | |
153 | serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN; | |
154 | } | |
155 | ||
843e784a | 156 | serio_continue_rx(serio_raw->serio); |
1da177e4 LT |
157 | |
158 | return !empty; | |
159 | } | |
160 | ||
15a564d8 DT |
161 | static ssize_t serio_raw_read(struct file *file, char __user *buffer, |
162 | size_t count, loff_t *ppos) | |
1da177e4 | 163 | { |
7c5bbb2e DT |
164 | struct serio_raw_client *client = file->private_data; |
165 | struct serio_raw *serio_raw = client->serio_raw; | |
4f179f71 | 166 | char uninitialized_var(c); |
7a0a27d2 CLC |
167 | ssize_t read = 0; |
168 | int retval; | |
1da177e4 | 169 | |
85f5b35d | 170 | if (serio_raw->dead) |
1da177e4 LT |
171 | return -ENODEV; |
172 | ||
173 | if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK)) | |
174 | return -EAGAIN; | |
175 | ||
7c5bbb2e | 176 | retval = wait_event_interruptible(serio_raw->wait, |
85f5b35d | 177 | serio_raw->head != serio_raw->tail || serio_raw->dead); |
1da177e4 LT |
178 | if (retval) |
179 | return retval; | |
180 | ||
85f5b35d | 181 | if (serio_raw->dead) |
1da177e4 LT |
182 | return -ENODEV; |
183 | ||
7a0a27d2 CLC |
184 | while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { |
185 | if (put_user(c, buffer++)) { | |
186 | retval = -EFAULT; | |
187 | break; | |
188 | } | |
189 | read++; | |
1da177e4 LT |
190 | } |
191 | ||
7a0a27d2 | 192 | return read ?: retval; |
1da177e4 LT |
193 | } |
194 | ||
15a564d8 DT |
195 | static ssize_t serio_raw_write(struct file *file, const char __user *buffer, |
196 | size_t count, loff_t *ppos) | |
1da177e4 | 197 | { |
7c5bbb2e DT |
198 | struct serio_raw_client *client = file->private_data; |
199 | struct serio_raw *serio_raw = client->serio_raw; | |
1da177e4 LT |
200 | ssize_t written = 0; |
201 | int retval; | |
202 | unsigned char c; | |
203 | ||
c4e32e9f | 204 | retval = mutex_lock_interruptible(&serio_raw_mutex); |
1da177e4 LT |
205 | if (retval) |
206 | return retval; | |
207 | ||
85f5b35d | 208 | if (serio_raw->dead) { |
1da177e4 LT |
209 | retval = -ENODEV; |
210 | goto out; | |
211 | } | |
212 | ||
213 | if (count > 32) | |
214 | count = 32; | |
215 | ||
216 | while (count--) { | |
217 | if (get_user(c, buffer++)) { | |
218 | retval = -EFAULT; | |
219 | goto out; | |
220 | } | |
7c5bbb2e | 221 | if (serio_write(serio_raw->serio, c)) { |
1da177e4 LT |
222 | retval = -EIO; |
223 | goto out; | |
224 | } | |
225 | written++; | |
d89c9bcb | 226 | } |
1da177e4 LT |
227 | |
228 | out: | |
c4e32e9f | 229 | mutex_unlock(&serio_raw_mutex); |
4fa07711 | 230 | return written ?: retval; |
1da177e4 LT |
231 | } |
232 | ||
233 | static unsigned int serio_raw_poll(struct file *file, poll_table *wait) | |
234 | { | |
7c5bbb2e DT |
235 | struct serio_raw_client *client = file->private_data; |
236 | struct serio_raw *serio_raw = client->serio_raw; | |
8c1c10d5 | 237 | unsigned int mask; |
1da177e4 | 238 | |
7c5bbb2e | 239 | poll_wait(file, &serio_raw->wait, wait); |
1da177e4 | 240 | |
8c1c10d5 | 241 | mask = serio_raw->dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM; |
7c5bbb2e | 242 | if (serio_raw->head != serio_raw->tail) |
0c62fbf6 | 243 | mask |= POLLIN | POLLRDNORM; |
1da177e4 | 244 | |
0c62fbf6 | 245 | return mask; |
1da177e4 LT |
246 | } |
247 | ||
2b8693c0 | 248 | static const struct file_operations serio_raw_fops = { |
7c5bbb2e DT |
249 | .owner = THIS_MODULE, |
250 | .open = serio_raw_open, | |
251 | .release = serio_raw_release, | |
252 | .read = serio_raw_read, | |
253 | .write = serio_raw_write, | |
254 | .poll = serio_raw_poll, | |
255 | .fasync = serio_raw_fasync, | |
256 | .llseek = noop_llseek, | |
1da177e4 LT |
257 | }; |
258 | ||
259 | ||
260 | /********************************************************************* | |
15a564d8 | 261 | * Interface with serio port * |
1da177e4 LT |
262 | *********************************************************************/ |
263 | ||
264 | static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data, | |
7d12e780 | 265 | unsigned int dfl) |
1da177e4 LT |
266 | { |
267 | struct serio_raw *serio_raw = serio_get_drvdata(serio); | |
7c5bbb2e | 268 | struct serio_raw_client *client; |
1da177e4 LT |
269 | unsigned int head = serio_raw->head; |
270 | ||
7c5bbb2e | 271 | /* we are holding serio->lock here so we are protected */ |
1da177e4 LT |
272 | serio_raw->queue[head] = data; |
273 | head = (head + 1) % SERIO_RAW_QUEUE_LEN; | |
274 | if (likely(head != serio_raw->tail)) { | |
275 | serio_raw->head = head; | |
7c5bbb2e DT |
276 | list_for_each_entry(client, &serio_raw->client_list, node) |
277 | kill_fasync(&client->fasync, SIGIO, POLL_IN); | |
1da177e4 LT |
278 | wake_up_interruptible(&serio_raw->wait); |
279 | } | |
280 | ||
281 | return IRQ_HANDLED; | |
282 | } | |
283 | ||
284 | static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) | |
285 | { | |
550eca7c | 286 | static atomic_t serio_raw_no = ATOMIC_INIT(0); |
1da177e4 LT |
287 | struct serio_raw *serio_raw; |
288 | int err; | |
289 | ||
15a564d8 DT |
290 | serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL); |
291 | if (!serio_raw) { | |
8d928477 | 292 | dev_dbg(&serio->dev, "can't allocate memory for a device\n"); |
1da177e4 LT |
293 | return -ENOMEM; |
294 | } | |
295 | ||
15a564d8 | 296 | snprintf(serio_raw->name, sizeof(serio_raw->name), |
550eca7c | 297 | "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1); |
ba538cd2 | 298 | kref_init(&serio_raw->kref); |
7c5bbb2e | 299 | INIT_LIST_HEAD(&serio_raw->client_list); |
1da177e4 LT |
300 | init_waitqueue_head(&serio_raw->wait); |
301 | ||
85f5b35d DT |
302 | serio_raw->serio = serio; |
303 | get_device(&serio->dev); | |
304 | ||
1da177e4 LT |
305 | serio_set_drvdata(serio, serio_raw); |
306 | ||
307 | err = serio_open(serio, drv); | |
308 | if (err) | |
550eca7c DT |
309 | goto err_free; |
310 | ||
311 | err = mutex_lock_killable(&serio_raw_mutex); | |
312 | if (err) | |
313 | goto err_close; | |
1da177e4 LT |
314 | |
315 | list_add_tail(&serio_raw->node, &serio_raw_list); | |
550eca7c | 316 | mutex_unlock(&serio_raw_mutex); |
1da177e4 LT |
317 | |
318 | serio_raw->dev.minor = PSMOUSE_MINOR; | |
319 | serio_raw->dev.name = serio_raw->name; | |
94fbcded | 320 | serio_raw->dev.parent = &serio->dev; |
1da177e4 LT |
321 | serio_raw->dev.fops = &serio_raw_fops; |
322 | ||
323 | err = misc_register(&serio_raw->dev); | |
324 | if (err) { | |
325 | serio_raw->dev.minor = MISC_DYNAMIC_MINOR; | |
326 | err = misc_register(&serio_raw->dev); | |
327 | } | |
328 | ||
329 | if (err) { | |
8d928477 DT |
330 | dev_err(&serio->dev, |
331 | "failed to register raw access device for %s\n", | |
1da177e4 | 332 | serio->phys); |
550eca7c | 333 | goto err_unlink; |
1da177e4 LT |
334 | } |
335 | ||
8d928477 DT |
336 | dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n", |
337 | serio->phys, serio_raw->name, serio_raw->dev.minor); | |
550eca7c | 338 | return 0; |
1da177e4 | 339 | |
550eca7c | 340 | err_unlink: |
1da177e4 | 341 | list_del_init(&serio_raw->node); |
550eca7c DT |
342 | err_close: |
343 | serio_close(serio); | |
344 | err_free: | |
1da177e4 | 345 | serio_set_drvdata(serio, NULL); |
550eca7c | 346 | kref_put(&serio_raw->kref, serio_raw_free); |
1da177e4 LT |
347 | return err; |
348 | } | |
349 | ||
350 | static int serio_raw_reconnect(struct serio *serio) | |
351 | { | |
352 | struct serio_raw *serio_raw = serio_get_drvdata(serio); | |
353 | struct serio_driver *drv = serio->drv; | |
354 | ||
355 | if (!drv || !serio_raw) { | |
8d928477 DT |
356 | dev_dbg(&serio->dev, |
357 | "reconnect request, but serio is disconnected, ignoring...\n"); | |
1da177e4 LT |
358 | return -1; |
359 | } | |
360 | ||
361 | /* | |
362 | * Nothing needs to be done here, we just need this method to | |
363 | * keep the same device. | |
364 | */ | |
365 | return 0; | |
366 | } | |
367 | ||
8c1c10d5 DT |
368 | /* |
369 | * Wake up users waiting for IO so they can disconnect from | |
370 | * dead device. | |
371 | */ | |
372 | static void serio_raw_hangup(struct serio_raw *serio_raw) | |
373 | { | |
374 | struct serio_raw_client *client; | |
375 | ||
376 | serio_pause_rx(serio_raw->serio); | |
377 | list_for_each_entry(client, &serio_raw->client_list, node) | |
378 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); | |
379 | serio_continue_rx(serio_raw->serio); | |
380 | ||
381 | wake_up_interruptible(&serio_raw->wait); | |
382 | } | |
383 | ||
384 | ||
1da177e4 LT |
385 | static void serio_raw_disconnect(struct serio *serio) |
386 | { | |
8c1c10d5 | 387 | struct serio_raw *serio_raw = serio_get_drvdata(serio); |
1da177e4 | 388 | |
550eca7c | 389 | misc_deregister(&serio_raw->dev); |
1da177e4 | 390 | |
550eca7c | 391 | mutex_lock(&serio_raw_mutex); |
85f5b35d | 392 | serio_raw->dead = true; |
550eca7c DT |
393 | list_del_init(&serio_raw->node); |
394 | mutex_unlock(&serio_raw_mutex); | |
395 | ||
8c1c10d5 | 396 | serio_raw_hangup(serio_raw); |
1da177e4 | 397 | |
550eca7c DT |
398 | serio_close(serio); |
399 | kref_put(&serio_raw->kref, serio_raw_free); | |
8c1c10d5 DT |
400 | |
401 | serio_set_drvdata(serio, NULL); | |
1da177e4 LT |
402 | } |
403 | ||
404 | static struct serio_device_id serio_raw_serio_ids[] = { | |
405 | { | |
406 | .type = SERIO_8042, | |
407 | .proto = SERIO_ANY, | |
408 | .id = SERIO_ANY, | |
409 | .extra = SERIO_ANY, | |
410 | }, | |
d19497e2 NV |
411 | { |
412 | .type = SERIO_8042_XL, | |
413 | .proto = SERIO_ANY, | |
414 | .id = SERIO_ANY, | |
415 | .extra = SERIO_ANY, | |
416 | }, | |
1da177e4 LT |
417 | { 0 } |
418 | }; | |
419 | ||
420 | MODULE_DEVICE_TABLE(serio, serio_raw_serio_ids); | |
421 | ||
422 | static struct serio_driver serio_raw_drv = { | |
423 | .driver = { | |
424 | .name = "serio_raw", | |
425 | }, | |
426 | .description = DRIVER_DESC, | |
427 | .id_table = serio_raw_serio_ids, | |
428 | .interrupt = serio_raw_interrupt, | |
429 | .connect = serio_raw_connect, | |
430 | .reconnect = serio_raw_reconnect, | |
431 | .disconnect = serio_raw_disconnect, | |
8c31eb01 | 432 | .manual_bind = true, |
1da177e4 LT |
433 | }; |
434 | ||
435 | static int __init serio_raw_init(void) | |
436 | { | |
153a9df0 | 437 | return serio_register_driver(&serio_raw_drv); |
1da177e4 LT |
438 | } |
439 | ||
440 | static void __exit serio_raw_exit(void) | |
441 | { | |
442 | serio_unregister_driver(&serio_raw_drv); | |
443 | } | |
444 | ||
445 | module_init(serio_raw_init); | |
446 | module_exit(serio_raw_exit); |