root/drivers/input/serio/parkbd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parkbd_readlines
  2. parkbd_writelines
  3. parkbd_write
  4. parkbd_interrupt
  5. parkbd_getport
  6. parkbd_allocate_serio
  7. parkbd_attach
  8. parkbd_detach
  9. parkbd_init
  10. parkbd_exit

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

/* [<][>][^][v][top][bottom][index][help] */