aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/input/yealink.txt187
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/usb/input/Kconfig14
-rw-r--r--drivers/usb/input/Makefile1
-rw-r--r--drivers/usb/input/map_to_7segment.h189
-rw-r--r--drivers/usb/input/yealink.c1010
-rw-r--r--drivers/usb/input/yealink.h220
7 files changed, 1627 insertions, 0 deletions
diff --git a/Documentation/input/yealink.txt b/Documentation/input/yealink.txt
new file mode 100644
index 000000000000..5665c32e2a94
--- /dev/null
+++ b/Documentation/input/yealink.txt
@@ -0,0 +1,187 @@
1yealink - Linux driver for usb-p1k phones
2
30. Status
4~~~~~~~~~
5
6The p1k is a relatively cheap usb 1.1 phone with:
7 - keyboard full support
8 - LCD full support
9 - LED full support
10 - dialtone full support
11 - ringtone full support
12 - audio playback via generic usb audio diver
13 - audio record via generic usb audio diver
14
15
161. Compilation (stand alone version)
17~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18
19Currently only kernel 2.6.x.y versions are supported.
20In order to build the yealink.ko module do:
21
22 make
23
24If you encounter problems please check if in the MAKE_OPTS variable in
25the Makefile is pointing to the location where your kernel sources
26are located, default /usr/src/linux.
27
28
29
302. keyboard features
31~~~~~~~~~~~~~~~~~~~~
32The current mapping in the kernel is provided by the map_p1k_to_key
33function:
34
35 Physical USB-P1K button layout input events
36
37
38 up up
39 IN OUT left, right
40 down down
41
42 pickup C hangup enter, backspace, escape
43 1 2 3 1, 2, 3
44 4 5 6 4, 5, 6,
45 7 8 9 7, 8, 9,
46 * 0 # *, 0, #,
47
48 The "up" and "down" keys, are symbolised by arrows on the button.
49 The "pickup" and "hangup" keys are symbolised by a green and red phone
50 on the button.
51
52
533. LCD features
54~~~~~~~~~~~~~~~
55The LCD is divided and organised as a 3 line display:
56
57 |[] [][] [][] [][] in |[][]
58 |[] M [][] D [][] : [][] out |[][]
59 store
60
61 NEW REP SU MO TU WE TH FR SA
62
63 [] [] [] [] [] [] [] [] [] [] [] []
64 [] [] [] [] [] [] [] [] [] [] [] []
65
66
67Line 1 Format (see below) : 18.e8.M8.88...188
68 Icon names : M D : IN OUT STORE
69Line 2 Format : .........
70 Icon name : NEW REP SU MO TU WE TH FR SA
71Line 3 Format : 888888888888
72
73
74Format description:
75 From a user space perspective the world is seperated in "digits" and "icons".
76 A digit can have a character set, an icon can only be ON or OFF.
77
78 Format specifier
79 '8' : Generic 7 segment digit with individual addressable segments
80
81 Reduced capabillity 7 segm digit, when segments are hard wired together.
82 '1' : 2 segments digit only able to produce a 1.
83 'e' : Most significant day of the month digit,
84 able to produce at least 1 2 3.
85 'M' : Most significant minute digit,
86 able to produce at least 0 1 2 3 4 5.
87
88 Icons or pictograms:
89 '.' : For example like AM, PM, SU, a 'dot' .. or other single segment
90 elements.
91
92
934. Driver usage
94~~~~~~~~~~~~~~~
95For userland the following interfaces are available using the sysfs interface:
96 /sys/.../
97 line1 Read/Write, lcd line1
98 line2 Read/Write, lcd line2
99 line3 Read/Write, lcd line3
100
101 get_icons Read, returns a set of available icons.
102 hide_icon Write, hide the element by writing the icon name.
103 show_icon Write, display the element by writing the icon name.
104
105 map_seg7 Read/Write, the 7 segments char set, common for all
106 yealink phones. (see map_to_7segment.h)
107
108 ringtone Write, upload binary representation of a ringtone,
109 see yealink.c. status EXPERIMENTAL due to potential
110 races between async. and sync usb calls.
111
112
1134.1 lineX
114~~~~~~~~~
115Reading /sys/../lineX will return the format string with its current value:
116
117 Example:
118 cat ./line3
119 888888888888
120 Linux Rocks!
121
122Writing to /sys/../lineX will set the coresponding LCD line.
123 - Excess characters are ignored.
124 - If less characters are written than allowed, the remaining digits are
125 unchanged.
126 - The tab '\t'and '\n' char does not overwrite the original content.
127 - Writing a space to an icon will always hide its content.
128
129 Example:
130 date +"%m.%e.%k:%M" | sed 's/^0/ /' > ./line1
131
132 Will update the LCD with the current date & time.
133
134
1354.2 get_icons
136~~~~~~~~~~~~~
137Reading will return all available icon names and its current settings:
138
139 cat ./get_icons
140 on M
141 on D
142 on :
143 IN
144 OUT
145 STORE
146 NEW
147 REP
148 SU
149 MO
150 TU
151 WE
152 TH
153 FR
154 SA
155 LED
156 DIALTONE
157 RINGTONE
158
159
1604.3 show/hide icons
161~~~~~~~~~~~~~~~~~~~
162Writing to these files will update the state of the icon.
163Only one icon at a time can be updated.
164
165If an icon is also on a ./lineX the corresponding value is
166updated with the first letter of the icon.
167
168 Example - light up the store icon:
169 echo -n "STORE" > ./show_icon
170
171 cat ./line1
172 18.e8.M8.88...188
173 S
174
175 Example - sound the ringtone for 10 seconds:
176 echo -n RINGTONE > /sys/..../show_icon
177 sleep 10
178 echo -n RINGTONE > /sys/..../hide_icon
179
180
1815. Credits & Acknowledgments
182~~~~~~~~~~~~~~~~~~~~~~~~~~~~
183 - Olivier Vandorpe, for starting the usbb2k-api project doing much of
184 the reverse engineering.
185 - Martin Diehl, for pointing out how to handle USB memory allocation.
186 - Dmitry Torokhov, for the numerous code reviews and suggestions.
187
diff --git a/MAINTAINERS b/MAINTAINERS
index 8e4e82921070..96ddac7c21bc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -116,6 +116,12 @@ M: ajk@iehk.rwth-aachen.de
116L: linux-hams@vger.kernel.org 116L: linux-hams@vger.kernel.org
117S: Maintained 117S: Maintained
118 118
119YEALINK PHONE DRIVER
120P: Henk Vergonet
121M: Henk.Vergonet@gmail.com
122L: usbb2k-api-dev@nongnu.org
123S: Maintained
124
1198139CP 10/100 FAST ETHERNET DRIVER 1258139CP 10/100 FAST ETHERNET DRIVER
120P: Jeff Garzik 126P: Jeff Garzik
121M: jgarzik@pobox.com 127M: jgarzik@pobox.com
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
index 298e4a25e3d3..482c4be521f5 100644
--- a/drivers/usb/input/Kconfig
+++ b/drivers/usb/input/Kconfig
@@ -230,6 +230,20 @@ config USB_EGALAX
230 To compile this driver as a module, choose M here: the 230 To compile this driver as a module, choose M here: the
231 module will be called touchkitusb. 231 module will be called touchkitusb.
232 232
233config USB_YEALINK
234 tristate "Yealink usb-p1k voip phone"
235 depends on USB && INPUT && EXPERIMENTAL
236 ---help---
237 Say Y here if you want to enable keyboard and LCD functions of the
238 Yealink usb-p1k usb phones. The audio part is enabled by the generic
239 usb sound driver, so you might want to enable that as well.
240
241 For information about how to use these additional functions, see
242 <file:Documentation/input/yealink.txt>.
243
244 To compile this driver as a module, choose M here: the module will be
245 called yealink.
246
233config USB_XPAD 247config USB_XPAD
234 tristate "X-Box gamepad support" 248 tristate "X-Box gamepad support"
235 depends on USB && INPUT 249 depends on USB && INPUT
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
index f1547be632d4..43b2f999edfe 100644
--- a/drivers/usb/input/Makefile
+++ b/drivers/usb/input/Makefile
@@ -39,4 +39,5 @@ obj-$(CONFIG_USB_EGALAX) += touchkitusb.o
39obj-$(CONFIG_USB_POWERMATE) += powermate.o 39obj-$(CONFIG_USB_POWERMATE) += powermate.o
40obj-$(CONFIG_USB_WACOM) += wacom.o 40obj-$(CONFIG_USB_WACOM) += wacom.o
41obj-$(CONFIG_USB_ACECAD) += acecad.o 41obj-$(CONFIG_USB_ACECAD) += acecad.o
42obj-$(CONFIG_USB_YEALINK) += yealink.o
42obj-$(CONFIG_USB_XPAD) += xpad.o 43obj-$(CONFIG_USB_XPAD) += xpad.o
diff --git a/drivers/usb/input/map_to_7segment.h b/drivers/usb/input/map_to_7segment.h
new file mode 100644
index 000000000000..52ff27f15127
--- /dev/null
+++ b/drivers/usb/input/map_to_7segment.h
@@ -0,0 +1,189 @@
1/*
2 * drivers/usb/input/map_to_7segment.h
3 *
4 * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#ifndef MAP_TO_7SEGMENT_H
22#define MAP_TO_7SEGMENT_H
23
24/* This file provides translation primitives and tables for the conversion
25 * of (ASCII) characters to a 7-segments notation.
26 *
27 * The 7 segment's wikipedia notation below is used as standard.
28 * See: http://en.wikipedia.org/wiki/Seven_segment_display
29 *
30 * Notation: +-a-+
31 * f b
32 * +-g-+
33 * e c
34 * +-d-+
35 *
36 * Usage:
37 *
38 * Register a map variable, and fill it with a character set:
39 * static SEG7_DEFAULT_MAP(map_seg7);
40 *
41 *
42 * Then use for conversion:
43 * seg7 = map_to_seg7(&map_seg7, some_char);
44 * ...
45 *
46 * In device drivers it is recommended, if required, to make the char map
47 * accessible via the sysfs interface using the following scheme:
48 *
49 * static ssize_t show_map(struct device *dev, char *buf) {
50 * memcpy(buf, &map_seg7, sizeof(map_seg7));
51 * return sizeof(map_seg7);
52 * }
53 * static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) {
54 * if(cnt != sizeof(map_seg7))
55 * return -EINVAL;
56 * memcpy(&map_seg7, buf, cnt);
57 * return cnt;
58 * }
59 * static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map);
60 *
61 * History:
62 * 2005-05-31 RFC linux-kernel@vger.kernel.org
63 */
64#include <linux/errno.h>
65
66
67#define BIT_SEG7_A 0
68#define BIT_SEG7_B 1
69#define BIT_SEG7_C 2
70#define BIT_SEG7_D 3
71#define BIT_SEG7_E 4
72#define BIT_SEG7_F 5
73#define BIT_SEG7_G 6
74#define BIT_SEG7_RESERVED 7
75
76struct seg7_conversion_map {
77 unsigned char table[128];
78};
79
80static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
81{
82 return c & 0x7f ? map->table[c] : -EINVAL;
83}
84
85#define SEG7_CONVERSION_MAP(_name, _map) \
86 struct seg7_conversion_map _name = { .table = { _map } }
87
88/*
89 * It is recommended to use a facility that allows user space to redefine
90 * custom character sets for LCD devices. Please use a sysfs interface
91 * as described above.
92 */
93#define MAP_TO_SEG7_SYSFS_FILE "map_seg7"
94
95/*******************************************************************************
96 * ASCII conversion table
97 ******************************************************************************/
98
99#define _SEG7(l,a,b,c,d,e,f,g) \
100 ( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \
101 e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G )
102
103#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
104 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
105
106#define _MAP_33_47_ASCII_SEG7_SYMBOL \
107 _SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\
108 _SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\
109 _SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\
110 _SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\
111 _SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1),
112
113#define _MAP_48_57_ASCII_SEG7_NUMERIC \
114 _SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\
115 _SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\
116 _SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\
117 _SEG7('9',1,1,1,1,0,1,1),
118
119#define _MAP_58_64_ASCII_SEG7_SYMBOL \
120 _SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\
121 _SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\
122 _SEG7('@',1,1,0,1,1,1,1),
123
124#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
125 _SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\
126 _SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
127 _SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\
128 _SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
129 _SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\
130 _SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\
131 _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\
132 _SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
133 _SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
134
135#define _MAP_91_96_ASCII_SEG7_SYMBOL \
136 _SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\
137 _SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0),
138
139#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
140 _SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\
141 _SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
142 _SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\
143 _SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
144 _SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\
145 _SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\
146 _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\
147 _SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
148 _SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
149
150#define _MAP_123_126_ASCII_SEG7_SYMBOL \
151 _SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\
152 _SEG7('~',1,0,0,0,0,0,0),
153
154/* Maps */
155
156/* This set tries to map as close as possible to the visible characteristics
157 * of the ASCII symbol, lowercase and uppercase letters may differ in
158 * presentation on the display.
159 */
160#define MAP_ASCII7SEG_ALPHANUM \
161 _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
162 _MAP_33_47_ASCII_SEG7_SYMBOL \
163 _MAP_48_57_ASCII_SEG7_NUMERIC \
164 _MAP_58_64_ASCII_SEG7_SYMBOL \
165 _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
166 _MAP_91_96_ASCII_SEG7_SYMBOL \
167 _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
168 _MAP_123_126_ASCII_SEG7_SYMBOL
169
170/* This set tries to map as close as possible to the symbolic characteristics
171 * of the ASCII character for maximum discrimination.
172 * For now this means all alpha chars are in lower case representations.
173 * (This for example facilitates the use of hex numbers with uppercase input.)
174 */
175#define MAP_ASCII7SEG_ALPHANUM_LC \
176 _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
177 _MAP_33_47_ASCII_SEG7_SYMBOL \
178 _MAP_48_57_ASCII_SEG7_NUMERIC \
179 _MAP_58_64_ASCII_SEG7_SYMBOL \
180 _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
181 _MAP_91_96_ASCII_SEG7_SYMBOL \
182 _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
183 _MAP_123_126_ASCII_SEG7_SYMBOL
184
185#define SEG7_DEFAULT_MAP(_name) \
186 SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM)
187
188#endif /* MAP_TO_7SEGMENT_H */
189
diff --git a/drivers/usb/input/yealink.c b/drivers/usb/input/yealink.c
new file mode 100644
index 000000000000..0748281b8f3e
--- /dev/null
+++ b/drivers/usb/input/yealink.c
@@ -0,0 +1,1010 @@
1/*
2 * drivers/usb/input/yealink.c
3 *
4 * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20/*
21 * Description:
22 * Driver for the USB-P1K voip usb phone.
23 * This device is produced by Yealink Network Technology Co Ltd
24 * but may be branded under several names:
25 * - Yealink usb-p1k
26 * - Tiptel 115
27 * - ...
28 *
29 * This driver is based on:
30 * - the usbb2k-api http://savannah.nongnu.org/projects/usbb2k-api/
31 * - information from http://memeteau.free.fr/usbb2k
32 * - the xpad-driver drivers/usb/input/xpad.c
33 *
34 * Thanks to:
35 * - Olivier Vandorpe, for providing the usbb2k-api.
36 * - Martin Diehl, for spotting my memory allocation bug.
37 *
38 * History:
39 * 20050527 henk First version, functional keyboard. Keyboard events
40 * will pop-up on the ../input/eventX bus.
41 * 20050531 henk Added led, LCD, dialtone and sysfs interface.
42 * 20050610 henk Cleanups, make it ready for public consumption.
43 * 20050630 henk Cleanups, fixes in response to comments.
44 * 20050701 henk sysfs write serialisation, fix potential unload races
45 * 20050801 henk Added ringtone, restructure USB
46 * 20050816 henk Merge 2.6.13-rc6
47 */
48
49#include <linux/config.h>
50#include <linux/kernel.h>
51#include <linux/input.h>
52#include <linux/init.h>
53#include <linux/slab.h>
54#include <linux/module.h>
55#include <linux/rwsem.h>
56#include <linux/usb.h>
57
58#include "map_to_7segment.h"
59#include "yealink.h"
60
61#define DRIVER_VERSION "yld-20050816"
62#define DRIVER_AUTHOR "Henk Vergonet"
63#define DRIVER_DESC "Yealink phone driver"
64
65#define YEALINK_POLLING_FREQUENCY 10 /* in [Hz] */
66
67struct yld_status {
68 u8 lcd[24];
69 u8 led;
70 u8 dialtone;
71 u8 ringtone;
72 u8 keynum;
73} __attribute__ ((packed));
74
75/*
76 * Register the LCD segment and icon map
77 */
78#define _LOC(k,l) { .a = (k), .m = (l) }
79#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm) \
80 { .type = (t), \
81 .u = { .s = { _LOC(a, am), _LOC(b, bm), _LOC(c, cm), \
82 _LOC(d, dm), _LOC(e, em), _LOC(g, gm), \
83 _LOC(f, fm) } } }
84#define _PIC(t, h, hm, n) \
85 { .type = (t), \
86 .u = { .p = { .name = (n), .a = (h), .m = (hm) } } }
87
88static const struct lcd_segment_map {
89 char type;
90 union {
91 struct pictogram_map {
92 u8 a,m;
93 char name[10];
94 } p;
95 struct segment_map {
96 u8 a,m;
97 } s[7];
98 } u;
99} lcdMap[] = {
100#include "yealink.h"
101};
102
103struct yealink_dev {
104 struct input_dev idev; /* input device */
105 struct usb_device *udev; /* usb device */
106
107 /* irq input channel */
108 struct yld_ctl_packet *irq_data;
109 dma_addr_t irq_dma;
110 struct urb *urb_irq;
111
112 /* control output channel */
113 struct yld_ctl_packet *ctl_data;
114 dma_addr_t ctl_dma;
115 struct usb_ctrlrequest *ctl_req;
116 dma_addr_t ctl_req_dma;
117 struct urb *urb_ctl;
118
119 char phys[64]; /* physical device path */
120
121 u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */
122 int key_code; /* last reported key */
123
124 int stat_ix;
125 union {
126 struct yld_status s;
127 u8 b[sizeof(struct yld_status)];
128 } master, copy;
129};
130
131
132/*******************************************************************************
133 * Yealink lcd interface
134 ******************************************************************************/
135
136/*
137 * Register a default 7 segment character set
138 */
139static SEG7_DEFAULT_MAP(map_seg7);
140
141 /* Display a char,
142 * char '\9' and '\n' are placeholders and do not overwrite the original text.
143 * A space will always hide an icon.
144 */
145static int setChar(struct yealink_dev *yld, int el, int chr)
146{
147 int i, a, m, val;
148
149 if (el >= ARRAY_SIZE(lcdMap))
150 return -EINVAL;
151
152 if (chr == '\t' || chr == '\n')
153 return 0;
154
155 yld->lcdMap[el] = chr;
156
157 if (lcdMap[el].type == '.') {
158 a = lcdMap[el].u.p.a;
159 m = lcdMap[el].u.p.m;
160 if (chr != ' ')
161 yld->master.b[a] |= m;
162 else
163 yld->master.b[a] &= ~m;
164 return 0;
165 }
166
167 val = map_to_seg7(&map_seg7, chr);
168 for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) {
169 m = lcdMap[el].u.s[i].m;
170
171 if (m == 0)
172 continue;
173
174 a = lcdMap[el].u.s[i].a;
175 if (val & 1)
176 yld->master.b[a] |= m;
177 else
178 yld->master.b[a] &= ~m;
179 val = val >> 1;
180 }
181 return 0;
182};
183
184/*******************************************************************************
185 * Yealink key interface
186 ******************************************************************************/
187
188/* Map device buttons to internal key events.
189 *
190 * USB-P1K button layout:
191 *
192 * up
193 * IN OUT
194 * down
195 *
196 * pickup C hangup
197 * 1 2 3
198 * 4 5 6
199 * 7 8 9
200 * * 0 #
201 *
202 * The "up" and "down" keys, are symbolised by arrows on the button.
203 * The "pickup" and "hangup" keys are symbolised by a green and red phone
204 * on the button.
205 */
206static int map_p1k_to_key(int scancode)
207{
208 switch(scancode) { /* phone key: */
209 case 0x23: return KEY_LEFT; /* IN */
210 case 0x33: return KEY_UP; /* up */
211 case 0x04: return KEY_RIGHT; /* OUT */
212 case 0x24: return KEY_DOWN; /* down */
213 case 0x03: return KEY_ENTER; /* pickup */
214 case 0x14: return KEY_BACKSPACE; /* C */
215 case 0x13: return KEY_ESC; /* hangup */
216 case 0x00: return KEY_1; /* 1 */
217 case 0x01: return KEY_2; /* 2 */
218 case 0x02: return KEY_3; /* 3 */
219 case 0x10: return KEY_4; /* 4 */
220 case 0x11: return KEY_5; /* 5 */
221 case 0x12: return KEY_6; /* 6 */
222 case 0x20: return KEY_7; /* 7 */
223 case 0x21: return KEY_8; /* 8 */
224 case 0x22: return KEY_9; /* 9 */
225 case 0x30: return KEY_KPASTERISK; /* * */
226 case 0x31: return KEY_0; /* 0 */
227 case 0x32: return KEY_LEFTSHIFT |
228 KEY_3 << 8; /* # */
229 }
230 return -EINVAL;
231}
232
233/* Completes a request by converting the data into events for the
234 * input subsystem.
235 *
236 * The key parameter can be cascaded: key2 << 8 | key1
237 */
238static void report_key(struct yealink_dev *yld, int key, struct pt_regs *regs)
239{
240 struct input_dev *idev = &yld->idev;
241
242 input_regs(idev, regs);
243 if (yld->key_code >= 0) {
244 /* old key up */
245 input_report_key(idev, yld->key_code & 0xff, 0);
246 if (yld->key_code >> 8)
247 input_report_key(idev, yld->key_code >> 8, 0);
248 }
249
250 yld->key_code = key;
251 if (key >= 0) {
252 /* new valid key */
253 input_report_key(idev, key & 0xff, 1);
254 if (key >> 8)
255 input_report_key(idev, key >> 8, 1);
256 }
257 input_sync(idev);
258}
259
260/*******************************************************************************
261 * Yealink usb communication interface
262 ******************************************************************************/
263
264static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p)
265{
266 u8 *buf = (u8 *)p;
267 int i;
268 u8 sum = 0;
269
270 for(i=0; i<USB_PKT_LEN-1; i++)
271 sum -= buf[i];
272 p->sum = sum;
273 return usb_control_msg(yld->udev,
274 usb_sndctrlpipe(yld->udev, 0),
275 USB_REQ_SET_CONFIGURATION,
276 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
277 0x200, 3,
278 p, sizeof(*p),
279 USB_CTRL_SET_TIMEOUT);
280}
281
282static u8 default_ringtone[] = {
283 0xEF, /* volume [0-255] */
284 0xFB, 0x1E, 0x00, 0x0C, /* 1250 [hz], 12/100 [s] */
285 0xFC, 0x18, 0x00, 0x0C, /* 1000 [hz], 12/100 [s] */
286 0xFB, 0x1E, 0x00, 0x0C,
287 0xFC, 0x18, 0x00, 0x0C,
288 0xFB, 0x1E, 0x00, 0x0C,
289 0xFC, 0x18, 0x00, 0x0C,
290 0xFB, 0x1E, 0x00, 0x0C,
291 0xFC, 0x18, 0x00, 0x0C,
292 0xFF, 0xFF, 0x01, 0x90, /* silent, 400/100 [s] */
293 0x00, 0x00 /* end of sequence */
294};
295
296static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size)
297{
298 struct yld_ctl_packet *p = yld->ctl_data;
299 int ix, len;
300
301 if (size <= 0)
302 return -EINVAL;
303
304 /* Set the ringtone volume */
305 memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
306 yld->ctl_data->cmd = CMD_RING_VOLUME;
307 yld->ctl_data->size = 1;
308 yld->ctl_data->data[0] = buf[0];
309 yealink_cmd(yld, p);
310
311 buf++;
312 size--;
313
314 p->cmd = CMD_RING_NOTE;
315 ix = 0;
316 while (size != ix) {
317 len = size - ix;
318 if (len > sizeof(p->data))
319 len = sizeof(p->data);
320 p->size = len;
321 p->offset = htons(ix);
322 memcpy(p->data, &buf[ix], len);
323 yealink_cmd(yld, p);
324 ix += len;
325 }
326 return 0;
327}
328
329/* keep stat_master & stat_copy in sync.
330 */
331static int yealink_do_idle_tasks(struct yealink_dev *yld)
332{
333 u8 val;
334 int i, ix, len;
335
336 ix = yld->stat_ix;
337
338 memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
339 yld->ctl_data->cmd = CMD_KEYPRESS;
340 yld->ctl_data->size = 1;
341 yld->ctl_data->sum = 0xff - CMD_KEYPRESS;
342
343 /* If state update pointer wraps do a KEYPRESS first. */
344 if (ix >= sizeof(yld->master)) {
345 yld->stat_ix = 0;
346 return 0;
347 }
348
349 /* find update candidates: copy != master */
350 do {
351 val = yld->master.b[ix];
352 if (val != yld->copy.b[ix])
353 goto send_update;
354 } while (++ix < sizeof(yld->master));
355
356 /* nothing todo, wait a bit and poll for a KEYPRESS */
357 yld->stat_ix = 0;
358 /* TODO how can we wait abit. ??
359 * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY);
360 */
361 return 0;
362
363send_update:
364
365 /* Setup an appropriate update request */
366 yld->copy.b[ix] = val;
367 yld->ctl_data->data[0] = val;
368
369 switch(ix) {
370 case offsetof(struct yld_status, led):
371 yld->ctl_data->cmd = CMD_LED;
372 yld->ctl_data->sum = -1 - CMD_LED - val;
373 break;
374 case offsetof(struct yld_status, dialtone):
375 yld->ctl_data->cmd = CMD_DIALTONE;
376 yld->ctl_data->sum = -1 - CMD_DIALTONE - val;
377 break;
378 case offsetof(struct yld_status, ringtone):
379 yld->ctl_data->cmd = CMD_RINGTONE;
380 yld->ctl_data->sum = -1 - CMD_RINGTONE - val;
381 break;
382 case offsetof(struct yld_status, keynum):
383 val--;
384 val &= 0x1f;
385 yld->ctl_data->cmd = CMD_SCANCODE;
386 yld->ctl_data->offset = htons(val);
387 yld->ctl_data->data[0] = 0;
388 yld->ctl_data->sum = -1 - CMD_SCANCODE - val;
389 break;
390 default:
391 len = sizeof(yld->master.s.lcd) - ix;
392 if (len > sizeof(yld->ctl_data->data))
393 len = sizeof(yld->ctl_data->data);
394
395 /* Combine up to <len> consecutive LCD bytes in a singe request
396 */
397 yld->ctl_data->cmd = CMD_LCD;
398 yld->ctl_data->offset = htons(ix);
399 yld->ctl_data->size = len;
400 yld->ctl_data->sum = -CMD_LCD - ix - val - len;
401 for(i=1; i<len; i++) {
402 ix++;
403 val = yld->master.b[ix];
404 yld->copy.b[ix] = val;
405 yld->ctl_data->data[i] = val;
406 yld->ctl_data->sum -= val;
407 }
408 }
409 yld->stat_ix = ix + 1;
410 return 1;
411}
412
413/* Decide on how to handle responses
414 *
415 * The state transition diagram is somethhing like:
416 *
417 * syncState<--+
418 * | |
419 * | idle
420 * \|/ |
421 * init --ok--> waitForKey --ok--> getKey
422 * ^ ^ |
423 * | +-------ok-------+
424 * error,start
425 *
426 */
427static void urb_irq_callback(struct urb *urb, struct pt_regs *regs)
428{
429 struct yealink_dev *yld = urb->context;
430 int ret;
431
432 if (urb->status)
433 err("%s - urb status %d", __FUNCTION__, urb->status);
434
435 switch (yld->irq_data->cmd) {
436 case CMD_KEYPRESS:
437
438 yld->master.s.keynum = yld->irq_data->data[0];
439 break;
440
441 case CMD_SCANCODE:
442 dbg("get scancode %x", yld->irq_data->data[0]);
443
444 report_key(yld, map_p1k_to_key(yld->irq_data->data[0]), regs);
445 break;
446
447 default:
448 err("unexpected response %x", yld->irq_data->cmd);
449 }
450
451 yealink_do_idle_tasks(yld);
452
453 ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
454 if (ret)
455 err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);
456}
457
458static void urb_ctl_callback(struct urb *urb, struct pt_regs *regs)
459{
460 struct yealink_dev *yld = urb->context;
461 int ret;
462
463 if (urb->status)
464 err("%s - urb status %d", __FUNCTION__, urb->status);
465
466 switch (yld->ctl_data->cmd) {
467 case CMD_KEYPRESS:
468 case CMD_SCANCODE:
469 /* ask for a response */
470 ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);
471 break;
472 default:
473 /* send new command */
474 yealink_do_idle_tasks(yld);
475 ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
476 }
477
478 if (ret)
479 err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);
480}
481
482/*******************************************************************************
483 * input event interface
484 ******************************************************************************/
485
486/* TODO should we issue a ringtone on a SND_BELL event?
487static int input_ev(struct input_dev *dev, unsigned int type,
488 unsigned int code, int value)
489{
490
491 if (type != EV_SND)
492 return -EINVAL;
493
494 switch (code) {
495 case SND_BELL:
496 case SND_TONE:
497 break;
498 default:
499 return -EINVAL;
500 }
501
502 return 0;
503}
504*/
505
506static int input_open(struct input_dev *dev)
507{
508 struct yealink_dev *yld = dev->private;
509 int i, ret;
510
511 dbg("%s", __FUNCTION__);
512
513 /* force updates to device */
514 for (i = 0; i<sizeof(yld->master); i++)
515 yld->copy.b[i] = ~yld->master.b[i];
516 yld->key_code = -1; /* no keys pressed */
517
518 yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone));
519
520 /* issue INIT */
521 memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
522 yld->ctl_data->cmd = CMD_INIT;
523 yld->ctl_data->size = 10;
524 yld->ctl_data->sum = 0x100-CMD_INIT-10;
525 if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) {
526 dbg("%s - usb_submit_urb failed with result %d",
527 __FUNCTION__, ret);
528 return ret;
529 }
530 return 0;
531}
532
533static void input_close(struct input_dev *dev)
534{
535 struct yealink_dev *yld = dev->private;
536
537 usb_kill_urb(yld->urb_ctl);
538 usb_kill_urb(yld->urb_irq);
539}
540
541/*******************************************************************************
542 * sysfs interface
543 ******************************************************************************/
544
545static DECLARE_RWSEM(sysfs_rwsema);
546
547/* Interface to the 7-segments translation table aka. char set.
548 */
549static ssize_t show_map(struct device *dev, struct device_attribute *attr,
550 char *buf)
551{
552 memcpy(buf, &map_seg7, sizeof(map_seg7));
553 return sizeof(map_seg7);
554}
555
556static ssize_t store_map(struct device *dev, struct device_attribute *attr,
557 const char *buf, size_t cnt)
558{
559 if (cnt != sizeof(map_seg7))
560 return -EINVAL;
561 memcpy(&map_seg7, buf, sizeof(map_seg7));
562 return sizeof(map_seg7);
563}
564
565/* Interface to the LCD.
566 */
567
568/* Reading /sys/../lineX will return the format string with its settings:
569 *
570 * Example:
571 * cat ./line3
572 * 888888888888
573 * Linux Rocks!
574 */
575static ssize_t show_line(struct device *dev, char *buf, int a, int b)
576{
577 struct yealink_dev *yld;
578 int i;
579
580 down_read(&sysfs_rwsema);
581 yld = dev_get_drvdata(dev);
582 if (yld == NULL) {
583 up_read(&sysfs_rwsema);
584 return -ENODEV;
585 }
586
587 for (i = a; i < b; i++)
588 *buf++ = lcdMap[i].type;
589 *buf++ = '\n';
590 for (i = a; i < b; i++)
591 *buf++ = yld->lcdMap[i];
592 *buf++ = '\n';
593 *buf = 0;
594
595 up_read(&sysfs_rwsema);
596 return 3 + ((b - a) << 1);
597}
598
599static ssize_t show_line1(struct device *dev, struct device_attribute *attr,
600 char *buf)
601{
602 return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET);
603}
604
605static ssize_t show_line2(struct device *dev, struct device_attribute *attr,
606 char *buf)
607{
608 return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET);
609}
610
611static ssize_t show_line3(struct device *dev, struct device_attribute *attr,
612 char *buf)
613{
614 return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET);
615}
616
617/* Writing to /sys/../lineX will set the coresponding LCD line.
618 * - Excess characters are ignored.
619 * - If less characters are written than allowed, the remaining digits are
620 * unchanged.
621 * - The '\n' or '\t' char is a placeholder, it does not overwrite the
622 * original content.
623 */
624static ssize_t store_line(struct device *dev, const char *buf, size_t count,
625 int el, size_t len)
626{
627 struct yealink_dev *yld;
628 int i;
629
630 down_write(&sysfs_rwsema);
631 yld = dev_get_drvdata(dev);
632 if (yld == NULL) {
633 up_write(&sysfs_rwsema);
634 return -ENODEV;
635 }
636
637 if (len > count)
638 len = count;
639 for (i = 0; i < len; i++)
640 setChar(yld, el++, buf[i]);
641
642 up_write(&sysfs_rwsema);
643 return count;
644}
645
646static ssize_t store_line1(struct device *dev, struct device_attribute *attr,
647 const char *buf, size_t count)
648{
649 return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE);
650}
651
652static ssize_t store_line2(struct device *dev, struct device_attribute *attr,
653 const char *buf, size_t count)
654{
655 return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE);
656}
657
658static ssize_t store_line3(struct device *dev, struct device_attribute *attr,
659 const char *buf, size_t count)
660{
661 return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE);
662}
663
664/* Interface to visible and audible "icons", these include:
665 * pictures on the LCD, the LED, and the dialtone signal.
666 */
667
668/* Get a list of "switchable elements" with their current state. */
669static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
670 char *buf)
671{
672 struct yealink_dev *yld;
673 int i, ret = 1;
674
675 down_read(&sysfs_rwsema);
676 yld = dev_get_drvdata(dev);
677 if (yld == NULL) {
678 up_read(&sysfs_rwsema);
679 return -ENODEV;
680 }
681
682 for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
683 if (lcdMap[i].type != '.')
684 continue;
685 ret += sprintf(&buf[ret], "%s %s\n",
686 yld->lcdMap[i] == ' ' ? " " : "on",
687 lcdMap[i].u.p.name);
688 }
689 up_read(&sysfs_rwsema);
690 return ret;
691}
692
693/* Change the visibility of a particular element. */
694static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
695 int chr)
696{
697 struct yealink_dev *yld;
698 int i;
699
700 down_write(&sysfs_rwsema);
701 yld = dev_get_drvdata(dev);
702 if (yld == NULL) {
703 up_write(&sysfs_rwsema);
704 return -ENODEV;
705 }
706
707 for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
708 if (lcdMap[i].type != '.')
709 continue;
710 if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) {
711 setChar(yld, i, chr);
712 break;
713 }
714 }
715
716 up_write(&sysfs_rwsema);
717 return count;
718}
719
720static ssize_t show_icon(struct device *dev, struct device_attribute *attr,
721 const char *buf, size_t count)
722{
723 return set_icon(dev, buf, count, buf[0]);
724}
725
726static ssize_t hide_icon(struct device *dev, struct device_attribute *attr,
727 const char *buf, size_t count)
728{
729 return set_icon(dev, buf, count, ' ');
730}
731
732/* Upload a ringtone to the device.
733 */
734
735/* Stores raw ringtone data in the phone */
736static ssize_t store_ringtone(struct device *dev,
737 struct device_attribute *attr,
738 const char *buf, size_t count)
739{
740 struct yealink_dev *yld;
741
742 down_write(&sysfs_rwsema);
743 yld = dev_get_drvdata(dev);
744 if (yld == NULL) {
745 up_write(&sysfs_rwsema);
746 return -ENODEV;
747 }
748
749 /* TODO locking with async usb control interface??? */
750 yealink_set_ringtone(yld, (char *)buf, count);
751 up_write(&sysfs_rwsema);
752 return count;
753}
754
755#define _M444 S_IRUGO
756#define _M664 S_IRUGO|S_IWUSR|S_IWGRP
757#define _M220 S_IWUSR|S_IWGRP
758
759static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map );
760static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 );
761static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 );
762static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 );
763static DEVICE_ATTR(get_icons , _M444, get_icons , NULL );
764static DEVICE_ATTR(show_icon , _M220, NULL , show_icon );
765static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon );
766static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone);
767
768static struct attribute *yld_attributes[] = {
769 &dev_attr_line1.attr,
770 &dev_attr_line2.attr,
771 &dev_attr_line3.attr,
772 &dev_attr_get_icons.attr,
773 &dev_attr_show_icon.attr,
774 &dev_attr_hide_icon.attr,
775 &dev_attr_map_seg7.attr,
776 &dev_attr_ringtone.attr,
777 NULL
778};
779
780static struct attribute_group yld_attr_group = {
781 .attrs = yld_attributes
782};
783
784/*******************************************************************************
785 * Linux interface and usb initialisation
786 ******************************************************************************/
787
788static const struct yld_device {
789 u16 idVendor;
790 u16 idProduct;
791 char *name;
792} yld_device[] = {
793 { 0x6993, 0xb001, "Yealink usb-p1k" },
794};
795
796static struct usb_device_id usb_table [] = {
797 { USB_INTERFACE_INFO(USB_CLASS_HID, 0, 0) },
798 { }
799};
800
801static int usb_cleanup(struct yealink_dev *yld, int err)
802{
803 if (yld == NULL)
804 return err;
805
806 if (yld->urb_irq) {
807 usb_kill_urb(yld->urb_irq);
808 usb_free_urb(yld->urb_irq);
809 }
810 if (yld->urb_ctl)
811 usb_free_urb(yld->urb_ctl);
812 if (yld->idev.dev)
813 input_unregister_device(&yld->idev);
814 if (yld->ctl_req)
815 usb_buffer_free(yld->udev, sizeof(*(yld->ctl_req)),
816 yld->ctl_req, yld->ctl_req_dma);
817 if (yld->ctl_data)
818 usb_buffer_free(yld->udev, USB_PKT_LEN,
819 yld->ctl_data, yld->ctl_dma);
820 if (yld->irq_data)
821 usb_buffer_free(yld->udev, USB_PKT_LEN,
822 yld->irq_data, yld->irq_dma);
823 kfree(yld);
824 return err;
825}
826
827static void usb_disconnect(struct usb_interface *intf)
828{
829 struct yealink_dev *yld;
830
831 down_write(&sysfs_rwsema);
832 yld = usb_get_intfdata(intf);
833 sysfs_remove_group(&intf->dev.kobj, &yld_attr_group);
834 usb_set_intfdata(intf, NULL);
835 up_write(&sysfs_rwsema);
836
837 usb_cleanup(yld, 0);
838}
839
840static int usb_match(struct usb_device *udev)
841{
842 int i;
843 for (i = 0; i < ARRAY_SIZE(yld_device); i++) {
844 if ((udev->descriptor.idVendor == yld_device[i].idVendor) &&
845 (udev->descriptor.idProduct == yld_device[i].idProduct))
846 return i;
847 }
848 return -ENODEV;
849}
850
851static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
852{
853 struct usb_device *udev = interface_to_usbdev (intf);
854 struct usb_host_interface *interface;
855 struct usb_endpoint_descriptor *endpoint;
856 struct yealink_dev *yld;
857 char path[64];
858 int ret, pipe, i;
859
860 i = usb_match(udev);
861 if (i < 0)
862 return -ENODEV;
863
864 interface = intf->cur_altsetting;
865 endpoint = &interface->endpoint[0].desc;
866 if (!(endpoint->bEndpointAddress & 0x80))
867 return -EIO;
868 if ((endpoint->bmAttributes & 3) != 3)
869 return -EIO;
870
871 if ((yld = kmalloc(sizeof(struct yealink_dev), GFP_KERNEL)) == NULL)
872 return -ENOMEM;
873
874 memset(yld, 0, sizeof(*yld));
875 yld->udev = udev;
876
877 /* allocate usb buffers */
878 yld->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
879 SLAB_ATOMIC, &yld->irq_dma);
880 if (yld->irq_data == NULL)
881 return usb_cleanup(yld, -ENOMEM);
882
883 yld->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
884 SLAB_ATOMIC, &yld->ctl_dma);
885 if (!yld->ctl_data)
886 return usb_cleanup(yld, -ENOMEM);
887
888 yld->ctl_req = usb_buffer_alloc(udev, sizeof(*(yld->ctl_req)),
889 SLAB_ATOMIC, &yld->ctl_req_dma);
890 if (yld->ctl_req == NULL)
891 return usb_cleanup(yld, -ENOMEM);
892
893 /* allocate urb structures */
894 yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
895 if (yld->urb_irq == NULL)
896 return usb_cleanup(yld, -ENOMEM);
897
898 yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
899 if (yld->urb_ctl == NULL)
900 return usb_cleanup(yld, -ENOMEM);
901
902 /* get a handle to the interrupt data pipe */
903 pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
904 ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
905 if (ret != USB_PKT_LEN)
906 err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
907
908 /* initialise irq urb */
909 usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data,
910 USB_PKT_LEN,
911 urb_irq_callback,
912 yld, endpoint->bInterval);
913 yld->urb_irq->transfer_dma = yld->irq_dma;
914 yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
915 yld->urb_irq->dev = udev;
916
917 /* initialise ctl urb */
918 yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
919 USB_DIR_OUT;
920 yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
921 yld->ctl_req->wValue = cpu_to_le16(0x200);
922 yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
923 yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
924
925 usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
926 (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN,
927 urb_ctl_callback, yld);
928 yld->urb_ctl->setup_dma = yld->ctl_req_dma;
929 yld->urb_ctl->transfer_dma = yld->ctl_dma;
930 yld->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
931 URB_NO_TRANSFER_DMA_MAP;
932 yld->urb_ctl->dev = udev;
933
934 /* find out the physical bus location */
935 if (usb_make_path(udev, path, sizeof(path)) > 0)
936 snprintf(yld->phys, sizeof(yld->phys)-1, "%s/input0", path);
937
938 /* register settings for the input device */
939 init_input_dev(&yld->idev);
940 yld->idev.private = yld;
941 yld->idev.id.bustype = BUS_USB;
942 yld->idev.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
943 yld->idev.id.product = le16_to_cpu(udev->descriptor.idProduct);
944 yld->idev.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
945 yld->idev.dev = &intf->dev;
946 yld->idev.name = yld_device[i].name;
947 yld->idev.phys = yld->phys;
948 /* yld->idev.event = input_ev; TODO */
949 yld->idev.open = input_open;
950 yld->idev.close = input_close;
951
952 /* register available key events */
953 yld->idev.evbit[0] = BIT(EV_KEY);
954 for (i = 0; i < 256; i++) {
955 int k = map_p1k_to_key(i);
956 if (k >= 0) {
957 set_bit(k & 0xff, yld->idev.keybit);
958 if (k >> 8)
959 set_bit(k >> 8, yld->idev.keybit);
960 }
961 }
962
963 printk(KERN_INFO "input: %s on %s\n", yld->idev.name, path);
964
965 input_register_device(&yld->idev);
966
967 usb_set_intfdata(intf, yld);
968
969 /* clear visible elements */
970 for (i=0; i<ARRAY_SIZE(lcdMap); i++)
971 setChar(yld, i, ' ');
972
973 /* display driver version on LCD line 3 */
974 store_line3(&intf->dev, NULL,
975 DRIVER_VERSION, sizeof(DRIVER_VERSION));
976
977 /* Register sysfs hooks (don't care about failure) */
978 sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
979 return 0;
980}
981
982static struct usb_driver yealink_driver = {
983 .owner = THIS_MODULE,
984 .name = "yealink",
985 .probe = usb_probe,
986 .disconnect = usb_disconnect,
987 .id_table = usb_table,
988};
989
990static int __init yealink_dev_init(void)
991{
992 int ret = usb_register(&yealink_driver);
993 if (ret == 0)
994 info(DRIVER_DESC ":" DRIVER_VERSION);
995 return ret;
996}
997
998static void __exit yealink_dev_exit(void)
999{
1000 usb_deregister(&yealink_driver);
1001}
1002
1003module_init(yealink_dev_init);
1004module_exit(yealink_dev_exit);
1005
1006MODULE_DEVICE_TABLE (usb, usb_table);
1007
1008MODULE_AUTHOR(DRIVER_AUTHOR);
1009MODULE_DESCRIPTION(DRIVER_DESC);
1010MODULE_LICENSE("GPL");
diff --git a/drivers/usb/input/yealink.h b/drivers/usb/input/yealink.h
new file mode 100644
index 000000000000..48af0be9cbdf
--- /dev/null
+++ b/drivers/usb/input/yealink.h
@@ -0,0 +1,220 @@
1/*
2 * drivers/usb/input/yealink.h
3 *
4 * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
5 *
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21#ifndef INPUT_YEALINK_H
22#define INPUT_YEALINK_H
23
24/* Using the control channel on interface 3 various aspects of the phone
25 * can be controlled like LCD, LED, dialtone and the ringtone.
26 */
27
28struct yld_ctl_packet {
29 u8 cmd; /* command code, see below */
30 u8 size; /* 1-11, size of used data bytes. */
31 u16 offset; /* internal packet offset */
32 u8 data[11];
33 s8 sum; /* negative sum of 15 preceding bytes */
34} __attribute__ ((packed));
35
36#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
37
38/* The following yld_ctl_packet's are available: */
39
40/* Init registers
41 *
42 * cmd 0x8e
43 * size 10
44 * offset 0
45 * data 0,0,0,0....
46 */
47#define CMD_INIT 0x8e
48
49/* Request key scan
50 *
51 * cmd 0x80
52 * size 1
53 * offset 0
54 * data[0] on return returns the key number, if it changes there's a new
55 * key pressed.
56 */
57#define CMD_KEYPRESS 0x80
58
59/* Request scancode
60 *
61 * cmd 0x81
62 * size 1
63 * offset key number [0-1f]
64 * data[0] on return returns the scancode
65 */
66#define CMD_SCANCODE 0x81
67
68/* Set LCD
69 *
70 * cmd 0x04
71 * size 1-11
72 * offset 0-23
73 * data segment bits
74 */
75#define CMD_LCD 0x04
76
77/* Set led
78 *
79 * cmd 0x05
80 * size 1
81 * offset 0
82 * data[0] 0 OFF / 1 ON
83 */
84#define CMD_LED 0x05
85
86/* Set ringtone volume
87 *
88 * cmd 0x11
89 * size 1
90 * offset 0
91 * data[0] 0-0xff volume
92 */
93#define CMD_RING_VOLUME 0x11
94
95/* Set ringtone notes
96 *
97 * cmd 0x02
98 * size 1-11
99 * offset 0->
100 * data binary representation LE16(-freq), LE16(duration) ....
101 */
102#define CMD_RING_NOTE 0x02
103
104/* Sound ringtone via the speaker on the back
105 *
106 * cmd 0x03
107 * size 1
108 * offset 0
109 * data[0] 0 OFF / 0x24 ON
110 */
111#define CMD_RINGTONE 0x03
112
113/* Sound dial tone via the ear speaker
114 *
115 * cmd 0x09
116 * size 1
117 * offset 0
118 * data[0] 0 OFF / 1 ON
119 */
120#define CMD_DIALTONE 0x09
121
122#endif /* INPUT_YEALINK_H */
123
124
125#if defined(_SEG) && defined(_PIC)
126/* This table maps the LCD segments onto individual bit positions in the
127 * yld_status struct.
128 */
129
130/* LCD, each segment must be driven seperately.
131 *
132 * Layout:
133 *
134 * |[] [][] [][] [][] in |[][]
135 * |[] M [][] D [][] : [][] out |[][]
136 * store
137 *
138 * NEW REP SU MO TU WE TH FR SA
139 *
140 * [] [] [] [] [] [] [] [] [] [] [] []
141 * [] [] [] [] [] [] [] [] [] [] [] []
142 */
143
144/* Line 1
145 * Format : 18.e8.M8.88...188
146 * Icon names : M D : IN OUT STORE
147 */
148#define LCD_LINE1_OFFSET 0
149#define LCD_LINE1_SIZE 17
150
151/* Note: first g then f => ! ! */
152/* _SEG( type a b c d e g f ) */
153 _SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
154 _SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
155 _PIC('.', 22,1 , "M" ),
156 _SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
157 _SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
158 _PIC('.', 15,8 , "D" ),
159 _SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
160 _SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
161 _PIC('.', 11,8 , ":" ),
162 _SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
163 _SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
164 _PIC('.', 7,1 , "IN" ),
165 _PIC('.', 7,2 , "OUT" ),
166 _PIC('.', 7,4 , "STORE" ),
167 _SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
168 _SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
169 _SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
170
171/* Line 2
172 * Format : .........
173 * Pict. name : NEW REP SU MO TU WE TH FR SA
174 */
175#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
176#define LCD_LINE2_SIZE 9
177
178 _PIC('.', 23,2 , "NEW" ),
179 _PIC('.', 23,4 , "REP" ),
180 _PIC('.', 1,8 , "SU" ),
181 _PIC('.', 1,4 , "MO" ),
182 _PIC('.', 1,2 , "TU" ),
183 _PIC('.', 1,1 , "WE" ),
184 _PIC('.', 0,1 , "TH" ),
185 _PIC('.', 0,2 , "FR" ),
186 _PIC('.', 0,4 , "SA" ),
187
188/* Line 3
189 * Format : 888888888888
190 */
191#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
192#define LCD_LINE3_SIZE 12
193
194 _SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
195 _SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
196 _SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
197 _SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
198 _SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
199 _SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
200 _SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
201 _SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
202 _SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
203 _SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
204 _SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
205 _SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
206
207/* Line 4
208 *
209 * The LED, DIALTONE and RINGTONE are implemented as icons and use the same
210 * sysfs interface.
211 */
212#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
213
214 _PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
215 _PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
216 _PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
217
218#undef _SEG
219#undef _PIC
220#endif /* _SEG && _PIC */