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