You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'usb-serial-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next
Johan writes: USB-serial updates for v4.5-rc1 These updates add support for Moxa UPort 1100-series devices through a new mxu11x0 driver. The cp210x driver gains proper support for cp2108 devices by working around a couple of firmware bugs, and generic wait-until-sent support (e.g. for tcdrain) is also added. Included are also some general clean ups. Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
@@ -475,6 +475,22 @@ config USB_SERIAL_MOS7840
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mos7840. If unsure, choose N.
|
||||
|
||||
config USB_SERIAL_MXUPORT11
|
||||
tristate "USB Moxa UPORT 11x0 Serial Driver"
|
||||
---help---
|
||||
Say Y here if you want to use a MOXA UPort 11x0 Serial hub.
|
||||
|
||||
This driver supports:
|
||||
|
||||
- UPort 1110 : 1 port RS-232 USB to Serial Hub.
|
||||
- UPort 1130 : 1 port RS-422/485 USB to Serial Hub.
|
||||
- UPort 1130I : 1 port RS-422/485 USB to Serial Hub with Isolation.
|
||||
- UPort 1150 : 1 port RS-232/422/485 USB to Serial Hub.
|
||||
- UPort 1150I : 1 port RS-232/422/485 USB to Serial Hub with Isolation.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mxu11x0.
|
||||
|
||||
config USB_SERIAL_MXUPORT
|
||||
tristate "USB Moxa UPORT Serial Driver"
|
||||
---help---
|
||||
|
||||
@@ -38,6 +38,7 @@ obj-$(CONFIG_USB_SERIAL_METRO) += metro-usb.o
|
||||
obj-$(CONFIG_USB_SERIAL_MOS7720) += mos7720.o
|
||||
obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
|
||||
obj-$(CONFIG_USB_SERIAL_MXUPORT) += mxuport.o
|
||||
obj-$(CONFIG_USB_SERIAL_MXUPORT11) += mxu11x0.o
|
||||
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
|
||||
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
|
||||
obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o
|
||||
|
||||
+162
-24
@@ -38,13 +38,14 @@ static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
|
||||
struct ktermios *);
|
||||
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
|
||||
struct ktermios*);
|
||||
static bool cp210x_tx_empty(struct usb_serial_port *port);
|
||||
static int cp210x_tiocmget(struct tty_struct *);
|
||||
static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
|
||||
static int cp210x_tiocmset_port(struct usb_serial_port *port,
|
||||
unsigned int, unsigned int);
|
||||
static void cp210x_break_ctl(struct tty_struct *, int);
|
||||
static int cp210x_startup(struct usb_serial *);
|
||||
static void cp210x_release(struct usb_serial *);
|
||||
static int cp210x_port_probe(struct usb_serial_port *);
|
||||
static int cp210x_port_remove(struct usb_serial_port *);
|
||||
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
|
||||
|
||||
static const struct usb_device_id id_table[] = {
|
||||
@@ -196,8 +197,9 @@ static const struct usb_device_id id_table[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
struct cp210x_serial_private {
|
||||
struct cp210x_port_private {
|
||||
__u8 bInterfaceNumber;
|
||||
bool has_swapped_line_ctl;
|
||||
};
|
||||
|
||||
static struct usb_serial_driver cp210x_device = {
|
||||
@@ -213,10 +215,11 @@ static struct usb_serial_driver cp210x_device = {
|
||||
.close = cp210x_close,
|
||||
.break_ctl = cp210x_break_ctl,
|
||||
.set_termios = cp210x_set_termios,
|
||||
.tx_empty = cp210x_tx_empty,
|
||||
.tiocmget = cp210x_tiocmget,
|
||||
.tiocmset = cp210x_tiocmset,
|
||||
.attach = cp210x_startup,
|
||||
.release = cp210x_release,
|
||||
.port_probe = cp210x_port_probe,
|
||||
.port_remove = cp210x_port_remove,
|
||||
.dtr_rts = cp210x_dtr_rts
|
||||
};
|
||||
|
||||
@@ -299,6 +302,25 @@ static struct usb_serial_driver * const serial_drivers[] = {
|
||||
#define CONTROL_WRITE_DTR 0x0100
|
||||
#define CONTROL_WRITE_RTS 0x0200
|
||||
|
||||
/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
|
||||
struct cp210x_comm_status {
|
||||
__le32 ulErrors;
|
||||
__le32 ulHoldReasons;
|
||||
__le32 ulAmountInInQueue;
|
||||
__le32 ulAmountInOutQueue;
|
||||
u8 bEofReceived;
|
||||
u8 bWaitForImmediate;
|
||||
u8 bReserved;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* CP210X_PURGE - 16 bits passed in wValue of USB request.
|
||||
* SiLabs app note AN571 gives a strange description of the 4 bits:
|
||||
* bit 0 or bit 2 clears the transmit queue and 1 or 3 receive.
|
||||
* writing 1 to all, however, purges cp2108 well enough to avoid the hang.
|
||||
*/
|
||||
#define PURGE_ALL 0x000f
|
||||
|
||||
/*
|
||||
* cp210x_get_config
|
||||
* Reads from the CP210x configuration registers
|
||||
@@ -310,7 +332,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
|
||||
unsigned int *data, int size)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
|
||||
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
|
||||
__le32 *buf;
|
||||
int result, i, length;
|
||||
|
||||
@@ -324,7 +346,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
|
||||
/* Issue the request, attempting to read 'size' bytes */
|
||||
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
||||
request, REQTYPE_INTERFACE_TO_HOST, 0x0000,
|
||||
spriv->bInterfaceNumber, buf, size,
|
||||
port_priv->bInterfaceNumber, buf, size,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
|
||||
/* Convert data into an array of integers */
|
||||
@@ -355,7 +377,7 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
|
||||
unsigned int *data, int size)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
|
||||
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
|
||||
__le32 *buf;
|
||||
int result, i, length;
|
||||
|
||||
@@ -374,13 +396,13 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
|
||||
result = usb_control_msg(serial->dev,
|
||||
usb_sndctrlpipe(serial->dev, 0),
|
||||
request, REQTYPE_HOST_TO_INTERFACE, 0x0000,
|
||||
spriv->bInterfaceNumber, buf, size,
|
||||
port_priv->bInterfaceNumber, buf, size,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
} else {
|
||||
result = usb_control_msg(serial->dev,
|
||||
usb_sndctrlpipe(serial->dev, 0),
|
||||
request, REQTYPE_HOST_TO_INTERFACE, data[0],
|
||||
spriv->bInterfaceNumber, NULL, 0,
|
||||
port_priv->bInterfaceNumber, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -409,6 +431,60 @@ static inline int cp210x_set_config_single(struct usb_serial_port *port,
|
||||
return cp210x_set_config(port, request, &data, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect CP2108 GET_LINE_CTL bug and activate workaround.
|
||||
* Write a known good value 0x800, read it back.
|
||||
* If it comes back swapped the bug is detected.
|
||||
* Preserve the original register value.
|
||||
*/
|
||||
static int cp210x_detect_swapped_line_ctl(struct usb_serial_port *port)
|
||||
{
|
||||
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
|
||||
unsigned int line_ctl_save;
|
||||
unsigned int line_ctl_test;
|
||||
int err;
|
||||
|
||||
err = cp210x_get_config(port, CP210X_GET_LINE_CTL, &line_ctl_save, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
line_ctl_test = 0x800;
|
||||
err = cp210x_set_config(port, CP210X_SET_LINE_CTL, &line_ctl_test, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = cp210x_get_config(port, CP210X_GET_LINE_CTL, &line_ctl_test, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (line_ctl_test == 8) {
|
||||
port_priv->has_swapped_line_ctl = true;
|
||||
line_ctl_save = swab16((u16)line_ctl_save);
|
||||
}
|
||||
|
||||
return cp210x_set_config(port, CP210X_SET_LINE_CTL, &line_ctl_save, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Must always be called instead of cp210x_get_config(CP210X_GET_LINE_CTL)
|
||||
* to workaround cp2108 bug and get correct value.
|
||||
*/
|
||||
static int cp210x_get_line_ctl(struct usb_serial_port *port, unsigned int *ctl)
|
||||
{
|
||||
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
|
||||
int err;
|
||||
|
||||
err = cp210x_get_config(port, CP210X_GET_LINE_CTL, ctl, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Workaround swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */
|
||||
if (port_priv->has_swapped_line_ctl)
|
||||
*ctl = swab16((u16)(*ctl));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cp210x_quantise_baudrate
|
||||
* Quantises the baud rate as per AN205 Table 1
|
||||
@@ -474,10 +550,62 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
|
||||
|
||||
static void cp210x_close(struct usb_serial_port *port)
|
||||
{
|
||||
unsigned int purge_ctl;
|
||||
|
||||
usb_serial_generic_close(port);
|
||||
|
||||
/* Clear both queues; cp2108 needs this to avoid an occasional hang */
|
||||
purge_ctl = PURGE_ALL;
|
||||
cp210x_set_config(port, CP210X_PURGE, &purge_ctl, 2);
|
||||
|
||||
cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read how many bytes are waiting in the TX queue.
|
||||
*/
|
||||
static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
|
||||
u32 *count)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
|
||||
struct cp210x_comm_status *sts;
|
||||
int result;
|
||||
|
||||
sts = kmalloc(sizeof(*sts), GFP_KERNEL);
|
||||
if (!sts)
|
||||
return -ENOMEM;
|
||||
|
||||
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
||||
CP210X_GET_COMM_STATUS, REQTYPE_INTERFACE_TO_HOST,
|
||||
0, port_priv->bInterfaceNumber, sts, sizeof(*sts),
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
if (result == sizeof(*sts)) {
|
||||
*count = le32_to_cpu(sts->ulAmountInOutQueue);
|
||||
result = 0;
|
||||
} else {
|
||||
dev_err(&port->dev, "failed to get comm status: %d\n", result);
|
||||
if (result >= 0)
|
||||
result = -EPROTO;
|
||||
}
|
||||
|
||||
kfree(sts);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool cp210x_tx_empty(struct usb_serial_port *port)
|
||||
{
|
||||
int err;
|
||||
u32 count;
|
||||
|
||||
err = cp210x_get_tx_queue_byte_count(port, &count);
|
||||
if (err)
|
||||
return true;
|
||||
|
||||
return !count;
|
||||
}
|
||||
|
||||
/*
|
||||
* cp210x_get_termios
|
||||
* Reads the baud rate, data bits, parity, stop bits and flow control mode
|
||||
@@ -519,7 +647,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
|
||||
|
||||
cflag = *cflagp;
|
||||
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
cp210x_get_line_ctl(port, &bits);
|
||||
cflag &= ~CSIZE;
|
||||
switch (bits & BITS_DATA_MASK) {
|
||||
case BITS_DATA_5:
|
||||
@@ -687,7 +815,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
||||
|
||||
/* If the number of data bits is to be updated */
|
||||
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
cp210x_get_line_ctl(port, &bits);
|
||||
bits &= ~BITS_DATA_MASK;
|
||||
switch (cflag & CSIZE) {
|
||||
case CS5:
|
||||
@@ -721,7 +849,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
||||
|
||||
if ((cflag & (PARENB|PARODD|CMSPAR)) !=
|
||||
(old_cflag & (PARENB|PARODD|CMSPAR))) {
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
cp210x_get_line_ctl(port, &bits);
|
||||
bits &= ~BITS_PARITY_MASK;
|
||||
if (cflag & PARENB) {
|
||||
if (cflag & CMSPAR) {
|
||||
@@ -747,7 +875,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
||||
}
|
||||
|
||||
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
|
||||
cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
|
||||
cp210x_get_line_ctl(port, &bits);
|
||||
bits &= ~BITS_STOP_MASK;
|
||||
if (cflag & CSTOPB) {
|
||||
bits |= BITS_STOP_2;
|
||||
@@ -862,29 +990,39 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
|
||||
cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
|
||||
}
|
||||
|
||||
static int cp210x_startup(struct usb_serial *serial)
|
||||
static int cp210x_port_probe(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct usb_host_interface *cur_altsetting;
|
||||
struct cp210x_serial_private *spriv;
|
||||
struct cp210x_port_private *port_priv;
|
||||
int ret;
|
||||
|
||||
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
|
||||
if (!spriv)
|
||||
port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
|
||||
if (!port_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
cur_altsetting = serial->interface->cur_altsetting;
|
||||
spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
|
||||
port_priv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
|
||||
|
||||
usb_set_serial_data(serial, spriv);
|
||||
usb_set_serial_port_data(port, port_priv);
|
||||
|
||||
ret = cp210x_detect_swapped_line_ctl(port);
|
||||
if (ret) {
|
||||
kfree(port_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cp210x_release(struct usb_serial *serial)
|
||||
static int cp210x_port_remove(struct usb_serial_port *port)
|
||||
{
|
||||
struct cp210x_serial_private *spriv;
|
||||
struct cp210x_port_private *port_priv;
|
||||
|
||||
spriv = usb_get_serial_data(serial);
|
||||
kfree(spriv);
|
||||
port_priv = usb_get_serial_port_data(port);
|
||||
kfree(port_priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_usb_serial_driver(serial_drivers, id_table);
|
||||
|
||||
@@ -1046,9 +1046,8 @@ static void edge_close(struct usb_serial_port *port)
|
||||
|
||||
edge_port->closePending = true;
|
||||
|
||||
if ((!edge_serial->is_epic) ||
|
||||
((edge_serial->is_epic) &&
|
||||
(edge_serial->epic_descriptor.Supports.IOSPChase))) {
|
||||
if (!edge_serial->is_epic ||
|
||||
edge_serial->epic_descriptor.Supports.IOSPChase) {
|
||||
/* flush and chase */
|
||||
edge_port->chaseResponsePending = true;
|
||||
|
||||
@@ -1061,9 +1060,8 @@ static void edge_close(struct usb_serial_port *port)
|
||||
edge_port->chaseResponsePending = false;
|
||||
}
|
||||
|
||||
if ((!edge_serial->is_epic) ||
|
||||
((edge_serial->is_epic) &&
|
||||
(edge_serial->epic_descriptor.Supports.IOSPClose))) {
|
||||
if (!edge_serial->is_epic ||
|
||||
edge_serial->epic_descriptor.Supports.IOSPClose) {
|
||||
/* close the port */
|
||||
dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLOSE_PORT\n", __func__);
|
||||
send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0);
|
||||
@@ -1612,9 +1610,8 @@ static void edge_break(struct tty_struct *tty, int break_state)
|
||||
struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
|
||||
int status;
|
||||
|
||||
if ((!edge_serial->is_epic) ||
|
||||
((edge_serial->is_epic) &&
|
||||
(edge_serial->epic_descriptor.Supports.IOSPChase))) {
|
||||
if (!edge_serial->is_epic ||
|
||||
edge_serial->epic_descriptor.Supports.IOSPChase) {
|
||||
/* flush and chase */
|
||||
edge_port->chaseResponsePending = true;
|
||||
|
||||
@@ -1628,9 +1625,8 @@ static void edge_break(struct tty_struct *tty, int break_state)
|
||||
}
|
||||
}
|
||||
|
||||
if ((!edge_serial->is_epic) ||
|
||||
((edge_serial->is_epic) &&
|
||||
(edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) {
|
||||
if (!edge_serial->is_epic ||
|
||||
edge_serial->epic_descriptor.Supports.IOSPSetClrBreak) {
|
||||
if (break_state == -1) {
|
||||
dev_dbg(&port->dev, "%s - Sending IOSP_CMD_SET_BREAK\n", __func__);
|
||||
status = send_iosp_ext_cmd(edge_port,
|
||||
@@ -2465,9 +2461,8 @@ static void change_port_settings(struct tty_struct *tty,
|
||||
unsigned char stop_char = STOP_CHAR(tty);
|
||||
unsigned char start_char = START_CHAR(tty);
|
||||
|
||||
if ((!edge_serial->is_epic) ||
|
||||
((edge_serial->is_epic) &&
|
||||
(edge_serial->epic_descriptor.Supports.IOSPSetXChar))) {
|
||||
if (!edge_serial->is_epic ||
|
||||
edge_serial->epic_descriptor.Supports.IOSPSetXChar) {
|
||||
send_iosp_ext_cmd(edge_port,
|
||||
IOSP_CMD_SET_XON_CHAR, start_char);
|
||||
send_iosp_ext_cmd(edge_port,
|
||||
@@ -2494,13 +2489,11 @@ static void change_port_settings(struct tty_struct *tty,
|
||||
}
|
||||
|
||||
/* Set flow control to the configured value */
|
||||
if ((!edge_serial->is_epic) ||
|
||||
((edge_serial->is_epic) &&
|
||||
(edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)))
|
||||
if (!edge_serial->is_epic ||
|
||||
edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)
|
||||
send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow);
|
||||
if ((!edge_serial->is_epic) ||
|
||||
((edge_serial->is_epic) &&
|
||||
(edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)))
|
||||
if (!edge_serial->is_epic ||
|
||||
edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)
|
||||
send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow);
|
||||
|
||||
|
||||
|
||||
@@ -635,7 +635,7 @@ static void mos7840_interrupt_callback(struct urb *urb)
|
||||
* Byte 4 IIR Port 4 (port.number is 3)
|
||||
* Byte 5 FIFO status for both */
|
||||
|
||||
if (length && length > 5) {
|
||||
if (length > 5) {
|
||||
dev_dbg(&urb->dev->dev, "%s", "Wrong data !!!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user