diff options
Diffstat (limited to 'drivers/net/irda/toim3232-sir.c')
| -rw-r--r-- | drivers/net/irda/toim3232-sir.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c new file mode 100644 index 000000000000..aa1a9b0ed83e --- /dev/null +++ b/drivers/net/irda/toim3232-sir.c | |||
| @@ -0,0 +1,375 @@ | |||
| 1 | /********************************************************************* | ||
| 2 | * | ||
| 3 | * Filename: toim3232-sir.c | ||
| 4 | * Version: 1.0 | ||
| 5 | * Description: Implementation of dongles based on the Vishay/Temic | ||
| 6 | * TOIM3232 SIR Endec chipset. Currently only the | ||
| 7 | * IRWave IR320ST-2 is tested, although it should work | ||
| 8 | * with any TOIM3232 or TOIM4232 chipset based RS232 | ||
| 9 | * dongle with minimal modification. | ||
| 10 | * Based heavily on the Tekram driver (tekram.c), | ||
| 11 | * with thanks to Dag Brattli and Martin Diehl. | ||
| 12 | * Status: Experimental. | ||
| 13 | * Author: David Basden <davidb-irda@rcpt.to> | ||
| 14 | * Created at: Thu Feb 09 23:47:32 2006 | ||
| 15 | * | ||
| 16 | * Copyright (c) 2006 David Basden. | ||
| 17 | * Copyright (c) 1998-1999 Dag Brattli, | ||
| 18 | * Copyright (c) 2002 Martin Diehl, | ||
| 19 | * All Rights Reserved. | ||
| 20 | * | ||
| 21 | * This program is free software; you can redistribute it and/or | ||
| 22 | * modify it under the terms of the GNU General Public License as | ||
| 23 | * published by the Free Software Foundation; either version 2 of | ||
| 24 | * the License, or (at your option) any later version. | ||
| 25 | * | ||
| 26 | * Neither Dag Brattli nor University of Tromsø admit liability nor | ||
| 27 | * provide warranty for any of this software. This material is | ||
| 28 | * provided "AS-IS" and at no charge. | ||
| 29 | * | ||
| 30 | ********************************************************************/ | ||
| 31 | |||
| 32 | /* | ||
| 33 | * This driver has currently only been tested on the IRWave IR320ST-2 | ||
| 34 | * | ||
| 35 | * PROTOCOL: | ||
| 36 | * | ||
| 37 | * The protocol for talking to the TOIM3232 is quite easy, and is | ||
| 38 | * designed to interface with RS232 with only level convertors. The | ||
| 39 | * BR/~D line on the chip is brought high to signal 'command mode', | ||
| 40 | * where a command byte is sent to select the baudrate of the RS232 | ||
| 41 | * interface and the pulse length of the IRDA output. When BR/~D | ||
| 42 | * is brought low, the dongle then changes to the selected baudrate, | ||
| 43 | * and the RS232 interface is used for data until BR/~D is brought | ||
| 44 | * high again. The initial speed for the TOIMx323 after RESET is | ||
| 45 | * 9600 baud. The baudrate for command-mode is the last selected | ||
| 46 | * baud-rate, or 9600 after a RESET. | ||
| 47 | * | ||
| 48 | * The dongle I have (below) adds some extra hardware on the front end, | ||
| 49 | * but this is mostly directed towards pariasitic power from the RS232 | ||
| 50 | * line rather than changing very much about how to communicate with | ||
| 51 | * the TOIM3232. | ||
| 52 | * | ||
| 53 | * The protocol to talk to the TOIM4232 chipset seems to be almost | ||
| 54 | * identical to the TOIM3232 (and the 4232 datasheet is more detailed) | ||
| 55 | * so this code will probably work on that as well, although I haven't | ||
| 56 | * tested it on that hardware. | ||
| 57 | * | ||
| 58 | * Target dongle variations that might be common: | ||
| 59 | * | ||
| 60 | * DTR and RTS function: | ||
| 61 | * The data sheet for the 4232 has a sample implementation that hooks the | ||
| 62 | * DTR and RTS lines to the RESET and BaudRate/~Data lines of the | ||
| 63 | * chip (through line-converters). Given both DTR and RTS would have to | ||
| 64 | * be held low in normal operation, and the TOIMx232 requires +5V to | ||
| 65 | * signal ground, most dongle designers would almost certainly choose | ||
| 66 | * an implementation that kept at least one of DTR or RTS high in | ||
| 67 | * normal operation to provide power to the dongle, but will likely | ||
| 68 | * vary between designs. | ||
| 69 | * | ||
| 70 | * User specified command bits: | ||
| 71 | * There are two user-controllable output lines from the TOIMx232 that | ||
| 72 | * can be set low or high by setting the appropriate bits in the | ||
| 73 | * high-nibble of the command byte (when setting speed and pulse length). | ||
| 74 | * These might be used to switch on and off added hardware or extra | ||
| 75 | * dongle features. | ||
| 76 | * | ||
| 77 | * | ||
| 78 | * Target hardware: IRWave IR320ST-2 | ||
| 79 | * | ||
| 80 | * The IRWave IR320ST-2 is a simple dongle based on the Vishay/Temic | ||
| 81 | * TOIM3232 SIR Endec and the Vishay/Temic TFDS4500 SIR IRDA transciever. | ||
| 82 | * It uses a hex inverter and some discrete components to buffer and | ||
| 83 | * line convert the RS232 down to 5V. | ||
| 84 | * | ||
| 85 | * The dongle is powered through a voltage regulator, fed by a large | ||
| 86 | * capacitor. To switch the dongle on, DTR is brought high to charge | ||
| 87 | * the capacitor and drive the voltage regulator. DTR isn't associated | ||
| 88 | * with any control lines on the TOIM3232. Parisitic power is also taken | ||
| 89 | * from the RTS, TD and RD lines when brought high, but through resistors. | ||
| 90 | * When DTR is low, the circuit might lose power even with RTS high. | ||
| 91 | * | ||
| 92 | * RTS is inverted and attached to the BR/~D input pin. When RTS | ||
| 93 | * is high, BR/~D is low, and the TOIM3232 is in the normal 'data' mode. | ||
| 94 | * RTS is brought low, BR/~D is high, and the TOIM3232 is in 'command | ||
| 95 | * mode'. | ||
| 96 | * | ||
| 97 | * For some unknown reason, the RESET line isn't actually connected | ||
| 98 | * to anything. This means to reset the dongle to get it to a known | ||
| 99 | * state (9600 baud) you must drop DTR and RTS low, wait for the power | ||
| 100 | * capacitor to discharge, and then bring DTR (and RTS for data mode) | ||
| 101 | * high again, and wait for the capacitor to charge, the power supply | ||
| 102 | * to stabilise, and the oscillator clock to stabilise. | ||
| 103 | * | ||
| 104 | * Fortunately, if the current baudrate is known, the chipset can | ||
| 105 | * easily change speed by entering command mode without having to | ||
| 106 | * reset the dongle first. | ||
| 107 | * | ||
| 108 | * Major Components: | ||
| 109 | * | ||
| 110 | * - Vishay/Temic TOIM3232 SIR Endec to change RS232 pulse timings | ||
| 111 | * to IRDA pulse timings | ||
| 112 | * - 3.6864MHz crystal to drive TOIM3232 clock oscillator | ||
| 113 | * - DM74lS04M Inverting Hex line buffer for RS232 input buffering | ||
| 114 | * and level conversion | ||
| 115 | * - PJ2951AC 150mA voltage regulator | ||
| 116 | * - Vishay/Temic TFDS4500 SIR IRDA front-end transceiver | ||
| 117 | * | ||
| 118 | */ | ||
| 119 | |||
| 120 | #include <linux/module.h> | ||
| 121 | #include <linux/delay.h> | ||
| 122 | #include <linux/init.h> | ||
| 123 | |||
| 124 | #include <net/irda/irda.h> | ||
| 125 | |||
| 126 | #include "sir-dev.h" | ||
| 127 | |||
| 128 | static int toim3232delay = 150; /* default is 150 ms */ | ||
| 129 | module_param(toim3232delay, int, 0); | ||
| 130 | MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay"); | ||
| 131 | |||
| 132 | #if 0 | ||
| 133 | static int toim3232flipdtr = 0; /* default is DTR high to reset */ | ||
| 134 | module_param(toim3232flipdtr, int, 0); | ||
| 135 | MODULE_PARM_DESC(toim3232flipdtr, "toim3232 dongle invert DTR (Reset)"); | ||
| 136 | |||
| 137 | static int toim3232fliprts = 0; /* default is RTS high for baud change */ | ||
| 138 | module_param(toim3232fliptrs, int, 0); | ||
| 139 | MODULE_PARM_DESC(toim3232fliprts, "toim3232 dongle invert RTS (BR/D)"); | ||
| 140 | #endif | ||
| 141 | |||
| 142 | static int toim3232_open(struct sir_dev *); | ||
| 143 | static int toim3232_close(struct sir_dev *); | ||
| 144 | static int toim3232_change_speed(struct sir_dev *, unsigned); | ||
| 145 | static int toim3232_reset(struct sir_dev *); | ||
| 146 | |||
| 147 | #define TOIM3232_115200 0x00 | ||
| 148 | #define TOIM3232_57600 0x01 | ||
| 149 | #define TOIM3232_38400 0x02 | ||
| 150 | #define TOIM3232_19200 0x03 | ||
| 151 | #define TOIM3232_9600 0x06 | ||
| 152 | #define TOIM3232_2400 0x0A | ||
| 153 | |||
| 154 | #define TOIM3232_PW 0x10 /* Pulse select bit */ | ||
| 155 | |||
| 156 | static struct dongle_driver toim3232 = { | ||
| 157 | .owner = THIS_MODULE, | ||
| 158 | .driver_name = "Vishay TOIM3232", | ||
| 159 | .type = IRDA_TOIM3232_DONGLE, | ||
| 160 | .open = toim3232_open, | ||
| 161 | .close = toim3232_close, | ||
| 162 | .reset = toim3232_reset, | ||
| 163 | .set_speed = toim3232_change_speed, | ||
| 164 | }; | ||
| 165 | |||
| 166 | static int __init toim3232_sir_init(void) | ||
| 167 | { | ||
| 168 | if (toim3232delay < 1 || toim3232delay > 500) | ||
| 169 | toim3232delay = 200; | ||
| 170 | IRDA_DEBUG(1, "%s - using %d ms delay\n", | ||
| 171 | toim3232.driver_name, toim3232delay); | ||
| 172 | return irda_register_dongle(&toim3232); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void __exit toim3232_sir_cleanup(void) | ||
| 176 | { | ||
| 177 | irda_unregister_dongle(&toim3232); | ||
| 178 | } | ||
| 179 | |||
| 180 | static int toim3232_open(struct sir_dev *dev) | ||
| 181 | { | ||
| 182 | struct qos_info *qos = &dev->qos; | ||
| 183 | |||
| 184 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
| 185 | |||
| 186 | /* Pull the lines high to start with. | ||
| 187 | * | ||
| 188 | * For the IR320ST-2, we need to charge the main supply capacitor to | ||
| 189 | * switch the device on. We keep DTR high throughout to do this. | ||
| 190 | * When RTS, TD and RD are high, they will also trickle-charge the | ||
| 191 | * cap. RTS is high for data transmission, and low for baud rate select. | ||
| 192 | * -- DGB | ||
| 193 | */ | ||
| 194 | sirdev_set_dtr_rts(dev, TRUE, TRUE); | ||
| 195 | |||
| 196 | /* The TOI3232 supports many speeds between 1200bps and 115000bps. | ||
| 197 | * We really only care about those supported by the IRDA spec, but | ||
| 198 | * 38400 seems to be implemented in many places */ | ||
| 199 | qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; | ||
| 200 | |||
| 201 | /* From the tekram driver. Not sure what a reasonable value is -- DGB */ | ||
| 202 | qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ | ||
| 203 | irda_qos_bits_to_value(qos); | ||
| 204 | |||
| 205 | /* irda thread waits 50 msec for power settling */ | ||
| 206 | |||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | static int toim3232_close(struct sir_dev *dev) | ||
| 211 | { | ||
| 212 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
| 213 | |||
| 214 | /* Power off dongle */ | ||
| 215 | sirdev_set_dtr_rts(dev, FALSE, FALSE); | ||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | /* | ||
| 221 | * Function toim3232change_speed (dev, state, speed) | ||
| 222 | * | ||
| 223 | * Set the speed for the TOIM3232 based dongle. Warning, this | ||
| 224 | * function must be called with a process context! | ||
| 225 | * | ||
| 226 | * Algorithm | ||
| 227 | * 1. keep DTR high but clear RTS to bring into baud programming mode | ||
| 228 | * 2. wait at least 7us to enter programming mode | ||
| 229 | * 3. send control word to set baud rate and timing | ||
| 230 | * 4. wait at least 1us | ||
| 231 | * 5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver) | ||
| 232 | * 6. should take effect immediately (although probably worth waiting) | ||
| 233 | */ | ||
| 234 | |||
| 235 | #define TOIM3232_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) | ||
| 236 | |||
| 237 | static int toim3232_change_speed(struct sir_dev *dev, unsigned speed) | ||
| 238 | { | ||
| 239 | unsigned state = dev->fsm.substate; | ||
| 240 | unsigned delay = 0; | ||
| 241 | u8 byte; | ||
| 242 | static int ret = 0; | ||
| 243 | |||
| 244 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
| 245 | |||
| 246 | switch(state) { | ||
| 247 | case SIRDEV_STATE_DONGLE_SPEED: | ||
| 248 | |||
| 249 | /* Figure out what we are going to send as a control byte */ | ||
| 250 | switch (speed) { | ||
| 251 | case 2400: | ||
| 252 | byte = TOIM3232_PW|TOIM3232_2400; | ||
| 253 | break; | ||
| 254 | default: | ||
| 255 | speed = 9600; | ||
| 256 | ret = -EINVAL; | ||
| 257 | /* fall thru */ | ||
| 258 | case 9600: | ||
| 259 | byte = TOIM3232_PW|TOIM3232_9600; | ||
| 260 | break; | ||
| 261 | case 19200: | ||
| 262 | byte = TOIM3232_PW|TOIM3232_19200; | ||
| 263 | break; | ||
| 264 | case 38400: | ||
| 265 | byte = TOIM3232_PW|TOIM3232_38400; | ||
| 266 | break; | ||
| 267 | case 57600: | ||
| 268 | byte = TOIM3232_PW|TOIM3232_57600; | ||
| 269 | break; | ||
| 270 | case 115200: | ||
| 271 | byte = TOIM3232_115200; | ||
| 272 | break; | ||
| 273 | } | ||
| 274 | |||
| 275 | /* Set DTR, Clear RTS: Go into baud programming mode */ | ||
| 276 | sirdev_set_dtr_rts(dev, TRUE, FALSE); | ||
| 277 | |||
| 278 | /* Wait at least 7us */ | ||
| 279 | udelay(14); | ||
| 280 | |||
| 281 | /* Write control byte */ | ||
| 282 | sirdev_raw_write(dev, &byte, 1); | ||
| 283 | |||
| 284 | dev->speed = speed; | ||
| 285 | |||
| 286 | state = TOIM3232_STATE_WAIT_SPEED; | ||
| 287 | delay = toim3232delay; | ||
| 288 | break; | ||
| 289 | |||
| 290 | case TOIM3232_STATE_WAIT_SPEED: | ||
| 291 | /* Have transmitted control byte * Wait for 'at least 1us' */ | ||
| 292 | udelay(14); | ||
| 293 | |||
| 294 | /* Set DTR, Set RTS: Go into normal data mode */ | ||
| 295 | sirdev_set_dtr_rts(dev, TRUE, TRUE); | ||
| 296 | |||
| 297 | /* Wait (TODO: check this is needed) */ | ||
| 298 | udelay(50); | ||
| 299 | break; | ||
| 300 | |||
| 301 | default: | ||
| 302 | printk(KERN_ERR "%s - undefined state %d\n", __FUNCTION__, state); | ||
| 303 | ret = -EINVAL; | ||
| 304 | break; | ||
| 305 | } | ||
| 306 | |||
| 307 | dev->fsm.substate = state; | ||
| 308 | return (delay > 0) ? delay : ret; | ||
| 309 | } | ||
| 310 | |||
| 311 | /* | ||
| 312 | * Function toim3232reset (driver) | ||
| 313 | * | ||
| 314 | * This function resets the toim3232 dongle. Warning, this function | ||
| 315 | * must be called with a process context!! | ||
| 316 | * | ||
| 317 | * What we should do is: | ||
| 318 | * 0. Pull RESET high | ||
| 319 | * 1. Wait for at least 7us | ||
| 320 | * 2. Pull RESET low | ||
| 321 | * 3. Wait for at least 7us | ||
| 322 | * 4. Pull BR/~D high | ||
| 323 | * 5. Wait for at least 7us | ||
| 324 | * 6. Send control byte to set baud rate | ||
| 325 | * 7. Wait at least 1us after stop bit | ||
| 326 | * 8. Pull BR/~D low | ||
| 327 | * 9. Should then be in data mode | ||
| 328 | * | ||
| 329 | * Because the IR320ST-2 doesn't have the RESET line connected for some reason, | ||
| 330 | * we'll have to do something else. | ||
| 331 | * | ||
| 332 | * The default speed after a RESET is 9600, so lets try just bringing it up in | ||
| 333 | * data mode after switching it off, waiting for the supply capacitor to | ||
| 334 | * discharge, and then switch it back on. This isn't actually pulling RESET | ||
| 335 | * high, but it seems to have the same effect. | ||
| 336 | * | ||
| 337 | * This behaviour will probably work on dongles that have the RESET line connected, | ||
| 338 | * but if not, add a flag for the IR320ST-2, and implment the above-listed proper | ||
| 339 | * behaviour. | ||
| 340 | * | ||
| 341 | * RTS is inverted and then fed to BR/~D, so to put it in programming mode, we | ||
| 342 | * need to have pull RTS low | ||
| 343 | */ | ||
| 344 | |||
| 345 | static int toim3232_reset(struct sir_dev *dev) | ||
| 346 | { | ||
| 347 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__); | ||
| 348 | |||
| 349 | /* Switch off both DTR and RTS to switch off dongle */ | ||
| 350 | sirdev_set_dtr_rts(dev, FALSE, FALSE); | ||
| 351 | |||
| 352 | /* Should sleep a while. This might be evil doing it this way.*/ | ||
| 353 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 354 | schedule_timeout(msecs_to_jiffies(50)); | ||
| 355 | |||
| 356 | /* Set DTR, Set RTS (data mode) */ | ||
| 357 | sirdev_set_dtr_rts(dev, TRUE, TRUE); | ||
| 358 | |||
| 359 | /* Wait at least 10 ms for power to stabilize again */ | ||
| 360 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 361 | schedule_timeout(msecs_to_jiffies(10)); | ||
| 362 | |||
| 363 | /* Speed should now be 9600 */ | ||
| 364 | dev->speed = 9600; | ||
| 365 | |||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | |||
| 369 | MODULE_AUTHOR("David Basden <davidb-linux@rcpt.to>"); | ||
| 370 | MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver"); | ||
| 371 | MODULE_LICENSE("GPL"); | ||
| 372 | MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */ | ||
| 373 | |||
| 374 | module_init(toim3232_sir_init); | ||
| 375 | module_exit(toim3232_sir_cleanup); | ||
