#define QU2BOXPWRON 0x8000 /* magic number to turn FPGA power on */
#define QU2BOX232 0x40 /* RS232 mode on MEI devices */
#define QU2BOXSPD9600 0x60 /* set speed to 9600 baud */
-#define FIFO_DEPTH 1024 /* size of hardware fifos */
+#define QT2_FIFO_DEPTH 1024 /* size of hardware fifos */
#define QT2_TX_HEADER_LENGTH 5
/* length of the header sent to the box with each write URB */
#define QT_OPEN_CLOSE_CHANNEL 0xca
/*#define QT_GET_SET_PREBUF_TRIG_LVL 0xcc
#define QT_SET_ATF 0xcd*/
-#define QT2_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 QT_STOP_RECEIVE 0xe0*/
-#define QT2_FLUSH_DEVICE 0xc4
-#define QT_GET_SET_QMCR 0xe1
+#define QT2_GET_SET_REGISTER 0xc0
+#define QT2_GET_SET_UART 0xc1
+#define QT2_HW_FLOW_CONTROL_MASK 0xc5
+#define QT2_SW_FLOW_CONTROL_MASK 0xc6
+#define QT2_SW_FLOW_CONTROL_DISABLE 0xc7
+#define QT2_BREAK_CONTROL 0xc8
+#define QT2_STOP_RECEIVE 0xe0
+#define QT2_FLUSH_DEVICE 0xc4
+#define QT2_GET_SET_QMCR 0xe1
/* sorts of flush we can do on */
#define QT2_FLUSH_RX 0x00
#define QT2_FLUSH_TX 0x01
-/* port setting constants */
-#define SERIAL_MCR_DTR 0x01
-#define SERIAL_MCR_RTS 0x02
-#define SERIAL_MCR_LOOP 0x10
+/* port setting constants, used to set up serial port speeds, flow
+ * control and so on */
+#define QT2_SERIAL_MCR_DTR 0x01
+#define QT2_SERIAL_MCR_RTS 0x02
+#define QT2_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 QT2_SERIAL_MSR_CTS 0x10
+#define QT2_SERIAL_MSR_CD 0x80
+#define QT2_SERIAL_MSR_RI 0x40
+#define QT2_SERIAL_MSR_DSR 0x20
+#define QT2_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 QT2_SERIAL_8_DATA 0x03
+#define QT2_SERIAL_7_DATA 0x02
+#define QT2_SERIAL_6_DATA 0x01
+#define QT2_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 QT2_SERIAL_ODD_PARITY 0x08
+#define QT2_SERIAL_EVEN_PARITY 0x18
+#define QT2_SERIAL_TWO_STOPB 0x04
+#define QT2_SERIAL_ONE_STOPB 0x00
-#define MAX_BAUD_RATE 921600
-#define MAX_BAUD_REMAINDER 4608
+#define QT2_MAX_BAUD_RATE 921600
+#define QT2_MAX_BAUD_REMAINDER 4608
-#define SERIAL_LSR_OE 0x02
-#define SERIAL_LSR_PE 0x04
-#define SERIAL_LSR_FE 0x08
-#define SERIAL_LSR_BI 0x10
+#define QT2_SERIAL_LSR_OE 0x02
+#define QT2_SERIAL_LSR_PE 0x04
+#define QT2_SERIAL_LSR_FE 0x08
+#define QT2_SERIAL_LSR_BI 0x10
/* value of Line Status Register when UART has completed
* emptying data out on the line */
.no_dynamic_id = 1,
};
-/** structure in which to keep all the messy stuff that this driver needs
- * alongside the usb_serial_port structure
- * @param read_urb_busy Flag indicating that port->read_urb is in use
- * @param close_pending flag indicating that this port is in the process of
+/**
+ * quatech2_port: Structure in which to keep all the messy stuff that this
+ * driver needs alongside the usb_serial_port structure
+ * @read_urb_busy: Flag indicating that port->read_urb is in use
+ * @close_pending: flag indicating that this port is in the process of
* being closed (and so no new reads / writes should be started).
- * @param shadowLSR Last received state of the line status register, holds the
+ * @shadowLSR: Last received state of the line status register, holds the
* value of the line status flags from the port
- * @param shadowMSR Last received state of the modem status register, holds
+ * @shadowMSR: Last received state of the modem status register, holds
* the value of the modem status received from the port
- * @param xmit_pending_bytes Number of bytes waiting to be sent out of
- * the serial port
- * @param xmit_fifo_room_bytes free space available in the transmit fifo
- * for this port on the box
- * @param rcv_flush Flag indicating that a receive flush has occured on
+ * @rcv_flush: Flag indicating that a receive flush has occured on
* the hardware.
- * @param xmit_flush Flag indicating that a transmit flush has been processed by
+ * @xmit_flush: Flag indicating that a transmit flush has been processed by
* the hardware.
- * @param fifo_empty_flag Flag indicating that the (per-port) buffer on the
- * device is empty.
- * @param tx_fifo_room Number of bytes room left in the buffer
- * @param tx_pending_bytes Number of bytes waiting to be sent
+ * @tx_pending_bytes: Number of bytes waiting to be sent. This total
+ * includes the size (excluding header) of URBs that have been submitted but
+ * have not yet been sent to to the device, and bytes that have been sent out
+ * of the port but not yet reported sent by the "xmit_empty" messages (which
+ * indicate the number of bytes sent each time they are recieved, despite the
+ * misleading name).
+ * - Starts at zero when port is initialised.
+ * - is incremented by the size of the data to be written (no headers)
+ * each time a write urb is dispatched.
+ * - is decremented each time a "transmit empty" message is received
+ * by the driver in the data stream.
+ * @lock: Mutex to lock access to this structure when we need to ensure that
+ * races don't occur to access bits of it.
+ * @open_count: The number of uses of the port currently having
+ * it open, i.e. the reference count.
*/
struct quatech2_port {
int magic;
bool close_pending;
__u8 shadowLSR;
__u8 shadowMSR;
- int xmit_pending_bytes;
- int xmit_fifo_room_bytes;
bool rcv_flush;
bool xmit_flush;
-/* bool fifo_empty_flag;
- int tx_fifo_room;*/
int tx_pending_bytes;
+ struct mutex modelock;
+ int open_count;
char active; /* someone has this device open */
unsigned char *xfer_to_tty_buffer;
wait_queue_head_t wait;
- int open_count; /* number of times this port has been opened */
- struct semaphore sem; /* locks this structure */
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
char RxHolding;
unsigned short Value);
static int qt2_box_flush(struct usb_serial *serial, unsigned char uart_number,
unsigned short rcv_or_xmit);
-static int qt2_write(struct tty_struct *tty, struct usb_serial_port *port,
- const unsigned char *buf, int count);
-static int qt2_tiocmget(struct tty_struct *tty, struct file *file);
-static int qt2_tiocmset(struct tty_struct *tty, struct file *file,
- unsigned int set, unsigned int clear);
+static int qt2_boxsetuart(struct usb_serial *serial, unsigned short Uart_Number,
+ unsigned short default_divisor, unsigned char default_LCR);
+static int qt2_boxsethw_flowctl(struct usb_serial *serial,
+ unsigned int UartNumber, bool bSet);
+static int qt2_boxsetsw_flowctl(struct usb_serial *serial, __u16 UartNumber,
+ unsigned char stop_char, unsigned char start_char);
+static int qt2_boxunsetsw_flowctl(struct usb_serial *serial, __u16 UartNumber);
+static int qt2_boxstoprx(struct usb_serial *serial, unsigned short uart_number,
+ unsigned short stop);
/* implementation functions, roughly in order of use, are here */
static int qt2_calc_num_ports(struct usb_serial *serial)
__func__, i);
return -ENOMEM;
}
+ /* initialise stuff in the structure */
+ qt2_port->open_count = 0; /* port is not open */
spin_lock_init(&qt2_port->lock);
+ mutex_init(&qt2_port->modelock);
qt2_set_port_private(port, qt2_port);
}
struct quatech2_dev *dev_extra; /* extra data for the device */
struct qt2_status_data ChannelData;
unsigned short default_divisor = QU2BOXSPD9600;
- unsigned char default_LCR = SERIAL_8_DATA;
+ unsigned char default_LCR = QT2_SERIAL_8_DATA;
int status;
int result;
return status;
}
port_extra->shadowLSR = ChannelData.line_status &
- (SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE |
- SERIAL_LSR_BI);
+ (QT2_SERIAL_LSR_OE | QT2_SERIAL_LSR_PE |
+ QT2_SERIAL_LSR_FE | QT2_SERIAL_LSR_BI);
port_extra->shadowMSR = ChannelData.modem_status &
- (SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI |
- SERIAL_MSR_CD);
+ (QT2_SERIAL_MSR_CTS | QT2_SERIAL_MSR_DSR |
+ QT2_SERIAL_MSR_RI | QT2_SERIAL_MSR_CD);
/* port_extra->fifo_empty_flag = true;*/
dbg("qt2_openboxchannel on channel %d completed.",
* Finally we need a bulk in URB to use for background reads from the
* device, which will deal with uplink data from the box to host.
*/
- dbg("serial number is %d", port->serial->minor);
dbg("port0 bulk in endpoint is %#.2x", port0->bulk_in_endpointAddress);
dbg("port0 bulk out endpoint is %#.2x",
port0->bulk_out_endpointAddress);
port->bulk_out_size,
qt2_write_bulk_callback,
port);
- /*port_extra->fifo_empty_flag = true;
- port_extra->tx_fifo_room = FIFO_DEPTH;*/
port_extra->tx_pending_bytes = 0;
if (dev_extra->open_ports == 0) {
/* initialize our wait queues */
init_waitqueue_head(&port_extra->wait);
+ /* increment the count of openings of this port by one */
+ port_extra->open_count++;
/* remember to store dev_extra, port_extra and port0_extra back again at
* end !*/
return 0;
}
-/* called when a port is closed by userspace */
+/* called when a port is closed by userspace. It won't be called, however,
+ * until calls to chars_in_buffer() reveal that the port has completed
+ * sending buffered data, and there is nothing else to do. Thus we don't have
+ * to rely on forcing data through in this function. */
/* Setting close_pending should keep new data from being written out,
* once all the data in the enpoint buffers is moved out we won't get
* any more. */
/* get the device private data */
port_extra = qt2_get_port_private(port); /* port private data */
- /* to check we have successfully flushed the buffers on the hardware,
- * we set the flags indicating flushes have occured to false, then ask
- * for flushes to occur, then sit in a timed loop until either we
- * get notified back that the flushes have happened (good) or we get
- * tired of waiting for the flush to happen and give up (bad).
- */
- port_extra->rcv_flush = false;
- port_extra->xmit_flush = false;
- qt2_box_flush(serial, port->number, QT2_FLUSH_TX); /* flush tx buffer */
- qt2_box_flush(serial, port->number, QT2_FLUSH_RX); /* flush rx buffer */
- /* now wait for the flags to magically go back to being true */
- jift = jiffies + (10 * HZ);
- do {
- if ((port_extra->rcv_flush == true) &&
- (port_extra->xmit_flush == true)) {
- dbg("Flush completed");
- break;
- }
- schedule();
- } while (jiffies <= jift);
+ /* we don't need to force flush though the hardware, so we skip using
+ * qt2_box_flush() here */
/* we can now (and only now) stop reading data */
port_extra->close_pending = true;
* still be pushing characters out over the line, so we have to
* wait testing the actual line status until the lines change
* indicating that the data is done transfering. */
+ /* FIXME: slow this polling down so it doesn't run the USB bus flat out
+ * if it actually has to spend any time in this loop (which it normally
+ * doesn't because the buffer is nearly empty) */
jift = jiffies + (10 * HZ); /* 10 sec timeout */
do {
status = qt2_box_get_register(serial, port->number,
port->bulk_out_buffer = NULL;
port->bulk_out_size = 0;
+ /* decrement the count of openings of this port by one */
+ port_extra->open_count--;
+ /* one less overall open as well */
dev_extra->open_ports--;
dbg("%s(): Exit, dev_extra->open_ports = %d", __func__,
dev_extra->open_ports);
}
-/* called to deliver writes from the next layer up to the device */
+/**
+ * qt2_write - write bytes from the tty layer out to the USB device.
+ * @buf: The data to be written, size at least count.
+ * @count: The number of bytes requested for transmission.
+ * @return The number of bytes actually accepted for transmission to the device.
+ */
static int qt2_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
__u8 header_array[5]; /* header used to direct writes to the correct
port on the device */
struct quatech2_port *port_extra; /* extra data for this port */
-
int result;
- /* get the parent device of the port */
- serial = port->serial;
+ serial = port->serial; /* get the parent device of the port */
+ port_extra = qt2_get_port_private(port); /* port extra info */
if (serial == NULL)
return -ENODEV;
- dbg("%s(): port %d", __func__, port->number);
+ dbg("%s(): port %d, requested to write %d bytes, %d already pending",
+ __func__, port->number, count, port_extra->tx_pending_bytes);
if (count <= 0) {
dbg("%s(): write request of <= 0 bytes", __func__);
return 0; /* no bytes written */
}
- port_extra = qt2_get_port_private(port);
/* check if the write urb is already in use, i.e. data already being
* sent to this port */
"-EINPROGRESS", __func__);
/* schedule_work(&port->work); commented in vendor driver */
return 0;
- } else if (port_extra->tx_pending_bytes >= FIFO_DEPTH) {
- /* such a lot queued up that we will fill the buffer again as
- * soon as it does empty? Overflowed buffer? */
- dbg("%s(): already writing, port_extra->tx_pending_bytes >="
- " FIFO_DEPTH", __func__);
+ } else if (port_extra->tx_pending_bytes >= QT2_FIFO_DEPTH) {
+ /* buffer is full (==). > should not occur, but would indicate
+ * that an overflow had occured */
+ dbg("%s(): port transmit buffer is full!", __func__);
/* schedule_work(&port->work); commented in vendor driver */
return 0;
}
* the maximum we will ever write to the device as 5 bytes less than
* one URB's worth, by reducing the value of the count argument
* appropriately*/
- if (count > port->bulk_out_size - QT2_TX_HEADER_LENGTH)
+ if (count > port->bulk_out_size - QT2_TX_HEADER_LENGTH) {
count = port->bulk_out_size - QT2_TX_HEADER_LENGTH;
+ dbg("%s(): write request bigger than urb, only accepting "
+ "%d bytes", __func__, count);
+ }
/* we must also ensure that the FIFO at the other end can cope with the
* URB we send it, otherwise it will have problems. As above, we can
* restrict the write size by just shrinking count.*/
- if (count > (FIFO_DEPTH - port_extra->tx_pending_bytes))
- count = FIFO_DEPTH - port_extra->tx_pending_bytes;
+ if (count > (QT2_FIFO_DEPTH - port_extra->tx_pending_bytes)) {
+ count = QT2_FIFO_DEPTH - port_extra->tx_pending_bytes;
+ dbg("%s(): not enough room in buffer, only accepting %d bytes",
+ __func__, count);
+ }
/* now build the header for transmission */
header_array[0] = 0x1b;
header_array[1] = 0x1b;
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
/* error couldn't submit urb */
- result = 0;
+ result = 0; /* return 0 as nothing got written */
dbg("%s(): failed submitting write urb, error %d",
__func__, result);
} else {
- port_extra->tx_pending_bytes += (count - QT2_TX_HEADER_LENGTH);
- /*port->fifo_empty_flag = false;
- port->xmit_fifo_room_bytes = FIFO_DEPTH -
- port->xmit_pending_bytes;*/
- result = count;
- dbg("%s(): submitted write urb, returning %d", __func__,
-result);
+ port_extra->tx_pending_bytes += count;
+ result = count; /* return number of bytes written, i.e. count */
+ dbg("%s(): submitted write urb, wrote %d bytes, "
+ "total pending bytes %d",
+ __func__, result, port_extra->tx_pending_bytes);
}
return result;
}
+/* This is used by the next layer up to know how much space is available
+ * in the buffer on the device. It is used on a device closure to avoid
+ * calling close() until the buffer is reported to be empty.
+ * The returned value must never go down by more than the number of bytes
+ * written for correct behaviour further up the driver stack, i.e. if I call
+ * it, then write 6 bytes, then call again I should get 6 less, or possibly
+ * only 5 less if one was written in the meantime, etc. I should never get 7
+ * less (or any bigger number) because I only wrote 6 bytes.
+ */
static int qt2_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
dbg("%s(): port_extra->close_pending == true", __func__);
return -ENODEV;
}
+ /* Q: how many bytes would a write() call actually succeed in writing
+ * if it happened now?
+ * A: one QT2_FIFO_DEPTH, less the number of bytes waiting to be sent
+ * out of the port, unless this is more than the size of the
+ * write_urb output buffer less the header, which is the maximum
+ * size write we can do.
+
+ * Most of the implementation of this is done when writes to the device
+ * are started or terminate. When we send a write to the device, we
+ * reduce the free space count by the size of the dispatched write.
+ * When a "transmit empty" message comes back up the USB read stream,
+ * we decrement the count by the number of bytes reported sent, thus
+ * keeping track of the difference between sent and recieved bytes.
+ */
- dbg("%s(): port %d", __func__, port->number);
- if ((port->write_urb->status != -EINPROGRESS) &&
- (port_extra->tx_pending_bytes == 0))
+ room = (QT2_FIFO_DEPTH - port_extra->tx_pending_bytes);
+ /* space in FIFO */
+ if (room > port->bulk_out_size - QT2_TX_HEADER_LENGTH)
room = port->bulk_out_size - QT2_TX_HEADER_LENGTH;
+ /* if more than the URB can hold, then cap to that limit */
+
+ dbg("%s(): port %d: write room is %d", __func__, port->number, room);
return room;
}
{
struct usb_serial_port *port = tty->driver_data;
/* parent usb_serial_port pointer */
- int chars = 0;
struct quatech2_port *port_extra; /* extra data for this port */
port_extra = qt2_get_port_private(port);
- dbg("%s(): port %d", __func__, port->number);
- if ((port->write_urb->status == -EINPROGRESS) &&
- (port_extra->tx_pending_bytes != 0))
- chars = port->write_urb->transfer_buffer_length;
- dbg("%s(): returns %d", __func__, chars);
- return chars;
+ dbg("%s(): port %d: chars_in_buffer = %d", __func__,
+ port->number, port_extra->tx_pending_bytes);
+ return port_extra->tx_pending_bytes;
}
-
+/* called when userspace does an ioctl() on the device. Note that
+ * TIOCMGET and TIOCMSET are filtered off to their own methods before they get
+ * here, so we don't have to handle them.
+ */
static int qt2_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
/* Declare a wait queue named "wait" */
unsigned int value;
- int status;
unsigned int UartNumber;
if (serial == NULL)
dbg("%s(): port %d, UartNumber %d, tty =0x%p", __func__,
port->number, UartNumber, tty);
- if (cmd == TIOCMGET) {
- return qt2_tiocmget(tty, file);
- /* same as tiocmget function */
- } else if (cmd == TIOCMSET) {
- if (copy_from_user(&value, (unsigned int *)arg,
- sizeof(unsigned int)))
- return -EFAULT;
- return qt2_tiocmset(tty, file, value, 0);
- /* same as tiocmset function */
- } else if (cmd == TIOCMBIS || cmd == TIOCMBIC) {
- status = qt2_box_get_register(port->serial, UartNumber,
- QT2_MODEM_CONTROL_REGISTER, &mcr_value);
- if (status < 0)
+ if (cmd == TIOCMBIS || cmd == TIOCMBIC) {
+ if (qt2_box_get_register(port->serial, UartNumber,
+ QT2_MODEM_CONTROL_REGISTER, &mcr_value) < 0)
return -ESPIPE;
if (copy_from_user(&value, (unsigned int *)arg,
- sizeof(unsigned int)))
+ sizeof(value)))
return -EFAULT;
switch (cmd) {
case TIOCMBIS:
if (value & TIOCM_RTS)
- mcr_value |= SERIAL_MCR_RTS;
+ mcr_value |= QT2_SERIAL_MCR_RTS;
if (value & TIOCM_DTR)
- mcr_value |= SERIAL_MCR_DTR;
+ mcr_value |= QT2_SERIAL_MCR_DTR;
if (value & TIOCM_LOOP)
- mcr_value |= SERIAL_MCR_LOOP;
+ mcr_value |= QT2_SERIAL_MCR_LOOP;
break;
case TIOCMBIC:
if (value & TIOCM_RTS)
- mcr_value &= ~SERIAL_MCR_RTS;
+ mcr_value &= ~QT2_SERIAL_MCR_RTS;
if (value & TIOCM_DTR)
- mcr_value &= ~SERIAL_MCR_DTR;
+ mcr_value &= ~QT2_SERIAL_MCR_DTR;
if (value & TIOCM_LOOP)
- mcr_value &= ~SERIAL_MCR_LOOP;
+ mcr_value &= ~QT2_SERIAL_MCR_LOOP;
break;
default:
break;
} /* end of local switch on cmd */
- status = qt2_box_set_register(port->serial, UartNumber,
- QT2_MODEM_CONTROL_REGISTER, mcr_value);
- if (status < 0) {
+ if (qt2_box_set_register(port->serial, UartNumber,
+ QT2_MODEM_CONTROL_REGISTER, mcr_value) < 0) {
return -ESPIPE;
} else {
port_extra->shadowMCR = mcr_value;
} else if (cmd == TIOCMIWAIT) {
dbg("%s() port %d, cmd == TIOCMIWAIT enter",
__func__, port->number);
- prev_msr_value = port_extra->shadowMSR & SERIAL_MSR_MASK;
+ prev_msr_value = port_extra->shadowMSR & QT2_SERIAL_MSR_MASK;
while (1) {
add_wait_queue(&port_extra->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
/* see if a signal woke us up */
if (signal_pending(current))
return -ERESTARTSYS;
- msr_value = port_extra->shadowMSR & SERIAL_MSR_MASK;
+ msr_value = port_extra->shadowMSR & QT2_SERIAL_MSR_MASK;
if (msr_value == prev_msr_value)
return -EIO; /* no change - error */
if ((arg & TIOCM_RNG &&
- ((prev_msr_value & SERIAL_MSR_RI) ==
- (msr_value & SERIAL_MSR_RI))) ||
+ ((prev_msr_value & QT2_SERIAL_MSR_RI) ==
+ (msr_value & QT2_SERIAL_MSR_RI))) ||
(arg & TIOCM_DSR &&
- ((prev_msr_value & SERIAL_MSR_DSR) ==
- (msr_value & SERIAL_MSR_DSR))) ||
+ ((prev_msr_value & QT2_SERIAL_MSR_DSR) ==
+ (msr_value & QT2_SERIAL_MSR_DSR))) ||
(arg & TIOCM_CD &&
- ((prev_msr_value & SERIAL_MSR_CD) ==
- (msr_value & SERIAL_MSR_CD))) ||
+ ((prev_msr_value & QT2_SERIAL_MSR_CD) ==
+ (msr_value & QT2_SERIAL_MSR_CD))) ||
(arg & TIOCM_CTS &&
- ((prev_msr_value & SERIAL_MSR_CTS) ==
- (msr_value & SERIAL_MSR_CTS)))) {
+ ((prev_msr_value & QT2_SERIAL_MSR_CTS) ==
+ (msr_value & QT2_SERIAL_MSR_CTS)))) {
return 0;
}
} /* end inifinite while */
+ /* FIXME: This while loop needs a way to break out if the device
+ * is disconnected while a process is waiting for the MSR to
+ * change, because once it's disconnected, it isn't going to
+ * change state ... */
} else {
/* any other ioctls we don't know about come here */
dbg("%s(): No ioctl for that one. port = %d", __func__,
}
}
+/* Called when the user wishes to change the port settings using the termios
+ * userspace interface */
+static void qt2_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
+{
+ struct usb_serial *serial; /* parent serial device */
+ int baud, divisor, remainder;
+ unsigned char LCR_change_to = 0;
+ int status;
+ __u16 UartNumber;
+
+ dbg("%s(): port %d", __func__, port->number);
+
+ serial = port->serial;
+
+ UartNumber = port->number;
+
+ if (old_termios && !tty_termios_hw_change(old_termios, tty->termios))
+ return;
+
+ switch (tty->termios->c_cflag) {
+ case CS5:
+ LCR_change_to |= QT2_SERIAL_5_DATA;
+ break;
+ case CS6:
+ LCR_change_to |= QT2_SERIAL_6_DATA;
+ break;
+ case CS7:
+ LCR_change_to |= QT2_SERIAL_7_DATA;
+ break;
+ default:
+ case CS8:
+ LCR_change_to |= QT2_SERIAL_8_DATA;
+ break;
+ }
+
+ /* Parity stuff */
+ if (tty->termios->c_cflag & PARENB) {
+ if (tty->termios->c_cflag & PARODD)
+ LCR_change_to |= QT2_SERIAL_ODD_PARITY;
+ else
+ LCR_change_to |= QT2_SERIAL_EVEN_PARITY;
+ }
+ /* Because LCR_change_to is initialised to zero, we don't have to worry
+ * about the case where PARENB is not set or clearing bits, because by
+ * default all of them are cleared, turning parity off.
+ * as we don't support mark/space parity, we should clear the
+ * mark/space parity bit in c_cflag, so the caller can tell we have
+ * ignored the request */
+ tty->termios->c_cflag &= ~CMSPAR;
+
+ if (tty->termios->c_cflag & CSTOPB)
+ LCR_change_to |= QT2_SERIAL_TWO_STOPB;
+ else
+ LCR_change_to |= QT2_SERIAL_ONE_STOPB;
+
+ /* Thats the LCR stuff, next we need to work out the divisor as the
+ * LCR and the divisor are set together */
+ baud = tty_get_baud_rate(tty);
+ if (!baud) {
+ /* pick a default, any default... */
+ baud = 9600;
+ }
+ dbg("%s(): got baud = %d", __func__, baud);
+
+ divisor = QT2_MAX_BAUD_RATE / baud;
+ remainder = QT2_MAX_BAUD_RATE % baud;
+ /* Round to nearest divisor */
+ if (((remainder * 2) >= baud) && (baud != 110))
+ divisor++;
+ dbg("%s(): setting divisor = %d, QT2_MAX_BAUD_RATE = %d , LCR = %#.2x",
+ __func__, divisor, QT2_MAX_BAUD_RATE, LCR_change_to);
+
+ status = qt2_boxsetuart(serial, UartNumber, (unsigned short) divisor,
+ LCR_change_to);
+ if (status < 0) {
+ dbg("qt2_boxsetuart() failed");
+ return;
+ } else {
+ /* now encode the baud rate we actually set, which may be
+ * different to the request */
+ baud = QT2_MAX_BAUD_RATE / divisor;
+ tty_encode_baud_rate(tty, baud, baud);
+ }
+
+ /* Now determine flow control */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ dbg("%s(): Enabling HW flow control port %d", __func__,
+ port->number);
+ /* Enable RTS/CTS flow control */
+ status = qt2_boxsethw_flowctl(serial, UartNumber, true);
+ if (status < 0) {
+ dbg("qt2_boxsethw_flowctl() failed");
+ return;
+ }
+ } else {
+ /* Disable RTS/CTS flow control */
+ dbg("%s(): disabling HW flow control port %d", __func__,
+ port->number);
+ status = qt2_boxsethw_flowctl(serial, UartNumber, false);
+ if (status < 0) {
+ dbg("qt2_boxsethw_flowctl failed");
+ 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 = qt2_boxsetsw_flowctl(serial, UartNumber, stop_char,
+ start_char);
+ if (status < 0)
+ dbg("qt2_boxsetsw_flowctl (enabled) failed");
+ } else {
+ /* disable SW flow control */
+ status = qt2_boxunsetsw_flowctl(serial, UartNumber);
+ if (status < 0)
+ dbg("qt2_boxunsetsw_flowctl (disabling) failed");
+ }
+}
+
static int qt2_tiocmget(struct tty_struct *tty, struct file *file)
{
struct usb_serial_port *port = tty->driver_data;
QT2_MODEM_STATUS_REGISTER, &msr_value);
}
if (status >= 0) {
- result = ((mcr_value & SERIAL_MCR_DTR) ? TIOCM_DTR : 0)
+ result = ((mcr_value & QT2_SERIAL_MCR_DTR) ? TIOCM_DTR : 0)
/*DTR set */
- | ((mcr_value & SERIAL_MCR_RTS) ? TIOCM_RTS : 0)
+ | ((mcr_value & QT2_SERIAL_MCR_RTS) ? TIOCM_RTS : 0)
/*RTS set */
- | ((msr_value & SERIAL_MSR_CTS) ? TIOCM_CTS : 0)
+ | ((msr_value & QT2_SERIAL_MSR_CTS) ? TIOCM_CTS : 0)
/* CTS set */
- | ((msr_value & SERIAL_MSR_CD) ? TIOCM_CAR : 0)
+ | ((msr_value & QT2_SERIAL_MSR_CD) ? TIOCM_CAR : 0)
/*Carrier detect set */
- | ((msr_value & SERIAL_MSR_RI) ? TIOCM_RI : 0)
+ | ((msr_value & QT2_SERIAL_MSR_RI) ? TIOCM_RI : 0)
/* Ring indicator set */
- | ((msr_value & SERIAL_MSR_DSR) ? TIOCM_DSR : 0);
+ | ((msr_value & QT2_SERIAL_MSR_DSR) ? TIOCM_DSR : 0);
/* DSR set */
return result;
} else {
/* Turn off RTS, DTR and loopback, then only turn on what was asked
* for */
- mcr_value &= ~(SERIAL_MCR_RTS | SERIAL_MCR_DTR | SERIAL_MCR_LOOP);
+ mcr_value &= ~(QT2_SERIAL_MCR_RTS | QT2_SERIAL_MCR_DTR |
+ QT2_SERIAL_MCR_LOOP);
if (set & TIOCM_RTS)
- mcr_value |= SERIAL_MCR_RTS;
+ mcr_value |= QT2_SERIAL_MCR_RTS;
if (set & TIOCM_DTR)
- mcr_value |= SERIAL_MCR_DTR;
+ mcr_value |= QT2_SERIAL_MCR_DTR;
if (set & TIOCM_LOOP)
- mcr_value |= SERIAL_MCR_LOOP;
+ mcr_value |= QT2_SERIAL_MCR_LOOP;
status = qt2_box_set_register(port->serial, UartNumber,
QT2_MODEM_CONTROL_REGISTER, mcr_value);
return 0;
}
+/** qt2_break - Turn BREAK on and off on the UARTs
+ */
+static void qt2_break(struct tty_struct *tty, int break_state)
+{
+ struct usb_serial_port *port = tty->driver_data; /* parent port */
+ struct usb_serial *serial = port->serial; /* parent device */
+ struct quatech2_port *port_extra; /* extra data for this port */
+ __u16 break_value;
+ unsigned int result;
+
+ port_extra = qt2_get_port_private(port);
+ if (!serial) {
+ dbg("%s(): port %d: no serial object", __func__, port->number);
+ return;
+ }
+
+ if (break_state == -1)
+ break_value = 1;
+ else
+ break_value = 0;
+ dbg("%s(): port %d, break_value %d", __func__, port->number,
+ break_value);
+
+ mutex_lock(&port_extra->modelock);
+ if (!port_extra->open_count) {
+ dbg("%s(): port not open", __func__);
+ goto exit;
+ }
+
+ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ QT2_BREAK_CONTROL, 0x40, break_value,
+ port->number, NULL, 0, 300);
+exit:
+ mutex_unlock(&port_extra->modelock);
+ dbg("%s(): exit port %d", __func__, port->number);
+
+}
+/**
+ * qt2_throttle: - stop reading new data from the port
+ */
+static void qt2_throttle(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct usb_serial *serial = port->serial;
+ struct quatech2_port *port_extra; /* extra data for this port */
+ dbg("%s(): port %d", __func__, port->number);
+
+ port_extra = qt2_get_port_private(port);
+ if (!serial) {
+ dbg("%s(): enter port %d no serial object", __func__,
+ port->number);
+ return;
+ }
+
+ mutex_lock(&port_extra->modelock); /* lock structure */
+ if (!port_extra->open_count) {
+ dbg("%s(): port not open", __func__);
+ goto exit;
+ }
+ /* Send command to box to stop receiving stuff. This will stop this
+ * particular UART from filling the endpoint - in the multiport case the
+ * FPGA UART will handle any flow control implmented, but for the single
+ * port it's handed differently and we just quit submitting urbs
+ */
+ if (serial->dev->descriptor.idProduct != QUATECH_SSU2_100)
+ qt2_boxstoprx(serial, port->number, 1);
+
+ port->throttled = 1;
+exit:
+ mutex_unlock(&port_extra->modelock);
+ dbg("%s(): port %d: setting port->throttled", __func__, port->number);
+ return;
+}
+
+/**
+ * qt2_unthrottle: - start receiving data through the port again after being
+ * throttled
+ */
+static void qt2_unthrottle(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct usb_serial *serial = port->serial;
+ struct quatech2_port *port_extra; /* extra data for this port */
+ struct usb_serial_port *port0; /* first port structure on device */
+ struct quatech2_dev *dev_extra; /* extra data for the device */
+
+ if (!serial) {
+ dbg("%s() enter port %d no serial object!", __func__,
+ port->number);
+ return;
+ }
+ dbg("%s(): enter port %d", __func__, port->number);
+ dev_extra = qt2_get_dev_private(serial);
+ port_extra = qt2_get_port_private(port);
+ port0 = serial->port[0]; /* get the first port's device structure */
+
+ mutex_lock(&port_extra->modelock);
+ if (!port_extra->open_count) {
+ dbg("%s(): port %d not open", __func__, port->number);
+ goto exit;
+ }
+
+ if (port->throttled != 0) {
+ dbg("%s(): port %d: unsetting port->throttled", __func__,
+ port->number);
+ port->throttled = 0;
+ /* Send command to box to start receiving stuff */
+ if (serial->dev->descriptor.idProduct != QUATECH_SSU2_100) {
+ qt2_boxstoprx(serial, port->number, 0);
+ } else if (dev_extra->ReadBulkStopped == true) {
+ usb_fill_bulk_urb(port0->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port0->bulk_in_endpointAddress),
+ port0->bulk_in_buffer,
+ port0->bulk_in_size,
+ qt2_read_bulk_callback,
+ serial);
+ }
+ }
+exit:
+ mutex_unlock(&port_extra->modelock);
+ dbg("%s(): exit port %d", __func__, port->number);
+ return;
+}
+
/* internal, private helper functions for the driver */
/* Power up the FPGA in the box to get it working */
}
/*
- * qt2_boxsetQMCR Issue a QT_GET_SET_QMCR vendor-spcific request on the
+ * qt2_boxsetQMCR Issue a QT2_GET_SET_QMCR vendor-spcific request on the
* default control pipe. If successful return the number of bytes written,
* otherwise return a negative error number of the problem.
*/
Uart_Number, PortSettings);
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- QT_GET_SET_QMCR, 0x40, PortSettings,
+ QT2_GET_SET_QMCR, 0x40, PortSettings,
(__u16)Uart_Number, NULL, 0, 5000);
return result;
}
UartNumandLCR = (LCR << 8) + Uart_Number;
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- QT_GET_SET_UART, 0x40, divisor, UartNumandLCR,
+ QT2_GET_SET_UART, 0x40, divisor, UartNumandLCR,
NULL, 0, 300);
return result;
}
bool escapeflag; /* flag set to true if this loop iteration is
* parsing an escape sequence, rather than
* ordinary data */
-
-
- dbg("%s(): callback running", __func__);
+ dbg("%s(): callback running, active port is %d", __func__,
+ active->number);
if (urb->status) {
/* read didn't go well */
/* single port device, input is already stopped, so we don't
* need any more input data */
dev_extra->ReadBulkStopped = true;
- return;
+ return;
}
/* finally, we are in a situation where we might consider the data
* that is contained within the URB, and what to do about it.
dbg("%s - bad tty pointer - exiting", __func__);
return;
}
- dbg("%s(): active port %d, tty_st =0x%p\n", __func__, active->number,
- tty_st);
RxCount = urb->actual_length; /* grab length of data handy */
if (RxCount) {
break;
}
qt2_process_xmit_empty(active,
- FOURTHCHAR,
- FIFTHCHAR);
+ FOURTHCHAR, FIFTHCHAR);
i += 4;
escapeflag = true;
break;
}
/* Port change. If port open push
* current data up to tty layer */
- if (dev_extra->open_ports > 0)
+ if (active_extra->open_count > 0)
tty_flip_buffer_push(tty_st);
dbg("Port Change: new port = %d",
dbg("%s(): failed resubmitting read urb, error %d",
__func__, result);
} else {
+ dbg("%s() successfully resubmitted read urb", __func__);
if (tty_st && RxCount) {
/* if some inbound data was processed, then
* we need to push that through the tty layer
/* cribbed from serqt_usb2 driver, but not sure which work needs
* scheduling - port0 or currently active port? */
/* schedule_work(&port->work); */
-
+ dbg("%s() completed", __func__);
return;
}
__func__, urb->status);
return;
}
-
+ /* FIXME What is supposed to be going on here?
+ * does this actually do anything useful, and should it?
+ */
/*port_softint((void *) serial); commented in vendor driver */
schedule_work(&port->work);
dbg("%s(): port %d exit", __func__, port->number);
{
/* obtain the private structure for the port */
struct quatech2_port *port_extra = qt2_get_port_private(port);
- port_extra->shadowLSR = LineStatus & (SERIAL_LSR_OE | SERIAL_LSR_PE |
- SERIAL_LSR_FE | SERIAL_LSR_BI);
+ port_extra->shadowLSR = LineStatus & (QT2_SERIAL_LSR_OE |
+ QT2_SERIAL_LSR_PE | QT2_SERIAL_LSR_FE | QT2_SERIAL_LSR_BI);
}
static void qt2_process_modem_status(struct usb_serial_port *port,
unsigned char ModemStatus)
byte_count = (int)(fifth_char * 16);
byte_count += (int)fourth_char;
- port_extra->xmit_pending_bytes -= (int)byte_count;
- port_extra->xmit_fifo_room_bytes = FIFO_DEPTH;
+ /* byte_count indicates how many bytes the device has written out. This
+ * message appears to occur regularly, and is used in the vendor driver
+ * to keep track of the fill state of the port transmit buffer */
+ port_extra->tx_pending_bytes -= byte_count;
+ /* reduce the stored data queue length by the known number of bytes
+ * sent */
+ dbg("port %d: %d bytes reported sent, %d still pending", port->number,
+ byte_count, port_extra->tx_pending_bytes);
+
+ /*port_extra->xmit_fifo_room_bytes = FIFO_DEPTH; ???*/
}
static void qt2_process_port_change(struct usb_serial_port *port,
return result;
}
+/** qt2_boxsetuart - Issue a SET_UART vendor-spcific request on the default
+ * control pipe. If successful sets baud rate divisor and LCR value.
+ */
+static int qt2_boxsetuart(struct usb_serial *serial, unsigned short Uart_Number,
+ unsigned short default_divisor, unsigned char default_LCR)
+{
+ unsigned short UartNumandLCR;
+
+ UartNumandLCR = (default_LCR << 8) + Uart_Number;
+
+ return usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ QT2_GET_SET_UART, 0x40, default_divisor, UartNumandLCR,
+ NULL, 0, 300);
+}
+/** qt2_boxsethw_flowctl - Turn hardware (RTS/CTS) flow control on and off for
+ * a hardware UART.
+ */
+static int qt2_boxsethw_flowctl(struct usb_serial *serial,
+ unsigned int UartNumber, bool bSet)
+{
+ __u8 MCR_Value = 0;
+ __u8 MSR_Value = 0;
+ __u16 MOUT_Value = 0;
+
+ if (bSet == true) {
+ MCR_Value = QT2_SERIAL_MCR_RTS;
+ /* flow control, box will clear RTS line to prevent remote
+ * device from transmitting more chars */
+ } else {
+ /* no flow control to remote device */
+ MCR_Value = 0;
+ }
+ MOUT_Value = MCR_Value << 8;
+
+ if (bSet == true) {
+ MSR_Value = QT2_SERIAL_MSR_CTS;
+ /* flow control on, box will inhibit tx data if CTS line is
+ * asserted */
+ } else {
+ /* Box will not inhibit tx data due to CTS line */
+ MSR_Value = 0;
+ }
+ MOUT_Value |= MSR_Value;
+ return usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ QT2_HW_FLOW_CONTROL_MASK, 0x40, MOUT_Value, UartNumber,
+ NULL, 0, 300);
+}
+
+/** qt2_boxsetsw_flowctl - Turn software (XON/XOFF) flow control on for
+ * a hardware UART, and set the XON and XOFF characters.
+ */
+static int qt2_boxsetsw_flowctl(struct usb_serial *serial, __u16 UartNumber,
+ unsigned char stop_char, unsigned char start_char)
+{
+ __u16 nSWflowout;
+
+ nSWflowout = start_char << 8;
+ nSWflowout = (unsigned short)stop_char;
+ return usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ QT2_SW_FLOW_CONTROL_MASK, 0x40, nSWflowout, UartNumber,
+ NULL, 0, 300);
+}
+
+/** qt2_boxunsetsw_flowctl - Turn software (XON/XOFF) flow control off for
+ * a hardware UART.
+ */
+static int qt2_boxunsetsw_flowctl(struct usb_serial *serial, __u16 UartNumber)
+{
+ return usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ QT2_SW_FLOW_CONTROL_DISABLE, 0x40, 0, UartNumber, NULL,
+ 0, 300);
+}
+
+/**
+ * qt2_boxstoprx - Start and stop reception of data by the FPGA UART in
+ * response to requests from the tty layer
+ * @serial: pointer to the usb_serial structure for the parent device
+ * @uart_number: which UART on the device we are addressing
+ * @stop: Whether to start or stop data reception. Set to 1 to stop data being
+ * received, and to 0 to start it being received.
+ */
+static int qt2_boxstoprx(struct usb_serial *serial, unsigned short uart_number,
+ unsigned short stop)
+{
+ return usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ QT2_STOP_RECEIVE, 0x40, stop, uart_number, NULL, 0, 300);
+}
+
+
/*
* last things in file: stuff to register this driver into the generic
* USB serial framework.
.write = qt2_write,
.write_room = qt2_write_room,
.chars_in_buffer = qt2_chars_in_buffer,
- /*.throttle = qt_throttle,
- .unthrottle = qt_unthrottle,*/
+ .throttle = qt2_throttle,
+ .unthrottle = qt2_unthrottle,
.calc_num_ports = qt2_calc_num_ports,
.ioctl = qt2_ioctl,
- /*.set_termios = qt_set_termios,
- .break_ctl = qt_break,*/
+ .set_termios = qt2_set_termios,
+ .break_ctl = qt2_break,
.tiocmget = qt2_tiocmget,
.tiocmset = qt2_tiocmset,
.attach = qt2_attach,