parkbd.c 5.5 KB

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