USB: ark3116: Callbacks for interrupt and bulk read
Signed-off-by: Bart Hartgers <bart.hartgers@gmail.com> Cc: Mike McCormack <mikem@ring3k.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
546b742968
commit
62d826c8dd
|
@ -605,6 +605,198 @@ static void ark3116_break_ctl(struct tty_struct *tty, int break_state)
|
|||
mutex_unlock(&priv->hw_lock);
|
||||
}
|
||||
|
||||
static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr)
|
||||
{
|
||||
struct ark3116_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->status_lock, flags);
|
||||
priv->msr = msr;
|
||||
spin_unlock_irqrestore(&priv->status_lock, flags);
|
||||
|
||||
if (msr & UART_MSR_ANY_DELTA) {
|
||||
/* update input line counters */
|
||||
if (msr & UART_MSR_DCTS)
|
||||
priv->icount.cts++;
|
||||
if (msr & UART_MSR_DDSR)
|
||||
priv->icount.dsr++;
|
||||
if (msr & UART_MSR_DDCD)
|
||||
priv->icount.dcd++;
|
||||
if (msr & UART_MSR_TERI)
|
||||
priv->icount.rng++;
|
||||
wake_up_interruptible(&priv->delta_msr_wait);
|
||||
}
|
||||
}
|
||||
|
||||
static void ark3116_update_lsr(struct usb_serial_port *port, __u8 lsr)
|
||||
{
|
||||
struct ark3116_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->status_lock, flags);
|
||||
/* combine bits */
|
||||
priv->lsr |= lsr;
|
||||
spin_unlock_irqrestore(&priv->status_lock, flags);
|
||||
|
||||
if (lsr&UART_LSR_BRK_ERROR_BITS) {
|
||||
if (lsr & UART_LSR_BI)
|
||||
priv->icount.brk++;
|
||||
if (lsr & UART_LSR_FE)
|
||||
priv->icount.frame++;
|
||||
if (lsr & UART_LSR_PE)
|
||||
priv->icount.parity++;
|
||||
if (lsr & UART_LSR_OE)
|
||||
priv->icount.overrun++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ark3116_read_int_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
int status = urb->status;
|
||||
const __u8 *data = urb->transfer_buffer;
|
||||
int result;
|
||||
|
||||
switch (status) {
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__func__, status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__func__, status);
|
||||
break;
|
||||
case 0: /* success */
|
||||
/* discovered this by trail and error... */
|
||||
if ((urb->actual_length == 4) && (data[0] == 0xe8)) {
|
||||
const __u8 id = data[1]&UART_IIR_ID;
|
||||
dbg("%s: iir=%02x", __func__, data[1]);
|
||||
if (id == UART_IIR_MSI) {
|
||||
dbg("%s: msr=%02x", __func__, data[3]);
|
||||
ark3116_update_msr(port, data[3]);
|
||||
break;
|
||||
} else if (id == UART_IIR_RLSI) {
|
||||
dbg("%s: lsr=%02x", __func__, data[2]);
|
||||
ark3116_update_lsr(port, data[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Not sure what this data meant...
|
||||
*/
|
||||
usb_serial_debug_data(debug, &port->dev,
|
||||
__func__,
|
||||
urb->actual_length,
|
||||
urb->transfer_buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
result = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&urb->dev->dev,
|
||||
"%s - Error %d submitting interrupt urb\n",
|
||||
__func__, result);
|
||||
}
|
||||
|
||||
|
||||
/* Data comes in via the bulk (data) URB, erors/interrupts via the int URB.
|
||||
* This means that we cannot be sure which data byte has an associated error
|
||||
* condition, so we report an error for all data in the next bulk read.
|
||||
*
|
||||
* Actually, there might even be a window between the bulk data leaving the
|
||||
* ark and reading/resetting the lsr in the read_bulk_callback where an
|
||||
* interrupt for the next data block could come in.
|
||||
* Without somekind of ordering on the ark, we would have to report the
|
||||
* error for the next block of data as well...
|
||||
* For now, let's pretend this can't happen.
|
||||
*/
|
||||
|
||||
static void send_to_tty(struct tty_struct *tty,
|
||||
const unsigned char *chars,
|
||||
size_t size, char flag)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
if (flag == TTY_NORMAL) {
|
||||
tty_insert_flip_string(tty, chars, size);
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < size; ++i)
|
||||
tty_insert_flip_char(tty, chars[i], flag);
|
||||
}
|
||||
}
|
||||
|
||||
static void ark3116_read_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
struct ark3116_private *priv = usb_get_serial_port_data(port);
|
||||
const __u8 *data = urb->transfer_buffer;
|
||||
int status = urb->status;
|
||||
struct tty_struct *tty;
|
||||
unsigned long flags;
|
||||
int result;
|
||||
char flag;
|
||||
__u32 lsr;
|
||||
|
||||
switch (status) {
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__func__, status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__func__, status);
|
||||
break;
|
||||
case 0: /* success */
|
||||
|
||||
spin_lock_irqsave(&priv->status_lock, flags);
|
||||
lsr = priv->lsr;
|
||||
/* clear error bits */
|
||||
priv->lsr &= ~UART_LSR_BRK_ERROR_BITS;
|
||||
spin_unlock_irqrestore(&priv->status_lock, flags);
|
||||
|
||||
if (unlikely(lsr & UART_LSR_BI))
|
||||
flag = TTY_BREAK;
|
||||
else if (unlikely(lsr & UART_LSR_PE))
|
||||
flag = TTY_PARITY;
|
||||
else if (unlikely(lsr & UART_LSR_FE))
|
||||
flag = TTY_FRAME;
|
||||
else
|
||||
flag = TTY_NORMAL;
|
||||
|
||||
tty = tty_port_tty_get(&port->port);
|
||||
if (tty) {
|
||||
tty_buffer_request_room(tty, urb->actual_length + 1);
|
||||
/* overrun is special, not associated with a char */
|
||||
if (unlikely(lsr & UART_LSR_OE))
|
||||
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
|
||||
send_to_tty(tty, data, urb->actual_length, flag);
|
||||
tty_flip_buffer_push(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
/* Throttle the device if requested by tty */
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
port->throttled = port->throttle_req;
|
||||
if (port->throttled) {
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
return;
|
||||
} else
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
/* Continue reading from device */
|
||||
result = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&urb->dev->dev, "%s - failed resubmitting"
|
||||
" read urb, error %d\n", __func__, result);
|
||||
}
|
||||
|
||||
static struct usb_driver ark3116_driver = {
|
||||
.name = "ark3116",
|
||||
.probe = usb_serial_probe,
|
||||
|
@ -631,6 +823,8 @@ static struct usb_serial_driver ark3116_device = {
|
|||
.open = ark3116_open,
|
||||
.close = ark3116_close,
|
||||
.break_ctl = ark3116_break_ctl,
|
||||
.read_int_callback = ark3116_read_int_callback,
|
||||
.read_bulk_callback = ark3116_read_bulk_callback,
|
||||
};
|
||||
|
||||
static int __init ark3116_init(void)
|
||||
|
|
Loading…
Reference in a new issue