Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  Parallel port to Keyboard port adapter driver for Linux
0004  *
0005  *  Copyright (c) 1999-2004 Vojtech Pavlik
0006  */
0007 
0008 
0009 /*
0010  * To connect an AT or XT keyboard to the parallel port, a fairly simple adapter
0011  * can be made:
0012  * 
0013  *  Parallel port            Keyboard port
0014  *
0015  *     +5V --------------------- +5V (4)
0016  *  
0017  *                 ______
0018  *     +5V -------|______|--.
0019  *                          |
0020  *     ACK (10) ------------|
0021  *                          |--- KBD CLOCK (5)
0022  *     STROBE (1) ---|<|----'
0023  *     
0024  *                 ______
0025  *     +5V -------|______|--.
0026  *                          |
0027  *     BUSY (11) -----------|
0028  *                          |--- KBD DATA (1)
0029  *     AUTOFD (14) --|<|----'
0030  *
0031  *     GND (18-25) ------------- GND (3)
0032  *     
0033  * The diodes can be fairly any type, and the resistors should be somewhere
0034  * around 5 kOhm, but the adapter will likely work without the resistors,
0035  * too.
0036  *
0037  * The +5V source can be taken either from USB, from mouse or keyboard ports,
0038  * or from a joystick port. Unfortunately, the parallel port of a PC doesn't
0039  * have a +5V pin, and feeding the keyboard from signal pins is out of question
0040  * with 300 mA power reqirement of a typical AT keyboard.
0041  */
0042 
0043 #include <linux/module.h>
0044 #include <linux/parport.h>
0045 #include <linux/slab.h>
0046 #include <linux/init.h>
0047 #include <linux/serio.h>
0048 
0049 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
0050 MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
0051 MODULE_LICENSE("GPL");
0052 
0053 static unsigned int parkbd_pp_no;
0054 module_param_named(port, parkbd_pp_no, int, 0);
0055 MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
0056 
0057 static unsigned int parkbd_mode = SERIO_8042;
0058 module_param_named(mode, parkbd_mode, uint, 0);
0059 MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)");
0060 
0061 #define PARKBD_CLOCK    0x01    /* Strobe & Ack */
0062 #define PARKBD_DATA 0x02    /* AutoFd & Busy */
0063 
0064 static int parkbd_buffer;
0065 static int parkbd_counter;
0066 static unsigned long parkbd_last;
0067 static int parkbd_writing;
0068 static unsigned long parkbd_start;
0069 
0070 static struct pardevice *parkbd_dev;
0071 static struct serio *parkbd_port;
0072 
0073 static int parkbd_readlines(void)
0074 {
0075     return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
0076 }
0077 
0078 static void parkbd_writelines(int data)
0079 {
0080     parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
0081 }
0082 
0083 static int parkbd_write(struct serio *port, unsigned char c)
0084 {
0085     unsigned char p;
0086 
0087     if (!parkbd_mode) return -1;
0088 
0089         p = c ^ (c >> 4);
0090     p = p ^ (p >> 2);
0091     p = p ^ (p >> 1);
0092 
0093     parkbd_counter = 0;
0094     parkbd_writing = 1;
0095     parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
0096 
0097     parkbd_writelines(2);
0098 
0099     return 0;
0100 }
0101 
0102 static void parkbd_interrupt(void *dev_id)
0103 {
0104 
0105     if (parkbd_writing) {
0106 
0107         if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
0108             parkbd_counter = 0;
0109             parkbd_buffer = 0;
0110             parkbd_writing = 0;
0111             parkbd_writelines(3);
0112             return;
0113         }
0114 
0115         parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
0116 
0117         if (parkbd_counter == 11) {
0118             parkbd_counter = 0;
0119             parkbd_buffer = 0;
0120             parkbd_writing = 0;
0121             parkbd_writelines(3);
0122         }
0123 
0124     } else {
0125 
0126         if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
0127             parkbd_counter = 0;
0128             parkbd_buffer = 0;
0129         }
0130 
0131         parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
0132 
0133         if (parkbd_counter == parkbd_mode + 10)
0134             serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
0135     }
0136 
0137     parkbd_last = jiffies;
0138 }
0139 
0140 static int parkbd_getport(struct parport *pp)
0141 {
0142     struct pardev_cb parkbd_parport_cb;
0143 
0144     memset(&parkbd_parport_cb, 0, sizeof(parkbd_parport_cb));
0145     parkbd_parport_cb.irq_func = parkbd_interrupt;
0146     parkbd_parport_cb.flags = PARPORT_FLAG_EXCL;
0147 
0148     parkbd_dev = parport_register_dev_model(pp, "parkbd",
0149                         &parkbd_parport_cb, 0);
0150 
0151     if (!parkbd_dev)
0152         return -ENODEV;
0153 
0154     if (parport_claim(parkbd_dev)) {
0155         parport_unregister_device(parkbd_dev);
0156         return -EBUSY;
0157     }
0158 
0159     parkbd_start = jiffies;
0160 
0161     return 0;
0162 }
0163 
0164 static struct serio *parkbd_allocate_serio(void)
0165 {
0166     struct serio *serio;
0167 
0168     serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
0169     if (serio) {
0170         serio->id.type = parkbd_mode;
0171         serio->write = parkbd_write;
0172         strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
0173         snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
0174     }
0175 
0176     return serio;
0177 }
0178 
0179 static void parkbd_attach(struct parport *pp)
0180 {
0181     if (pp->number != parkbd_pp_no) {
0182         pr_debug("Not using parport%d.\n", pp->number);
0183         return;
0184     }
0185 
0186     if (parkbd_getport(pp))
0187         return;
0188 
0189     parkbd_port = parkbd_allocate_serio();
0190     if (!parkbd_port) {
0191         parport_release(parkbd_dev);
0192         parport_unregister_device(parkbd_dev);
0193         return;
0194     }
0195 
0196     parkbd_writelines(3);
0197 
0198     serio_register_port(parkbd_port);
0199 
0200     printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
0201                         parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
0202 
0203     return;
0204 }
0205 
0206 static void parkbd_detach(struct parport *port)
0207 {
0208     if (!parkbd_port || port->number != parkbd_pp_no)
0209         return;
0210 
0211     parport_release(parkbd_dev);
0212     serio_unregister_port(parkbd_port);
0213     parport_unregister_device(parkbd_dev);
0214     parkbd_port = NULL;
0215 }
0216 
0217 static struct parport_driver parkbd_parport_driver = {
0218     .name = "parkbd",
0219     .match_port = parkbd_attach,
0220     .detach = parkbd_detach,
0221     .devmodel = true,
0222 };
0223 module_parport_driver(parkbd_parport_driver);