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: Donald Lee <donald@asix.com.tw>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Donald 2012-04-19 15:00:45 +08:00 коммит произвёл Greg Kroah-Hartman
Родитель c256667f04
Коммит 0eafe4de1a
1 изменённых файлов: 188 добавлений и 14 удалений

Просмотреть файл

@ -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);