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