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> 49*5a0e3ad6STejun 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 1441da177e4SLinus Torvalds static int parkbd_getport(void) 1451da177e4SLinus Torvalds { 1461da177e4SLinus Torvalds struct parport *pp; 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds pp = parport_find_number(parkbd_pp_no); 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds if (pp == NULL) { 1511da177e4SLinus Torvalds printk(KERN_ERR "parkbd: no such parport\n"); 1521da177e4SLinus Torvalds return -ENODEV; 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL); 1561da177e4SLinus Torvalds parport_put_port(pp); 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds if (!parkbd_dev) 1591da177e4SLinus Torvalds return -ENODEV; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds if (parport_claim(parkbd_dev)) { 1621da177e4SLinus Torvalds parport_unregister_device(parkbd_dev); 1631da177e4SLinus Torvalds return -EBUSY; 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds parkbd_start = jiffies; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds return 0; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds static struct serio * __init parkbd_allocate_serio(void) 1721da177e4SLinus Torvalds { 1731da177e4SLinus Torvalds struct serio *serio; 1741da177e4SLinus Torvalds 175b39787a9SEric Sesterhenn serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 1761da177e4SLinus Torvalds if (serio) { 1771da177e4SLinus Torvalds serio->id.type = parkbd_mode; 1781da177e4SLinus Torvalds serio->write = parkbd_write, 1791da177e4SLinus Torvalds strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name)); 1801da177e4SLinus Torvalds snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name); 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds return serio; 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds static int __init parkbd_init(void) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds int err; 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds err = parkbd_getport(); 1911da177e4SLinus Torvalds if (err) 1921da177e4SLinus Torvalds return err; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds parkbd_port = parkbd_allocate_serio(); 1951da177e4SLinus Torvalds if (!parkbd_port) { 1961da177e4SLinus Torvalds parport_release(parkbd_dev); 1971da177e4SLinus Torvalds return -ENOMEM; 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 2071da177e4SLinus Torvalds return 0; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds static void __exit parkbd_exit(void) 2111da177e4SLinus Torvalds { 2121da177e4SLinus Torvalds parport_release(parkbd_dev); 2131da177e4SLinus Torvalds serio_unregister_port(parkbd_port); 2141da177e4SLinus Torvalds parport_unregister_device(parkbd_dev); 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds module_init(parkbd_init); 2181da177e4SLinus Torvalds module_exit(parkbd_exit); 219