/drivers/usb/serial/ark3116.c
C | 881 lines | 575 code | 110 blank | 196 comment | 94 complexity | cfd72c1d273869933212679e49bb5052 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
- /*
- * Copyright (C) 2009 by Bart Hartgers (bart.hartgers+ark3116@gmail.com)
- * Original version:
- * Copyright (C) 2006
- * Simon Schulz (ark3116_driver <at> auctionant.de)
- *
- * ark3116
- * - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547,
- * productid=0x0232) (used in a datacable called KQ-U8A)
- *
- * Supports full modem status lines, break, hardware flow control. Does not
- * support software flow control, since I do not know how to enable it in hw.
- *
- * This driver is a essentially new implementation. I initially dug
- * into the old ark3116.c driver and suddenly realized the ark3116 is
- * a 16450 with a USB interface glued to it. See comments at the
- * bottom of this file.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/ioctl.h>
- #include <linux/tty.h>
- #include <linux/slab.h>
- #include <linux/tty_flip.h>
- #include <linux/module.h>
- #include <linux/usb.h>
- #include <linux/usb/serial.h>
- #include <linux/serial.h>
- #include <linux/serial_reg.h>
- #include <linux/uaccess.h>
- #include <linux/mutex.h>
- #include <linux/spinlock.h>
- static int debug;
- /*
- * Version information
- */
- #define DRIVER_VERSION "v0.7"
- #define DRIVER_AUTHOR "Bart Hartgers <bart.hartgers+ark3116@gmail.com>"
- #define DRIVER_DESC "USB ARK3116 serial/IrDA driver"
- #define DRIVER_DEV_DESC "ARK3116 RS232/IrDA"
- #define DRIVER_NAME "ark3116"
- /* usb timeout of 1 second */
- #define ARK_TIMEOUT (1*HZ)
- static const struct usb_device_id id_table[] = {
- { USB_DEVICE(0x6547, 0x0232) },
- { USB_DEVICE(0x18ec, 0x3118) }, /* USB to IrDA adapter */
- { },
- };
- MODULE_DEVICE_TABLE(usb, id_table);
- static int is_irda(struct usb_serial *serial)
- {
- struct usb_device *dev = serial->dev;
- if (le16_to_cpu(dev->descriptor.idVendor) == 0x18ec &&
- le16_to_cpu(dev->descriptor.idProduct) == 0x3118)
- return 1;
- return 0;
- }
- struct ark3116_private {
- wait_queue_head_t delta_msr_wait;
- struct async_icount icount;
- int irda; /* 1 for irda device */
- /* protects hw register updates */
- struct mutex hw_lock;
- int quot; /* baudrate divisor */
- __u32 lcr; /* line control register value */
- __u32 hcr; /* handshake control register (0x8)
- * value */
- __u32 mcr; /* modem contol register value */
- /* protects the status values below */
- spinlock_t status_lock;
- __u32 msr; /* modem status register value */
- __u32 lsr; /* line status register value */
- };
- static int ark3116_write_reg(struct usb_serial *serial,
- unsigned reg, __u8 val)
- {
- int result;
- /* 0xfe 0x40 are magic values taken from original driver */
- result = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- 0xfe, 0x40, val, reg,
- NULL, 0, ARK_TIMEOUT);
- return result;
- }
- static int ark3116_read_reg(struct usb_serial *serial,
- unsigned reg, unsigned char *buf)
- {
- int result;
- /* 0xfe 0xc0 are magic values taken from original driver */
- result = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
- 0xfe, 0xc0, 0, reg,
- buf, 1, ARK_TIMEOUT);
- if (result < 0)
- return result;
- else
- return buf[0];
- }
- static inline int calc_divisor(int bps)
- {
- /* Original ark3116 made some exceptions in rounding here
- * because windows did the same. Assume that is not really
- * necessary.
- * Crystal is 12MHz, probably because of USB, but we divide by 4?
- */
- return (12000000 + 2*bps) / (4*bps);
- }
- static int ark3116_attach(struct usb_serial *serial)
- {
- struct usb_serial_port *port = serial->port[0];
- struct ark3116_private *priv;
- /* make sure we have our end-points */
- if ((serial->num_bulk_in == 0) ||
- (serial->num_bulk_out == 0) ||
- (serial->num_interrupt_in == 0)) {
- dev_err(&serial->dev->dev,
- "%s - missing endpoint - "
- "bulk in: %d, bulk out: %d, int in %d\n",
- KBUILD_MODNAME,
- serial->num_bulk_in,
- serial->num_bulk_out,
- serial->num_interrupt_in);
- return -EINVAL;
- }
- priv = kzalloc(sizeof(struct ark3116_private),
- GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- init_waitqueue_head(&priv->delta_msr_wait);
- mutex_init(&priv->hw_lock);
- spin_lock_init(&priv->status_lock);
- priv->irda = is_irda(serial);
- usb_set_serial_port_data(port, priv);
- /* setup the hardware */
- ark3116_write_reg(serial, UART_IER, 0);
- /* disable DMA */
- ark3116_write_reg(serial, UART_FCR, 0);
- /* handshake control */
- priv->hcr = 0;
- ark3116_write_reg(serial, 0x8 , 0);
- /* modem control */
- priv->mcr = 0;
- ark3116_write_reg(serial, UART_MCR, 0);
- if (!(priv->irda)) {
- ark3116_write_reg(serial, 0xb , 0);
- } else {
- ark3116_write_reg(serial, 0xb , 1);
- ark3116_write_reg(serial, 0xc , 0);
- ark3116_write_reg(serial, 0xd , 0x41);
- ark3116_write_reg(serial, 0xa , 1);
- }
- /* setup baudrate */
- ark3116_write_reg(serial, UART_LCR, UART_LCR_DLAB);
- /* setup for 9600 8N1 */
- priv->quot = calc_divisor(9600);
- ark3116_write_reg(serial, UART_DLL, priv->quot & 0xff);
- ark3116_write_reg(serial, UART_DLM, (priv->quot>>8) & 0xff);
- priv->lcr = UART_LCR_WLEN8;
- ark3116_write_reg(serial, UART_LCR, UART_LCR_WLEN8);
- ark3116_write_reg(serial, 0xe, 0);
- if (priv->irda)
- ark3116_write_reg(serial, 0x9, 0);
- dev_info(&serial->dev->dev,
- "%s using %s mode\n",
- KBUILD_MODNAME,
- priv->irda ? "IrDA" : "RS232");
- return 0;
- }
- static void ark3116_release(struct usb_serial *serial)
- {
- struct usb_serial_port *port = serial->port[0];
- struct ark3116_private *priv = usb_get_serial_port_data(port);
- /* device is closed, so URBs and DMA should be down */
- usb_set_serial_port_data(port, NULL);
- mutex_destroy(&priv->hw_lock);
- kfree(priv);
- }
- static void ark3116_init_termios(struct tty_struct *tty)
- {
- struct ktermios *termios = tty->termios;
- *termios = tty_std_termios;
- termios->c_cflag = B9600 | CS8
- | CREAD | HUPCL | CLOCAL;
- termios->c_ispeed = 9600;
- termios->c_ospeed = 9600;
- }
- static void ark3116_set_termios(struct tty_struct *tty,
- struct usb_serial_port *port,
- struct ktermios *old_termios)
- {
- struct usb_serial *serial = port->serial;
- struct ark3116_private *priv = usb_get_serial_port_data(port);
- struct ktermios *termios = tty->termios;
- unsigned int cflag = termios->c_cflag;
- int bps = tty_get_baud_rate(tty);
- int quot;
- __u8 lcr, hcr, eval;
- /* set data bit count */
- switch (cflag & CSIZE) {
- case CS5:
- lcr = UART_LCR_WLEN5;
- break;
- case CS6:
- lcr = UART_LCR_WLEN6;
- break;
- case CS7:
- lcr = UART_LCR_WLEN7;
-