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