1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2000 Justin Cormack 4 */ 5 6 /* 7 * Newton keyboard driver for Linux 8 */ 9 10 #include <linux/slab.h> 11 #include <linux/module.h> 12 #include <linux/input.h> 13 #include <linux/serio.h> 14 15 #define DRIVER_DESC "Newton keyboard driver" 16 17 MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>"); 18 MODULE_DESCRIPTION(DRIVER_DESC); 19 MODULE_LICENSE("GPL"); 20 21 #define NKBD_KEY 0x7f 22 #define NKBD_PRESS 0x80 23 24 static unsigned char nkbd_keycode[128] = { 25 KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X, 26 KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R, 27 KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5, 28 KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O, 29 KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE, 30 KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT, 31 KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA, 32 KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0, 33 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37 KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0 38 }; 39 40 struct nkbd { 41 unsigned char keycode[128]; 42 struct input_dev *dev; 43 struct serio *serio; 44 char phys[32]; 45 }; 46 47 static irqreturn_t nkbd_interrupt(struct serio *serio, 48 unsigned char data, unsigned int flags) 49 { 50 struct nkbd *nkbd = serio_get_drvdata(serio); 51 52 /* invalid scan codes are probably the init sequence, so we ignore them */ 53 if (nkbd->keycode[data & NKBD_KEY]) { 54 input_report_key(nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS); 55 input_sync(nkbd->dev); 56 } 57 58 else if (data == 0xe7) /* end of init sequence */ 59 printk(KERN_INFO "input: %s on %s\n", nkbd->dev->name, serio->phys); 60 return IRQ_HANDLED; 61 62 } 63 64 static int nkbd_connect(struct serio *serio, struct serio_driver *drv) 65 { 66 struct nkbd *nkbd; 67 struct input_dev *input_dev; 68 int err = -ENOMEM; 69 int i; 70 71 nkbd = kzalloc(sizeof(struct nkbd), GFP_KERNEL); 72 input_dev = input_allocate_device(); 73 if (!nkbd || !input_dev) 74 goto fail1; 75 76 nkbd->serio = serio; 77 nkbd->dev = input_dev; 78 snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys); 79 memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode)); 80 81 input_dev->name = "Newton Keyboard"; 82 input_dev->phys = nkbd->phys; 83 input_dev->id.bustype = BUS_RS232; 84 input_dev->id.vendor = SERIO_NEWTON; 85 input_dev->id.product = 0x0001; 86 input_dev->id.version = 0x0100; 87 input_dev->dev.parent = &serio->dev; 88 89 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 90 input_dev->keycode = nkbd->keycode; 91 input_dev->keycodesize = sizeof(unsigned char); 92 input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode); 93 for (i = 0; i < 128; i++) 94 set_bit(nkbd->keycode[i], input_dev->keybit); 95 clear_bit(0, input_dev->keybit); 96 97 serio_set_drvdata(serio, nkbd); 98 99 err = serio_open(serio, drv); 100 if (err) 101 goto fail2; 102 103 err = input_register_device(nkbd->dev); 104 if (err) 105 goto fail3; 106 107 return 0; 108 109 fail3: serio_close(serio); 110 fail2: serio_set_drvdata(serio, NULL); 111 fail1: input_free_device(input_dev); 112 kfree(nkbd); 113 return err; 114 } 115 116 static void nkbd_disconnect(struct serio *serio) 117 { 118 struct nkbd *nkbd = serio_get_drvdata(serio); 119 120 serio_close(serio); 121 serio_set_drvdata(serio, NULL); 122 input_unregister_device(nkbd->dev); 123 kfree(nkbd); 124 } 125 126 static const struct serio_device_id nkbd_serio_ids[] = { 127 { 128 .type = SERIO_RS232, 129 .proto = SERIO_NEWTON, 130 .id = SERIO_ANY, 131 .extra = SERIO_ANY, 132 }, 133 { 0 } 134 }; 135 136 MODULE_DEVICE_TABLE(serio, nkbd_serio_ids); 137 138 static struct serio_driver nkbd_drv = { 139 .driver = { 140 .name = "newtonkbd", 141 }, 142 .description = DRIVER_DESC, 143 .id_table = nkbd_serio_ids, 144 .interrupt = nkbd_interrupt, 145 .connect = nkbd_connect, 146 .disconnect = nkbd_disconnect, 147 }; 148 149 module_serio_driver(nkbd_drv); 150