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

Commit 0eafe4de authored by Donald's avatar Donald Committed by Greg Kroah-Hartman
Browse files

USB: serial: mos7840: add support for MCS7810 devices



This patch added the support of MCS7810 device for the mos7840 driver.
The MCS7810 device supports single USB2.0-to-Serial port with
a LED indicator for reflecting transmission or reception activity.

Signed-off-by: default avatarDonald Lee <donald@asix.com.tw>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c256667f
Loading
Loading
Loading
Loading
+188 −14
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@
#define USB_VENDOR_ID_MOSCHIP           0x9710
#define MOSCHIP_DEVICE_ID_7840          0x7840
#define MOSCHIP_DEVICE_ID_7820          0x7820
#define MOSCHIP_DEVICE_ID_7810          0x7810
/* The native component can have its vendor/device id's overridden
 * in vendor-specific implementations.  Such devices can be handled
 * by making a change here, in moschip_port_id_table, and in
@@ -184,10 +185,16 @@
#define NUM_URBS                        16	/* URB Count */
#define URB_TRANSFER_BUFFER_SIZE        32	/* URB Size  */

/* LED on/off milliseconds*/
#define LED_ON_MS	500
#define LED_OFF_MS	500

static int device_type;

static const struct usb_device_id moschip_port_id_table[] = {
	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)},
	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)},
	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
@@ -209,6 +216,7 @@ static const struct usb_device_id moschip_port_id_table[] = {
static const struct usb_device_id moschip_id_table_combined[] __devinitconst = {
	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
	{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)},
	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)},
	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
	{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
@@ -261,8 +269,13 @@ struct moschip_port {
	struct urb *write_urb_pool[NUM_URBS];
	char busy[NUM_URBS];
	bool read_urb_busy;
};

	/* For device(s) with LED indicator */
	bool has_led;
	bool led_flag;
	struct timer_list led_timer1;	/* Timer for LED on */
	struct timer_list led_timer2;	/* Timer for LED off */
};

static bool debug;

@@ -572,6 +585,69 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
	return ret;
}

static void mos7840_set_led_callback(struct urb *urb)
{
	switch (urb->status) {
	case 0:
		/* Success */
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* This urb is terminated, clean up */
		dbg("%s - urb shutting down with status: %d", __func__,
			urb->status);
		break;
	default:
		dbg("%s - nonzero urb status received: %d", __func__,
			urb->status);
	}
}

static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
				__u16 reg)
{
	struct usb_device *dev = mcs->port->serial->dev;
	struct usb_ctrlrequest *dr = mcs->dr;

	dr->bRequestType = MCS_WR_RTYPE;
	dr->bRequest = MCS_WRREQ;
	dr->wValue = cpu_to_le16(wval);
	dr->wIndex = cpu_to_le16(reg);
	dr->wLength = cpu_to_le16(0);

	usb_fill_control_urb(mcs->control_urb, dev, usb_sndctrlpipe(dev, 0),
		(unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL);

	usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
}

static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg,
				__u16 val)
{
	struct usb_device *dev = port->serial->dev;

	usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE,
			val, reg, NULL, 0, MOS_WDR_TIMEOUT);
}

static void mos7840_led_off(unsigned long arg)
{
	struct moschip_port *mcs = (struct moschip_port *) arg;

	/* Turn off LED */
	mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER);
	mod_timer(&mcs->led_timer2,
				jiffies + msecs_to_jiffies(LED_OFF_MS));
}

static void mos7840_led_flag_off(unsigned long arg)
{
	struct moschip_port *mcs = (struct moschip_port *) arg;

	mcs->led_flag = false;
}

/*****************************************************************************
 * mos7840_interrupt_callback
 *	this is the callback function for when we have received data on the
@@ -792,6 +868,14 @@ static void mos7840_bulk_in_callback(struct urb *urb)
		return;
	}

	/* Turn on LED */
	if (mos7840_port->has_led && !mos7840_port->led_flag) {
		mos7840_port->led_flag = true;
		mos7840_set_led_async(mos7840_port, 0x0301,
					MODEM_CONTROL_REGISTER);
		mod_timer(&mos7840_port->led_timer1,
				jiffies + msecs_to_jiffies(LED_ON_MS));
	}

	mos7840_port->read_urb_busy = true;
	retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
@@ -1554,6 +1638,14 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
	data1 = urb->transfer_buffer;
	dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress);

	/* Turn on LED */
	if (mos7840_port->has_led && !mos7840_port->led_flag) {
		mos7840_port->led_flag = true;
		mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0301);
		mod_timer(&mos7840_port->led_timer1,
				jiffies + msecs_to_jiffies(LED_ON_MS));
	}

	/* send it down the pipe */
	status = usb_submit_urb(urb, GFP_ATOMIC);

