cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

parkbd.c (5393B)


      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
     49MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
     50MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
     51MODULE_LICENSE("GPL");
     52
     53static unsigned int parkbd_pp_no;
     54module_param_named(port, parkbd_pp_no, int, 0);
     55MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
     56
     57static unsigned int parkbd_mode = SERIO_8042;
     58module_param_named(mode, parkbd_mode, uint, 0);
     59MODULE_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
     64static int parkbd_buffer;
     65static int parkbd_counter;
     66static unsigned long parkbd_last;
     67static int parkbd_writing;
     68static unsigned long parkbd_start;
     69
     70static struct pardevice *parkbd_dev;
     71static struct serio *parkbd_port;
     72
     73static int parkbd_readlines(void)
     74{
     75	return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
     76}
     77
     78static void parkbd_writelines(int data)
     79{
     80	parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
     81}
     82
     83static 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
    102static 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
    140static 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
    164static 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
    179static 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
    206static 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
    217static struct parport_driver parkbd_parport_driver = {
    218	.name = "parkbd",
    219	.match_port = parkbd_attach,
    220	.detach = parkbd_detach,
    221	.devmodel = true,
    222};
    223module_parport_driver(parkbd_parport_driver);