// SPDX-License-Identifier: GPL-2.0 /* * Ours Technology Inc. OTi-6858 USB to serial adapter driver. * * Copyleft (C) 2007 Kees Lemmens (adapted for kernel 2.6.20) * Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail) * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2003 IBM Corp. * * Many thanks to the authors of pl2303 driver: all functions in this file * are heavily based on pl2303 code, buffering code is a 1-to-1 copy. * * Warning! You use this driver on your own risk! The only official * description of this device I have is datasheet from manufacturer, * and it doesn't contain almost any information needed to write a driver. * Almost all knowlegde used while writing this driver was gathered by: * - analyzing traffic between device and the M$ Windows 2000 driver, * - trying different bit combinations and checking pin states * with a voltmeter, * - receiving malformed frames and producing buffer overflows * to learn how errors are reported, * So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE * CONNECTED TO IT! * * See Documentation/usb/usb-serial.rst for more information on using this * driver * * TODO: * - implement correct flushing for ioctls and oti6858_close() * - check how errors (rx overflow, parity error, framing error) are reported * - implement oti6858_break_ctl() * - implement more ioctls * - test/implement flow control * - allow setting custom baud rates
*/
new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL); if (!new_setup) { /* we will try again */
schedule_delayed_work(&priv->delayed_setup_work,
msecs_to_jiffies(2)); return;
}
spin_lock_irqsave(&priv->lock, flags);
frame_fmt = priv->pending_setup.frame_fmt;
control = priv->pending_setup.control;
spin_unlock_irqrestore(&priv->lock, flags);
frame_fmt &= ~FMT_DATA_BITS_MASK; switch (cflag & CSIZE) { case CS5:
frame_fmt |= FMT_DATA_BITS_5; break; case CS6:
frame_fmt |= FMT_DATA_BITS_6; break; case CS7:
frame_fmt |= FMT_DATA_BITS_7; break; default: case CS8:
frame_fmt |= FMT_DATA_BITS_8; break;
}
/* manufacturer claims that this device can work with baud rates * up to 3 Mbps; I've tested it only on 115200 bps, so I can't * guarantee that any other baud rate will work (especially * the higher ones)
*/
br = tty_get_baud_rate(tty); if (br == 0) {
divisor = 0;
} else { int real_br; int new_divisor;
br = min(br, OTI6858_MAX_BAUD_RATE);
spin_lock_irqsave(&port->lock, flags); /* clear out any remaining data in the buffer */
kfifo_reset_out(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(&port->dev, "%s(): after buf_clear()\n", __func__);
/* FIXME: check if this is correct (active high/low) */
spin_lock_irqsave(&priv->lock, flags);
control = priv->pending_setup.control; if ((set & TIOCM_RTS) != 0)
control |= CONTROL_RTS_HIGH; if ((set & TIOCM_DTR) != 0)
control |= CONTROL_DTR_HIGH; if ((clear & TIOCM_RTS) != 0)
control &= ~CONTROL_RTS_HIGH; if ((clear & TIOCM_DTR) != 0)
control &= ~CONTROL_DTR_HIGH;
if (control != priv->pending_setup.control)
priv->pending_setup.control = control;
/* FIXME: check if this is correct (active high/low) */ if ((pin_state & PIN_RTS) != 0)
result |= TIOCM_RTS; if ((pin_state & PIN_CTS) != 0)
result |= TIOCM_CTS; if ((pin_state & PIN_DSR) != 0)
result |= TIOCM_DSR; if ((pin_state & PIN_DTR) != 0)
result |= TIOCM_DTR; if ((pin_state & PIN_RI) != 0)
result |= TIOCM_RI; if ((pin_state & PIN_DCD) != 0)
result |= TIOCM_CD;
staticvoid oti6858_read_int_callback(struct urb *urb)
{ struct usb_serial_port *port = urb->context; struct oti6858_private *priv = usb_get_serial_port_data(port); int transient = 0, can_recv = 0, resubmit = 1; int status = urb->status;
switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */
dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n",
__func__, status); return; default:
dev_dbg(&urb->dev->dev, "%s(): nonzero urb status received: %d\n",
__func__, status); break;
}
if (!priv->transient) {
u8 delta = xs->pin_state ^ priv->status.pin_state;
if (delta & PIN_MSR_MASK) { if (delta & PIN_CTS)
port->icount.cts++; if (delta & PIN_DSR)
port->icount.dsr++; if (delta & PIN_RI)
port->icount.rng++; if (delta & PIN_DCD)
port->icount.dcd++;
if (status != 0) {
dev_dbg(&urb->dev->dev, "%s(): unable to handle the error, exiting\n", __func__); return;
}
if (urb->actual_length > 0) {
tty_insert_flip_string(&port->port, data, urb->actual_length);
tty_flip_buffer_push(&port->port);
}
/* schedule the interrupt urb */
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result != 0 && result != -EPERM) {
dev_err(&port->dev, "%s(): usb_submit_urb() failed," " error %d\n", __func__, result);
}
}
staticvoid oti6858_write_bulk_callback(struct urb *urb)
{ struct usb_serial_port *port = urb->context; struct oti6858_private *priv = usb_get_serial_port_data(port); int status = urb->status; int result;
switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */
dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n", __func__, status);
priv->flags.write_urb_in_use = 0; return; default: /* error in the urb, so we have to resubmit it */
dev_dbg(&urb->dev->dev, "%s(): nonzero write bulk status received: %d\n", __func__, status);
dev_dbg(&urb->dev->dev, "%s(): overflow in write\n", __func__);
/* schedule the interrupt urb if we are still open */
dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result != 0) {
dev_err(&port->dev, "%s(): failed submitting int urb," " error %d\n", __func__, result);
}
}
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.