parkbd.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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/slab.h>
  47. #include <linux/init.h>
  48. #include <linux/serio.h>
  49. MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  50. MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
  51. MODULE_LICENSE("GPL");
  52. static unsigned int parkbd_pp_no;
  53. module_param_named(port, parkbd_pp_no, int, 0);
  54. MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
  55. static unsigned int parkbd_mode = SERIO_8042;
  56. module_param_named(mode, parkbd_mode, uint, 0);
  57. MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)");
  58. #define PARKBD_CLOCK 0x01 /* Strobe & Ack */
  59. #define PARKBD_DATA 0x02 /* AutoFd & Busy */
  60. static int parkbd_buffer;
  61. static int parkbd_counter;
  62. static unsigned long parkbd_last;
  63. static int parkbd_writing;
  64. static unsigned long parkbd_start;
  65. static struct pardevice *parkbd_dev;
  66. static struct serio *parkbd_port;
  67. static int parkbd_readlines(void)
  68. {
  69. return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
  70. }
  71. static void parkbd_writelines(int data)
  72. {
  73. parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
  74. }
  75. static int parkbd_write(struct serio *port, unsigned char c)
  76. {
  77. unsigned char p;
  78. if (!parkbd_mode) return -1;
  79. p = c ^ (c >> 4);
  80. p = p ^ (p >> 2);
  81. p = p ^ (p >> 1);
  82. parkbd_counter = 0;
  83. parkbd_writing = 1;
  84. parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
  85. parkbd_writelines(2);
  86. return 0;
  87. }
  88. static void parkbd_interrupt(void *dev_id)
  89. {
  90. if (parkbd_writing) {
  91. if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
  92. parkbd_counter = 0;
  93. parkbd_buffer = 0;
  94. parkbd_writing = 0;
  95. parkbd_writelines(3);
  96. return;
  97. }
  98. parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
  99. if (parkbd_counter == 11) {
  100. parkbd_counter = 0;
  101. parkbd_buffer = 0;
  102. parkbd_writing = 0;
  103. parkbd_writelines(3);
  104. }
  105. } else {
  106. if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
  107. parkbd_counter = 0;
  108. parkbd_buffer = 0;
  109. }
  110. parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
  111. if (parkbd_counter == parkbd_mode + 10)
  112. serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
  113. }
  114. parkbd_last = jiffies;
  115. }
  116. static int parkbd_getport(void)
  117. {
  118. struct parport *pp;
  119. pp = parport_find_number(parkbd_pp_no);
  120. if (pp == NULL) {
  121. printk(KERN_ERR "parkbd: no such parport\n");
  122. return -ENODEV;
  123. }
  124. parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
  125. parport_put_port(pp);
  126. if (!parkbd_dev)
  127. return -ENODEV;
  128. if (parport_claim(parkbd_dev)) {
  129. parport_unregister_device(parkbd_dev);
  130. return -EBUSY;
  131. }
  132. parkbd_start = jiffies;
  133. return 0;
  134. }
  135. static struct serio * __init parkbd_allocate_serio(void)
  136. {
  137. struct serio *serio;
  138. serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
  139. if (serio) {
  140. serio->id.type = parkbd_mode;
  141. serio->write = parkbd_write,
  142. strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
  143. snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
  144. }
  145. return serio;
  146. }
  147. static int __init parkbd_init(void)
  148. {
  149. int err;
  150. err = parkbd_getport();
  151. if (err)
  152. return err;
  153. parkbd_port = parkbd_allocate_serio();
  154. if (!parkbd_port) {
  155. parport_release(parkbd_dev);
  156. return -ENOMEM;
  157. }
  158. parkbd_writelines(3);
  159. serio_register_port(parkbd_port);
  160. printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
  161. parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
  162. return 0;
  163. }
  164. static void __exit parkbd_exit(void)
  165. {
  166. parport_release(parkbd_dev);
  167. serio_unregister_port(parkbd_port);
  168. parport_unregister_device(parkbd_dev);
  169. }
  170. module_init(parkbd_init);
  171. module_exit(parkbd_exit);