linux/drivers/input/serio/parkbd.c
<<
>>
Prefs
   1/*
   2 *  Parallel port to Keyboard port adapter driver for Linux
   3 *
   4 *  Copyright (c) 1999-2004 Vojtech Pavlik
   5 */
   6
   7/*
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License version 2 as published by
  10 * the Free Software Foundation.
  11 */
  12
  13/*
  14 * To connect an AT or XT keyboard to the parallel port, a fairly simple adapter
  15 * can be made:
  16 * 
  17 *  Parallel port            Keyboard port
  18 *
  19 *     +5V --------------------- +5V (4)
  20 *  
  21 *                 ______
  22 *     +5V -------|______|--.
  23 *                          |
  24 *     ACK (10) ------------|
  25 *                          |--- KBD CLOCK (5)
  26 *     STROBE (1) ---|<|----'
  27 *     
  28 *                 ______
  29 *     +5V -------|______|--.
  30 *                          |
  31 *     BUSY (11) -----------|
  32 *                          |--- KBD DATA (1)
  33 *     AUTOFD (14) --|<|----'
  34 *
  35 *     GND (18-25) ------------- GND (3)
  36 *     
  37 * The diodes can be fairly any type, and the resistors should be somewhere
  38 * around 5 kOhm, but the adapter will likely work without the resistors,
  39 * too.
  40 *
  41 * The +5V source can be taken either from USB, from mouse or keyboard ports,
  42 * or from a joystick port. Unfortunately, the parallel port of a PC doesn't
  43 * have a +5V pin, and feeding the keyboard from signal pins is out of question
  44 * with 300 mA power reqirement of a typical AT keyboard.
  45 */
  46
  47#include <linux/module.h>
  48#include <linux/parport.h>
  49#include <linux/slab.h>
  50#include <linux/init.h>
  51#include <linux/serio.h>
  52
  53MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  54MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
  55MODULE_LICENSE("GPL");
  56
  57static unsigned int parkbd_pp_no;
  58module_param_named(port, parkbd_pp_no, int, 0);
  59MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
  60
  61static unsigned int parkbd_mode = SERIO_8042;
  62module_param_named(mode, parkbd_mode, uint, 0);
  63MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)");
  64
  65#define PARKBD_CLOCK    0x01    /* Strobe & Ack */
  66#define PARKBD_DATA     0x02    /* AutoFd & Busy */
  67
  68static int parkbd_buffer;
  69static int parkbd_counter;
  70static unsigned long parkbd_last;
  71static int parkbd_writing;
  72static unsigned long parkbd_start;
  73
  74static struct pardevice *parkbd_dev;
  75static struct serio *parkbd_port;
  76
  77static int parkbd_readlines(void)
  78{
  79        return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
  80}
  81
  82static void parkbd_writelines(int data)
  83{
  84        parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
  85}
  86
  87static int parkbd_write(struct serio *port, unsigned char c)
  88{
  89        unsigned char p;
  90
  91        if (!parkbd_mode) return -1;
  92
  93        p = c ^ (c >> 4);
  94        p = p ^ (p >> 2);
  95        p = p ^ (p >> 1);
  96
  97        parkbd_counter = 0;
  98        parkbd_writing = 1;
  99        parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
 100
 101        parkbd_writelines(2);
 102
 103        return 0;
 104}
 105
 106static void parkbd_interrupt(void *dev_id)
 107{
 108
 109        if (parkbd_writing) {
 110
 111                if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
 112                        parkbd_counter = 0;
 113                        parkbd_buffer = 0;
 114                        parkbd_writing = 0;
 115                        parkbd_writelines(3);
 116                        return;
 117                }
 118
 119                parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
 120
 121                if (parkbd_counter == 11) {
 122                        parkbd_counter = 0;
 123                        parkbd_buffer = 0;
 124                        parkbd_writing = 0;
 125                        parkbd_writelines(3);
 126                }
 127
 128        } else {
 129
 130                if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
 131                        parkbd_counter = 0;
 132                        parkbd_buffer = 0;
 133                }
 134
 135                parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
 136
 137                if (parkbd_counter == parkbd_mode + 10)
 138                        serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
 139        }
 140
 141        parkbd_last = jiffies;
 142}
 143
 144static int parkbd_getport(void)
 145{
 146        struct parport *pp;
 147
 148        pp = parport_find_number(parkbd_pp_no);
 149
 150        if (pp == NULL) {
 151                printk(KERN_ERR "parkbd: no such parport\n");
 152                return -ENODEV;
 153        }
 154
 155        parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
 156        parport_put_port(pp);
 157
 158        if (!parkbd_dev)
 159                return -ENODEV;
 160
 161        if (parport_claim(parkbd_dev)) {
 162                parport_unregister_device(parkbd_dev);
 163                return -EBUSY;
 164        }
 165
 166        parkbd_start = jiffies;
 167
 168        return 0;
 169}
 170
 171static struct serio * __init parkbd_allocate_serio(void)
 172{
 173        struct serio *serio;
 174
 175        serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
 176        if (serio) {
 177                serio->id.type = parkbd_mode;
 178                serio->write = parkbd_write,
 179                strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
 180                snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
 181        }
 182
 183        return serio;
 184}
 185
 186static int __init parkbd_init(void)
 187{
 188        int err;
 189
 190        err = parkbd_getport();
 191        if (err)
 192                return err;
 193
 194        parkbd_port = parkbd_allocate_serio();
 195        if (!parkbd_port) {
 196                parport_release(parkbd_dev);
 197                return -ENOMEM;
 198        }
 199
 200        parkbd_writelines(3);
 201
 202        serio_register_port(parkbd_port);
 203
 204        printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
 205                        parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
 206
 207        return 0;
 208}
 209
 210static void __exit parkbd_exit(void)
 211{
 212        parport_release(parkbd_dev);
 213        serio_unregister_port(parkbd_port);
 214        parport_unregister_device(parkbd_dev);
 215}
 216
 217module_init(parkbd_init);
 218module_exit(parkbd_exit);
 219