11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Copyright (c) 2000 Justin Cormack
41da177e4SLinus Torvalds */
51da177e4SLinus Torvalds
61da177e4SLinus Torvalds /*
71da177e4SLinus Torvalds * Newton keyboard driver for Linux
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/slab.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/input.h>
131da177e4SLinus Torvalds #include <linux/serio.h>
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds #define DRIVER_DESC "Newton keyboard driver"
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
181da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
191da177e4SLinus Torvalds MODULE_LICENSE("GPL");
201da177e4SLinus Torvalds
211da177e4SLinus Torvalds #define NKBD_KEY 0x7f
221da177e4SLinus Torvalds #define NKBD_PRESS 0x80
231da177e4SLinus Torvalds
241da177e4SLinus Torvalds static unsigned char nkbd_keycode[128] = {
251da177e4SLinus Torvalds KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
261da177e4SLinus Torvalds KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
271da177e4SLinus Torvalds KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
281da177e4SLinus Torvalds KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O,
291da177e4SLinus Torvalds KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE,
301da177e4SLinus Torvalds KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT,
311da177e4SLinus Torvalds KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA,
321da177e4SLinus Torvalds KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0,
331da177e4SLinus Torvalds 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
341da177e4SLinus Torvalds 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351da177e4SLinus Torvalds 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
361da177e4SLinus Torvalds 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371da177e4SLinus Torvalds KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
381da177e4SLinus Torvalds };
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds struct nkbd {
411da177e4SLinus Torvalds unsigned char keycode[128];
423c42f0c3SDmitry Torokhov struct input_dev *dev;
431da177e4SLinus Torvalds struct serio *serio;
441da177e4SLinus Torvalds char phys[32];
451da177e4SLinus Torvalds };
461da177e4SLinus Torvalds
nkbd_interrupt(struct serio * serio,unsigned char data,unsigned int flags)471da177e4SLinus Torvalds static irqreturn_t nkbd_interrupt(struct serio *serio,
487d12e780SDavid Howells unsigned char data, unsigned int flags)
491da177e4SLinus Torvalds {
501da177e4SLinus Torvalds struct nkbd *nkbd = serio_get_drvdata(serio);
511da177e4SLinus Torvalds
521da177e4SLinus Torvalds /* invalid scan codes are probably the init sequence, so we ignore them */
531da177e4SLinus Torvalds if (nkbd->keycode[data & NKBD_KEY]) {
543c42f0c3SDmitry Torokhov input_report_key(nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS);
553c42f0c3SDmitry Torokhov input_sync(nkbd->dev);
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds
581da177e4SLinus Torvalds else if (data == 0xe7) /* end of init sequence */
593c42f0c3SDmitry Torokhov printk(KERN_INFO "input: %s on %s\n", nkbd->dev->name, serio->phys);
601da177e4SLinus Torvalds return IRQ_HANDLED;
611da177e4SLinus Torvalds
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds
nkbd_connect(struct serio * serio,struct serio_driver * drv)641da177e4SLinus Torvalds static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds struct nkbd *nkbd;
673c42f0c3SDmitry Torokhov struct input_dev *input_dev;
683c42f0c3SDmitry Torokhov int err = -ENOMEM;
691da177e4SLinus Torvalds int i;
701da177e4SLinus Torvalds
71*bb8706a4SErick Archer nkbd = kzalloc(sizeof(*nkbd), GFP_KERNEL);
723c42f0c3SDmitry Torokhov input_dev = input_allocate_device();
733c42f0c3SDmitry Torokhov if (!nkbd || !input_dev)
742b03b60eSDmitry Torokhov goto fail1;
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds nkbd->serio = serio;
773c42f0c3SDmitry Torokhov nkbd->dev = input_dev;
78ea08c6faSDmitry Torokhov snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys);
793c42f0c3SDmitry Torokhov memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
801da177e4SLinus Torvalds
813c42f0c3SDmitry Torokhov input_dev->name = "Newton Keyboard";
823c42f0c3SDmitry Torokhov input_dev->phys = nkbd->phys;
833c42f0c3SDmitry Torokhov input_dev->id.bustype = BUS_RS232;
843c42f0c3SDmitry Torokhov input_dev->id.vendor = SERIO_NEWTON;
853c42f0c3SDmitry Torokhov input_dev->id.product = 0x0001;
863c42f0c3SDmitry Torokhov input_dev->id.version = 0x0100;
87469ba4dfSDmitry Torokhov input_dev->dev.parent = &serio->dev;
883c42f0c3SDmitry Torokhov
897b19ada2SJiri Slaby input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
903c42f0c3SDmitry Torokhov input_dev->keycode = nkbd->keycode;
913c42f0c3SDmitry Torokhov input_dev->keycodesize = sizeof(unsigned char);
923c42f0c3SDmitry Torokhov input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode);
933c42f0c3SDmitry Torokhov for (i = 0; i < 128; i++)
943c42f0c3SDmitry Torokhov set_bit(nkbd->keycode[i], input_dev->keybit);
953c42f0c3SDmitry Torokhov clear_bit(0, input_dev->keybit);
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds serio_set_drvdata(serio, nkbd);
981da177e4SLinus Torvalds
991da177e4SLinus Torvalds err = serio_open(serio, drv);
1003c42f0c3SDmitry Torokhov if (err)
1012b03b60eSDmitry Torokhov goto fail2;
1023c42f0c3SDmitry Torokhov
1032b03b60eSDmitry Torokhov err = input_register_device(nkbd->dev);
1042b03b60eSDmitry Torokhov if (err)
1052b03b60eSDmitry Torokhov goto fail3;
1062b03b60eSDmitry Torokhov
1073c42f0c3SDmitry Torokhov return 0;
1083c42f0c3SDmitry Torokhov
1092b03b60eSDmitry Torokhov fail3: serio_close(serio);
1102b03b60eSDmitry Torokhov fail2: serio_set_drvdata(serio, NULL);
1112b03b60eSDmitry Torokhov fail1: input_free_device(input_dev);
1121da177e4SLinus Torvalds kfree(nkbd);
1131da177e4SLinus Torvalds return err;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds
nkbd_disconnect(struct serio * serio)1161da177e4SLinus Torvalds static void nkbd_disconnect(struct serio *serio)
1171da177e4SLinus Torvalds {
1181da177e4SLinus Torvalds struct nkbd *nkbd = serio_get_drvdata(serio);
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds serio_close(serio);
1211da177e4SLinus Torvalds serio_set_drvdata(serio, NULL);
1223c42f0c3SDmitry Torokhov input_unregister_device(nkbd->dev);
1231da177e4SLinus Torvalds kfree(nkbd);
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds
1269780930aSArvind Yadav static const struct serio_device_id nkbd_serio_ids[] = {
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds .type = SERIO_RS232,
1291da177e4SLinus Torvalds .proto = SERIO_NEWTON,
1301da177e4SLinus Torvalds .id = SERIO_ANY,
1311da177e4SLinus Torvalds .extra = SERIO_ANY,
1321da177e4SLinus Torvalds },
1331da177e4SLinus Torvalds { 0 }
1341da177e4SLinus Torvalds };
1351da177e4SLinus Torvalds
1361da177e4SLinus Torvalds MODULE_DEVICE_TABLE(serio, nkbd_serio_ids);
1371da177e4SLinus Torvalds
1381da177e4SLinus Torvalds static struct serio_driver nkbd_drv = {
1391da177e4SLinus Torvalds .driver = {
1401da177e4SLinus Torvalds .name = "newtonkbd",
1411da177e4SLinus Torvalds },
1421da177e4SLinus Torvalds .description = DRIVER_DESC,
1431da177e4SLinus Torvalds .id_table = nkbd_serio_ids,
1441da177e4SLinus Torvalds .interrupt = nkbd_interrupt,
1451da177e4SLinus Torvalds .connect = nkbd_connect,
1461da177e4SLinus Torvalds .disconnect = nkbd_disconnect,
1471da177e4SLinus Torvalds };
1481da177e4SLinus Torvalds
14965ac9f7aSAxel Lin module_serio_driver(nkbd_drv);
150