Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sunkbd.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 1999-2001 Vojtech Pavlik
3  */
4 
5 /*
6  * Sun keyboard driver for Linux
7  */
8 
9 /*
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  *
24  * Should you need to contact me, the author, you can do so either by
25  * e-mail - mail your message to <[email protected]>, or by paper mail:
26  * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
27  */
28 
29 #include <linux/delay.h>
30 #include <linux/sched.h>
31 #include <linux/slab.h>
32 #include <linux/module.h>
33 #include <linux/interrupt.h>
34 #include <linux/init.h>
35 #include <linux/input.h>
36 #include <linux/serio.h>
37 #include <linux/workqueue.h>
38 
39 #define DRIVER_DESC "Sun keyboard driver"
40 
41 MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
43 MODULE_LICENSE("GPL");
44 
45 static unsigned char sunkbd_keycode[128] = {
46  0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
47  65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
48  4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
49  116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
50  26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
51  33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
52  104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
53  79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
54 };
55 
56 #define SUNKBD_CMD_RESET 0x1
57 #define SUNKBD_CMD_BELLON 0x2
58 #define SUNKBD_CMD_BELLOFF 0x3
59 #define SUNKBD_CMD_CLICK 0xa
60 #define SUNKBD_CMD_NOCLICK 0xb
61 #define SUNKBD_CMD_SETLED 0xe
62 #define SUNKBD_CMD_LAYOUT 0xf
63 
64 #define SUNKBD_RET_RESET 0xff
65 #define SUNKBD_RET_ALLUP 0x7f
66 #define SUNKBD_RET_LAYOUT 0xfe
67 
68 #define SUNKBD_LAYOUT_5_MASK 0x20
69 #define SUNKBD_RELEASE 0x80
70 #define SUNKBD_KEY 0x7f
71 
72 /*
73  * Per-keyboard data.
74  */
75 
76 struct sunkbd {
77  unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
78  struct input_dev *dev;
79  struct serio *serio;
80  struct work_struct tq;
82  char name[64];
83  char phys[32];
84  char type;
85  bool enabled;
86  volatile s8 reset;
87  volatile s8 layout;
88 };
89 
90 /*
91  * sunkbd_interrupt() is called by the low level driver when a character
92  * is received.
93  */
94 
95 static irqreturn_t sunkbd_interrupt(struct serio *serio,
96  unsigned char data, unsigned int flags)
97 {
98  struct sunkbd *sunkbd = serio_get_drvdata(serio);
99 
100  if (sunkbd->reset <= -1) {
101  /*
102  * If cp[i] is 0xff, sunkbd->reset will stay -1.
103  * The keyboard sends 0xff 0xff 0xID on powerup.
104  */
105  sunkbd->reset = data;
106  wake_up_interruptible(&sunkbd->wait);
107  goto out;
108  }
109 
110  if (sunkbd->layout == -1) {
111  sunkbd->layout = data;
112  wake_up_interruptible(&sunkbd->wait);
113  goto out;
114  }
115 
116  switch (data) {
117 
118  case SUNKBD_RET_RESET:
119  schedule_work(&sunkbd->tq);
120  sunkbd->reset = -1;
121  break;
122 
123  case SUNKBD_RET_LAYOUT:
124  sunkbd->layout = -1;
125  break;
126 
127  case SUNKBD_RET_ALLUP: /* All keys released */
128  break;
129 
130  default:
131  if (!sunkbd->enabled)
132  break;
133 
134  if (sunkbd->keycode[data & SUNKBD_KEY]) {
135  input_report_key(sunkbd->dev,
136  sunkbd->keycode[data & SUNKBD_KEY],
137  !(data & SUNKBD_RELEASE));
138  input_sync(sunkbd->dev);
139  } else {
141  "sunkbd.c: Unknown key (scancode %#x) %s.\n",
142  data & SUNKBD_KEY,
143  data & SUNKBD_RELEASE ? "released" : "pressed");
144  }
145  }
146 out:
147  return IRQ_HANDLED;
148 }
149 
150 /*
151  * sunkbd_event() handles events from the input module.
152  */
153 
154 static int sunkbd_event(struct input_dev *dev,
155  unsigned int type, unsigned int code, int value)
156 {
157  struct sunkbd *sunkbd = input_get_drvdata(dev);
158 
159  switch (type) {
160 
161  case EV_LED:
162 
163  serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
164  serio_write(sunkbd->serio,
165  (!!test_bit(LED_CAPSL, dev->led) << 3) |
166  (!!test_bit(LED_SCROLLL, dev->led) << 2) |
167  (!!test_bit(LED_COMPOSE, dev->led) << 1) |
168  !!test_bit(LED_NUML, dev->led));
169  return 0;
170 
171  case EV_SND:
172 
173  switch (code) {
174 
175  case SND_CLICK:
176  serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
177  return 0;
178 
179  case SND_BELL:
180  serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
181  return 0;
182  }
183 
184  break;
185  }
186 
187  return -1;
188 }
189 
190 /*
191  * sunkbd_initialize() checks for a Sun keyboard attached, and determines
192  * its type.
193  */
194 
195 static int sunkbd_initialize(struct sunkbd *sunkbd)
196 {
197  sunkbd->reset = -2;
198  serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
199  wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
200  if (sunkbd->reset < 0)
201  return -1;
202 
203  sunkbd->type = sunkbd->reset;
204 
205  if (sunkbd->type == 4) { /* Type 4 keyboard */
206  sunkbd->layout = -2;
207  serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
209  sunkbd->layout >= 0, HZ / 4);
210  if (sunkbd->layout < 0)
211  return -1;
212  if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
213  sunkbd->type = 5;
214  }
215 
216  return 0;
217 }
218 
219 /*
220  * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
221  * were in.
222  */
223 
224 static void sunkbd_reinit(struct work_struct *work)
225 {
226  struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
227 
228  wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
229 
230  serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
231  serio_write(sunkbd->serio,
232  (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) |
233  (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
234  (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
235  !!test_bit(LED_NUML, sunkbd->dev->led));
236  serio_write(sunkbd->serio,
237  SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
238  serio_write(sunkbd->serio,
239  SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
240 }
241 
242 static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
243 {
244  serio_pause_rx(sunkbd->serio);
245  sunkbd->enabled = enable;
246  serio_continue_rx(sunkbd->serio);
247 }
248 
249 /*
250  * sunkbd_connect() probes for a Sun keyboard and fills the necessary
251  * structures.
252  */
253 
254 static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
255 {
256  struct sunkbd *sunkbd;
257  struct input_dev *input_dev;
258  int err = -ENOMEM;
259  int i;
260 
261  sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
262  input_dev = input_allocate_device();
263  if (!sunkbd || !input_dev)
264  goto fail1;
265 
266  sunkbd->serio = serio;
267  sunkbd->dev = input_dev;
268  init_waitqueue_head(&sunkbd->wait);
269  INIT_WORK(&sunkbd->tq, sunkbd_reinit);
270  snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
271 
272  serio_set_drvdata(serio, sunkbd);
273 
274  err = serio_open(serio, drv);
275  if (err)
276  goto fail2;
277 
278  if (sunkbd_initialize(sunkbd) < 0) {
279  err = -ENODEV;
280  goto fail3;
281  }
282 
283  snprintf(sunkbd->name, sizeof(sunkbd->name),
284  "Sun Type %d keyboard", sunkbd->type);
285  memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
286 
287  input_dev->name = sunkbd->name;
288  input_dev->phys = sunkbd->phys;
289  input_dev->id.bustype = BUS_RS232;
290  input_dev->id.vendor = SERIO_SUNKBD;
291  input_dev->id.product = sunkbd->type;
292  input_dev->id.version = 0x0100;
293  input_dev->dev.parent = &serio->dev;
294 
295  input_set_drvdata(input_dev, sunkbd);
296 
297  input_dev->event = sunkbd_event;
298 
299  input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
301  input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
303  input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
304 
305  input_dev->keycode = sunkbd->keycode;
306  input_dev->keycodesize = sizeof(unsigned char);
307  input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
308  for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
309  __set_bit(sunkbd->keycode[i], input_dev->keybit);
310  __clear_bit(KEY_RESERVED, input_dev->keybit);
311 
312  sunkbd_enable(sunkbd, true);
313 
314  err = input_register_device(sunkbd->dev);
315  if (err)
316  goto fail4;
317 
318  return 0;
319 
320  fail4: sunkbd_enable(sunkbd, false);
321  fail3: serio_close(serio);
322  fail2: serio_set_drvdata(serio, NULL);
323  fail1: input_free_device(input_dev);
324  kfree(sunkbd);
325  return err;
326 }
327 
328 /*
329  * sunkbd_disconnect() unregisters and closes behind us.
330  */
331 
332 static void sunkbd_disconnect(struct serio *serio)
333 {
334  struct sunkbd *sunkbd = serio_get_drvdata(serio);
335 
336  sunkbd_enable(sunkbd, false);
337  input_unregister_device(sunkbd->dev);
338  serio_close(serio);
339  serio_set_drvdata(serio, NULL);
340  kfree(sunkbd);
341 }
342 
343 static struct serio_device_id sunkbd_serio_ids[] = {
344  {
345  .type = SERIO_RS232,
346  .proto = SERIO_SUNKBD,
347  .id = SERIO_ANY,
348  .extra = SERIO_ANY,
349  },
350  {
351  .type = SERIO_RS232,
352  .proto = SERIO_UNKNOWN, /* sunkbd does probe */
353  .id = SERIO_ANY,
354  .extra = SERIO_ANY,
355  },
356  { 0 }
357 };
358 
359 MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
360 
361 static struct serio_driver sunkbd_drv = {
362  .driver = {
363  .name = "sunkbd",
364  },
365  .description = DRIVER_DESC,
366  .id_table = sunkbd_serio_ids,
367  .interrupt = sunkbd_interrupt,
368  .connect = sunkbd_connect,
369  .disconnect = sunkbd_disconnect,
370 };
371 
372 module_serio_driver(sunkbd_drv);