actisys.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*********************************************************************
  2. *
  3. * Filename: actisys.c
  4. * Version: 1.0
  5. * Description: Implementation for the ACTiSYS IR-220L and IR-220L+
  6. * dongles
  7. * Status: Beta.
  8. * Authors: Dag Brattli <dagb@cs.uit.no> (initially)
  9. * Jean Tourrilhes <jt@hpl.hp.com> (new version)
  10. * Created at: Wed Oct 21 20:02:35 1998
  11. * Modified at: Fri Dec 17 09:10:43 1999
  12. * Modified by: Dag Brattli <dagb@cs.uit.no>
  13. *
  14. * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  15. * Copyright (c) 1999 Jean Tourrilhes
  16. *
  17. * This program is free software; you can redistribute it and/or
  18. * modify it under the terms of the GNU General Public License as
  19. * published by the Free Software Foundation; either version 2 of
  20. * the License, or (at your option) any later version.
  21. *
  22. * Neither Dag Brattli nor University of Tromsø admit liability nor
  23. * provide warranty for any of this software. This material is
  24. * provided "AS-IS" and at no charge.
  25. *
  26. ********************************************************************/
  27. /*
  28. * Changelog
  29. *
  30. * 0.8 -> 0.9999 - Jean
  31. * o New initialisation procedure : much safer and correct
  32. * o New procedure the change speed : much faster and simpler
  33. * o Other cleanups & comments
  34. * Thanks to Lichen Wang @ Actisys for his excellent help...
  35. */
  36. #include <linux/module.h>
  37. #include <linux/delay.h>
  38. #include <linux/tty.h>
  39. #include <linux/init.h>
  40. #include <net/irda/irda.h>
  41. #include <net/irda/irda_device.h>
  42. /*
  43. * Define the timing of the pulses we send to the dongle (to reset it, and
  44. * to toggle speeds). Basically, the limit here is the propagation speed of
  45. * the signals through the serial port, the dongle being much faster. Any
  46. * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can
  47. * go through cleanly . If you are on the wild side, you can try to lower
  48. * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!)
  49. */
  50. #define MIN_DELAY 10 /* 10 us to be on the conservative side */
  51. static int actisys_change_speed(struct irda_task *task);
  52. static int actisys_reset(struct irda_task *task);
  53. static void actisys_open(dongle_t *self, struct qos_info *qos);
  54. static void actisys_close(dongle_t *self);
  55. /* These are the baudrates supported, in the order available */
  56. /* Note : the 220L doesn't support 38400, but we will fix that below */
  57. static __u32 baud_rates[] = { 9600, 19200, 57600, 115200, 38400 };
  58. #define MAX_SPEEDS 5
  59. static struct dongle_reg dongle = {
  60. .type = IRDA_ACTISYS_DONGLE,
  61. .open = actisys_open,
  62. .close = actisys_close,
  63. .reset = actisys_reset,
  64. .change_speed = actisys_change_speed,
  65. .owner = THIS_MODULE,
  66. };
  67. static struct dongle_reg dongle_plus = {
  68. .type = IRDA_ACTISYS_PLUS_DONGLE,
  69. .open = actisys_open,
  70. .close = actisys_close,
  71. .reset = actisys_reset,
  72. .change_speed = actisys_change_speed,
  73. .owner = THIS_MODULE,
  74. };
  75. /*
  76. * Function actisys_change_speed (task)
  77. *
  78. * There is two model of Actisys dongle we are dealing with,
  79. * the 220L and 220L+. At this point, only irattach knows with
  80. * kind the user has requested (it was an argument on irattach
  81. * command line).
  82. * So, we register a dongle of each sort and let irattach
  83. * pick the right one...
  84. */
  85. static int __init actisys_init(void)
  86. {
  87. int ret;
  88. /* First, register an Actisys 220L dongle */
  89. ret = irda_device_register_dongle(&dongle);
  90. if (ret < 0)
  91. return ret;
  92. /* Now, register an Actisys 220L+ dongle */
  93. ret = irda_device_register_dongle(&dongle_plus);
  94. if (ret < 0) {
  95. irda_device_unregister_dongle(&dongle);
  96. return ret;
  97. }
  98. return 0;
  99. }
  100. static void __exit actisys_cleanup(void)
  101. {
  102. /* We have to remove both dongles */
  103. irda_device_unregister_dongle(&dongle);
  104. irda_device_unregister_dongle(&dongle_plus);
  105. }
  106. static void actisys_open(dongle_t *self, struct qos_info *qos)
  107. {
  108. /* Power on the dongle */
  109. self->set_dtr_rts(self->dev, TRUE, TRUE);
  110. /* Set the speeds we can accept */
  111. qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
  112. /* Remove support for 38400 if this is not a 220L+ dongle */
  113. if (self->issue->type == IRDA_ACTISYS_DONGLE)
  114. qos->baud_rate.bits &= ~IR_38400;
  115. qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */
  116. }
  117. static void actisys_close(dongle_t *self)
  118. {
  119. /* Power off the dongle */
  120. self->set_dtr_rts(self->dev, FALSE, FALSE);
  121. }
  122. /*
  123. * Function actisys_change_speed (task)
  124. *
  125. * Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles.
  126. * To cycle through the available baud rates, pulse RTS low for a few us.
  127. *
  128. * First, we reset the dongle to always start from a known state.
  129. * Then, we cycle through the speeds by pulsing RTS low and then up.
  130. * The dongle allow us to pulse quite fast, se we can set speed in one go,
  131. * which is must faster ( < 100 us) and less complex than what is found
  132. * in some other dongle drivers...
  133. * Note that even if the new speed is the same as the current speed,
  134. * we reassert the speed. This make sure that things are all right,
  135. * and it's fast anyway...
  136. * By the way, this function will work for both type of dongles,
  137. * because the additional speed is at the end of the sequence...
  138. */
  139. static int actisys_change_speed(struct irda_task *task)
  140. {
  141. dongle_t *self = (dongle_t *) task->instance;
  142. __u32 speed = (__u32) task->param; /* Target speed */
  143. int ret = 0;
  144. int i = 0;
  145. IRDA_DEBUG(4, "%s(), speed=%d (was %d)\n", __FUNCTION__, speed,
  146. self->speed);
  147. /* Go to a known state by reseting the dongle */
  148. /* Reset the dongle : set DTR low for 10 us */
  149. self->set_dtr_rts(self->dev, FALSE, TRUE);
  150. udelay(MIN_DELAY);
  151. /* Go back to normal mode (we are now at 9600 b/s) */
  152. self->set_dtr_rts(self->dev, TRUE, TRUE);
  153. /*
  154. * Now, we can set the speed requested. Send RTS pulses until we
  155. * reach the target speed
  156. */
  157. for (i=0; i<MAX_SPEEDS; i++) {
  158. if (speed == baud_rates[i]) {
  159. self->speed = baud_rates[i];
  160. break;
  161. }
  162. /* Make sure previous pulse is finished */
  163. udelay(MIN_DELAY);
  164. /* Set RTS low for 10 us */
  165. self->set_dtr_rts(self->dev, TRUE, FALSE);
  166. udelay(MIN_DELAY);
  167. /* Set RTS high for 10 us */
  168. self->set_dtr_rts(self->dev, TRUE, TRUE);
  169. }
  170. /* Check if life is sweet... */
  171. if (i >= MAX_SPEEDS)
  172. ret = -1; /* This should not happen */
  173. /* Basta lavoro, on se casse d'ici... */
  174. irda_task_next_state(task, IRDA_TASK_DONE);
  175. return ret;
  176. }
  177. /*
  178. * Function actisys_reset (task)
  179. *
  180. * Reset the Actisys type dongle. Warning, this function must only be
  181. * called with a process context!
  182. *
  183. * We need to do two things in this function :
  184. * o first make sure that the dongle is in a state where it can operate
  185. * o second put the dongle in a know state
  186. *
  187. * The dongle is powered of the RTS and DTR lines. In the dongle, there
  188. * is a big capacitor to accommodate the current spikes. This capacitor
  189. * takes a least 50 ms to be charged. In theory, the Bios set those lines
  190. * up, so by the time we arrive here we should be set. It doesn't hurt
  191. * to be on the conservative side, so we will wait...
  192. * Then, we set the speed to 9600 b/s to get in a known state (see in
  193. * change_speed for details). It is needed because the IrDA stack
  194. * has tried to set the speed immediately after our first return,
  195. * so before we can be sure the dongle is up and running.
  196. */
  197. static int actisys_reset(struct irda_task *task)
  198. {
  199. dongle_t *self = (dongle_t *) task->instance;
  200. int ret = 0;
  201. IRDA_ASSERT(task != NULL, return -1;);
  202. self->reset_task = task;
  203. switch (task->state) {
  204. case IRDA_TASK_INIT:
  205. /* Set both DTR & RTS to power up the dongle */
  206. /* In theory redundant with power up in actisys_open() */
  207. self->set_dtr_rts(self->dev, TRUE, TRUE);
  208. /* Sleep 50 ms to make sure capacitor is charged */
  209. ret = msecs_to_jiffies(50);
  210. irda_task_next_state(task, IRDA_TASK_WAIT);
  211. break;
  212. case IRDA_TASK_WAIT:
  213. /* Reset the dongle : set DTR low for 10 us */
  214. self->set_dtr_rts(self->dev, FALSE, TRUE);
  215. udelay(MIN_DELAY);
  216. /* Go back to normal mode */
  217. self->set_dtr_rts(self->dev, TRUE, TRUE);
  218. irda_task_next_state(task, IRDA_TASK_DONE);
  219. self->reset_task = NULL;
  220. self->speed = 9600; /* That's the default */
  221. break;
  222. default:
  223. IRDA_ERROR("%s(), unknown state %d\n",
  224. __FUNCTION__, task->state);
  225. irda_task_next_state(task, IRDA_TASK_DONE);
  226. self->reset_task = NULL;
  227. ret = -1;
  228. break;
  229. }
  230. return ret;
  231. }
  232. MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> - Jean Tourrilhes <jt@hpl.hp.com>");
  233. MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver");
  234. MODULE_LICENSE("GPL");
  235. MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */
  236. MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */
  237. /*
  238. * Function init_module (void)
  239. *
  240. * Initialize Actisys module
  241. *
  242. */
  243. module_init(actisys_init);
  244. /*
  245. * Function cleanup_module (void)
  246. *
  247. * Cleanup Actisys module
  248. *
  249. */
  250. module_exit(actisys_cleanup);