⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 yealink.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * drivers/usb/input/yealink.c * * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * Description: *   Driver for the USB-P1K voip usb phone. *   This device is produced by Yealink Network Technology Co Ltd *   but may be branded under several names: *	- Yealink usb-p1k *	- Tiptel 115 *	- ... * * This driver is based on: *   - the usbb2k-api	http://savannah.nongnu.org/projects/usbb2k-api/ *   - information from	http://memeteau.free.fr/usbb2k *   - the xpad-driver	drivers/usb/input/xpad.c * * Thanks to: *   - Olivier Vandorpe, for providing the usbb2k-api. *   - Martin Diehl, for spotting my memory allocation bug. * * History: *   20050527 henk	First version, functional keyboard. Keyboard events *			will pop-up on the ../input/eventX bus. *   20050531 henk	Added led, LCD, dialtone and sysfs interface. *   20050610 henk	Cleanups, make it ready for public consumption. *   20050630 henk	Cleanups, fixes in response to comments. *   20050701 henk	sysfs write serialisation, fix potential unload races *   20050801 henk	Added ringtone, restructure USB *   20050816 henk	Merge 2.6.13-rc6 */#include <linux/config.h>#include <linux/kernel.h>#include <linux/input.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/rwsem.h>#include <linux/usb.h>#include <linux/usb_input.h>#include "map_to_7segment.h"#include "yealink.h"#define DRIVER_VERSION "yld-20050816"#define DRIVER_AUTHOR "Henk Vergonet"#define DRIVER_DESC "Yealink phone driver"#define YEALINK_POLLING_FREQUENCY	10	/* in [Hz] */struct yld_status {	u8	lcd[24];	u8	led;	u8	dialtone;	u8	ringtone;	u8	keynum;} __attribute__ ((packed));/* * Register the LCD segment and icon map */#define _LOC(k,l)	{ .a = (k), .m = (l) }#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm)	\	{ .type	= (t),							\	  .u = { .s = {	_LOC(a, am), _LOC(b, bm), _LOC(c, cm),		\		        _LOC(d, dm), _LOC(e, em), _LOC(g, gm),		\			_LOC(f, fm) } } }#define _PIC(t, h, hm, n)						\	{ .type	= (t),							\ 	  .u = { .p = { .name = (n), .a = (h), .m = (hm) } } }static const struct lcd_segment_map {	char	type;	union {		struct pictogram_map {			u8	a,m;			char	name[10];		}	p;		struct segment_map {			u8	a,m;		} s[7];	} u;} lcdMap[] = {#include "yealink.h"};struct yealink_dev {	struct input_dev *idev;		/* input device */	struct usb_device *udev;	/* usb device */	/* irq input channel */	struct yld_ctl_packet	*irq_data;	dma_addr_t		irq_dma;	struct urb		*urb_irq;	/* control output channel */	struct yld_ctl_packet	*ctl_data;	dma_addr_t		ctl_dma;	struct usb_ctrlrequest	*ctl_req;	dma_addr_t		ctl_req_dma;	struct urb		*urb_ctl;	char phys[64];			/* physical device path */	u8 lcdMap[ARRAY_SIZE(lcdMap)];	/* state of LCD, LED ... */	int key_code;			/* last reported key	 */	int	stat_ix;	union {		struct yld_status s;		u8		  b[sizeof(struct yld_status)];	} master, copy;};/******************************************************************************* * Yealink lcd interface ******************************************************************************//* * Register a default 7 segment character set */static SEG7_DEFAULT_MAP(map_seg7); /* Display a char,  * char '\9' and '\n' are placeholders and do not overwrite the original text.  * A space will always hide an icon.  */static int setChar(struct yealink_dev *yld, int el, int chr){	int i, a, m, val;	if (el >= ARRAY_SIZE(lcdMap))		return -EINVAL;	if (chr == '\t' || chr == '\n')	    return 0;	yld->lcdMap[el] = chr;	if (lcdMap[el].type == '.') {		a = lcdMap[el].u.p.a;		m = lcdMap[el].u.p.m;		if (chr != ' ')			yld->master.b[a] |= m;		else			yld->master.b[a] &= ~m;		return 0;	}	val = map_to_seg7(&map_seg7, chr);	for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) {		m = lcdMap[el].u.s[i].m;		if (m == 0)			continue;		a = lcdMap[el].u.s[i].a;		if (val & 1)			yld->master.b[a] |= m;		else			yld->master.b[a] &= ~m;		val = val >> 1;	}	return 0;};/******************************************************************************* * Yealink key interface ******************************************************************************//* Map device buttons to internal key events. * * USB-P1K button layout: * *             up *       IN           OUT *            down * *     pickup   C    hangup *       1      2      3 *       4      5      6 *       7      8      9 *       *      0      # * * The "up" and "down" keys, are symbolised by arrows on the button. * The "pickup" and "hangup" keys are symbolised by a green and red phone * on the button. */static int map_p1k_to_key(int scancode){	switch(scancode) {		/* phone key:	*/	case 0x23: return KEY_LEFT;	/*   IN		*/	case 0x33: return KEY_UP;	/*   up		*/	case 0x04: return KEY_RIGHT;	/*   OUT	*/	case 0x24: return KEY_DOWN;	/*   down	*/	case 0x03: return KEY_ENTER;	/*   pickup	*/	case 0x14: return KEY_BACKSPACE; /*  C		*/	case 0x13: return KEY_ESC;	/*   hangup	*/	case 0x00: return KEY_1;	/*   1		*/	case 0x01: return KEY_2;	/*   2 		*/	case 0x02: return KEY_3;	/*   3		*/	case 0x10: return KEY_4;	/*   4		*/	case 0x11: return KEY_5;	/*   5		*/	case 0x12: return KEY_6;	/*   6		*/	case 0x20: return KEY_7;	/*   7		*/	case 0x21: return KEY_8;	/*   8		*/	case 0x22: return KEY_9;	/*   9		*/	case 0x30: return KEY_KPASTERISK; /* *		*/	case 0x31: return KEY_0;	/*   0		*/	case 0x32: return KEY_LEFTSHIFT |			  KEY_3 << 8;	/*   #		*/	}	return -EINVAL;}/* Completes a request by converting the data into events for the * input subsystem. * * The key parameter can be cascaded: key2 << 8 | key1 */static void report_key(struct yealink_dev *yld, int key, struct pt_regs *regs){	struct input_dev *idev = yld->idev;	input_regs(idev, regs);	if (yld->key_code >= 0) {		/* old key up */		input_report_key(idev, yld->key_code & 0xff, 0);		if (yld->key_code >> 8)			input_report_key(idev, yld->key_code >> 8, 0);	}	yld->key_code = key;	if (key >= 0) {		/* new valid key */		input_report_key(idev, key & 0xff, 1);		if (key >> 8)			input_report_key(idev, key >> 8, 1);	}	input_sync(idev);}/******************************************************************************* * Yealink usb communication interface ******************************************************************************/static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p){	u8	*buf = (u8 *)p;	int	i;	u8	sum = 0;	for(i=0; i<USB_PKT_LEN-1; i++)		sum -= buf[i];	p->sum = sum;	return usb_control_msg(yld->udev,			usb_sndctrlpipe(yld->udev, 0),			USB_REQ_SET_CONFIGURATION,			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,			0x200, 3,			p, sizeof(*p),			USB_CTRL_SET_TIMEOUT);}static u8 default_ringtone[] = {	0xEF,			/* volume [0-255] */	0xFB, 0x1E, 0x00, 0x0C,	/* 1250 [hz], 12/100 [s] */	0xFC, 0x18, 0x00, 0x0C,	/* 1000 [hz], 12/100 [s] */	0xFB, 0x1E, 0x00, 0x0C,	0xFC, 0x18, 0x00, 0x0C,	0xFB, 0x1E, 0x00, 0x0C,	0xFC, 0x18, 0x00, 0x0C,	0xFB, 0x1E, 0x00, 0x0C,	0xFC, 0x18, 0x00, 0x0C,	0xFF, 0xFF, 0x01, 0x90,	/* silent, 400/100 [s] */	0x00, 0x00		/* end of sequence */};static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size){	struct yld_ctl_packet *p = yld->ctl_data;	int	ix, len;	if (size <= 0)		return -EINVAL;	/* Set the ringtone volume */	memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));	yld->ctl_data->cmd	= CMD_RING_VOLUME;	yld->ctl_data->size	= 1;	yld->ctl_data->data[0]	= buf[0];	yealink_cmd(yld, p);	buf++;	size--;	p->cmd = CMD_RING_NOTE;	ix = 0;	while (size != ix) {		len = size - ix;		if (len > sizeof(p->data))			len = sizeof(p->data);		p->size	  = len;		p->offset = cpu_to_be16(ix);		memcpy(p->data, &buf[ix], len);		yealink_cmd(yld, p);		ix += len;	}	return 0;}/* keep stat_master & stat_copy in sync. */static int yealink_do_idle_tasks(struct yealink_dev *yld){	u8 val;	int i, ix, len;	ix = yld->stat_ix;	memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));	yld->ctl_data->cmd  = CMD_KEYPRESS;	yld->ctl_data->size = 1;	yld->ctl_data->sum  = 0xff - CMD_KEYPRESS;	/* If state update pointer wraps do a KEYPRESS first. */	if (ix >= sizeof(yld->master)) {		yld->stat_ix = 0;		return 0;	}	/* find update candidates: copy != master */	do {		val = yld->master.b[ix];		if (val != yld->copy.b[ix])			goto send_update;	} while (++ix < sizeof(yld->master));	/* nothing todo, wait a bit and poll for a KEYPRESS */	yld->stat_ix = 0;	/* TODO how can we wait abit. ??	 * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY);	 */	return 0;send_update:	/* Setup an appropriate update request */	yld->copy.b[ix] = val;	yld->ctl_data->data[0] = val;	switch(ix) {	case offsetof(struct yld_status, led):		yld->ctl_data->cmd	= CMD_LED;		yld->ctl_data->sum	= -1 - CMD_LED - val;		break;	case offsetof(struct yld_status, dialtone):		yld->ctl_data->cmd	= CMD_DIALTONE;		yld->ctl_data->sum	= -1 - CMD_DIALTONE - val;		break;	case offsetof(struct yld_status, ringtone):		yld->ctl_data->cmd	= CMD_RINGTONE;		yld->ctl_data->sum	= -1 - CMD_RINGTONE - val;		break;	case offsetof(struct yld_status, keynum):		val--;		val &= 0x1f;		yld->ctl_data->cmd	= CMD_SCANCODE;		yld->ctl_data->offset	= cpu_to_be16(val);		yld->ctl_data->data[0]	= 0;		yld->ctl_data->sum	= -1 - CMD_SCANCODE - val;		break;	default:		len = sizeof(yld->master.s.lcd) - ix;		if (len > sizeof(yld->ctl_data->data))			len = sizeof(yld->ctl_data->data);		/* Combine up to <len> consecutive LCD bytes in a singe request		 */		yld->ctl_data->cmd	= CMD_LCD;		yld->ctl_data->offset	= cpu_to_be16(ix);		yld->ctl_data->size	= len;		yld->ctl_data->sum	= -CMD_LCD - ix - val - len;		for(i=1; i<len; i++) {			ix++;			val = yld->master.b[ix];			yld->copy.b[ix]		= val;			yld->ctl_data->data[i]	= val;			yld->ctl_data->sum     -= val;		}	}	yld->stat_ix = ix + 1;	return 1;}/* Decide on how to handle responses * * The state transition diagram is somethhing like: * *          syncState<--+ *               |      | *               |    idle *              \|/     | * init --ok--> waitForKey --ok--> getKey *  ^               ^                | *  |               +-------ok-------+ * error,start * */static void urb_irq_callback(struct urb *urb, struct pt_regs *regs){	struct yealink_dev *yld = urb->context;	int ret;	if (urb->status)		err("%s - urb status %d", __FUNCTION__, urb->status);	switch (yld->irq_data->cmd) {	case CMD_KEYPRESS:		yld->master.s.keynum = yld->irq_data->data[0];		break;	case CMD_SCANCODE:		dbg("get scancode %x", yld->irq_data->data[0]);		report_key(yld, map_p1k_to_key(yld->irq_data->data[0]), regs);		break;	default:		err("unexpected response %x", yld->irq_data->cmd);	}	yealink_do_idle_tasks(yld);	ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);	if (ret)		err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);}static void urb_ctl_callback(struct urb *urb, struct pt_regs *regs){	struct yealink_dev *yld = urb->context;	int ret;	if (urb->status)		err("%s - urb status %d", __FUNCTION__, urb->status);	switch (yld->ctl_data->cmd) {	case CMD_KEYPRESS:	case CMD_SCANCODE:		/* ask for a response */		ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);		break;	default:		/* send new command */		yealink_do_idle_tasks(yld);		ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);	}	if (ret)		err("%s - usb_submit_urb failed %d", __FUNCTION__, ret);}/******************************************************************************* * input event interface ******************************************************************************//* TODO should we issue a ringtone on a SND_BELL event?static int input_ev(struct input_dev *dev, unsigned int type,		unsigned int code, int value){	if (type != EV_SND)		return -EINVAL;	switch (code) {	case SND_BELL:	case SND_TONE:		break;	default:		return -EINVAL;	}	return 0;}*/static int input_open(struct input_dev *dev){	struct yealink_dev *yld = dev->private;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -