parkbd.c 5.0 KB

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