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:
Родитель
c256667f04
Коммит
0eafe4de1a
|
@ -114,6 +114,7 @@
|
||||||
#define USB_VENDOR_ID_MOSCHIP 0x9710
|
#define USB_VENDOR_ID_MOSCHIP 0x9710
|
||||||
#define MOSCHIP_DEVICE_ID_7840 0x7840
|
#define MOSCHIP_DEVICE_ID_7840 0x7840
|
||||||
#define MOSCHIP_DEVICE_ID_7820 0x7820
|
#define MOSCHIP_DEVICE_ID_7820 0x7820
|
||||||
|
#define MOSCHIP_DEVICE_ID_7810 0x7810
|
||||||
/* The native component can have its vendor/device id's overridden
|
/* The native component can have its vendor/device id's overridden
|
||||||
* in vendor-specific implementations. Such devices can be handled
|
* in vendor-specific implementations. Such devices can be handled
|
||||||
* by making a change here, in moschip_port_id_table, and in
|
* by making a change here, in moschip_port_id_table, and in
|
||||||
|
@ -184,10 +185,16 @@
|
||||||
#define NUM_URBS 16 /* URB Count */
|
#define NUM_URBS 16 /* URB Count */
|
||||||
#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */
|
#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[] = {
|
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_7840)},
|
||||||
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
|
{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_2)},
|
||||||
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
|
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
|
||||||
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
|
{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 = {
|
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_7840)},
|
||||||
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
|
{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_2)},
|
||||||
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
|
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
|
||||||
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
|
{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];
|
struct urb *write_urb_pool[NUM_URBS];
|
||||||
char busy[NUM_URBS];
|
char busy[NUM_URBS];
|
||||||
bool read_urb_busy;
|
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;
|
static bool debug;
|
||||||
|
|
||||||
|
@ -572,6 +585,69 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
|
||||||
return ret;
|
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
|
* mos7840_interrupt_callback
|
||||||
* this is the callback function for when we have received data on the
|
* 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;
|
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;
|
mos7840_port->read_urb_busy = true;
|
||||||
retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
|
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;
|
data1 = urb->transfer_buffer;
|
||||||
dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress);
|
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 */
|
/* send it down the pipe */
|
||||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
|
|
||||||
|
@ -2327,28 +2419,74 @@ static int mos7840_ioctl(struct tty_struct *tty,
|
||||||
return -ENOIOCTLCMD;
|
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)
|
static int mos7840_calc_num_ports(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
__u16 Data = 0x00;
|
__u16 data = 0x00;
|
||||||
int ret = 0;
|
|
||||||
int mos7840_num_ports;
|
int mos7840_num_ports;
|
||||||
|
|
||||||
ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
||||||
MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &Data,
|
MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, &data,
|
||||||
VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
|
VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT);
|
||||||
|
|
||||||
if ((Data & 0x01) == 0) {
|
if (serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7810 ||
|
||||||
mos7840_num_ports = 2;
|
serial->dev->descriptor.idProduct == MOSCHIP_DEVICE_ID_7820) {
|
||||||
serial->num_bulk_in = 2;
|
device_type = serial->dev->descriptor.idProduct;
|
||||||
serial->num_bulk_out = 2;
|
|
||||||
serial->num_ports = 2;
|
|
||||||
} else {
|
} else {
|
||||||
mos7840_num_ports = 4;
|
/* For a MCS7840 device GPIO0 must be set to 1 */
|
||||||
serial->num_bulk_in = 4;
|
if ((data & 0x01) == 1)
|
||||||
serial->num_bulk_out = 4;
|
device_type = MOSCHIP_DEVICE_ID_7840;
|
||||||
serial->num_ports = 4;
|
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;
|
return mos7840_num_ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2563,6 +2701,34 @@ static int mos7840_startup(struct usb_serial *serial)
|
||||||
status = -ENOMEM;
|
status = -ENOMEM;
|
||||||
goto error;
|
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...........");
|
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]);
|
mos7840_port = mos7840_get_port_private(serial->port[i]);
|
||||||
dbg("mos7840_port %d = %p", i, mos7840_port);
|
dbg("mos7840_port %d = %p", i, mos7840_port);
|
||||||
if (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->ctrl_buf);
|
||||||
kfree(mos7840_port->dr);
|
kfree(mos7840_port->dr);
|
||||||
kfree(mos7840_port);
|
kfree(mos7840_port);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче