mirror of
https://github.com/torvalds/linux.git
synced 2024-12-24 03:42:52 +00:00
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:
parent
c256667f04
commit
0eafe4de1a
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user