2009-05-31 13:49:43 -06:00
|
|
|
/*
|
|
|
|
* This code was developed for the Quatech USB line for linux, it used
|
|
|
|
* much of the code developed by Greg Kroah-Hartman for USB serial devices
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/tty_driver.h>
|
|
|
|
#include <linux/tty_flip.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/serial.h>
|
|
|
|
#include <linux/usb.h>
|
|
|
|
#include <linux/usb/serial.h>
|
|
|
|
#include <linux/uaccess.h>
|
|
|
|
|
|
|
|
/* Version Information */
|
|
|
|
#define DRIVER_VERSION "v2.14"
|
|
|
|
#define DRIVER_AUTHOR "Tim Gobeli, Quatech, Inc"
|
|
|
|
#define DRIVER_DESC "Quatech USB to Serial Driver"
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */
|
|
|
|
#define QUATECH_SSU200 0xC030 /* SSU200 */
|
|
|
|
#define QUATECH_DSU100 0xC040 /* DSU100 */
|
|
|
|
#define QUATECH_DSU200 0xC050 /* DSU200 */
|
|
|
|
#define QUATECH_QSU100 0xC060 /* QSU100 */
|
|
|
|
#define QUATECH_QSU200 0xC070 /* QSU200 */
|
|
|
|
#define QUATECH_ESU100A 0xC080 /* ESU100A */
|
|
|
|
#define QUATECH_ESU100B 0xC081 /* ESU100B */
|
|
|
|
#define QUATECH_ESU200A 0xC0A0 /* ESU200A */
|
|
|
|
#define QUATECH_ESU200B 0xC0A1 /* ESU200B */
|
|
|
|
#define QUATECH_HSU100A 0xC090 /* HSU100A */
|
|
|
|
#define QUATECH_HSU100B 0xC091 /* HSU100B */
|
|
|
|
#define QUATECH_HSU100C 0xC092 /* HSU100C */
|
|
|
|
#define QUATECH_HSU100D 0xC093 /* HSU100D */
|
|
|
|
#define QUATECH_HSU200A 0xC0B0 /* HSU200A */
|
|
|
|
#define QUATECH_HSU200B 0xC0B1 /* HSU200B */
|
|
|
|
#define QUATECH_HSU200C 0xC0B2 /* HSU200C */
|
|
|
|
#define QUATECH_HSU200D 0xC0B3 /* HSU200D */
|
|
|
|
|
|
|
|
#define QT_SET_GET_DEVICE 0xc2
|
|
|
|
#define QT_OPEN_CLOSE_CHANNEL 0xca
|
|
|
|
#define QT_GET_SET_PREBUF_TRIG_LVL 0xcc
|
|
|
|
#define QT_SET_ATF 0xcd
|
|
|
|
#define QT_GET_SET_REGISTER 0xc0
|
|
|
|
#define QT_GET_SET_UART 0xc1
|
|
|
|
#define QT_HW_FLOW_CONTROL_MASK 0xc5
|
|
|
|
#define QT_SW_FLOW_CONTROL_MASK 0xc6
|
|
|
|
#define QT_SW_FLOW_CONTROL_DISABLE 0xc7
|
|
|
|
#define QT_BREAK_CONTROL 0xc8
|
|
|
|
|
|
|
|
#define USBD_TRANSFER_DIRECTION_IN 0xc0
|
|
|
|
#define USBD_TRANSFER_DIRECTION_OUT 0x40
|
|
|
|
|
|
|
|
#define MAX_BAUD_RATE 460800
|
|
|
|
#define MAX_BAUD_REMAINDER 4608
|
|
|
|
|
|
|
|
#define DIV_LATCH_LS 0x00
|
|
|
|
#define XMT_HOLD_REGISTER 0x00
|
|
|
|
#define XVR_BUFFER_REGISTER 0x00
|
|
|
|
#define DIV_LATCH_MS 0x01
|
|
|
|
#define FIFO_CONTROL_REGISTER 0x02
|
|
|
|
#define LINE_CONTROL_REGISTER 0x03
|
|
|
|
#define MODEM_CONTROL_REGISTER 0x04
|
|
|
|
#define LINE_STATUS_REGISTER 0x05
|
|
|
|
#define MODEM_STATUS_REGISTER 0x06
|
|
|
|
|
|
|
|
#define SERIAL_MCR_DTR 0x01
|
|
|
|
#define SERIAL_MCR_RTS 0x02
|
|
|
|
#define SERIAL_MCR_LOOP 0x10
|
|
|
|
|
|
|
|
#define SERIAL_MSR_CTS 0x10
|
|
|
|
#define SERIAL_MSR_CD 0x80
|
|
|
|
#define SERIAL_MSR_RI 0x40
|
|
|
|
#define SERIAL_MSR_DSR 0x20
|
|
|
|
#define SERIAL_MSR_MASK 0xf0
|
|
|
|
|
|
|
|
#define SERIAL_8_DATA 0x03
|
|
|
|
#define SERIAL_7_DATA 0x02
|
|
|
|
#define SERIAL_6_DATA 0x01
|
|
|
|
#define SERIAL_5_DATA 0x00
|
|
|
|
|
|
|
|
#define SERIAL_ODD_PARITY 0X08
|
|
|
|
#define SERIAL_EVEN_PARITY 0X18
|
|
|
|
#define SERIAL_TWO_STOPB 0x04
|
|
|
|
#define SERIAL_ONE_STOPB 0x00
|
|
|
|
|
|
|
|
#define DEFAULT_DIVISOR 0x30 /* gives 9600 baud rate */
|
|
|
|
#define DEFAULT_LCR SERIAL_8_DATA /* 8, none , 1 */
|
|
|
|
|
|
|
|
#define FULLPWRBIT 0x00000080
|
|
|
|
#define NEXT_BOARD_POWER_BIT 0x00000004
|
|
|
|
|
|
|
|
#define SERIAL_LSR_OE 0x02
|
|
|
|
#define SERIAL_LSR_PE 0x04
|
|
|
|
#define SERIAL_LSR_FE 0x08
|
|
|
|
#define SERIAL_LSR_BI 0x10
|
|
|
|
|
|
|
|
#define SERIAL_MSR_CTS 0x10
|
|
|
|
#define SERIAL_MSR_CD 0x80
|
|
|
|
#define SERIAL_MSR_RI 0x40
|
|
|
|
#define SERIAL_MSR_DSR 0x20
|
|
|
|
#define SERIAL_MSR_MASK 0xf0
|
|
|
|
|
|
|
|
#define PREFUFF_LEVEL_CONSERVATIVE 128
|
|
|
|
#define ATC_DISABLED 0x0
|
|
|
|
|
|
|
|
#define RR_BITS 0x03 /* for clearing clock bits */
|
|
|
|
#define DUPMODE_BITS 0xc0
|
|
|
|
#define CLKS_X4 0x02
|
|
|
|
|
|
|
|
#define LOOPMODE_BITS 0x41 /* LOOP1 = b6, LOOP0 = b0 (PORT B) */
|
|
|
|
#define ALL_LOOPBACK 0x01
|
|
|
|
#define MODEM_CTRL 0x40
|
|
|
|
#define RS232_MODE 0x00
|
|
|
|
|
2012-05-08 16:46:14 -06:00
|
|
|
static const struct usb_device_id id_table[] = {
|
2009-05-31 13:49:43 -06:00
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU200)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_DSU100)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_DSU200)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_QSU100)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_QSU200)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_ESU100A)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_ESU100B)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_ESU200A)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_ESU200B)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU100A)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU100B)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU100C)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU100D)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU200A)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU200B)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU200C)},
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_HSU200D)},
|
|
|
|
{} /* Terminating entry */
|
|
|
|
};
|
2012-05-08 16:46:14 -06:00
|
|
|
MODULE_DEVICE_TABLE(usb, id_table);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
struct qt_get_device_data {
|
|
|
|
__u8 porta;
|
|
|
|
__u8 portb;
|
|
|
|
__u8 portc;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct qt_open_channel_data {
|
|
|
|
__u8 line_status;
|
|
|
|
__u8 modem_status;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct quatech_port {
|
|
|
|
int port_num; /* number of the port */
|
|
|
|
struct urb *write_urb; /* write URB for this port */
|
|
|
|
struct urb *read_urb; /* read URB for this port */
|
|
|
|
struct urb *int_urb;
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
__u8 shadow_lcr; /* last LCR value received */
|
|
|
|
__u8 shadow_mcr; /* last MCR value received */
|
|
|
|
__u8 shadow_msr; /* last MSR value received */
|
|
|
|
__u8 shadow_lsr; /* last LSR value received */
|
2009-05-31 13:49:43 -06:00
|
|
|
char open_ports;
|
|
|
|
|
|
|
|
/* Used for TIOCMIWAIT */
|
|
|
|
wait_queue_head_t msr_wait;
|
|
|
|
char prev_status, diff_status;
|
|
|
|
|
|
|
|
wait_queue_head_t wait;
|
|
|
|
|
|
|
|
struct async_icount icount;
|
|
|
|
|
|
|
|
struct usb_serial_port *port; /* owner of this object */
|
2013-06-14 08:55:57 -06:00
|
|
|
struct qt_get_device_data device_data;
|
2011-08-09 00:23:42 -06:00
|
|
|
struct mutex lock;
|
2009-05-31 13:49:43 -06:00
|
|
|
bool read_urb_busy;
|
2013-06-14 08:55:57 -06:00
|
|
|
int rx_holding;
|
|
|
|
int read_bulk_stopped;
|
|
|
|
char close_pending;
|
2009-05-31 13:49:43 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
static int port_paranoia_check(struct usb_serial_port *port,
|
|
|
|
const char *function)
|
|
|
|
{
|
|
|
|
if (!port) {
|
2012-09-18 02:35:12 -06:00
|
|
|
pr_debug("%s - port == NULL", function);
|
2009-05-31 13:49:43 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!port->serial) {
|
2012-09-18 02:35:12 -06:00
|
|
|
pr_debug("%s - port->serial == NULL\n", function);
|
2009-05-31 13:49:43 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int serial_paranoia_check(struct usb_serial *serial,
|
|
|
|
const char *function)
|
|
|
|
{
|
|
|
|
if (!serial) {
|
2012-09-18 02:35:12 -06:00
|
|
|
pr_debug("%s - serial == NULL\n", function);
|
2009-05-31 13:49:43 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!serial->type) {
|
2012-09-18 02:35:12 -06:00
|
|
|
pr_debug("%s - serial->type == NULL!", function);
|
2009-05-31 13:49:43 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct quatech_port *qt_get_port_private(struct usb_serial_port
|
|
|
|
*port)
|
|
|
|
{
|
|
|
|
return (struct quatech_port *)usb_get_serial_port_data(port);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void qt_set_port_private(struct usb_serial_port *port,
|
|
|
|
struct quatech_port *data)
|
|
|
|
{
|
|
|
|
usb_set_serial_port_data(port, (void *)data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct usb_serial *get_usb_serial(struct usb_serial_port *port,
|
|
|
|
const char *function)
|
|
|
|
{
|
|
|
|
/* if no port was specified, or it fails a paranoia check */
|
|
|
|
if (!port ||
|
|
|
|
port_paranoia_check(port, function) ||
|
|
|
|
serial_paranoia_check(port->serial, function)) {
|
|
|
|
/*
|
|
|
|
* then say that we dont have a valid usb_serial thing,
|
|
|
|
* which will end up genrating -ENODEV return values
|
|
|
|
*/
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return port->serial;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
static void process_line_status(struct quatech_port *qt_port,
|
2009-05-31 13:49:43 -06:00
|
|
|
unsigned char line_status)
|
|
|
|
{
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_port->shadow_lsr =
|
2009-05-31 13:49:43 -06:00
|
|
|
line_status & (SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE |
|
|
|
|
SERIAL_LSR_BI);
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
static void process_modem_status(struct quatech_port *qt_port,
|
2009-05-31 13:49:43 -06:00
|
|
|
unsigned char modem_status)
|
|
|
|
{
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_port->shadow_msr = modem_status;
|
2009-05-31 13:49:43 -06:00
|
|
|
wake_up_interruptible(&qt_port->wait);
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
static void process_rx_char(struct usb_serial_port *port, unsigned char data)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
struct urb *urb = port->read_urb;
|
2009-06-22 11:42:36 -06:00
|
|
|
if (urb->actual_length)
|
2013-01-03 07:53:03 -07:00
|
|
|
tty_insert_flip_char(&port->port, data, TTY_NORMAL);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qt_write_bulk_callback(struct urb *urb)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct quatech_port *quatech_port;
|
|
|
|
|
|
|
|
status = urb->status;
|
|
|
|
|
|
|
|
if (status) {
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&urb->dev->dev,
|
|
|
|
"nonzero write bulk status received:%d\n", status);
|
2009-05-31 13:49:43 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
quatech_port = urb->context;
|
|
|
|
|
2013-03-07 05:12:29 -07:00
|
|
|
tty_port_tty_wakeup(&quatech_port->port->port);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qt_interrupt_callback(struct urb *urb)
|
|
|
|
{
|
|
|
|
/* FIXME */
|
|
|
|
}
|
|
|
|
|
2013-01-03 07:53:06 -07:00
|
|
|
static void qt_status_change_check(struct urb *urb,
|
2012-11-09 14:33:56 -07:00
|
|
|
struct quatech_port *qt_port,
|
|
|
|
struct usb_serial_port *port)
|
|
|
|
{
|
|
|
|
int flag, i;
|
|
|
|
unsigned char *data = urb->transfer_buffer;
|
2013-06-14 08:55:57 -06:00
|
|
|
unsigned int rx_count = urb->actual_length;
|
2012-11-09 14:33:56 -07:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
for (i = 0; i < rx_count; ++i) {
|
2012-11-09 14:33:56 -07:00
|
|
|
/* Look ahead code here */
|
2013-06-14 08:55:57 -06:00
|
|
|
if ((i <= (rx_count - 3)) && (data[i] == 0x1b)
|
2012-11-09 14:33:56 -07:00
|
|
|
&& (data[i + 1] == 0x1b)) {
|
|
|
|
flag = 0;
|
|
|
|
switch (data[i + 2]) {
|
|
|
|
case 0x00:
|
2013-06-14 08:55:57 -06:00
|
|
|
if (i > (rx_count - 4)) {
|
2012-11-16 13:19:22 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"Illegal escape seuences in received data\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
process_line_status(qt_port, data[i + 3]);
|
2012-11-16 13:19:22 -07:00
|
|
|
|
|
|
|
i += 3;
|
|
|
|
flag = 1;
|
|
|
|
break;
|
|
|
|
|
2012-11-09 14:33:56 -07:00
|
|
|
case 0x01:
|
2013-06-14 08:55:57 -06:00
|
|
|
if (i > (rx_count - 4)) {
|
2012-11-16 13:19:22 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"Illegal escape seuences in received data\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
process_modem_status(qt_port, data[i + 3]);
|
2012-11-16 13:19:22 -07:00
|
|
|
|
|
|
|
i += 3;
|
|
|
|
flag = 1;
|
2012-11-09 14:33:56 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xff:
|
|
|
|
dev_dbg(&port->dev, "No status sequence.\n");
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
process_rx_char(port, data[i]);
|
|
|
|
process_rx_char(port, data[i + 1]);
|
2012-11-09 14:33:56 -07:00
|
|
|
|
|
|
|
i += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (flag == 1)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-01-03 07:53:03 -07:00
|
|
|
if (urb->actual_length)
|
|
|
|
tty_insert_flip_char(&port->port, data[i], TTY_NORMAL);
|
2012-11-09 14:33:56 -07:00
|
|
|
|
|
|
|
}
|
2013-01-03 07:53:06 -07:00
|
|
|
tty_flip_buffer_push(&port->port);
|
2012-11-09 14:33:56 -07:00
|
|
|
}
|
|
|
|
|
2009-05-31 13:49:43 -06:00
|
|
|
static void qt_read_bulk_callback(struct urb *urb)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct usb_serial_port *port = urb->context;
|
|
|
|
struct usb_serial *serial = get_usb_serial(port, __func__);
|
|
|
|
struct quatech_port *qt_port = qt_get_port_private(port);
|
2012-11-09 14:33:56 -07:00
|
|
|
int result;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (urb->status) {
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_port->read_bulk_stopped = 1;
|
2012-11-09 14:33:56 -07:00
|
|
|
dev_dbg(&urb->dev->dev,
|
|
|
|
"%s - nonzero write bulk status received: %d\n",
|
2012-09-18 02:35:12 -06:00
|
|
|
__func__, urb->status);
|
2009-05-31 13:49:43 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
2013-06-14 08:55:57 -06:00
|
|
|
"%s - port->rx_holding = %d\n", __func__, qt_port->rx_holding);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (port_paranoia_check(port, __func__) != 0) {
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_port->read_bulk_stopped = 1;
|
2013-01-03 07:53:07 -07:00
|
|
|
return;
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
2012-09-18 02:35:12 -06:00
|
|
|
if (!serial)
|
2013-01-03 07:53:07 -07:00
|
|
|
return;
|
2012-09-18 02:35:12 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
if (qt_port->close_pending == 1) {
|
2009-05-31 13:49:43 -06:00
|
|
|
/* Were closing , stop reading */
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
2013-06-14 08:55:57 -06:00
|
|
|
"%s - (qt_port->close_pending == 1\n", __func__);
|
|
|
|
qt_port->read_bulk_stopped = 1;
|
2013-01-03 07:53:07 -07:00
|
|
|
return;
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-06-14 08:55:57 -06:00
|
|
|
* rx_holding is asserted by throttle, if we assert it, we're not
|
2009-05-31 13:49:43 -06:00
|
|
|
* receiving any more characters and let the box handle the flow
|
|
|
|
* control
|
|
|
|
*/
|
2013-06-14 08:55:57 -06:00
|
|
|
if (qt_port->rx_holding == 1) {
|
|
|
|
qt_port->read_bulk_stopped = 1;
|
2013-01-03 07:53:07 -07:00
|
|
|
return;
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (urb->status) {
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_port->read_bulk_stopped = 1;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2012-11-09 14:33:56 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"%s - nonzero read bulk status received: %d\n",
|
2012-09-18 02:35:12 -06:00
|
|
|
__func__, urb->status);
|
2013-01-03 07:53:07 -07:00
|
|
|
return;
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
2012-11-09 14:33:56 -07:00
|
|
|
if (urb->actual_length)
|
2013-01-03 07:53:06 -07:00
|
|
|
qt_status_change_check(urb, qt_port, port);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* Continue trying to always read */
|
|
|
|
usb_fill_bulk_urb(port->read_urb, serial->dev,
|
|
|
|
usb_rcvbulkpipe(serial->dev,
|
|
|
|
port->bulk_in_endpointAddress),
|
|
|
|
port->read_urb->transfer_buffer,
|
|
|
|
port->read_urb->transfer_buffer_length,
|
|
|
|
qt_read_bulk_callback, port);
|
|
|
|
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
|
|
|
|
if (result)
|
2012-11-09 14:33:56 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"%s - failed resubmitting read urb, error %d",
|
2012-09-18 02:35:12 -06:00
|
|
|
__func__, result);
|
2009-05-31 13:49:43 -06:00
|
|
|
else {
|
2012-11-09 14:33:56 -07:00
|
|
|
if (urb->actual_length) {
|
2013-01-03 07:53:06 -07:00
|
|
|
tty_flip_buffer_push(&port->port);
|
2013-01-03 07:53:07 -07:00
|
|
|
tty_schedule_flip(&port->port);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
schedule_work(&port->work);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qt_get_device
|
|
|
|
* Issue a GET_DEVICE vendor-specific request on the default control pipe If
|
|
|
|
* successful, fills in the qt_get_device_data structure pointed to by
|
|
|
|
* device_data, otherwise return a negative error number of the problem.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int qt_get_device(struct usb_serial *serial,
|
|
|
|
struct qt_get_device_data *device_data)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
unsigned char *transfer_buffer;
|
|
|
|
|
|
|
|
transfer_buffer =
|
|
|
|
kmalloc(sizeof(struct qt_get_device_data), GFP_KERNEL);
|
|
|
|
if (!transfer_buffer)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
|
|
|
QT_SET_GET_DEVICE, 0xc0, 0, 0,
|
|
|
|
transfer_buffer,
|
|
|
|
sizeof(struct qt_get_device_data), 300);
|
|
|
|
if (result > 0)
|
|
|
|
memcpy(device_data, transfer_buffer,
|
|
|
|
sizeof(struct qt_get_device_data));
|
|
|
|
kfree(transfer_buffer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2013-06-14 08:55:57 -06:00
|
|
|
* box_set_prebuffer_level
|
2009-05-31 13:49:43 -06:00
|
|
|
TELLS BOX WHEN TO ASSERT FLOW CONTROL
|
|
|
|
****************************************************************************/
|
2013-06-14 08:55:57 -06:00
|
|
|
static int box_set_prebuffer_level(struct usb_serial *serial)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
__u16 buffer_length;
|
|
|
|
|
|
|
|
buffer_length = PREFUFF_LEVEL_CONSERVATIVE;
|
|
|
|
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
|
|
|
QT_GET_SET_PREBUF_TRIG_LVL, 0x40,
|
|
|
|
buffer_length, 0, NULL, 0, 300);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2013-06-14 08:55:57 -06:00
|
|
|
* box_set_atc
|
2009-05-31 13:49:43 -06:00
|
|
|
TELLS BOX WHEN TO ASSERT automatic transmitter control
|
|
|
|
****************************************************************************/
|
2013-06-14 08:55:57 -06:00
|
|
|
static int box_set_atc(struct usb_serial *serial, __u16 n_mode)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
__u16 buffer_length;
|
|
|
|
|
|
|
|
buffer_length = PREFUFF_LEVEL_CONSERVATIVE;
|
|
|
|
|
|
|
|
result =
|
|
|
|
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
2013-06-14 08:55:57 -06:00
|
|
|
QT_SET_ATF, 0x40, n_mode, 0, NULL, 0, 300);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qt_set_device
|
|
|
|
* Issue a SET_DEVICE vendor-specific request on the default control pipe If
|
|
|
|
* successful returns the number of bytes written, otherwise it returns a
|
|
|
|
* negative error number of the problem.
|
|
|
|
*/
|
|
|
|
static int qt_set_device(struct usb_serial *serial,
|
|
|
|
struct qt_get_device_data *device_data)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
__u16 length;
|
2013-06-14 08:55:57 -06:00
|
|
|
__u16 port_settings;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
port_settings = ((__u16) (device_data->portb));
|
|
|
|
port_settings = (port_settings << 8);
|
|
|
|
port_settings += ((__u16) (device_data->porta));
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
length = sizeof(struct qt_get_device_data);
|
|
|
|
|
|
|
|
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
2013-06-14 08:55:57 -06:00
|
|
|
QT_SET_GET_DEVICE, 0x40, port_settings,
|
2009-05-31 13:49:43 -06:00
|
|
|
0, NULL, 0, 300);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:58 -06:00
|
|
|
static int qt_open_channel(struct usb_serial *serial, __u16 uart_num,
|
2013-06-14 08:55:57 -06:00
|
|
|
struct qt_open_channel_data *pdevice_data)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
|
|
|
QT_OPEN_CLOSE_CHANNEL,
|
2013-06-14 08:55:58 -06:00
|
|
|
USBD_TRANSFER_DIRECTION_IN, 1, uart_num,
|
2013-06-14 08:55:57 -06:00
|
|
|
pdevice_data,
|
2009-05-31 13:49:43 -06:00
|
|
|
sizeof(struct qt_open_channel_data), 300);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:58 -06:00
|
|
|
static int qt_close_channel(struct usb_serial *serial, __u16 uart_num)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
|
|
|
QT_OPEN_CLOSE_CHANNEL,
|
2013-06-14 08:55:58 -06:00
|
|
|
USBD_TRANSFER_DIRECTION_OUT, 0, uart_num,
|
2009-05-31 13:49:43 -06:00
|
|
|
NULL, 0, 300);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2013-06-14 08:55:57 -06:00
|
|
|
* box_get_register
|
2009-05-31 13:49:43 -06:00
|
|
|
* issuse a GET_REGISTER vendor-spcific request on the default control pipe
|
2013-06-14 08:55:57 -06:00
|
|
|
* If successful, fills in the p_value with the register value asked for
|
2009-05-31 13:49:43 -06:00
|
|
|
****************************************************************************/
|
2013-06-14 08:55:58 -06:00
|
|
|
static int box_get_register(struct usb_serial *serial, unsigned short uart_num,
|
2013-06-14 08:55:57 -06:00
|
|
|
unsigned short register_num, __u8 *p_value)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
__u16 current_length;
|
|
|
|
|
|
|
|
current_length = sizeof(struct qt_get_device_data);
|
|
|
|
|
|
|
|
result =
|
|
|
|
usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
2013-06-14 08:55:57 -06:00
|
|
|
QT_GET_SET_REGISTER, 0xC0, register_num,
|
2013-06-14 08:55:58 -06:00
|
|
|
uart_num, (void *)p_value, sizeof(*p_value), 300);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
2013-06-14 08:55:57 -06:00
|
|
|
* box_set_register
|
2009-05-31 13:49:43 -06:00
|
|
|
* issuse a GET_REGISTER vendor-spcific request on the default control pipe
|
2013-06-14 08:55:57 -06:00
|
|
|
* If successful, fills in the p_value with the register value asked for
|
2009-05-31 13:49:43 -06:00
|
|
|
****************************************************************************/
|
2013-06-14 08:55:58 -06:00
|
|
|
static int box_set_register(struct usb_serial *serial, unsigned short uart_num,
|
2013-06-14 08:55:57 -06:00
|
|
|
unsigned short register_num, unsigned short value)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
2013-06-14 08:55:57 -06:00
|
|
|
unsigned short reg_and_byte;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
reg_and_byte = value;
|
|
|
|
reg_and_byte = reg_and_byte << 8;
|
|
|
|
reg_and_byte = reg_and_byte + register_num;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
2013-06-14 08:55:57 -06:00
|
|
|
QT_GET_SET_REGISTER, 0xC0, register_num,
|
2013-06-14 08:55:58 -06:00
|
|
|
uart_num, NULL, 0, 300);
|
2009-05-31 13:49:43 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
result =
|
|
|
|
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
2013-06-14 08:55:58 -06:00
|
|
|
QT_GET_SET_REGISTER, 0x40, reg_and_byte, uart_num,
|
2009-05-31 13:49:43 -06:00
|
|
|
NULL, 0, 300);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* qt_setuart
|
2012-06-18 08:42:23 -06:00
|
|
|
* issues a SET_UART vendor-specific request on the default control pipe
|
2009-05-31 13:49:43 -06:00
|
|
|
* If successful sets baud rate divisor and LCR value
|
|
|
|
*/
|
2013-06-14 08:55:58 -06:00
|
|
|
static int qt_setuart(struct usb_serial *serial, unsigned short uart_num,
|
2013-06-14 08:55:57 -06:00
|
|
|
unsigned short default_divisor, unsigned char default_lcr)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
2013-06-14 08:55:57 -06:00
|
|
|
unsigned short uart_num_and_lcr;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:58 -06:00
|
|
|
uart_num_and_lcr = (default_lcr << 8) + uart_num;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
result =
|
|
|
|
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
|
|
|
QT_GET_SET_UART, 0x40, default_divisor,
|
2013-06-14 08:55:57 -06:00
|
|
|
uart_num_and_lcr, NULL, 0, 300);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
static int box_set_hw_flow_ctrl(struct usb_serial *serial, unsigned int index,
|
|
|
|
int b_set)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
__u8 mcr = 0;
|
2013-06-14 08:55:57 -06:00
|
|
|
__u8 msr = 0, mout_value = 0;
|
2009-05-31 13:49:43 -06:00
|
|
|
unsigned int status;
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
if (b_set == 1) {
|
2009-05-31 13:49:43 -06:00
|
|
|
/* flow control, box will clear RTS line to prevent remote */
|
|
|
|
mcr = SERIAL_MCR_RTS;
|
|
|
|
} /* device from xmitting more chars */
|
|
|
|
else {
|
|
|
|
/* no flow control to remote device */
|
|
|
|
mcr = 0;
|
|
|
|
|
|
|
|
}
|
2013-06-14 08:55:57 -06:00
|
|
|
mout_value = mcr << 8;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
if (b_set == 1) {
|
2009-05-31 13:49:43 -06:00
|
|
|
/* flow control, box will inhibit xmit data if CTS line is
|
|
|
|
* asserted */
|
|
|
|
msr = SERIAL_MSR_CTS;
|
|
|
|
} else {
|
|
|
|
/* Box will not inhimbe xmit data due to CTS line */
|
|
|
|
msr = 0;
|
|
|
|
}
|
2013-06-14 08:55:57 -06:00
|
|
|
mout_value |= msr;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
status =
|
|
|
|
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
2013-06-14 08:55:57 -06:00
|
|
|
QT_HW_FLOW_CONTROL_MASK, 0x40, mout_value,
|
2009-05-31 13:49:43 -06:00
|
|
|
index, NULL, 0, 300);
|
|
|
|
return status;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
static int box_set_sw_flow_ctrl(struct usb_serial *serial, __u16 index,
|
2009-05-31 13:49:43 -06:00
|
|
|
unsigned char stop_char, unsigned char start_char)
|
|
|
|
{
|
2013-06-14 08:55:57 -06:00
|
|
|
__u16 n_sw_flow_out;
|
2009-05-31 13:49:43 -06:00
|
|
|
int result;
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
n_sw_flow_out = start_char << 8;
|
|
|
|
n_sw_flow_out = (unsigned short)stop_char;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
result =
|
|
|
|
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
2013-06-14 08:55:57 -06:00
|
|
|
QT_SW_FLOW_CONTROL_MASK, 0x40, n_sw_flow_out,
|
2009-05-31 13:49:43 -06:00
|
|
|
index, NULL, 0, 300);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
static int box_disable_sw_flow_ctrl(struct usb_serial *serial, __u16 index)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
result =
|
|
|
|
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
|
|
|
QT_SW_FLOW_CONTROL_DISABLE, 0x40, 0, index,
|
|
|
|
NULL, 0, 300);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qt_startup(struct usb_serial *serial)
|
|
|
|
{
|
2012-09-18 02:35:12 -06:00
|
|
|
struct device *dev = &serial->dev->dev;
|
2009-05-31 13:49:43 -06:00
|
|
|
struct usb_serial_port *port;
|
|
|
|
struct quatech_port *qt_port;
|
2013-06-14 08:55:57 -06:00
|
|
|
struct qt_get_device_data device_data;
|
2009-05-31 13:49:43 -06:00
|
|
|
int i;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
/* Now setup per port private data */
|
|
|
|
for (i = 0; i < serial->num_ports; i++) {
|
|
|
|
port = serial->port[i];
|
|
|
|
qt_port = kzalloc(sizeof(*qt_port), GFP_KERNEL);
|
|
|
|
if (!qt_port) {
|
2010-04-11 04:22:53 -06:00
|
|
|
for (--i; i >= 0; i--) {
|
2009-07-13 02:46:57 -06:00
|
|
|
port = serial->port[i];
|
|
|
|
kfree(usb_get_serial_port_data(port));
|
|
|
|
usb_set_serial_port_data(port, NULL);
|
|
|
|
}
|
2009-05-31 13:49:43 -06:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_init(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
usb_set_serial_port_data(port, qt_port);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
status = qt_get_device(serial, &device_data);
|
2012-09-18 02:35:12 -06:00
|
|
|
if (status < 0)
|
2009-05-31 13:49:43 -06:00
|
|
|
goto startup_error;
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
dev_dbg(dev, "device_data.portb = 0x%x\n", device_data.portb);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
device_data.portb &= ~FULLPWRBIT;
|
|
|
|
dev_dbg(dev, "Changing device_data.portb to 0x%x\n", device_data.portb);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
status = qt_set_device(serial, &device_data);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(dev, "qt_set_device failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
goto startup_error;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
status = qt_get_device(serial, &device_data);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(dev, "qt_get_device failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
goto startup_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (serial->dev->descriptor.idProduct) {
|
|
|
|
case QUATECH_DSU100:
|
|
|
|
case QUATECH_QSU100:
|
|
|
|
case QUATECH_ESU100A:
|
|
|
|
case QUATECH_ESU100B:
|
|
|
|
case QUATECH_HSU100A:
|
|
|
|
case QUATECH_HSU100B:
|
|
|
|
case QUATECH_HSU100C:
|
|
|
|
case QUATECH_HSU100D:
|
2013-06-14 08:55:57 -06:00
|
|
|
device_data.porta &= ~(RR_BITS | DUPMODE_BITS);
|
|
|
|
device_data.porta |= CLKS_X4;
|
|
|
|
device_data.portb &= ~(LOOPMODE_BITS);
|
|
|
|
device_data.portb |= RS232_MODE;
|
2009-05-31 13:49:43 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case QUATECH_SSU200:
|
|
|
|
case QUATECH_DSU200:
|
|
|
|
case QUATECH_QSU200:
|
|
|
|
case QUATECH_ESU200A:
|
|
|
|
case QUATECH_ESU200B:
|
|
|
|
case QUATECH_HSU200A:
|
|
|
|
case QUATECH_HSU200B:
|
|
|
|
case QUATECH_HSU200C:
|
|
|
|
case QUATECH_HSU200D:
|
2013-06-14 08:55:57 -06:00
|
|
|
device_data.porta &= ~(RR_BITS | DUPMODE_BITS);
|
|
|
|
device_data.porta |= CLKS_X4;
|
|
|
|
device_data.portb &= ~(LOOPMODE_BITS);
|
|
|
|
device_data.portb |= ALL_LOOPBACK;
|
2009-05-31 13:49:43 -06:00
|
|
|
break;
|
|
|
|
default:
|
2013-06-14 08:55:57 -06:00
|
|
|
device_data.porta &= ~(RR_BITS | DUPMODE_BITS);
|
|
|
|
device_data.porta |= CLKS_X4;
|
|
|
|
device_data.portb &= ~(LOOPMODE_BITS);
|
|
|
|
device_data.portb |= RS232_MODE;
|
2009-05-31 13:49:43 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
status = box_set_prebuffer_level(serial); /* sets to default value */
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0) {
|
2013-06-14 08:55:57 -06:00
|
|
|
dev_dbg(dev, "box_set_prebuffer_level failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
goto startup_error;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
status = box_set_atc(serial, ATC_DISABLED);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0) {
|
2013-06-14 08:55:57 -06:00
|
|
|
dev_dbg(dev, "box_set_atc failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
goto startup_error;
|
|
|
|
}
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
dev_dbg(dev, "device_data.portb = 0x%x\n", device_data.portb);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
device_data.portb |= NEXT_BOARD_POWER_BIT;
|
|
|
|
dev_dbg(dev, "Changing device_data.portb to 0x%x\n", device_data.portb);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
status = qt_set_device(serial, &device_data);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(dev, "qt_set_device failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
goto startup_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
startup_error:
|
|
|
|
for (i = 0; i < serial->num_ports; i++) {
|
|
|
|
port = serial->port[i];
|
|
|
|
qt_port = qt_get_port_private(port);
|
|
|
|
kfree(qt_port);
|
|
|
|
usb_set_serial_port_data(port, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qt_release(struct usb_serial *serial)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port;
|
|
|
|
struct quatech_port *qt_port;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < serial->num_ports; i++) {
|
|
|
|
port = serial->port[i];
|
|
|
|
if (!port)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
qt_port = usb_get_serial_port_data(port);
|
|
|
|
kfree(qt_port);
|
|
|
|
usb_set_serial_port_data(port, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-11-09 14:34:11 -07:00
|
|
|
static void qt_submit_urb_from_open(struct usb_serial *serial,
|
|
|
|
struct usb_serial_port *port)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
struct usb_serial_port *port0 = serial->port[0];
|
|
|
|
|
|
|
|
/* set up interrupt urb */
|
|
|
|
usb_fill_int_urb(port0->interrupt_in_urb,
|
|
|
|
serial->dev,
|
|
|
|
usb_rcvintpipe(serial->dev,
|
|
|
|
port0->interrupt_in_endpointAddress),
|
|
|
|
port0->interrupt_in_buffer,
|
|
|
|
port0->interrupt_in_urb->transfer_buffer_length,
|
|
|
|
qt_interrupt_callback, serial,
|
|
|
|
port0->interrupt_in_urb->interval);
|
|
|
|
|
|
|
|
result = usb_submit_urb(port0->interrupt_in_urb,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (result) {
|
|
|
|
dev_err(&port->dev,
|
|
|
|
"%s - Error %d submitting interrupt urb\n",
|
|
|
|
__func__, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-19 10:32:57 -06:00
|
|
|
static int qt_open(struct tty_struct *tty,
|
2009-08-06 13:22:01 -06:00
|
|
|
struct usb_serial_port *port)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
struct usb_serial *serial;
|
|
|
|
struct quatech_port *quatech_port;
|
|
|
|
struct quatech_port *port0;
|
2013-06-14 08:55:57 -06:00
|
|
|
struct qt_open_channel_data channel_data;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
int result;
|
|
|
|
|
|
|
|
if (port_paranoia_check(port, __func__))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
serial = port->serial;
|
|
|
|
|
|
|
|
if (serial_paranoia_check(serial, __func__))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
quatech_port = qt_get_port_private(port);
|
|
|
|
port0 = qt_get_port_private(serial->port[0]);
|
|
|
|
|
|
|
|
if (quatech_port == NULL || port0 == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
usb_clear_halt(serial->dev, port->write_urb->pipe);
|
|
|
|
usb_clear_halt(serial->dev, port->read_urb->pipe);
|
|
|
|
port0->open_ports++;
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
result = qt_get_device(serial, &port0->device_data);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* Port specific setups */
|
Staging tree merge for 3.11-rc1
Here's the large staging tree merge for 3.11-rc1
Huge thing here is the Lustre client code. Unfortunatly, due to it not
building properly on a wide variety of different architectures (this was
production code???), it is currently disabled from the build so as to
not annoy people.
Other than Lustre, there are loads of comedi patches, working to clean
up that subsystem, iio updates and new drivers, and a load of cleanups
from the OPW applicants in their quest to get a summer internship.
All of these have been in the linux-next releases for a while (hence the
Lustre code being disabled.)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.19 (GNU/Linux)
iEYEABECAAYFAlHRsacACgkQMUfUDdst+yklBQCgzOUv0SlVYNhRj9KDDu1vxWTG
FnYAnRhRG9oXXkocjH6/cu+YOf21ClpZ
=WDt3
-----END PGP SIGNATURE-----
Merge tag 'staging-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging tree update from Greg KH:
"Here's the large staging tree merge for 3.11-rc1
Huge thing here is the Lustre client code. Unfortunatly, due to it
not building properly on a wide variety of different architectures
(this was production code???), it is currently disabled from the build
so as to not annoy people.
Other than Lustre, there are loads of comedi patches, working to clean
up that subsystem, iio updates and new drivers, and a load of cleanups
from the OPW applicants in their quest to get a summer internship.
All of these have been in the linux-next releases for a while (hence
the Lustre code being disabled)"
Fixed up trivial conflict in drivers/staging/serqt_usb2/serqt_usb2.c due
to independent renamings in the staging driver cleanup and the USB
tree..
* tag 'staging-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (868 commits)
Revert "Revert "Revert "staging/lustre: drop CONFIG_BROKEN dependency"""
staging: rtl8192u: fix line length in r819xU_phy.h
staging: rtl8192u: rename variables in r819xU_phy.h
staging: rtl8192u: fix comments in r819xU_phy.h
staging: rtl8192u: fix whitespace in r819xU_phy.h
staging: rtl8192u: fix newlines in r819xU_phy.c
staging: comedi: unioxx5: use comedi_alloc_spriv()
staging: comedi: unioxx5: fix unioxx5_detach()
silicom: checkpatch: errors caused by macros
Staging: silicom: remove the board_t typedef in bpctl_mod.c
Staging: silicom: capitalize labels in the bp_media_type enum
Staging: silicom: remove bp_media_type enum typedef
staging: rtl8192u: replace msleep(1) with usleep_range() in r819xU_phy.c
staging: rtl8192u: rename dwRegRead and rtStatus in r819xU_phy.c
staging: rtl8192u: replace __FUNCTION__ in r819xU_phy.c
staging: rtl8192u: limit line size in r819xU_phy.c
zram: allow request end to coincide with disksize
staging: drm/imx: use generic irq chip unused field to block out invalid irqs
staging: drm/imx: use generic irqchip
staging: drm/imx: ipu-dmfc: use defines for ipu channel numbers
...
2013-07-02 12:40:23 -06:00
|
|
|
result = qt_open_channel(serial, port->port_number, &channel_data);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (result < 0) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "qt_open_channel failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
return result;
|
|
|
|
}
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "qt_open_channel completed.\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* FIXME: are these needed? Does it even do anything useful? */
|
2013-06-14 08:55:57 -06:00
|
|
|
quatech_port->shadow_lsr = channel_data.line_status &
|
2009-05-31 13:49:43 -06:00
|
|
|
(SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE | SERIAL_LSR_BI);
|
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
quatech_port->shadow_msr = channel_data.modem_status &
|
2009-05-31 13:49:43 -06:00
|
|
|
(SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI | SERIAL_MSR_CD);
|
|
|
|
|
|
|
|
/* Set Baud rate to default and turn off (default)flow control here */
|
2013-06-06 11:32:00 -06:00
|
|
|
result = qt_setuart(serial, port->port_number, DEFAULT_DIVISOR, DEFAULT_LCR);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (result < 0) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "qt_setuart failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
return result;
|
|
|
|
}
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "qt_setuart completed.\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/*
|
2009-11-14 08:09:06 -07:00
|
|
|
* Put this here to make it responsive to stty and defaults set by
|
2009-05-31 13:49:43 -06:00
|
|
|
* the tty layer
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Check to see if we've set up our endpoint info yet */
|
|
|
|
if (port0->open_ports == 1) {
|
2012-11-09 14:34:11 -07:00
|
|
|
if (serial->port[0]->interrupt_in_buffer == NULL)
|
|
|
|
qt_submit_urb_from_open(serial, port);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
2013-06-07 12:04:28 -06:00
|
|
|
dev_dbg(&port->dev, "minor number is %d\n", port->minor);
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
|
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
|
|
|
|
dev_dbg(&port->dev, "Interrupt endpoint is %d\n",
|
|
|
|
port->interrupt_in_endpointAddress);
|
|
|
|
dev_dbg(&port->dev, "port's number in the device is %d\n",
|
|
|
|
quatech_port->port_num);
|
2009-05-31 13:49:43 -06:00
|
|
|
quatech_port->read_urb = port->read_urb;
|
|
|
|
|
|
|
|
/* set up our bulk in urb */
|
|
|
|
|
|
|
|
usb_fill_bulk_urb(quatech_port->read_urb,
|
|
|
|
serial->dev,
|
|
|
|
usb_rcvbulkpipe(serial->dev,
|
|
|
|
port->bulk_in_endpointAddress),
|
|
|
|
port->bulk_in_buffer,
|
|
|
|
quatech_port->read_urb->transfer_buffer_length,
|
|
|
|
qt_read_bulk_callback, quatech_port);
|
|
|
|
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev, "qt_open: bulkin endpoint is %d\n",
|
|
|
|
port->bulk_in_endpointAddress);
|
2009-05-31 13:49:43 -06:00
|
|
|
quatech_port->read_urb_busy = true;
|
|
|
|
result = usb_submit_urb(quatech_port->read_urb, GFP_KERNEL);
|
|
|
|
if (result) {
|
|
|
|
dev_err(&port->dev,
|
|
|
|
"%s - Error %d submitting control urb\n",
|
|
|
|
__func__, result);
|
|
|
|
quatech_port->read_urb_busy = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize our wait queues */
|
|
|
|
init_waitqueue_head(&quatech_port->wait);
|
|
|
|
init_waitqueue_head(&quatech_port->msr_wait);
|
|
|
|
|
|
|
|
/* initialize our icount structure */
|
|
|
|
memset(&(quatech_port->icount), 0x00, sizeof(quatech_port->icount));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qt_chars_in_buffer(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct usb_serial *serial;
|
|
|
|
int chars = 0;
|
|
|
|
|
|
|
|
serial = get_usb_serial(port, __func__);
|
|
|
|
|
|
|
|
if (serial->num_bulk_out) {
|
|
|
|
if (port->write_urb->status == -EINPROGRESS)
|
|
|
|
chars = port->write_urb->transfer_buffer_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
return chars;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qt_block_until_empty(struct tty_struct *tty,
|
|
|
|
struct quatech_port *qt_port)
|
|
|
|
{
|
|
|
|
int timeout = HZ / 10;
|
|
|
|
int wait = 30;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
count = qt_chars_in_buffer(tty);
|
|
|
|
|
|
|
|
if (count <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
interruptible_sleep_on_timeout(&qt_port->wait, timeout);
|
|
|
|
|
|
|
|
wait--;
|
|
|
|
if (wait == 0) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&qt_port->port->dev, "%s - TIMEOUT", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
wait = 30;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-14 13:33:52 -06:00
|
|
|
static void qt_close(struct usb_serial_port *port)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
struct usb_serial *serial = port->serial;
|
|
|
|
struct quatech_port *qt_port;
|
|
|
|
struct quatech_port *port0;
|
2009-06-19 10:32:56 -06:00
|
|
|
struct tty_struct *tty;
|
2009-05-31 13:49:43 -06:00
|
|
|
int status;
|
|
|
|
unsigned int index;
|
|
|
|
status = 0;
|
|
|
|
|
2009-06-19 10:32:56 -06:00
|
|
|
tty = tty_port_tty_get(&port->port);
|
2013-06-07 12:04:28 -06:00
|
|
|
index = port->port_number;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
qt_port = qt_get_port_private(port);
|
|
|
|
port0 = qt_get_port_private(serial->port[0]);
|
|
|
|
|
|
|
|
/* shutdown any bulk reads that might be going on */
|
|
|
|
if (serial->num_bulk_out)
|
|
|
|
usb_unlink_urb(port->write_urb);
|
|
|
|
if (serial->num_bulk_in)
|
|
|
|
usb_unlink_urb(port->read_urb);
|
|
|
|
|
|
|
|
/* wait up to for transmitter to empty */
|
|
|
|
if (serial->dev)
|
|
|
|
qt_block_until_empty(tty, qt_port);
|
2009-07-14 13:33:52 -06:00
|
|
|
tty_kref_put(tty);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* Close uart channel */
|
|
|
|
status = qt_close_channel(serial, index);
|
|
|
|
if (status < 0)
|
2013-06-06 11:32:00 -06:00
|
|
|
dev_dbg(&port->dev, "%s - qt_close_channel failed.\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
port0->open_ports--;
|
|
|
|
|
2013-06-06 11:32:00 -06:00
|
|
|
dev_dbg(&port->dev, "qt_num_open_ports in close%d\n", port0->open_ports);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (port0->open_ports == 0) {
|
|
|
|
if (serial->port[0]->interrupt_in_urb) {
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev, "Shutdown interrupt_in_urb\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
usb_kill_urb(serial->port[0]->interrupt_in_urb);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qt_port->write_urb) {
|
|
|
|
/* if this urb had a transfer buffer already (old tx) free it */
|
2011-03-12 22:29:09 -07:00
|
|
|
kfree(qt_port->write_urb->transfer_buffer);
|
2009-05-31 13:49:43 -06:00
|
|
|
usb_free_urb(qt_port->write_urb);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qt_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|
|
|
const unsigned char *buf, int count)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
struct usb_serial *serial = get_usb_serial(port, __func__);
|
|
|
|
|
|
|
|
if (serial == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (count == 0) {
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"%s - write request of 0 bytes\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* only do something if we have a bulk out endpoint */
|
|
|
|
if (serial->num_bulk_out) {
|
|
|
|
if (port->write_urb->status == -EINPROGRESS) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "%s - already writing\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
count =
|
|
|
|
(count > port->bulk_out_size) ? port->bulk_out_size : count;
|
|
|
|
memcpy(port->write_urb->transfer_buffer, buf, count);
|
|
|
|
|
|
|
|
/* set up our urb */
|
|
|
|
|
|
|
|
usb_fill_bulk_urb(port->write_urb, serial->dev,
|
|
|
|
usb_sndbulkpipe(serial->dev,
|
|
|
|
port->
|
|
|
|
bulk_out_endpointAddress),
|
|
|
|
port->write_urb->transfer_buffer, count,
|
|
|
|
qt_write_bulk_callback, port);
|
|
|
|
|
|
|
|
/* send the data out the bulk port */
|
|
|
|
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
|
|
|
if (result)
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"%s - failed submitting write urb, error %d\n",
|
2012-09-18 02:35:12 -06:00
|
|
|
__func__, result);
|
2009-05-31 13:49:43 -06:00
|
|
|
else
|
|
|
|
result = count;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no bulk out, so return 0 bytes written */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qt_write_room(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct usb_serial *serial;
|
|
|
|
struct quatech_port *qt_port;
|
|
|
|
|
|
|
|
int retval = -EINVAL;
|
|
|
|
|
2012-09-18 02:35:12 -06:00
|
|
|
if (port_paranoia_check(port, __func__))
|
2009-05-31 13:49:43 -06:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
serial = get_usb_serial(port, __func__);
|
|
|
|
|
|
|
|
if (!serial)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
qt_port = qt_get_port_private(port);
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_lock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (serial->num_bulk_out) {
|
|
|
|
if (port->write_urb->status != -EINPROGRESS)
|
|
|
|
retval = port->bulk_out_size;
|
|
|
|
}
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_unlock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
return retval;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-02-14 09:27:06 -07:00
|
|
|
static int qt_ioctl(struct tty_struct *tty,
|
2009-05-31 13:49:43 -06:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct quatech_port *qt_port = qt_get_port_private(port);
|
|
|
|
unsigned int index;
|
|
|
|
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-07 12:04:28 -06:00
|
|
|
index = port->port_number;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (cmd == TIOCMIWAIT) {
|
|
|
|
while (qt_port != NULL) {
|
|
|
|
interruptible_sleep_on(&qt_port->msr_wait);
|
|
|
|
if (signal_pending(current))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
else {
|
|
|
|
char diff = qt_port->diff_status;
|
|
|
|
|
|
|
|
if (diff == 0)
|
|
|
|
return -EIO; /* no change => error */
|
|
|
|
|
|
|
|
/* Consume all events */
|
|
|
|
qt_port->diff_status = 0;
|
|
|
|
|
|
|
|
if (((arg & TIOCM_RNG)
|
|
|
|
&& (diff & SERIAL_MSR_RI))
|
|
|
|
|| ((arg & TIOCM_DSR)
|
|
|
|
&& (diff & SERIAL_MSR_DSR))
|
|
|
|
|| ((arg & TIOCM_CD)
|
|
|
|
&& (diff & SERIAL_MSR_CD))
|
|
|
|
|| ((arg & TIOCM_CTS)
|
|
|
|
&& (diff & SERIAL_MSR_CTS))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-06-06 11:32:00 -06:00
|
|
|
dev_dbg(&port->dev, "%s -No ioctl for that one.\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qt_set_termios(struct tty_struct *tty,
|
|
|
|
struct usb_serial_port *port,
|
|
|
|
struct ktermios *old_termios)
|
|
|
|
{
|
2012-07-23 05:35:44 -06:00
|
|
|
struct ktermios *termios = &tty->termios;
|
2013-06-14 08:55:57 -06:00
|
|
|
unsigned char new_lcr = 0;
|
2009-05-31 13:49:43 -06:00
|
|
|
unsigned int cflag = termios->c_cflag;
|
|
|
|
unsigned int index;
|
|
|
|
int baud, divisor, remainder;
|
|
|
|
int status;
|
|
|
|
|
2013-06-07 12:04:28 -06:00
|
|
|
index = port->port_number;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2012-07-23 05:35:44 -06:00
|
|
|
switch (cflag & CSIZE) {
|
2009-05-31 13:49:43 -06:00
|
|
|
case CS5:
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_5_DATA;
|
2009-05-31 13:49:43 -06:00
|
|
|
break;
|
|
|
|
case CS6:
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_6_DATA;
|
2009-05-31 13:49:43 -06:00
|
|
|
break;
|
|
|
|
case CS7:
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_7_DATA;
|
2009-05-31 13:49:43 -06:00
|
|
|
break;
|
|
|
|
default:
|
2012-07-23 05:35:44 -06:00
|
|
|
termios->c_cflag &= ~CSIZE;
|
|
|
|
termios->c_cflag |= CS8;
|
2009-05-31 13:49:43 -06:00
|
|
|
case CS8:
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_8_DATA;
|
2009-05-31 13:49:43 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parity stuff */
|
|
|
|
if (cflag & PARENB) {
|
|
|
|
if (cflag & PARODD)
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_ODD_PARITY;
|
2009-05-31 13:49:43 -06:00
|
|
|
else
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_EVEN_PARITY;
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
if (cflag & CSTOPB)
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_TWO_STOPB;
|
2009-05-31 13:49:43 -06:00
|
|
|
else
|
2013-06-14 08:55:57 -06:00
|
|
|
new_lcr |= SERIAL_ONE_STOPB;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "%s - 4\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* Thats the LCR stuff, go ahead and set it */
|
|
|
|
baud = tty_get_baud_rate(tty);
|
|
|
|
if (!baud)
|
|
|
|
/* pick a default, any default... */
|
|
|
|
baud = 9600;
|
|
|
|
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
divisor = MAX_BAUD_RATE / baud;
|
|
|
|
remainder = MAX_BAUD_RATE % baud;
|
|
|
|
/* Round to nearest divisor */
|
|
|
|
if (((remainder * 2) >= baud) && (baud != 110))
|
|
|
|
divisor++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set Baud rate to default and turn off (default)flow control here
|
|
|
|
*/
|
|
|
|
status =
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_setuart(port->serial, index, (unsigned short)divisor, new_lcr);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0) {
|
2012-09-18 02:35:12 -06:00
|
|
|
dev_dbg(&port->dev, "qt_setuart failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now determine flow control */
|
|
|
|
if (cflag & CRTSCTS) {
|
2013-06-06 11:32:00 -06:00
|
|
|
dev_dbg(&port->dev, "%s - Enabling HW flow control\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* Enable RTS/CTS flow control */
|
2013-06-14 08:55:57 -06:00
|
|
|
status = box_set_hw_flow_ctrl(port->serial, index, 1);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (status < 0) {
|
2013-06-14 08:55:57 -06:00
|
|
|
dev_dbg(&port->dev, "box_set_hw_flow_ctrl failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Disable RTS/CTS flow control */
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
2013-06-06 11:32:00 -06:00
|
|
|
"%s - disabling HW flow control\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
status = box_set_hw_flow_ctrl(port->serial, index, 0);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0) {
|
2013-06-14 08:55:57 -06:00
|
|
|
dev_dbg(&port->dev, "box_set_hw_flow_ctrl failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we are implementing XON/XOFF, set the start and stop character in
|
|
|
|
* the device */
|
|
|
|
if (I_IXOFF(tty) || I_IXON(tty)) {
|
|
|
|
unsigned char stop_char = STOP_CHAR(tty);
|
|
|
|
unsigned char start_char = START_CHAR(tty);
|
|
|
|
status =
|
2013-06-14 08:55:57 -06:00
|
|
|
box_set_sw_flow_ctrl(port->serial, index, stop_char,
|
2009-05-31 13:49:43 -06:00
|
|
|
start_char);
|
|
|
|
if (status < 0)
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
2013-06-14 08:55:57 -06:00
|
|
|
"box_set_sw_flow_ctrl (enabled) failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
} else {
|
|
|
|
/* disable SW flow control */
|
2013-06-14 08:55:57 -06:00
|
|
|
status = box_disable_sw_flow_ctrl(port->serial, index);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0)
|
2012-11-09 14:33:02 -07:00
|
|
|
dev_dbg(&port->dev,
|
2013-06-14 08:55:57 -06:00
|
|
|
"box_set_sw_flow_ctrl (diabling) failed\n");
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
}
|
2012-07-23 05:35:44 -06:00
|
|
|
termios->c_cflag &= ~CMSPAR;
|
2012-11-09 14:33:02 -07:00
|
|
|
/* FIXME:
|
|
|
|
Error cases should be returning the actual bits changed only
|
|
|
|
*/
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qt_break(struct tty_struct *tty, int break_state)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct usb_serial *serial = get_usb_serial(port, __func__);
|
|
|
|
struct quatech_port *qt_port;
|
|
|
|
u16 index, onoff;
|
|
|
|
unsigned int result;
|
|
|
|
|
2013-06-07 12:04:28 -06:00
|
|
|
index = port->port_number;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
qt_port = qt_get_port_private(port);
|
|
|
|
|
|
|
|
if (break_state == -1)
|
|
|
|
onoff = 1;
|
|
|
|
else
|
|
|
|
onoff = 0;
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_lock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
result =
|
|
|
|
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
|
|
|
|
QT_BREAK_CONTROL, 0x40, onoff, index, NULL, 0, 300);
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_unlock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int qt_real_tiocmget(struct tty_struct *tty,
|
|
|
|
struct usb_serial_port *port,
|
2011-02-14 09:26:14 -07:00
|
|
|
struct usb_serial *serial)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
|
|
|
|
u8 mcr;
|
|
|
|
u8 msr;
|
|
|
|
unsigned int result = 0;
|
|
|
|
int status;
|
|
|
|
unsigned int index;
|
|
|
|
|
2013-06-07 12:04:28 -06:00
|
|
|
index = port->port_number;
|
2009-05-31 13:49:43 -06:00
|
|
|
status =
|
2013-06-14 08:55:57 -06:00
|
|
|
box_get_register(port->serial, index, MODEM_CONTROL_REGISTER, &mcr);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status >= 0) {
|
|
|
|
status =
|
2013-06-14 08:55:57 -06:00
|
|
|
box_get_register(port->serial, index,
|
2009-05-31 13:49:43 -06:00
|
|
|
MODEM_STATUS_REGISTER, &msr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status >= 0) {
|
|
|
|
result = ((mcr & SERIAL_MCR_DTR) ? TIOCM_DTR : 0)
|
|
|
|
/* DTR IS SET */
|
|
|
|
| ((mcr & SERIAL_MCR_RTS) ? TIOCM_RTS : 0)
|
|
|
|
/* RTS IS SET */
|
|
|
|
| ((msr & SERIAL_MSR_CTS) ? TIOCM_CTS : 0)
|
|
|
|
/* CTS is set */
|
|
|
|
| ((msr & SERIAL_MSR_CD) ? TIOCM_CAR : 0)
|
|
|
|
/* Carrier detect is set */
|
|
|
|
| ((msr & SERIAL_MSR_RI) ? TIOCM_RI : 0)
|
|
|
|
/* Ring indicator set */
|
|
|
|
| ((msr & SERIAL_MSR_DSR) ? TIOCM_DSR : 0);
|
|
|
|
/* DSR is set */
|
|
|
|
return result;
|
|
|
|
|
|
|
|
} else
|
|
|
|
return -ESPIPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int qt_real_tiocmset(struct tty_struct *tty,
|
|
|
|
struct usb_serial_port *port,
|
|
|
|
struct usb_serial *serial,
|
|
|
|
unsigned int value)
|
|
|
|
{
|
|
|
|
|
|
|
|
u8 mcr;
|
|
|
|
int status;
|
|
|
|
unsigned int index;
|
|
|
|
|
2013-06-07 12:04:28 -06:00
|
|
|
index = port->port_number;
|
2009-05-31 13:49:43 -06:00
|
|
|
status =
|
2013-06-14 08:55:57 -06:00
|
|
|
box_get_register(port->serial, index, MODEM_CONTROL_REGISTER, &mcr);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0)
|
|
|
|
return -ESPIPE;
|
|
|
|
|
|
|
|
/*
|
2012-06-18 08:42:23 -06:00
|
|
|
* Turn off the RTS and DTR and loopback and then only turn on what was
|
2009-05-31 13:49:43 -06:00
|
|
|
* asked for
|
|
|
|
*/
|
|
|
|
mcr &= ~(SERIAL_MCR_RTS | SERIAL_MCR_DTR | SERIAL_MCR_LOOP);
|
|
|
|
if (value & TIOCM_RTS)
|
|
|
|
mcr |= SERIAL_MCR_RTS;
|
|
|
|
if (value & TIOCM_DTR)
|
|
|
|
mcr |= SERIAL_MCR_DTR;
|
|
|
|
if (value & TIOCM_LOOP)
|
|
|
|
mcr |= SERIAL_MCR_LOOP;
|
|
|
|
|
|
|
|
status =
|
2013-06-14 08:55:57 -06:00
|
|
|
box_set_register(port->serial, index, MODEM_CONTROL_REGISTER, mcr);
|
2009-05-31 13:49:43 -06:00
|
|
|
if (status < 0)
|
|
|
|
return -ESPIPE;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-14 09:26:14 -07:00
|
|
|
static int qt_tiocmget(struct tty_struct *tty)
|
2009-05-31 13:49:43 -06:00
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct usb_serial *serial = get_usb_serial(port, __func__);
|
|
|
|
struct quatech_port *qt_port = qt_get_port_private(port);
|
2012-08-15 03:11:03 -06:00
|
|
|
int retval;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (!serial)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_lock(&qt_port->lock);
|
2011-02-14 09:26:14 -07:00
|
|
|
retval = qt_real_tiocmget(tty, port, serial);
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_unlock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2011-02-14 09:26:50 -07:00
|
|
|
static int qt_tiocmset(struct tty_struct *tty,
|
2009-05-31 13:49:43 -06:00
|
|
|
unsigned int set, unsigned int clear)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct usb_serial *serial = get_usb_serial(port, __func__);
|
|
|
|
struct quatech_port *qt_port = qt_get_port_private(port);
|
2012-08-15 03:11:03 -06:00
|
|
|
int retval;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
if (!serial)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_lock(&qt_port->lock);
|
2011-02-14 09:26:50 -07:00
|
|
|
retval = qt_real_tiocmset(tty, port, serial, set);
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_unlock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qt_throttle(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct usb_serial *serial = get_usb_serial(port, __func__);
|
|
|
|
struct quatech_port *qt_port;
|
|
|
|
|
|
|
|
if (!serial)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qt_port = qt_get_port_private(port);
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_lock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* pass on to the driver specific version of this function */
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_port->rx_holding = 1;
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_unlock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
2012-11-09 14:34:27 -07:00
|
|
|
static void qt_submit_urb_from_unthrottle(struct usb_serial_port *port,
|
|
|
|
struct usb_serial *serial)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
/* Start reading from the device */
|
|
|
|
usb_fill_bulk_urb(port->read_urb, serial->dev,
|
|
|
|
usb_rcvbulkpipe(serial->dev,
|
|
|
|
port->bulk_in_endpointAddress),
|
|
|
|
port->read_urb->transfer_buffer,
|
|
|
|
port->read_urb->transfer_buffer_length,
|
|
|
|
qt_read_bulk_callback, port);
|
|
|
|
|
|
|
|
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
dev_err(&port->dev,
|
|
|
|
"%s - failed restarting read urb, error %d\n",
|
|
|
|
__func__, result);
|
|
|
|
}
|
|
|
|
|
2009-05-31 13:49:43 -06:00
|
|
|
static void qt_unthrottle(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
|
|
|
struct usb_serial *serial = get_usb_serial(port, __func__);
|
|
|
|
struct quatech_port *qt_port;
|
|
|
|
|
|
|
|
if (!serial)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qt_port = qt_get_port_private(port);
|
|
|
|
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_lock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
if (qt_port->rx_holding == 1) {
|
|
|
|
dev_dbg(&port->dev, "%s -qt_port->rx_holding == 1\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
2013-06-14 08:55:57 -06:00
|
|
|
qt_port->rx_holding = 0;
|
|
|
|
dev_dbg(&port->dev, "%s - qt_port->rx_holding = 0\n", __func__);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
/* if we have a bulk endpoint, start it up */
|
2013-06-14 08:55:57 -06:00
|
|
|
if ((serial->num_bulk_in) && (qt_port->read_bulk_stopped == 1))
|
2012-11-09 14:34:27 -07:00
|
|
|
qt_submit_urb_from_unthrottle(port, serial);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
2011-08-09 00:23:42 -06:00
|
|
|
mutex_unlock(&qt_port->lock);
|
2009-05-31 13:49:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qt_calc_num_ports(struct usb_serial *serial)
|
|
|
|
{
|
|
|
|
int num_ports;
|
|
|
|
|
|
|
|
num_ports =
|
|
|
|
(serial->interface->cur_altsetting->desc.bNumEndpoints - 1) / 2;
|
|
|
|
|
|
|
|
return num_ports;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct usb_serial_driver quatech_device = {
|
|
|
|
.driver = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.name = "serqt",
|
|
|
|
},
|
|
|
|
.description = DRIVER_DESC,
|
2012-05-08 16:46:14 -06:00
|
|
|
.id_table = id_table,
|
2009-05-31 13:49:43 -06:00
|
|
|
.num_ports = 8,
|
|
|
|
.open = qt_open,
|
|
|
|
.close = qt_close,
|
|
|
|
.write = qt_write,
|
|
|
|
.write_room = qt_write_room,
|
|
|
|
.chars_in_buffer = qt_chars_in_buffer,
|
|
|
|
.throttle = qt_throttle,
|
|
|
|
.unthrottle = qt_unthrottle,
|
|
|
|
.calc_num_ports = qt_calc_num_ports,
|
|
|
|
.ioctl = qt_ioctl,
|
|
|
|
.set_termios = qt_set_termios,
|
|
|
|
.break_ctl = qt_break,
|
|
|
|
.tiocmget = qt_tiocmget,
|
|
|
|
.tiocmset = qt_tiocmset,
|
|
|
|
.attach = qt_startup,
|
|
|
|
.release = qt_release,
|
|
|
|
};
|
|
|
|
|
2012-02-23 12:57:37 -07:00
|
|
|
static struct usb_serial_driver * const serial_drivers[] = {
|
|
|
|
&quatech_device, NULL
|
|
|
|
};
|
|
|
|
|
2012-05-08 16:46:14 -06:00
|
|
|
module_usb_serial_driver(serial_drivers, id_table);
|
2009-05-31 13:49:43 -06:00
|
|
|
|
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
|
|
MODULE_LICENSE("GPL");
|