diff options
-rw-r--r-- | drivers/staging/quatech_usb2/quatech_usb2.c | 694 |
1 files changed, 646 insertions, 48 deletions
diff --git a/drivers/staging/quatech_usb2/quatech_usb2.c b/drivers/staging/quatech_usb2/quatech_usb2.c index 0eaea2e7ce8a..bbbf9b88fac0 100644 --- a/drivers/staging/quatech_usb2/quatech_usb2.c +++ b/drivers/staging/quatech_usb2/quatech_usb2.c @@ -39,23 +39,33 @@ static int debug; #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_TX_HEADER_LENGTH 5 +/* length of the header sent to the box with each write URB */ + /* directions for USB transfers */ #define USBD_TRANSFER_DIRECTION_IN 0xc0 #define USBD_TRANSFER_DIRECTION_OUT 0x40 -/* special Quatech command IDs */ + +/* special Quatech command IDs. These are pushed down the + USB control pipe to get the box on the end to do things */ #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_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 QT_FLUSH_DEVICE 0xc4*/ +#define QT_STOP_RECEIVE 0xe0*/ +#define QT2_FLUSH_DEVICE 0xc4 #define QT_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 @@ -85,6 +95,19 @@ static int debug; #define SERIAL_LSR_FE 0x08 #define SERIAL_LSR_BI 0x10 +/* value of Line Status Register when UART has completed + * emptying data out on the line */ +#define QT2_LSR_TEMT 0x40 + +/* register numbers on each UART, for use with qt2_box_[get|set]_register*/ +#define QT2_XMT_HOLD_REGISTER 0x00 +#define QT2_XVR_BUFFER_REGISTER 0x00 +#define QT2_FIFO_CONTROL_REGISTER 0x02 +#define QT2_LINE_CONTROL_REGISTER 0x03 +#define QT2_MODEM_CONTROL_REGISTER 0x04 +#define QT2_LINE_STATUS_REGISTER 0x05 +#define QT2_MODEM_STATUS_REGISTER 0x06 + /* handy macros for doing escape sequence parsing on data reads */ #define THISCHAR ((unsigned char *)(urb->transfer_buffer))[i] #define NEXTCHAR ((unsigned char *)(urb->transfer_buffer))[i + 1] @@ -118,7 +141,7 @@ static struct usb_driver quausb2_usb_driver = { * 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 - * being closed. + * being closed (and so no new reads / writes should be started). * @param 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 @@ -127,10 +150,14 @@ static struct usb_driver quausb2_usb_driver = { * 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 been requested by + * @param rcv_flush Flag indicating that a receive flush has occured on * the hardware. - * @param xmit_flush Flag indicating that a transmit flush has been requested by + * @param 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 */ struct quatech2_port { int magic; @@ -140,8 +167,11 @@ struct quatech2_port { __u8 shadowMSR; int xmit_pending_bytes; int xmit_fifo_room_bytes; - char rcv_flush; - char xmit_flush; + bool rcv_flush; + bool xmit_flush; +/* bool fifo_empty_flag; + int tx_fifo_room;*/ + int tx_pending_bytes; char active; /* someone has this device open */ unsigned char *xfer_to_tty_buffer; @@ -151,7 +181,6 @@ struct quatech2_port { __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ char RxHolding; - char fifo_empty_flag; struct semaphore pend_xmit_sem; /* locks this structure */ spinlock_t lock; }; @@ -164,11 +193,16 @@ struct quatech2_port { * the read stream is currently directed to. Escape sequences in the read * stream will change this around as data arrives from different ports on the * box + * @buffer_size: The max size buffer each URB can take, used to set the size of + * the buffers allocated for writing to each port on the device (we need to + * store this because it is known only to the endpoint, but used each time a + * port is opened and a new buffer is allocated. */ struct quatech2_dev { bool ReadBulkStopped; char open_ports; struct usb_serial_port *current_port; + int buffer_size; }; /* structure which holds line and modem status flags */ @@ -200,6 +234,7 @@ static int qt2_closeboxchannel(struct usb_serial *serial, __u16 static int qt2_conf_uart(struct usb_serial *serial, unsigned short Uart_Number, unsigned short divisor, unsigned char LCR); static void qt2_read_bulk_callback(struct urb *urb); +static void qt2_write_bulk_callback(struct urb *urb); static void qt2_process_line_status(struct usb_serial_port *port, unsigned char LineStatus); static void qt2_process_modem_status(struct usb_serial_port *port, @@ -212,6 +247,19 @@ static void qt2_process_rcv_flush(struct usb_serial_port *port); static void qt2_process_xmit_flush(struct usb_serial_port *port); static void qt2_process_rx_char(struct usb_serial_port *port, unsigned char data); +static int qt2_box_get_register(struct usb_serial *serial, + unsigned char uart_number, unsigned short register_num, + __u8 *pValue); +static int qt2_box_set_register(struct usb_serial *serial, + unsigned short Uart_Number, unsigned short Register_Num, + 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); /* implementation functions, roughly in order of use, are here */ static int qt2_calc_num_ports(struct usb_serial *serial) @@ -291,7 +339,7 @@ static int qt2_attach(struct usb_serial *serial) return -ENOMEM; } spin_lock_init(&qt2_port->lock); - usb_set_serial_port_data(port, qt2_port); + qt2_set_port_private(port, qt2_port); } /* gain access to port[0]'s structure because we want to store @@ -307,15 +355,17 @@ static int qt2_attach(struct usb_serial *serial) if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x02)) { /* we found a bulk in endpoint */ - dbg("found bulk in at 0x%x", + dbg("found bulk in at %#.2x", endpoint->bEndpointAddress); } if (((endpoint->bEndpointAddress & 0x80) == 0x00) && ((endpoint->bmAttributes & 3) == 0x02)) { /* we found a bulk out endpoint */ - dbg("found bulk out at 0x%x", + dbg("found bulk out at %#.2x", endpoint->bEndpointAddress); + qt2_dev->buffer_size = endpoint->wMaxPacketSize; + /* max size of URB needs recording for the device */ } } /* end printing endpoint addresses */ @@ -368,7 +418,8 @@ static void qt2_release(struct usb_serial *serial) usb_set_serial_port_data(port, NULL); } } -/* This function is called once per serial port on the device. +/* This function is called once per serial port on the device, when + * that port is opened by a userspace application. * The tty_struct and the usb_serial_port belong to this port, * i.e. there are multiple ones for a multi-port device. * However the usb_serial_port structure has a back-pointer @@ -379,12 +430,11 @@ static void qt2_release(struct usb_serial *serial) * This is most helpful if the device shares resources (e.g. end * points) between different ports */ -int qt2_open(struct tty_struct *tty, - struct usb_serial_port *port, struct file *filp) +int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial; /* device structure */ struct usb_serial_port *port0; /* first port structure on device */ - struct quatech2_port *port_extra; /* extra data for this port */ + struct quatech2_port *port_extra; /* extra data for this port */ struct quatech2_port *port0_extra; /* extra data for first port */ struct quatech2_dev *dev_extra; /* extra data for the device */ struct qt2_status_data ChannelData; @@ -405,22 +455,23 @@ int qt2_open(struct tty_struct *tty, } dev_extra = qt2_get_dev_private(serial); /* get the device private data */ + if (dev_extra == NULL) { + dbg("device extra data pointer is null"); + return -ENODEV; + } port0 = serial->port[0]; /* get the first port's device structure */ - if (port_paranoia_check(port, __func__)) { + if (port_paranoia_check(port0, __func__)) { dbg("port0 usb_serial_port struct failed sanity check"); return -ENODEV; } + port_extra = qt2_get_port_private(port); port0_extra = qt2_get_port_private(port0); - if (port_extra == NULL || port0_extra == NULL) { - dbg("failed to get private data for port and port0"); + dbg("failed to get private data for port or port0"); return -ENODEV; } - usb_clear_halt(serial->dev, port->write_urb->pipe); - usb_clear_halt(serial->dev, port->read_urb->pipe); - /* FIXME: are these needed? Does it even do anything useful? */ /* get the modem and line status values from the UART */ status = qt2_openboxchannel(serial, port->number, @@ -433,12 +484,11 @@ int qt2_open(struct tty_struct *tty, port_extra->shadowLSR = ChannelData.line_status & (SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE | SERIAL_LSR_BI); - port_extra->shadowMSR = ChannelData.modem_status & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI | SERIAL_MSR_CD); - port_extra->fifo_empty_flag = true; +/* port_extra->fifo_empty_flag = true;*/ dbg("qt2_openboxchannel on channel %d completed.", port->number); @@ -463,19 +513,57 @@ int qt2_open(struct tty_struct *tty, * when we do a write to a port, we will use the same endpoint * regardless of the port, with a 5-byte header added on to * tell the box which port it should eventually come out of, so we only - * need the one set of endpoints. + * need the one set of endpoints. We will have one URB per port for + * writing, so that multiple ports can be writing at once. * 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("port number is %d", port->number); 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); + /* set up write_urb for bulk out transfers on this port. The USB + * serial framework will have allocated a blank URB, buffer etc for + * port0 when it put the endpoints there, but not for any of the other + * ports on the device because there are no more endpoints. Thus we + * have to allocate our own URBs for ports 1-7 + */ + if (port->write_urb == NULL) { + dbg("port->write_urb == NULL, allocating one"); + port->write_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!port->write_urb) { + err("Allocating write URB failed"); + return -ENOMEM; + } + /* buffer same size as port0 */ + port->bulk_out_size = dev_extra->buffer_size; + port->bulk_out_buffer = kmalloc(port->bulk_out_size, + GFP_KERNEL); + if (!port->bulk_out_buffer) { + err("Couldn't allocate bulk_out_buffer"); + return -ENOMEM; + } + } + if (serial->dev == NULL) + dbg("serial->dev == NULL"); + dbg("port->bulk_out_size is %d", port->bulk_out_size); + + usb_fill_bulk_urb(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port0->bulk_out_endpointAddress), + port->bulk_out_buffer, + 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) { - /* this is first port to be opened, so need some URBs */ - /* initialise read_urb for bulk in transfers */ + /* this is first port to be opened, so need the read URB + * initialised for bulk in transfers (this is shared amongst + * all the ports on the device) */ usb_fill_bulk_urb(port0->read_urb, serial->dev, usb_rcvbulkpipe(serial->dev, port0->bulk_in_endpointAddress), @@ -490,25 +578,444 @@ int qt2_open(struct tty_struct *tty, result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) { dev_err(&port->dev, - "%s - Error %d submitting bulk in urb\n", + "%s(): Error %d submitting bulk in urb", __func__, result); port_extra->read_urb_busy = false; + dev_extra->ReadBulkStopped = true; } + + /* When the first port is opened, initialise the value of + * current_port in dev_extra to this port, so it is set + * to something. Once the box sends data it will send the + * relevant escape sequences to get it to the right port anyway + */ + dev_extra->current_port = port; } /* initialize our wait queues */ init_waitqueue_head(&port_extra->wait); - /* remember to store port_extra and port0 back again at end !*/ + /* remember to store dev_extra, port_extra and port0_extra back again at + * end !*/ qt2_set_port_private(port, port_extra); qt2_set_port_private(serial->port[0], port0_extra); qt2_set_dev_private(serial, dev_extra); - dev_extra->open_ports++; /* one more port opened */ + dev_extra->open_ports++; /* one more port opened */ return 0; } +/* called when a port is closed by userspace */ +/* 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. */ +/* BoxStopReceive would keep any more data from coming from a given + * port, but isn't called by the vendor driver, although their comments + * mention it. Should it be used here to stop the inbound data + * flow? + */ +static void qt2_close(struct usb_serial_port *port) +{ + /* time out value for flush loops */ + unsigned long jift; + struct quatech2_port *port_extra; /* extra data for this port */ + struct usb_serial *serial; /* device structure */ + struct quatech2_dev *dev_extra; /* extra data for the device */ + __u8 lsr_value = 0; /* value of Line Status Register */ + int status; /* result of last USB comms function */ + + dbg("%s(): port %d", __func__, port->number); + serial = port->serial; /* get the parent device structure */ + dev_extra = qt2_get_dev_private(serial); + /* 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 can now (and only now) stop reading data */ + port_extra->close_pending = true; + dbg("%s(): port_extra->close_pending = true", __func__); + /* although the USB side is now empty, the UART itself may + * 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. */ + jift = jiffies + (10 * HZ); /* 10 sec timeout */ + do { + status = qt2_box_get_register(serial, port->number, + QT2_LINE_STATUS_REGISTER, &lsr_value); + if (status < 0) { + dbg("%s(): qt2_box_get_register failed", __func__); + break; + } + if ((lsr_value & QT2_LSR_TEMT)) { + dbg("UART done sending"); + break; + } + schedule(); + } while (jiffies <= jift); + + status = qt2_closeboxchannel(serial, port->number); + if (status < 0) + dbg("%s(): port %d qt2_box_open_close_channel failed", + __func__, port->number); + /* to avoid leaking URBs, we should now free the write_urb for this + * port and set the pointer to null so that next time the port is opened + * a new URB is allocated. This avoids leaking URBs when the device is + * removed */ + usb_free_urb(port->write_urb); + kfree(port->bulk_out_buffer); + port->bulk_out_buffer = NULL; + port->bulk_out_size = 0; + + 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 */ +static int qt2_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) +{ + struct usb_serial *serial; /* parent device struct */ + __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; + if (serial == NULL) + return -ENODEV; + dbg("%s(): port %d", __func__, port->number); + + 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 */ + if ((port->write_urb->status == -EINPROGRESS)) { + /* Fifo hasn't been emptied since last write to this port */ + dbg("%s(): already writing, port->write_urb->status == " + "-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__); + /* schedule_work(&port->work); commented in vendor driver */ + return 0; + } + + /* We must fill the first 5 bytes of anything we sent with a transmit + * header which directes the data to the correct port. The maximum + * size we can send out in one URB is port->bulk_out_size, which caps + * the number of bytes of real data we can send in each write. As the + * semantics of write allow us to write less than we were give, we cap + * 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) + count = port->bulk_out_size - QT2_TX_HEADER_LENGTH; + /* 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; + /* now build the header for transmission */ + header_array[0] = 0x1b; + header_array[1] = 0x1b; + header_array[2] = (__u8)port->number; + header_array[3] = (__u8)count; + header_array[4] = (__u8)count >> 8; + /* copy header into URB */ + memcpy(port->write_urb->transfer_buffer, header_array, + QT2_TX_HEADER_LENGTH); + /* and actual data to write */ + memcpy(port->write_urb->transfer_buffer + 5, buf, count); + + dbg("%s(): first data byte to send = %#.2x", __func__, *buf); + + /* 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 + 5, + (qt2_write_bulk_callback), port); + /* send the data out the bulk port */ + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (result) { + /* error couldn't submit urb */ + result = 0; + 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); + } + return result; +} + +static int qt2_write_room(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + /* parent usb_serial_port pointer */ + struct quatech2_port *port_extra; /* extra data for this port */ + int room = 0; + port_extra = qt2_get_port_private(port); + + if (port_extra->close_pending == true) { + dbg("%s(): port_extra->close_pending == true", __func__); + return -ENODEV; + } + + dbg("%s(): port %d", __func__, port->number); + if ((port->write_urb->status != -EINPROGRESS) && + (port_extra->tx_pending_bytes == 0)) + room = port->bulk_out_size - QT2_TX_HEADER_LENGTH; + return room; +} + +static int qt2_chars_in_buffer(struct tty_struct *tty) +{ + 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; +} + + +static int qt2_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_serial *serial = port->serial; + __u8 mcr_value; /* Modem Control Register value */ + __u8 msr_value; /* Modem Status Register value */ + unsigned short prev_msr_value; /* Previous value of Modem Status + * Register used to implement waiting for a line status change to + * occur */ + struct quatech2_port *port_extra; /* extra data for this port */ + DECLARE_WAITQUEUE(wait, current); + /* Declare a wait queue named "wait" */ + + unsigned int value; + int status; + unsigned int UartNumber; + + if (serial == NULL) + return -ENODEV; + UartNumber = tty->index - serial->minor; + port_extra = qt2_get_port_private(port); + + 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) + return -ESPIPE; + if (copy_from_user(&value, (unsigned int *)arg, + sizeof(unsigned int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (value & TIOCM_RTS) + mcr_value |= SERIAL_MCR_RTS; + if (value & TIOCM_DTR) + mcr_value |= SERIAL_MCR_DTR; + if (value & TIOCM_LOOP) + mcr_value |= SERIAL_MCR_LOOP; + break; + case TIOCMBIC: + if (value & TIOCM_RTS) + mcr_value &= ~SERIAL_MCR_RTS; + if (value & TIOCM_DTR) + mcr_value &= ~SERIAL_MCR_DTR; + if (value & TIOCM_LOOP) + mcr_value &= ~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) { + return -ESPIPE; + } else { + port_extra->shadowMCR = mcr_value; + return 0; + } + } else if (cmd == TIOCMIWAIT) { + dbg("%s() port %d, cmd == TIOCMIWAIT enter", + __func__, port->number); + prev_msr_value = port_extra->shadowMSR & SERIAL_MSR_MASK; + while (1) { + add_wait_queue(&port_extra->wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + dbg("%s(): port %d, cmd == TIOCMIWAIT here\n", + __func__, port->number); + remove_wait_queue(&port_extra->wait, &wait); + /* see if a signal woke us up */ + if (signal_pending(current)) + return -ERESTARTSYS; + msr_value = port_extra->shadowMSR & 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))) || + (arg & TIOCM_DSR && + ((prev_msr_value & SERIAL_MSR_DSR) == + (msr_value & SERIAL_MSR_DSR))) || + (arg & TIOCM_CD && + ((prev_msr_value & SERIAL_MSR_CD) == + (msr_value & SERIAL_MSR_CD))) || + (arg & TIOCM_CTS && + ((prev_msr_value & SERIAL_MSR_CTS) == + (msr_value & SERIAL_MSR_CTS)))) { + return 0; + } + } /* end inifinite while */ + } else { + /* any other ioctls we don't know about come here */ + dbg("%s(): No ioctl for that one. port = %d", __func__, + port->number); + return -ENOIOCTLCMD; + } +} + +static int qt2_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_serial *serial = port->serial; + + __u8 mcr_value; /* Modem Control Register value */ + __u8 msr_value; /* Modem Status Register value */ + unsigned int result = 0; + int status; + unsigned int UartNumber; + + if (serial == NULL) + return -ENODEV; + + dbg("%s(): port %d, tty =0x%p", __func__, port->number, tty); + UartNumber = tty->index - serial->minor; + dbg("UartNumber is %d", UartNumber); + + status = qt2_box_get_register(port->serial, UartNumber, + QT2_MODEM_CONTROL_REGISTER, &mcr_value); + if (status >= 0) { + status = qt2_box_get_register(port->serial, UartNumber, + QT2_MODEM_STATUS_REGISTER, &msr_value); + } + if (status >= 0) { + result = ((mcr_value & SERIAL_MCR_DTR) ? TIOCM_DTR : 0) + /*DTR set */ + | ((mcr_value & SERIAL_MCR_RTS) ? TIOCM_RTS : 0) + /*RTS set */ + | ((msr_value & SERIAL_MSR_CTS) ? TIOCM_CTS : 0) + /* CTS set */ + | ((msr_value & SERIAL_MSR_CD) ? TIOCM_CAR : 0) + /*Carrier detect set */ + | ((msr_value & SERIAL_MSR_RI) ? TIOCM_RI : 0) + /* Ring indicator set */ + | ((msr_value & SERIAL_MSR_DSR) ? TIOCM_DSR : 0); + /* DSR set */ + return result; + } else { + return -ESPIPE; + } +} + +static int qt2_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_serial *serial = port->serial; + __u8 mcr_value; /* Modem Control Register value */ + int status; + unsigned int UartNumber; + + if (serial == NULL) + return -ENODEV; + + UartNumber = tty->index - serial->minor; + dbg("%s(): port %d, UartNumber %d", __func__, port->number, UartNumber); + + status = qt2_box_get_register(port->serial, UartNumber, + QT2_MODEM_CONTROL_REGISTER, &mcr_value); + if (status < 0) + return -ESPIPE; + + /* 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); + if (set & TIOCM_RTS) + mcr_value |= SERIAL_MCR_RTS; + if (set & TIOCM_DTR) + mcr_value |= SERIAL_MCR_DTR; + if (set & TIOCM_LOOP) + mcr_value |= SERIAL_MCR_LOOP; + + status = qt2_box_set_register(port->serial, UartNumber, + QT2_MODEM_CONTROL_REGISTER, mcr_value); + if (status < 0) + return -ESPIPE; + else + return 0; +} + /* internal, private helper functions for the driver */ /* Power up the FPGA in the box to get it working */ @@ -643,7 +1150,7 @@ static int qt2_conf_uart(struct usb_serial *serial, unsigned short Uart_Number, return result; } -/** @brief Callback for asynchronous submission of URBs on bulk in +/** @brief Callback for asynchronous submission of read URBs on bulk in * endpoints * * Registered in qt2_open_port(), used to deal with incomming data @@ -678,7 +1185,7 @@ static void qt2_read_bulk_callback(struct urb *urb) if (urb->status) { /* read didn't go well */ dev_extra->ReadBulkStopped = true; - dbg("%s(): nonzero write bulk status received: %d", + dbg("%s(): nonzero bulk read status received: %d", __func__, urb->status); return; } @@ -908,6 +1415,34 @@ __func__); return; } + +/** @brief Callback for asynchronous submission of write URBs on bulk in + * endpoints + * + * Registered in qt2_write(), used to deal with outgoing data + * to the box. + */ +static void qt2_write_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = port->serial; + dbg("%s(): port %d", __func__, port->number); + if (!serial) { + dbg("%s(): bad serial pointer, exiting", __func__); + return; + } + if (urb->status) { + dbg("%s(): nonzero write bulk status received: %d", + __func__, urb->status); + return; + } + + /*port_softint((void *) serial); commented in vendor driver */ + schedule_work(&port->work); + dbg("%s(): port %d exit", __func__, port->number); + return; +} + static void qt2_process_line_status(struct usb_serial_port *port, unsigned char LineStatus) { @@ -922,8 +1457,11 @@ static void qt2_process_modem_status(struct usb_serial_port *port, /* obtain the private structure for the port */ struct quatech2_port *port_extra = qt2_get_port_private(port); port_extra->shadowMSR = ModemStatus; - /* ?? */ wake_up_interruptible(&port_extra->wait); + /* this wakes up the otherwise indefinitely waiting code for + * the TIOCMIWAIT ioctl, so that it can notice that + * port_extra->shadowMSR has changed and the ioctl needs to return. + */ } static void qt2_process_xmit_empty(struct usb_serial_port *port, @@ -980,6 +1518,66 @@ static void qt2_process_rx_char(struct usb_serial_port *port, /*tty_flip_buffer_push(tty);*/ } } + +/** @brief Retreive the value of a register from the device + * + * Issues a GET_REGISTER vendor-spcific request over the USB control + * pipe to obtain a value back from a specific register on a specific + * UART + * @param serial Serial device handle to access the device through + * @param uart_number Which UART the value is wanted from + * @param register_num Which register to read the value from + * @param pValue Pointer to somewhere to put the retrieved value + */ +static int qt2_box_get_register(struct usb_serial *serial, + unsigned char uart_number, unsigned short register_num, + __u8 *pValue) +{ + int result; + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + QT2_GET_SET_REGISTER, 0xC0, register_num, + uart_number, (void *)pValue, sizeof(*pValue), 300); + return result; +} + +/** qt2_box_set_register + * Issue a SET_REGISTER vendor-specific request on the default control pipe + */ +static int qt2_box_set_register(struct usb_serial *serial, + unsigned short Uart_Number, unsigned short Register_Num, + unsigned short Value) +{ + int result; + unsigned short reg_and_byte; + + reg_and_byte = Value; + reg_and_byte = reg_and_byte << 8; + reg_and_byte = reg_and_byte + Register_Num; + + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + QT2_GET_SET_REGISTER, 0x40, reg_and_byte, + Uart_Number, NULL, 0, 300); + return result; +} + + +/** @brief Request the Tx or Rx buffers on the USB side be flushed + * + * Tx flush: When all the currently buffered data has been sent, send an escape + * sequence back up the data stream to us + * Rx flush: add a flag in the data stream now so we know when it's made it's + * way up to us. + */ +static int qt2_box_flush(struct usb_serial *serial, unsigned char uart_number, + unsigned short rcv_or_xmit) +{ + int result; + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + QT2_FLUSH_DEVICE, 0x40, rcv_or_xmit, uart_number, NULL, 0, + 300); + return result; +} + /* * last things in file: stuff to register this driver into the generic * USB serial framework. @@ -995,20 +1593,22 @@ static struct usb_serial_driver quatech2_device = { .id_table = quausb2_id_table, .num_ports = 8, .open = qt2_open, - /*.close = qt_close, - .write = qt_write, - .write_room = qt_write_room, - .chars_in_buffer = qt_chars_in_buffer, - .throttle = qt_throttle, + .close = qt2_close, + .write = qt2_write, + .write_room = qt2_write_room, + .chars_in_buffer = qt2_chars_in_buffer, + /*.throttle = qt_throttle, .unthrottle = qt_unthrottle,*/ .calc_num_ports = qt2_calc_num_ports, - /*.ioctl = qt_ioctl, - .set_termios = qt_set_termios, - .break_ctl = qt_break, - .tiocmget = qt_tiocmget, - .tiocmset = qt_tiocmset,*/ + .ioctl = qt2_ioctl, + /*.set_termios = qt_set_termios, + .break_ctl = qt_break,*/ + .tiocmget = qt2_tiocmget, + .tiocmset = qt2_tiocmset, .attach = qt2_attach, .release = qt2_release, + .read_bulk_callback = qt2_read_bulk_callback, + .write_bulk_callback = qt2_write_bulk_callback, }; static int __init quausb2_usb_init(void) @@ -1038,8 +1638,6 @@ failed_usb_serial_register: return retval; } - - static void __exit quausb2_usb_exit(void) { usb_deregister(&quausb2_usb_driver); |