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