Hi folks,
I'm hoping to solicit some guidance on how a driver for this device
could fit into the usb driver code base. It is an odd creature that
implements a serial port and IEEE 1284-compliant parallel port, both on
the same usb interface :-( As such, it violates some basic assumptions
made by the usbserial core. The patch below implements support for the
serial port by adding a short but ugly device-specific hack to
usb-serial.c, along with the necessary changes to mos7720.c (the
existing driver for Moschip's dual serial port device). Works well for
me and doesn't break anyone else, but I suspect the hack may disqualify
it for inclusion in the kernel.
I'd also like to fully implement the parallel port (as opposed to
disguising it as a serial port, so that it can be used with ppdev),
which doesn't seem to belong in usb/serial at all. Should the device be
divorced from the usbserial core, maybe implemented as a stand-alone
driver in usb/misc?
I'd really like to add a driver for this device (fame! glory!) and would
be happy to work under the direction of Greg and others, if possible.
Thanks in advance.
Happy New Year everyone!
---
drivers/usb/serial/mos7720.c | 106
+++++++++++++++++++++++++++++++++++++--
drivers/usb/serial/usb-serial.c | 21 +++++++-
2 files changed, 120 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 763e32a..a39bdc8 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -81,12 +81,15 @@ struct moschip_serial {
static int debug;
+static struct usb_serial_driver moschip7720_2port_driver;
+
#define USB_VENDOR_ID_MOSCHIP 0x9710
#define MOSCHIP_DEVICE_ID_7720 0x7720
#define MOSCHIP_DEVICE_ID_7715 0x7715
static struct usb_device_id moschip_port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) },
+ { USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7715) },
{ } /* terminating entry */
};
MODULE_DEVICE_TABLE(usb, moschip_port_id_table);
@@ -186,6 +189,75 @@ exit:
}
/*
+ * mos7715_interrupt_callback
+ * this is the 7715's callback function for when we have received
data on the
+ * interrupt endpoint.
+ */
+static void mos7715_interrupt_callback(struct urb *urb)
+{
+ int result;
+ int length;
+ int status = urb->status;
+ __u8 *data;
+ __u8 iir;
+
+ switch (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__,
+ status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __func__,
+ status);
+ goto exit;
+ }
+
+ length = urb->actual_length;
+ data = urb->transfer_buffer;
+
+ /* Structure of data from 7715 device:
+ * Byte 1: IIR serial Port
+ * Byte 2: unused
+ * Byte 2: DSR parallel port
+ * Byte 4: FIFO status for both */
+
+ if (unlikely(length != 4)) {
+ dbg("Wrong data !!!");
+ return;
+ }
+
+ iir = data[0];
+ if (!(iir & 0x01)) { /* serial port interrupt pending */
+ switch (iir & 0x0f) {
+ case SERIAL_IIR_RLS:
+ dbg("Serial Port: Receiver status error or address "
+ "bit detected in 9-bit mode\n");
+ break;
+ case SERIAL_IIR_CTI:
+ dbg("Serial Port: Receiver time out");
+ break;
+ case SERIAL_IIR_MS:
+ dbg("Serial Port: Modem status change");
+ break;
+ }
+ }
+
+exit:
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting control urb\n",
+ __func__, result);
+ return;
+}
+
+/*
* mos7720_bulk_in_callback
* this is the callback function for when we have received data on the
* bulk in endpoint.
@@ -285,7 +357,7 @@ static int send_mos_cmd(struct usb_serial *serial,
__u8 request, __u16 value,
if (value < MOS_MAX_PORT) {
if (product == MOSCHIP_DEVICE_ID_7715)
- value = value*0x100+0x100;
+ value = 0x0200; /* identifies the 7715's single serial port */
else
value = value*0x100+0x200;
} else {
@@ -319,6 +391,32 @@ static int send_mos_cmd(struct usb_serial *serial,
__u8 request, __u16 value,
return status;
}
+
+/*
+ * mos77xx_probe
+ * this function installs the appropriate read interrupt endpoint
callback
+ * depending on whether the device is a 7720 or 7715, thus avoiding
costly
+ * run-time checks in the high-frequency callback routine itself.
+ */
+static int mos77xx_probe(struct usb_serial *serial, const struct
usb_device_id *id)
+{
+ if (id->idProduct == MOSCHIP_DEVICE_ID_7715)
+ moschip7720_2port_driver.read_int_callback =
mos7715_interrupt_callback;
+ else
+ moschip7720_2port_driver.read_int_callback =
mos7720_interrupt_callback;
+
+ return 0;
+}
+
+static int mos77xx_calc_num_ports(struct usb_serial *serial)
+{
+ u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+ if (product == MOSCHIP_DEVICE_ID_7715)
+ return 1;
+
+ return 2;
+}
+
static int mos7720_open(struct tty_struct *tty, struct usb_serial_port
*port)
{
struct usb_serial *serial;
@@ -1581,14 +1679,15 @@ static struct usb_serial_driver
moschip7720_2port_driver = {
.owner = THIS_MODULE,
.name = "moschip7720",
},
- .description = "Moschip 2 port adapter",
+ .description = "Moschip port adapter",
.usb_driver = &usb_driver,
.id_table = moschip_port_id_table,
- .num_ports = 2,
+ .calc_num_ports = mos77xx_calc_num_ports,
.open = mos7720_open,
.close = mos7720_close,
.throttle = mos7720_throttle,
.unthrottle = mos7720_unthrottle,
+ .probe = mos77xx_probe,
.attach = mos7720_startup,
.release = mos7720_release,
.ioctl = mos7720_ioctl,
@@ -1600,7 +1699,6 @@ static struct usb_serial_driver
moschip7720_2port_driver = {
.chars_in_buffer = mos7720_chars_in_buffer,
.break_ctl = mos7720_break,
.read_bulk_callback = mos7720_bulk_in_callback,
- .read_int_callback = mos7720_interrupt_callback,
};
static int __init moschip7720_init(void)
diff --git a/drivers/usb/serial/usb-serial.c
b/drivers/usb/serial/usb-serial.c
index 33c85f7..de55701 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -721,6 +721,7 @@ int usb_serial_probe(struct usb_interface *interface,
int num_bulk_out = 0;
int num_ports = 0;
int max_endpoints;
+ int starting_endpoint = 0;
lock_kernel(); /* guard against unloading a serial driver module */
type = search_serial_device(interface);
@@ -881,6 +882,20 @@ int usb_serial_probe(struct usb_interface *interface,
max_endpoints = max(max_endpoints, num_interrupt_in);
max_endpoints = max(max_endpoints, num_interrupt_out);
max_endpoints = max(max_endpoints, (int)serial->num_ports);
+
+#if defined(CONFIG_USB_SERIAL_MOS7720) ||
defined(CONFIG_USB_SERIAL_MOS7720_MODULE)
+#define USB_VENDOR_ID_MOSCHIP 0x9710 /* TODO: will go in
header file */
+#define MOSCHIP_DEVICE_ID_7715 0x7715 /* TODO: will go in
header file */
+ /* 7715 uses first endpoint pair for parallel port */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_MOSCHIP) &&
+ (le16_to_cpu(dev->descriptor.idProduct) ==
MOSCHIP_DEVICE_ID_7715)) {
+ starting_endpoint = 1; /* skip first (parallel port) endpoint
pair */
+ max_endpoints = 1; /* one endpoint pair for single serial
port */
+ num_bulk_in = 1; /* don't count parallel port endpoints */
+ num_bulk_out = 1;
+ }
+#endif
+
serial->num_port_pointers = max_endpoints;
unlock_kernel();
@@ -907,7 +922,7 @@ int usb_serial_probe(struct usb_interface *interface,
/* set up the endpoint information */
for (i = 0; i < num_bulk_in; ++i) {
- endpoint = bulk_in_endpoint[i];
+ endpoint = bulk_in_endpoint[i + starting_endpoint];
port = serial->port[i];
port->read_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->read_urb) {
@@ -931,7 +946,7 @@ int usb_serial_probe(struct usb_interface *interface,
}
for (i = 0; i < num_bulk_out; ++i) {
- endpoint = bulk_out_endpoint[i];
+ endpoint = bulk_out_endpoint[i + starting_endpoint];
port = serial->port[i];
port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->write_urb) {
@@ -989,7 +1004,7 @@ int usb_serial_probe(struct usb_interface *interface,
if (serial->type->write_int_callback) {
for (i = 0; i < num_interrupt_out; ++i) {
- endpoint = interrupt_out_endpoint[i];
+ endpoint = interrupt_out_endpoint[i + starting_endpoint];
port = serial->port[i];
port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->interrupt_out_urb) {
--
1.6.4.4
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html