Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * KLSI KL5KUSB105 chip RS232 converter driver
0004  *
0005  *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
0006  *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
0007  *
0008  * All information about the device was acquired using SniffUSB ans snoopUSB
0009  * on Windows98.
0010  * It was written out of frustration with the PalmConnect USB Serial adapter
0011  * sold by Palm Inc.
0012  * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
0013  * information that was not already available.
0014  *
0015  * It seems that KLSI bought some silicon-design information from ScanLogic,
0016  * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
0017  * KLSI has firmware available for their devices; it is probable that the
0018  * firmware differs from that used by KLSI in their products. If you have an
0019  * original KLSI device and can provide some information on it, I would be
0020  * most interested in adding support for it here. If you have any information
0021  * on the protocol used (or find errors in my reverse-engineered stuff), please
0022  * let me know.
0023  *
0024  * The code was only tested with a PalmConnect USB adapter; if you
0025  * are adventurous, try it with any KLSI-based device and let me know how it
0026  * breaks so that I can fix it!
0027  */
0028 
0029 /* TODO:
0030  *  check modem line signals
0031  *  implement handshaking or decide that we do not support it
0032  */
0033 
0034 #include <linux/kernel.h>
0035 #include <linux/errno.h>
0036 #include <linux/slab.h>
0037 #include <linux/tty.h>
0038 #include <linux/tty_driver.h>
0039 #include <linux/tty_flip.h>
0040 #include <linux/module.h>
0041 #include <linux/uaccess.h>
0042 #include <asm/unaligned.h>
0043 #include <linux/usb.h>
0044 #include <linux/usb/serial.h>
0045 #include "kl5kusb105.h"
0046 
0047 #define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
0048 #define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
0049 
0050 
0051 /*
0052  * Function prototypes
0053  */
0054 static int klsi_105_port_probe(struct usb_serial_port *port);
0055 static void klsi_105_port_remove(struct usb_serial_port *port);
0056 static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
0057 static void klsi_105_close(struct usb_serial_port *port);
0058 static void klsi_105_set_termios(struct tty_struct *tty,
0059             struct usb_serial_port *port, struct ktermios *old);
0060 static int  klsi_105_tiocmget(struct tty_struct *tty);
0061 static void klsi_105_process_read_urb(struct urb *urb);
0062 static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
0063                         void *dest, size_t size);
0064 
0065 /*
0066  * All of the device info needed for the KLSI converters.
0067  */
0068 static const struct usb_device_id id_table[] = {
0069     { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
0070     { }     /* Terminating entry */
0071 };
0072 
0073 MODULE_DEVICE_TABLE(usb, id_table);
0074 
0075 static struct usb_serial_driver kl5kusb105d_device = {
0076     .driver = {
0077         .owner =    THIS_MODULE,
0078         .name =     "kl5kusb105d",
0079     },
0080     .description =      "KL5KUSB105D / PalmConnect",
0081     .id_table =     id_table,
0082     .num_ports =        1,
0083     .bulk_out_size =    64,
0084     .open =         klsi_105_open,
0085     .close =        klsi_105_close,
0086     .set_termios =      klsi_105_set_termios,
0087     .tiocmget =     klsi_105_tiocmget,
0088     .port_probe =       klsi_105_port_probe,
0089     .port_remove =      klsi_105_port_remove,
0090     .throttle =     usb_serial_generic_throttle,
0091     .unthrottle =       usb_serial_generic_unthrottle,
0092     .process_read_urb = klsi_105_process_read_urb,
0093     .prepare_write_buffer = klsi_105_prepare_write_buffer,
0094 };
0095 
0096 static struct usb_serial_driver * const serial_drivers[] = {
0097     &kl5kusb105d_device, NULL
0098 };
0099 
0100 struct klsi_105_port_settings {
0101     u8  pktlen;     /* always 5, it seems */
0102     u8  baudrate;
0103     u8  databits;
0104     u8  unknown1;
0105     u8  unknown2;
0106 };
0107 
0108 struct klsi_105_private {
0109     struct klsi_105_port_settings   cfg;
0110     unsigned long           line_state; /* modem line settings */
0111     spinlock_t          lock;
0112 };
0113 
0114 
0115 /*
0116  * Handle vendor specific USB requests
0117  */
0118 
0119 
0120 #define KLSI_TIMEOUT     5000 /* default urb timeout */
0121 
0122 static int klsi_105_chg_port_settings(struct usb_serial_port *port,
0123                       struct klsi_105_port_settings *settings)
0124 {
0125     int rc;
0126 
0127     rc = usb_control_msg_send(port->serial->dev,
0128                   0,
0129                   KL5KUSB105A_SIO_SET_DATA,
0130                   USB_TYPE_VENDOR | USB_DIR_OUT |
0131                   USB_RECIP_INTERFACE,
0132                   0, /* value */
0133                   0, /* index */
0134                   settings,
0135                   sizeof(struct klsi_105_port_settings),
0136                   KLSI_TIMEOUT,
0137                   GFP_KERNEL);
0138     if (rc)
0139         dev_err(&port->dev,
0140             "Change port settings failed (error = %d)\n", rc);
0141 
0142     dev_dbg(&port->dev,
0143         "pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n",
0144         settings->pktlen, settings->baudrate, settings->databits,
0145         settings->unknown1, settings->unknown2);
0146 
0147     return rc;
0148 }
0149 
0150 /*
0151  * Read line control via vendor command and return result through
0152  * the state pointer.
0153  */
0154 static int klsi_105_get_line_state(struct usb_serial_port *port,
0155                    unsigned long *state)
0156 {
0157     u16 status;
0158     int rc;
0159 
0160     rc = usb_control_msg_recv(port->serial->dev, 0,
0161                   KL5KUSB105A_SIO_POLL,
0162                   USB_TYPE_VENDOR | USB_DIR_IN,
0163                   0, /* value */
0164                   0, /* index */
0165                   &status, sizeof(status),
0166                   10000,
0167                   GFP_KERNEL);
0168     if (rc) {
0169         dev_err(&port->dev, "reading line status failed: %d\n", rc);
0170         return rc;
0171     }
0172 
0173     le16_to_cpus(&status);
0174 
0175     dev_dbg(&port->dev, "read status %04x\n", status);
0176 
0177     *state = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0) |
0178          ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0);
0179 
0180     return 0;
0181 }
0182 
0183 
0184 /*
0185  * Driver's tty interface functions
0186  */
0187 
0188 static int klsi_105_port_probe(struct usb_serial_port *port)
0189 {
0190     struct klsi_105_private *priv;
0191 
0192     priv = kmalloc(sizeof(*priv), GFP_KERNEL);
0193     if (!priv)
0194         return -ENOMEM;
0195 
0196     /* set initial values for control structures */
0197     priv->cfg.pktlen    = 5;
0198     priv->cfg.baudrate  = kl5kusb105a_sio_b9600;
0199     priv->cfg.databits  = kl5kusb105a_dtb_8;
0200     priv->cfg.unknown1  = 0;
0201     priv->cfg.unknown2  = 1;
0202 
0203     priv->line_state    = 0;
0204 
0205     spin_lock_init(&priv->lock);
0206 
0207     usb_set_serial_port_data(port, priv);
0208 
0209     return 0;
0210 }
0211 
0212 static void klsi_105_port_remove(struct usb_serial_port *port)
0213 {
0214     struct klsi_105_private *priv;
0215 
0216     priv = usb_get_serial_port_data(port);
0217     kfree(priv);
0218 }
0219 
0220 static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
0221 {
0222     struct klsi_105_private *priv = usb_get_serial_port_data(port);
0223     int retval = 0;
0224     int rc;
0225     unsigned long line_state;
0226     struct klsi_105_port_settings cfg;
0227     unsigned long flags;
0228 
0229     /* Do a defined restart:
0230      * Set up sane default baud rate and send the 'READ_ON'
0231      * vendor command.
0232      * FIXME: set modem line control (how?)
0233      * Then read the modem line control and store values in
0234      * priv->line_state.
0235      */
0236 
0237     cfg.pktlen   = 5;
0238     cfg.baudrate = kl5kusb105a_sio_b9600;
0239     cfg.databits = kl5kusb105a_dtb_8;
0240     cfg.unknown1 = 0;
0241     cfg.unknown2 = 1;
0242     klsi_105_chg_port_settings(port, &cfg);
0243 
0244     spin_lock_irqsave(&priv->lock, flags);
0245     priv->cfg.pktlen   = cfg.pktlen;
0246     priv->cfg.baudrate = cfg.baudrate;
0247     priv->cfg.databits = cfg.databits;
0248     priv->cfg.unknown1 = cfg.unknown1;
0249     priv->cfg.unknown2 = cfg.unknown2;
0250     spin_unlock_irqrestore(&priv->lock, flags);
0251 
0252     /* READ_ON and urb submission */
0253     rc = usb_serial_generic_open(tty, port);
0254     if (rc)
0255         return rc;
0256 
0257     rc = usb_control_msg(port->serial->dev,
0258                  usb_sndctrlpipe(port->serial->dev, 0),
0259                  KL5KUSB105A_SIO_CONFIGURE,
0260                  USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
0261                  KL5KUSB105A_SIO_CONFIGURE_READ_ON,
0262                  0, /* index */
0263                  NULL,
0264                  0,
0265                  KLSI_TIMEOUT);
0266     if (rc < 0) {
0267         dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
0268         retval = rc;
0269         goto err_generic_close;
0270     } else
0271         dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
0272 
0273     rc = klsi_105_get_line_state(port, &line_state);
0274     if (rc < 0) {
0275         retval = rc;
0276         goto err_disable_read;
0277     }
0278 
0279     spin_lock_irqsave(&priv->lock, flags);
0280     priv->line_state = line_state;
0281     spin_unlock_irqrestore(&priv->lock, flags);
0282     dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
0283             line_state);
0284 
0285     return 0;
0286 
0287 err_disable_read:
0288     usb_control_msg(port->serial->dev,
0289                  usb_sndctrlpipe(port->serial->dev, 0),
0290                  KL5KUSB105A_SIO_CONFIGURE,
0291                  USB_TYPE_VENDOR | USB_DIR_OUT,
0292                  KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
0293                  0, /* index */
0294                  NULL, 0,
0295                  KLSI_TIMEOUT);
0296 err_generic_close:
0297     usb_serial_generic_close(port);
0298 
0299     return retval;
0300 }
0301 
0302 static void klsi_105_close(struct usb_serial_port *port)
0303 {
0304     int rc;
0305 
0306     /* send READ_OFF */
0307     rc = usb_control_msg(port->serial->dev,
0308                  usb_sndctrlpipe(port->serial->dev, 0),
0309                  KL5KUSB105A_SIO_CONFIGURE,
0310                  USB_TYPE_VENDOR | USB_DIR_OUT,
0311                  KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
0312                  0, /* index */
0313                  NULL, 0,
0314                  KLSI_TIMEOUT);
0315     if (rc < 0)
0316         dev_err(&port->dev, "failed to disable read: %d\n", rc);
0317 
0318     /* shutdown our bulk reads and writes */
0319     usb_serial_generic_close(port);
0320 }
0321 
0322 /* We need to write a complete 64-byte data block and encode the
0323  * number actually sent in the first double-byte, LSB-order. That
0324  * leaves at most 62 bytes of payload.
0325  */
0326 #define KLSI_HDR_LEN        2
0327 static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
0328                         void *dest, size_t size)
0329 {
0330     unsigned char *buf = dest;
0331     int count;
0332 
0333     count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
0334                                 &port->lock);
0335     put_unaligned_le16(count, buf);
0336 
0337     return count + KLSI_HDR_LEN;
0338 }
0339 
0340 /* The data received is preceded by a length double-byte in LSB-first order.
0341  */
0342 static void klsi_105_process_read_urb(struct urb *urb)
0343 {
0344     struct usb_serial_port *port = urb->context;
0345     unsigned char *data = urb->transfer_buffer;
0346     unsigned len;
0347 
0348     /* empty urbs seem to happen, we ignore them */
0349     if (!urb->actual_length)
0350         return;
0351 
0352     if (urb->actual_length <= KLSI_HDR_LEN) {
0353         dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
0354         return;
0355     }
0356 
0357     len = get_unaligned_le16(data);
0358     if (len > urb->actual_length - KLSI_HDR_LEN) {
0359         dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__);
0360         len = urb->actual_length - KLSI_HDR_LEN;
0361     }
0362 
0363     tty_insert_flip_string(&port->port, data + KLSI_HDR_LEN, len);
0364     tty_flip_buffer_push(&port->port);
0365 }
0366 
0367 static void klsi_105_set_termios(struct tty_struct *tty,
0368                  struct usb_serial_port *port,
0369                  struct ktermios *old_termios)
0370 {
0371     struct klsi_105_private *priv = usb_get_serial_port_data(port);
0372     struct device *dev = &port->dev;
0373     unsigned int iflag = tty->termios.c_iflag;
0374     unsigned int old_iflag = old_termios->c_iflag;
0375     unsigned int cflag = tty->termios.c_cflag;
0376     unsigned int old_cflag = old_termios->c_cflag;
0377     struct klsi_105_port_settings *cfg;
0378     unsigned long flags;
0379     speed_t baud;
0380 
0381     cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
0382     if (!cfg)
0383         return;
0384 
0385     /* lock while we are modifying the settings */
0386     spin_lock_irqsave(&priv->lock, flags);
0387 
0388     /*
0389      * Update baud rate
0390      */
0391     baud = tty_get_baud_rate(tty);
0392 
0393     switch (baud) {
0394     case 0: /* handled below */
0395         break;
0396     case 1200:
0397         priv->cfg.baudrate = kl5kusb105a_sio_b1200;
0398         break;
0399     case 2400:
0400         priv->cfg.baudrate = kl5kusb105a_sio_b2400;
0401         break;
0402     case 4800:
0403         priv->cfg.baudrate = kl5kusb105a_sio_b4800;
0404         break;
0405     case 9600:
0406         priv->cfg.baudrate = kl5kusb105a_sio_b9600;
0407         break;
0408     case 19200:
0409         priv->cfg.baudrate = kl5kusb105a_sio_b19200;
0410         break;
0411     case 38400:
0412         priv->cfg.baudrate = kl5kusb105a_sio_b38400;
0413         break;
0414     case 57600:
0415         priv->cfg.baudrate = kl5kusb105a_sio_b57600;
0416         break;
0417     case 115200:
0418         priv->cfg.baudrate = kl5kusb105a_sio_b115200;
0419         break;
0420     default:
0421         dev_dbg(dev, "unsupported baudrate, using 9600\n");
0422         priv->cfg.baudrate = kl5kusb105a_sio_b9600;
0423         baud = 9600;
0424         break;
0425     }
0426 
0427     /*
0428      * FIXME: implement B0 handling
0429      *
0430      * Maybe this should be simulated by sending read disable and read
0431      * enable messages?
0432      */
0433 
0434     tty_encode_baud_rate(tty, baud, baud);
0435 
0436     if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
0437         /* set the number of data bits */
0438         switch (cflag & CSIZE) {
0439         case CS5:
0440             dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__);
0441             spin_unlock_irqrestore(&priv->lock, flags);
0442             goto err;
0443         case CS6:
0444             dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__);
0445             spin_unlock_irqrestore(&priv->lock, flags);
0446             goto err;
0447         case CS7:
0448             priv->cfg.databits = kl5kusb105a_dtb_7;
0449             break;
0450         case CS8:
0451             priv->cfg.databits = kl5kusb105a_dtb_8;
0452             break;
0453         default:
0454             dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n");
0455             priv->cfg.databits = kl5kusb105a_dtb_8;
0456             break;
0457         }
0458     }
0459 
0460     /*
0461      * Update line control register (LCR)
0462      */
0463     if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
0464         || (cflag & CSTOPB) != (old_cflag & CSTOPB)) {
0465         /* Not currently supported */
0466         tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB);
0467     }
0468     /*
0469      * Set flow control: well, I do not really now how to handle DTR/RTS.
0470      * Just do what we have seen with SniffUSB on Win98.
0471      */
0472     if ((iflag & IXOFF) != (old_iflag & IXOFF)
0473         || (iflag & IXON) != (old_iflag & IXON)
0474         ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
0475         /* Not currently supported */
0476         tty->termios.c_cflag &= ~CRTSCTS;
0477     }
0478     memcpy(cfg, &priv->cfg, sizeof(*cfg));
0479     spin_unlock_irqrestore(&priv->lock, flags);
0480 
0481     /* now commit changes to device */
0482     klsi_105_chg_port_settings(port, cfg);
0483 err:
0484     kfree(cfg);
0485 }
0486 
0487 static int klsi_105_tiocmget(struct tty_struct *tty)
0488 {
0489     struct usb_serial_port *port = tty->driver_data;
0490     struct klsi_105_private *priv = usb_get_serial_port_data(port);
0491     unsigned long flags;
0492     int rc;
0493     unsigned long line_state;
0494 
0495     rc = klsi_105_get_line_state(port, &line_state);
0496     if (rc < 0) {
0497         dev_err(&port->dev,
0498             "Reading line control failed (error = %d)\n", rc);
0499         /* better return value? EAGAIN? */
0500         return rc;
0501     }
0502 
0503     spin_lock_irqsave(&priv->lock, flags);
0504     priv->line_state = line_state;
0505     spin_unlock_irqrestore(&priv->lock, flags);
0506     dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
0507     return (int)line_state;
0508 }
0509 
0510 module_usb_serial_driver(serial_drivers, id_table);
0511 
0512 MODULE_AUTHOR(DRIVER_AUTHOR);
0513 MODULE_DESCRIPTION(DRIVER_DESC);
0514 MODULE_LICENSE("GPL");