940 lines · 24.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Driver for the VoIP USB phones with CM109 chipsets. * * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org> */ /* *   Tested devices: *	- Komunikate KIP1000 *	- Genius G-talk *	- Allied-Telesis Corega USBPH01 *	- ... * * This driver is based on the yealink.c driver * * Thanks to: *   - Authors of yealink.c *   - Thomas Reitmayr *   - Oliver Neukum for good review comments and code *   - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap *   - Dmitry Torokhov for valuable input and review * * Todo: *   - Read/write EEPROM */ #include <linux/kernel.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/rwsem.h>#include <linux/usb/input.h> #define DRIVER_VERSION "20080805"#define DRIVER_AUTHOR  "Alfred E. Heggestad"#define DRIVER_DESC    "CM109 phone driver" static char *phone = "kip1000";module_param(phone, charp, S_IRUSR);MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}"); enum {	/* HID Registers */	HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down  */	HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0       */	HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1            */	HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL             */	HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */	HID_OR1 = 0x01, /* GPO - General Purpose Output                 */	HID_OR2 = 0x02, /* Set GPIO to input/output mode                */	HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL          */ 	/* HID_IR0 */	RECORD_MUTE   = 1 << 3,	PLAYBACK_MUTE = 1 << 2,	VOLUME_DOWN   = 1 << 1,	VOLUME_UP     = 1 << 0, 	/* HID_OR0 */	/* bits 7-6	   0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer	      and SPDIF	   1: HID_OR0-3 are used as generic HID registers	   2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,	      EEPROM_DATA0-1, EEPROM_CTRL (see Note)	   3: Reserved	 */	HID_OR_GPO_BUZ_SPDIF   = 0 << 6,	HID_OR_GENERIC_HID_REG = 1 << 6,	HID_OR_MAP_MCU_EEPROM  = 2 << 6, 	BUZZER_ON = 1 << 5, 	/* up to 256 normal keys, up to 15 special key combinations */	KEYMAP_SIZE = 256 + 15,}; /* CM109 protocol packet */struct cm109_ctl_packet {	u8 byte[4];} __attribute__ ((packed)); enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) }; /* CM109 device structure */struct cm109_dev {	struct input_dev *idev;	 /* input device */	struct usb_device *udev; /* usb device */	struct usb_interface *intf; 	/* irq input channel */	struct cm109_ctl_packet *irq_data;	dma_addr_t irq_dma;	struct urb *urb_irq; 	/* control output channel */	struct cm109_ctl_packet *ctl_data;	dma_addr_t ctl_dma;	struct usb_ctrlrequest *ctl_req;	struct urb *urb_ctl;	/*	 * The 3 bitfields below are protected by ctl_submit_lock.	 * They have to be separate since they are accessed from IRQ	 * context.	 */	unsigned irq_urb_pending:1;	/* irq_urb is in flight */	unsigned ctl_urb_pending:1;	/* ctl_urb is in flight */	unsigned buzzer_pending:1;	/* need to issue buzz command */	spinlock_t ctl_submit_lock; 	unsigned char buzzer_state;	/* on/off */ 	/* flags */	unsigned open:1;	unsigned resetting:1;	unsigned shutdown:1; 	/* This mutex protects writes to the above flags */	struct mutex pm_mutex; 	unsigned short keymap[KEYMAP_SIZE]; 	char phys[64];		/* physical device path */	int key_code;		/* last reported key */	int keybit;		/* 0=new scan  1,2,4,8=scan columns  */	u8 gpi;			/* Cached value of GPI (high nibble) */}; /****************************************************************************** * CM109 key interface *****************************************************************************/ static unsigned short special_keymap(int code){	if (code > 0xff) {		switch (code - 0xff) {		case RECORD_MUTE:	return KEY_MICMUTE;		case PLAYBACK_MUTE:	return KEY_MUTE;		case VOLUME_DOWN:	return KEY_VOLUMEDOWN;		case VOLUME_UP:		return KEY_VOLUMEUP;		}	}	return KEY_RESERVED;} /* Map device buttons to internal key events. * * 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.  Komunikate KIP1000 Keyboard Matrix      -> -- 1 -- 2 -- 3  --> GPI pin 4 (0x10)      |    |    |    |     <- -- 4 -- 5 -- 6  --> GPI pin 5 (0x20)      |    |    |    |     END - 7 -- 8 -- 9  --> GPI pin 6 (0x40)      |    |    |    |     OK -- * -- 0 -- #  --> GPI pin 7 (0x80)      |    |    |    |      /|\  /|\  /|\  /|\      |    |    |    |GPOpin:  3    2    1    0     0x8  0x4  0x2  0x1  */static unsigned short keymap_kip1000(int scancode){	switch (scancode) {				/* phone key:   */	case 0x82: return KEY_NUMERIC_0;		/*   0          */	case 0x14: return KEY_NUMERIC_1;		/*   1          */	case 0x12: return KEY_NUMERIC_2;		/*   2          */	case 0x11: return KEY_NUMERIC_3;		/*   3          */	case 0x24: return KEY_NUMERIC_4;		/*   4          */	case 0x22: return KEY_NUMERIC_5;		/*   5          */	case 0x21: return KEY_NUMERIC_6;		/*   6          */	case 0x44: return KEY_NUMERIC_7;		/*   7          */	case 0x42: return KEY_NUMERIC_8;		/*   8          */	case 0x41: return KEY_NUMERIC_9;		/*   9          */	case 0x81: return KEY_NUMERIC_POUND;		/*   #          */	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */	case 0x88: return KEY_ENTER;			/*   pickup     */	case 0x48: return KEY_ESC;			/*   hangup     */	case 0x28: return KEY_LEFT;			/*   IN         */	case 0x18: return KEY_RIGHT;			/*   OUT        */	default:   return special_keymap(scancode);	}} /*  Contributed by Shaun Jackman <sjackman@gmail.com>   Genius G-Talk keyboard matrix     0 1 2 3  4: 0 4 8 Talk  5: 1 5 9 End  6: 2 6 # Up  7: 3 7 * Down*/static unsigned short keymap_gtalk(int scancode){	switch (scancode) {	case 0x11: return KEY_NUMERIC_0;	case 0x21: return KEY_NUMERIC_1;	case 0x41: return KEY_NUMERIC_2;	case 0x81: return KEY_NUMERIC_3;	case 0x12: return KEY_NUMERIC_4;	case 0x22: return KEY_NUMERIC_5;	case 0x42: return KEY_NUMERIC_6;	case 0x82: return KEY_NUMERIC_7;	case 0x14: return KEY_NUMERIC_8;	case 0x24: return KEY_NUMERIC_9;	case 0x44: return KEY_NUMERIC_POUND;	/* # */	case 0x84: return KEY_NUMERIC_STAR;	/* * */	case 0x18: return KEY_ENTER;		/* Talk (green handset) */	case 0x28: return KEY_ESC;		/* End (red handset) */	case 0x48: return KEY_UP;		/* Menu up (rocker switch) */	case 0x88: return KEY_DOWN;		/* Menu down (rocker switch) */	default:   return special_keymap(scancode);	}} /* * Keymap for Allied-Telesis Corega USBPH01 * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html * * Contributed by july@nat.bg */static unsigned short keymap_usbph01(int scancode){	switch (scancode) {	case 0x11: return KEY_NUMERIC_0;		/*   0          */	case 0x21: return KEY_NUMERIC_1;		/*   1          */	case 0x41: return KEY_NUMERIC_2;		/*   2          */	case 0x81: return KEY_NUMERIC_3;		/*   3          */	case 0x12: return KEY_NUMERIC_4;		/*   4          */	case 0x22: return KEY_NUMERIC_5;		/*   5          */	case 0x42: return KEY_NUMERIC_6;		/*   6          */	case 0x82: return KEY_NUMERIC_7;		/*   7          */	case 0x14: return KEY_NUMERIC_8;		/*   8          */	case 0x24: return KEY_NUMERIC_9;		/*   9          */	case 0x44: return KEY_NUMERIC_POUND;		/*   #          */	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */	case 0x18: return KEY_ENTER;			/*   pickup     */	case 0x28: return KEY_ESC;			/*   hangup     */	case 0x48: return KEY_LEFT;			/*   IN         */	case 0x88: return KEY_RIGHT;			/*   OUT        */	default:   return special_keymap(scancode);	}} /* * Keymap for ATCom AU-100 * http://www.atcom.cn/products.html  * http://www.packetizer.com/products/au100/ * http://www.voip-info.org/wiki/view/AU-100 * * Contributed by daniel@gimpelevich.san-francisco.ca.us */static unsigned short keymap_atcom(int scancode){	switch (scancode) {				/* phone key:   */	case 0x82: return KEY_NUMERIC_0;		/*   0          */	case 0x11: return KEY_NUMERIC_1;		/*   1          */	case 0x12: return KEY_NUMERIC_2;		/*   2          */	case 0x14: return KEY_NUMERIC_3;		/*   3          */	case 0x21: return KEY_NUMERIC_4;		/*   4          */	case 0x22: return KEY_NUMERIC_5;		/*   5          */	case 0x24: return KEY_NUMERIC_6;		/*   6          */	case 0x41: return KEY_NUMERIC_7;		/*   7          */	case 0x42: return KEY_NUMERIC_8;		/*   8          */	case 0x44: return KEY_NUMERIC_9;		/*   9          */	case 0x84: return KEY_NUMERIC_POUND;		/*   #          */	case 0x81: return KEY_NUMERIC_STAR;		/*   *          */	case 0x18: return KEY_ENTER;			/*   pickup     */	case 0x28: return KEY_ESC;			/*   hangup     */	case 0x48: return KEY_LEFT;			/* left arrow   */	case 0x88: return KEY_RIGHT;			/* right arrow  */	default:   return special_keymap(scancode);	}} static unsigned short (*keymap)(int) = keymap_kip1000; /* * Completes a request by converting the data into events for the * input subsystem. */static void report_key(struct cm109_dev *dev, int key){	struct input_dev *idev = dev->idev; 	if (dev->key_code >= 0) {		/* old key up */		input_report_key(idev, dev->key_code, 0);	} 	dev->key_code = key;	if (key >= 0) {		/* new valid key */		input_report_key(idev, key, 1);	} 	input_sync(idev);} /* * Converts data of special key presses (volume, mute) into events * for the input subsystem, sends press-n-release for mute keys. */static void cm109_report_special(struct cm109_dev *dev){	static const u8 autorelease = RECORD_MUTE | PLAYBACK_MUTE;	struct input_dev *idev = dev->idev;	u8 data = dev->irq_data->byte[HID_IR0];	unsigned short keycode;	int i; 	for (i = 0; i < 4; i++) {		keycode = dev->keymap[0xff + BIT(i)];		if (keycode == KEY_RESERVED)			continue; 		input_report_key(idev, keycode, data & BIT(i));		if (data & autorelease & BIT(i)) {			input_sync(idev);			input_report_key(idev, keycode, 0);		}	}	input_sync(idev);} /****************************************************************************** * CM109 usb communication interface *****************************************************************************/ static void cm109_submit_buzz_toggle(struct cm109_dev *dev){	int error; 	if (dev->buzzer_state)		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;	else		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; 	error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);	if (error)		dev_err(&dev->intf->dev,			"%s: usb_submit_urb (urb_ctl) failed %d\n",			__func__, error);} static void cm109_submit_ctl(struct cm109_dev *dev){	int error; 	guard(spinlock_irqsave)(&dev->ctl_submit_lock); 	dev->irq_urb_pending = 0; 	if (unlikely(dev->shutdown))		return; 	if (dev->buzzer_state)		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;	else		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; 	dev->ctl_data->byte[HID_OR1] = dev->keybit;	dev->ctl_data->byte[HID_OR2] = dev->keybit; 	dev->buzzer_pending = 0;	dev->ctl_urb_pending = 1; 	error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);	if (error)		dev_err(&dev->intf->dev,			"%s: usb_submit_urb (urb_ctl) failed %d\n",			__func__, error);} /* * IRQ handler */static void cm109_urb_irq_callback(struct urb *urb){	struct cm109_dev *dev = urb->context;	const int status = urb->status; 	dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",	     dev->irq_data->byte[0],	     dev->irq_data->byte[1],	     dev->irq_data->byte[2],	     dev->irq_data->byte[3],	     dev->keybit); 	if (status) {		if (status == -ESHUTDOWN)			return;		dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",				    __func__, status);		goto out;	} 	/* Special keys */	cm109_report_special(dev); 	/* Scan key column */	if (dev->keybit == 0xf) { 		/* Any changes ? */		if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))			goto out; 		dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;		dev->keybit = 0x1;	} else {		report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]); 		dev->keybit <<= 1;		if (dev->keybit > 0x8)			dev->keybit = 0xf;	}  out:	cm109_submit_ctl(dev);} static void cm109_urb_ctl_callback(struct urb *urb){	struct cm109_dev *dev = urb->context;	const int status = urb->status;	int error; 	dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",	     dev->ctl_data->byte[0],	     dev->ctl_data->byte[1],	     dev->ctl_data->byte[2],	     dev->ctl_data->byte[3]); 	if (status) {		if (status == -ESHUTDOWN)			return;		dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",				    __func__, status);	} 	guard(spinlock_irqsave)(&dev->ctl_submit_lock); 	dev->ctl_urb_pending = 0; 	if (unlikely(dev->shutdown))		return; 	if (dev->buzzer_pending || status) {		dev->buzzer_pending = 0;		dev->ctl_urb_pending = 1;		cm109_submit_buzz_toggle(dev);	} else if (likely(!dev->irq_urb_pending)) {		/* ask for key data */		dev->irq_urb_pending = 1;		error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);		if (error)			dev_err(&dev->intf->dev,				"%s: usb_submit_urb (urb_irq) failed %d\n",				__func__, error);	}} static void cm109_toggle_buzzer_async(struct cm109_dev *dev){	guard(spinlock_irqsave)(&dev->ctl_submit_lock); 	if (dev->ctl_urb_pending) {		/* URB completion will resubmit */		dev->buzzer_pending = 1;	} else {		dev->ctl_urb_pending = 1;		cm109_submit_buzz_toggle(dev);	}} static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on){	int error; 	if (on)		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;	else		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; 	error = usb_control_msg(dev->udev,				usb_sndctrlpipe(dev->udev, 0),				dev->ctl_req->bRequest,				dev->ctl_req->bRequestType,

Lines 1–500 of 940.