18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 1999-2001 Vojtech Pavlik 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on the work of: 68c2ecf20Sopenharmony_ci * Andree Borrmann Mats Sjövall 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/parport.h> 218c2ecf20Sopenharmony_ci#include <linux/input.h> 228c2ecf20Sopenharmony_ci#include <linux/mutex.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver"); 278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct db9_config { 308c2ecf20Sopenharmony_ci int args[2]; 318c2ecf20Sopenharmony_ci unsigned int nargs; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define DB9_MAX_PORTS 3 358c2ecf20Sopenharmony_cistatic struct db9_config db9_cfg[DB9_MAX_PORTS]; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cimodule_param_array_named(dev, db9_cfg[0].args, int, &db9_cfg[0].nargs, 0); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)"); 398c2ecf20Sopenharmony_cimodule_param_array_named(dev2, db9_cfg[1].args, int, &db9_cfg[1].nargs, 0); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)"); 418c2ecf20Sopenharmony_cimodule_param_array_named(dev3, db9_cfg[2].args, int, &db9_cfg[2].nargs, 0); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define DB9_ARG_PARPORT 0 458c2ecf20Sopenharmony_ci#define DB9_ARG_MODE 1 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define DB9_MULTI_STICK 0x01 488c2ecf20Sopenharmony_ci#define DB9_MULTI2_STICK 0x02 498c2ecf20Sopenharmony_ci#define DB9_GENESIS_PAD 0x03 508c2ecf20Sopenharmony_ci#define DB9_GENESIS5_PAD 0x05 518c2ecf20Sopenharmony_ci#define DB9_GENESIS6_PAD 0x06 528c2ecf20Sopenharmony_ci#define DB9_SATURN_PAD 0x07 538c2ecf20Sopenharmony_ci#define DB9_MULTI_0802 0x08 548c2ecf20Sopenharmony_ci#define DB9_MULTI_0802_2 0x09 558c2ecf20Sopenharmony_ci#define DB9_CD32_PAD 0x0A 568c2ecf20Sopenharmony_ci#define DB9_SATURN_DPP 0x0B 578c2ecf20Sopenharmony_ci#define DB9_SATURN_DPP_2 0x0C 588c2ecf20Sopenharmony_ci#define DB9_MAX_PAD 0x0D 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define DB9_UP 0x01 618c2ecf20Sopenharmony_ci#define DB9_DOWN 0x02 628c2ecf20Sopenharmony_ci#define DB9_LEFT 0x04 638c2ecf20Sopenharmony_ci#define DB9_RIGHT 0x08 648c2ecf20Sopenharmony_ci#define DB9_FIRE1 0x10 658c2ecf20Sopenharmony_ci#define DB9_FIRE2 0x20 668c2ecf20Sopenharmony_ci#define DB9_FIRE3 0x40 678c2ecf20Sopenharmony_ci#define DB9_FIRE4 0x80 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define DB9_NORMAL 0x0a 708c2ecf20Sopenharmony_ci#define DB9_NOSELECT 0x08 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define DB9_GENESIS6_DELAY 14 738c2ecf20Sopenharmony_ci#define DB9_REFRESH_TIME HZ/100 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define DB9_MAX_DEVICES 2 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistruct db9_mode_data { 788c2ecf20Sopenharmony_ci const char *name; 798c2ecf20Sopenharmony_ci const short *buttons; 808c2ecf20Sopenharmony_ci int n_buttons; 818c2ecf20Sopenharmony_ci int n_pads; 828c2ecf20Sopenharmony_ci int n_axis; 838c2ecf20Sopenharmony_ci int bidirectional; 848c2ecf20Sopenharmony_ci int reverse; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct db9 { 888c2ecf20Sopenharmony_ci struct input_dev *dev[DB9_MAX_DEVICES]; 898c2ecf20Sopenharmony_ci struct timer_list timer; 908c2ecf20Sopenharmony_ci struct pardevice *pd; 918c2ecf20Sopenharmony_ci int mode; 928c2ecf20Sopenharmony_ci int used; 938c2ecf20Sopenharmony_ci int parportno; 948c2ecf20Sopenharmony_ci struct mutex mutex; 958c2ecf20Sopenharmony_ci char phys[DB9_MAX_DEVICES][32]; 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct db9 *db9_base[3]; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic const short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB }; 1018c2ecf20Sopenharmony_cistatic const short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE }; 1028c2ecf20Sopenharmony_cistatic const short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START }; 1038c2ecf20Sopenharmony_cistatic const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y }; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const struct db9_mode_data db9_modes[] = { 1068c2ecf20Sopenharmony_ci { NULL, NULL, 0, 0, 0, 0, 0 }, 1078c2ecf20Sopenharmony_ci { "Multisystem joystick", db9_multi_btn, 1, 1, 2, 1, 1 }, 1088c2ecf20Sopenharmony_ci { "Multisystem joystick (2 fire)", db9_multi_btn, 2, 1, 2, 1, 1 }, 1098c2ecf20Sopenharmony_ci { "Genesis pad", db9_genesis_btn, 4, 1, 2, 1, 1 }, 1108c2ecf20Sopenharmony_ci { NULL, NULL, 0, 0, 0, 0, 0 }, 1118c2ecf20Sopenharmony_ci { "Genesis 5 pad", db9_genesis_btn, 6, 1, 2, 1, 1 }, 1128c2ecf20Sopenharmony_ci { "Genesis 6 pad", db9_genesis_btn, 8, 1, 2, 1, 1 }, 1138c2ecf20Sopenharmony_ci { "Saturn pad", db9_cd32_btn, 9, 6, 7, 0, 1 }, 1148c2ecf20Sopenharmony_ci { "Multisystem (0.8.0.2) joystick", db9_multi_btn, 1, 1, 2, 1, 1 }, 1158c2ecf20Sopenharmony_ci { "Multisystem (0.8.0.2-dual) joystick", db9_multi_btn, 1, 2, 2, 1, 1 }, 1168c2ecf20Sopenharmony_ci { "Amiga CD-32 pad", db9_cd32_btn, 7, 1, 2, 1, 1 }, 1178c2ecf20Sopenharmony_ci { "Saturn dpp", db9_cd32_btn, 9, 6, 7, 0, 0 }, 1188c2ecf20Sopenharmony_ci { "Saturn dpp dual", db9_cd32_btn, 9, 12, 7, 0, 0 }, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * Saturn controllers 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci#define DB9_SATURN_DELAY 300 1258c2ecf20Sopenharmony_cistatic const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 }; 1268c2ecf20Sopenharmony_cistatic const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 }; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * db9_saturn_write_sub() writes 2 bit data. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_cistatic void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned char c; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci switch (type) { 1368c2ecf20Sopenharmony_ci case 1: /* DPP1 */ 1378c2ecf20Sopenharmony_ci c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data; 1388c2ecf20Sopenharmony_ci parport_write_data(port, c); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case 2: /* DPP2 */ 1418c2ecf20Sopenharmony_ci c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03; 1428c2ecf20Sopenharmony_ci parport_write_data(port, c); 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case 0: /* DB9 */ 1458c2ecf20Sopenharmony_ci c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered; 1468c2ecf20Sopenharmony_ci parport_write_control(port, c); 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * gc_saturn_read_sub() reads 4 bit data. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic unsigned char db9_saturn_read_sub(struct parport *port, int type) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned char data; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (type) { 1598c2ecf20Sopenharmony_ci /* DPP */ 1608c2ecf20Sopenharmony_ci data = parport_read_status(port) ^ 0x80; 1618c2ecf20Sopenharmony_ci return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0) 1628c2ecf20Sopenharmony_ci | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0); 1638c2ecf20Sopenharmony_ci } else { 1648c2ecf20Sopenharmony_ci /* DB9 */ 1658c2ecf20Sopenharmony_ci data = parport_read_data(port) & 0x0f; 1668c2ecf20Sopenharmony_ci return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0) 1678c2ecf20Sopenharmony_ci | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * db9_saturn_read_analog() sends clock and reads 8 bit data. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_cistatic unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci unsigned char data; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 0, powered, 0); 1798c2ecf20Sopenharmony_ci udelay(DB9_SATURN_DELAY); 1808c2ecf20Sopenharmony_ci data = db9_saturn_read_sub(port, type) << 4; 1818c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 2, powered, 0); 1828c2ecf20Sopenharmony_ci udelay(DB9_SATURN_DELAY); 1838c2ecf20Sopenharmony_ci data |= db9_saturn_read_sub(port, type); 1848c2ecf20Sopenharmony_ci return data; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * db9_saturn_read_packet() reads whole saturn packet at connector 1898c2ecf20Sopenharmony_ci * and returns device identifier code. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_cistatic unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci int i, j; 1948c2ecf20Sopenharmony_ci unsigned char tmp; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 3, powered, 0); 1978c2ecf20Sopenharmony_ci data[0] = db9_saturn_read_sub(port, type); 1988c2ecf20Sopenharmony_ci switch (data[0] & 0x0f) { 1998c2ecf20Sopenharmony_ci case 0xf: 2008c2ecf20Sopenharmony_ci /* 1111 no pad */ 2018c2ecf20Sopenharmony_ci return data[0] = 0xff; 2028c2ecf20Sopenharmony_ci case 0x4: case 0x4 | 0x8: 2038c2ecf20Sopenharmony_ci /* ?100 : digital controller */ 2048c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 0, powered, 1); 2058c2ecf20Sopenharmony_ci data[2] = db9_saturn_read_sub(port, type) << 4; 2068c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 2, powered, 1); 2078c2ecf20Sopenharmony_ci data[1] = db9_saturn_read_sub(port, type) << 4; 2088c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 1, powered, 1); 2098c2ecf20Sopenharmony_ci data[1] |= db9_saturn_read_sub(port, type); 2108c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 3, powered, 1); 2118c2ecf20Sopenharmony_ci /* data[2] |= db9_saturn_read_sub(port, type); */ 2128c2ecf20Sopenharmony_ci data[2] |= data[0]; 2138c2ecf20Sopenharmony_ci return data[0] = 0x02; 2148c2ecf20Sopenharmony_ci case 0x1: 2158c2ecf20Sopenharmony_ci /* 0001 : analog controller or multitap */ 2168c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 2, powered, 0); 2178c2ecf20Sopenharmony_ci udelay(DB9_SATURN_DELAY); 2188c2ecf20Sopenharmony_ci data[0] = db9_saturn_read_analog(port, type, powered); 2198c2ecf20Sopenharmony_ci if (data[0] != 0x41) { 2208c2ecf20Sopenharmony_ci /* read analog controller */ 2218c2ecf20Sopenharmony_ci for (i = 0; i < (data[0] & 0x0f); i++) 2228c2ecf20Sopenharmony_ci data[i + 1] = db9_saturn_read_analog(port, type, powered); 2238c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 3, powered, 0); 2248c2ecf20Sopenharmony_ci return data[0]; 2258c2ecf20Sopenharmony_ci } else { 2268c2ecf20Sopenharmony_ci /* read multitap */ 2278c2ecf20Sopenharmony_ci if (db9_saturn_read_analog(port, type, powered) != 0x60) 2288c2ecf20Sopenharmony_ci return data[0] = 0xff; 2298c2ecf20Sopenharmony_ci for (i = 0; i < 60; i += 10) { 2308c2ecf20Sopenharmony_ci data[i] = db9_saturn_read_analog(port, type, powered); 2318c2ecf20Sopenharmony_ci if (data[i] != 0xff) 2328c2ecf20Sopenharmony_ci /* read each pad */ 2338c2ecf20Sopenharmony_ci for (j = 0; j < (data[i] & 0x0f); j++) 2348c2ecf20Sopenharmony_ci data[i + j + 1] = db9_saturn_read_analog(port, type, powered); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 3, powered, 0); 2378c2ecf20Sopenharmony_ci return 0x41; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci case 0x0: 2408c2ecf20Sopenharmony_ci /* 0000 : mouse */ 2418c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 2, powered, 0); 2428c2ecf20Sopenharmony_ci udelay(DB9_SATURN_DELAY); 2438c2ecf20Sopenharmony_ci tmp = db9_saturn_read_analog(port, type, powered); 2448c2ecf20Sopenharmony_ci if (tmp == 0xff) { 2458c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 2468c2ecf20Sopenharmony_ci data[i + 1] = db9_saturn_read_analog(port, type, powered); 2478c2ecf20Sopenharmony_ci db9_saturn_write_sub(port, type, 3, powered, 0); 2488c2ecf20Sopenharmony_ci return data[0] = 0xe3; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci fallthrough; 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci return data[0]; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* 2578c2ecf20Sopenharmony_ci * db9_saturn_report() analyzes packet and reports. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *devs[], int n, int max_pads) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct input_dev *dev; 2628c2ecf20Sopenharmony_ci int tmp, i, j; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci tmp = (id == 0x41) ? 60 : 10; 2658c2ecf20Sopenharmony_ci for (j = 0; j < tmp && n < max_pads; j += 10, n++) { 2668c2ecf20Sopenharmony_ci dev = devs[n]; 2678c2ecf20Sopenharmony_ci switch (data[j]) { 2688c2ecf20Sopenharmony_ci case 0x16: /* multi controller (analog 4 axis) */ 2698c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[5], data[j + 6]); 2708c2ecf20Sopenharmony_ci fallthrough; 2718c2ecf20Sopenharmony_ci case 0x15: /* mission stick (analog 3 axis) */ 2728c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[3], data[j + 4]); 2738c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[4], data[j + 5]); 2748c2ecf20Sopenharmony_ci fallthrough; 2758c2ecf20Sopenharmony_ci case 0x13: /* racing controller (analog 1 axis) */ 2768c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[2], data[j + 3]); 2778c2ecf20Sopenharmony_ci fallthrough; 2788c2ecf20Sopenharmony_ci case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */ 2798c2ecf20Sopenharmony_ci case 0x02: /* digital pad (digital 2 axis + buttons) */ 2808c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64)); 2818c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16)); 2828c2ecf20Sopenharmony_ci for (i = 0; i < 9; i++) 2838c2ecf20Sopenharmony_ci input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case 0x19: /* mission stick x2 (analog 6 axis + buttons) */ 2868c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64)); 2878c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16)); 2888c2ecf20Sopenharmony_ci for (i = 0; i < 9; i++) 2898c2ecf20Sopenharmony_ci input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]); 2908c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[2], data[j + 3]); 2918c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[3], data[j + 4]); 2928c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[4], data[j + 5]); 2938c2ecf20Sopenharmony_ci /* 2948c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1)); 2958c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1)); 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[6], data[j + 7]); 2988c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[7], data[j + 8]); 2998c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[5], data[j + 9]); 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */ 3028c2ecf20Sopenharmony_ci input_report_key(dev, BTN_A, data[j + 3] & 0x80); 3038c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[2], data[j + 3] & 0x7f); 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */ 3068c2ecf20Sopenharmony_ci input_report_key(dev, BTN_START, data[j + 1] & 0x08); 3078c2ecf20Sopenharmony_ci input_report_key(dev, BTN_A, data[j + 1] & 0x04); 3088c2ecf20Sopenharmony_ci input_report_key(dev, BTN_C, data[j + 1] & 0x02); 3098c2ecf20Sopenharmony_ci input_report_key(dev, BTN_B, data[j + 1] & 0x01); 3108c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[2], data[j + 2] ^ 0x80); 3118c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */ 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci case 0xff: 3148c2ecf20Sopenharmony_ci default: /* no pad */ 3158c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[0], 0); 3168c2ecf20Sopenharmony_ci input_report_abs(dev, db9_abs[1], 0); 3178c2ecf20Sopenharmony_ci for (i = 0; i < 9; i++) 3188c2ecf20Sopenharmony_ci input_report_key(dev, db9_cd32_btn[i], 0); 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci return n; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int db9_saturn(int mode, struct parport *port, struct input_dev *devs[]) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci unsigned char id, data[60]; 3288c2ecf20Sopenharmony_ci int type, n, max_pads; 3298c2ecf20Sopenharmony_ci int tmp, i; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci switch (mode) { 3328c2ecf20Sopenharmony_ci case DB9_SATURN_PAD: 3338c2ecf20Sopenharmony_ci type = 0; 3348c2ecf20Sopenharmony_ci n = 1; 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case DB9_SATURN_DPP: 3378c2ecf20Sopenharmony_ci type = 1; 3388c2ecf20Sopenharmony_ci n = 1; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case DB9_SATURN_DPP_2: 3418c2ecf20Sopenharmony_ci type = 1; 3428c2ecf20Sopenharmony_ci n = 2; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci default: 3458c2ecf20Sopenharmony_ci return -1; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci max_pads = min(db9_modes[mode].n_pads, DB9_MAX_DEVICES); 3488c2ecf20Sopenharmony_ci for (tmp = 0, i = 0; i < n; i++) { 3498c2ecf20Sopenharmony_ci id = db9_saturn_read_packet(port, data, type + i, 1); 3508c2ecf20Sopenharmony_ci tmp = db9_saturn_report(id, data, devs, tmp, max_pads); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void db9_timer(struct timer_list *t) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct db9 *db9 = from_timer(db9, t, timer); 3588c2ecf20Sopenharmony_ci struct parport *port = db9->pd->port; 3598c2ecf20Sopenharmony_ci struct input_dev *dev = db9->dev[0]; 3608c2ecf20Sopenharmony_ci struct input_dev *dev2 = db9->dev[1]; 3618c2ecf20Sopenharmony_ci int data, i; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci switch (db9->mode) { 3648c2ecf20Sopenharmony_ci case DB9_MULTI_0802_2: 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci data = parport_read_data(port) >> 3; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci input_report_abs(dev2, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 3698c2ecf20Sopenharmony_ci input_report_abs(dev2, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 3708c2ecf20Sopenharmony_ci input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1); 3718c2ecf20Sopenharmony_ci fallthrough; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci case DB9_MULTI_0802: 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci data = parport_read_status(port) >> 3; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 3788c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 3798c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TRIGGER, data & DB9_FIRE1); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci case DB9_MULTI_STICK: 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci data = parport_read_data(port); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 3878c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 3888c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1); 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci case DB9_MULTI2_STICK: 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci data = parport_read_data(port); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 3968c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 3978c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1); 3988c2ecf20Sopenharmony_ci input_report_key(dev, BTN_THUMB, ~data & DB9_FIRE2); 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci case DB9_GENESIS_PAD: 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NOSELECT); 4048c2ecf20Sopenharmony_ci data = parport_read_data(port); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 4078c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 4088c2ecf20Sopenharmony_ci input_report_key(dev, BTN_B, ~data & DB9_FIRE1); 4098c2ecf20Sopenharmony_ci input_report_key(dev, BTN_C, ~data & DB9_FIRE2); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NORMAL); 4128c2ecf20Sopenharmony_ci data = parport_read_data(port); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci input_report_key(dev, BTN_A, ~data & DB9_FIRE1); 4158c2ecf20Sopenharmony_ci input_report_key(dev, BTN_START, ~data & DB9_FIRE2); 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci case DB9_GENESIS5_PAD: 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NOSELECT); 4218c2ecf20Sopenharmony_ci data = parport_read_data(port); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 4248c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 4258c2ecf20Sopenharmony_ci input_report_key(dev, BTN_B, ~data & DB9_FIRE1); 4268c2ecf20Sopenharmony_ci input_report_key(dev, BTN_C, ~data & DB9_FIRE2); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NORMAL); 4298c2ecf20Sopenharmony_ci data = parport_read_data(port); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci input_report_key(dev, BTN_A, ~data & DB9_FIRE1); 4328c2ecf20Sopenharmony_ci input_report_key(dev, BTN_X, ~data & DB9_FIRE2); 4338c2ecf20Sopenharmony_ci input_report_key(dev, BTN_Y, ~data & DB9_LEFT); 4348c2ecf20Sopenharmony_ci input_report_key(dev, BTN_START, ~data & DB9_RIGHT); 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci case DB9_GENESIS6_PAD: 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NOSELECT); /* 1 */ 4408c2ecf20Sopenharmony_ci udelay(DB9_GENESIS6_DELAY); 4418c2ecf20Sopenharmony_ci data = parport_read_data(port); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 4448c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 4458c2ecf20Sopenharmony_ci input_report_key(dev, BTN_B, ~data & DB9_FIRE1); 4468c2ecf20Sopenharmony_ci input_report_key(dev, BTN_C, ~data & DB9_FIRE2); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NORMAL); 4498c2ecf20Sopenharmony_ci udelay(DB9_GENESIS6_DELAY); 4508c2ecf20Sopenharmony_ci data = parport_read_data(port); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci input_report_key(dev, BTN_A, ~data & DB9_FIRE1); 4538c2ecf20Sopenharmony_ci input_report_key(dev, BTN_START, ~data & DB9_FIRE2); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NOSELECT); /* 2 */ 4568c2ecf20Sopenharmony_ci udelay(DB9_GENESIS6_DELAY); 4578c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NORMAL); 4588c2ecf20Sopenharmony_ci udelay(DB9_GENESIS6_DELAY); 4598c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NOSELECT); /* 3 */ 4608c2ecf20Sopenharmony_ci udelay(DB9_GENESIS6_DELAY); 4618c2ecf20Sopenharmony_ci data=parport_read_data(port); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci input_report_key(dev, BTN_X, ~data & DB9_LEFT); 4648c2ecf20Sopenharmony_ci input_report_key(dev, BTN_Y, ~data & DB9_DOWN); 4658c2ecf20Sopenharmony_ci input_report_key(dev, BTN_Z, ~data & DB9_UP); 4668c2ecf20Sopenharmony_ci input_report_key(dev, BTN_MODE, ~data & DB9_RIGHT); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NORMAL); 4698c2ecf20Sopenharmony_ci udelay(DB9_GENESIS6_DELAY); 4708c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NOSELECT); /* 4 */ 4718c2ecf20Sopenharmony_ci udelay(DB9_GENESIS6_DELAY); 4728c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NORMAL); 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci case DB9_SATURN_PAD: 4768c2ecf20Sopenharmony_ci case DB9_SATURN_DPP: 4778c2ecf20Sopenharmony_ci case DB9_SATURN_DPP_2: 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci db9_saturn(db9->mode, port, db9->dev); 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci case DB9_CD32_PAD: 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci data = parport_read_data(port); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1)); 4878c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1)); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci parport_write_control(port, 0x0a); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci for (i = 0; i < 7; i++) { 4928c2ecf20Sopenharmony_ci data = parport_read_data(port); 4938c2ecf20Sopenharmony_ci parport_write_control(port, 0x02); 4948c2ecf20Sopenharmony_ci parport_write_control(port, 0x0a); 4958c2ecf20Sopenharmony_ci input_report_key(dev, db9_cd32_btn[i], ~data & DB9_FIRE2); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci parport_write_control(port, 0x00); 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci input_sync(dev); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int db9_open(struct input_dev *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct db9 *db9 = input_get_drvdata(dev); 5108c2ecf20Sopenharmony_ci struct parport *port = db9->pd->port; 5118c2ecf20Sopenharmony_ci int err; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci err = mutex_lock_interruptible(&db9->mutex); 5148c2ecf20Sopenharmony_ci if (err) 5158c2ecf20Sopenharmony_ci return err; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!db9->used++) { 5188c2ecf20Sopenharmony_ci parport_claim(db9->pd); 5198c2ecf20Sopenharmony_ci parport_write_data(port, 0xff); 5208c2ecf20Sopenharmony_ci if (db9_modes[db9->mode].reverse) { 5218c2ecf20Sopenharmony_ci parport_data_reverse(port); 5228c2ecf20Sopenharmony_ci parport_write_control(port, DB9_NORMAL); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci mutex_unlock(&db9->mutex); 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic void db9_close(struct input_dev *dev) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct db9 *db9 = input_get_drvdata(dev); 5348c2ecf20Sopenharmony_ci struct parport *port = db9->pd->port; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci mutex_lock(&db9->mutex); 5378c2ecf20Sopenharmony_ci if (!--db9->used) { 5388c2ecf20Sopenharmony_ci del_timer_sync(&db9->timer); 5398c2ecf20Sopenharmony_ci parport_write_control(port, 0x00); 5408c2ecf20Sopenharmony_ci parport_data_forward(port); 5418c2ecf20Sopenharmony_ci parport_release(db9->pd); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci mutex_unlock(&db9->mutex); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic void db9_attach(struct parport *pp) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct db9 *db9; 5498c2ecf20Sopenharmony_ci const struct db9_mode_data *db9_mode; 5508c2ecf20Sopenharmony_ci struct pardevice *pd; 5518c2ecf20Sopenharmony_ci struct input_dev *input_dev; 5528c2ecf20Sopenharmony_ci int i, j, port_idx; 5538c2ecf20Sopenharmony_ci int mode; 5548c2ecf20Sopenharmony_ci struct pardev_cb db9_parport_cb; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for (port_idx = 0; port_idx < DB9_MAX_PORTS; port_idx++) { 5578c2ecf20Sopenharmony_ci if (db9_cfg[port_idx].nargs == 0 || 5588c2ecf20Sopenharmony_ci db9_cfg[port_idx].args[DB9_ARG_PARPORT] < 0) 5598c2ecf20Sopenharmony_ci continue; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (db9_cfg[port_idx].args[DB9_ARG_PARPORT] == pp->number) 5628c2ecf20Sopenharmony_ci break; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (port_idx == DB9_MAX_PORTS) { 5668c2ecf20Sopenharmony_ci pr_debug("Not using parport%d.\n", pp->number); 5678c2ecf20Sopenharmony_ci return; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci mode = db9_cfg[port_idx].args[DB9_ARG_MODE]; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (mode < 1 || mode >= DB9_MAX_PAD || !db9_modes[mode].n_buttons) { 5738c2ecf20Sopenharmony_ci printk(KERN_ERR "db9.c: Bad device type %d\n", mode); 5748c2ecf20Sopenharmony_ci return; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci db9_mode = &db9_modes[mode]; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (db9_mode->bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) { 5808c2ecf20Sopenharmony_ci printk(KERN_ERR "db9.c: specified parport is not bidirectional\n"); 5818c2ecf20Sopenharmony_ci return; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci memset(&db9_parport_cb, 0, sizeof(db9_parport_cb)); 5858c2ecf20Sopenharmony_ci db9_parport_cb.flags = PARPORT_FLAG_EXCL; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci pd = parport_register_dev_model(pp, "db9", &db9_parport_cb, port_idx); 5888c2ecf20Sopenharmony_ci if (!pd) { 5898c2ecf20Sopenharmony_ci printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n"); 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci db9 = kzalloc(sizeof(struct db9), GFP_KERNEL); 5948c2ecf20Sopenharmony_ci if (!db9) 5958c2ecf20Sopenharmony_ci goto err_unreg_pardev; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci mutex_init(&db9->mutex); 5988c2ecf20Sopenharmony_ci db9->pd = pd; 5998c2ecf20Sopenharmony_ci db9->mode = mode; 6008c2ecf20Sopenharmony_ci db9->parportno = pp->number; 6018c2ecf20Sopenharmony_ci timer_setup(&db9->timer, db9_timer, 0); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci for (i = 0; i < (min(db9_mode->n_pads, DB9_MAX_DEVICES)); i++) { 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci db9->dev[i] = input_dev = input_allocate_device(); 6068c2ecf20Sopenharmony_ci if (!input_dev) { 6078c2ecf20Sopenharmony_ci printk(KERN_ERR "db9.c: Not enough memory for input device\n"); 6088c2ecf20Sopenharmony_ci goto err_unreg_devs; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci snprintf(db9->phys[i], sizeof(db9->phys[i]), 6128c2ecf20Sopenharmony_ci "%s/input%d", db9->pd->port->name, i); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci input_dev->name = db9_mode->name; 6158c2ecf20Sopenharmony_ci input_dev->phys = db9->phys[i]; 6168c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_PARPORT; 6178c2ecf20Sopenharmony_ci input_dev->id.vendor = 0x0002; 6188c2ecf20Sopenharmony_ci input_dev->id.product = mode; 6198c2ecf20Sopenharmony_ci input_dev->id.version = 0x0100; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, db9); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci input_dev->open = db9_open; 6248c2ecf20Sopenharmony_ci input_dev->close = db9_close; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 6278c2ecf20Sopenharmony_ci for (j = 0; j < db9_mode->n_buttons; j++) 6288c2ecf20Sopenharmony_ci set_bit(db9_mode->buttons[j], input_dev->keybit); 6298c2ecf20Sopenharmony_ci for (j = 0; j < db9_mode->n_axis; j++) { 6308c2ecf20Sopenharmony_ci if (j < 2) 6318c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, db9_abs[j], -1, 1, 0, 0); 6328c2ecf20Sopenharmony_ci else 6338c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, db9_abs[j], 1, 255, 0, 0); 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (input_register_device(input_dev)) 6378c2ecf20Sopenharmony_ci goto err_free_dev; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci db9_base[port_idx] = db9; 6418c2ecf20Sopenharmony_ci return; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci err_free_dev: 6448c2ecf20Sopenharmony_ci input_free_device(db9->dev[i]); 6458c2ecf20Sopenharmony_ci err_unreg_devs: 6468c2ecf20Sopenharmony_ci while (--i >= 0) 6478c2ecf20Sopenharmony_ci input_unregister_device(db9->dev[i]); 6488c2ecf20Sopenharmony_ci kfree(db9); 6498c2ecf20Sopenharmony_ci err_unreg_pardev: 6508c2ecf20Sopenharmony_ci parport_unregister_device(pd); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic void db9_detach(struct parport *port) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci int i; 6568c2ecf20Sopenharmony_ci struct db9 *db9; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci for (i = 0; i < DB9_MAX_PORTS; i++) { 6598c2ecf20Sopenharmony_ci if (db9_base[i] && db9_base[i]->parportno == port->number) 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (i == DB9_MAX_PORTS) 6648c2ecf20Sopenharmony_ci return; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci db9 = db9_base[i]; 6678c2ecf20Sopenharmony_ci db9_base[i] = NULL; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci for (i = 0; i < min(db9_modes[db9->mode].n_pads, DB9_MAX_DEVICES); i++) 6708c2ecf20Sopenharmony_ci input_unregister_device(db9->dev[i]); 6718c2ecf20Sopenharmony_ci parport_unregister_device(db9->pd); 6728c2ecf20Sopenharmony_ci kfree(db9); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic struct parport_driver db9_parport_driver = { 6768c2ecf20Sopenharmony_ci .name = "db9", 6778c2ecf20Sopenharmony_ci .match_port = db9_attach, 6788c2ecf20Sopenharmony_ci .detach = db9_detach, 6798c2ecf20Sopenharmony_ci .devmodel = true, 6808c2ecf20Sopenharmony_ci}; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int __init db9_init(void) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci int i; 6858c2ecf20Sopenharmony_ci int have_dev = 0; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci for (i = 0; i < DB9_MAX_PORTS; i++) { 6888c2ecf20Sopenharmony_ci if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0) 6898c2ecf20Sopenharmony_ci continue; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (db9_cfg[i].nargs < 2) { 6928c2ecf20Sopenharmony_ci printk(KERN_ERR "db9.c: Device type must be specified.\n"); 6938c2ecf20Sopenharmony_ci return -EINVAL; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci have_dev = 1; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (!have_dev) 7008c2ecf20Sopenharmony_ci return -ENODEV; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci return parport_register_driver(&db9_parport_driver); 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic void __exit db9_exit(void) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci parport_unregister_driver(&db9_parport_driver); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cimodule_init(db9_init); 7118c2ecf20Sopenharmony_cimodule_exit(db9_exit); 712