@@ -2327,28 +2419,74 @@ static int mos7840_ioctl(struct tty_struct *tty,
	return -ENOIOCTLCMD;
}

static int mos7810_check(struct usb_serial *serial)
{
	int i, pass_count = 0;
	__u16 data = 0, mcr_data = 0;
	__u16 test_pattern = 0x55AA;

	/* Store MCR setting */
	usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
		MCS_RDREQ, MCS_RD_RTYPE, 0x0300, MODEM_CONTROL_REGISTER,
		&mcr_data, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);

	for (i = 0; i < 16; i++) {
		/* Send the 1-bit test pattern out to MCS7810 test pin */
		usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
			MCS_WRREQ, MCS_WR_RTYPE,
			(0x0300 | (((test_pattern >> i) & 0x0001) << 1)),
			MODEM_CONTROL_REGISTER, NULL, 0, MOS_WDR_TIMEOUT);

		/* Read the test pattern back */
		usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
			MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &data,
			VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);

		/* If this is a MCS7810 device, both test patterns must match */
		if (((test_pattern >> i) ^ (~data >> 1)) & 0x0001)
			break;

		pass_count++;
	}

	/* Restore MCR setting */
	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCS_WRREQ,
		MCS_WR_RTYPE, 0x0300 | mcr_data, MODEM_CONTROL_REGISTER, NULL,
		0, MOS_WDR_TIMEOUT);

	if (pass_count == 16)
		return 1;

	return 0;
}

static int mos7840_calc_num_ports(struct usb_serial *serial)
{
	__u16 Data = 0x00;
	int ret = 0;
	__u16 data = 0x00;
	int mos7840_num_ports;

	ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
		MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &Data,
	usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
		MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &data,
		VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);

	if ((Data & 0x01) == 0) {
		mos7840_num_ports = 2;
		serial->num_bulk_in = 2;
		serial->num_bulk_out = 2;
		serial->num_ports = 2;
	if (serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7810 ||
		serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7820) {
		device_type = serial->dev->descriptor.idProduct;
	} else {
		mos7840_num_ports = 4;
		serial->num_bulk_in = 4;
		serial->num_bulk_out = 4;
		serial->num_ports = 4;
		/* For a MCS7840 device GPIO0 must be set to 1 */
		if ((data & 0x01) == 1)
			device_type = MOSCHIP_DEVICE_ID_7840;
		else if (mos7810_check(serial))
			device_type = MOSCHIP_DEVICE_ID_7810;
		else
			device_type = MOSCHIP_DEVICE_ID_7820;
	}

	mos7840_num_ports = (device_type >> 4) & 0x000F;
	serial->num_bulk_in = mos7840_num_ports;
	serial->num_bulk_out = mos7840_num_ports;
	serial->num_ports = mos7840_num_ports;

	return mos7840_num_ports;
}

@@ -2563,6 +2701,34 @@ static int mos7840_startup(struct usb_serial *serial)
			status = -ENOMEM;
			goto error;
		}

		mos7840_port->has_led = false;

		/* Initialize LED timers */
		if (device_type == MOSCHIP_DEVICE_ID_7810) {
			mos7840_port->has_led = true;

			init_timer(&mos7840_port->led_timer1);
			mos7840_port->led_timer1.function = mos7840_led_off;
			mos7840_port->led_timer1.expires =
					jiffies + msecs_to_jiffies(LED_ON_MS);
			mos7840_port->led_timer1.data =
						(unsigned long)mos7840_port;

			init_timer(&mos7840_port->led_timer2);
			mos7840_port->led_timer2.function =
						mos7840_led_flag_off;
			mos7840_port->led_timer2.expires =
					jiffies + msecs_to_jiffies(LED_OFF_MS);
			mos7840_port->led_timer2.data =
						(unsigned long)mos7840_port;

			mos7840_port->led_flag = false;

			/* Turn off LED */
			mos7840_set_led_sync(serial->port[i],
						MODEM_CONTROL_REGISTER, 0x0300);
		}
	}
	dbg ("mos7840_startup: all ports configured...........");

@@ -2654,6 +2820,14 @@ static void mos7840_release(struct usb_serial *serial)
		mos7840_port = mos7840_get_port_private(serial->port[i]);
		dbg("mos7840_port %d = %p", i, mos7840_port);
		if (mos7840_port) {
			if (mos7840_port->has_led) {
				/* Turn off LED */
				mos7840_set_led_sync(mos7840_port->port,
						MODEM_CONTROL_REGISTER, 0x0300);

				del_timer_sync(&mos7840_port->led_timer1);
				del_timer_sync(&mos7840_port->led_timer2);
			}
			kfree(mos7840_port->ctrl_buf);
			kfree(mos7840_port->dr);
			kfree(mos7840_port);