Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b40e9dc0 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'usb-serial-4.5-rc1' of...

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: default avatarJohan Hovold <johan@kernel.org>
parents daf27335 6ff9d276
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -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---
+1 −0
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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_port_data(port, port_priv);

	usb_set_serial_data(serial, spriv);
	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;

	port_priv = usb_get_serial_port_data(port);
	kfree(port_priv);

	spriv = usb_get_serial_data(serial);
	kfree(spriv);
	return 0;
}

module_usb_serial_driver(serial_drivers, id_table);
+14 −21
Original line number Diff line number Diff line
@@ -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);


+1 −1
Original line number Diff line number Diff line
@@ -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;
	}
Loading