aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/usb/usb-serial.txt16
-rw-r--r--drivers/usb/serial/Kconfig11
-rw-r--r--drivers/usb/serial/mos7720.c806
3 files changed, 747 insertions, 86 deletions
diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt
index 540c91ccf86f..f4d214510259 100644
--- a/Documentation/usb/usb-serial.txt
+++ b/Documentation/usb/usb-serial.txt
@@ -440,6 +440,22 @@ Winchiphead CH341 Driver
For any questions or problems with this driver, please contact
frank@kingswood-consulting.co.uk.
+Moschip MCS7720, MCS7715 driver
+
+ These chips are present in devices sold by various manufacturers, such as Syba
+ and Cables Unlimited. There may be others. The 7720 provides two serial
+ ports, and the 7715 provides one serial and one standard PC parallel port.
+ Support for the 7715's parallel port is enabled by a separate option, which
+ will not appear unless parallel port support is first enabled at the top-level
+ of the Device Drivers config menu. Currently only compatibility mode is
+ supported on the parallel port (no ECP/EPP).
+
+ TODO:
+ - Implement ECP/EPP modes for the parallel port.
+ - Baud rates higher than 115200 are currently broken.
+ - Devices with a single serial port based on the Moschip MCS7703 may work
+ with this driver with a simple addition to the usb_device_id table. I
+ don't have one of these devices, so I can't say for sure.
Generic Serial driver
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index efb6dc7aa450..a0b2247eeaa1 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -425,6 +425,17 @@ config USB_SERIAL_MOS7720
To compile this driver as a module, choose M here: the
module will be called mos7720.
+if USB_SERIAL_MOS7720
+config USB_SERIAL_MOS7715_PARPORT
+ bool "Support for parallel port on the Moschip 7715"
+ select PARPORT_NOT_PC
+ depends on PARPORT
+ ---help---
+ Say Y if you have a Moschip 7715 device and would like to use
+ the parallel port it provides. The port will register with
+ the parport subsystem as a low-level driver.
+endif
+
config USB_SERIAL_MOS7840
tristate "USB Moschip 7840/7820 USB Serial Driver"
---help---
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 0d47f2c4d59f..2d35d11d04e3 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -34,12 +34,12 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
-
+#include <linux/parport.h>
/*
* Version Information
*/
-#define DRIVER_VERSION "1.0.0.4F"
+#define DRIVER_VERSION "2.0"
#define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
#define DRIVER_DESC "Moschip USB Serial Driver"
@@ -63,7 +63,7 @@
#define NUM_URBS 16 /* URB Count */
#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */
-/* This structure holds all of the local port information */
+/* This structure holds all of the local serial port information */
struct moschip_port {
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
@@ -74,11 +74,6 @@ struct moschip_port {
struct urb *write_urb_pool[NUM_URBS];
};
-/* This structure holds all of the individual serial device information */
-struct moschip_serial {
- int interrupt_started;
-};
-
static int debug;
static struct usb_serial_driver moschip7720_2port_driver;
@@ -94,6 +89,649 @@ static const struct usb_device_id moschip_port_id_table[] = {
};
MODULE_DEVICE_TABLE(usb, moschip_port_id_table);
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+
+/* initial values for parport regs */
+#define DCR_INIT_VAL 0x0c /* SLCTIN, nINIT */
+#define ECR_INIT_VAL 0x00 /* SPP mode */
+
+struct urbtracker {
+ struct mos7715_parport *mos_parport;
+ struct list_head urblist_entry;
+ struct kref ref_count;
+ struct urb *urb;
+};
+
+enum mos7715_pp_modes {
+ SPP = 0<<5,
+ PS2 = 1<<5, /* moschip calls this 'NIBBLE' mode */
+ PPF = 2<<5, /* moschip calls this 'CB-FIFO mode */
+};
+
+struct mos7715_parport {
+ struct parport *pp; /* back to containing struct */
+ struct kref ref_count; /* to instance of this struct */
+ struct list_head deferred_urbs; /* list deferred async urbs */
+ struct list_head active_urbs; /* list async urbs in flight */
+ spinlock_t listlock; /* protects list access */
+ bool msg_pending; /* usb sync call pending */
+ struct completion syncmsg_compl; /* usb sync call completed */
+ struct tasklet_struct urb_tasklet; /* for sending deferred urbs */
+ struct usb_serial *serial; /* back to containing struct */
+ __u8 shadowECR; /* parallel port regs... */
+ __u8 shadowDCR;
+ atomic_t shadowDSR; /* updated in int-in callback */
+};
+
+/* lock guards against dereferencing NULL ptr in parport ops callbacks */
+static DEFINE_SPINLOCK(release_lock);
+
+enum mos_regs {
+ THR, /* serial port regs */
+ RHR,
+ IER,
+ FCR,
+ ISR,
+ LCR,
+ MCR,
+ LSR,
+ MSR,
+ SPR,
+ DLL,
+ DLM,
+ DPR, /* parallel port regs */
+ DSR,
+ DCR,
+ ECR,
+ SP1_REG, /* device control regs */
+ SP2_REG, /* serial port 2 (7720 only) */
+ PP_REG,
+ SP_CONTROL_REG,
+};
+
+/*
+ * Return the correct value for the Windex field of the setup packet
+ * for a control endpoint message. See the 7715 datasheet.
+ */
+static inline __u16 get_reg_index(enum mos_regs reg)
+{
+ static const __u16 mos7715_index_lookup_table[] = {
+ 0x00, /* THR */
+ 0x00, /* RHR */
+ 0x01, /* IER */
+ 0x02, /* FCR */
+ 0x02, /* ISR */
+ 0x03, /* LCR */
+ 0x04, /* MCR */
+ 0x05, /* LSR */
+ 0x06, /* MSR */
+ 0x07, /* SPR */
+ 0x00, /* DLL */
+ 0x01, /* DLM */
+ 0x00, /* DPR */
+ 0x01, /* DSR */
+ 0x02, /* DCR */
+ 0x0a, /* ECR */
+ 0x01, /* SP1_REG */
+ 0x02, /* SP2_REG (7720 only) */
+ 0x04, /* PP_REG (7715 only) */
+ 0x08, /* SP_CONTROL_REG */
+ };
+ return mos7715_index_lookup_table[reg];
+}
+
+/*
+ * Return the correct value for the upper byte of the Wvalue field of
+ * the setup packet for a control endpoint message.
+ */
+static inline __u16 get_reg_value(enum mos_regs reg)
+{
+ if (reg >= SP1_REG) /* control reg */
+ return 0x0000;
+ else /* parallel port reg (7715 only) */
+ return 0x0100;
+}
+
+/*
+ * Write data byte to the specified device register. The data is embedded in
+ * the value field of the setup packet.
+ */
+static int write_parport_reg(struct mos7715_parport *mos_parport,
+ enum mos_regs reg, __u8 data)
+{
+ struct usb_serial *serial = mos_parport->serial;
+ struct usb_device *usbdev = serial->dev;
+ unsigned int pipe = usb_sndctrlpipe(usbdev, 0);
+ __u8 request = (__u8)0x0e;
+ __u8 requesttype = (__u8)0x40;
+ __u16 value = get_reg_value(reg) + data;
+ __u16 index = get_reg_index(reg);
+ __u16 size = 0;
+ int status;
+ status = usb_control_msg(usbdev, pipe, request, requesttype, value,
+ index, NULL, size, MOS_WDR_TIMEOUT);
+ if (status < 0)
+ dev_err(&usbdev->dev,
+ "mos7720: usb_control_msg() failed: %d", status);
+ return status;
+}
+
+/*
+ * Read data byte from the specified device register. The data returned by the
+ * device is embedded in the value field of the setup packet.
+ */
+static int read_parport_reg(struct mos7715_parport *mos_parport,
+ enum mos_regs reg, __u8 *data)
+{
+ struct usb_device *usbdev = mos_parport->serial->dev;
+ unsigned int pipe = usb_rcvctrlpipe(usbdev, 0);
+ __u8 request = (__u8)0x0d;
+ __u8 requesttype = (__u8)0xc0;
+ __u16 value = get_reg_value(reg);
+ __u16 index = get_reg_index(reg);
+ __u16 size = 1;
+ int status = usb_control_msg(usbdev, pipe, request, requesttype, value,
+ index, data, size, MOS_WDR_TIMEOUT);
+ if (status < 0)
+ dev_err(&usbdev->dev,
+ "mos7720: usb_control_msg() failed: %d", status);
+ return status;
+}
+
+static inline int mos7715_change_mode(struct mos7715_parport *mos_parport,
+ enum mos7715_pp_modes mode)
+{
+ mos_parport->shadowECR = mode;
+ write_parport_reg(mos_parport, ECR, mos_parport->shadowECR);
+ return 0;
+}
+
+static void destroy_mos_parport(struct kref *kref)
+{
+ struct mos7715_parport *mos_parport =
+ container_of(kref, struct mos7715_parport, ref_count);
+
+ dbg("%s called", __func__);
+ kfree(mos_parport);
+}
+
+static void destroy_urbtracker(struct kref *kref)
+{
+ struct urbtracker *urbtrack =
+ container_of(kref, struct urbtracker, ref_count);
+ struct mos7715_parport *mos_parport = urbtrack->mos_parport;
+ dbg("%s called", __func__);
+ usb_free_urb(urbtrack->urb);
+ kfree(urbtrack);
+ kref_put(&mos_parport->ref_count, destroy_mos_parport);
+}
+
+/*
+ * This runs as a tasklet when sending an urb in a non-blocking parallel
+ * port callback had to be deferred because the disconnect mutex could not be
+ * obtained at the time.
+ */
+static void send_deferred_urbs(unsigned long _mos_parport)
+{
+ int ret_val;
+ unsigned long flags;
+ struct mos7715_parport *mos_parport = (void *)_mos_parport;
+ struct urbtracker *urbtrack;
+ struct list_head *cursor, *next;
+
+ dbg("%s called", __func__);
+
+ /* if release function ran, game over */
+ if (unlikely(mos_parport->serial == NULL))
+ return;
+
+ /* try again to get the mutex */
+ if (!mutex_trylock(&mos_parport->serial->disc_mutex)) {
+ dbg("%s: rescheduling tasklet", __func__);
+ tasklet_schedule(&mos_parport->urb_tasklet);
+ return;
+ }
+
+ /* if device disconnected, game over */
+ if (unlikely(mos_parport->serial->disconnected)) {
+ mutex_unlock(&mos_parport->serial->disc_mutex);
+ return;
+ }
+
+ spin_lock_irqsave(&mos_parport->listlock, flags);
+ if (list_empty(&mos_parport->deferred_urbs)) {
+ spin_unlock_irqrestore(&mos_parport->listlock, flags);
+ mutex_unlock(&mos_parport->serial->disc_mutex);
+ dbg("%s: deferred_urbs list empty", __func__);
+ return;
+ }
+
+ /* move contents of deferred_urbs list to active_urbs list and submit */
+ list_for_each_safe(cursor, next, &mos_parport->deferred_urbs)
+ list_move_tail(cursor, &mos_parport->active_urbs);
+ list_for_each_entry(urbtrack, &mos_parport->active_urbs,
+ urblist_entry) {
+ ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
+ dbg("%s: urb submitted", __func__);
+ if (ret_val) {
+ dev_err(&mos_parport->serial->dev->dev,
+ "usb_submit_urb() failed: %d", ret_val);
+ list_del(&urbtrack->urblist_entry);
+ kref_put(&urbtrack->ref_count, destroy_urbtracker);
+ }
+ }
+ spin_unlock_irqrestore(&mos_parport->listlock, flags);
+ mutex_unlock(&mos_parport->serial->disc_mutex);
+}
+
+/* callback for parallel port control urbs submitted asynchronously */
+static void async_complete(struct urb *urb)
+{
+ struct urbtracker *urbtrack = urb->context;
+ int status = urb->status;
+ dbg("%s called", __func__);
+ if (unlikely(status))
+ dbg("%s - nonzero urb status received: %d", __func__, status);
+
+ /* remove the urbtracker from the active_urbs list */
+ spin_lock(&urbtrack->mos_parport->listlock);
+ list_del(&urbtrack->urblist_entry);
+ spin_unlock(&urbtrack->mos_parport->listlock);
+ kref_put(&urbtrack->ref_count, destroy_urbtracker);
+}
+
+static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
+ enum mos_regs reg, __u8 data)
+{
+ struct urbtracker *urbtrack;
+ int ret_val;
+ unsigned long flags;
+ struct usb_ctrlrequest setup;
+ struct usb_serial *serial = mos_parport->serial;
+ struct usb_device *usbdev = serial->dev;
+ dbg("%s called", __func__);
+
+ /* create and initialize the control urb and containing urbtracker */
+ urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC);
+ if (urbtrack == NULL) {
+ dev_err(&usbdev->dev, "out of memory");
+ return -ENOMEM;
+ }
+ kref_get(&mos_parport->ref_count);
+ urbtrack->mos_parport = mos_parport;
+ urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (urbtrack->urb == NULL) {
+ dev_err(&usbdev->dev, "out of urbs");
+ kfree(urbtrack);
+ return -ENOMEM;
+ }
+ setup.bRequestType = (__u8)0x40;
+ setup.bRequest = (__u8)0x0e;
+ setup.wValue = get_reg_value(reg);
+ setup.wIndex = get_reg_index(reg);
+ setup.wLength = 0;
+ usb_fill_control_urb(urbtrack->urb, usbdev,
+ usb_sndctrlpipe(usbdev, 0),
+ (unsigned char *)&setup,
+ NULL, 0, async_complete, urbtrack);
+ kref_init(&urbtrack->ref_count);
+ INIT_LIST_HEAD(&urbtrack->urblist_entry);
+
+ /*
+ * get the disconnect mutex, or add tracker to the deferred_urbs list
+ * and schedule a tasklet to try again later
+ */
+ if (!mutex_trylock(&serial->disc_mutex)) {
+ spin_lock_irqsave(&mos_parport->listlock, flags);
+ list_add_tail(&urbtrack->urblist_entry,
+ &mos_parport->deferred_urbs);
+ spin_unlock_irqrestore(&mos_parport->listlock, flags);
+ tasklet_schedule(&mos_parport->urb_tasklet);
+ dbg("tasklet scheduled");
+ return 0;
+ }
+
+ /* bail if device disconnected */
+ if (serial->disconnected) {
+ kref_put(&urbtrack->ref_count, destroy_urbtracker);
+ mutex_unlock(&serial->disc_mutex);
+ return -ENODEV;
+ }
+
+ /* add the tracker to the active_urbs list and submit */
+ spin_lock_irqsave(&mos_parport->listlock, flags);
+ list_add_tail(&urbtrack->urblist_entry, &mos_parport->active_urbs);
+ spin_unlock_irqrestore(&mos_parport->listlock, flags);
+ ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
+ mutex_unlock(&serial->disc_mutex);
+ if (ret_val) {
+ dev_err(&usbdev->dev,
+ "%s: submit_urb() failed: %d", __func__, ret_val);
+ spin_lock_irqsave(&mos_parport->listlock, flags);
+ list_del(&urbtrack->urblist_entry);
+ spin_unlock_irqrestore(&mos_parport->listlock, flags);
+ kref_put(&urbtrack->ref_count, destroy_urbtracker);
+ return ret_val;
+ }
+ return 0;
+}
+
+/*
+ * This is the the common top part of all parallel port callback operations that
+ * send synchronous messages to the device. This implements convoluted locking
+ * that avoids two scenarios: (1) a port operation is called after usbserial
+ * has called our release function, at which point struct mos7715_parport has
+ * been destroyed, and (2) the device has been disconnected, but usbserial has
+ * not called the release function yet because someone has a serial port open.
+ * The shared release_lock prevents the first, and the mutex and disconnected
+ * flag maintained by usbserial covers the second. We also use the msg_pending
+ * flag to ensure that all synchronous usb messgage calls have completed before
+ * our release function can return.
+ */
+static int parport_prologue(struct parport *pp)
+{
+ struct mos7715_parport *mos_parport;
+
+ spin_lock(&release_lock);
+ mos_parport = pp->private_data;
+ if (unlikely(mos_parport == NULL)) {
+ /* release fn called, port struct destroyed */
+ spin_unlock(&release_lock);
+ return -1;
+ }
+ mos_parport->msg_pending = true; /* synch usb call pending */
+ INIT_COMPLETION(mos_parport->syncmsg_compl);
+ spin_unlock(&release_lock);
+
+ mutex_lock(&mos_parport->serial->disc_mutex);
+ if (mos_parport->serial->disconnected) {
+ /* device disconnected */
+ mutex_unlock(&mos_parport->serial->disc_mutex);
+ mos_parport->msg_pending = false;
+ complete(&mos_parport->syncmsg_compl);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This is the the common bottom part of all parallel port functions that send
+ * synchronous messages to the device.
+ */
+static inline void parport_epilogue(struct parport *pp)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ mutex_unlock(&mos_parport->serial->disc_mutex);
+ mos_parport->msg_pending = false;
+ complete(&mos_parport->syncmsg_compl);
+}
+
+static void parport_mos7715_write_data(struct parport *pp, unsigned char d)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ dbg("%s called: %2.2x", __func__, d);
+ if (parport_prologue(pp) < 0)
+ return;
+ mos7715_change_mode(mos_parport, SPP);
+ write_parport_reg(mos_parport, DPR, (__u8)d);
+ parport_epilogue(pp);
+}
+
+static unsigned char parport_mos7715_read_data(struct parport *pp)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ unsigned char d;
+ dbg("%s called", __func__);
+ if (parport_prologue(pp) < 0)
+ return 0;
+ read_parport_reg(mos_parport, DPR, &d);
+ parport_epilogue(pp);
+ return d;
+}
+
+static void parport_mos7715_write_control(struct parport *pp, unsigned char d)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ __u8 data;
+ dbg("%s called: %2.2x", __func__, d);
+ if (parport_prologue(pp) < 0)
+ return;
+ data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0);
+ write_parport_reg(mos_parport, DCR, data);
+ mos_parport->shadowDCR = data;
+ parport_epilogue(pp);
+}
+
+static unsigned char parport_mos7715_read_control(struct parport *pp)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ __u8 dcr;
+ dbg("%s called", __func__);
+ spin_lock(&release_lock);
+ mos_parport = pp->private_data;
+ if (unlikely(mos_parport == NULL)) {
+ spin_unlock(&release_lock);
+ return 0;
+ }
+ dcr = mos_parport->shadowDCR & 0x0f;
+ spin_unlock(&release_lock);
+ return dcr;
+}
+
+static unsigned char parport_mos7715_frob_control(struct parport *pp,
+ unsigned char mask,
+ unsigned char val)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ __u8 dcr;
+ dbg("%s called", __func__);
+ mask &= 0x0f;
+ val &= 0x0f;
+ if (parport_prologue(pp) < 0)
+ return 0;
+ mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val;
+ write_parport_reg(mos_parport, DCR, mos_parport->shadowDCR);
+ dcr = mos_parport->shadowDCR & 0x0f;
+ parport_epilogue(pp);
+ return dcr;
+}
+
+static unsigned char parport_mos7715_read_status(struct parport *pp)
+{
+ unsigned char status;
+ struct mos7715_parport *mos_parport = pp->private_data;
+ dbg("%s called", __func__);
+ spin_lock(&release_lock);
+ mos_parport = pp->private_data;
+ if (unlikely(mos_parport == NULL)) { /* release called */
+ spin_unlock(&release_lock);
+ return 0;
+ }
+ status = atomic_read(&mos_parport->shadowDSR) & 0xf8;
+ spin_unlock(&release_lock);
+ return status;
+}
+
+static void parport_mos7715_enable_irq(struct parport *pp)
+{
+ dbg("%s called", __func__);
+}
+static void parport_mos7715_disable_irq(struct parport *pp)
+{
+ dbg("%s called", __func__);
+}
+
+static void parport_mos7715_data_forward(struct parport *pp)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ dbg("%s called", __func__);
+ if (parport_prologue(pp) < 0)
+ return;
+ mos7715_change_mode(mos_parport, PS2);
+ mos_parport->shadowDCR &= ~0x20;
+ write_parport_reg(mos_parport, DCR, mos_parport->shadowDCR);
+ parport_epilogue(pp);
+}
+
+static void parport_mos7715_data_reverse(struct parport *pp)
+{
+ struct mos7715_parport *mos_parport = pp->private_data;
+ dbg("%s called", __func__);
+ if (parport_prologue(pp) < 0)
+ return;
+ mos7715_change_mode(mos_parport, PS2);
+ mos_parport->shadowDCR |= 0x20;
+ write_parport_reg(mos_parport, DCR, mos_parport->shadowDCR);
+ parport_epilogue(pp);
+}
+
+static void parport_mos7715_init_state(struct pardevice *dev,
+ struct parport_state *s)
+{
+ dbg("%s called", __func__);
+ s->u.pc.ctr = DCR_INIT_VAL;
+ s->u.pc.ecr = ECR_INIT_VAL;
+}
+
+/* N.B. Parport core code requires that this function not block */
+static void parport_mos7715_save_state(struct parport *pp,
+ struct parport_state *s)
+{
+ struct mos7715_parport *mos_parport;
+ dbg("%s called", __func__);
+ spin_lock(&release_lock);
+ mos_parport = pp->private_data;
+ if (unlikely(mos_parport == NULL)) { /* release called */
+ spin_unlock(&release_lock);
+ return;
+ }
+ s->u.pc.ctr = mos_parport->shadowDCR;
+ s->u.pc.ecr = mos_parport->shadowECR;
+ spin_unlock(&release_lock);
+}
+
+/* N.B. Parport core code requires that this function not block */
+static void parport_mos7715_restore_state(struct parport *pp,
+ struct parport_state *s)
+{
+ struct mos7715_parport *mos_parport;
+ dbg("%s called", __func__);
+ spin_lock(&release_lock);
+ mos_parport = pp->private_data;
+ if (unlikely(mos_parport == NULL)) { /* release called */
+ spin_unlock(&release_lock);
+ return;
+ }
+ write_parport_reg_nonblock(mos_parport, DCR, mos_parport->shadowDCR);
+ write_parport_reg_nonblock(mos_parport, ECR, mos_parport->shadowECR);
+ spin_unlock(&release_lock);
+}
+
+static size_t parport_mos7715_write_compat(struct parport *pp,
+ const void *buffer,
+ size_t len, int flags)
+{
+ int retval;
+ struct mos7715_parport *mos_parport = pp->private_data;
+ int actual_len;
+ dbg("%s called: %u chars", __func__, (unsigned int)len);
+ if (parport_prologue(pp) < 0)
+ return 0;
+ mos7715_change_mode(mos_parport, PPF);
+ retval = usb_bulk_msg(mos_parport->serial->dev,
+ usb_sndbulkpipe(mos_parport->serial->dev, 2),
+ (void *)buffer, len, &actual_len,
+ MOS_WDR_TIMEOUT);
+ parport_epilogue(pp);
+ if (retval) {
+ dev_err(&mos_parport->serial->dev->dev,
+ "mos7720: usb_bulk_msg() failed: %d", retval);
+ return 0;
+ }
+ return actual_len;
+}
+
+static struct parport_operations parport_mos7715_ops = {
+ .owner = THIS_MODULE,
+ .write_data = parport_mos7715_write_data,
+ .read_data = parport_mos7715_read_data,
+
+ .write_control = parport_mos7715_write_control,
+ .read_control = parport_mos7715_read_control,
+ .frob_control = parport_mos7715_frob_control,
+
+ .read_status = parport_mos7715_read_status,
+
+ .enable_irq = parport_mos7715_enable_irq,
+ .disable_irq = parport_mos7715_disable_irq,
+
+ .data_forward = parport_mos7715_data_forward,
+ .data_reverse = parport_mos7715_data_reverse,
+
+ .init_state = parport_mos7715_init_state,
+ .save_state = parport_mos7715_save_state,
+ .restore_state = parport_mos7715_restore_state,
+
+ .compat_write_data = parport_mos7715_write_compat,
+
+ .nibble_read_data = parport_ieee1284_read_nibble,
+ .byte_read_data = parport_ieee1284_read_byte,
+};
+
+/*
+ * Allocate and initialize parallel port control struct, initialize
+ * the parallel port hardware device, and register with the parport subsystem.
+ */
+static int mos7715_parport_init(struct usb_serial *serial)
+{
+ struct mos7715_parport *mos_parport;
+
+ /* allocate and initialize parallel port control struct */
+ mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL);
+ if (mos_parport == NULL) {
+ dbg("mos7715_parport_init: kzalloc failed");
+ return -ENOMEM;
+ }
+ mos_parport->msg_pending = false;
+ kref_init(&mos_parport->ref_count);
+ spin_lock_init(&mos_parport->listlock);
+ INIT_LIST_HEAD(&mos_parport->active_urbs);
+ INIT_LIST_HEAD(&mos_parport->deferred_urbs);
+ usb_set_serial_data(serial, mos_parport); /* hijack private pointer */
+ mos_parport->serial = serial;
+ tasklet_init(&mos_parport->urb_tasklet, send_deferred_urbs,
+ (unsigned long) mos_parport);
+ init_completion(&mos_parport->syncmsg_compl);
+
+ /* cycle parallel port reset bit */
+ write_parport_reg(mos_parport, PP_REG, (__u8)0x80);
+ write_parport_reg(mos_parport, PP_REG, (__u8)0x00);
+
+ /* initialize device registers */
+ mos_parport->shadowDCR = DCR_INIT_VAL;
+ write_parport_reg(mos_parport, DCR, mos_parport->shadowDCR);
+ mos_parport->shadowECR = ECR_INIT_VAL;
+ write_parport_reg(mos_parport, ECR, mos_parport->shadowECR);
+
+ /* register with parport core */
+ mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE,
+ PARPORT_DMA_NONE,
+ &parport_mos7715_ops);
+ if (mos_parport->pp == NULL) {
+ dev_err(&serial->interface->dev,
+ "Could not register parport\n");
+ kref_put(&mos_parport->ref_count, destroy_mos_parport);
+ return -EIO;
+ }
+ mos_parport->pp->private_data = mos_parport;
+ mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP;
+ mos_parport->pp->dev = &serial->interface->dev;
+ parport_announce_port(mos_parport->pp);
+
+ return 0;
+}
+#endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */
/*
* mos7720_interrupt_callback
@@ -109,8 +747,6 @@ static void mos7720_interrupt_callback(struct urb *urb)
__u8 sp1;
__u8 sp2;
- dbg(" : Entering");
-
switch (status) {
case 0:
/* success */
@@ -118,6 +754,7 @@ static void mos7720_interrupt_callback(struct urb *urb)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
+ case -ENODEV:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __func__,
status);
@@ -161,7 +798,7 @@ static void mos7720_interrupt_callback(struct urb *urb)
dbg("Serial Port 1: Receiver time out");
break;
case SERIAL_IIR_MS:
- dbg("Serial Port 1: Modem status change");
+ /* dbg("Serial Port 1: Modem status change"); */
break;
}
@@ -174,7 +811,7 @@ static void mos7720_interrupt_callback(struct urb *urb)
dbg("Serial Port 2: Receiver time out");
break;
case SERIAL_IIR_MS:
- dbg("Serial Port 2: Modem status change");
+ /* dbg("Serial Port 2: Modem status change"); */
break;
}
}
@@ -208,6 +845,7 @@ static void mos7715_interrupt_callback(struct urb *urb)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
+ case -ENODEV:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __func__,
status);
@@ -243,11 +881,21 @@ static void mos7715_interrupt_callback(struct urb *urb)
dbg("Serial Port: Receiver time out");
break;
case SERIAL_IIR_MS:
- dbg("Serial Port: Modem status change");
+ /* dbg("Serial Port: Modem status change"); */
break;
}
}
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+ { /* update local copy of DSR reg */
+ struct usb_serial_port *port = urb->context;
+ struct mos7715_parport *mos_parport = port->serial->private;
+ if (unlikely(mos_parport == NULL))
+ return;
+ atomic_set(&mos_parport->shadowDSR, data[2]);
+ }
+#endif
+
exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
@@ -267,7 +915,6 @@ static void mos7720_bulk_in_callback(struct urb *urb)
int retval;
unsigned char *data ;
struct usb_serial_port *port;
- struct moschip_port *mos7720_port;
struct tty_struct *tty;
int status = urb->status;
@@ -276,13 +923,7 @@ static void mos7720_bulk_in_callback(struct urb *urb)
return;
}
- mos7720_port = urb->context;
- if (!mos7720_port) {
- dbg("NULL mos7720_port pointer");
- return ;
- }
-
- port = mos7720_port->port;
+ port = urb->context;
dbg("Entering...%s", __func__);
@@ -332,8 +973,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb)
return ;
}
- dbg("Entering .........");
-
tty = tty_port_tty_get(&mos7720_port->port->port);
if (tty && mos7720_port->open)
@@ -424,7 +1063,6 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
struct usb_serial *serial;
struct usb_serial_port *port0;
struct urb *urb;
- struct moschip_serial *mos7720_serial;
struct moschip_port *mos7720_port;
int response;
int port_number;
@@ -440,11 +1078,6 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
port0 = serial->port[0];
- mos7720_serial = usb_get_serial_data(serial);
-
- if (mos7720_serial == NULL || port0 == NULL)
- return -ENODEV;
-
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
@@ -549,43 +1182,6 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
data = 0x0c;
send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);
- /* see if we've set up our endpoint info yet *
- * (can't set it up in mos7720_startup as the *
- * structures were not set up at that time.) */
- if (!mos7720_serial->interrupt_started) {
- dbg("Interrupt buffer NULL !!!");
-
- /* not set up yet, so do it now */
- mos7720_serial->interrupt_started = 1;
-
- dbg("To Submit URB !!!");
-
- /* set up our interrupt urb */
- usb_fill_int_urb(port0->interrupt_in_urb, serial->dev,
- usb_rcvintpipe(serial->dev,
- port->interrupt_in_endpointAddress),
- port0->interrupt_in_buffer,
- port0->interrupt_in_urb->transfer_buffer_length,
- mos7720_interrupt_callback, mos7720_port,
- port0->interrupt_in_urb->interval);
-
- /* start interrupt read for this mos7720 this interrupt *
- * will continue as long as the mos7720 is connected */
- dbg("Submit URB over !!!");
- response = usb_submit_urb(port0->interrupt_in_urb, GFP_KERNEL);
- if (response)
- dev_err(&port->dev,
- "%s - Error %d submitting control urb\n",
- __func__, response);
- }
-
- /* set up our bulk in urb */
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev,
- port->bulk_in_endpointAddress),
- port->bulk_in_buffer,
- port->read_urb->transfer_buffer_length,
- mos7720_bulk_in_callback, mos7720_port);
response = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (response)
dev_err(&port->dev, "%s - Error %d submitting read urb\n",
@@ -897,6 +1493,7 @@ static void mos7720_unthrottle(struct tty_struct *tty)
}
}
+/* FIXME: this function does not work */
static int set_higher_rates(struct moschip_port *mos7720_port,
unsigned int baud)
{
@@ -939,6 +1536,7 @@ static int set_higher_rates(struct moschip_port *mos7720_port,
* Set for higher rates *
***********************************************/
+ /* writing baud rate verbatum into uart clock field clearly not right */
data = baud * 0x10;
send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data);
@@ -1308,7 +1906,7 @@ static void mos7720_set_termios(struct tty_struct *tty,
return;
}
- dbg("setting termios - ASPIRE");
+ dbg("%s\n", "setting termios - ASPIRE");
cflag = tty->termios->c_cflag;
@@ -1326,7 +1924,7 @@ static void mos7720_set_termios(struct tty_struct *tty,
change_port_settings(tty, mos7720_port, old_termios);
if (!port->read_urb) {
- dbg("URB KILLED !!!!!");
+ dbg("%s", "URB KILLED !!!!!");
return;
}
@@ -1590,12 +2188,12 @@ static int mos7720_ioctl(struct tty_struct *tty, struct file *file,
static int mos7720_startup(struct usb_serial *serial)
{
- struct moschip_serial *mos7720_serial;
struct moschip_port *mos7720_port;
struct usb_device *dev;
int i;
char data;
u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+ int ret_val;
dbg("%s: Entering ..........", __func__);
@@ -1606,15 +2204,6 @@ static int mos7720_startup(struct usb_serial *serial)
dev = serial->dev;
- /* create our private serial structure */
- mos7720_serial = kzalloc(sizeof(struct moschip_serial), GFP_KERNEL);
- if (mos7720_serial == NULL) {
- dev_err(&dev->dev, "%s - Out of memory\n", __func__);
- return -ENOMEM;
- }
-
- usb_set_serial_data(serial, mos7720_serial);
-
/*
* The 7715 uses the first bulk in/out endpoint pair for the parallel
* port, and the second for the serial port. Because the usbserial core
@@ -1638,16 +2227,12 @@ static int mos7720_startup(struct usb_serial *serial)
serial->port[1]->interrupt_in_buffer = NULL;
}
- /* we set up the pointers to the endpoints in the mos7720_open *
- * function, as the structures aren't created yet. */
- /* set up port private structures */
+ /* set up serial port private structures */
for (i = 0; i < serial->num_ports; ++i) {
mos7720_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
if (mos7720_port == NULL) {
dev_err(&dev->dev, "%s - Out of memory\n", __func__);
- usb_set_serial_data(serial, NULL);
- kfree(mos7720_serial);
return -ENOMEM;
}
@@ -1669,6 +2254,20 @@ static int mos7720_startup(struct usb_serial *serial)
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5*HZ);
+ /* start the interrupt urb */
+ ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
+ if (ret_val)
+ dev_err(&dev->dev,
+ "%s - Error %d submitting control urb\n",
+ __func__, ret_val);
+
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+ if (product == MOSCHIP_DEVICE_ID_7715) {
+ ret_val = mos7715_parport_init(serial);
+ if (ret_val < 0)
+ return ret_val;
+ }
+#endif
/* LSR For Port 1 */
send_mos_cmd(serial, MOS_READ, 0x00, UART_LSR, &data);
dbg("LSR:%x", data);
@@ -1684,12 +2283,47 @@ static void mos7720_release(struct usb_serial *serial)
{
int i;
+#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
+ /* close the parallel port */
+
+ if (le16_to_cpu(serial->dev->descriptor.idProduct)
+ == MOSCHIP_DEVICE_ID_7715) {
+ struct urbtracker *urbtrack;
+ unsigned long flags;
+ struct mos7715_parport *mos_parport =
+ usb_get_serial_data(serial);
+
+ /* prevent NULL ptr dereference in port callbacks */
+ spin_lock(&release_lock);
+ mos_parport->pp->private_data = NULL;
+ spin_unlock(&release_lock);
+
+ /* wait for synchronous usb calls to return */
+ if (mos_parport->msg_pending)
+ wait_for_completion_timeout(&mos_parport->syncmsg_compl,
+ MOS_WDR_TIMEOUT);
+
+ parport_remove_port(mos_parport->pp);
+ usb_set_serial_data(serial, NULL);
+ mos_parport->serial = NULL;
+
+ /* if tasklet currently scheduled, wait for it to complete */
+ tasklet_kill(&mos_parport->urb_tasklet);
+
+ /* unlink any urbs sent by the tasklet */
+ spin_lock_irqsave(&mos_parport->listlock, flags);
+ list_for_each_entry(urbtrack,
+ &mos_parport->active_urbs,
+ urblist_entry)
+ usb_unlink_urb(urbtrack->urb);
+ spin_unlock_irqrestore(&mos_parport->listlock, flags);
+
+ kref_put(&mos_parport->ref_count, destroy_mos_parport);
+ }
+#endif
/* free private structure allocated for serial port */
for (i = 0; i < serial->num_ports; ++i)
kfree(usb_get_serial_port_data(serial->port[i]));
-
- /* free private structure allocated for serial device */
- kfree(usb_get_serial_data(serial));
}
static struct usb_driver usb_driver = {