|
@@ -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 = {
|