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