linux/drivers/net/irda/toim3232-sir.c
<<
>>
Prefs
   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 transceiver.
  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#include <linux/sched.h>
 124
 125#include <net/irda/irda.h>
 126
 127#include "sir-dev.h"
 128
 129static int toim3232delay = 150; /* default is 150 ms */
 130module_param(toim3232delay, int, 0);
 131MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay");
 132
 133static int toim3232_open(struct sir_dev *);
 134static int toim3232_close(struct sir_dev *);
 135static int toim3232_change_speed(struct sir_dev *, unsigned);
 136static int toim3232_reset(struct sir_dev *);
 137
 138#define TOIM3232_115200 0x00
 139#define TOIM3232_57600  0x01
 140#define TOIM3232_38400  0x02
 141#define TOIM3232_19200  0x03
 142#define TOIM3232_9600   0x06
 143#define TOIM3232_2400   0x0A
 144
 145#define TOIM3232_PW     0x10 /* Pulse select bit */
 146
 147static struct dongle_driver toim3232 = {
 148        .owner          = THIS_MODULE,
 149        .driver_name    = "Vishay TOIM3232",
 150        .type           = IRDA_TOIM3232_DONGLE,
 151        .open           = toim3232_open,
 152        .close          = toim3232_close,
 153        .reset          = toim3232_reset,
 154        .set_speed      = toim3232_change_speed,
 155};
 156
 157static int __init toim3232_sir_init(void)
 158{
 159        if (toim3232delay < 1  ||  toim3232delay > 500)
 160                toim3232delay = 200;
 161        pr_debug("%s - using %d ms delay\n",
 162                 toim3232.driver_name, toim3232delay);
 163        return irda_register_dongle(&toim3232);
 164}
 165
 166static void __exit toim3232_sir_cleanup(void)
 167{
 168        irda_unregister_dongle(&toim3232);
 169}
 170
 171static int toim3232_open(struct sir_dev *dev)
 172{
 173        struct qos_info *qos = &dev->qos;
 174
 175        /* Pull the lines high to start with.
 176         *
 177         * For the IR320ST-2, we need to charge the main supply capacitor to
 178         * switch the device on. We keep DTR high throughout to do this.
 179         * When RTS, TD and RD are high, they will also trickle-charge the
 180         * cap. RTS is high for data transmission, and low for baud rate select.
 181         *      -- DGB
 182         */
 183        sirdev_set_dtr_rts(dev, TRUE, TRUE);
 184
 185        /* The TOI3232 supports many speeds between 1200bps and 115000bps.
 186         * We really only care about those supported by the IRDA spec, but
 187         * 38400 seems to be implemented in many places */
 188        qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
 189
 190        /* From the tekram driver. Not sure what a reasonable value is -- DGB */
 191        qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */
 192        irda_qos_bits_to_value(qos);
 193
 194        /* irda thread waits 50 msec for power settling */
 195
 196        return 0;
 197}
 198
 199static int toim3232_close(struct sir_dev *dev)
 200{
 201        /* Power off dongle */
 202        sirdev_set_dtr_rts(dev, FALSE, FALSE);
 203
 204        return 0;
 205}
 206
 207/*
 208 * Function toim3232change_speed (dev, state, speed)
 209 *
 210 *    Set the speed for the TOIM3232 based dongle. Warning, this
 211 *    function must be called with a process context!
 212 *
 213 *    Algorithm
 214 *    1. keep DTR high but clear RTS to bring into baud programming mode
 215 *    2. wait at least 7us to enter programming mode
 216 *    3. send control word to set baud rate and timing
 217 *    4. wait at least 1us
 218 *    5. bring RTS high to enter DATA mode (RS232 is passed through to transceiver)
 219 *    6. should take effect immediately (although probably worth waiting)
 220 */
 221
 222#define TOIM3232_STATE_WAIT_SPEED       (SIRDEV_STATE_DONGLE_SPEED + 1)
 223
 224static int toim3232_change_speed(struct sir_dev *dev, unsigned speed)
 225{
 226        unsigned state = dev->fsm.substate;
 227        unsigned delay = 0;
 228        u8 byte;
 229        static int ret = 0;
 230
 231        switch(state) {
 232        case SIRDEV_STATE_DONGLE_SPEED:
 233
 234                /* Figure out what we are going to send as a control byte */
 235                switch (speed) {
 236                case 2400:
 237                        byte = TOIM3232_PW|TOIM3232_2400;
 238                        break;
 239                default:
 240                        speed = 9600;
 241                        ret = -EINVAL;
 242                        /* fall thru */
 243                case 9600:
 244                        byte = TOIM3232_PW|TOIM3232_9600;
 245                        break;
 246                case 19200:
 247                        byte = TOIM3232_PW|TOIM3232_19200;
 248                        break;
 249                case 38400:
 250                        byte = TOIM3232_PW|TOIM3232_38400;
 251                        break;
 252                case 57600:
 253                        byte = TOIM3232_PW|TOIM3232_57600;
 254                        break;
 255                case 115200:
 256                        byte = TOIM3232_115200;
 257                        break;
 258                }
 259
 260                /* Set DTR, Clear RTS: Go into baud programming mode */
 261                sirdev_set_dtr_rts(dev, TRUE, FALSE);
 262
 263                /* Wait at least 7us */
 264                udelay(14);
 265
 266                /* Write control byte */
 267                sirdev_raw_write(dev, &byte, 1);
 268
 269                dev->speed = speed;
 270
 271                state = TOIM3232_STATE_WAIT_SPEED;
 272                delay = toim3232delay;
 273                break;
 274
 275        case TOIM3232_STATE_WAIT_SPEED:
 276                /* Have transmitted control byte * Wait for 'at least 1us' */
 277                udelay(14);
 278
 279                /* Set DTR, Set RTS: Go into normal data mode */
 280                sirdev_set_dtr_rts(dev, TRUE, TRUE);
 281
 282                /* Wait (TODO: check this is needed) */
 283                udelay(50);
 284                break;
 285
 286        default:
 287                printk(KERN_ERR "%s - undefined state %d\n", __func__, state);
 288                ret = -EINVAL;
 289                break;
 290        }
 291
 292        dev->fsm.substate = state;
 293        return (delay > 0) ? delay : ret;
 294}
 295
 296/*
 297 * Function toim3232reset (driver)
 298 *
 299 *      This function resets the toim3232 dongle. Warning, this function
 300 *      must be called with a process context!!
 301 *
 302 * What we should do is:
 303 *      0. Pull RESET high
 304 *      1. Wait for at least 7us
 305 *      2. Pull RESET low
 306 *      3. Wait for at least 7us
 307 *      4. Pull BR/~D high
 308 *      5. Wait for at least 7us
 309 *      6. Send control byte to set baud rate
 310 *      7. Wait at least 1us after stop bit
 311 *      8. Pull BR/~D low
 312 *      9. Should then be in data mode
 313 *
 314 * Because the IR320ST-2 doesn't have the RESET line connected for some reason,
 315 * we'll have to do something else.
 316 *
 317 * The default speed after a RESET is 9600, so lets try just bringing it up in
 318 * data mode after switching it off, waiting for the supply capacitor to
 319 * discharge, and then switch it back on. This isn't actually pulling RESET
 320 * high, but it seems to have the same effect.
 321 *
 322 * This behaviour will probably work on dongles that have the RESET line connected,
 323 * but if not, add a flag for the IR320ST-2, and implment the above-listed proper
 324 * behaviour.
 325 *
 326 * RTS is inverted and then fed to BR/~D, so to put it in programming mode, we
 327 * need to have pull RTS low
 328 */
 329
 330static int toim3232_reset(struct sir_dev *dev)
 331{
 332        /* Switch off both DTR and RTS to switch off dongle */
 333        sirdev_set_dtr_rts(dev, FALSE, FALSE);
 334
 335        /* Should sleep a while. This might be evil doing it this way.*/
 336        set_current_state(TASK_UNINTERRUPTIBLE);
 337        schedule_timeout(msecs_to_jiffies(50));
 338
 339        /* Set DTR, Set RTS (data mode) */
 340        sirdev_set_dtr_rts(dev, TRUE, TRUE);
 341
 342        /* Wait at least 10 ms for power to stabilize again */
 343        set_current_state(TASK_UNINTERRUPTIBLE);
 344        schedule_timeout(msecs_to_jiffies(10));
 345
 346        /* Speed should now be 9600 */
 347        dev->speed = 9600;
 348
 349        return 0;
 350}
 351
 352MODULE_AUTHOR("David Basden <davidb-linux@rcpt.to>");
 353MODULE_DESCRIPTION("Vishay/Temic TOIM3232 based dongle driver");
 354MODULE_LICENSE("GPL");
 355MODULE_ALIAS("irda-dongle-12"); /* IRDA_TOIM3232_DONGLE */
 356
 357module_init(toim3232_sir_init);
 358module_exit(toim3232_sir_cleanup);
 359