1*f35d0616SMarek Vasut /* 2*f35d0616SMarek Vasut * Stowaway keyboard driver for Linux 3*f35d0616SMarek Vasut */ 4*f35d0616SMarek Vasut 5*f35d0616SMarek Vasut /* 6*f35d0616SMarek Vasut * Copyright (c) 2006 Marek Vasut 7*f35d0616SMarek Vasut * 8*f35d0616SMarek Vasut * Based on Newton keyboard driver for Linux 9*f35d0616SMarek Vasut * by Justin Cormack 10*f35d0616SMarek Vasut */ 11*f35d0616SMarek Vasut 12*f35d0616SMarek Vasut /* 13*f35d0616SMarek Vasut * This program is free software; you can redistribute it and/or modify 14*f35d0616SMarek Vasut * it under the terms of the GNU General Public License as published by 15*f35d0616SMarek Vasut * the Free Software Foundation; either version 2 of the License, or 16*f35d0616SMarek Vasut * (at your option) any later version. 17*f35d0616SMarek Vasut * 18*f35d0616SMarek Vasut * This program is distributed in the hope that it will be useful, 19*f35d0616SMarek Vasut * but WITHOUT ANY WARRANTY; without even the implied warranty of 20*f35d0616SMarek Vasut * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21*f35d0616SMarek Vasut * GNU General Public License for more details. 22*f35d0616SMarek Vasut * 23*f35d0616SMarek Vasut * You should have received a copy of the GNU General Public License 24*f35d0616SMarek Vasut * along with this program; if not, write to the Free Software 25*f35d0616SMarek Vasut * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26*f35d0616SMarek Vasut * 27*f35d0616SMarek Vasut * Should you need to contact me, the author, you can do so either by 28*f35d0616SMarek Vasut * e-mail - mail your message to <marek.vasut@gmail.com>, or by paper mail: 29*f35d0616SMarek Vasut * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic 30*f35d0616SMarek Vasut */ 31*f35d0616SMarek Vasut 32*f35d0616SMarek Vasut #include <linux/slab.h> 33*f35d0616SMarek Vasut #include <linux/module.h> 34*f35d0616SMarek Vasut #include <linux/input.h> 35*f35d0616SMarek Vasut #include <linux/init.h> 36*f35d0616SMarek Vasut #include <linux/serio.h> 37*f35d0616SMarek Vasut 38*f35d0616SMarek Vasut #define DRIVER_DESC "Stowaway keyboard driver" 39*f35d0616SMarek Vasut 40*f35d0616SMarek Vasut MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 41*f35d0616SMarek Vasut MODULE_DESCRIPTION(DRIVER_DESC); 42*f35d0616SMarek Vasut MODULE_LICENSE("GPL"); 43*f35d0616SMarek Vasut 44*f35d0616SMarek Vasut #define SKBD_KEY_MASK 0x7f 45*f35d0616SMarek Vasut #define SKBD_RELEASE 0x80 46*f35d0616SMarek Vasut 47*f35d0616SMarek Vasut static unsigned char skbd_keycode[128] = { 48*f35d0616SMarek Vasut KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7, 49*f35d0616SMarek Vasut 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE, 50*f35d0616SMarek Vasut KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE, 51*f35d0616SMarek Vasut KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 52*f35d0616SMarek Vasut 0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0, 53*f35d0616SMarek Vasut 0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N, 54*f35d0616SMarek Vasut KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC, 55*f35d0616SMarek Vasut KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P, 56*f35d0616SMarek Vasut KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, 57*f35d0616SMarek Vasut KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT, 58*f35d0616SMarek Vasut KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 59*f35d0616SMarek Vasut KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 60*f35d0616SMarek Vasut 0, 0, 0, 0, 0, 0, 0, 0, 61*f35d0616SMarek Vasut 0, 0, 0, 0, 0, 0, 0, 0, 62*f35d0616SMarek Vasut 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, 63*f35d0616SMarek Vasut KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0 64*f35d0616SMarek Vasut }; 65*f35d0616SMarek Vasut 66*f35d0616SMarek Vasut struct skbd { 67*f35d0616SMarek Vasut unsigned char keycode[128]; 68*f35d0616SMarek Vasut struct input_dev *dev; 69*f35d0616SMarek Vasut struct serio *serio; 70*f35d0616SMarek Vasut char phys[32]; 71*f35d0616SMarek Vasut }; 72*f35d0616SMarek Vasut 73*f35d0616SMarek Vasut static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data, 74*f35d0616SMarek Vasut unsigned int flags, struct pt_regs *regs) 75*f35d0616SMarek Vasut { 76*f35d0616SMarek Vasut struct skbd *skbd = serio_get_drvdata(serio); 77*f35d0616SMarek Vasut struct input_dev *dev = skbd->dev; 78*f35d0616SMarek Vasut 79*f35d0616SMarek Vasut if (skbd->keycode[data & SKBD_KEY_MASK]) { 80*f35d0616SMarek Vasut input_regs(dev, regs); 81*f35d0616SMarek Vasut input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK], 82*f35d0616SMarek Vasut !(data & SKBD_RELEASE)); 83*f35d0616SMarek Vasut input_sync(dev); 84*f35d0616SMarek Vasut } 85*f35d0616SMarek Vasut 86*f35d0616SMarek Vasut return IRQ_HANDLED; 87*f35d0616SMarek Vasut } 88*f35d0616SMarek Vasut 89*f35d0616SMarek Vasut static int skbd_connect(struct serio *serio, struct serio_driver *drv) 90*f35d0616SMarek Vasut { 91*f35d0616SMarek Vasut struct skbd *skbd; 92*f35d0616SMarek Vasut struct input_dev *input_dev; 93*f35d0616SMarek Vasut int err = -ENOMEM; 94*f35d0616SMarek Vasut int i; 95*f35d0616SMarek Vasut 96*f35d0616SMarek Vasut skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL); 97*f35d0616SMarek Vasut input_dev = input_allocate_device(); 98*f35d0616SMarek Vasut if (!skbd || !input_dev) 99*f35d0616SMarek Vasut goto fail1; 100*f35d0616SMarek Vasut 101*f35d0616SMarek Vasut skbd->serio = serio; 102*f35d0616SMarek Vasut skbd->dev = input_dev; 103*f35d0616SMarek Vasut snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys); 104*f35d0616SMarek Vasut memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode)); 105*f35d0616SMarek Vasut 106*f35d0616SMarek Vasut input_dev->name = "Stowaway Keyboard"; 107*f35d0616SMarek Vasut input_dev->phys = skbd->phys; 108*f35d0616SMarek Vasut input_dev->id.bustype = BUS_RS232; 109*f35d0616SMarek Vasut input_dev->id.vendor = SERIO_STOWAWAY; 110*f35d0616SMarek Vasut input_dev->id.product = 0x0001; 111*f35d0616SMarek Vasut input_dev->id.version = 0x0100; 112*f35d0616SMarek Vasut input_dev->cdev.dev = &serio->dev; 113*f35d0616SMarek Vasut input_dev->private = skbd; 114*f35d0616SMarek Vasut 115*f35d0616SMarek Vasut input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); 116*f35d0616SMarek Vasut input_dev->keycode = skbd->keycode; 117*f35d0616SMarek Vasut input_dev->keycodesize = sizeof(unsigned char); 118*f35d0616SMarek Vasut input_dev->keycodemax = ARRAY_SIZE(skbd_keycode); 119*f35d0616SMarek Vasut for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++) 120*f35d0616SMarek Vasut set_bit(skbd_keycode[i], input_dev->keybit); 121*f35d0616SMarek Vasut clear_bit(0, input_dev->keybit); 122*f35d0616SMarek Vasut 123*f35d0616SMarek Vasut serio_set_drvdata(serio, skbd); 124*f35d0616SMarek Vasut 125*f35d0616SMarek Vasut err = serio_open(serio, drv); 126*f35d0616SMarek Vasut if (err) 127*f35d0616SMarek Vasut goto fail2; 128*f35d0616SMarek Vasut 129*f35d0616SMarek Vasut err = input_register_device(skbd->dev); 130*f35d0616SMarek Vasut if (err) 131*f35d0616SMarek Vasut goto fail3; 132*f35d0616SMarek Vasut 133*f35d0616SMarek Vasut return 0; 134*f35d0616SMarek Vasut 135*f35d0616SMarek Vasut fail3: serio_close(serio); 136*f35d0616SMarek Vasut fail2: serio_set_drvdata(serio, NULL); 137*f35d0616SMarek Vasut fail1: input_free_device(input_dev); 138*f35d0616SMarek Vasut kfree(skbd); 139*f35d0616SMarek Vasut return err; 140*f35d0616SMarek Vasut } 141*f35d0616SMarek Vasut 142*f35d0616SMarek Vasut static void skbd_disconnect(struct serio *serio) 143*f35d0616SMarek Vasut { 144*f35d0616SMarek Vasut struct skbd *skbd = serio_get_drvdata(serio); 145*f35d0616SMarek Vasut 146*f35d0616SMarek Vasut serio_close(serio); 147*f35d0616SMarek Vasut serio_set_drvdata(serio, NULL); 148*f35d0616SMarek Vasut input_unregister_device(skbd->dev); 149*f35d0616SMarek Vasut kfree(skbd); 150*f35d0616SMarek Vasut } 151*f35d0616SMarek Vasut 152*f35d0616SMarek Vasut static struct serio_device_id skbd_serio_ids[] = { 153*f35d0616SMarek Vasut { 154*f35d0616SMarek Vasut .type = SERIO_RS232, 155*f35d0616SMarek Vasut .proto = SERIO_STOWAWAY, 156*f35d0616SMarek Vasut .id = SERIO_ANY, 157*f35d0616SMarek Vasut .extra = SERIO_ANY, 158*f35d0616SMarek Vasut }, 159*f35d0616SMarek Vasut { 0 } 160*f35d0616SMarek Vasut }; 161*f35d0616SMarek Vasut 162*f35d0616SMarek Vasut MODULE_DEVICE_TABLE(serio, skbd_serio_ids); 163*f35d0616SMarek Vasut 164*f35d0616SMarek Vasut static struct serio_driver skbd_drv = { 165*f35d0616SMarek Vasut .driver = { 166*f35d0616SMarek Vasut .name = "stowaway", 167*f35d0616SMarek Vasut }, 168*f35d0616SMarek Vasut .description = DRIVER_DESC, 169*f35d0616SMarek Vasut .id_table = skbd_serio_ids, 170*f35d0616SMarek Vasut .interrupt = skbd_interrupt, 171*f35d0616SMarek Vasut .connect = skbd_connect, 172*f35d0616SMarek Vasut .disconnect = skbd_disconnect, 173*f35d0616SMarek Vasut }; 174*f35d0616SMarek Vasut 175*f35d0616SMarek Vasut static int __init skbd_init(void) 176*f35d0616SMarek Vasut { 177*f35d0616SMarek Vasut serio_register_driver(&skbd_drv); 178*f35d0616SMarek Vasut return 0; 179*f35d0616SMarek Vasut } 180*f35d0616SMarek Vasut 181*f35d0616SMarek Vasut static void __exit skbd_exit(void) 182*f35d0616SMarek Vasut { 183*f35d0616SMarek Vasut serio_unregister_driver(&skbd_drv); 184*f35d0616SMarek Vasut } 185*f35d0616SMarek Vasut 186*f35d0616SMarek Vasut module_init(skbd_init); 187*f35d0616SMarek Vasut module_exit(skbd_exit); 188