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