#define CH341_REQ_MODEM_CTRL 0xA4
#define CH341_REG_BREAK 0x05
+ #define CH341_REG_PRESCALER 0x12
+ #define CH341_REG_DIVISOR 0x13
#define CH341_REG_LCR 0x18
+ #define CH341_REG_LCR2 0x25
+
#define CH341_NBREAK_BITS 0x01
#define CH341_LCR_ENABLE_RX 0x80
#define CH341_LCR_CS5 0x00
#define CH341_QUIRK_LIMITED_PRESCALER BIT(0)
+ #define CH341_QUIRK_SIMULATE_BREAK BIT(1)
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x4348, 0x5523) },
+ { USB_DEVICE(0x1a86, 0x7522) },
{ USB_DEVICE(0x1a86, 0x7523) },
{ USB_DEVICE(0x1a86, 0x5523) },
{ },
u8 msr;
u8 lcr;
unsigned long quirks;
+ unsigned long break_end;
};
static void ch341_set_termios(struct tty_struct *tty,
CH341_MIN_RATE(3),
};
+ /* 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:
*
* 2 <= div <= 256 if fact = 0, or
* 9 <= div <= 256 if fact = 1
*/
- static int ch341_get_divisor(struct ch341_private *priv)
+ static int ch341_get_divisor(struct ch341_private *priv, speed_t speed)
{
unsigned int fact, div, clk_div;
- speed_t speed = priv->baud_rate;
bool force_fact0 = false;
int ps;
* Clamp to supported range, this makes the (ps < 0) and (div < 2)
* sanity checks below redundant.
*/
- speed = clamp(speed, 46U, 3000000U);
+ speed = clamp_val(speed, CH341_MIN_BPS, CH341_MAX_BPS);
/*
* Start with highest possible base clock (fact = 1) that will give a
}
static int ch341_set_baudrate_lcr(struct usb_device *dev,
- struct ch341_private *priv, u8 lcr)
+ struct ch341_private *priv,
+ speed_t baud_rate, u8 lcr)
{
int val;
int r;
- if (!priv->baud_rate)
+ if (!baud_rate)
return -EINVAL;
- val = ch341_get_divisor(priv);
+ val = ch341_get_divisor(priv, baud_rate);
if (val < 0)
return -EINVAL;
*/
val |= BIT(7);
- r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, val);
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
+ CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER,
+ val);
if (r)
return r;
- r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
+ /*
+ * 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.
+ */
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
+ CH341_REG_LCR2 << 8 | CH341_REG_LCR, lcr);
if (r)
return r;
if (r < 0)
goto out;
- r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
+ r = ch341_set_baudrate_lcr(dev, priv, priv->baud_rate, priv->lcr);
if (r < 0)
goto out;
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT);
if (r == -EPIPE) {
- dev_dbg(&port->dev, "break control not supported\n");
- quirks = CH341_QUIRK_LIMITED_PRESCALER;
+ dev_info(&port->dev, "break control not supported, using simulated break\n");
+ quirks = CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK;
r = 0;
goto out;
}
if (baud_rate) {
priv->baud_rate = baud_rate;
- r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr);
+ r = ch341_set_baudrate_lcr(port->serial->dev, priv,
+ priv->baud_rate, lcr);
if (r < 0 && old_termios) {
priv->baud_rate = tty_termios_baud_rate(old_termios);
tty_termios_copy_hw(&tty->termios, old_termios);
ch341_set_handshake(port->serial->dev, priv->mcr);
}
+ /*
+ * 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.
+ */
+ static void 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);
+ unsigned long now, delay;
+ int r;
+
+ 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;
+ }
+
+ 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);
+ }
+
+ restore:
+ /* Restore original baud rate */
+ r = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate,
+ priv->lcr);
+ if (r < 0)
+ dev_err(&port->dev,
+ "restoring original baud rate of %u failed: %d\n",
+ priv->baud_rate, r);
+ }
+
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
{
const uint16_t ch341_break_reg =
((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
struct usb_serial_port *port = tty->driver_data;
+ struct ch341_private *priv = usb_get_serial_port_data(port);
int r;
uint16_t reg_contents;
uint8_t *break_reg;
+ if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) {
+ ch341_simulate_break(tty, break_state);
+ return;
+ }
+
break_reg = kmalloc(2, GFP_KERNEL);
if (!break_reg)
return;
static const struct usb_device_id id_table_cyphidcomrs232[] = {
{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+ { USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
{ } /* Terminating entry */
{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },
{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+ { USB_DEVICE(VENDOR_ID_SAI, PRODUCT_ID_CYPHIDCOM) },
{ USB_DEVICE(VENDOR_ID_POWERCOM, PRODUCT_ID_UPS) },
{ USB_DEVICE(VENDOR_ID_FRWD, PRODUCT_ID_CYPHIDCOM_FRWD) },
{ USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) },
return;
case -EPIPE:
/* Can't call usb_clear_halt while in_interrupt */
- /* FALLS THROUGH */
+ fallthrough;
default:
/* something ugly is going on... */
dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
return;
case -EPIPE:
/* Cannot call usb_clear_halt while in_interrupt */
- /* FALLTHROUGH */
+ fallthrough;
default:
dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
__func__, status);
static void iuu_rxcmd(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
int status = urb->status;
if (status) {
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 1,
read_rxcmd_callback, port);
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
static int iuu_reset(struct usb_serial_port *port, u8 wt)
static void iuu_status_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
int status = urb->status;
dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, 256,
iuu_update_status_callback, port);
- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ usb_submit_urb(port->read_urb, GFP_ATOMIC);
}
static int iuu_status(struct usb_serial_port *port)
static void iuu_led_activity_on(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
char *buf_ptr = port->write_urb->transfer_buffer;
- *buf_ptr++ = IUU_SET_LED;
+
if (xmas) {
- get_random_bytes(buf_ptr, 6);
- *(buf_ptr+7) = 1;
+ buf_ptr[0] = IUU_SET_LED;
+ get_random_bytes(buf_ptr + 1, 6);
+ buf_ptr[7] = 1;
} else {
iuu_rgbf_fill_buffer(buf_ptr, 255, 255, 0, 0, 0, 0, 255);
}
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 8 ,
iuu_rxcmd, port);
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
static void iuu_led_activity_off(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
char *buf_ptr = port->write_urb->transfer_buffer;
+
if (xmas) {
iuu_rxcmd(urb);
return;
- } else {
- *buf_ptr++ = IUU_SET_LED;
- iuu_rgbf_fill_buffer(buf_ptr, 0, 0, 255, 255, 0, 0, 255);
}
+
+ iuu_rgbf_fill_buffer(buf_ptr, 0, 0, 255, 255, 0, 0, 255);
+
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 8 ,
iuu_rxcmd, port);
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
struct iuu_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
- if (count > 256)
- return -ENOMEM;
-
spin_lock_irqsave(&priv->lock, flags);
+ count = min(count, 256 - priv->writelen);
+ if (count == 0)
+ goto out;
+
/* fill the buffer */
memcpy(priv->writebuf + priv->writelen, buf, count);
priv->writelen += count;
+out:
spin_unlock_irqrestore(&priv->lock, flags);
return count;
/* These Quectel products use Quectel's vendor ID */
#define QUECTEL_PRODUCT_EC21 0x0121
#define QUECTEL_PRODUCT_EC25 0x0125
+#define QUECTEL_PRODUCT_EG95 0x0195
#define QUECTEL_PRODUCT_BG96 0x0296
#define QUECTEL_PRODUCT_EP06 0x0306
#define QUECTEL_PRODUCT_EM12 0x0512
.driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25),
.driver_info = RSVD(4) },
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95),
+ .driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
.driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
.driver_info = RSVD(4) | RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0105, 0xff), /* Fibocom NL678 series */
.driver_info = RSVD(6) },
+ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */
+ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */
+ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1406, 0xff) }, /* GosunCn GM500 ECM/NCM */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
dev_dbg(dev, "%s: urb %p port %p has data %p\n", __func__, urb, port, portdata);
if (status == 0) {
- struct usb_ctrlrequest *req_pkt =
- (struct usb_ctrlrequest *)urb->transfer_buffer;
+ struct usb_ctrlrequest *req_pkt = urb->transfer_buffer;
if (!req_pkt) {
dev_dbg(dev, "%s: NULL req_pkt\n", __func__);
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/serial.h>
- #include <linux/sysrq.h>
#include <linux/kfifo.h>
/* The maximum number of ports one device can grab at once */
* Return 0 to continue on with the initialization sequence. Anything
* else will abort it.
* @attach: pointer to the driver's attach function.
- * This will be called when the struct usb_serial structure is fully set
+ * This will be called when the struct usb_serial structure is fully
* set up. Do any local initialization of the device, or any private
* memory structure allocation at this point in time.
* @disconnect: pointer to the driver's disconnect function. This will be
#define to_usb_serial_driver(d) \
container_of(d, struct usb_serial_driver, driver)
- extern int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[],
+ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[],
const char *name, const struct usb_device_id *id_table);
- extern void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[]);
- extern void usb_serial_port_softint(struct usb_serial_port *port);
+ void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[]);
+ void usb_serial_port_softint(struct usb_serial_port *port);
- extern int usb_serial_suspend(struct usb_interface *intf, pm_message_t message);
- extern int usb_serial_resume(struct usb_interface *intf);
+ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message);
+ int usb_serial_resume(struct usb_interface *intf);
/* USB Serial console functions */
#ifdef CONFIG_USB_SERIAL_CONSOLE
- extern void usb_serial_console_init(int minor);
- extern void usb_serial_console_exit(void);
- extern void usb_serial_console_disconnect(struct usb_serial *serial);
+ void usb_serial_console_init(int minor);
+ void usb_serial_console_exit(void);
+ void usb_serial_console_disconnect(struct usb_serial *serial);
#else
static inline void usb_serial_console_init(int minor) { }
static inline void usb_serial_console_exit(void) { }
#endif
/* Functions needed by other parts of the usbserial core */
- extern struct usb_serial_port *usb_serial_port_get_by_minor(unsigned int minor);
- extern void usb_serial_put(struct usb_serial *serial);
- extern int usb_serial_generic_open(struct tty_struct *tty,
- struct usb_serial_port *port);
- extern int usb_serial_generic_write_start(struct usb_serial_port *port,
- gfp_t mem_flags);
- extern int usb_serial_generic_write(struct tty_struct *tty,
- struct usb_serial_port *port, const unsigned char *buf, int count);
- extern void usb_serial_generic_close(struct usb_serial_port *port);
- extern int usb_serial_generic_resume(struct usb_serial *serial);
- extern int usb_serial_generic_write_room(struct tty_struct *tty);
- extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty);
- extern void usb_serial_generic_wait_until_sent(struct tty_struct *tty,
- long timeout);
- extern void usb_serial_generic_read_bulk_callback(struct urb *urb);
- extern void usb_serial_generic_write_bulk_callback(struct urb *urb);
- extern void usb_serial_generic_throttle(struct tty_struct *tty);
- extern void usb_serial_generic_unthrottle(struct tty_struct *tty);
- extern int usb_serial_generic_tiocmiwait(struct tty_struct *tty,
- unsigned long arg);
- extern int usb_serial_generic_get_icount(struct tty_struct *tty,
- struct serial_icounter_struct *icount);
- extern int usb_serial_generic_register(void);
- extern void usb_serial_generic_deregister(void);
- extern int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port,
- gfp_t mem_flags);
- extern void usb_serial_generic_process_read_urb(struct urb *urb);
- extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
- void *dest, size_t size);
- extern int usb_serial_handle_sysrq_char(struct usb_serial_port *port,
- unsigned int ch);
- extern int usb_serial_handle_break(struct usb_serial_port *port);
- extern void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
- struct tty_struct *tty,
- unsigned int status);
+ struct usb_serial_port *usb_serial_port_get_by_minor(unsigned int minor);
+ void usb_serial_put(struct usb_serial *serial);
+ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port);
+ int usb_serial_generic_write_start(struct usb_serial_port *port, gfp_t mem_flags);
+ int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port,
+ const unsigned char *buf, int count);
+ void usb_serial_generic_close(struct usb_serial_port *port);
+ int usb_serial_generic_resume(struct usb_serial *serial);
+ int usb_serial_generic_write_room(struct tty_struct *tty);
+ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty);
+ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout);
+ void usb_serial_generic_read_bulk_callback(struct urb *urb);
+ void usb_serial_generic_write_bulk_callback(struct urb *urb);
+ void usb_serial_generic_throttle(struct tty_struct *tty);
+ void usb_serial_generic_unthrottle(struct tty_struct *tty);
+ int usb_serial_generic_tiocmiwait(struct tty_struct *tty, unsigned long arg);
+ int usb_serial_generic_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount);
+ int usb_serial_generic_register(void);
+ void usb_serial_generic_deregister(void);
+ int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port, gfp_t mem_flags);
+ void usb_serial_generic_process_read_urb(struct urb *urb);
+ int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size);
+
+ #if defined(CONFIG_USB_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+ int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch);
+ int usb_serial_handle_break(struct usb_serial_port *port);
+ #else
+ static inline int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
+ {
+ return 0;
+ }
+ static inline int usb_serial_handle_break(struct usb_serial_port *port)
+ {
+ return 0;
+ }
+ #endif
+
+ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
+ struct tty_struct *tty, unsigned int status);
- extern int usb_serial_bus_register(struct usb_serial_driver *device);
- extern void usb_serial_bus_deregister(struct usb_serial_driver *device);
+ int usb_serial_bus_register(struct usb_serial_driver *device);
+ void usb_serial_bus_deregister(struct usb_serial_driver *device);
extern struct bus_type usb_serial_bus_type;
extern struct tty_driver *usb_serial_tty_driver;