mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-05 16:37:50 +00:00
[netdrvr] usb/hso: throttle to prevent loss of serial data
Patch to stop loss of characters on the hso modems, this patch throttles & unthrottles the modem by not putting out urbs until the tty/line discipline layer has enough space for newly received packets. serial ports. This is required for firmware diagnostics being done at Option. Signed-off-by: Denis Joseph Barrow <D.Barow@option.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
parent
ead7b9406c
commit
8ef5ba63b9
1 changed files with 234 additions and 92 deletions
|
@ -92,9 +92,6 @@
|
||||||
|
|
||||||
#define HSO_NET_TX_TIMEOUT (HZ*10)
|
#define HSO_NET_TX_TIMEOUT (HZ*10)
|
||||||
|
|
||||||
/* Serial port defines and structs. */
|
|
||||||
#define HSO_SERIAL_FLAG_RX_SENT 0
|
|
||||||
|
|
||||||
#define HSO_SERIAL_MAGIC 0x48534f31
|
#define HSO_SERIAL_MAGIC 0x48534f31
|
||||||
|
|
||||||
/* Number of ttys to handle */
|
/* Number of ttys to handle */
|
||||||
|
@ -179,6 +176,12 @@ struct hso_net {
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum rx_ctrl_state{
|
||||||
|
RX_IDLE,
|
||||||
|
RX_SENT,
|
||||||
|
RX_PENDING
|
||||||
|
};
|
||||||
|
|
||||||
struct hso_serial {
|
struct hso_serial {
|
||||||
struct hso_device *parent;
|
struct hso_device *parent;
|
||||||
int magic;
|
int magic;
|
||||||
|
@ -205,7 +208,7 @@ struct hso_serial {
|
||||||
struct usb_endpoint_descriptor *in_endp;
|
struct usb_endpoint_descriptor *in_endp;
|
||||||
struct usb_endpoint_descriptor *out_endp;
|
struct usb_endpoint_descriptor *out_endp;
|
||||||
|
|
||||||
unsigned long flags;
|
enum rx_ctrl_state rx_state;
|
||||||
u8 rts_state;
|
u8 rts_state;
|
||||||
u8 dtr_state;
|
u8 dtr_state;
|
||||||
unsigned tx_urb_used:1;
|
unsigned tx_urb_used:1;
|
||||||
|
@ -216,6 +219,15 @@ struct hso_serial {
|
||||||
spinlock_t serial_lock;
|
spinlock_t serial_lock;
|
||||||
|
|
||||||
int (*write_data) (struct hso_serial *serial);
|
int (*write_data) (struct hso_serial *serial);
|
||||||
|
/* Hacks required to get flow control
|
||||||
|
* working on the serial receive buffers
|
||||||
|
* so as not to drop characters on the floor.
|
||||||
|
*/
|
||||||
|
int curr_rx_urb_idx;
|
||||||
|
u16 curr_rx_urb_offset;
|
||||||
|
u8 rx_urb_filled[MAX_RX_URBS];
|
||||||
|
struct tasklet_struct unthrottle_tasklet;
|
||||||
|
struct work_struct retry_unthrottle_workqueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hso_device {
|
struct hso_device {
|
||||||
|
@ -271,7 +283,7 @@ struct hso_device {
|
||||||
static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
|
static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
|
||||||
unsigned int set, unsigned int clear);
|
unsigned int set, unsigned int clear);
|
||||||
static void ctrl_callback(struct urb *urb);
|
static void ctrl_callback(struct urb *urb);
|
||||||
static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial);
|
static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial);
|
||||||
static void hso_kick_transmit(struct hso_serial *serial);
|
static void hso_kick_transmit(struct hso_serial *serial);
|
||||||
/* Helper functions */
|
/* Helper functions */
|
||||||
static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int,
|
static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int,
|
||||||
|
@ -287,6 +299,8 @@ static int hso_start_net_device(struct hso_device *hso_dev);
|
||||||
static void hso_free_shared_int(struct hso_shared_int *shared_int);
|
static void hso_free_shared_int(struct hso_shared_int *shared_int);
|
||||||
static int hso_stop_net_device(struct hso_device *hso_dev);
|
static int hso_stop_net_device(struct hso_device *hso_dev);
|
||||||
static void hso_serial_ref_free(struct kref *ref);
|
static void hso_serial_ref_free(struct kref *ref);
|
||||||
|
static void hso_std_serial_read_bulk_callback(struct urb *urb);
|
||||||
|
static int hso_mux_serial_read(struct hso_serial *serial);
|
||||||
static void async_get_intf(struct work_struct *data);
|
static void async_get_intf(struct work_struct *data);
|
||||||
static void async_put_intf(struct work_struct *data);
|
static void async_put_intf(struct work_struct *data);
|
||||||
static int hso_put_activity(struct hso_device *hso_dev);
|
static int hso_put_activity(struct hso_device *hso_dev);
|
||||||
|
@ -458,6 +472,17 @@ static ssize_t hso_sysfs_show_porttype(struct device *dev,
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL);
|
static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL);
|
||||||
|
|
||||||
|
static int hso_urb_to_index(struct hso_serial *serial, struct urb *urb)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
for (idx = 0; idx < serial->num_rx_urbs; idx++)
|
||||||
|
if (serial->rx_urb[idx] == urb)
|
||||||
|
return idx;
|
||||||
|
dev_err(serial->parent->dev, "hso_urb_to_index failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* converts mux value to a port spec value */
|
/* converts mux value to a port spec value */
|
||||||
static u32 hso_mux_to_port(int mux)
|
static u32 hso_mux_to_port(int mux)
|
||||||
{
|
{
|
||||||
|
@ -1039,6 +1064,158 @@ static void _hso_serial_set_termios(struct tty_struct *tty,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hso_resubmit_rx_bulk_urb(struct hso_serial *serial, struct urb *urb)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
#ifdef CONFIG_HSO_AUTOPM
|
||||||
|
usb_mark_last_busy(urb->dev);
|
||||||
|
#endif
|
||||||
|
/* We are done with this URB, resubmit it. Prep the USB to wait for
|
||||||
|
* another frame */
|
||||||
|
usb_fill_bulk_urb(urb, serial->parent->usb,
|
||||||
|
usb_rcvbulkpipe(serial->parent->usb,
|
||||||
|
serial->in_endp->
|
||||||
|
bEndpointAddress & 0x7F),
|
||||||
|
urb->transfer_buffer, serial->rx_data_length,
|
||||||
|
hso_std_serial_read_bulk_callback, serial);
|
||||||
|
/* Give this to the USB subsystem so it can tell us when more data
|
||||||
|
* arrives. */
|
||||||
|
result = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
|
if (result) {
|
||||||
|
dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d\n",
|
||||||
|
__func__, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
struct urb *curr_urb;
|
||||||
|
|
||||||
|
while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) {
|
||||||
|
curr_urb = serial->rx_urb[serial->curr_rx_urb_idx];
|
||||||
|
count = put_rxbuf_data(curr_urb, serial);
|
||||||
|
if (count == -1)
|
||||||
|
return;
|
||||||
|
if (count == 0) {
|
||||||
|
serial->curr_rx_urb_idx++;
|
||||||
|
if (serial->curr_rx_urb_idx >= serial->num_rx_urbs)
|
||||||
|
serial->curr_rx_urb_idx = 0;
|
||||||
|
hso_resubmit_rx_bulk_urb(serial, curr_urb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
struct urb *urb;
|
||||||
|
|
||||||
|
urb = serial->rx_urb[0];
|
||||||
|
if (serial->open_count > 0) {
|
||||||
|
count = put_rxbuf_data(urb, serial);
|
||||||
|
if (count == -1)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Re issue a read as long as we receive data. */
|
||||||
|
|
||||||
|
if (count == 0 && ((urb->actual_length != 0) ||
|
||||||
|
(serial->rx_state == RX_PENDING))) {
|
||||||
|
serial->rx_state = RX_SENT;
|
||||||
|
hso_mux_serial_read(serial);
|
||||||
|
} else
|
||||||
|
serial->rx_state = RX_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* read callback for Diag and CS port */
|
||||||
|
static void hso_std_serial_read_bulk_callback(struct urb *urb)
|
||||||
|
{
|
||||||
|
struct hso_serial *serial = urb->context;
|
||||||
|
int status = urb->status;
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if (!serial) {
|
||||||
|
D1("serial == NULL");
|
||||||
|
return;
|
||||||
|
} else if (status) {
|
||||||
|
log_usb_status(status, __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
D4("\n--- Got serial_read_bulk callback %02x ---", status);
|
||||||
|
D1("Actual length = %d\n", urb->actual_length);
|
||||||
|
DUMP1(urb->transfer_buffer, urb->actual_length);
|
||||||
|
|
||||||
|
/* Anyone listening? */
|
||||||
|
if (serial->open_count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
if (serial->parent->port_spec & HSO_INFO_CRC_BUG) {
|
||||||
|
u32 rest;
|
||||||
|
u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
||||||
|
rest =
|
||||||
|
urb->actual_length %
|
||||||
|
serial->in_endp->wMaxPacketSize;
|
||||||
|
if (((rest == 5) || (rest == 6))
|
||||||
|
&& !memcmp(((u8 *) urb->transfer_buffer) +
|
||||||
|
urb->actual_length - 4, crc_check, 4)) {
|
||||||
|
urb->actual_length -= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Valid data, handle RX data */
|
||||||
|
spin_lock(&serial->serial_lock);
|
||||||
|
serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 1;
|
||||||
|
put_rxbuf_data_and_resubmit_bulk_urb(serial);
|
||||||
|
spin_unlock(&serial->serial_lock);
|
||||||
|
} else if (status == -ENOENT || status == -ECONNRESET) {
|
||||||
|
/* Unlinked - check for throttled port. */
|
||||||
|
D2("Port %d, successfully unlinked urb", serial->minor);
|
||||||
|
spin_lock(&serial->serial_lock);
|
||||||
|
serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0;
|
||||||
|
hso_resubmit_rx_bulk_urb(serial, urb);
|
||||||
|
spin_unlock(&serial->serial_lock);
|
||||||
|
} else {
|
||||||
|
D2("Port %d, status = %d for read urb", serial->minor, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This needs to be a tasklet otherwise we will
|
||||||
|
* end up recursively calling this function.
|
||||||
|
*/
|
||||||
|
void hso_unthrottle_tasklet(struct hso_serial *serial)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&serial->serial_lock, flags);
|
||||||
|
if ((serial->parent->port_spec & HSO_INTF_MUX))
|
||||||
|
put_rxbuf_data_and_resubmit_ctrl_urb(serial);
|
||||||
|
else
|
||||||
|
put_rxbuf_data_and_resubmit_bulk_urb(serial);
|
||||||
|
spin_unlock_irqrestore(&serial->serial_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hso_unthrottle(struct tty_struct *tty)
|
||||||
|
{
|
||||||
|
struct hso_serial *serial = get_serial_by_tty(tty);
|
||||||
|
|
||||||
|
tasklet_hi_schedule(&serial->unthrottle_tasklet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hso_unthrottle_workfunc(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct hso_serial *serial =
|
||||||
|
container_of(work, struct hso_serial,
|
||||||
|
retry_unthrottle_workqueue);
|
||||||
|
hso_unthrottle_tasklet(serial);
|
||||||
|
}
|
||||||
|
|
||||||
/* open the requested serial port */
|
/* open the requested serial port */
|
||||||
static int hso_serial_open(struct tty_struct *tty, struct file *filp)
|
static int hso_serial_open(struct tty_struct *tty, struct file *filp)
|
||||||
{
|
{
|
||||||
|
@ -1064,13 +1241,18 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
|
||||||
tty->driver_data = serial;
|
tty->driver_data = serial;
|
||||||
serial->tty = tty;
|
serial->tty = tty;
|
||||||
|
|
||||||
/* check for port allready opened, if not set the termios */
|
/* check for port already opened, if not set the termios */
|
||||||
serial->open_count++;
|
serial->open_count++;
|
||||||
if (serial->open_count == 1) {
|
if (serial->open_count == 1) {
|
||||||
tty->low_latency = 1;
|
tty->low_latency = 1;
|
||||||
serial->flags = 0;
|
serial->rx_state = RX_IDLE;
|
||||||
/* Force default termio settings */
|
/* Force default termio settings */
|
||||||
_hso_serial_set_termios(tty, NULL);
|
_hso_serial_set_termios(tty, NULL);
|
||||||
|
tasklet_init(&serial->unthrottle_tasklet,
|
||||||
|
(void (*)(unsigned long))hso_unthrottle_tasklet,
|
||||||
|
(unsigned long)serial);
|
||||||
|
INIT_WORK(&serial->retry_unthrottle_workqueue,
|
||||||
|
hso_unthrottle_workfunc);
|
||||||
result = hso_start_serial_device(serial->parent, GFP_KERNEL);
|
result = hso_start_serial_device(serial->parent, GFP_KERNEL);
|
||||||
if (result) {
|
if (result) {
|
||||||
hso_stop_serial_device(serial->parent);
|
hso_stop_serial_device(serial->parent);
|
||||||
|
@ -1117,9 +1299,13 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)
|
||||||
}
|
}
|
||||||
if (!usb_gone)
|
if (!usb_gone)
|
||||||
hso_stop_serial_device(serial->parent);
|
hso_stop_serial_device(serial->parent);
|
||||||
|
tasklet_kill(&serial->unthrottle_tasklet);
|
||||||
|
cancel_work_sync(&serial->retry_unthrottle_workqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!usb_gone)
|
if (!usb_gone)
|
||||||
usb_autopm_put_interface(serial->parent->interface);
|
usb_autopm_put_interface(serial->parent->interface);
|
||||||
|
|
||||||
mutex_unlock(&serial->parent->mutex);
|
mutex_unlock(&serial->parent->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1422,15 +1608,21 @@ static void intr_callback(struct urb *urb)
|
||||||
(1 << i));
|
(1 << i));
|
||||||
if (serial != NULL) {
|
if (serial != NULL) {
|
||||||
D1("Pending read interrupt on port %d\n", i);
|
D1("Pending read interrupt on port %d\n", i);
|
||||||
if (!test_and_set_bit(HSO_SERIAL_FLAG_RX_SENT,
|
spin_lock(&serial->serial_lock);
|
||||||
&serial->flags)) {
|
if (serial->rx_state == RX_IDLE) {
|
||||||
/* Setup and send a ctrl req read on
|
/* Setup and send a ctrl req read on
|
||||||
* port i */
|
* port i */
|
||||||
hso_mux_serial_read(serial);
|
if (!serial->rx_urb_filled[0]) {
|
||||||
|
serial->rx_state = RX_SENT;
|
||||||
|
hso_mux_serial_read(serial);
|
||||||
|
} else
|
||||||
|
serial->rx_state = RX_PENDING;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
D1("Already pending a read on "
|
D1("Already pending a read on "
|
||||||
"port %d\n", i);
|
"port %d\n", i);
|
||||||
}
|
}
|
||||||
|
spin_unlock(&serial->serial_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1532,16 +1724,10 @@ static void ctrl_callback(struct urb *urb)
|
||||||
if (req->bRequestType ==
|
if (req->bRequestType ==
|
||||||
(USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) {
|
(USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) {
|
||||||
/* response to a read command */
|
/* response to a read command */
|
||||||
if (serial->open_count > 0) {
|
serial->rx_urb_filled[0] = 1;
|
||||||
/* handle RX data the normal way */
|
spin_lock(&serial->serial_lock);
|
||||||
put_rxbuf_data(urb, serial);
|
put_rxbuf_data_and_resubmit_ctrl_urb(serial);
|
||||||
}
|
spin_unlock(&serial->serial_lock);
|
||||||
|
|
||||||
/* Re issue a read as long as we receive data. */
|
|
||||||
if (urb->actual_length != 0)
|
|
||||||
hso_mux_serial_read(serial);
|
|
||||||
else
|
|
||||||
clear_bit(HSO_SERIAL_FLAG_RX_SENT, &serial->flags);
|
|
||||||
} else {
|
} else {
|
||||||
hso_put_activity(serial->parent);
|
hso_put_activity(serial->parent);
|
||||||
if (serial->tty)
|
if (serial->tty)
|
||||||
|
@ -1552,91 +1738,42 @@ static void ctrl_callback(struct urb *urb)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* handle RX data for serial port */
|
/* handle RX data for serial port */
|
||||||
static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
|
static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
|
||||||
{
|
{
|
||||||
struct tty_struct *tty = serial->tty;
|
struct tty_struct *tty = serial->tty;
|
||||||
|
int write_length_remaining = 0;
|
||||||
|
int curr_write_len;
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
if (urb == NULL || serial == NULL) {
|
if (urb == NULL || serial == NULL) {
|
||||||
D1("serial = NULL");
|
D1("serial = NULL");
|
||||||
return;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push data to tty */
|
/* Push data to tty */
|
||||||
if (tty && urb->actual_length) {
|
if (tty) {
|
||||||
|
write_length_remaining = urb->actual_length -
|
||||||
|
serial->curr_rx_urb_offset;
|
||||||
D1("data to push to tty");
|
D1("data to push to tty");
|
||||||
tty_insert_flip_string(tty, urb->transfer_buffer,
|
while (write_length_remaining) {
|
||||||
urb->actual_length);
|
if (test_bit(TTY_THROTTLED, &tty->flags))
|
||||||
tty_flip_buffer_push(tty);
|
return -1;
|
||||||
}
|
curr_write_len = tty_insert_flip_string
|
||||||
}
|
(tty, urb->transfer_buffer +
|
||||||
|
serial->curr_rx_urb_offset,
|
||||||
/* read callback for Diag and CS port */
|
write_length_remaining);
|
||||||
static void hso_std_serial_read_bulk_callback(struct urb *urb)
|
serial->curr_rx_urb_offset += curr_write_len;
|
||||||
{
|
write_length_remaining -= curr_write_len;
|
||||||
struct hso_serial *serial = urb->context;
|
tty_flip_buffer_push(tty);
|
||||||
int result;
|
|
||||||
int status = urb->status;
|
|
||||||
|
|
||||||
/* sanity check */
|
|
||||||
if (!serial) {
|
|
||||||
D1("serial == NULL");
|
|
||||||
return;
|
|
||||||
} else if (status) {
|
|
||||||
log_usb_status(status, __func__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
D4("\n--- Got serial_read_bulk callback %02x ---", status);
|
|
||||||
D1("Actual length = %d\n", urb->actual_length);
|
|
||||||
DUMP1(urb->transfer_buffer, urb->actual_length);
|
|
||||||
|
|
||||||
/* Anyone listening? */
|
|
||||||
if (serial->open_count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (status == 0) {
|
|
||||||
if (serial->parent->port_spec & HSO_INFO_CRC_BUG) {
|
|
||||||
u32 rest;
|
|
||||||
u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
|
||||||
rest =
|
|
||||||
urb->actual_length %
|
|
||||||
serial->in_endp->wMaxPacketSize;
|
|
||||||
if (((rest == 5) || (rest == 6))
|
|
||||||
&& !memcmp(((u8 *) urb->transfer_buffer) +
|
|
||||||
urb->actual_length - 4, crc_check, 4)) {
|
|
||||||
urb->actual_length -= 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* Valid data, handle RX data */
|
|
||||||
put_rxbuf_data(urb, serial);
|
|
||||||
} else if (status == -ENOENT || status == -ECONNRESET) {
|
|
||||||
/* Unlinked - check for throttled port. */
|
|
||||||
D2("Port %d, successfully unlinked urb", serial->minor);
|
|
||||||
} else {
|
|
||||||
D2("Port %d, status = %d for read urb", serial->minor, status);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
if (write_length_remaining == 0) {
|
||||||
usb_mark_last_busy(urb->dev);
|
serial->curr_rx_urb_offset = 0;
|
||||||
|
serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0;
|
||||||
/* We are done with this URB, resubmit it. Prep the USB to wait for
|
|
||||||
* another frame */
|
|
||||||
usb_fill_bulk_urb(urb, serial->parent->usb,
|
|
||||||
usb_rcvbulkpipe(serial->parent->usb,
|
|
||||||
serial->in_endp->
|
|
||||||
bEndpointAddress & 0x7F),
|
|
||||||
urb->transfer_buffer, serial->rx_data_length,
|
|
||||||
hso_std_serial_read_bulk_callback, serial);
|
|
||||||
/* Give this to the USB subsystem so it can tell us when more data
|
|
||||||
* arrives. */
|
|
||||||
result = usb_submit_urb(urb, GFP_ATOMIC);
|
|
||||||
if (result) {
|
|
||||||
dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d",
|
|
||||||
__func__, result);
|
|
||||||
}
|
}
|
||||||
|
return write_length_remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Base driver functions */
|
/* Base driver functions */
|
||||||
|
|
||||||
static void hso_log_port(struct hso_device *hso_dev)
|
static void hso_log_port(struct hso_device *hso_dev)
|
||||||
|
@ -1794,9 +1931,13 @@ static int hso_stop_serial_device(struct hso_device *hso_dev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
for (i = 0; i < serial->num_rx_urbs; i++) {
|
for (i = 0; i < serial->num_rx_urbs; i++) {
|
||||||
if (serial->rx_urb[i])
|
if (serial->rx_urb[i]) {
|
||||||
usb_kill_urb(serial->rx_urb[i]);
|
usb_kill_urb(serial->rx_urb[i]);
|
||||||
|
serial->rx_urb_filled[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
serial->curr_rx_urb_idx = 0;
|
||||||
|
serial->curr_rx_urb_offset = 0;
|
||||||
|
|
||||||
if (serial->tx_urb)
|
if (serial->tx_urb)
|
||||||
usb_kill_urb(serial->tx_urb);
|
usb_kill_urb(serial->tx_urb);
|
||||||
|
@ -2741,6 +2882,7 @@ static const struct tty_operations hso_serial_ops = {
|
||||||
.chars_in_buffer = hso_serial_chars_in_buffer,
|
.chars_in_buffer = hso_serial_chars_in_buffer,
|
||||||
.tiocmget = hso_serial_tiocmget,
|
.tiocmget = hso_serial_tiocmget,
|
||||||
.tiocmset = hso_serial_tiocmset,
|
.tiocmset = hso_serial_tiocmset,
|
||||||
|
.unthrottle = hso_unthrottle
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_driver hso_driver = {
|
static struct usb_driver hso_driver = {
|
||||||
|
|
Loading…
Reference in a new issue