// SPDX-License-Identifier: GPL-2.0+ /* * Digi AccelePort USB-4 and USB-2 Serial Converters * * Copyright 2000 by Digi International * * Shamelessly based on Brian Warner's keyspan_pda.c and Greg Kroah-Hartman's * usb-serial driver. * * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com)
*/
#define DRIVER_AUTHOR "Peter Berger , Al Borchers " #define DRIVER_DESC "Digi AccelePort USB-2/USB-4 Serial Converter driver"
/* port output buffer length -- must be <= transfer buffer length - 2 */ /* so we can be sure to send the full buffer in one urb */ #define DIGI_OUT_BUF_SIZE 8
/* port input buffer length -- must be >= transfer buffer length - 3 */ /* so we can be sure to hold at least one full buffer from one urb */ #define DIGI_IN_BUF_SIZE 64
/* retry timeout while sleeping */ #define DIGI_RETRY_TIMEOUT (HZ/10)
/* timeout while waiting for tty output to drain in close */ /* this delay is used twice in close, so the total delay could */ /* be twice this value */ #define DIGI_CLOSE_TIMEOUT (5*HZ)
struct digi_serial {
spinlock_t ds_serial_lock; struct usb_serial_port *ds_oob_port; /* out-of-band port */ int ds_oob_port_num; /* index of out-of-band port */ int ds_device_started;
};
struct digi_port {
spinlock_t dp_port_lock; int dp_port_num; int dp_out_buf_len; unsignedchar dp_out_buf[DIGI_OUT_BUF_SIZE]; int dp_write_urb_in_use; unsignedint dp_modem_signals; int dp_transmit_idle;
wait_queue_head_t dp_transmit_idle_wait; int dp_throttled; int dp_throttle_restart;
wait_queue_head_t dp_flush_wait;
wait_queue_head_t dp_close_wait; /* wait queue for close */
wait_queue_head_t write_wait; struct usb_serial_port *dp_port;
};
/* * Cond Wait Interruptible Timeout Irqrestore * * Do spin_unlock_irqrestore and interruptible_sleep_on_timeout * so that wake ups are not lost if they occur between the unlock * and the sleep. In other words, spin_unlock_irqrestore and * interruptible_sleep_on_timeout are "atomic" with respect to * wake ups. This is used to implement condition variables. * * interruptible_sleep_on_timeout is deprecated and has been replaced * with the equivalent code.
*/
/* * Digi Write OOB Command * * Write commands on the out of band port. Commands are 4 * bytes each, multiple commands can be sent at once, and * no command will be split across USB packets. Returns 0 * if successful, -EINTR if interrupted while sleeping and * the interruptible flag is true, or a negative error * returned by usb_submit_urb.
*/
staticint digi_write_oob_command(struct usb_serial_port *port, unsignedchar *buf, int count, int interruptible)
{ int ret = 0; int len; struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); unsignedlong flags;
spin_lock_irqsave(&oob_priv->dp_port_lock, flags); while (count > 0) { while (oob_priv->dp_write_urb_in_use) {
cond_wait_interruptible_timeout_irqrestore(
&oob_priv->write_wait, DIGI_RETRY_TIMEOUT,
&oob_priv->dp_port_lock, flags); if (interruptible && signal_pending(current)) return -EINTR;
spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
}
/* len must be a multiple of 4, so commands are not split */
len = min(count, oob_port->bulk_out_size); if (len > 4)
len &= ~3;
memcpy(oob_port->write_urb->transfer_buffer, buf, len);
oob_port->write_urb->transfer_buffer_length = len;
ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); if (ret == 0) {
oob_priv->dp_write_urb_in_use = 1;
count -= len;
buf += len;
}
}
spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags); if (ret)
dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n",
__func__, ret); return ret;
}
/* * Digi Write In Band Command * * Write commands on the given port. Commands are 4 * bytes each, multiple commands can be sent at once, and * no command will be split across USB packets. If timeout * is non-zero, write in band command will return after * waiting unsuccessfully for the URB status to clear for * timeout ticks. Returns 0 if successful, or a negative * error returned by digi_write.
*/
staticint digi_write_inb_command(struct usb_serial_port *port, unsignedchar *buf, int count, unsignedlong timeout)
{ int ret = 0; int len; struct digi_port *priv = usb_get_serial_port_data(port); unsignedchar *data = port->write_urb->transfer_buffer; unsignedlong flags;
if (timeout)
timeout += jiffies; else
timeout = ULONG_MAX;
spin_lock_irqsave(&priv->dp_port_lock, flags); while (count > 0 && ret == 0) { while (priv->dp_write_urb_in_use &&
time_before(jiffies, timeout)) {
cond_wait_interruptible_timeout_irqrestore(
&priv->write_wait, DIGI_RETRY_TIMEOUT,
&priv->dp_port_lock, flags); if (signal_pending(current)) return -EINTR;
spin_lock_irqsave(&priv->dp_port_lock, flags);
}
/* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */
len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len); if (len > 4)
len &= ~3;
/* * Digi Set Modem Signals * * Sets or clears DTR and RTS on the port, according to the * modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags * for the modem_signals argument. Returns 0 if successful, * -EINTR if interrupted while sleeping, or a non-zero error * returned by usb_submit_urb.
*/
staticint digi_set_modem_signals(struct usb_serial_port *port, unsignedint modem_signals, int interruptible)
{
/* * Digi Transmit Idle * * Digi transmit idle waits, up to timeout ticks, for the transmitter * to go idle. It returns 0 if successful or a negative error. * * There are race conditions here if more than one process is calling * digi_transmit_idle on the same port at the same time. However, this * is only called from close, and only one process can be in close on a * port at a time, so its ok.
*/
/* copy user data (which can sleep) before getting spin lock */
count = min(count, port->bulk_out_size-2);
count = min(64, count);
/* be sure only one write proceeds at a time */ /* there are races on the port private buffer */
spin_lock_irqsave(&priv->dp_port_lock, flags);
/* wait for urb status clear to submit another urb */ if (priv->dp_write_urb_in_use) { /* buffer data if count is 1 (probably put_char) if possible */ if (count == 1 && priv->dp_out_buf_len < DIGI_OUT_BUF_SIZE) {
priv->dp_out_buf[priv->dp_out_buf_len++] = *buf;
new_len = 1;
} else {
new_len = 0;
}
spin_unlock_irqrestore(&priv->dp_port_lock, flags); return new_len;
}
/* allow space for any buffered data and for new data, up to */ /* transfer buffer size - 2 (for command and length bytes) */
new_len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len);
data_len = new_len + priv->dp_out_buf_len;
if (data_len == 0) {
spin_unlock_irqrestore(&priv->dp_port_lock, flags); return 0;
}
struct usb_serial_port *port = urb->context; struct usb_serial *serial; struct digi_port *priv; struct digi_serial *serial_priv; unsignedlong flags; int ret = 0; int status = urb->status; bool wakeup;
/* port and serial sanity check */ if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) {
pr_err("%s: port or port->private is NULL, status=%d\n",
__func__, status); return;
}
serial = port->serial; if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) {
dev_err(&port->dev, "%s: serial or serial->private is NULL, status=%d\n",
__func__, status); return;
}
ret = digi_write_oob_command(port, buf, 20, 0); if (ret != 0)
dev_dbg(&port->dev, "digi_close: write oob failed, ret=%d\n",
ret); /* wait for final commands on oob port to complete */
prepare_to_wait(&priv->dp_flush_wait, &wait,
TASK_INTERRUPTIBLE);
schedule_timeout(DIGI_CLOSE_TIMEOUT);
finish_wait(&priv->dp_flush_wait, &wait);
/* * Digi Startup Device * * Starts reads on all ports. Must be called AFTER startup, with * urbs initialized. Returns 0 if successful, non-zero error otherwise.
*/
staticint digi_startup_device(struct usb_serial *serial)
{ int i, ret = 0; struct digi_serial *serial_priv = usb_get_serial_data(serial); struct usb_serial_port *port;
/* be sure this happens exactly once */
spin_lock(&serial_priv->ds_serial_lock); if (serial_priv->ds_device_started) {
spin_unlock(&serial_priv->ds_serial_lock); return 0;
}
serial_priv->ds_device_started = 1;
spin_unlock(&serial_priv->ds_serial_lock);
/* start reading from each bulk in endpoint for the device */ /* set USB_DISABLE_SPD flag for write bulk urbs */ for (i = 0; i < serial->type->num_ports + 1; i++) {
port = serial->port[i];
ret = usb_submit_urb(port->read_urb, GFP_KERNEL); if (ret != 0) {
dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d, port=%d\n",
__func__, ret, i); break;
}
} return ret;
}
ret = digi_port_init(serial_priv->ds_oob_port,
serial_priv->ds_oob_port_num); if (ret) {
kfree(serial_priv); return ret;
}
usb_set_serial_data(serial, serial_priv);
return 0;
}
staticvoid digi_disconnect(struct usb_serial *serial)
{ int i;
/* stop reads and writes on all ports */ for (i = 0; i < serial->type->num_ports + 1; i++) {
usb_kill_urb(serial->port[i]->read_urb);
usb_kill_urb(serial->port[i]->write_urb);
}
}
staticvoid digi_read_bulk_callback(struct urb *urb)
{ struct usb_serial_port *port = urb->context; struct digi_port *priv; struct digi_serial *serial_priv; int ret; int status = urb->status;
/* port sanity check, do not resubmit if port is not valid */ if (port == NULL) return;
priv = usb_get_serial_port_data(port); if (priv == NULL) {
dev_err(&port->dev, "%s: port->private is NULL, status=%d\n",
__func__, status); return;
} if (port->serial == NULL ||
(serial_priv = usb_get_serial_data(port->serial)) == NULL) {
dev_err(&port->dev, "%s: serial is bad or serial->private " "is NULL, status=%d\n", __func__, status); return;
}
/* do not resubmit urb if it has any status error */ if (status) {
dev_err(&port->dev, "%s: nonzero read bulk status: status=%d, port=%d\n",
__func__, status, priv->dp_port_num); return;
}
/* handle oob or inb callback, do not resubmit if error */ if (priv->dp_port_num == serial_priv->ds_oob_port_num) { if (digi_read_oob_callback(urb) != 0) return;
} else { if (digi_read_inb_callback(urb) != 0) return;
}
/* continue read */
ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret != 0 && ret != -EPERM) {
dev_err(&port->dev, "%s: failed resubmitting urb, ret=%d, port=%d\n",
__func__, ret, priv->dp_port_num);
}
}
/* * Digi Read INB Callback * * Digi Read INB Callback handles reads on the in band ports, sending * the data on to the tty subsystem. When called we know port and * port->private are not NULL and port->serial has been validated. * It returns 0 if successful, 1 if successful but the port is * throttled, and -1 if the sanity checks failed.
*/
staticint digi_read_inb_callback(struct urb *urb)
{ struct usb_serial_port *port = urb->context; struct digi_port *priv = usb_get_serial_port_data(port); unsignedchar *buf = urb->transfer_buffer; unsignedlong flags; int opcode; int len; int port_status; unsignedchar *data; int tty_flag, throttled;
if (opcode == DIGI_CMD_RECEIVE_DATA && len < 1) {
dev_err(&port->dev, "malformed data packet received\n"); return -1;
}
spin_lock_irqsave(&priv->dp_port_lock, flags);
/* check for throttle; if set, do not resubmit read urb */ /* indicate the read chain needs to be restarted on unthrottle */
throttled = priv->dp_throttled; if (throttled)
priv->dp_throttle_restart = 1;
/* receive data */ if (opcode == DIGI_CMD_RECEIVE_DATA) {
port_status = buf[2];
data = &buf[3];
/* get flag from port_status */
tty_flag = 0;
/* overrun is special, not associated with a char */ if (port_status & DIGI_OVERRUN_ERROR)
tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
/* break takes precedence over parity, */ /* which takes precedence over framing errors */ if (port_status & DIGI_BREAK_ERROR)
tty_flag = TTY_BREAK; elseif (port_status & DIGI_PARITY_ERROR)
tty_flag = TTY_PARITY; elseif (port_status & DIGI_FRAMING_ERROR)
tty_flag = TTY_FRAME;
/* data length is len-1 (one byte of len is port_status) */
--len; if (len > 0) {
tty_insert_flip_string_fixed_flag(&port->port, data,
tty_flag, len);
tty_flip_buffer_push(&port->port);
}
}
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
/* * Digi Read OOB Callback * * Digi Read OOB Callback handles reads on the out of band port. * When called we know port and port->private are not NULL and * the port->serial is valid. It returns 0 if successful, and * -1 if the sanity checks failed.
*/
/* handle each oob command */ for (i = 0; i < urb->actual_length - 3; i += 4) {
opcode = buf[i];
line = buf[i + 1];
status = buf[i + 2];
val = buf[i + 3];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.