Skip to content

Commit

Permalink
USB: serial: mos7840: add support for MCS7810 devices
Browse files Browse the repository at this point in the history
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: Donald Lee <donald@asix.com.tw>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Donald authored and Greg Kroah-Hartman committed Apr 20, 2012
1 parent c256667 commit 0eafe4d
Showing 1 changed file with 188 additions and 14 deletions.
202 changes: 188 additions & 14 deletions drivers/usb/serial/mos7840.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)},
Expand All @@ -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)},
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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...........");

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 0eafe4d

Please sign in to comment.