18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for the VoIP USB phones with CM109 chipsets.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci *   Tested devices:
108c2ecf20Sopenharmony_ci *	- Komunikate KIP1000
118c2ecf20Sopenharmony_ci *	- Genius G-talk
128c2ecf20Sopenharmony_ci *	- Allied-Telesis Corega USBPH01
138c2ecf20Sopenharmony_ci *	- ...
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * This driver is based on the yealink.c driver
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Thanks to:
188c2ecf20Sopenharmony_ci *   - Authors of yealink.c
198c2ecf20Sopenharmony_ci *   - Thomas Reitmayr
208c2ecf20Sopenharmony_ci *   - Oliver Neukum for good review comments and code
218c2ecf20Sopenharmony_ci *   - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
228c2ecf20Sopenharmony_ci *   - Dmitry Torokhov for valuable input and review
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Todo:
258c2ecf20Sopenharmony_ci *   - Read/write EEPROM
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/init.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/module.h>
328c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
338c2ecf20Sopenharmony_ci#include <linux/rwsem.h>
348c2ecf20Sopenharmony_ci#include <linux/usb/input.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define DRIVER_VERSION "20080805"
378c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR  "Alfred E. Heggestad"
388c2ecf20Sopenharmony_ci#define DRIVER_DESC    "CM109 phone driver"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic char *phone = "kip1000";
418c2ecf20Sopenharmony_cimodule_param(phone, charp, S_IRUSR);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cienum {
458c2ecf20Sopenharmony_ci	/* HID Registers */
468c2ecf20Sopenharmony_ci	HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down  */
478c2ecf20Sopenharmony_ci	HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0       */
488c2ecf20Sopenharmony_ci	HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1            */
498c2ecf20Sopenharmony_ci	HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL             */
508c2ecf20Sopenharmony_ci	HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
518c2ecf20Sopenharmony_ci	HID_OR1 = 0x01, /* GPO - General Purpose Output                 */
528c2ecf20Sopenharmony_ci	HID_OR2 = 0x02, /* Set GPIO to input/output mode                */
538c2ecf20Sopenharmony_ci	HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL          */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* HID_IR0 */
568c2ecf20Sopenharmony_ci	RECORD_MUTE   = 1 << 3,
578c2ecf20Sopenharmony_ci	PLAYBACK_MUTE = 1 << 2,
588c2ecf20Sopenharmony_ci	VOLUME_DOWN   = 1 << 1,
598c2ecf20Sopenharmony_ci	VOLUME_UP     = 1 << 0,
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* HID_OR0 */
628c2ecf20Sopenharmony_ci	/* bits 7-6
638c2ecf20Sopenharmony_ci	   0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
648c2ecf20Sopenharmony_ci	      and SPDIF
658c2ecf20Sopenharmony_ci	   1: HID_OR0-3 are used as generic HID registers
668c2ecf20Sopenharmony_ci	   2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
678c2ecf20Sopenharmony_ci	      EEPROM_DATA0-1, EEPROM_CTRL (see Note)
688c2ecf20Sopenharmony_ci	   3: Reserved
698c2ecf20Sopenharmony_ci	 */
708c2ecf20Sopenharmony_ci	HID_OR_GPO_BUZ_SPDIF   = 0 << 6,
718c2ecf20Sopenharmony_ci	HID_OR_GENERIC_HID_REG = 1 << 6,
728c2ecf20Sopenharmony_ci	HID_OR_MAP_MCU_EEPROM  = 2 << 6,
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	BUZZER_ON = 1 << 5,
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* up to 256 normal keys, up to 15 special key combinations */
778c2ecf20Sopenharmony_ci	KEYMAP_SIZE = 256 + 15,
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* CM109 protocol packet */
818c2ecf20Sopenharmony_cistruct cm109_ctl_packet {
828c2ecf20Sopenharmony_ci	u8 byte[4];
838c2ecf20Sopenharmony_ci} __attribute__ ((packed));
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cienum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* CM109 device structure */
888c2ecf20Sopenharmony_cistruct cm109_dev {
898c2ecf20Sopenharmony_ci	struct input_dev *idev;	 /* input device */
908c2ecf20Sopenharmony_ci	struct usb_device *udev; /* usb device */
918c2ecf20Sopenharmony_ci	struct usb_interface *intf;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* irq input channel */
948c2ecf20Sopenharmony_ci	struct cm109_ctl_packet *irq_data;
958c2ecf20Sopenharmony_ci	dma_addr_t irq_dma;
968c2ecf20Sopenharmony_ci	struct urb *urb_irq;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* control output channel */
998c2ecf20Sopenharmony_ci	struct cm109_ctl_packet *ctl_data;
1008c2ecf20Sopenharmony_ci	dma_addr_t ctl_dma;
1018c2ecf20Sopenharmony_ci	struct usb_ctrlrequest *ctl_req;
1028c2ecf20Sopenharmony_ci	struct urb *urb_ctl;
1038c2ecf20Sopenharmony_ci	/*
1048c2ecf20Sopenharmony_ci	 * The 3 bitfields below are protected by ctl_submit_lock.
1058c2ecf20Sopenharmony_ci	 * They have to be separate since they are accessed from IRQ
1068c2ecf20Sopenharmony_ci	 * context.
1078c2ecf20Sopenharmony_ci	 */
1088c2ecf20Sopenharmony_ci	unsigned irq_urb_pending:1;	/* irq_urb is in flight */
1098c2ecf20Sopenharmony_ci	unsigned ctl_urb_pending:1;	/* ctl_urb is in flight */
1108c2ecf20Sopenharmony_ci	unsigned buzzer_pending:1;	/* need to issue buzz command */
1118c2ecf20Sopenharmony_ci	spinlock_t ctl_submit_lock;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	unsigned char buzzer_state;	/* on/off */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* flags */
1168c2ecf20Sopenharmony_ci	unsigned open:1;
1178c2ecf20Sopenharmony_ci	unsigned resetting:1;
1188c2ecf20Sopenharmony_ci	unsigned shutdown:1;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* This mutex protects writes to the above flags */
1218c2ecf20Sopenharmony_ci	struct mutex pm_mutex;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	unsigned short keymap[KEYMAP_SIZE];
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	char phys[64];		/* physical device path */
1268c2ecf20Sopenharmony_ci	int key_code;		/* last reported key */
1278c2ecf20Sopenharmony_ci	int keybit;		/* 0=new scan  1,2,4,8=scan columns  */
1288c2ecf20Sopenharmony_ci	u8 gpi;			/* Cached value of GPI (high nibble) */
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/******************************************************************************
1328c2ecf20Sopenharmony_ci * CM109 key interface
1338c2ecf20Sopenharmony_ci *****************************************************************************/
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic unsigned short special_keymap(int code)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	if (code > 0xff) {
1388c2ecf20Sopenharmony_ci		switch (code - 0xff) {
1398c2ecf20Sopenharmony_ci		case RECORD_MUTE:	return KEY_MICMUTE;
1408c2ecf20Sopenharmony_ci		case PLAYBACK_MUTE:	return KEY_MUTE;
1418c2ecf20Sopenharmony_ci		case VOLUME_DOWN:	return KEY_VOLUMEDOWN;
1428c2ecf20Sopenharmony_ci		case VOLUME_UP:		return KEY_VOLUMEUP;
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	return KEY_RESERVED;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* Map device buttons to internal key events.
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci * The "up" and "down" keys, are symbolised by arrows on the button.
1518c2ecf20Sopenharmony_ci * The "pickup" and "hangup" keys are symbolised by a green and red phone
1528c2ecf20Sopenharmony_ci * on the button.
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci Komunikate KIP1000 Keyboard Matrix
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci     -> -- 1 -- 2 -- 3  --> GPI pin 4 (0x10)
1578c2ecf20Sopenharmony_ci      |    |    |    |
1588c2ecf20Sopenharmony_ci     <- -- 4 -- 5 -- 6  --> GPI pin 5 (0x20)
1598c2ecf20Sopenharmony_ci      |    |    |    |
1608c2ecf20Sopenharmony_ci     END - 7 -- 8 -- 9  --> GPI pin 6 (0x40)
1618c2ecf20Sopenharmony_ci      |    |    |    |
1628c2ecf20Sopenharmony_ci     OK -- * -- 0 -- #  --> GPI pin 7 (0x80)
1638c2ecf20Sopenharmony_ci      |    |    |    |
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci     /|\  /|\  /|\  /|\
1668c2ecf20Sopenharmony_ci      |    |    |    |
1678c2ecf20Sopenharmony_ciGPO
1688c2ecf20Sopenharmony_cipin:  3    2    1    0
1698c2ecf20Sopenharmony_ci     0x8  0x4  0x2  0x1
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic unsigned short keymap_kip1000(int scancode)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	switch (scancode) {				/* phone key:   */
1758c2ecf20Sopenharmony_ci	case 0x82: return KEY_NUMERIC_0;		/*   0          */
1768c2ecf20Sopenharmony_ci	case 0x14: return KEY_NUMERIC_1;		/*   1          */
1778c2ecf20Sopenharmony_ci	case 0x12: return KEY_NUMERIC_2;		/*   2          */
1788c2ecf20Sopenharmony_ci	case 0x11: return KEY_NUMERIC_3;		/*   3          */
1798c2ecf20Sopenharmony_ci	case 0x24: return KEY_NUMERIC_4;		/*   4          */
1808c2ecf20Sopenharmony_ci	case 0x22: return KEY_NUMERIC_5;		/*   5          */
1818c2ecf20Sopenharmony_ci	case 0x21: return KEY_NUMERIC_6;		/*   6          */
1828c2ecf20Sopenharmony_ci	case 0x44: return KEY_NUMERIC_7;		/*   7          */
1838c2ecf20Sopenharmony_ci	case 0x42: return KEY_NUMERIC_8;		/*   8          */
1848c2ecf20Sopenharmony_ci	case 0x41: return KEY_NUMERIC_9;		/*   9          */
1858c2ecf20Sopenharmony_ci	case 0x81: return KEY_NUMERIC_POUND;		/*   #          */
1868c2ecf20Sopenharmony_ci	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
1878c2ecf20Sopenharmony_ci	case 0x88: return KEY_ENTER;			/*   pickup     */
1888c2ecf20Sopenharmony_ci	case 0x48: return KEY_ESC;			/*   hangup     */
1898c2ecf20Sopenharmony_ci	case 0x28: return KEY_LEFT;			/*   IN         */
1908c2ecf20Sopenharmony_ci	case 0x18: return KEY_RIGHT;			/*   OUT        */
1918c2ecf20Sopenharmony_ci	default:   return special_keymap(scancode);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci  Contributed by Shaun Jackman <sjackman@gmail.com>
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci  Genius G-Talk keyboard matrix
1998c2ecf20Sopenharmony_ci     0 1 2 3
2008c2ecf20Sopenharmony_ci  4: 0 4 8 Talk
2018c2ecf20Sopenharmony_ci  5: 1 5 9 End
2028c2ecf20Sopenharmony_ci  6: 2 6 # Up
2038c2ecf20Sopenharmony_ci  7: 3 7 * Down
2048c2ecf20Sopenharmony_ci*/
2058c2ecf20Sopenharmony_cistatic unsigned short keymap_gtalk(int scancode)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	switch (scancode) {
2088c2ecf20Sopenharmony_ci	case 0x11: return KEY_NUMERIC_0;
2098c2ecf20Sopenharmony_ci	case 0x21: return KEY_NUMERIC_1;
2108c2ecf20Sopenharmony_ci	case 0x41: return KEY_NUMERIC_2;
2118c2ecf20Sopenharmony_ci	case 0x81: return KEY_NUMERIC_3;
2128c2ecf20Sopenharmony_ci	case 0x12: return KEY_NUMERIC_4;
2138c2ecf20Sopenharmony_ci	case 0x22: return KEY_NUMERIC_5;
2148c2ecf20Sopenharmony_ci	case 0x42: return KEY_NUMERIC_6;
2158c2ecf20Sopenharmony_ci	case 0x82: return KEY_NUMERIC_7;
2168c2ecf20Sopenharmony_ci	case 0x14: return KEY_NUMERIC_8;
2178c2ecf20Sopenharmony_ci	case 0x24: return KEY_NUMERIC_9;
2188c2ecf20Sopenharmony_ci	case 0x44: return KEY_NUMERIC_POUND;	/* # */
2198c2ecf20Sopenharmony_ci	case 0x84: return KEY_NUMERIC_STAR;	/* * */
2208c2ecf20Sopenharmony_ci	case 0x18: return KEY_ENTER;		/* Talk (green handset) */
2218c2ecf20Sopenharmony_ci	case 0x28: return KEY_ESC;		/* End (red handset) */
2228c2ecf20Sopenharmony_ci	case 0x48: return KEY_UP;		/* Menu up (rocker switch) */
2238c2ecf20Sopenharmony_ci	case 0x88: return KEY_DOWN;		/* Menu down (rocker switch) */
2248c2ecf20Sopenharmony_ci	default:   return special_keymap(scancode);
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/*
2298c2ecf20Sopenharmony_ci * Keymap for Allied-Telesis Corega USBPH01
2308c2ecf20Sopenharmony_ci * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
2318c2ecf20Sopenharmony_ci *
2328c2ecf20Sopenharmony_ci * Contributed by july@nat.bg
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic unsigned short keymap_usbph01(int scancode)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	switch (scancode) {
2378c2ecf20Sopenharmony_ci	case 0x11: return KEY_NUMERIC_0;		/*   0          */
2388c2ecf20Sopenharmony_ci	case 0x21: return KEY_NUMERIC_1;		/*   1          */
2398c2ecf20Sopenharmony_ci	case 0x41: return KEY_NUMERIC_2;		/*   2          */
2408c2ecf20Sopenharmony_ci	case 0x81: return KEY_NUMERIC_3;		/*   3          */
2418c2ecf20Sopenharmony_ci	case 0x12: return KEY_NUMERIC_4;		/*   4          */
2428c2ecf20Sopenharmony_ci	case 0x22: return KEY_NUMERIC_5;		/*   5          */
2438c2ecf20Sopenharmony_ci	case 0x42: return KEY_NUMERIC_6;		/*   6          */
2448c2ecf20Sopenharmony_ci	case 0x82: return KEY_NUMERIC_7;		/*   7          */
2458c2ecf20Sopenharmony_ci	case 0x14: return KEY_NUMERIC_8;		/*   8          */
2468c2ecf20Sopenharmony_ci	case 0x24: return KEY_NUMERIC_9;		/*   9          */
2478c2ecf20Sopenharmony_ci	case 0x44: return KEY_NUMERIC_POUND;		/*   #          */
2488c2ecf20Sopenharmony_ci	case 0x84: return KEY_NUMERIC_STAR;		/*   *          */
2498c2ecf20Sopenharmony_ci	case 0x18: return KEY_ENTER;			/*   pickup     */
2508c2ecf20Sopenharmony_ci	case 0x28: return KEY_ESC;			/*   hangup     */
2518c2ecf20Sopenharmony_ci	case 0x48: return KEY_LEFT;			/*   IN         */
2528c2ecf20Sopenharmony_ci	case 0x88: return KEY_RIGHT;			/*   OUT        */
2538c2ecf20Sopenharmony_ci	default:   return special_keymap(scancode);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/*
2588c2ecf20Sopenharmony_ci * Keymap for ATCom AU-100
2598c2ecf20Sopenharmony_ci * http://www.atcom.cn/products.html
2608c2ecf20Sopenharmony_ci * http://www.packetizer.com/products/au100/
2618c2ecf20Sopenharmony_ci * http://www.voip-info.org/wiki/view/AU-100
2628c2ecf20Sopenharmony_ci *
2638c2ecf20Sopenharmony_ci * Contributed by daniel@gimpelevich.san-francisco.ca.us
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_cistatic unsigned short keymap_atcom(int scancode)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	switch (scancode) {				/* phone key:   */
2688c2ecf20Sopenharmony_ci	case 0x82: return KEY_NUMERIC_0;		/*   0          */
2698c2ecf20Sopenharmony_ci	case 0x11: return KEY_NUMERIC_1;		/*   1          */
2708c2ecf20Sopenharmony_ci	case 0x12: return KEY_NUMERIC_2;		/*   2          */
2718c2ecf20Sopenharmony_ci	case 0x14: return KEY_NUMERIC_3;		/*   3          */
2728c2ecf20Sopenharmony_ci	case 0x21: return KEY_NUMERIC_4;		/*   4          */
2738c2ecf20Sopenharmony_ci	case 0x22: return KEY_NUMERIC_5;		/*   5          */
2748c2ecf20Sopenharmony_ci	case 0x24: return KEY_NUMERIC_6;		/*   6          */
2758c2ecf20Sopenharmony_ci	case 0x41: return KEY_NUMERIC_7;		/*   7          */
2768c2ecf20Sopenharmony_ci	case 0x42: return KEY_NUMERIC_8;		/*   8          */
2778c2ecf20Sopenharmony_ci	case 0x44: return KEY_NUMERIC_9;		/*   9          */
2788c2ecf20Sopenharmony_ci	case 0x84: return KEY_NUMERIC_POUND;		/*   #          */
2798c2ecf20Sopenharmony_ci	case 0x81: return KEY_NUMERIC_STAR;		/*   *          */
2808c2ecf20Sopenharmony_ci	case 0x18: return KEY_ENTER;			/*   pickup     */
2818c2ecf20Sopenharmony_ci	case 0x28: return KEY_ESC;			/*   hangup     */
2828c2ecf20Sopenharmony_ci	case 0x48: return KEY_LEFT;			/* left arrow   */
2838c2ecf20Sopenharmony_ci	case 0x88: return KEY_RIGHT;			/* right arrow  */
2848c2ecf20Sopenharmony_ci	default:   return special_keymap(scancode);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic unsigned short (*keymap)(int) = keymap_kip1000;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/*
2918c2ecf20Sopenharmony_ci * Completes a request by converting the data into events for the
2928c2ecf20Sopenharmony_ci * input subsystem.
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_cistatic void report_key(struct cm109_dev *dev, int key)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct input_dev *idev = dev->idev;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (dev->key_code >= 0) {
2998c2ecf20Sopenharmony_ci		/* old key up */
3008c2ecf20Sopenharmony_ci		input_report_key(idev, dev->key_code, 0);
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	dev->key_code = key;
3048c2ecf20Sopenharmony_ci	if (key >= 0) {
3058c2ecf20Sopenharmony_ci		/* new valid key */
3068c2ecf20Sopenharmony_ci		input_report_key(idev, key, 1);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	input_sync(idev);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/*
3138c2ecf20Sopenharmony_ci * Converts data of special key presses (volume, mute) into events
3148c2ecf20Sopenharmony_ci * for the input subsystem, sends press-n-release for mute keys.
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_cistatic void cm109_report_special(struct cm109_dev *dev)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	static const u8 autorelease = RECORD_MUTE | PLAYBACK_MUTE;
3198c2ecf20Sopenharmony_ci	struct input_dev *idev = dev->idev;
3208c2ecf20Sopenharmony_ci	u8 data = dev->irq_data->byte[HID_IR0];
3218c2ecf20Sopenharmony_ci	unsigned short keycode;
3228c2ecf20Sopenharmony_ci	int i;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
3258c2ecf20Sopenharmony_ci		keycode = dev->keymap[0xff + BIT(i)];
3268c2ecf20Sopenharmony_ci		if (keycode == KEY_RESERVED)
3278c2ecf20Sopenharmony_ci			continue;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		input_report_key(idev, keycode, data & BIT(i));
3308c2ecf20Sopenharmony_ci		if (data & autorelease & BIT(i)) {
3318c2ecf20Sopenharmony_ci			input_sync(idev);
3328c2ecf20Sopenharmony_ci			input_report_key(idev, keycode, 0);
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci	input_sync(idev);
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci/******************************************************************************
3398c2ecf20Sopenharmony_ci * CM109 usb communication interface
3408c2ecf20Sopenharmony_ci *****************************************************************************/
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic void cm109_submit_buzz_toggle(struct cm109_dev *dev)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	int error;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (dev->buzzer_state)
3478c2ecf20Sopenharmony_ci		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
3488c2ecf20Sopenharmony_ci	else
3498c2ecf20Sopenharmony_ci		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
3528c2ecf20Sopenharmony_ci	if (error)
3538c2ecf20Sopenharmony_ci		dev_err(&dev->intf->dev,
3548c2ecf20Sopenharmony_ci			"%s: usb_submit_urb (urb_ctl) failed %d\n",
3558c2ecf20Sopenharmony_ci			__func__, error);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/*
3598c2ecf20Sopenharmony_ci * IRQ handler
3608c2ecf20Sopenharmony_ci */
3618c2ecf20Sopenharmony_cistatic void cm109_urb_irq_callback(struct urb *urb)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct cm109_dev *dev = urb->context;
3648c2ecf20Sopenharmony_ci	const int status = urb->status;
3658c2ecf20Sopenharmony_ci	int error;
3668c2ecf20Sopenharmony_ci	unsigned long flags;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
3698c2ecf20Sopenharmony_ci	     dev->irq_data->byte[0],
3708c2ecf20Sopenharmony_ci	     dev->irq_data->byte[1],
3718c2ecf20Sopenharmony_ci	     dev->irq_data->byte[2],
3728c2ecf20Sopenharmony_ci	     dev->irq_data->byte[3],
3738c2ecf20Sopenharmony_ci	     dev->keybit);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (status) {
3768c2ecf20Sopenharmony_ci		if (status == -ESHUTDOWN)
3778c2ecf20Sopenharmony_ci			return;
3788c2ecf20Sopenharmony_ci		dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
3798c2ecf20Sopenharmony_ci				    __func__, status);
3808c2ecf20Sopenharmony_ci		goto out;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Special keys */
3848c2ecf20Sopenharmony_ci	cm109_report_special(dev);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Scan key column */
3878c2ecf20Sopenharmony_ci	if (dev->keybit == 0xf) {
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		/* Any changes ? */
3908c2ecf20Sopenharmony_ci		if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
3918c2ecf20Sopenharmony_ci			goto out;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
3948c2ecf20Sopenharmony_ci		dev->keybit = 0x1;
3958c2ecf20Sopenharmony_ci	} else {
3968c2ecf20Sopenharmony_ci		report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		dev->keybit <<= 1;
3998c2ecf20Sopenharmony_ci		if (dev->keybit > 0x8)
4008c2ecf20Sopenharmony_ci			dev->keybit = 0xf;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci out:
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->ctl_submit_lock, flags);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	dev->irq_urb_pending = 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (likely(!dev->shutdown)) {
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		if (dev->buzzer_state)
4128c2ecf20Sopenharmony_ci			dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
4138c2ecf20Sopenharmony_ci		else
4148c2ecf20Sopenharmony_ci			dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		dev->ctl_data->byte[HID_OR1] = dev->keybit;
4178c2ecf20Sopenharmony_ci		dev->ctl_data->byte[HID_OR2] = dev->keybit;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		dev->buzzer_pending = 0;
4208c2ecf20Sopenharmony_ci		dev->ctl_urb_pending = 1;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
4238c2ecf20Sopenharmony_ci		if (error)
4248c2ecf20Sopenharmony_ci			dev_err(&dev->intf->dev,
4258c2ecf20Sopenharmony_ci				"%s: usb_submit_urb (urb_ctl) failed %d\n",
4268c2ecf20Sopenharmony_ci				__func__, error);
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic void cm109_urb_ctl_callback(struct urb *urb)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct cm109_dev *dev = urb->context;
4358c2ecf20Sopenharmony_ci	const int status = urb->status;
4368c2ecf20Sopenharmony_ci	int error;
4378c2ecf20Sopenharmony_ci	unsigned long flags;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
4408c2ecf20Sopenharmony_ci	     dev->ctl_data->byte[0],
4418c2ecf20Sopenharmony_ci	     dev->ctl_data->byte[1],
4428c2ecf20Sopenharmony_ci	     dev->ctl_data->byte[2],
4438c2ecf20Sopenharmony_ci	     dev->ctl_data->byte[3]);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (status) {
4468c2ecf20Sopenharmony_ci		if (status == -ESHUTDOWN)
4478c2ecf20Sopenharmony_ci			return;
4488c2ecf20Sopenharmony_ci		dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
4498c2ecf20Sopenharmony_ci				    __func__, status);
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->ctl_submit_lock, flags);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	dev->ctl_urb_pending = 0;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if (likely(!dev->shutdown)) {
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		if (dev->buzzer_pending || status) {
4598c2ecf20Sopenharmony_ci			dev->buzzer_pending = 0;
4608c2ecf20Sopenharmony_ci			dev->ctl_urb_pending = 1;
4618c2ecf20Sopenharmony_ci			cm109_submit_buzz_toggle(dev);
4628c2ecf20Sopenharmony_ci		} else if (likely(!dev->irq_urb_pending)) {
4638c2ecf20Sopenharmony_ci			/* ask for key data */
4648c2ecf20Sopenharmony_ci			dev->irq_urb_pending = 1;
4658c2ecf20Sopenharmony_ci			error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
4668c2ecf20Sopenharmony_ci			if (error)
4678c2ecf20Sopenharmony_ci				dev_err(&dev->intf->dev,
4688c2ecf20Sopenharmony_ci					"%s: usb_submit_urb (urb_irq) failed %d\n",
4698c2ecf20Sopenharmony_ci					__func__, error);
4708c2ecf20Sopenharmony_ci		}
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic void cm109_toggle_buzzer_async(struct cm109_dev *dev)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	unsigned long flags;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->ctl_submit_lock, flags);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (dev->ctl_urb_pending) {
4838c2ecf20Sopenharmony_ci		/* URB completion will resubmit */
4848c2ecf20Sopenharmony_ci		dev->buzzer_pending = 1;
4858c2ecf20Sopenharmony_ci	} else {
4868c2ecf20Sopenharmony_ci		dev->ctl_urb_pending = 1;
4878c2ecf20Sopenharmony_ci		cm109_submit_buzz_toggle(dev);
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	int error;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (on)
4988c2ecf20Sopenharmony_ci		dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
4998c2ecf20Sopenharmony_ci	else
5008c2ecf20Sopenharmony_ci		dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	error = usb_control_msg(dev->udev,
5038c2ecf20Sopenharmony_ci				usb_sndctrlpipe(dev->udev, 0),
5048c2ecf20Sopenharmony_ci				dev->ctl_req->bRequest,
5058c2ecf20Sopenharmony_ci				dev->ctl_req->bRequestType,
5068c2ecf20Sopenharmony_ci				le16_to_cpu(dev->ctl_req->wValue),
5078c2ecf20Sopenharmony_ci				le16_to_cpu(dev->ctl_req->wIndex),
5088c2ecf20Sopenharmony_ci				dev->ctl_data,
5098c2ecf20Sopenharmony_ci				USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
5108c2ecf20Sopenharmony_ci	if (error < 0 && error != -EINTR)
5118c2ecf20Sopenharmony_ci		dev_err(&dev->intf->dev, "%s: usb_control_msg() failed %d\n",
5128c2ecf20Sopenharmony_ci			__func__, error);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic void cm109_stop_traffic(struct cm109_dev *dev)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	dev->shutdown = 1;
5188c2ecf20Sopenharmony_ci	/*
5198c2ecf20Sopenharmony_ci	 * Make sure other CPUs see this
5208c2ecf20Sopenharmony_ci	 */
5218c2ecf20Sopenharmony_ci	smp_wmb();
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	usb_kill_urb(dev->urb_ctl);
5248c2ecf20Sopenharmony_ci	usb_kill_urb(dev->urb_irq);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	cm109_toggle_buzzer_sync(dev, 0);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	dev->shutdown = 0;
5298c2ecf20Sopenharmony_ci	smp_wmb();
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void cm109_restore_state(struct cm109_dev *dev)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	if (dev->open) {
5358c2ecf20Sopenharmony_ci		/*
5368c2ecf20Sopenharmony_ci		 * Restore buzzer state.
5378c2ecf20Sopenharmony_ci		 * This will also kick regular URB submission
5388c2ecf20Sopenharmony_ci		 */
5398c2ecf20Sopenharmony_ci		cm109_toggle_buzzer_async(dev);
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci/******************************************************************************
5448c2ecf20Sopenharmony_ci * input event interface
5458c2ecf20Sopenharmony_ci *****************************************************************************/
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int cm109_input_open(struct input_dev *idev)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct cm109_dev *dev = input_get_drvdata(idev);
5508c2ecf20Sopenharmony_ci	int error;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	error = usb_autopm_get_interface(dev->intf);
5538c2ecf20Sopenharmony_ci	if (error < 0) {
5548c2ecf20Sopenharmony_ci		dev_err(&idev->dev, "%s - cannot autoresume, result %d\n",
5558c2ecf20Sopenharmony_ci			__func__, error);
5568c2ecf20Sopenharmony_ci		return error;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	mutex_lock(&dev->pm_mutex);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	dev->buzzer_state = 0;
5628c2ecf20Sopenharmony_ci	dev->key_code = -1;	/* no keys pressed */
5638c2ecf20Sopenharmony_ci	dev->keybit = 0xf;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* issue INIT */
5668c2ecf20Sopenharmony_ci	dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
5678c2ecf20Sopenharmony_ci	dev->ctl_data->byte[HID_OR1] = dev->keybit;
5688c2ecf20Sopenharmony_ci	dev->ctl_data->byte[HID_OR2] = dev->keybit;
5698c2ecf20Sopenharmony_ci	dev->ctl_data->byte[HID_OR3] = 0x00;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	dev->ctl_urb_pending = 1;
5728c2ecf20Sopenharmony_ci	error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
5738c2ecf20Sopenharmony_ci	if (error) {
5748c2ecf20Sopenharmony_ci		dev->ctl_urb_pending = 0;
5758c2ecf20Sopenharmony_ci		dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
5768c2ecf20Sopenharmony_ci			__func__, error);
5778c2ecf20Sopenharmony_ci	} else {
5788c2ecf20Sopenharmony_ci		dev->open = 1;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	mutex_unlock(&dev->pm_mutex);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (error)
5848c2ecf20Sopenharmony_ci		usb_autopm_put_interface(dev->intf);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return error;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic void cm109_input_close(struct input_dev *idev)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct cm109_dev *dev = input_get_drvdata(idev);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	mutex_lock(&dev->pm_mutex);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	/*
5968c2ecf20Sopenharmony_ci	 * Once we are here event delivery is stopped so we
5978c2ecf20Sopenharmony_ci	 * don't need to worry about someone starting buzzer
5988c2ecf20Sopenharmony_ci	 * again
5998c2ecf20Sopenharmony_ci	 */
6008c2ecf20Sopenharmony_ci	cm109_stop_traffic(dev);
6018c2ecf20Sopenharmony_ci	dev->open = 0;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	mutex_unlock(&dev->pm_mutex);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic int cm109_input_ev(struct input_dev *idev, unsigned int type,
6098c2ecf20Sopenharmony_ci			  unsigned int code, int value)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct cm109_dev *dev = input_get_drvdata(idev);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	dev_dbg(&dev->intf->dev,
6148c2ecf20Sopenharmony_ci		"input_ev: type=%u code=%u value=%d\n", type, code, value);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (type != EV_SND)
6178c2ecf20Sopenharmony_ci		return -EINVAL;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	switch (code) {
6208c2ecf20Sopenharmony_ci	case SND_TONE:
6218c2ecf20Sopenharmony_ci	case SND_BELL:
6228c2ecf20Sopenharmony_ci		dev->buzzer_state = !!value;
6238c2ecf20Sopenharmony_ci		if (!dev->resetting)
6248c2ecf20Sopenharmony_ci			cm109_toggle_buzzer_async(dev);
6258c2ecf20Sopenharmony_ci		return 0;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	default:
6288c2ecf20Sopenharmony_ci		return -EINVAL;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci/******************************************************************************
6348c2ecf20Sopenharmony_ci * Linux interface and usb initialisation
6358c2ecf20Sopenharmony_ci *****************************************************************************/
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistruct driver_info {
6388c2ecf20Sopenharmony_ci	char *name;
6398c2ecf20Sopenharmony_ci};
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic const struct driver_info info_cm109 = {
6428c2ecf20Sopenharmony_ci	.name = "CM109 USB driver",
6438c2ecf20Sopenharmony_ci};
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cienum {
6468c2ecf20Sopenharmony_ci	VENDOR_ID        = 0x0d8c, /* C-Media Electronics */
6478c2ecf20Sopenharmony_ci	PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
6488c2ecf20Sopenharmony_ci};
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci/* table of devices that work with this driver */
6518c2ecf20Sopenharmony_cistatic const struct usb_device_id cm109_usb_table[] = {
6528c2ecf20Sopenharmony_ci	{
6538c2ecf20Sopenharmony_ci		.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
6548c2ecf20Sopenharmony_ci				USB_DEVICE_ID_MATCH_INT_INFO,
6558c2ecf20Sopenharmony_ci		.idVendor = VENDOR_ID,
6568c2ecf20Sopenharmony_ci		.idProduct = PRODUCT_ID_CM109,
6578c2ecf20Sopenharmony_ci		.bInterfaceClass = USB_CLASS_HID,
6588c2ecf20Sopenharmony_ci		.bInterfaceSubClass = 0,
6598c2ecf20Sopenharmony_ci		.bInterfaceProtocol = 0,
6608c2ecf20Sopenharmony_ci		.driver_info = (kernel_ulong_t) &info_cm109
6618c2ecf20Sopenharmony_ci	},
6628c2ecf20Sopenharmony_ci	/* you can add more devices here with product ID 0x0008 - 0x000f */
6638c2ecf20Sopenharmony_ci	{ }
6648c2ecf20Sopenharmony_ci};
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic void cm109_usb_cleanup(struct cm109_dev *dev)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	kfree(dev->ctl_req);
6698c2ecf20Sopenharmony_ci	usb_free_coherent(dev->udev, USB_PKT_LEN, dev->ctl_data, dev->ctl_dma);
6708c2ecf20Sopenharmony_ci	usb_free_coherent(dev->udev, USB_PKT_LEN, dev->irq_data, dev->irq_dma);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	usb_free_urb(dev->urb_irq);	/* parameter validation in core/urb */
6738c2ecf20Sopenharmony_ci	usb_free_urb(dev->urb_ctl);	/* parameter validation in core/urb */
6748c2ecf20Sopenharmony_ci	kfree(dev);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic void cm109_usb_disconnect(struct usb_interface *interface)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct cm109_dev *dev = usb_get_intfdata(interface);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, NULL);
6828c2ecf20Sopenharmony_ci	input_unregister_device(dev->idev);
6838c2ecf20Sopenharmony_ci	cm109_usb_cleanup(dev);
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic int cm109_usb_probe(struct usb_interface *intf,
6878c2ecf20Sopenharmony_ci			   const struct usb_device_id *id)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
6908c2ecf20Sopenharmony_ci	struct driver_info *nfo = (struct driver_info *)id->driver_info;
6918c2ecf20Sopenharmony_ci	struct usb_host_interface *interface;
6928c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
6938c2ecf20Sopenharmony_ci	struct cm109_dev *dev;
6948c2ecf20Sopenharmony_ci	struct input_dev *input_dev = NULL;
6958c2ecf20Sopenharmony_ci	int ret, pipe, i;
6968c2ecf20Sopenharmony_ci	int error = -ENOMEM;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	interface = intf->cur_altsetting;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (interface->desc.bNumEndpoints < 1)
7018c2ecf20Sopenharmony_ci		return -ENODEV;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	endpoint = &interface->endpoint[0].desc;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (!usb_endpoint_is_int_in(endpoint))
7068c2ecf20Sopenharmony_ci		return -ENODEV;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
7098c2ecf20Sopenharmony_ci	if (!dev)
7108c2ecf20Sopenharmony_ci		return -ENOMEM;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	spin_lock_init(&dev->ctl_submit_lock);
7138c2ecf20Sopenharmony_ci	mutex_init(&dev->pm_mutex);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	dev->udev = udev;
7168c2ecf20Sopenharmony_ci	dev->intf = intf;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	dev->idev = input_dev = input_allocate_device();
7198c2ecf20Sopenharmony_ci	if (!input_dev)
7208c2ecf20Sopenharmony_ci		goto err_out;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* allocate usb buffers */
7238c2ecf20Sopenharmony_ci	dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
7248c2ecf20Sopenharmony_ci					   GFP_KERNEL, &dev->irq_dma);
7258c2ecf20Sopenharmony_ci	if (!dev->irq_data)
7268c2ecf20Sopenharmony_ci		goto err_out;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
7298c2ecf20Sopenharmony_ci					   GFP_KERNEL, &dev->ctl_dma);
7308c2ecf20Sopenharmony_ci	if (!dev->ctl_data)
7318c2ecf20Sopenharmony_ci		goto err_out;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	dev->ctl_req = kmalloc(sizeof(*(dev->ctl_req)), GFP_KERNEL);
7348c2ecf20Sopenharmony_ci	if (!dev->ctl_req)
7358c2ecf20Sopenharmony_ci		goto err_out;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* allocate urb structures */
7388c2ecf20Sopenharmony_ci	dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
7398c2ecf20Sopenharmony_ci	if (!dev->urb_irq)
7408c2ecf20Sopenharmony_ci		goto err_out;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
7438c2ecf20Sopenharmony_ci	if (!dev->urb_ctl)
7448c2ecf20Sopenharmony_ci		goto err_out;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	/* get a handle to the interrupt data pipe */
7478c2ecf20Sopenharmony_ci	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
7488c2ecf20Sopenharmony_ci	ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
7498c2ecf20Sopenharmony_ci	if (ret != USB_PKT_LEN)
7508c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
7518c2ecf20Sopenharmony_ci			ret, USB_PKT_LEN);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	/* initialise irq urb */
7548c2ecf20Sopenharmony_ci	usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
7558c2ecf20Sopenharmony_ci			 USB_PKT_LEN,
7568c2ecf20Sopenharmony_ci			 cm109_urb_irq_callback, dev, endpoint->bInterval);
7578c2ecf20Sopenharmony_ci	dev->urb_irq->transfer_dma = dev->irq_dma;
7588c2ecf20Sopenharmony_ci	dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
7598c2ecf20Sopenharmony_ci	dev->urb_irq->dev = udev;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	/* initialise ctl urb */
7628c2ecf20Sopenharmony_ci	dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
7638c2ecf20Sopenharmony_ci					USB_DIR_OUT;
7648c2ecf20Sopenharmony_ci	dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
7658c2ecf20Sopenharmony_ci	dev->ctl_req->wValue = cpu_to_le16(0x200);
7668c2ecf20Sopenharmony_ci	dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
7678c2ecf20Sopenharmony_ci	dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
7708c2ecf20Sopenharmony_ci			     (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
7718c2ecf20Sopenharmony_ci			     cm109_urb_ctl_callback, dev);
7728c2ecf20Sopenharmony_ci	dev->urb_ctl->transfer_dma = dev->ctl_dma;
7738c2ecf20Sopenharmony_ci	dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
7748c2ecf20Sopenharmony_ci	dev->urb_ctl->dev = udev;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* find out the physical bus location */
7778c2ecf20Sopenharmony_ci	usb_make_path(udev, dev->phys, sizeof(dev->phys));
7788c2ecf20Sopenharmony_ci	strlcat(dev->phys, "/input0", sizeof(dev->phys));
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* register settings for the input device */
7818c2ecf20Sopenharmony_ci	input_dev->name = nfo->name;
7828c2ecf20Sopenharmony_ci	input_dev->phys = dev->phys;
7838c2ecf20Sopenharmony_ci	usb_to_input_id(udev, &input_dev->id);
7848c2ecf20Sopenharmony_ci	input_dev->dev.parent = &intf->dev;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, dev);
7878c2ecf20Sopenharmony_ci	input_dev->open = cm109_input_open;
7888c2ecf20Sopenharmony_ci	input_dev->close = cm109_input_close;
7898c2ecf20Sopenharmony_ci	input_dev->event = cm109_input_ev;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	input_dev->keycode = dev->keymap;
7928c2ecf20Sopenharmony_ci	input_dev->keycodesize = sizeof(unsigned char);
7938c2ecf20Sopenharmony_ci	input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
7968c2ecf20Sopenharmony_ci	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/* register available key events */
7998c2ecf20Sopenharmony_ci	for (i = 0; i < KEYMAP_SIZE; i++) {
8008c2ecf20Sopenharmony_ci		unsigned short k = keymap(i);
8018c2ecf20Sopenharmony_ci		dev->keymap[i] = k;
8028c2ecf20Sopenharmony_ci		__set_bit(k, input_dev->keybit);
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci	__clear_bit(KEY_RESERVED, input_dev->keybit);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	error = input_register_device(dev->idev);
8078c2ecf20Sopenharmony_ci	if (error)
8088c2ecf20Sopenharmony_ci		goto err_out;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, dev);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return 0;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci err_out:
8158c2ecf20Sopenharmony_ci	input_free_device(input_dev);
8168c2ecf20Sopenharmony_ci	cm109_usb_cleanup(dev);
8178c2ecf20Sopenharmony_ci	return error;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct cm109_dev *dev = usb_get_intfdata(intf);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	mutex_lock(&dev->pm_mutex);
8278c2ecf20Sopenharmony_ci	cm109_stop_traffic(dev);
8288c2ecf20Sopenharmony_ci	mutex_unlock(&dev->pm_mutex);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	return 0;
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic int cm109_usb_resume(struct usb_interface *intf)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	struct cm109_dev *dev = usb_get_intfdata(intf);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "cm109: usb_resume\n");
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	mutex_lock(&dev->pm_mutex);
8408c2ecf20Sopenharmony_ci	cm109_restore_state(dev);
8418c2ecf20Sopenharmony_ci	mutex_unlock(&dev->pm_mutex);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	return 0;
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic int cm109_usb_pre_reset(struct usb_interface *intf)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct cm109_dev *dev = usb_get_intfdata(intf);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	mutex_lock(&dev->pm_mutex);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/*
8538c2ecf20Sopenharmony_ci	 * Make sure input events don't try to toggle buzzer
8548c2ecf20Sopenharmony_ci	 * while we are resetting
8558c2ecf20Sopenharmony_ci	 */
8568c2ecf20Sopenharmony_ci	dev->resetting = 1;
8578c2ecf20Sopenharmony_ci	smp_wmb();
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	cm109_stop_traffic(dev);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	return 0;
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic int cm109_usb_post_reset(struct usb_interface *intf)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	struct cm109_dev *dev = usb_get_intfdata(intf);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	dev->resetting = 0;
8698c2ecf20Sopenharmony_ci	smp_wmb();
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	cm109_restore_state(dev);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	mutex_unlock(&dev->pm_mutex);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	return 0;
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic struct usb_driver cm109_driver = {
8798c2ecf20Sopenharmony_ci	.name		= "cm109",
8808c2ecf20Sopenharmony_ci	.probe		= cm109_usb_probe,
8818c2ecf20Sopenharmony_ci	.disconnect	= cm109_usb_disconnect,
8828c2ecf20Sopenharmony_ci	.suspend	= cm109_usb_suspend,
8838c2ecf20Sopenharmony_ci	.resume		= cm109_usb_resume,
8848c2ecf20Sopenharmony_ci	.reset_resume	= cm109_usb_resume,
8858c2ecf20Sopenharmony_ci	.pre_reset	= cm109_usb_pre_reset,
8868c2ecf20Sopenharmony_ci	.post_reset	= cm109_usb_post_reset,
8878c2ecf20Sopenharmony_ci	.id_table	= cm109_usb_table,
8888c2ecf20Sopenharmony_ci	.supports_autosuspend = 1,
8898c2ecf20Sopenharmony_ci};
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cistatic int __init cm109_select_keymap(void)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	/* Load the phone keymap */
8948c2ecf20Sopenharmony_ci	if (!strcasecmp(phone, "kip1000")) {
8958c2ecf20Sopenharmony_ci		keymap = keymap_kip1000;
8968c2ecf20Sopenharmony_ci		printk(KERN_INFO KBUILD_MODNAME ": "
8978c2ecf20Sopenharmony_ci			"Keymap for Komunikate KIP1000 phone loaded\n");
8988c2ecf20Sopenharmony_ci	} else if (!strcasecmp(phone, "gtalk")) {
8998c2ecf20Sopenharmony_ci		keymap = keymap_gtalk;
9008c2ecf20Sopenharmony_ci		printk(KERN_INFO KBUILD_MODNAME ": "
9018c2ecf20Sopenharmony_ci			"Keymap for Genius G-talk phone loaded\n");
9028c2ecf20Sopenharmony_ci	} else if (!strcasecmp(phone, "usbph01")) {
9038c2ecf20Sopenharmony_ci		keymap = keymap_usbph01;
9048c2ecf20Sopenharmony_ci		printk(KERN_INFO KBUILD_MODNAME ": "
9058c2ecf20Sopenharmony_ci			"Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
9068c2ecf20Sopenharmony_ci	} else if (!strcasecmp(phone, "atcom")) {
9078c2ecf20Sopenharmony_ci		keymap = keymap_atcom;
9088c2ecf20Sopenharmony_ci		printk(KERN_INFO KBUILD_MODNAME ": "
9098c2ecf20Sopenharmony_ci			"Keymap for ATCom AU-100 phone loaded\n");
9108c2ecf20Sopenharmony_ci	} else {
9118c2ecf20Sopenharmony_ci		printk(KERN_ERR KBUILD_MODNAME ": "
9128c2ecf20Sopenharmony_ci			"Unsupported phone: %s\n", phone);
9138c2ecf20Sopenharmony_ci		return -EINVAL;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	return 0;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic int __init cm109_init(void)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	int err;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	err = cm109_select_keymap();
9248c2ecf20Sopenharmony_ci	if (err)
9258c2ecf20Sopenharmony_ci		return err;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	err = usb_register(&cm109_driver);
9288c2ecf20Sopenharmony_ci	if (err)
9298c2ecf20Sopenharmony_ci		return err;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	printk(KERN_INFO KBUILD_MODNAME ": "
9328c2ecf20Sopenharmony_ci		DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	return 0;
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_cistatic void __exit cm109_exit(void)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	usb_deregister(&cm109_driver);
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cimodule_init(cm109_init);
9438c2ecf20Sopenharmony_cimodule_exit(cm109_exit);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, cm109_usb_table);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
9488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
9498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
950