#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"
#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 */
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;
+
#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[] = {
+static const 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);
+#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
__u8 sp1;
__u8 sp2;
- dbg("%s", " : Entering\n");
-
switch (status) {
case 0:
/* success */
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
+ case -ENODEV:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __func__,
status);
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;
}
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;
}
}
return;
}
+/*
+ * 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:
+ case -ENODEV:
+ /* 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;
+ }
+ }
+
+#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)
+ 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
int retval;
unsigned char *data ;
struct usb_serial_port *port;
- struct moschip_port *mos7720_port;
struct tty_struct *tty;
int status = urb->status;
return;
}
- mos7720_port = urb->context;
- if (!mos7720_port) {
- dbg("%s", "NULL mos7720_port pointer \n");
- return ;
- }
-
- port = mos7720_port->port;
+ port = urb->context;
dbg("Entering...%s", __func__);
tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
- tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
return ;
}
- dbg("Entering .........");
-
tty = tty_port_tty_get(&mos7720_port->port->port);
if (tty && mos7720_port->open)
* this function will be used for sending command to device
*/
static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value,
- __u16 index, void *data)
+ __u16 index, u8 *data)
{
int status;
- unsigned int pipe;
+ u8 *buf;
u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
- __u8 requesttype;
- __u16 size = 0x0000;
if (value < MOS_MAX_PORT) {
if (product == MOSCHIP_DEVICE_ID_7715)
- value = value*0x100+0x100;
+ value = 0x0200; /* identifies the 7715's serial port */
else
value = value*0x100+0x200;
} else {
}
if (request == MOS_WRITE) {
- request = (__u8)MOS_WRITE;
- requesttype = (__u8)0x40;
- value = value + (__u16)*((unsigned char *)data);
- data = NULL;
- pipe = usb_sndctrlpipe(serial->dev, 0);
+ value = value + *data;
+ status = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0), MOS_WRITE,
+ 0x40, value, index, NULL, 0, MOS_WDR_TIMEOUT);
} else {
- request = (__u8)MOS_READ;
- requesttype = (__u8)0xC0;
- size = 0x01;
- pipe = usb_rcvctrlpipe(serial->dev, 0);
+ buf = kmalloc(1, GFP_KERNEL);
+ if (!buf) {
+ status = -ENOMEM;
+ goto out;
+ }
+ status = usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0), MOS_READ,
+ 0xc0, value, index, buf, 1, MOS_WDR_TIMEOUT);
+ *data = *buf;
+ kfree(buf);
}
-
- status = usb_control_msg(serial->dev, pipe, request, requesttype,
- value, index, data, size, MOS_WDR_TIMEOUT);
-
+out:
if (status < 0)
- dbg("Command Write failed Value %x index %x\n", value, index);
+ dbg("Command Write failed Value %x index %x", value, index);
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;
struct usb_serial_port *port0;
struct urb *urb;
- struct moschip_serial *mos7720_serial;
struct moschip_port *mos7720_port;
int response;
int port_number;
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);
*/
port_number = port->number - port->serial->minor;
send_mos_cmd(port->serial, MOS_READ, port_number, UART_LSR, &data);
- dbg("SS::%p LSR:%x\n", mos7720_port, data);
+ dbg("SS::%p LSR:%x", mos7720_port, data);
dbg("Check:Sending Command ..........");
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",
struct moschip_port *mos7720_port;
int status;
- dbg("%s- port %d\n", __func__, port->number);
+ dbg("%s- port %d", __func__, port->number);
mos7720_port = usb_get_serial_port_data(port);
}
}
+/* FIXME: this function does not work */
static int set_higher_rates(struct moschip_port *mos7720_port,
unsigned int baud)
{
* 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);
change_port_settings(tty, mos7720_port, old_termios);
if (!port->read_urb) {
- dbg("%s", "URB KILLED !!!!!\n");
+ dbg("%s", "URB KILLED !!!!!");
return;
}
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__);
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;
+ /*
+ * 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
+ * assumes both pairs are serial ports, we must engage in a bit of
+ * subterfuge and swap the pointers for ports 0 and 1 in order to make
+ * port 0 point to the serial port. However, both moschip devices use a
+ * single interrupt-in endpoint for both ports (as mentioned a little
+ * further down), and this endpoint was assigned to port 0. So after
+ * the swap, we must copy the interrupt endpoint elements from port 1
+ * (as newly assigned) to port 0, and null out port 1 pointers.
+ */
+ if (product == MOSCHIP_DEVICE_ID_7715) {
+ struct usb_serial_port *tmp = serial->port[0];
+ serial->port[0] = serial->port[1];
+ serial->port[1] = tmp;
+ serial->port[0]->interrupt_in_urb = tmp->interrupt_in_urb;
+ serial->port[0]->interrupt_in_buffer = tmp->interrupt_in_buffer;
+ serial->port[0]->interrupt_in_endpointAddress =
+ tmp->interrupt_in_endpointAddress;
+ serial->port[1]->interrupt_in_urb = NULL;
+ serial->port[1]->interrupt_in_buffer = NULL;
}
- usb_set_serial_data(serial, mos7720_serial);
- /* 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;
}
/* Initialize all port interrupt end point to port 0 int
* endpoint. Our device has only one interrupt endpoint
- * comman to all ports */
+ * common to all ports */
serial->port[i]->interrupt_in_endpointAddress =
serial->port[0]->interrupt_in_endpointAddress;
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);
{
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 = {
.description = "Moschip 2 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,
.chars_in_buffer = mos7720_chars_in_buffer,
.break_ctl = mos7720_break,
.read_bulk_callback = mos7720_bulk_in_callback,
- .read_int_callback = mos7720_interrupt_callback,
+ .read_int_callback = NULL /* dynamically assigned in probe() */
};
static int __init moschip7720_init(void)