1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * drivers/usb/input/yealink.c |
4 | * |
5 | * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com> |
6 | */ |
7 | /* |
8 | * Description: |
9 | * Driver for the USB-P1K voip usb phone. |
10 | * This device is produced by Yealink Network Technology Co Ltd |
11 | * but may be branded under several names: |
12 | * - Yealink usb-p1k |
13 | * - Tiptel 115 |
14 | * - ... |
15 | * |
16 | * This driver is based on: |
17 | * - the usbb2k-api http://savannah.nongnu.org/projects/usbb2k-api/ |
18 | * - information from http://memeteau.free.fr/usbb2k |
19 | * - the xpad-driver drivers/input/joystick/xpad.c |
20 | * |
21 | * Thanks to: |
22 | * - Olivier Vandorpe, for providing the usbb2k-api. |
23 | * - Martin Diehl, for spotting my memory allocation bug. |
24 | * |
25 | * History: |
26 | * 20050527 henk First version, functional keyboard. Keyboard events |
27 | * will pop-up on the ../input/eventX bus. |
28 | * 20050531 henk Added led, LCD, dialtone and sysfs interface. |
29 | * 20050610 henk Cleanups, make it ready for public consumption. |
30 | * 20050630 henk Cleanups, fixes in response to comments. |
31 | * 20050701 henk sysfs write serialisation, fix potential unload races |
32 | * 20050801 henk Added ringtone, restructure USB |
33 | * 20050816 henk Merge 2.6.13-rc6 |
34 | */ |
35 | |
36 | #include <linux/kernel.h> |
37 | #include <linux/slab.h> |
38 | #include <linux/module.h> |
39 | #include <linux/rwsem.h> |
40 | #include <linux/usb/input.h> |
41 | #include <linux/map_to_7segment.h> |
42 | |
43 | #include "yealink.h" |
44 | |
45 | #define DRIVER_VERSION "yld-20051230" |
46 | |
47 | #define YEALINK_POLLING_FREQUENCY 10 /* in [Hz] */ |
48 | |
49 | struct yld_status { |
50 | u8 lcd[24]; |
51 | u8 led; |
52 | u8 dialtone; |
53 | u8 ringtone; |
54 | u8 keynum; |
55 | } __attribute__ ((packed)); |
56 | |
57 | /* |
58 | * Register the LCD segment and icon map |
59 | */ |
60 | #define _LOC(k,l) { .a = (k), .m = (l) } |
61 | #define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm) \ |
62 | { .type = (t), \ |
63 | .u = { .s = { _LOC(a, am), _LOC(b, bm), _LOC(c, cm), \ |
64 | _LOC(d, dm), _LOC(e, em), _LOC(g, gm), \ |
65 | _LOC(f, fm) } } } |
66 | #define _PIC(t, h, hm, n) \ |
67 | { .type = (t), \ |
68 | .u = { .p = { .name = (n), .a = (h), .m = (hm) } } } |
69 | |
70 | static const struct lcd_segment_map { |
71 | char type; |
72 | union { |
73 | struct pictogram_map { |
74 | u8 a,m; |
75 | char name[10]; |
76 | } p; |
77 | struct segment_map { |
78 | u8 a,m; |
79 | } s[7]; |
80 | } u; |
81 | } lcdMap[] = { |
82 | #include "yealink.h" |
83 | }; |
84 | |
85 | struct yealink_dev { |
86 | struct input_dev *idev; /* input device */ |
87 | struct usb_device *udev; /* usb device */ |
88 | struct usb_interface *intf; /* usb interface */ |
89 | |
90 | /* irq input channel */ |
91 | struct yld_ctl_packet *irq_data; |
92 | dma_addr_t irq_dma; |
93 | struct urb *urb_irq; |
94 | |
95 | /* control output channel */ |
96 | struct yld_ctl_packet *ctl_data; |
97 | dma_addr_t ctl_dma; |
98 | struct usb_ctrlrequest *ctl_req; |
99 | struct urb *urb_ctl; |
100 | |
101 | char phys[64]; /* physical device path */ |
102 | |
103 | u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */ |
104 | int key_code; /* last reported key */ |
105 | |
106 | unsigned int shutdown:1; |
107 | |
108 | int stat_ix; |
109 | union { |
110 | struct yld_status s; |
111 | u8 b[sizeof(struct yld_status)]; |
112 | } master, copy; |
113 | }; |
114 | |
115 | |
116 | /******************************************************************************* |
117 | * Yealink lcd interface |
118 | ******************************************************************************/ |
119 | |
120 | /* |
121 | * Register a default 7 segment character set |
122 | */ |
123 | static SEG7_DEFAULT_MAP(map_seg7); |
124 | |
125 | /* Display a char, |
126 | * char '\9' and '\n' are placeholders and do not overwrite the original text. |
127 | * A space will always hide an icon. |
128 | */ |
129 | static int setChar(struct yealink_dev *yld, int el, int chr) |
130 | { |
131 | int i, a, m, val; |
132 | |
133 | if (el >= ARRAY_SIZE(lcdMap)) |
134 | return -EINVAL; |
135 | |
136 | if (chr == '\t' || chr == '\n') |
137 | return 0; |
138 | |
139 | yld->lcdMap[el] = chr; |
140 | |
141 | if (lcdMap[el].type == '.') { |
142 | a = lcdMap[el].u.p.a; |
143 | m = lcdMap[el].u.p.m; |
144 | if (chr != ' ') |
145 | yld->master.b[a] |= m; |
146 | else |
147 | yld->master.b[a] &= ~m; |
148 | return 0; |
149 | } |
150 | |
151 | val = map_to_seg7(map: &map_seg7, c: chr); |
152 | for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) { |
153 | m = lcdMap[el].u.s[i].m; |
154 | |
155 | if (m == 0) |
156 | continue; |
157 | |
158 | a = lcdMap[el].u.s[i].a; |
159 | if (val & 1) |
160 | yld->master.b[a] |= m; |
161 | else |
162 | yld->master.b[a] &= ~m; |
163 | val = val >> 1; |
164 | } |
165 | return 0; |
166 | }; |
167 | |
168 | /******************************************************************************* |
169 | * Yealink key interface |
170 | ******************************************************************************/ |
171 | |
172 | /* Map device buttons to internal key events. |
173 | * |
174 | * USB-P1K button layout: |
175 | * |
176 | * up |
177 | * IN OUT |
178 | * down |
179 | * |
180 | * pickup C hangup |
181 | * 1 2 3 |
182 | * 4 5 6 |
183 | * 7 8 9 |
184 | * * 0 # |
185 | * |
186 | * The "up" and "down" keys, are symbolised by arrows on the button. |
187 | * The "pickup" and "hangup" keys are symbolised by a green and red phone |
188 | * on the button. |
189 | */ |
190 | static int map_p1k_to_key(int scancode) |
191 | { |
192 | switch(scancode) { /* phone key: */ |
193 | case 0x23: return KEY_LEFT; /* IN */ |
194 | case 0x33: return KEY_UP; /* up */ |
195 | case 0x04: return KEY_RIGHT; /* OUT */ |
196 | case 0x24: return KEY_DOWN; /* down */ |
197 | case 0x03: return KEY_ENTER; /* pickup */ |
198 | case 0x14: return KEY_BACKSPACE; /* C */ |
199 | case 0x13: return KEY_ESC; /* hangup */ |
200 | case 0x00: return KEY_1; /* 1 */ |
201 | case 0x01: return KEY_2; /* 2 */ |
202 | case 0x02: return KEY_3; /* 3 */ |
203 | case 0x10: return KEY_4; /* 4 */ |
204 | case 0x11: return KEY_5; /* 5 */ |
205 | case 0x12: return KEY_6; /* 6 */ |
206 | case 0x20: return KEY_7; /* 7 */ |
207 | case 0x21: return KEY_8; /* 8 */ |
208 | case 0x22: return KEY_9; /* 9 */ |
209 | case 0x30: return KEY_KPASTERISK; /* * */ |
210 | case 0x31: return KEY_0; /* 0 */ |
211 | case 0x32: return KEY_LEFTSHIFT | |
212 | KEY_3 << 8; /* # */ |
213 | } |
214 | return -EINVAL; |
215 | } |
216 | |
217 | /* Completes a request by converting the data into events for the |
218 | * input subsystem. |
219 | * |
220 | * The key parameter can be cascaded: key2 << 8 | key1 |
221 | */ |
222 | static void report_key(struct yealink_dev *yld, int key) |
223 | { |
224 | struct input_dev *idev = yld->idev; |
225 | |
226 | if (yld->key_code >= 0) { |
227 | /* old key up */ |
228 | input_report_key(dev: idev, code: yld->key_code & 0xff, value: 0); |
229 | if (yld->key_code >> 8) |
230 | input_report_key(dev: idev, code: yld->key_code >> 8, value: 0); |
231 | } |
232 | |
233 | yld->key_code = key; |
234 | if (key >= 0) { |
235 | /* new valid key */ |
236 | input_report_key(dev: idev, code: key & 0xff, value: 1); |
237 | if (key >> 8) |
238 | input_report_key(dev: idev, code: key >> 8, value: 1); |
239 | } |
240 | input_sync(dev: idev); |
241 | } |
242 | |
243 | /******************************************************************************* |
244 | * Yealink usb communication interface |
245 | ******************************************************************************/ |
246 | |
247 | static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p) |
248 | { |
249 | u8 *buf = (u8 *)p; |
250 | int i; |
251 | u8 sum = 0; |
252 | |
253 | for(i=0; i<USB_PKT_LEN-1; i++) |
254 | sum -= buf[i]; |
255 | p->sum = sum; |
256 | return usb_control_msg(dev: yld->udev, |
257 | usb_sndctrlpipe(yld->udev, 0), |
258 | USB_REQ_SET_CONFIGURATION, |
259 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, |
260 | value: 0x200, index: 3, |
261 | data: p, size: sizeof(*p), |
262 | USB_CTRL_SET_TIMEOUT); |
263 | } |
264 | |
265 | static u8 default_ringtone[] = { |
266 | 0xEF, /* volume [0-255] */ |
267 | 0xFB, 0x1E, 0x00, 0x0C, /* 1250 [hz], 12/100 [s] */ |
268 | 0xFC, 0x18, 0x00, 0x0C, /* 1000 [hz], 12/100 [s] */ |
269 | 0xFB, 0x1E, 0x00, 0x0C, |
270 | 0xFC, 0x18, 0x00, 0x0C, |
271 | 0xFB, 0x1E, 0x00, 0x0C, |
272 | 0xFC, 0x18, 0x00, 0x0C, |
273 | 0xFB, 0x1E, 0x00, 0x0C, |
274 | 0xFC, 0x18, 0x00, 0x0C, |
275 | 0xFF, 0xFF, 0x01, 0x90, /* silent, 400/100 [s] */ |
276 | 0x00, 0x00 /* end of sequence */ |
277 | }; |
278 | |
279 | static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size) |
280 | { |
281 | struct yld_ctl_packet *p = yld->ctl_data; |
282 | int ix, len; |
283 | |
284 | if (size <= 0) |
285 | return -EINVAL; |
286 | |
287 | /* Set the ringtone volume */ |
288 | memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); |
289 | yld->ctl_data->cmd = CMD_RING_VOLUME; |
290 | yld->ctl_data->size = 1; |
291 | yld->ctl_data->data[0] = buf[0]; |
292 | yealink_cmd(yld, p); |
293 | |
294 | buf++; |
295 | size--; |
296 | |
297 | p->cmd = CMD_RING_NOTE; |
298 | ix = 0; |
299 | while (size != ix) { |
300 | len = size - ix; |
301 | if (len > sizeof(p->data)) |
302 | len = sizeof(p->data); |
303 | p->size = len; |
304 | p->offset = cpu_to_be16(ix); |
305 | memcpy(p->data, &buf[ix], len); |
306 | yealink_cmd(yld, p); |
307 | ix += len; |
308 | } |
309 | return 0; |
310 | } |
311 | |
312 | /* keep stat_master & stat_copy in sync. |
313 | */ |
314 | static int yealink_do_idle_tasks(struct yealink_dev *yld) |
315 | { |
316 | u8 val; |
317 | int i, ix, len; |
318 | |
319 | ix = yld->stat_ix; |
320 | |
321 | memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); |
322 | yld->ctl_data->cmd = CMD_KEYPRESS; |
323 | yld->ctl_data->size = 1; |
324 | yld->ctl_data->sum = 0xff - CMD_KEYPRESS; |
325 | |
326 | /* If state update pointer wraps do a KEYPRESS first. */ |
327 | if (ix >= sizeof(yld->master)) { |
328 | yld->stat_ix = 0; |
329 | return 0; |
330 | } |
331 | |
332 | /* find update candidates: copy != master */ |
333 | do { |
334 | val = yld->master.b[ix]; |
335 | if (val != yld->copy.b[ix]) |
336 | goto send_update; |
337 | } while (++ix < sizeof(yld->master)); |
338 | |
339 | /* nothing todo, wait a bit and poll for a KEYPRESS */ |
340 | yld->stat_ix = 0; |
341 | /* TODO how can we wait abit. ?? |
342 | * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY); |
343 | */ |
344 | return 0; |
345 | |
346 | send_update: |
347 | |
348 | /* Setup an appropriate update request */ |
349 | yld->copy.b[ix] = val; |
350 | yld->ctl_data->data[0] = val; |
351 | |
352 | switch(ix) { |
353 | case offsetof(struct yld_status, led): |
354 | yld->ctl_data->cmd = CMD_LED; |
355 | yld->ctl_data->sum = -1 - CMD_LED - val; |
356 | break; |
357 | case offsetof(struct yld_status, dialtone): |
358 | yld->ctl_data->cmd = CMD_DIALTONE; |
359 | yld->ctl_data->sum = -1 - CMD_DIALTONE - val; |
360 | break; |
361 | case offsetof(struct yld_status, ringtone): |
362 | yld->ctl_data->cmd = CMD_RINGTONE; |
363 | yld->ctl_data->sum = -1 - CMD_RINGTONE - val; |
364 | break; |
365 | case offsetof(struct yld_status, keynum): |
366 | val--; |
367 | val &= 0x1f; |
368 | yld->ctl_data->cmd = CMD_SCANCODE; |
369 | yld->ctl_data->offset = cpu_to_be16(val); |
370 | yld->ctl_data->data[0] = 0; |
371 | yld->ctl_data->sum = -1 - CMD_SCANCODE - val; |
372 | break; |
373 | default: |
374 | len = sizeof(yld->master.s.lcd) - ix; |
375 | if (len > sizeof(yld->ctl_data->data)) |
376 | len = sizeof(yld->ctl_data->data); |
377 | |
378 | /* Combine up to <len> consecutive LCD bytes in a singe request |
379 | */ |
380 | yld->ctl_data->cmd = CMD_LCD; |
381 | yld->ctl_data->offset = cpu_to_be16(ix); |
382 | yld->ctl_data->size = len; |
383 | yld->ctl_data->sum = -CMD_LCD - ix - val - len; |
384 | for(i=1; i<len; i++) { |
385 | ix++; |
386 | val = yld->master.b[ix]; |
387 | yld->copy.b[ix] = val; |
388 | yld->ctl_data->data[i] = val; |
389 | yld->ctl_data->sum -= val; |
390 | } |
391 | } |
392 | yld->stat_ix = ix + 1; |
393 | return 1; |
394 | } |
395 | |
396 | /* Decide on how to handle responses |
397 | * |
398 | * The state transition diagram is somethhing like: |
399 | * |
400 | * syncState<--+ |
401 | * | | |
402 | * | idle |
403 | * \|/ | |
404 | * init --ok--> waitForKey --ok--> getKey |
405 | * ^ ^ | |
406 | * | +-------ok-------+ |
407 | * error,start |
408 | * |
409 | */ |
410 | static void urb_irq_callback(struct urb *urb) |
411 | { |
412 | struct yealink_dev *yld = urb->context; |
413 | int ret, status = urb->status; |
414 | |
415 | if (status) |
416 | dev_err(&yld->intf->dev, "%s - urb status %d\n" , |
417 | __func__, status); |
418 | |
419 | switch (yld->irq_data->cmd) { |
420 | case CMD_KEYPRESS: |
421 | |
422 | yld->master.s.keynum = yld->irq_data->data[0]; |
423 | break; |
424 | |
425 | case CMD_SCANCODE: |
426 | dev_dbg(&yld->intf->dev, "get scancode %x\n" , |
427 | yld->irq_data->data[0]); |
428 | |
429 | report_key(yld, key: map_p1k_to_key(scancode: yld->irq_data->data[0])); |
430 | break; |
431 | |
432 | default: |
433 | dev_err(&yld->intf->dev, "unexpected response %x\n" , |
434 | yld->irq_data->cmd); |
435 | } |
436 | |
437 | yealink_do_idle_tasks(yld); |
438 | |
439 | if (!yld->shutdown) { |
440 | ret = usb_submit_urb(urb: yld->urb_ctl, GFP_ATOMIC); |
441 | if (ret && ret != -EPERM) |
442 | dev_err(&yld->intf->dev, |
443 | "%s - usb_submit_urb failed %d\n" , |
444 | __func__, ret); |
445 | } |
446 | } |
447 | |
448 | static void urb_ctl_callback(struct urb *urb) |
449 | { |
450 | struct yealink_dev *yld = urb->context; |
451 | int ret = 0, status = urb->status; |
452 | |
453 | if (status) |
454 | dev_err(&yld->intf->dev, "%s - urb status %d\n" , |
455 | __func__, status); |
456 | |
457 | switch (yld->ctl_data->cmd) { |
458 | case CMD_KEYPRESS: |
459 | case CMD_SCANCODE: |
460 | /* ask for a response */ |
461 | if (!yld->shutdown) |
462 | ret = usb_submit_urb(urb: yld->urb_irq, GFP_ATOMIC); |
463 | break; |
464 | default: |
465 | /* send new command */ |
466 | yealink_do_idle_tasks(yld); |
467 | if (!yld->shutdown) |
468 | ret = usb_submit_urb(urb: yld->urb_ctl, GFP_ATOMIC); |
469 | break; |
470 | } |
471 | |
472 | if (ret && ret != -EPERM) |
473 | dev_err(&yld->intf->dev, "%s - usb_submit_urb failed %d\n" , |
474 | __func__, ret); |
475 | } |
476 | |
477 | /******************************************************************************* |
478 | * input event interface |
479 | ******************************************************************************/ |
480 | |
481 | /* TODO should we issue a ringtone on a SND_BELL event? |
482 | static int input_ev(struct input_dev *dev, unsigned int type, |
483 | unsigned int code, int value) |
484 | { |
485 | |
486 | if (type != EV_SND) |
487 | return -EINVAL; |
488 | |
489 | switch (code) { |
490 | case SND_BELL: |
491 | case SND_TONE: |
492 | break; |
493 | default: |
494 | return -EINVAL; |
495 | } |
496 | |
497 | return 0; |
498 | } |
499 | */ |
500 | |
501 | static int input_open(struct input_dev *dev) |
502 | { |
503 | struct yealink_dev *yld = input_get_drvdata(dev); |
504 | int i, ret; |
505 | |
506 | dev_dbg(&yld->intf->dev, "%s\n" , __func__); |
507 | |
508 | /* force updates to device */ |
509 | for (i = 0; i<sizeof(yld->master); i++) |
510 | yld->copy.b[i] = ~yld->master.b[i]; |
511 | yld->key_code = -1; /* no keys pressed */ |
512 | |
513 | yealink_set_ringtone(yld, buf: default_ringtone, size: sizeof(default_ringtone)); |
514 | |
515 | /* issue INIT */ |
516 | memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data))); |
517 | yld->ctl_data->cmd = CMD_INIT; |
518 | yld->ctl_data->size = 10; |
519 | yld->ctl_data->sum = 0x100-CMD_INIT-10; |
520 | if ((ret = usb_submit_urb(urb: yld->urb_ctl, GFP_KERNEL)) != 0) { |
521 | dev_dbg(&yld->intf->dev, |
522 | "%s - usb_submit_urb failed with result %d\n" , |
523 | __func__, ret); |
524 | return ret; |
525 | } |
526 | return 0; |
527 | } |
528 | |
529 | static void input_close(struct input_dev *dev) |
530 | { |
531 | struct yealink_dev *yld = input_get_drvdata(dev); |
532 | |
533 | yld->shutdown = 1; |
534 | /* |
535 | * Make sure the flag is seen by other CPUs before we start |
536 | * killing URBs so new URBs won't be submitted |
537 | */ |
538 | smp_wmb(); |
539 | |
540 | usb_kill_urb(urb: yld->urb_ctl); |
541 | usb_kill_urb(urb: yld->urb_irq); |
542 | |
543 | yld->shutdown = 0; |
544 | smp_wmb(); |
545 | } |
546 | |
547 | /******************************************************************************* |
548 | * sysfs interface |
549 | ******************************************************************************/ |
550 | |
551 | static DECLARE_RWSEM(sysfs_rwsema); |
552 | |
553 | /* Interface to the 7-segments translation table aka. char set. |
554 | */ |
555 | static ssize_t show_map(struct device *dev, struct device_attribute *attr, |
556 | char *buf) |
557 | { |
558 | memcpy(buf, &map_seg7, sizeof(map_seg7)); |
559 | return sizeof(map_seg7); |
560 | } |
561 | |
562 | static ssize_t store_map(struct device *dev, struct device_attribute *attr, |
563 | const char *buf, size_t cnt) |
564 | { |
565 | if (cnt != sizeof(map_seg7)) |
566 | return -EINVAL; |
567 | memcpy(&map_seg7, buf, sizeof(map_seg7)); |
568 | return sizeof(map_seg7); |
569 | } |
570 | |
571 | /* Interface to the LCD. |
572 | */ |
573 | |
574 | /* Reading /sys/../lineX will return the format string with its settings: |
575 | * |
576 | * Example: |
577 | * cat ./line3 |
578 | * 888888888888 |
579 | * Linux Rocks! |
580 | */ |
581 | static ssize_t show_line(struct device *dev, char *buf, int a, int b) |
582 | { |
583 | struct yealink_dev *yld; |
584 | int i; |
585 | |
586 | down_read(sem: &sysfs_rwsema); |
587 | yld = dev_get_drvdata(dev); |
588 | if (yld == NULL) { |
589 | up_read(sem: &sysfs_rwsema); |
590 | return -ENODEV; |
591 | } |
592 | |
593 | for (i = a; i < b; i++) |
594 | *buf++ = lcdMap[i].type; |
595 | *buf++ = '\n'; |
596 | for (i = a; i < b; i++) |
597 | *buf++ = yld->lcdMap[i]; |
598 | *buf++ = '\n'; |
599 | *buf = 0; |
600 | |
601 | up_read(sem: &sysfs_rwsema); |
602 | return 3 + ((b - a) << 1); |
603 | } |
604 | |
605 | static ssize_t show_line1(struct device *dev, struct device_attribute *attr, |
606 | char *buf) |
607 | { |
608 | return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET); |
609 | } |
610 | |
611 | static ssize_t show_line2(struct device *dev, struct device_attribute *attr, |
612 | char *buf) |
613 | { |
614 | return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET); |
615 | } |
616 | |
617 | static ssize_t show_line3(struct device *dev, struct device_attribute *attr, |
618 | char *buf) |
619 | { |
620 | return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET); |
621 | } |
622 | |
623 | /* Writing to /sys/../lineX will set the coresponding LCD line. |
624 | * - Excess characters are ignored. |
625 | * - If less characters are written than allowed, the remaining digits are |
626 | * unchanged. |
627 | * - The '\n' or '\t' char is a placeholder, it does not overwrite the |
628 | * original content. |
629 | */ |
630 | static ssize_t store_line(struct device *dev, const char *buf, size_t count, |
631 | int el, size_t len) |
632 | { |
633 | struct yealink_dev *yld; |
634 | int i; |
635 | |
636 | down_write(sem: &sysfs_rwsema); |
637 | yld = dev_get_drvdata(dev); |
638 | if (yld == NULL) { |
639 | up_write(sem: &sysfs_rwsema); |
640 | return -ENODEV; |
641 | } |
642 | |
643 | if (len > count) |
644 | len = count; |
645 | for (i = 0; i < len; i++) |
646 | setChar(yld, el: el++, chr: buf[i]); |
647 | |
648 | up_write(sem: &sysfs_rwsema); |
649 | return count; |
650 | } |
651 | |
652 | static ssize_t store_line1(struct device *dev, struct device_attribute *attr, |
653 | const char *buf, size_t count) |
654 | { |
655 | return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE); |
656 | } |
657 | |
658 | static ssize_t store_line2(struct device *dev, struct device_attribute *attr, |
659 | const char *buf, size_t count) |
660 | { |
661 | return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE); |
662 | } |
663 | |
664 | static ssize_t store_line3(struct device *dev, struct device_attribute *attr, |
665 | const char *buf, size_t count) |
666 | { |
667 | return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE); |
668 | } |
669 | |
670 | /* Interface to visible and audible "icons", these include: |
671 | * pictures on the LCD, the LED, and the dialtone signal. |
672 | */ |
673 | |
674 | /* Get a list of "switchable elements" with their current state. */ |
675 | static ssize_t get_icons(struct device *dev, struct device_attribute *attr, |
676 | char *buf) |
677 | { |
678 | struct yealink_dev *yld; |
679 | int i, ret = 1; |
680 | |
681 | down_read(sem: &sysfs_rwsema); |
682 | yld = dev_get_drvdata(dev); |
683 | if (yld == NULL) { |
684 | up_read(sem: &sysfs_rwsema); |
685 | return -ENODEV; |
686 | } |
687 | |
688 | for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { |
689 | if (lcdMap[i].type != '.') |
690 | continue; |
691 | ret += sprintf(buf: &buf[ret], fmt: "%s %s\n" , |
692 | yld->lcdMap[i] == ' ' ? " " : "on" , |
693 | lcdMap[i].u.p.name); |
694 | } |
695 | up_read(sem: &sysfs_rwsema); |
696 | return ret; |
697 | } |
698 | |
699 | /* Change the visibility of a particular element. */ |
700 | static ssize_t set_icon(struct device *dev, const char *buf, size_t count, |
701 | int chr) |
702 | { |
703 | struct yealink_dev *yld; |
704 | int i; |
705 | |
706 | down_write(sem: &sysfs_rwsema); |
707 | yld = dev_get_drvdata(dev); |
708 | if (yld == NULL) { |
709 | up_write(sem: &sysfs_rwsema); |
710 | return -ENODEV; |
711 | } |
712 | |
713 | for (i = 0; i < ARRAY_SIZE(lcdMap); i++) { |
714 | if (lcdMap[i].type != '.') |
715 | continue; |
716 | if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) { |
717 | setChar(yld, el: i, chr); |
718 | break; |
719 | } |
720 | } |
721 | |
722 | up_write(sem: &sysfs_rwsema); |
723 | return count; |
724 | } |
725 | |
726 | static ssize_t show_icon(struct device *dev, struct device_attribute *attr, |
727 | const char *buf, size_t count) |
728 | { |
729 | return set_icon(dev, buf, count, chr: buf[0]); |
730 | } |
731 | |
732 | static ssize_t hide_icon(struct device *dev, struct device_attribute *attr, |
733 | const char *buf, size_t count) |
734 | { |
735 | return set_icon(dev, buf, count, chr: ' '); |
736 | } |
737 | |
738 | /* Upload a ringtone to the device. |
739 | */ |
740 | |
741 | /* Stores raw ringtone data in the phone */ |
742 | static ssize_t store_ringtone(struct device *dev, |
743 | struct device_attribute *attr, |
744 | const char *buf, size_t count) |
745 | { |
746 | struct yealink_dev *yld; |
747 | |
748 | down_write(sem: &sysfs_rwsema); |
749 | yld = dev_get_drvdata(dev); |
750 | if (yld == NULL) { |
751 | up_write(sem: &sysfs_rwsema); |
752 | return -ENODEV; |
753 | } |
754 | |
755 | /* TODO locking with async usb control interface??? */ |
756 | yealink_set_ringtone(yld, buf: (char *)buf, size: count); |
757 | up_write(sem: &sysfs_rwsema); |
758 | return count; |
759 | } |
760 | |
761 | #define _M444 S_IRUGO |
762 | #define _M664 S_IRUGO|S_IWUSR|S_IWGRP |
763 | #define _M220 S_IWUSR|S_IWGRP |
764 | |
765 | static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map ); |
766 | static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 ); |
767 | static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 ); |
768 | static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 ); |
769 | static DEVICE_ATTR(get_icons , _M444, get_icons , NULL ); |
770 | static DEVICE_ATTR(show_icon , _M220, NULL , show_icon ); |
771 | static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon ); |
772 | static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone); |
773 | |
774 | static struct attribute *yld_attributes[] = { |
775 | &dev_attr_line1.attr, |
776 | &dev_attr_line2.attr, |
777 | &dev_attr_line3.attr, |
778 | &dev_attr_get_icons.attr, |
779 | &dev_attr_show_icon.attr, |
780 | &dev_attr_hide_icon.attr, |
781 | &dev_attr_map_seg7.attr, |
782 | &dev_attr_ringtone.attr, |
783 | NULL |
784 | }; |
785 | |
786 | static const struct attribute_group yld_attr_group = { |
787 | .attrs = yld_attributes |
788 | }; |
789 | |
790 | /******************************************************************************* |
791 | * Linux interface and usb initialisation |
792 | ******************************************************************************/ |
793 | |
794 | struct driver_info { |
795 | char *name; |
796 | }; |
797 | |
798 | static const struct driver_info info_P1K = { |
799 | .name = "Yealink usb-p1k" , |
800 | }; |
801 | |
802 | static const struct usb_device_id usb_table [] = { |
803 | { |
804 | .match_flags = USB_DEVICE_ID_MATCH_DEVICE | |
805 | USB_DEVICE_ID_MATCH_INT_INFO, |
806 | .idVendor = 0x6993, |
807 | .idProduct = 0xb001, |
808 | .bInterfaceClass = USB_CLASS_HID, |
809 | .bInterfaceSubClass = 0, |
810 | .bInterfaceProtocol = 0, |
811 | .driver_info = (kernel_ulong_t)&info_P1K |
812 | }, |
813 | { } |
814 | }; |
815 | |
816 | static int usb_cleanup(struct yealink_dev *yld, int err) |
817 | { |
818 | if (yld == NULL) |
819 | return err; |
820 | |
821 | if (yld->idev) { |
822 | if (err) |
823 | input_free_device(dev: yld->idev); |
824 | else |
825 | input_unregister_device(yld->idev); |
826 | } |
827 | |
828 | usb_free_urb(urb: yld->urb_irq); |
829 | usb_free_urb(urb: yld->urb_ctl); |
830 | |
831 | kfree(objp: yld->ctl_req); |
832 | usb_free_coherent(dev: yld->udev, USB_PKT_LEN, addr: yld->ctl_data, dma: yld->ctl_dma); |
833 | usb_free_coherent(dev: yld->udev, USB_PKT_LEN, addr: yld->irq_data, dma: yld->irq_dma); |
834 | |
835 | kfree(objp: yld); |
836 | return err; |
837 | } |
838 | |
839 | static void usb_disconnect(struct usb_interface *intf) |
840 | { |
841 | struct yealink_dev *yld; |
842 | |
843 | down_write(sem: &sysfs_rwsema); |
844 | yld = usb_get_intfdata(intf); |
845 | sysfs_remove_group(kobj: &intf->dev.kobj, grp: &yld_attr_group); |
846 | usb_set_intfdata(intf, NULL); |
847 | up_write(sem: &sysfs_rwsema); |
848 | |
849 | usb_cleanup(yld, err: 0); |
850 | } |
851 | |
852 | static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) |
853 | { |
854 | struct usb_device *udev = interface_to_usbdev (intf); |
855 | struct driver_info *nfo = (struct driver_info *)id->driver_info; |
856 | struct usb_host_interface *interface; |
857 | struct usb_endpoint_descriptor *endpoint; |
858 | struct yealink_dev *yld; |
859 | struct input_dev *input_dev; |
860 | int ret, pipe, i; |
861 | |
862 | interface = intf->cur_altsetting; |
863 | |
864 | if (interface->desc.bNumEndpoints < 1) |
865 | return -ENODEV; |
866 | |
867 | endpoint = &interface->endpoint[0].desc; |
868 | if (!usb_endpoint_is_int_in(epd: endpoint)) |
869 | return -ENODEV; |
870 | |
871 | yld = kzalloc(size: sizeof(struct yealink_dev), GFP_KERNEL); |
872 | if (!yld) |
873 | return -ENOMEM; |
874 | |
875 | yld->udev = udev; |
876 | yld->intf = intf; |
877 | |
878 | yld->idev = input_dev = input_allocate_device(); |
879 | if (!input_dev) |
880 | return usb_cleanup(yld, err: -ENOMEM); |
881 | |
882 | /* allocate usb buffers */ |
883 | yld->irq_data = usb_alloc_coherent(dev: udev, USB_PKT_LEN, |
884 | GFP_KERNEL, dma: &yld->irq_dma); |
885 | if (yld->irq_data == NULL) |
886 | return usb_cleanup(yld, err: -ENOMEM); |
887 | |
888 | yld->ctl_data = usb_alloc_coherent(dev: udev, USB_PKT_LEN, |
889 | GFP_KERNEL, dma: &yld->ctl_dma); |
890 | if (!yld->ctl_data) |
891 | return usb_cleanup(yld, err: -ENOMEM); |
892 | |
893 | yld->ctl_req = kmalloc(size: sizeof(*(yld->ctl_req)), GFP_KERNEL); |
894 | if (yld->ctl_req == NULL) |
895 | return usb_cleanup(yld, err: -ENOMEM); |
896 | |
897 | /* allocate urb structures */ |
898 | yld->urb_irq = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
899 | if (yld->urb_irq == NULL) |
900 | return usb_cleanup(yld, err: -ENOMEM); |
901 | |
902 | yld->urb_ctl = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
903 | if (yld->urb_ctl == NULL) |
904 | return usb_cleanup(yld, err: -ENOMEM); |
905 | |
906 | /* get a handle to the interrupt data pipe */ |
907 | pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); |
908 | ret = usb_maxpacket(udev, pipe); |
909 | if (ret != USB_PKT_LEN) |
910 | dev_err(&intf->dev, "invalid payload size %d, expected %zd\n" , |
911 | ret, USB_PKT_LEN); |
912 | |
913 | /* initialise irq urb */ |
914 | usb_fill_int_urb(urb: yld->urb_irq, dev: udev, pipe, transfer_buffer: yld->irq_data, |
915 | USB_PKT_LEN, |
916 | complete_fn: urb_irq_callback, |
917 | context: yld, interval: endpoint->bInterval); |
918 | yld->urb_irq->transfer_dma = yld->irq_dma; |
919 | yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
920 | yld->urb_irq->dev = udev; |
921 | |
922 | /* initialise ctl urb */ |
923 | yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | |
924 | USB_DIR_OUT; |
925 | yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION; |
926 | yld->ctl_req->wValue = cpu_to_le16(0x200); |
927 | yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); |
928 | yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN); |
929 | |
930 | usb_fill_control_urb(urb: yld->urb_ctl, dev: udev, usb_sndctrlpipe(udev, 0), |
931 | setup_packet: (void *)yld->ctl_req, transfer_buffer: yld->ctl_data, USB_PKT_LEN, |
932 | complete_fn: urb_ctl_callback, context: yld); |
933 | yld->urb_ctl->transfer_dma = yld->ctl_dma; |
934 | yld->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
935 | yld->urb_ctl->dev = udev; |
936 | |
937 | /* find out the physical bus location */ |
938 | usb_make_path(dev: udev, buf: yld->phys, size: sizeof(yld->phys)); |
939 | strlcat(p: yld->phys, q: "/input0" , avail: sizeof(yld->phys)); |
940 | |
941 | /* register settings for the input device */ |
942 | input_dev->name = nfo->name; |
943 | input_dev->phys = yld->phys; |
944 | usb_to_input_id(dev: udev, id: &input_dev->id); |
945 | input_dev->dev.parent = &intf->dev; |
946 | |
947 | input_set_drvdata(dev: input_dev, data: yld); |
948 | |
949 | input_dev->open = input_open; |
950 | input_dev->close = input_close; |
951 | /* input_dev->event = input_ev; TODO */ |
952 | |
953 | /* register available key events */ |
954 | input_dev->evbit[0] = BIT_MASK(EV_KEY); |
955 | for (i = 0; i < 256; i++) { |
956 | int k = map_p1k_to_key(scancode: i); |
957 | if (k >= 0) { |
958 | set_bit(nr: k & 0xff, addr: input_dev->keybit); |
959 | if (k >> 8) |
960 | set_bit(nr: k >> 8, addr: input_dev->keybit); |
961 | } |
962 | } |
963 | |
964 | ret = input_register_device(yld->idev); |
965 | if (ret) |
966 | return usb_cleanup(yld, err: ret); |
967 | |
968 | usb_set_intfdata(intf, data: yld); |
969 | |
970 | /* clear visible elements */ |
971 | for (i = 0; i < ARRAY_SIZE(lcdMap); i++) |
972 | setChar(yld, el: i, chr: ' '); |
973 | |
974 | /* display driver version on LCD line 3 */ |
975 | store_line3(dev: &intf->dev, NULL, |
976 | DRIVER_VERSION, count: sizeof(DRIVER_VERSION)); |
977 | |
978 | /* Register sysfs hooks (don't care about failure) */ |
979 | ret = sysfs_create_group(kobj: &intf->dev.kobj, grp: &yld_attr_group); |
980 | return 0; |
981 | } |
982 | |
983 | static struct usb_driver yealink_driver = { |
984 | .name = "yealink" , |
985 | .probe = usb_probe, |
986 | .disconnect = usb_disconnect, |
987 | .id_table = usb_table, |
988 | }; |
989 | |
990 | module_usb_driver(yealink_driver); |
991 | |
992 | MODULE_DEVICE_TABLE (usb, usb_table); |
993 | |
994 | MODULE_AUTHOR("Henk Vergonet" ); |
995 | MODULE_DESCRIPTION("Yealink phone driver" ); |
996 | MODULE_LICENSE("GPL" ); |
997 | |