Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] input-driver-yealink-P1K-usb-phone

This patch aggregates all modifications in the -mm tree and adds
complete ringtone support.

The following features are supported:
- keyboard full support
- LCD full support
- LED full support
- dialtone full support
- ringtone full support
- audio playback via generic usb audio diver
- audio record via generic usb audio diver

For driver documentation see: Documentation/input/yealink.txt
For vendor documentation see: http://yealink.com

Signed-off-by: Henk <Henk.Vergonet@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Henk and committed by
Greg Kroah-Hartman
aca951a2 d6450e19

+1627
+187
Documentation/input/yealink.txt
··· 1 + yealink - Linux driver for usb-p1k phones 2 + 3 + 0. Status 4 + ~~~~~~~~~ 5 + 6 + The 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 + 16 + 1. Compilation (stand alone version) 17 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 + 19 + Currently only kernel 2.6.x.y versions are supported. 20 + In order to build the yealink.ko module do: 21 + 22 + make 23 + 24 + If you encounter problems please check if in the MAKE_OPTS variable in 25 + the Makefile is pointing to the location where your kernel sources 26 + are located, default /usr/src/linux. 27 + 28 + 29 + 30 + 2. keyboard features 31 + ~~~~~~~~~~~~~~~~~~~~ 32 + The current mapping in the kernel is provided by the map_p1k_to_key 33 + function: 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 + 53 + 3. LCD features 54 + ~~~~~~~~~~~~~~~ 55 + The 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 + 67 + Line 1 Format (see below) : 18.e8.M8.88...188 68 + Icon names : M D : IN OUT STORE 69 + Line 2 Format : ......... 70 + Icon name : NEW REP SU MO TU WE TH FR SA 71 + Line 3 Format : 888888888888 72 + 73 + 74 + Format 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 + 93 + 4. Driver usage 94 + ~~~~~~~~~~~~~~~ 95 + For 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 + 113 + 4.1 lineX 114 + ~~~~~~~~~ 115 + Reading /sys/../lineX will return the format string with its current value: 116 + 117 + Example: 118 + cat ./line3 119 + 888888888888 120 + Linux Rocks! 121 + 122 + Writing 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 + 135 + 4.2 get_icons 136 + ~~~~~~~~~~~~~ 137 + Reading 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 + 160 + 4.3 show/hide icons 161 + ~~~~~~~~~~~~~~~~~~~ 162 + Writing to these files will update the state of the icon. 163 + Only one icon at a time can be updated. 164 + 165 + If an icon is also on a ./lineX the corresponding value is 166 + updated 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 + 181 + 5. 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 +
+6
MAINTAINERS
··· 116 116 L: linux-hams@vger.kernel.org 117 117 S: Maintained 118 118 119 + YEALINK PHONE DRIVER 120 + P: Henk Vergonet 121 + M: Henk.Vergonet@gmail.com 122 + L: usbb2k-api-dev@nongnu.org 123 + S: Maintained 124 + 119 125 8139CP 10/100 FAST ETHERNET DRIVER 120 126 P: Jeff Garzik 121 127 M: jgarzik@pobox.com
+14
drivers/usb/input/Kconfig
··· 230 230 To compile this driver as a module, choose M here: the 231 231 module will be called touchkitusb. 232 232 233 + config 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 + 233 247 config USB_XPAD 234 248 tristate "X-Box gamepad support" 235 249 depends on USB && INPUT
+1
drivers/usb/input/Makefile
··· 39 39 obj-$(CONFIG_USB_POWERMATE) += powermate.o 40 40 obj-$(CONFIG_USB_WACOM) += wacom.o 41 41 obj-$(CONFIG_USB_ACECAD) += acecad.o 42 + obj-$(CONFIG_USB_YEALINK) += yealink.o 42 43 obj-$(CONFIG_USB_XPAD) += xpad.o
+189
drivers/usb/input/map_to_7segment.h
··· 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 + 76 + struct seg7_conversion_map { 77 + unsigned char table[128]; 78 + }; 79 + 80 + static 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 +
+1010
drivers/usb/input/yealink.c
··· 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 + 67 + struct 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 + 88 + static 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 + 103 + struct 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 + */ 139 + static 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 + */ 145 + static 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 + */ 206 + static 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 + */ 238 + static 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 + 264 + static 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 + 282 + static 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 + 296 + static 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 + */ 331 + static 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 + 363 + send_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 + */ 427 + static 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 + 458 + static 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? 487 + static 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 + 506 + static 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 + 533 + static 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 + 545 + static DECLARE_RWSEM(sysfs_rwsema); 546 + 547 + /* Interface to the 7-segments translation table aka. char set. 548 + */ 549 + static 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 + 556 + static 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 + */ 575 + static 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 + 599 + static 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 + 605 + static 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 + 611 + static 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 + */ 624 + static 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 + 646 + static 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 + 652 + static 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 + 658 + static 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. */ 669 + static 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. */ 694 + static 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 + 720 + static 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 + 726 + static 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 */ 736 + static 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 + 759 + static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map ); 760 + static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 ); 761 + static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 ); 762 + static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 ); 763 + static DEVICE_ATTR(get_icons , _M444, get_icons , NULL ); 764 + static DEVICE_ATTR(show_icon , _M220, NULL , show_icon ); 765 + static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon ); 766 + static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone); 767 + 768 + static 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 + 780 + static struct attribute_group yld_attr_group = { 781 + .attrs = yld_attributes 782 + }; 783 + 784 + /******************************************************************************* 785 + * Linux interface and usb initialisation 786 + ******************************************************************************/ 787 + 788 + static const struct yld_device { 789 + u16 idVendor; 790 + u16 idProduct; 791 + char *name; 792 + } yld_device[] = { 793 + { 0x6993, 0xb001, "Yealink usb-p1k" }, 794 + }; 795 + 796 + static struct usb_device_id usb_table [] = { 797 + { USB_INTERFACE_INFO(USB_CLASS_HID, 0, 0) }, 798 + { } 799 + }; 800 + 801 + static 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 + 827 + static 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 + 840 + static 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 + 851 + static 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 + 982 + static 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 + 990 + static 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 + 998 + static void __exit yealink_dev_exit(void) 999 + { 1000 + usb_deregister(&yealink_driver); 1001 + } 1002 + 1003 + module_init(yealink_dev_init); 1004 + module_exit(yealink_dev_exit); 1005 + 1006 + MODULE_DEVICE_TABLE (usb, usb_table); 1007 + 1008 + MODULE_AUTHOR(DRIVER_AUTHOR); 1009 + MODULE_DESCRIPTION(DRIVER_DESC); 1010 + MODULE_LICENSE("GPL");
+220
drivers/usb/input/yealink.h
··· 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 + 28 + struct 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 */