[RFC PATCH] usbserial: add support for serial port on the moschip 7715

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux