11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Parallel port to Keyboard port adapter driver for Linux 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 1999-2004 Vojtech Pavlik 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds /* 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify it 91da177e4SLinus Torvalds * under the terms of the GNU General Public License version 2 as published by 101da177e4SLinus Torvalds * the Free Software Foundation. 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds 131da177e4SLinus Torvalds /* 141da177e4SLinus Torvalds * To connect an AT or XT keyboard to the parallel port, a fairly simple adapter 151da177e4SLinus Torvalds * can be made: 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * Parallel port Keyboard port 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * +5V --------------------- +5V (4) 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds * ______ 221da177e4SLinus Torvalds * +5V -------|______|--. 231da177e4SLinus Torvalds * | 241da177e4SLinus Torvalds * ACK (10) ------------| 251da177e4SLinus Torvalds * |--- KBD CLOCK (5) 261da177e4SLinus Torvalds * STROBE (1) ---|<|----' 271da177e4SLinus Torvalds * 281da177e4SLinus Torvalds * ______ 291da177e4SLinus Torvalds * +5V -------|______|--. 301da177e4SLinus Torvalds * | 311da177e4SLinus Torvalds * BUSY (11) -----------| 321da177e4SLinus Torvalds * |--- KBD DATA (1) 331da177e4SLinus Torvalds * AUTOFD (14) --|<|----' 341da177e4SLinus Torvalds * 351da177e4SLinus Torvalds * GND (18-25) ------------- GND (3) 361da177e4SLinus Torvalds * 371da177e4SLinus Torvalds * The diodes can be fairly any type, and the resistors should be somewhere 381da177e4SLinus Torvalds * around 5 kOhm, but the adapter will likely work without the resistors, 391da177e4SLinus Torvalds * too. 401da177e4SLinus Torvalds * 411da177e4SLinus Torvalds * The +5V source can be taken either from USB, from mouse or keyboard ports, 421da177e4SLinus Torvalds * or from a joystick port. Unfortunately, the parallel port of a PC doesn't 431da177e4SLinus Torvalds * have a +5V pin, and feeding the keyboard from signal pins is out of question 441da177e4SLinus Torvalds * with 300 mA power reqirement of a typical AT keyboard. 451da177e4SLinus Torvalds */ 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds #include <linux/module.h> 481da177e4SLinus Torvalds #include <linux/parport.h> 495a0e3ad6STejun Heo #include <linux/slab.h> 501da177e4SLinus Torvalds #include <linux/init.h> 511da177e4SLinus Torvalds #include <linux/serio.h> 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 541da177e4SLinus Torvalds MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver"); 551da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds static unsigned int parkbd_pp_no; 581da177e4SLinus Torvalds module_param_named(port, parkbd_pp_no, int, 0); 591da177e4SLinus Torvalds MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)"); 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds static unsigned int parkbd_mode = SERIO_8042; 621da177e4SLinus Torvalds module_param_named(mode, parkbd_mode, uint, 0); 631da177e4SLinus Torvalds MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)"); 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds #define PARKBD_CLOCK 0x01 /* Strobe & Ack */ 661da177e4SLinus Torvalds #define PARKBD_DATA 0x02 /* AutoFd & Busy */ 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds static int parkbd_buffer; 691da177e4SLinus Torvalds static int parkbd_counter; 701da177e4SLinus Torvalds static unsigned long parkbd_last; 711da177e4SLinus Torvalds static int parkbd_writing; 721da177e4SLinus Torvalds static unsigned long parkbd_start; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds static struct pardevice *parkbd_dev; 751da177e4SLinus Torvalds static struct serio *parkbd_port; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds static int parkbd_readlines(void) 781da177e4SLinus Torvalds { 791da177e4SLinus Torvalds return (parport_read_status(parkbd_dev->port) >> 6) ^ 2; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds static void parkbd_writelines(int data) 831da177e4SLinus Torvalds { 841da177e4SLinus Torvalds parport_write_control(parkbd_dev->port, (~data & 3) | 0x10); 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds static int parkbd_write(struct serio *port, unsigned char c) 881da177e4SLinus Torvalds { 891da177e4SLinus Torvalds unsigned char p; 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds if (!parkbd_mode) return -1; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds p = c ^ (c >> 4); 941da177e4SLinus Torvalds p = p ^ (p >> 2); 951da177e4SLinus Torvalds p = p ^ (p >> 1); 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds parkbd_counter = 0; 981da177e4SLinus Torvalds parkbd_writing = 1; 991da177e4SLinus Torvalds parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds parkbd_writelines(2); 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds return 0; 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 1065712cb3dSJeff Garzik static void parkbd_interrupt(void *dev_id) 1071da177e4SLinus Torvalds { 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds if (parkbd_writing) { 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) { 1121da177e4SLinus Torvalds parkbd_counter = 0; 1131da177e4SLinus Torvalds parkbd_buffer = 0; 1141da177e4SLinus Torvalds parkbd_writing = 0; 1151da177e4SLinus Torvalds parkbd_writelines(3); 1161da177e4SLinus Torvalds return; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2); 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds if (parkbd_counter == 11) { 1221da177e4SLinus Torvalds parkbd_counter = 0; 1231da177e4SLinus Torvalds parkbd_buffer = 0; 1241da177e4SLinus Torvalds parkbd_writing = 0; 1251da177e4SLinus Torvalds parkbd_writelines(3); 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds } else { 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) { 1311da177e4SLinus Torvalds parkbd_counter = 0; 1321da177e4SLinus Torvalds parkbd_buffer = 0; 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++; 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds if (parkbd_counter == parkbd_mode + 10) 1387d12e780SDavid Howells serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0); 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds parkbd_last = jiffies; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 14433ca8ab9SSudip Mukherjee static int parkbd_getport(struct parport *pp) 1451da177e4SLinus Torvalds { 14633ca8ab9SSudip Mukherjee struct pardev_cb parkbd_parport_cb; 1471da177e4SLinus Torvalds 148*0c6da073SSudip Mukherjee memset(&parkbd_parport_cb, 0, sizeof(parkbd_parport_cb)); 14933ca8ab9SSudip Mukherjee parkbd_parport_cb.irq_func = parkbd_interrupt; 15033ca8ab9SSudip Mukherjee parkbd_parport_cb.flags = PARPORT_FLAG_EXCL; 1511da177e4SLinus Torvalds 15233ca8ab9SSudip Mukherjee parkbd_dev = parport_register_dev_model(pp, "parkbd", 15333ca8ab9SSudip Mukherjee &parkbd_parport_cb, 0); 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds if (!parkbd_dev) 1561da177e4SLinus Torvalds return -ENODEV; 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds if (parport_claim(parkbd_dev)) { 1591da177e4SLinus Torvalds parport_unregister_device(parkbd_dev); 1601da177e4SLinus Torvalds return -EBUSY; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds parkbd_start = jiffies; 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds return 0; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1685e0baca8SGeert Uytterhoeven static struct serio *parkbd_allocate_serio(void) 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds struct serio *serio; 1711da177e4SLinus Torvalds 172b39787a9SEric Sesterhenn serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 1731da177e4SLinus Torvalds if (serio) { 1741da177e4SLinus Torvalds serio->id.type = parkbd_mode; 1751da177e4SLinus Torvalds serio->write = parkbd_write, 1761da177e4SLinus Torvalds strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name)); 1771da177e4SLinus Torvalds snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name); 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds return serio; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 18333ca8ab9SSudip Mukherjee static void parkbd_attach(struct parport *pp) 1841da177e4SLinus Torvalds { 18533ca8ab9SSudip Mukherjee if (pp->number != parkbd_pp_no) { 18633ca8ab9SSudip Mukherjee pr_debug("Not using parport%d.\n", pp->number); 18733ca8ab9SSudip Mukherjee return; 18833ca8ab9SSudip Mukherjee } 1891da177e4SLinus Torvalds 19033ca8ab9SSudip Mukherjee if (parkbd_getport(pp)) 19133ca8ab9SSudip Mukherjee return; 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds parkbd_port = parkbd_allocate_serio(); 1941da177e4SLinus Torvalds if (!parkbd_port) { 1951da177e4SLinus Torvalds parport_release(parkbd_dev); 1961a5e2519SSudip Mukherjee parport_unregister_device(parkbd_dev); 19733ca8ab9SSudip Mukherjee return; 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds parkbd_writelines(3); 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds serio_register_port(parkbd_port); 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds printk(KERN_INFO "serio: PARKBD %s adapter on %s\n", 2051da177e4SLinus Torvalds parkbd_mode ? "AT" : "XT", parkbd_dev->port->name); 2061da177e4SLinus Torvalds 20733ca8ab9SSudip Mukherjee return; 20833ca8ab9SSudip Mukherjee } 20933ca8ab9SSudip Mukherjee 21033ca8ab9SSudip Mukherjee static void parkbd_detach(struct parport *port) 21133ca8ab9SSudip Mukherjee { 21233ca8ab9SSudip Mukherjee if (!parkbd_port || port->number != parkbd_pp_no) 21333ca8ab9SSudip Mukherjee return; 21433ca8ab9SSudip Mukherjee 21533ca8ab9SSudip Mukherjee parport_release(parkbd_dev); 21633ca8ab9SSudip Mukherjee serio_unregister_port(parkbd_port); 21733ca8ab9SSudip Mukherjee parport_unregister_device(parkbd_dev); 21833ca8ab9SSudip Mukherjee parkbd_port = NULL; 21933ca8ab9SSudip Mukherjee } 22033ca8ab9SSudip Mukherjee 22133ca8ab9SSudip Mukherjee static struct parport_driver parkbd_parport_driver = { 22233ca8ab9SSudip Mukherjee .name = "parkbd", 22333ca8ab9SSudip Mukherjee .match_port = parkbd_attach, 22433ca8ab9SSudip Mukherjee .detach = parkbd_detach, 22533ca8ab9SSudip Mukherjee .devmodel = true, 22633ca8ab9SSudip Mukherjee }; 22733ca8ab9SSudip Mukherjee 22833ca8ab9SSudip Mukherjee static int __init parkbd_init(void) 22933ca8ab9SSudip Mukherjee { 23033ca8ab9SSudip Mukherjee return parport_register_driver(&parkbd_parport_driver); 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds static void __exit parkbd_exit(void) 2341da177e4SLinus Torvalds { 23533ca8ab9SSudip Mukherjee parport_unregister_driver(&parkbd_parport_driver); 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds module_init(parkbd_init); 2391da177e4SLinus Torvalds module_exit(parkbd_exit); 240