// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk> * Copyright 2007, Werner Cornelius <werner@cornelius-consult.de> * Copyright 2009, Boris Hajduk <boris@hajduk.org> * * ch341.c implements a serial port driver for the Winchiphead CH341. * * The CH341 device can be used to implement an RS232 asynchronous * serial port, an IEEE-1284 parallel printer port or a memory-like * interface. In all cases the CH341 supports an I2C interface as well. * This driver only supports the asynchronous serial interface.
*/
/******************************/ /* interrupt pipe definitions */ /******************************/ /* always 4 interrupt bytes */ /* first irq byte normally 0x08 */ /* second irq byte base 0x7d + below */ /* third irq byte base 0x94 + below */ /* fourth irq byte normally 0xee */
/* second interrupt byte */ #define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
/* status returned in third interrupt answer byte, inverted in data
from irq */ #define CH341_BIT_CTS 0x01 #define CH341_BIT_DSR 0x02 #define CH341_BIT_RI 0x04 #define CH341_BIT_DCD 0x08 #define CH341_BITS_MODEM_STAT 0x0f /* all bits */
/* Break support - the information used to implement this was gleaned from * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
*/
/* Supported range is 46 to 3000000 bps. */ #define CH341_MIN_BPS DIV_ROUND_UP(CH341_CLKRATE, CH341_CLK_DIV(0, 0) * 256) #define CH341_MAX_BPS (CH341_CLKRATE / (CH341_CLK_DIV(3, 0) * 2))
/* * The device line speed is given by the following equation: * * baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where * * 0 <= ps <= 3, * 0 <= fact <= 1, * 2 <= div <= 256 if fact = 0, or * 9 <= div <= 256 if fact = 1
*/ staticint ch341_get_divisor(struct ch341_private *priv, speed_t speed)
{ unsignedint fact, div, clk_div; bool force_fact0 = false; int ps;
/* * Clamp to supported range, this makes the (ps < 0) and (div < 2) * sanity checks below redundant.
*/
speed = clamp_val(speed, CH341_MIN_BPS, CH341_MAX_BPS);
/* * Start with highest possible base clock (fact = 1) that will give a * divisor strictly less than 512.
*/
fact = 1; for (ps = 3; ps >= 0; ps--) { if (speed > ch341_min_rates[ps]) break;
}
/* Some devices require a lower base clock if ps < 3. */ if (ps < 3 && (priv->quirks & CH341_QUIRK_LIMITED_PRESCALER))
force_fact0 = true;
/* Halve base clock (fact = 0) if required. */ if (div < 9 || div > 255 || force_fact0) {
div /= 2;
clk_div *= 2;
fact = 0;
}
if (div < 2) return -EINVAL;
/* * Pick next divisor if resulting rate is closer to the requested one, * scale up to avoid rounding errors on low rates.
*/ if (16 * CH341_CLKRATE / (clk_div * div) - 16 * speed >=
16 * speed - 16 * CH341_CLKRATE / (clk_div * (div + 1)))
div++;
/* * Prefer lower base clock (fact = 0) if even divisor. * * Note that this makes the receiver more tolerant to errors.
*/ if (fact == 1 && div % 2 == 0) {
div /= 2;
fact = 0;
}
return (0x100 - div) << 8 | fact << 2 | ps;
}
staticint ch341_set_baudrate_lcr(struct usb_device *dev, struct ch341_private *priv,
speed_t baud_rate, u8 lcr)
{ int val; int r;
if (!baud_rate) return -EINVAL;
val = ch341_get_divisor(priv, baud_rate); if (val < 0) return -EINVAL;
/* * CH341A buffers data until a full endpoint-size packet (32 bytes) * has been received unless bit 7 is set. * * At least one device with version 0x27 appears to have this bit * inverted.
*/ if (priv->version > 0x27)
val |= BIT(7);
r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER,
val); if (r) return r;
/* * Chip versions before version 0x30 as read using * CH341_REQ_READ_VERSION used separate registers for line control * (stop bits, parity and word length). Version 0x30 and above use * CH341_REG_LCR only and CH341_REG_LCR2 is always set to zero.
*/ if (priv->version < 0x30) return 0;
r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
CH341_REG_LCR2 << 8 | CH341_REG_LCR, lcr); if (r) return r;
/* * A subset of CH34x devices does not support all features. The * prescaler is limited and there is no support for sending a RS232 * break condition. A read failure when trying to set up the latter is * used to detect these devices.
*/
r = usb_control_msg_recv(udev, 0, CH341_REQ_READ_REG,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
CH341_REG_BREAK, 0, &buffer, size,
DEFAULT_TIMEOUT, GFP_KERNEL); if (r == -EPIPE) {
dev_info(&port->dev, "break control not supported, using simulated break\n");
quirks = CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK;
r = 0;
} elseif (r) {
dev_err(&port->dev, "failed to read break control: %d\n", r);
}
/* * A subset of all CH34x devices don't support a real break condition and * reading CH341_REG_BREAK fails (see also ch341_detect_quirks). This function * simulates a break condition by lowering the baud rate to the minimum * supported by the hardware upon enabling the break condition and sending * a NUL byte. * * Incoming data is corrupted while the break condition is being simulated. * * Normally the duration of the break condition can be controlled individually * by userspace using TIOCSBRK and TIOCCBRK or by passing an argument to * TCSBRKP. Due to how the simulation is implemented the duration can't be * controlled. The duration is always about (1s / 46bd * 9bit) = 196ms.
*/ staticint ch341_simulate_break(struct tty_struct *tty, int break_state)
{ struct usb_serial_port *port = tty->driver_data; struct ch341_private *priv = usb_get_serial_port_data(port); unsignedlong now, delay; int r, r2;
if (break_state != 0) {
dev_dbg(&port->dev, "enter break state requested\n");
r = ch341_set_baudrate_lcr(port->serial->dev, priv,
CH341_MIN_BPS,
CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8); if (r < 0) {
dev_err(&port->dev, "failed to change baud rate to %u: %d\n",
CH341_MIN_BPS, r); goto restore;
}
r = tty_put_char(tty, '\0'); if (r < 0) {
dev_err(&port->dev, "failed to write NUL byte for simulated break condition: %d\n",
r); goto restore;
}
/* * Compute expected transmission duration including safety * margin. The original baud rate is only restored after the * computed point in time. * * 11 bits = 1 start, 8 data, 1 stop, 1 margin
*/
priv->break_end = jiffies + (11 * HZ / CH341_MIN_BPS);
return 0;
}
dev_dbg(&port->dev, "leave break state requested\n");
now = jiffies;
if (time_before(now, priv->break_end)) { /* Wait until NUL byte is written */
delay = priv->break_end - now;
dev_dbg(&port->dev, "wait %d ms while transmitting NUL byte at %u baud\n",
jiffies_to_msecs(delay), CH341_MIN_BPS);
schedule_timeout_interruptible(delay);
}
r = 0;
restore: /* Restore original baud rate */
r2 = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate,
priv->lcr); if (r2 < 0) {
dev_err(&port->dev, "restoring original baud rate of %u failed: %d\n",
priv->baud_rate, r2); return r2;
}
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.