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