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