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