diff options
Diffstat (limited to 'drivers/staging/ced1401/usb1401.c')
-rw-r--r-- | drivers/staging/ced1401/usb1401.c | 2360 |
1 files changed, 1219 insertions, 1141 deletions
diff --git a/drivers/staging/ced1401/usb1401.c b/drivers/staging/ced1401/usb1401.c index bf08baa..69b7f20 100644 --- a/drivers/staging/ced1401/usb1401.c +++ b/drivers/staging/ced1401/usb1401.c @@ -23,7 +23,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - Endpoints ********* There are 4 endpoints plus the control endpoint in the standard interface @@ -92,14 +91,13 @@ synchronous non-Urb based transfers. #include <linux/highmem.h> #include <linux/version.h> #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) ) - #include <linux/init.h> - #include <linux/slab.h> - #include <linux/module.h> - #include <linux/kref.h> - #include <linux/uaccess.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/uaccess.h> #endif - #include "usb1401.h" /* Define these values to match your devices */ @@ -107,13 +105,12 @@ synchronous non-Urb based transfers. #define USB_CED_PRODUCT_ID 0xa0f0 /* table of devices that work with this driver */ -static const struct usb_device_id ced_table[] = -{ - { USB_DEVICE(USB_CED_VENDOR_ID, USB_CED_PRODUCT_ID) }, - { } /* Terminating entry */ +static const struct usb_device_id ced_table[] = { + {USB_DEVICE(USB_CED_VENDOR_ID, USB_CED_PRODUCT_ID)}, + {} /* Terminating entry */ }; -MODULE_DEVICE_TABLE(usb, ced_table); +MODULE_DEVICE_TABLE(usb, ced_table); /* Get a minor range for your devices from the usb maintainer */ #define USB_CED_MINOR_BASE 192 @@ -134,151 +131,152 @@ The cause for these errors is that the driver makes use of the functions usb_buf This is needed on Debian 2.6.32-5-amd64 */ #if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ) - #define usb_alloc_coherent usb_buffer_alloc - #define usb_free_coherent usb_buffer_free - #define noop_llseek NULL +#define usb_alloc_coherent usb_buffer_alloc +#define usb_free_coherent usb_buffer_free +#define noop_llseek NULL #endif static struct usb_driver ced_driver; static void ced_delete(struct kref *kref) { - DEVICE_EXTENSION *pdx = to_DEVICE_EXTENSION(kref); - - // Free up the output buffer, then free the output urb. Note that the interface member - // of pdx will probably be NULL, so cannot be used to get to dev. - usb_free_coherent(pdx->udev, OUTBUF_SZ, pdx->pCoherCharOut, pdx->pUrbCharOut->transfer_dma); - usb_free_urb(pdx->pUrbCharOut); - - // Do the same for chan input - usb_free_coherent(pdx->udev, INBUF_SZ, pdx->pCoherCharIn, pdx->pUrbCharIn->transfer_dma); - usb_free_urb(pdx->pUrbCharIn); - - // Do the same for the block transfers - usb_free_coherent(pdx->udev, STAGED_SZ, pdx->pCoherStagedIO, pdx->pStagedUrb->transfer_dma); - usb_free_urb(pdx->pStagedUrb); - - usb_put_dev(pdx->udev); - kfree(pdx); + DEVICE_EXTENSION *pdx = to_DEVICE_EXTENSION(kref); + + // Free up the output buffer, then free the output urb. Note that the interface member + // of pdx will probably be NULL, so cannot be used to get to dev. + usb_free_coherent(pdx->udev, OUTBUF_SZ, pdx->pCoherCharOut, + pdx->pUrbCharOut->transfer_dma); + usb_free_urb(pdx->pUrbCharOut); + + // Do the same for chan input + usb_free_coherent(pdx->udev, INBUF_SZ, pdx->pCoherCharIn, + pdx->pUrbCharIn->transfer_dma); + usb_free_urb(pdx->pUrbCharIn); + + // Do the same for the block transfers + usb_free_coherent(pdx->udev, STAGED_SZ, pdx->pCoherStagedIO, + pdx->pStagedUrb->transfer_dma); + usb_free_urb(pdx->pStagedUrb); + + usb_put_dev(pdx->udev); + kfree(pdx); } // This is the driver end of the open() call from user space. static int ced_open(struct inode *inode, struct file *file) { - DEVICE_EXTENSION *pdx; - int retval = 0; - int subminor = iminor(inode); - struct usb_interface* interface = usb_find_interface(&ced_driver, subminor); - if (!interface) - { - pr_err("%s - error, can't find device for minor %d", __func__, subminor); - retval = -ENODEV; - goto exit; - } - - pdx = usb_get_intfdata(interface); - if (!pdx) - { - retval = -ENODEV; - goto exit; - } - - dev_dbg(&interface->dev, "%s got pdx", __func__); - - /* increment our usage count for the device */ - kref_get(&pdx->kref); - - /* lock the device to allow correctly handling errors - * in resumption */ - mutex_lock(&pdx->io_mutex); - - if (!pdx->open_count++) - { - retval = usb_autopm_get_interface(interface); - if (retval) - { - pdx->open_count--; - mutex_unlock(&pdx->io_mutex); - kref_put(&pdx->kref, ced_delete); - goto exit; - } - } - else - { //uncomment this block if you want exclusive open - dev_err(&interface->dev, "%s fail: already open", __func__); + DEVICE_EXTENSION *pdx; + int retval = 0; + int subminor = iminor(inode); + struct usb_interface *interface = + usb_find_interface(&ced_driver, subminor); + if (!interface) { + pr_err("%s - error, can't find device for minor %d", __func__, + subminor); + retval = -ENODEV; + goto exit; + } + + pdx = usb_get_intfdata(interface); + if (!pdx) { + retval = -ENODEV; + goto exit; + } + + dev_dbg(&interface->dev, "%s got pdx", __func__); + + /* increment our usage count for the device */ + kref_get(&pdx->kref); + + /* lock the device to allow correctly handling errors + * in resumption */ + mutex_lock(&pdx->io_mutex); + + if (!pdx->open_count++) { + retval = usb_autopm_get_interface(interface); + if (retval) { + pdx->open_count--; + mutex_unlock(&pdx->io_mutex); + kref_put(&pdx->kref, ced_delete); + goto exit; + } + } else { //uncomment this block if you want exclusive open + dev_err(&interface->dev, "%s fail: already open", __func__); retval = -EBUSY; pdx->open_count--; mutex_unlock(&pdx->io_mutex); kref_put(&pdx->kref, ced_delete); goto exit; } - /* prevent the device from being autosuspended */ + /* prevent the device from being autosuspended */ - /* save our object in the file's private structure */ - file->private_data = pdx; - mutex_unlock(&pdx->io_mutex); + /* save our object in the file's private structure */ + file->private_data = pdx; + mutex_unlock(&pdx->io_mutex); exit: - return retval; + return retval; } static int ced_release(struct inode *inode, struct file *file) { - DEVICE_EXTENSION *pdx = file->private_data; - if (pdx == NULL) - return -ENODEV; - - dev_dbg(&pdx->interface->dev,"%s called", __func__); - mutex_lock(&pdx->io_mutex); - if (!--pdx->open_count && pdx->interface) // Allow autosuspend - usb_autopm_put_interface(pdx->interface); - mutex_unlock(&pdx->io_mutex); - - kref_put(&pdx->kref, ced_delete); // decrement the count on our device - return 0; + DEVICE_EXTENSION *pdx = file->private_data; + if (pdx == NULL) + return -ENODEV; + + dev_dbg(&pdx->interface->dev, "%s called", __func__); + mutex_lock(&pdx->io_mutex); + if (!--pdx->open_count && pdx->interface) // Allow autosuspend + usb_autopm_put_interface(pdx->interface); + mutex_unlock(&pdx->io_mutex); + + kref_put(&pdx->kref, ced_delete); // decrement the count on our device + return 0; } static int ced_flush(struct file *file, fl_owner_t id) { - int res; - DEVICE_EXTENSION *pdx = file->private_data; - if (pdx == NULL) - return -ENODEV; + int res; + DEVICE_EXTENSION *pdx = file->private_data; + if (pdx == NULL) + return -ENODEV; - dev_dbg(&pdx->interface->dev,"%s char in pend=%d", __func__, pdx->bReadCharsPending); + dev_dbg(&pdx->interface->dev, "%s char in pend=%d", __func__, + pdx->bReadCharsPending); - /* wait for io to stop */ - mutex_lock(&pdx->io_mutex); - dev_dbg(&pdx->interface->dev,"%s got io_mutex", __func__); - ced_draw_down(pdx); + /* wait for io to stop */ + mutex_lock(&pdx->io_mutex); + dev_dbg(&pdx->interface->dev, "%s got io_mutex", __func__); + ced_draw_down(pdx); - /* read out errors, leave subsequent opens a clean slate */ - spin_lock_irq(&pdx->err_lock); - res = pdx->errors ? (pdx->errors == -EPIPE ? -EPIPE : -EIO) : 0; - pdx->errors = 0; - spin_unlock_irq(&pdx->err_lock); + /* read out errors, leave subsequent opens a clean slate */ + spin_lock_irq(&pdx->err_lock); + res = pdx->errors ? (pdx->errors == -EPIPE ? -EPIPE : -EIO) : 0; + pdx->errors = 0; + spin_unlock_irq(&pdx->err_lock); - mutex_unlock(&pdx->io_mutex); - dev_dbg(&pdx->interface->dev,"%s exit reached", __func__); + mutex_unlock(&pdx->io_mutex); + dev_dbg(&pdx->interface->dev, "%s exit reached", __func__); - return res; + return res; } - static ssize_t ced_read(struct file *file, char *buffer, size_t count, - loff_t *ppos) + loff_t * ppos) { - DEVICE_EXTENSION *pdx = file->private_data; - dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", __func__); - return 0; // as we do not do reads this way + DEVICE_EXTENSION *pdx = file->private_data; + dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", + __func__); + return 0; // as we do not do reads this way } static ssize_t ced_write(struct file *file, const char *user_buffer, - size_t count, loff_t *ppos) + size_t count, loff_t * ppos) { - DEVICE_EXTENSION *pdx = file->private_data; - dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", __func__); - return 0; + DEVICE_EXTENSION *pdx = file->private_data; + dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", + __func__); + return 0; } /*************************************************************************** @@ -288,80 +286,86 @@ static ssize_t ced_write(struct file *file, const char *user_buffer, ** not help with a device extension held by a file. ** return true if can accept new io requests, else false */ -static bool CanAcceptIoRequests(DEVICE_EXTENSION* pdx) +static bool CanAcceptIoRequests(DEVICE_EXTENSION * pdx) { - return pdx && pdx->interface; // Can we accept IO requests + return pdx && pdx->interface; // Can we accept IO requests } /**************************************************************************** ** Callback routine to complete writes. This may need to fire off another ** urb to complete the transfer. ****************************************************************************/ -static void ced_writechar_callback(struct urb* pUrb) +static void ced_writechar_callback(struct urb *pUrb) { - DEVICE_EXTENSION *pdx = pUrb->context; - int nGot = pUrb->actual_length; // what we transferred - - if (pUrb->status) - { // sync/async unlink faults aren't errors - if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN)) - { - dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status); - } - - spin_lock(&pdx->err_lock); - pdx->errors = pUrb->status; - spin_unlock(&pdx->err_lock); - nGot = 0; // and tidy up again if so - - spin_lock(&pdx->charOutLock); // already at irq level - pdx->dwOutBuffGet = 0; // Reset the output buffer - pdx->dwOutBuffPut = 0; - pdx->dwNumOutput = 0; // Clear the char count - pdx->bPipeError[0] = 1; // Flag an error for later - pdx->bSendCharsPending = false; // Allow other threads again - spin_unlock(&pdx->charOutLock); // already at irq level - dev_dbg(&pdx->interface->dev, "%s - char out done, 0 chars sent", __func__); - } - else - { - dev_dbg(&pdx->interface->dev, "%s - char out done, %d chars sent", __func__, nGot); - spin_lock(&pdx->charOutLock); // already at irq level - pdx->dwNumOutput -= nGot; // Now adjust the char send buffer - pdx->dwOutBuffGet += nGot; // to match what we did - if (pdx->dwOutBuffGet >= OUTBUF_SZ) // Can't do this any earlier as data could be overwritten - pdx->dwOutBuffGet = 0; - - if (pdx->dwNumOutput > 0) // if more to be done... - { - int nPipe = 0; // The pipe number to use - int iReturn; - char* pDat = &pdx->outputBuffer[pdx->dwOutBuffGet]; - unsigned int dwCount = pdx->dwNumOutput; // maximum to send - if ((pdx->dwOutBuffGet+dwCount) > OUTBUF_SZ) // does it cross buffer end? - dwCount = OUTBUF_SZ - pdx->dwOutBuffGet; - spin_unlock(&pdx->charOutLock); // we are done with stuff that changes - memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer - usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev, - usb_sndbulkpipe(pdx->udev, pdx->epAddr[0]), - pdx->pCoherCharOut, dwCount, ced_writechar_callback, pdx); - pdx->pUrbCharOut->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted); // in case we need to kill it - iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_ATOMIC); - dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__, dwCount, pDat); - spin_lock(&pdx->charOutLock); // grab lock for errors - if (iReturn) - { - pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later - pdx->bSendCharsPending = false; // Allow other threads again - usb_unanchor_urb(pdx->pUrbCharOut); - dev_err(&pdx->interface->dev, "%s usb_submit_urb() returned %d", __func__, iReturn); - } - } - else - pdx->bSendCharsPending = false; // Allow other threads again - spin_unlock(&pdx->charOutLock); // already at irq level - } + DEVICE_EXTENSION *pdx = pUrb->context; + int nGot = pUrb->actual_length; // what we transferred + + if (pUrb->status) { // sync/async unlink faults aren't errors + if (! + (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET + || pUrb->status == -ESHUTDOWN)) { + dev_err(&pdx->interface->dev, + "%s - nonzero write bulk status received: %d", + __func__, pUrb->status); + } + + spin_lock(&pdx->err_lock); + pdx->errors = pUrb->status; + spin_unlock(&pdx->err_lock); + nGot = 0; // and tidy up again if so + + spin_lock(&pdx->charOutLock); // already at irq level + pdx->dwOutBuffGet = 0; // Reset the output buffer + pdx->dwOutBuffPut = 0; + pdx->dwNumOutput = 0; // Clear the char count + pdx->bPipeError[0] = 1; // Flag an error for later + pdx->bSendCharsPending = false; // Allow other threads again + spin_unlock(&pdx->charOutLock); // already at irq level + dev_dbg(&pdx->interface->dev, + "%s - char out done, 0 chars sent", __func__); + } else { + dev_dbg(&pdx->interface->dev, + "%s - char out done, %d chars sent", __func__, nGot); + spin_lock(&pdx->charOutLock); // already at irq level + pdx->dwNumOutput -= nGot; // Now adjust the char send buffer + pdx->dwOutBuffGet += nGot; // to match what we did + if (pdx->dwOutBuffGet >= OUTBUF_SZ) // Can't do this any earlier as data could be overwritten + pdx->dwOutBuffGet = 0; + + if (pdx->dwNumOutput > 0) // if more to be done... + { + int nPipe = 0; // The pipe number to use + int iReturn; + char *pDat = &pdx->outputBuffer[pdx->dwOutBuffGet]; + unsigned int dwCount = pdx->dwNumOutput; // maximum to send + if ((pdx->dwOutBuffGet + dwCount) > OUTBUF_SZ) // does it cross buffer end? + dwCount = OUTBUF_SZ - pdx->dwOutBuffGet; + spin_unlock(&pdx->charOutLock); // we are done with stuff that changes + memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer + usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev, + usb_sndbulkpipe(pdx->udev, + pdx->epAddr[0]), + pdx->pCoherCharOut, dwCount, + ced_writechar_callback, pdx); + pdx->pUrbCharOut->transfer_flags |= + URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted); // in case we need to kill it + iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_ATOMIC); + dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__, + dwCount, pDat); + spin_lock(&pdx->charOutLock); // grab lock for errors + if (iReturn) { + pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later + pdx->bSendCharsPending = false; // Allow other threads again + usb_unanchor_urb(pdx->pUrbCharOut); + dev_err(&pdx->interface->dev, + "%s usb_submit_urb() returned %d", + __func__, iReturn); + } + } else + pdx->bSendCharsPending = false; // Allow other threads again + spin_unlock(&pdx->charOutLock); // already at irq level + } } /**************************************************************************** @@ -369,93 +373,91 @@ static void ced_writechar_callback(struct urb* pUrb) ** Transmit the characters in the output buffer to the 1401. This may need ** breaking down into multiple transfers. ****************************************************************************/ -int SendChars(DEVICE_EXTENSION* pdx) +int SendChars(DEVICE_EXTENSION * pdx) { - int iReturn = U14ERR_NOERROR; - - spin_lock_irq(&pdx->charOutLock); // Protect ourselves - - if ((!pdx->bSendCharsPending) && // Not currently sending - (pdx->dwNumOutput > 0) && // has characters to output - (CanAcceptIoRequests(pdx))) // and current activity is OK - { - unsigned int dwCount = pdx->dwNumOutput; // Get a copy of the character count - pdx->bSendCharsPending = true; // Set flag to lock out other threads - - dev_dbg(&pdx->interface->dev, "Send %d chars to 1401, EP0 flag %d\n", dwCount, pdx->nPipes == 3); - // If we have only 3 end points we must send the characters to the 1401 using EP0. - if (pdx->nPipes == 3) - { - // For EP0 character transmissions to the 1401, we have to hang about until they - // are gone, as otherwise without more character IO activity they will never go. - unsigned int count = dwCount; // Local char counter - unsigned int index = 0; // The index into the char buffer - - spin_unlock_irq(&pdx->charOutLock); // Free spinlock as we call USBD - - while ((count > 0) && (iReturn == U14ERR_NOERROR)) - { - // We have to break the transfer up into 64-byte chunks because of a 2270 problem - int n = count > 64 ? 64 : count; // Chars for this xfer, max of 64 - int nSent = usb_control_msg(pdx->udev, - usb_sndctrlpipe(pdx->udev,0), // use end point 0 - DB_CHARS, // bRequest - (H_TO_D|VENDOR|DEVREQ), // to the device, vendor request to the device - 0,0, // value and index are both 0 - &pdx->outputBuffer[index], // where to send from - n, // how much to send - 1000); // timeout in jiffies - if (nSent <= 0) - { - iReturn = nSent ? nSent : -ETIMEDOUT; // if 0 chars says we timed out - dev_err(&pdx->interface->dev, "Send %d chars by EP0 failed: %d", n, iReturn); - } - else - { - dev_dbg(&pdx->interface->dev, "Sent %d chars by EP0", n); - count -= nSent; - index += nSent; - } - } - - spin_lock_irq(&pdx->charOutLock); // Protect pdx changes, released by general code - pdx->dwOutBuffGet = 0; // so reset the output buffer - pdx->dwOutBuffPut = 0; - pdx->dwNumOutput = 0; // and clear the buffer count - pdx->bSendCharsPending = false; // Allow other threads again - } - else - { // Here for sending chars normally - we hold the spin lock - int nPipe = 0; // The pipe number to use - char* pDat = &pdx->outputBuffer[pdx->dwOutBuffGet]; - - if ((pdx->dwOutBuffGet+dwCount) > OUTBUF_SZ) // does it cross buffer end? - dwCount = OUTBUF_SZ - pdx->dwOutBuffGet; - spin_unlock_irq(&pdx->charOutLock); // we are done with stuff that changes - memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer - usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev, - usb_sndbulkpipe(pdx->udev, pdx->epAddr[0]), - pdx->pCoherCharOut, dwCount, ced_writechar_callback, pdx); - pdx->pUrbCharOut->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted); - iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_KERNEL); - spin_lock_irq(&pdx->charOutLock); // grab lock for errors - if (iReturn) - { - pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later - pdx->bSendCharsPending = false; // Allow other threads again - usb_unanchor_urb(pdx->pUrbCharOut); // remove from list of active urbs - } - } - } - else - if (pdx->bSendCharsPending && (pdx->dwNumOutput > 0)) - dev_dbg(&pdx->interface->dev, "SendChars bSendCharsPending:true"); - - - dev_dbg(&pdx->interface->dev, "SendChars exit code: %d", iReturn); - spin_unlock_irq(&pdx->charOutLock); // Now let go of the spinlock - return iReturn; + int iReturn = U14ERR_NOERROR; + + spin_lock_irq(&pdx->charOutLock); // Protect ourselves + + if ((!pdx->bSendCharsPending) && // Not currently sending + (pdx->dwNumOutput > 0) && // has characters to output + (CanAcceptIoRequests(pdx))) // and current activity is OK + { + unsigned int dwCount = pdx->dwNumOutput; // Get a copy of the character count + pdx->bSendCharsPending = true; // Set flag to lock out other threads + + dev_dbg(&pdx->interface->dev, + "Send %d chars to 1401, EP0 flag %d\n", dwCount, + pdx->nPipes == 3); + // If we have only 3 end points we must send the characters to the 1401 using EP0. + if (pdx->nPipes == 3) { + // For EP0 character transmissions to the 1401, we have to hang about until they + // are gone, as otherwise without more character IO activity they will never go. + unsigned int count = dwCount; // Local char counter + unsigned int index = 0; // The index into the char buffer + + spin_unlock_irq(&pdx->charOutLock); // Free spinlock as we call USBD + + while ((count > 0) && (iReturn == U14ERR_NOERROR)) { + // We have to break the transfer up into 64-byte chunks because of a 2270 problem + int n = count > 64 ? 64 : count; // Chars for this xfer, max of 64 + int nSent = usb_control_msg(pdx->udev, + usb_sndctrlpipe(pdx->udev, 0), // use end point 0 + DB_CHARS, // bRequest + (H_TO_D | VENDOR | DEVREQ), // to the device, vendor request to the device + 0, 0, // value and index are both 0 + &pdx->outputBuffer[index], // where to send from + n, // how much to send + 1000); // timeout in jiffies + if (nSent <= 0) { + iReturn = nSent ? nSent : -ETIMEDOUT; // if 0 chars says we timed out + dev_err(&pdx->interface->dev, + "Send %d chars by EP0 failed: %d", + n, iReturn); + } else { + dev_dbg(&pdx->interface->dev, + "Sent %d chars by EP0", n); + count -= nSent; + index += nSent; + } + } + + spin_lock_irq(&pdx->charOutLock); // Protect pdx changes, released by general code + pdx->dwOutBuffGet = 0; // so reset the output buffer + pdx->dwOutBuffPut = 0; + pdx->dwNumOutput = 0; // and clear the buffer count + pdx->bSendCharsPending = false; // Allow other threads again + } else { // Here for sending chars normally - we hold the spin lock + int nPipe = 0; // The pipe number to use + char *pDat = &pdx->outputBuffer[pdx->dwOutBuffGet]; + + if ((pdx->dwOutBuffGet + dwCount) > OUTBUF_SZ) // does it cross buffer end? + dwCount = OUTBUF_SZ - pdx->dwOutBuffGet; + spin_unlock_irq(&pdx->charOutLock); // we are done with stuff that changes + memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer + usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev, + usb_sndbulkpipe(pdx->udev, + pdx->epAddr[0]), + pdx->pCoherCharOut, dwCount, + ced_writechar_callback, pdx); + pdx->pUrbCharOut->transfer_flags |= + URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted); + iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_KERNEL); + spin_lock_irq(&pdx->charOutLock); // grab lock for errors + if (iReturn) { + pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later + pdx->bSendCharsPending = false; // Allow other threads again + usb_unanchor_urb(pdx->pUrbCharOut); // remove from list of active urbs + } + } + } else if (pdx->bSendCharsPending && (pdx->dwNumOutput > 0)) + dev_dbg(&pdx->interface->dev, + "SendChars bSendCharsPending:true"); + + dev_dbg(&pdx->interface->dev, "SendChars exit code: %d", iReturn); + spin_unlock_irq(&pdx->charOutLock); // Now let go of the spinlock + return iReturn; } /*************************************************************************** @@ -472,228 +474,265 @@ int SendChars(DEVICE_EXTENSION* pdx) ** pdx Is our device extension which holds all we know about the transfer. ** n The number of bytes to move one way or the other. ***************************************************************************/ -static void CopyUserSpace(DEVICE_EXTENSION *pdx, int n) +static void CopyUserSpace(DEVICE_EXTENSION * pdx, int n) { - unsigned int nArea = pdx->StagedId; - if (nArea < MAX_TRANSAREAS) - { - TRANSAREA *pArea = &pdx->rTransDef[nArea]; // area to be used - unsigned int dwOffset = pdx->StagedDone + pdx->StagedOffset + pArea->dwBaseOffset; - char* pCoherBuf = pdx->pCoherStagedIO; // coherent buffer - if (!pArea->bUsed) - { - dev_err(&pdx->interface->dev, "%s area %d unused", __func__, nArea); - return; - } - - while (n) - { - int nPage = dwOffset >> PAGE_SHIFT; // page number in table - if (nPage < pArea->nPages) - { - char *pvAddress = (char*)kmap_atomic(pArea->pPages[nPage]); - if (pvAddress) - { - unsigned int uiPageOff = dwOffset & (PAGE_SIZE-1); // offset into the page - size_t uiXfer = PAGE_SIZE - uiPageOff; // max to transfer on this page - if (uiXfer > n) // limit byte count if too much - uiXfer = n; // for the page - if (pdx->StagedRead) - memcpy(pvAddress+uiPageOff, pCoherBuf, uiXfer); - else - memcpy(pCoherBuf, pvAddress+uiPageOff, uiXfer); - kunmap_atomic(pvAddress); - dwOffset += uiXfer; - pCoherBuf += uiXfer; - n -= uiXfer; - } - else - { - dev_err(&pdx->interface->dev, "%s did not map page %d", __func__, nPage); - return; - } - - } - else - { - dev_err(&pdx->interface->dev, "%s exceeded pages %d", __func__, nPage); - return; - } - } - } - else - dev_err(&pdx->interface->dev, "%s bad area %d", __func__, nArea); + unsigned int nArea = pdx->StagedId; + if (nArea < MAX_TRANSAREAS) { + TRANSAREA *pArea = &pdx->rTransDef[nArea]; // area to be used + unsigned int dwOffset = + pdx->StagedDone + pdx->StagedOffset + pArea->dwBaseOffset; + char *pCoherBuf = pdx->pCoherStagedIO; // coherent buffer + if (!pArea->bUsed) { + dev_err(&pdx->interface->dev, "%s area %d unused", + __func__, nArea); + return; + } + + while (n) { + int nPage = dwOffset >> PAGE_SHIFT; // page number in table + if (nPage < pArea->nPages) { + char *pvAddress = + (char *)kmap_atomic(pArea->pPages[nPage]); + if (pvAddress) { + unsigned int uiPageOff = dwOffset & (PAGE_SIZE - 1); // offset into the page + size_t uiXfer = PAGE_SIZE - uiPageOff; // max to transfer on this page + if (uiXfer > n) // limit byte count if too much + uiXfer = n; // for the page + if (pdx->StagedRead) + memcpy(pvAddress + uiPageOff, + pCoherBuf, uiXfer); + else + memcpy(pCoherBuf, + pvAddress + uiPageOff, + uiXfer); + kunmap_atomic(pvAddress); + dwOffset += uiXfer; + pCoherBuf += uiXfer; + n -= uiXfer; + } else { + dev_err(&pdx->interface->dev, + "%s did not map page %d", + __func__, nPage); + return; + } + + } else { + dev_err(&pdx->interface->dev, + "%s exceeded pages %d", __func__, + nPage); + return; + } + } + } else + dev_err(&pdx->interface->dev, "%s bad area %d", __func__, + nArea); } // Forward declarations for stuff used circularly -static int StageChunk(DEVICE_EXTENSION *pdx); +static int StageChunk(DEVICE_EXTENSION * pdx); /*************************************************************************** ** ReadWrite_Complete ** ** Completion routine for our staged read/write Irps */ -static void staged_callback(struct urb* pUrb) +static void staged_callback(struct urb *pUrb) { - DEVICE_EXTENSION *pdx = pUrb->context; - unsigned int nGot = pUrb->actual_length; // what we transferred - bool bCancel = false; - bool bRestartCharInput; // used at the end - - spin_lock(&pdx->stagedLock); // stop ReadWriteMem() action while this routine is running - pdx->bStagedUrbPending = false; // clear the flag for staged IRP pending - - if (pUrb->status) - { // sync/async unlink faults aren't errors - if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN)) - { - dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status); - } - else - dev_info(&pdx->interface->dev, "%s - staged xfer cancelled", __func__); - - spin_lock(&pdx->err_lock); - pdx->errors = pUrb->status; - spin_unlock(&pdx->err_lock); - nGot = 0; // and tidy up again if so - bCancel = true; - } - else - { - dev_dbg(&pdx->interface->dev, "%s %d chars xferred", __func__, nGot); - if (pdx->StagedRead) // if reading, save to user space - CopyUserSpace(pdx, nGot); // copy from buffer to user - if (nGot == 0) - dev_dbg(&pdx->interface->dev, "%s ZLP", __func__); - } - - // Update the transfer length based on the TransferBufferLength value in the URB - pdx->StagedDone += nGot; - - dev_dbg(&pdx->interface->dev, "%s, done %d bytes of %d", __func__, pdx->StagedDone, pdx->StagedLength); - - if ((pdx->StagedDone == pdx->StagedLength) || // If no more to do - (bCancel)) // or this IRP was cancelled - { - TRANSAREA* pArea = &pdx->rTransDef[pdx->StagedId]; // Transfer area info - dev_dbg(&pdx->interface->dev, "%s transfer done, bytes %d, cancel %d", __func__, pdx->StagedDone, bCancel); - - // Here is where we sort out what to do with this transfer if using a circular buffer. We have - // a completed transfer that can be assumed to fit into the transfer area. We should be able to - // add this to the end of a growing block or to use it to start a new block unless the code - // that calculates the offset to use (in ReadWriteMem) is totally duff. - if ((pArea->bCircular) && (pArea->bCircToHost) && (!bCancel) && // Time to sort out circular buffer info? - (pdx->StagedRead)) // Only for tohost transfers for now - { - if (pArea->aBlocks[1].dwSize > 0) // If block 1 is in use we must append to it - { - if (pdx->StagedOffset == (pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize)) - { - pArea->aBlocks[1].dwSize += pdx->StagedLength; - dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 1 now %d bytes at %d", - pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset); - } - else - { - // Here things have gone very, very, wrong, but I cannot see how this can actually be achieved - pArea->aBlocks[1].dwOffset = pdx->StagedOffset; - pArea->aBlocks[1].dwSize = pdx->StagedLength; - dev_err(&pdx->interface->dev, "%s ERROR, circ block 1 re-started %d bytes at %d", - __func__, pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset); - } - } - else // If block 1 is not used, we try to add to block 0 - { - if (pArea->aBlocks[0].dwSize > 0) // Got stored block 0 information? - { // Must append onto the existing block 0 - if (pdx->StagedOffset == (pArea->aBlocks[0].dwOffset + pArea->aBlocks[0].dwSize)) - { - pArea->aBlocks[0].dwSize += pdx->StagedLength; // Just add this transfer in - dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 0 now %d bytes at %d", - pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset); - } - else // If it doesn't append, put into new block 1 - { - pArea->aBlocks[1].dwOffset = pdx->StagedOffset; - pArea->aBlocks[1].dwSize = pdx->StagedLength; - dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 1 started %d bytes at %d", - pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset); - } - } - else // No info stored yet, just save in block 0 - { - pArea->aBlocks[0].dwOffset = pdx->StagedOffset; - pArea->aBlocks[0].dwSize = pdx->StagedLength; - dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 0 started %d bytes at %d", - pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset); - } - } - } - - if (!bCancel) // Don't generate an event if cancelled - { - dev_dbg(&pdx->interface->dev, "RWM_Complete, bCircular %d, bToHost %d, eStart %d, eSize %d", - pArea->bCircular, pArea->bEventToHost, pArea->dwEventSt, pArea->dwEventSz); - if ((pArea->dwEventSz) && // Set a user-mode event... - (pdx->StagedRead == pArea->bEventToHost)) // ...on transfers in this direction? - { - int iWakeUp = 0; // assume - // If we have completed the right sort of DMA transfer then set the event to notify - // the user code to wake up anyone that is waiting. - if ((pArea->bCircular) && // Circular areas use a simpler test - (pArea->bCircToHost)) // only in supported direction - { // Is total data waiting up to size limit? - unsigned int dwTotal = pArea->aBlocks[0].dwSize + pArea->aBlocks[1].dwSize; - iWakeUp = (dwTotal >= pArea->dwEventSz); - } - else - { - unsigned int transEnd = pdx->StagedOffset + pdx->StagedLength; - unsigned int eventEnd = pArea->dwEventSt + pArea->dwEventSz; - iWakeUp = (pdx->StagedOffset < eventEnd) && (transEnd > pArea->dwEventSt); - } - - if (iWakeUp) - { - dev_dbg(&pdx->interface->dev, "About to set event to notify app"); - wake_up_interruptible(&pArea->wqEvent); // wake up waiting processes - ++pArea->iWakeUp; // increment wakeup count - } - } - } - - pdx->dwDMAFlag = MODE_CHAR; // Switch back to char mode before ReadWriteMem call - - if (!bCancel) // Don't look for waiting transfer if cancelled - { - // If we have a transfer waiting, kick it off - if (pdx->bXFerWaiting) // Got a block xfer waiting? - { - int iReturn; - dev_info(&pdx->interface->dev, "*** RWM_Complete *** pending transfer will now be set up!!!"); - iReturn = ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard, pdx->rDMAInfo.wIdent, pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize); - - if (iReturn) - dev_err(&pdx->interface->dev, "RWM_Complete rw setup failed %d", iReturn); - } - } - - } - else // Here for more to do - StageChunk(pdx); // fire off the next bit - - // While we hold the stagedLock, see if we should reallow character input ints - // Don't allow if cancelled, or if a new block has started or if there is a waiting block. - // This feels wrong as we should ask which spin lock protects dwDMAFlag. - bRestartCharInput = !bCancel && (pdx->dwDMAFlag == MODE_CHAR) && !pdx->bXFerWaiting; - - spin_unlock(&pdx->stagedLock); // Finally release the lock again - - // This is not correct as dwDMAFlag is protected by the staged lock, but it is treated - // in Allowi as if it were protected by the char lock. In any case, most systems will - // not be upset by char input during DMA... sigh. Needs sorting out. - if (bRestartCharInput) // may be out of date, but... - Allowi(pdx, true); // ...Allowi tests a lock too. - dev_dbg(&pdx->interface->dev, "%s done", __func__); + DEVICE_EXTENSION *pdx = pUrb->context; + unsigned int nGot = pUrb->actual_length; // what we transferred + bool bCancel = false; + bool bRestartCharInput; // used at the end + + spin_lock(&pdx->stagedLock); // stop ReadWriteMem() action while this routine is running + pdx->bStagedUrbPending = false; // clear the flag for staged IRP pending + + if (pUrb->status) { // sync/async unlink faults aren't errors + if (! + (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET + || pUrb->status == -ESHUTDOWN)) { + dev_err(&pdx->interface->dev, + "%s - nonzero write bulk status received: %d", + __func__, pUrb->status); + } else + dev_info(&pdx->interface->dev, + "%s - staged xfer cancelled", __func__); + + spin_lock(&pdx->err_lock); + pdx->errors = pUrb->status; + spin_unlock(&pdx->err_lock); + nGot = 0; // and tidy up again if so + bCancel = true; + } else { + dev_dbg(&pdx->interface->dev, "%s %d chars xferred", __func__, + nGot); + if (pdx->StagedRead) // if reading, save to user space + CopyUserSpace(pdx, nGot); // copy from buffer to user + if (nGot == 0) + dev_dbg(&pdx->interface->dev, "%s ZLP", __func__); + } + + // Update the transfer length based on the TransferBufferLength value in the URB + pdx->StagedDone += nGot; + + dev_dbg(&pdx->interface->dev, "%s, done %d bytes of %d", __func__, + pdx->StagedDone, pdx->StagedLength); + + if ((pdx->StagedDone == pdx->StagedLength) || // If no more to do + (bCancel)) // or this IRP was cancelled + { + TRANSAREA *pArea = &pdx->rTransDef[pdx->StagedId]; // Transfer area info + dev_dbg(&pdx->interface->dev, + "%s transfer done, bytes %d, cancel %d", __func__, + pdx->StagedDone, bCancel); + + // Here is where we sort out what to do with this transfer if using a circular buffer. We have + // a completed transfer that can be assumed to fit into the transfer area. We should be able to + // add this to the end of a growing block or to use it to start a new block unless the code + // that calculates the offset to use (in ReadWriteMem) is totally duff. + if ((pArea->bCircular) && (pArea->bCircToHost) && (!bCancel) && // Time to sort out circular buffer info? + (pdx->StagedRead)) // Only for tohost transfers for now + { + if (pArea->aBlocks[1].dwSize > 0) // If block 1 is in use we must append to it + { + if (pdx->StagedOffset == + (pArea->aBlocks[1].dwOffset + + pArea->aBlocks[1].dwSize)) { + pArea->aBlocks[1].dwSize += + pdx->StagedLength; + dev_dbg(&pdx->interface->dev, + "RWM_Complete, circ block 1 now %d bytes at %d", + pArea->aBlocks[1].dwSize, + pArea->aBlocks[1].dwOffset); + } else { + // Here things have gone very, very, wrong, but I cannot see how this can actually be achieved + pArea->aBlocks[1].dwOffset = + pdx->StagedOffset; + pArea->aBlocks[1].dwSize = + pdx->StagedLength; + dev_err(&pdx->interface->dev, + "%s ERROR, circ block 1 re-started %d bytes at %d", + __func__, + pArea->aBlocks[1].dwSize, + pArea->aBlocks[1].dwOffset); + } + } else // If block 1 is not used, we try to add to block 0 + { + if (pArea->aBlocks[0].dwSize > 0) // Got stored block 0 information? + { // Must append onto the existing block 0 + if (pdx->StagedOffset == + (pArea->aBlocks[0].dwOffset + + pArea->aBlocks[0].dwSize)) { + pArea->aBlocks[0].dwSize += pdx->StagedLength; // Just add this transfer in + dev_dbg(&pdx->interface->dev, + "RWM_Complete, circ block 0 now %d bytes at %d", + pArea->aBlocks[0]. + dwSize, + pArea->aBlocks[0]. + dwOffset); + } else // If it doesn't append, put into new block 1 + { + pArea->aBlocks[1].dwOffset = + pdx->StagedOffset; + pArea->aBlocks[1].dwSize = + pdx->StagedLength; + dev_dbg(&pdx->interface->dev, + "RWM_Complete, circ block 1 started %d bytes at %d", + pArea->aBlocks[1]. + dwSize, + pArea->aBlocks[1]. + dwOffset); + } + } else // No info stored yet, just save in block 0 + { + pArea->aBlocks[0].dwOffset = + pdx->StagedOffset; + pArea->aBlocks[0].dwSize = + pdx->StagedLength; + dev_dbg(&pdx->interface->dev, + "RWM_Complete, circ block 0 started %d bytes at %d", + pArea->aBlocks[0].dwSize, + pArea->aBlocks[0].dwOffset); + } + } + } + + if (!bCancel) // Don't generate an event if cancelled + { + dev_dbg(&pdx->interface->dev, + "RWM_Complete, bCircular %d, bToHost %d, eStart %d, eSize %d", + pArea->bCircular, pArea->bEventToHost, + pArea->dwEventSt, pArea->dwEventSz); + if ((pArea->dwEventSz) && // Set a user-mode event... + (pdx->StagedRead == pArea->bEventToHost)) // ...on transfers in this direction? + { + int iWakeUp = 0; // assume + // If we have completed the right sort of DMA transfer then set the event to notify + // the user code to wake up anyone that is waiting. + if ((pArea->bCircular) && // Circular areas use a simpler test + (pArea->bCircToHost)) // only in supported direction + { // Is total data waiting up to size limit? + unsigned int dwTotal = + pArea->aBlocks[0].dwSize + + pArea->aBlocks[1].dwSize; + iWakeUp = (dwTotal >= pArea->dwEventSz); + } else { + unsigned int transEnd = + pdx->StagedOffset + + pdx->StagedLength; + unsigned int eventEnd = + pArea->dwEventSt + pArea->dwEventSz; + iWakeUp = (pdx->StagedOffset < eventEnd) + && (transEnd > pArea->dwEventSt); + } + + if (iWakeUp) { + dev_dbg(&pdx->interface->dev, + "About to set event to notify app"); + wake_up_interruptible(&pArea->wqEvent); // wake up waiting processes + ++pArea->iWakeUp; // increment wakeup count + } + } + } + + pdx->dwDMAFlag = MODE_CHAR; // Switch back to char mode before ReadWriteMem call + + if (!bCancel) // Don't look for waiting transfer if cancelled + { + // If we have a transfer waiting, kick it off + if (pdx->bXFerWaiting) // Got a block xfer waiting? + { + int iReturn; + dev_info(&pdx->interface->dev, + "*** RWM_Complete *** pending transfer will now be set up!!!"); + iReturn = + ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard, + pdx->rDMAInfo.wIdent, + pdx->rDMAInfo.dwOffset, + pdx->rDMAInfo.dwSize); + + if (iReturn) + dev_err(&pdx->interface->dev, + "RWM_Complete rw setup failed %d", + iReturn); + } + } + + } else // Here for more to do + StageChunk(pdx); // fire off the next bit + + // While we hold the stagedLock, see if we should reallow character input ints + // Don't allow if cancelled, or if a new block has started or if there is a waiting block. + // This feels wrong as we should ask which spin lock protects dwDMAFlag. + bRestartCharInput = !bCancel && (pdx->dwDMAFlag == MODE_CHAR) + && !pdx->bXFerWaiting; + + spin_unlock(&pdx->stagedLock); // Finally release the lock again + + // This is not correct as dwDMAFlag is protected by the staged lock, but it is treated + // in Allowi as if it were protected by the char lock. In any case, most systems will + // not be upset by char input during DMA... sigh. Needs sorting out. + if (bRestartCharInput) // may be out of date, but... + Allowi(pdx, true); // ...Allowi tests a lock too. + dev_dbg(&pdx->interface->dev, "%s done", __func__); } /**************************************************************************** @@ -704,46 +743,50 @@ static void staged_callback(struct urb* pUrb) ** The calling code must have acquired the staging spinlock before calling ** this function, and is responsible for releasing it. We are at callback level. ****************************************************************************/ -static int StageChunk(DEVICE_EXTENSION *pdx) +static int StageChunk(DEVICE_EXTENSION * pdx) { - int iReturn = U14ERR_NOERROR; + int iReturn = U14ERR_NOERROR; unsigned int ChunkSize; - int nPipe = pdx->StagedRead ? 3 : 2; // The pipe number to use for reads or writes - if (pdx->nPipes == 3) nPipe--; // Adjust for the 3-pipe case - if (nPipe < 0) // and trap case that should never happen - return U14ERR_FAIL; - - if (!CanAcceptIoRequests(pdx)) // got sudden remove? - { - dev_info(&pdx->interface->dev, "%s sudden remove, giving up", __func__); - return U14ERR_FAIL; // could do with a better error - } - - ChunkSize = (pdx->StagedLength - pdx->StagedDone); // transfer length remaining - if (ChunkSize > STAGED_SZ) // make sure to keep legal - ChunkSize = STAGED_SZ; // limit to max allowed - - if (!pdx->StagedRead) // if writing... - CopyUserSpace(pdx, ChunkSize); // ...copy data into the buffer - - usb_fill_bulk_urb(pdx->pStagedUrb, pdx->udev, - pdx->StagedRead ? usb_rcvbulkpipe(pdx->udev, pdx->epAddr[nPipe]): - usb_sndbulkpipe(pdx->udev, pdx->epAddr[nPipe]), - pdx->pCoherStagedIO, ChunkSize, staged_callback, pdx); - pdx->pStagedUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - usb_anchor_urb(pdx->pStagedUrb, &pdx->submitted); // in case we need to kill it - iReturn = usb_submit_urb(pdx->pStagedUrb, GFP_ATOMIC); - if (iReturn) - { - usb_unanchor_urb(pdx->pStagedUrb); // kill it - pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later - dev_err(&pdx->interface->dev, "%s submit urb failed, code %d", __func__, iReturn); - } - else - pdx->bStagedUrbPending = true; // Set the flag for staged URB pending - dev_dbg(&pdx->interface->dev, "%s done so far:%d, this size:%d", __func__, pdx->StagedDone, ChunkSize); - - return iReturn; + int nPipe = pdx->StagedRead ? 3 : 2; // The pipe number to use for reads or writes + if (pdx->nPipes == 3) + nPipe--; // Adjust for the 3-pipe case + if (nPipe < 0) // and trap case that should never happen + return U14ERR_FAIL; + + if (!CanAcceptIoRequests(pdx)) // got sudden remove? + { + dev_info(&pdx->interface->dev, "%s sudden remove, giving up", + __func__); + return U14ERR_FAIL; // could do with a better error + } + + ChunkSize = (pdx->StagedLength - pdx->StagedDone); // transfer length remaining + if (ChunkSize > STAGED_SZ) // make sure to keep legal + ChunkSize = STAGED_SZ; // limit to max allowed + + if (!pdx->StagedRead) // if writing... + CopyUserSpace(pdx, ChunkSize); // ...copy data into the buffer + + usb_fill_bulk_urb(pdx->pStagedUrb, pdx->udev, + pdx->StagedRead ? usb_rcvbulkpipe(pdx->udev, + pdx-> + epAddr[nPipe]) : + usb_sndbulkpipe(pdx->udev, pdx->epAddr[nPipe]), + pdx->pCoherStagedIO, ChunkSize, staged_callback, pdx); + pdx->pStagedUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(pdx->pStagedUrb, &pdx->submitted); // in case we need to kill it + iReturn = usb_submit_urb(pdx->pStagedUrb, GFP_ATOMIC); + if (iReturn) { + usb_unanchor_urb(pdx->pStagedUrb); // kill it + pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later + dev_err(&pdx->interface->dev, "%s submit urb failed, code %d", + __func__, iReturn); + } else + pdx->bStagedUrbPending = true; // Set the flag for staged URB pending + dev_dbg(&pdx->interface->dev, "%s done so far:%d, this size:%d", + __func__, pdx->StagedDone, ChunkSize); + + return iReturn; } /*************************************************************************** @@ -763,85 +806,95 @@ static int StageChunk(DEVICE_EXTENSION *pdx) ** transfer. ** dwLen - the number of bytes to transfer. */ -int ReadWriteMem(DEVICE_EXTENSION *pdx, bool Read, unsigned short wIdent, - unsigned int dwOffs, unsigned int dwLen) +int ReadWriteMem(DEVICE_EXTENSION * pdx, bool Read, unsigned short wIdent, + unsigned int dwOffs, unsigned int dwLen) { - TRANSAREA* pArea = &pdx->rTransDef[wIdent]; // Transfer area info - - if (!CanAcceptIoRequests(pdx)) // Are we in a state to accept new requests? - { - dev_err(&pdx->interface->dev, "%s can't accept requests", __func__); - return U14ERR_FAIL; - } - - dev_dbg(&pdx->interface->dev, "%s xfer %d bytes to %s, offset %d, area %d", - __func__, dwLen, Read ? "host" : "1401", dwOffs, wIdent); - - // Amazingly, we can get an escape sequence back before the current staged Urb is done, so we - // have to check for this situation and, if so, wait until all is OK. - if (pdx->bStagedUrbPending) - { - pdx->bXFerWaiting = true; // Flag we are waiting - dev_info(&pdx->interface->dev, "%s xfer is waiting, as previous staged pending", __func__); - return U14ERR_NOERROR; - } - - if (dwLen == 0) // allow 0-len read or write; just return success - { - dev_dbg(&pdx->interface->dev, "%s OK; zero-len read/write request", __func__); - return U14ERR_NOERROR; - } - - if ((pArea->bCircular) && // Circular transfer? - (pArea->bCircToHost) && (Read)) // In a supported direction - { // If so, we sort out offset ourself - bool bWait = false; // Flag for transfer having to wait - - dev_dbg(&pdx->interface->dev, "Circular buffers are %d at %d and %d at %d", - pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset, pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset); - if (pArea->aBlocks[1].dwSize > 0) // Using the second block already? - { - dwOffs = pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize; // take offset from that - bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0? - bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer - } - else // Area 1 not in use, try to use area 0 - { - if (pArea->aBlocks[0].dwSize == 0) // Reset block 0 if not in use - pArea->aBlocks[0].dwOffset = 0; - dwOffs = pArea->aBlocks[0].dwOffset + pArea->aBlocks[0].dwSize; - if ((dwOffs+dwLen) > pArea->dwLength) // Off the end of the buffer? - { - pArea->aBlocks[1].dwOffset = 0; // Set up to use second block - dwOffs = 0; - bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0? - bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer - } - } - - if (bWait) // This transfer will have to wait? - { - pdx->bXFerWaiting = true; // Flag we are waiting - dev_dbg(&pdx->interface->dev, "%s xfer waiting for circular buffer space", __func__); - return U14ERR_NOERROR; - } - - dev_dbg(&pdx->interface->dev, "%s circular xfer, %d bytes starting at %d", __func__, dwLen, dwOffs); - } - - // Save the parameters for the read\write transfer - pdx->StagedRead = Read; // Save the parameters for this read - pdx->StagedId = wIdent; // ID allows us to get transfer area info - pdx->StagedOffset = dwOffs; // The area within the transfer area - pdx->StagedLength = dwLen; - pdx->StagedDone = 0; // Initialise the byte count - pdx->dwDMAFlag = MODE_LINEAR; // Set DMA mode flag at this point - pdx->bXFerWaiting = false; // Clearly not a transfer waiting now + TRANSAREA *pArea = &pdx->rTransDef[wIdent]; // Transfer area info + + if (!CanAcceptIoRequests(pdx)) // Are we in a state to accept new requests? + { + dev_err(&pdx->interface->dev, "%s can't accept requests", + __func__); + return U14ERR_FAIL; + } + + dev_dbg(&pdx->interface->dev, + "%s xfer %d bytes to %s, offset %d, area %d", __func__, dwLen, + Read ? "host" : "1401", dwOffs, wIdent); + + // Amazingly, we can get an escape sequence back before the current staged Urb is done, so we + // have to check for this situation and, if so, wait until all is OK. + if (pdx->bStagedUrbPending) { + pdx->bXFerWaiting = true; // Flag we are waiting + dev_info(&pdx->interface->dev, + "%s xfer is waiting, as previous staged pending", + __func__); + return U14ERR_NOERROR; + } + + if (dwLen == 0) // allow 0-len read or write; just return success + { + dev_dbg(&pdx->interface->dev, + "%s OK; zero-len read/write request", __func__); + return U14ERR_NOERROR; + } + + if ((pArea->bCircular) && // Circular transfer? + (pArea->bCircToHost) && (Read)) // In a supported direction + { // If so, we sort out offset ourself + bool bWait = false; // Flag for transfer having to wait + + dev_dbg(&pdx->interface->dev, + "Circular buffers are %d at %d and %d at %d", + pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset, + pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset); + if (pArea->aBlocks[1].dwSize > 0) // Using the second block already? + { + dwOffs = pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize; // take offset from that + bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0? + bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer + } else // Area 1 not in use, try to use area 0 + { + if (pArea->aBlocks[0].dwSize == 0) // Reset block 0 if not in use + pArea->aBlocks[0].dwOffset = 0; + dwOffs = + pArea->aBlocks[0].dwOffset + + pArea->aBlocks[0].dwSize; + if ((dwOffs + dwLen) > pArea->dwLength) // Off the end of the buffer? + { + pArea->aBlocks[1].dwOffset = 0; // Set up to use second block + dwOffs = 0; + bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0? + bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer + } + } + + if (bWait) // This transfer will have to wait? + { + pdx->bXFerWaiting = true; // Flag we are waiting + dev_dbg(&pdx->interface->dev, + "%s xfer waiting for circular buffer space", + __func__); + return U14ERR_NOERROR; + } + + dev_dbg(&pdx->interface->dev, + "%s circular xfer, %d bytes starting at %d", __func__, + dwLen, dwOffs); + } + // Save the parameters for the read\write transfer + pdx->StagedRead = Read; // Save the parameters for this read + pdx->StagedId = wIdent; // ID allows us to get transfer area info + pdx->StagedOffset = dwOffs; // The area within the transfer area + pdx->StagedLength = dwLen; + pdx->StagedDone = 0; // Initialise the byte count + pdx->dwDMAFlag = MODE_LINEAR; // Set DMA mode flag at this point + pdx->bXFerWaiting = false; // Clearly not a transfer waiting now // KeClearEvent(&pdx->StagingDoneEvent); // Clear the transfer done event - StageChunk(pdx); // fire off the first chunk + StageChunk(pdx); // fire off the first chunk - return U14ERR_NOERROR; + return U14ERR_NOERROR; } /**************************************************************************** @@ -852,20 +905,21 @@ int ReadWriteMem(DEVICE_EXTENSION *pdx, bool Read, unsigned short wIdent, ** data we return FALSE. Used as part of decoding a DMA request. ** ****************************************************************************/ -static bool ReadChar(unsigned char* pChar, char* pBuf, unsigned int* pdDone, unsigned int dGot) +static bool ReadChar(unsigned char *pChar, char *pBuf, unsigned int *pdDone, + unsigned int dGot) { - bool bRead = false; - unsigned int dDone = *pdDone; - - if (dDone < dGot) // If there is more data - { - *pChar = (unsigned char)pBuf[dDone];// Extract the next char - dDone++; // Increment the done count - *pdDone = dDone; - bRead = true; // and flag success - } - - return bRead; + bool bRead = false; + unsigned int dDone = *pdDone; + + if (dDone < dGot) // If there is more data + { + *pChar = (unsigned char)pBuf[dDone]; // Extract the next char + dDone++; // Increment the done count + *pdDone = dDone; + bRead = true; // and flag success + } + + return bRead; } #ifdef NOTUSED @@ -876,12 +930,14 @@ static bool ReadChar(unsigned char* pChar, char* pBuf, unsigned int* pdDone, uns ** Reads a word from the 1401, just uses ReadChar twice; passes on any error ** *****************************************************************************/ -static bool ReadWord(unsigned short* pWord, char* pBuf, unsigned int* pdDone, unsigned int dGot) +static bool ReadWord(unsigned short *pWord, char *pBuf, unsigned int *pdDone, + unsigned int dGot) { - if (ReadChar((unsigned char*)pWord, pBuf, pdDone, dGot)) - return ReadChar(((unsigned char*)pWord)+1, pBuf, pdDone, dGot); - else - return false; + if (ReadChar((unsigned char *)pWord, pBuf, pdDone, dGot)) + return ReadChar(((unsigned char *)pWord) + 1, pBuf, pdDone, + dGot); + else + return false; } #endif @@ -895,39 +951,35 @@ static bool ReadWord(unsigned short* pWord, char* pBuf, unsigned int* pdDone, un ** to indicate three byte total. ** *****************************************************************************/ -static bool ReadHuff(volatile unsigned int* pDWord, char* pBuf, unsigned int* pdDone, unsigned int dGot) +static bool ReadHuff(volatile unsigned int *pDWord, char *pBuf, + unsigned int *pdDone, unsigned int dGot) { - unsigned char ucData; /* for each read to ReadChar */ - bool bReturn = true; /* assume we will succeed */ - unsigned int dwData = 0; /* Accumulator for the data */ - - if (ReadChar(&ucData, pBuf, pdDone, dGot)) - { - dwData = ucData; /* copy the data */ - if ((dwData & 0x00000080) != 0) /* Bit set for more data ? */ - { - dwData &= 0x0000007F; /* Clear the relevant bit */ - if (ReadChar(&ucData, pBuf, pdDone, dGot)) - { - dwData = (dwData << 8) | ucData; - if ((dwData & 0x00004000) != 0) /* three byte sequence ? */ - { - dwData &= 0x00003FFF; /* Clear the relevant bit */ - if (ReadChar(&ucData, pBuf, pdDone, dGot)) - dwData = (dwData << 8) | ucData; - else - bReturn = false; - } - } - else - bReturn = false; /* couldn't read data */ - } - } - else - bReturn = false; - - *pDWord = dwData; /* return the data */ - return bReturn; + unsigned char ucData; /* for each read to ReadChar */ + bool bReturn = true; /* assume we will succeed */ + unsigned int dwData = 0; /* Accumulator for the data */ + + if (ReadChar(&ucData, pBuf, pdDone, dGot)) { + dwData = ucData; /* copy the data */ + if ((dwData & 0x00000080) != 0) { /* Bit set for more data ? */ + dwData &= 0x0000007F; /* Clear the relevant bit */ + if (ReadChar(&ucData, pBuf, pdDone, dGot)) { + dwData = (dwData << 8) | ucData; + if ((dwData & 0x00004000) != 0) { /* three byte sequence ? */ + dwData &= 0x00003FFF; /* Clear the relevant bit */ + if (ReadChar + (&ucData, pBuf, pdDone, dGot)) + dwData = (dwData << 8) | ucData; + else + bReturn = false; + } + } else + bReturn = false; /* couldn't read data */ + } + } else + bReturn = false; + + *pDWord = dwData; /* return the data */ + return bReturn; } /*************************************************************************** @@ -944,66 +996,77 @@ static bool ReadHuff(volatile unsigned int* pDWord, char* pBuf, unsigned int* pd ** we start handling the data at offset zero. ** *****************************************************************************/ -static bool ReadDMAInfo(volatile DMADESC* pDmaDesc, DEVICE_EXTENSION *pdx, - char* pBuf, unsigned int dwCount) +static bool ReadDMAInfo(volatile DMADESC * pDmaDesc, DEVICE_EXTENSION * pdx, + char *pBuf, unsigned int dwCount) { - bool bResult = false; // assume we won't succeed - unsigned char ucData; - unsigned int dDone = 0; // We haven't parsed anything so far - - dev_dbg(&pdx->interface->dev, "%s", __func__); - - if (ReadChar(&ucData, pBuf, &dDone, dwCount)) - { - unsigned char ucTransCode = (ucData & 0x0F); // get code for transfer type - unsigned short wIdent = ((ucData >> 4) & 0x07); // and area identifier - - // fill in the structure we were given - pDmaDesc->wTransType = ucTransCode; // type of transfer - pDmaDesc->wIdent = wIdent; // area to use - pDmaDesc->dwSize = 0; // initialise other bits - pDmaDesc->dwOffset = 0; - - dev_dbg(&pdx->interface->dev, "%s type: %d ident: %d", __func__, pDmaDesc->wTransType, pDmaDesc->wIdent); - - pDmaDesc->bOutWard = (ucTransCode != TM_EXTTOHOST); // set transfer direction - - switch (ucTransCode) - { - case TM_EXTTOHOST: // Extended linear transfer modes (the only ones!) - case TM_EXTTO1401: - { - bResult = ReadHuff(&(pDmaDesc->dwOffset), pBuf, &dDone, dwCount) && - ReadHuff(&(pDmaDesc->dwSize), pBuf, &dDone, dwCount); - if (bResult) - { - dev_dbg(&pdx->interface->dev, "%s xfer offset & size %d %d", - __func__, pDmaDesc->dwOffset, pDmaDesc->dwSize); - - if ((wIdent >= MAX_TRANSAREAS) || // Illegal area number, or... - (!pdx->rTransDef[wIdent].bUsed) || // area not set up, or... - (pDmaDesc->dwOffset > pdx->rTransDef[wIdent].dwLength) || // range/size - ((pDmaDesc->dwOffset + pDmaDesc->dwSize) > (pdx->rTransDef[wIdent].dwLength))) - { - bResult = false; // bad parameter(s) - dev_dbg(&pdx->interface->dev, "%s bad param - id %d, bUsed %d, offset %d, size %d, area length %d", - __func__, wIdent, pdx->rTransDef[wIdent].bUsed, pDmaDesc->dwOffset, pDmaDesc->dwSize, - pdx->rTransDef[wIdent].dwLength); - } - } - break; - } - default: - break; - } - } - else - bResult = false; - - if (!bResult) // now check parameters for validity - dev_err(&pdx->interface->dev, "%s error reading Esc sequence", __func__); - - return bResult; + bool bResult = false; // assume we won't succeed + unsigned char ucData; + unsigned int dDone = 0; // We haven't parsed anything so far + + dev_dbg(&pdx->interface->dev, "%s", __func__); + + if (ReadChar(&ucData, pBuf, &dDone, dwCount)) { + unsigned char ucTransCode = (ucData & 0x0F); // get code for transfer type + unsigned short wIdent = ((ucData >> 4) & 0x07); // and area identifier + + // fill in the structure we were given + pDmaDesc->wTransType = ucTransCode; // type of transfer + pDmaDesc->wIdent = wIdent; // area to use + pDmaDesc->dwSize = 0; // initialise other bits + pDmaDesc->dwOffset = 0; + + dev_dbg(&pdx->interface->dev, "%s type: %d ident: %d", __func__, + pDmaDesc->wTransType, pDmaDesc->wIdent); + + pDmaDesc->bOutWard = (ucTransCode != TM_EXTTOHOST); // set transfer direction + + switch (ucTransCode) { + case TM_EXTTOHOST: // Extended linear transfer modes (the only ones!) + case TM_EXTTO1401: + { + bResult = + ReadHuff(&(pDmaDesc->dwOffset), pBuf, + &dDone, dwCount) + && ReadHuff(&(pDmaDesc->dwSize), pBuf, + &dDone, dwCount); + if (bResult) { + dev_dbg(&pdx->interface->dev, + "%s xfer offset & size %d %d", + __func__, pDmaDesc->dwOffset, + pDmaDesc->dwSize); + + if ((wIdent >= MAX_TRANSAREAS) || // Illegal area number, or... + (!pdx->rTransDef[wIdent].bUsed) || // area not set up, or... + (pDmaDesc->dwOffset > pdx->rTransDef[wIdent].dwLength) || // range/size + ((pDmaDesc->dwOffset + + pDmaDesc->dwSize) > + (pdx->rTransDef[wIdent]. + dwLength))) { + bResult = false; // bad parameter(s) + dev_dbg(&pdx->interface->dev, + "%s bad param - id %d, bUsed %d, offset %d, size %d, area length %d", + __func__, wIdent, + pdx->rTransDef[wIdent]. + bUsed, + pDmaDesc->dwOffset, + pDmaDesc->dwSize, + pdx->rTransDef[wIdent]. + dwLength); + } + } + break; + } + default: + break; + } + } else + bResult = false; + + if (!bResult) // now check parameters for validity + dev_err(&pdx->interface->dev, "%s error reading Esc sequence", + __func__); + + return bResult; } /**************************************************************************** @@ -1020,122 +1083,130 @@ static bool ReadDMAInfo(volatile DMADESC* pDmaDesc, DEVICE_EXTENSION *pdx, ** this is known to be at least 2 or we will not be called. ** ****************************************************************************/ -static int Handle1401Esc(DEVICE_EXTENSION* pdx, char* pCh, unsigned int dwCount) +static int Handle1401Esc(DEVICE_EXTENSION * pdx, char *pCh, + unsigned int dwCount) { - int iReturn = U14ERR_FAIL; - - // I have no idea what this next test is about. '?' is 0x3f, which is area 3, code - // 15. At the moment, this is not used, so it does no harm, but unless someone can - // tell me what this is for, it should be removed from this and the Windows driver. - if (pCh[0] == '?') // Is this an information response - { // Parse and save the information - } - else - { - spin_lock(&pdx->stagedLock); // Lock others out - - if (ReadDMAInfo(&pdx->rDMAInfo, pdx, pCh, dwCount)) // Get DMA parameters - { - unsigned short wTransType = pdx->rDMAInfo.wTransType; // check transfer type - - dev_dbg(&pdx->interface->dev, "%s xfer to %s, offset %d, length %d", __func__, - pdx->rDMAInfo.bOutWard ? "1401" : "host", - pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize); - - if (pdx->bXFerWaiting) // Check here for badly out of kilter... - { // This can never happen, really - dev_err(&pdx->interface->dev, "ERROR: DMA setup while transfer still waiting"); - spin_unlock(&pdx->stagedLock); - } - else - { - if ((wTransType == TM_EXTTOHOST) || (wTransType == TM_EXTTO1401)) - { - iReturn = ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard, pdx->rDMAInfo.wIdent, pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize); - if (iReturn != U14ERR_NOERROR) - dev_err(&pdx->interface->dev, "%s ReadWriteMem() failed %d", __func__, iReturn); - } - else // This covers non-linear transfer setup - dev_err(&pdx->interface->dev, "%s Unknown block xfer type %d", __func__, wTransType); - } - } - else // Failed to read parameters - dev_err(&pdx->interface->dev, "%s ReadDMAInfo() fail", __func__); - - spin_unlock(&pdx->stagedLock); // OK here - } - - dev_dbg(&pdx->interface->dev, "%s returns %d", __func__, iReturn); - - return iReturn; + int iReturn = U14ERR_FAIL; + + // I have no idea what this next test is about. '?' is 0x3f, which is area 3, code + // 15. At the moment, this is not used, so it does no harm, but unless someone can + // tell me what this is for, it should be removed from this and the Windows driver. + if (pCh[0] == '?') // Is this an information response + { // Parse and save the information + } else { + spin_lock(&pdx->stagedLock); // Lock others out + + if (ReadDMAInfo(&pdx->rDMAInfo, pdx, pCh, dwCount)) // Get DMA parameters + { + unsigned short wTransType = pdx->rDMAInfo.wTransType; // check transfer type + + dev_dbg(&pdx->interface->dev, + "%s xfer to %s, offset %d, length %d", __func__, + pdx->rDMAInfo.bOutWard ? "1401" : "host", + pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize); + + if (pdx->bXFerWaiting) // Check here for badly out of kilter... + { // This can never happen, really + dev_err(&pdx->interface->dev, + "ERROR: DMA setup while transfer still waiting"); + spin_unlock(&pdx->stagedLock); + } else { + if ((wTransType == TM_EXTTOHOST) + || (wTransType == TM_EXTTO1401)) { + iReturn = + ReadWriteMem(pdx, + !pdx->rDMAInfo. + bOutWard, + pdx->rDMAInfo.wIdent, + pdx->rDMAInfo.dwOffset, + pdx->rDMAInfo.dwSize); + if (iReturn != U14ERR_NOERROR) + dev_err(&pdx->interface->dev, + "%s ReadWriteMem() failed %d", + __func__, iReturn); + } else // This covers non-linear transfer setup + dev_err(&pdx->interface->dev, + "%s Unknown block xfer type %d", + __func__, wTransType); + } + } else // Failed to read parameters + dev_err(&pdx->interface->dev, "%s ReadDMAInfo() fail", + __func__); + + spin_unlock(&pdx->stagedLock); // OK here + } + + dev_dbg(&pdx->interface->dev, "%s returns %d", __func__, iReturn); + + return iReturn; } /**************************************************************************** ** Callback for the character read complete or error ****************************************************************************/ -static void ced_readchar_callback(struct urb* pUrb) +static void ced_readchar_callback(struct urb *pUrb) { - DEVICE_EXTENSION *pdx = pUrb->context; - int nGot = pUrb->actual_length; // what we transferred - - if (pUrb->status) // Do we have a problem to handle? - { - int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use for error - // sync/async unlink faults aren't errors... just saying device removed or stopped - if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN)) - { - dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status); - } - else - dev_dbg(&pdx->interface->dev, "%s - 0 chars pUrb->status=%d (shutdown?)", __func__, pUrb->status); - - spin_lock(&pdx->err_lock); - pdx->errors = pUrb->status; - spin_unlock(&pdx->err_lock); - nGot = 0; // and tidy up again if so - - spin_lock(&pdx->charInLock); // already at irq level - pdx->bPipeError[nPipe] = 1; // Flag an error for later - } - else - { - if ((nGot > 1) && ((pdx->pCoherCharIn[0] & 0x7f) == 0x1b)) // Esc sequence? - { - Handle1401Esc(pdx, &pdx->pCoherCharIn[1], nGot-1); // handle it - spin_lock(&pdx->charInLock); // already at irq level - } - else - { - spin_lock(&pdx->charInLock); // already at irq level - if (nGot > 0) - { - unsigned int i; - if (nGot < INBUF_SZ) - { - pdx->pCoherCharIn[nGot] = 0; // tidy the string - dev_dbg(&pdx->interface->dev, "%s got %d chars >%s<", __func__, nGot, pdx->pCoherCharIn); - } - - // We know that whatever we read must fit in the input buffer - for (i = 0; i < nGot; i++) - { - pdx->inputBuffer[pdx->dwInBuffPut++] = pdx->pCoherCharIn[i] & 0x7F; - if (pdx->dwInBuffPut >= INBUF_SZ) - pdx->dwInBuffPut = 0; - } - - if ((pdx->dwNumInput + nGot) <= INBUF_SZ) - pdx->dwNumInput += nGot; // Adjust the buffer count accordingly - } - else - dev_dbg(&pdx->interface->dev, "%s read ZLP", __func__); - } - } - - pdx->bReadCharsPending = false; // No longer have a pending read - spin_unlock(&pdx->charInLock); // already at irq level - - Allowi(pdx, true); // see if we can do the next one + DEVICE_EXTENSION *pdx = pUrb->context; + int nGot = pUrb->actual_length; // what we transferred + + if (pUrb->status) // Do we have a problem to handle? + { + int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use for error + // sync/async unlink faults aren't errors... just saying device removed or stopped + if (! + (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET + || pUrb->status == -ESHUTDOWN)) { + dev_err(&pdx->interface->dev, + "%s - nonzero write bulk status received: %d", + __func__, pUrb->status); + } else + dev_dbg(&pdx->interface->dev, + "%s - 0 chars pUrb->status=%d (shutdown?)", + __func__, pUrb->status); + + spin_lock(&pdx->err_lock); + pdx->errors = pUrb->status; + spin_unlock(&pdx->err_lock); + nGot = 0; // and tidy up again if so + + spin_lock(&pdx->charInLock); // already at irq level + pdx->bPipeError[nPipe] = 1; // Flag an error for later + } else { + if ((nGot > 1) && ((pdx->pCoherCharIn[0] & 0x7f) == 0x1b)) // Esc sequence? + { + Handle1401Esc(pdx, &pdx->pCoherCharIn[1], nGot - 1); // handle it + spin_lock(&pdx->charInLock); // already at irq level + } else { + spin_lock(&pdx->charInLock); // already at irq level + if (nGot > 0) { + unsigned int i; + if (nGot < INBUF_SZ) { + pdx->pCoherCharIn[nGot] = 0; // tidy the string + dev_dbg(&pdx->interface->dev, + "%s got %d chars >%s<", + __func__, nGot, + pdx->pCoherCharIn); + } + // We know that whatever we read must fit in the input buffer + for (i = 0; i < nGot; i++) { + pdx->inputBuffer[pdx->dwInBuffPut++] = + pdx->pCoherCharIn[i] & 0x7F; + if (pdx->dwInBuffPut >= INBUF_SZ) + pdx->dwInBuffPut = 0; + } + + if ((pdx->dwNumInput + nGot) <= INBUF_SZ) + pdx->dwNumInput += nGot; // Adjust the buffer count accordingly + } else + dev_dbg(&pdx->interface->dev, "%s read ZLP", + __func__); + } + } + + pdx->bReadCharsPending = false; // No longer have a pending read + spin_unlock(&pdx->charInLock); // already at irq level + + Allowi(pdx, true); // see if we can do the next one } /**************************************************************************** @@ -1145,48 +1216,50 @@ static void ced_readchar_callback(struct urb* pUrb) ** we can pick up any inward transfers. This can be called in multiple contexts ** so we use the irqsave version of the spinlock. ****************************************************************************/ -int Allowi(DEVICE_EXTENSION* pdx, bool bInCallback) +int Allowi(DEVICE_EXTENSION * pdx, bool bInCallback) { - int iReturn = U14ERR_NOERROR; - unsigned long flags; - spin_lock_irqsave(&pdx->charInLock, flags); // can be called in multiple contexts - - // We don't want char input running while DMA is in progress as we know that this - // can cause sequencing problems for the 2270. So don't. It will also allow the - // ERR response to get back to the host code too early on some PCs, even if there - // is no actual driver failure, so we don't allow this at all. - if (!pdx->bInDrawDown && // stop input if - !pdx->bReadCharsPending && // If no read request outstanding - (pdx->dwNumInput < (INBUF_SZ/2)) && // and there is some space - (pdx->dwDMAFlag == MODE_CHAR) && // not doing any DMA - (!pdx->bXFerWaiting) && // no xfer waiting to start - (CanAcceptIoRequests(pdx))) // and activity is generally OK - { // then off we go - unsigned int nMax = INBUF_SZ-pdx->dwNumInput; // max we could read - int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use - - dev_dbg(&pdx->interface->dev, "%s %d chars in input buffer", __func__, pdx->dwNumInput); - - usb_fill_int_urb(pdx->pUrbCharIn, pdx->udev, - usb_rcvintpipe(pdx->udev, pdx->epAddr[nPipe]), - pdx->pCoherCharIn, nMax, ced_readchar_callback, - pdx, pdx->bInterval); - pdx->pUrbCharIn->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; // short xfers are OK by default - usb_anchor_urb(pdx->pUrbCharIn, &pdx->submitted); // in case we need to kill it - iReturn = usb_submit_urb(pdx->pUrbCharIn, bInCallback ? GFP_ATOMIC : GFP_KERNEL); - if (iReturn) - { - usb_unanchor_urb(pdx->pUrbCharIn); // remove from list of active Urbs - pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later - dev_err(&pdx->interface->dev,"%s submit urb failed: %d", __func__, iReturn); - } - else - pdx->bReadCharsPending = true; // Flag that we are active here - } - - spin_unlock_irqrestore(&pdx->charInLock, flags); - - return iReturn; + int iReturn = U14ERR_NOERROR; + unsigned long flags; + spin_lock_irqsave(&pdx->charInLock, flags); // can be called in multiple contexts + + // We don't want char input running while DMA is in progress as we know that this + // can cause sequencing problems for the 2270. So don't. It will also allow the + // ERR response to get back to the host code too early on some PCs, even if there + // is no actual driver failure, so we don't allow this at all. + if (!pdx->bInDrawDown && // stop input if + !pdx->bReadCharsPending && // If no read request outstanding + (pdx->dwNumInput < (INBUF_SZ / 2)) && // and there is some space + (pdx->dwDMAFlag == MODE_CHAR) && // not doing any DMA + (!pdx->bXFerWaiting) && // no xfer waiting to start + (CanAcceptIoRequests(pdx))) // and activity is generally OK + { // then off we go + unsigned int nMax = INBUF_SZ - pdx->dwNumInput; // max we could read + int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use + + dev_dbg(&pdx->interface->dev, "%s %d chars in input buffer", + __func__, pdx->dwNumInput); + + usb_fill_int_urb(pdx->pUrbCharIn, pdx->udev, + usb_rcvintpipe(pdx->udev, pdx->epAddr[nPipe]), + pdx->pCoherCharIn, nMax, ced_readchar_callback, + pdx, pdx->bInterval); + pdx->pUrbCharIn->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; // short xfers are OK by default + usb_anchor_urb(pdx->pUrbCharIn, &pdx->submitted); // in case we need to kill it + iReturn = + usb_submit_urb(pdx->pUrbCharIn, + bInCallback ? GFP_ATOMIC : GFP_KERNEL); + if (iReturn) { + usb_unanchor_urb(pdx->pUrbCharIn); // remove from list of active Urbs + pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later + dev_err(&pdx->interface->dev, + "%s submit urb failed: %d", __func__, iReturn); + } else + pdx->bReadCharsPending = true; // Flag that we are active here + } + + spin_unlock_irqrestore(&pdx->charInLock, flags); + + return iReturn; } @@ -1198,147 +1271,147 @@ int Allowi(DEVICE_EXTENSION* pdx, bool bInCallback) ** enough for a 64-bit pointer. *****************************************************************************/ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) -static long ced_ioctl(struct file * file, unsigned int cmd, unsigned long ulArg) +static long ced_ioctl(struct file *file, unsigned int cmd, unsigned long ulArg) #else -static int ced_ioctl(struct inode * node, struct file * file, unsigned int cmd, unsigned long ulArg) +static int ced_ioctl(struct inode *node, struct file *file, unsigned int cmd, + unsigned long ulArg) #endif { - int err = 0; - DEVICE_EXTENSION *pdx = file->private_data; - if (!CanAcceptIoRequests(pdx)) // check we still exist - return -ENODEV; + int err = 0; + DEVICE_EXTENSION *pdx = file->private_data; + if (!CanAcceptIoRequests(pdx)) // check we still exist + return -ENODEV; - // Check that access is allowed, where is is needed. Anything that would have an indeterminate - // size will be checked by the specific command. - if (_IOC_DIR(cmd) & _IOC_READ) // read from point of view of user... - err = !access_ok(VERIFY_WRITE, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel write - else if (_IOC_DIR(cmd) & _IOC_WRITE) // and write from point of view of user... - err = !access_ok(VERIFY_READ, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel read - if (err) - return -EFAULT; + // Check that access is allowed, where is is needed. Anything that would have an indeterminate + // size will be checked by the specific command. + if (_IOC_DIR(cmd) & _IOC_READ) // read from point of view of user... + err = !access_ok(VERIFY_WRITE, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel write + else if (_IOC_DIR(cmd) & _IOC_WRITE) // and write from point of view of user... + err = !access_ok(VERIFY_READ, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel read + if (err) + return -EFAULT; - switch (_IOC_NR(cmd)) - { - case _IOC_NR(IOCTL_CED_SENDSTRING(0)): - return SendString(pdx, (const char __user*)ulArg, _IOC_SIZE(cmd)); + switch (_IOC_NR(cmd)) { + case _IOC_NR(IOCTL_CED_SENDSTRING(0)): + return SendString(pdx, (const char __user *)ulArg, + _IOC_SIZE(cmd)); - case _IOC_NR(IOCTL_CED_RESET1401): - return Reset1401(pdx); + case _IOC_NR(IOCTL_CED_RESET1401): + return Reset1401(pdx); - case _IOC_NR(IOCTL_CED_GETCHAR): - return GetChar(pdx); + case _IOC_NR(IOCTL_CED_GETCHAR): + return GetChar(pdx); - case _IOC_NR(IOCTL_CED_SENDCHAR): - return SendChar(pdx, (char)ulArg); + case _IOC_NR(IOCTL_CED_SENDCHAR): + return SendChar(pdx, (char)ulArg); - case _IOC_NR(IOCTL_CED_STAT1401): - return Stat1401(pdx); + case _IOC_NR(IOCTL_CED_STAT1401): + return Stat1401(pdx); - case _IOC_NR(IOCTL_CED_LINECOUNT): - return LineCount(pdx); + case _IOC_NR(IOCTL_CED_LINECOUNT): + return LineCount(pdx); - case _IOC_NR(IOCTL_CED_GETSTRING(0)): - return GetString(pdx, (char __user*)ulArg, _IOC_SIZE(cmd)); + case _IOC_NR(IOCTL_CED_GETSTRING(0)): + return GetString(pdx, (char __user *)ulArg, _IOC_SIZE(cmd)); - case _IOC_NR(IOCTL_CED_SETTRANSFER): - return SetTransfer(pdx, (TRANSFERDESC __user*)ulArg); + case _IOC_NR(IOCTL_CED_SETTRANSFER): + return SetTransfer(pdx, (TRANSFERDESC __user *) ulArg); - case _IOC_NR(IOCTL_CED_UNSETTRANSFER): - return UnsetTransfer(pdx, (int)ulArg); + case _IOC_NR(IOCTL_CED_UNSETTRANSFER): + return UnsetTransfer(pdx, (int)ulArg); - case _IOC_NR(IOCTL_CED_SETEVENT): - return SetEvent(pdx, (TRANSFEREVENT __user*)ulArg); + case _IOC_NR(IOCTL_CED_SETEVENT): + return SetEvent(pdx, (TRANSFEREVENT __user *) ulArg); - case _IOC_NR(IOCTL_CED_GETOUTBUFSPACE): - return GetOutBufSpace(pdx); + case _IOC_NR(IOCTL_CED_GETOUTBUFSPACE): + return GetOutBufSpace(pdx); - case _IOC_NR(IOCTL_CED_GETBASEADDRESS): - return -1; + case _IOC_NR(IOCTL_CED_GETBASEADDRESS): + return -1; - case _IOC_NR(IOCTL_CED_GETDRIVERREVISION): - return (2<<24)|(DRIVERMAJREV<<16) | DRIVERMINREV; // USB | MAJOR | MINOR + case _IOC_NR(IOCTL_CED_GETDRIVERREVISION): + return (2 << 24) | (DRIVERMAJREV << 16) | DRIVERMINREV; // USB | MAJOR | MINOR - case _IOC_NR(IOCTL_CED_GETTRANSFER): - return GetTransfer(pdx, (TGET_TX_BLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_GETTRANSFER): + return GetTransfer(pdx, (TGET_TX_BLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_KILLIO1401): - return KillIO1401(pdx); + case _IOC_NR(IOCTL_CED_KILLIO1401): + return KillIO1401(pdx); - case _IOC_NR(IOCTL_CED_STATEOF1401): - return StateOf1401(pdx); + case _IOC_NR(IOCTL_CED_STATEOF1401): + return StateOf1401(pdx); - case _IOC_NR(IOCTL_CED_GRAB1401): - case _IOC_NR(IOCTL_CED_FREE1401): - return U14ERR_NOERROR; + case _IOC_NR(IOCTL_CED_GRAB1401): + case _IOC_NR(IOCTL_CED_FREE1401): + return U14ERR_NOERROR; - case _IOC_NR(IOCTL_CED_STARTSELFTEST): - return StartSelfTest(pdx); + case _IOC_NR(IOCTL_CED_STARTSELFTEST): + return StartSelfTest(pdx); - case _IOC_NR(IOCTL_CED_CHECKSELFTEST): - return CheckSelfTest(pdx, (TGET_SELFTEST __user*)ulArg); + case _IOC_NR(IOCTL_CED_CHECKSELFTEST): + return CheckSelfTest(pdx, (TGET_SELFTEST __user *) ulArg); - case _IOC_NR(IOCTL_CED_TYPEOF1401): - return TypeOf1401(pdx); + case _IOC_NR(IOCTL_CED_TYPEOF1401): + return TypeOf1401(pdx); - case _IOC_NR(IOCTL_CED_TRANSFERFLAGS): - return TransferFlags(pdx); + case _IOC_NR(IOCTL_CED_TRANSFERFLAGS): + return TransferFlags(pdx); - case _IOC_NR(IOCTL_CED_DBGPEEK): - return DbgPeek(pdx, (TDBGBLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_DBGPEEK): + return DbgPeek(pdx, (TDBGBLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_DBGPOKE): - return DbgPoke(pdx, (TDBGBLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_DBGPOKE): + return DbgPoke(pdx, (TDBGBLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_DBGRAMPDATA): - return DbgRampData(pdx, (TDBGBLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_DBGRAMPDATA): + return DbgRampData(pdx, (TDBGBLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_DBGRAMPADDR): - return DbgRampAddr(pdx, (TDBGBLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_DBGRAMPADDR): + return DbgRampAddr(pdx, (TDBGBLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_DBGGETDATA): - return DbgGetData(pdx, (TDBGBLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_DBGGETDATA): + return DbgGetData(pdx, (TDBGBLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_DBGSTOPLOOP): - return DbgStopLoop(pdx); + case _IOC_NR(IOCTL_CED_DBGSTOPLOOP): + return DbgStopLoop(pdx); - case _IOC_NR(IOCTL_CED_FULLRESET): - pdx->bForceReset = true; // Set a flag for a full reset - break; + case _IOC_NR(IOCTL_CED_FULLRESET): + pdx->bForceReset = true; // Set a flag for a full reset + break; - case _IOC_NR(IOCTL_CED_SETCIRCULAR): - return SetCircular(pdx, (TRANSFERDESC __user*)ulArg); + case _IOC_NR(IOCTL_CED_SETCIRCULAR): + return SetCircular(pdx, (TRANSFERDESC __user *) ulArg); - case _IOC_NR(IOCTL_CED_GETCIRCBLOCK): - return GetCircBlock(pdx, (TCIRCBLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_GETCIRCBLOCK): + return GetCircBlock(pdx, (TCIRCBLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_FREECIRCBLOCK): - return FreeCircBlock(pdx, (TCIRCBLOCK __user*)ulArg); + case _IOC_NR(IOCTL_CED_FREECIRCBLOCK): + return FreeCircBlock(pdx, (TCIRCBLOCK __user *) ulArg); - case _IOC_NR(IOCTL_CED_WAITEVENT): - return WaitEvent(pdx, (int)(ulArg & 0xff), (int)(ulArg >> 8)); + case _IOC_NR(IOCTL_CED_WAITEVENT): + return WaitEvent(pdx, (int)(ulArg & 0xff), (int)(ulArg >> 8)); - case _IOC_NR(IOCTL_CED_TESTEVENT): - return TestEvent(pdx, (int)ulArg); + case _IOC_NR(IOCTL_CED_TESTEVENT): + return TestEvent(pdx, (int)ulArg); - default: - return U14ERR_NO_SUCH_FN; - } - return U14ERR_NOERROR; + default: + return U14ERR_NO_SUCH_FN; + } + return U14ERR_NOERROR; } -static const struct file_operations ced_fops = -{ - .owner = THIS_MODULE, - .read = ced_read, - .write = ced_write, - .open = ced_open, - .release = ced_release, - .flush = ced_flush, - .llseek = noop_llseek, +static const struct file_operations ced_fops = { + .owner = THIS_MODULE, + .read = ced_read, + .write = ced_write, + .open = ced_open, + .release = ced_release, + .flush = ced_flush, + .llseek = noop_llseek, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) - .unlocked_ioctl = ced_ioctl, + .unlocked_ioctl = ced_ioctl, #else - .ioctl = ced_ioctl, + .ioctl = ced_ioctl, #endif }; @@ -1346,245 +1419,250 @@ static const struct file_operations ced_fops = * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core */ -static struct usb_class_driver ced_class = -{ - .name = "cedusb%d", - .fops = &ced_fops, - .minor_base = USB_CED_MINOR_BASE, +static struct usb_class_driver ced_class = { + .name = "cedusb%d", + .fops = &ced_fops, + .minor_base = USB_CED_MINOR_BASE, }; // Check that the device that matches a 1401 vendor and product ID is OK to use and // initialise our DEVICE_EXTENSION. -static int ced_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int ced_probe(struct usb_interface *interface, + const struct usb_device_id *id) { - DEVICE_EXTENSION *pdx; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i, bcdDevice; - int retval = -ENOMEM; - - // allocate memory for our device extension and initialize it - pdx = kzalloc(sizeof(*pdx), GFP_KERNEL); - if (!pdx) - { - dev_err(&interface->dev, "Out of memory\n"); - goto error; - } - - for (i=0; i<MAX_TRANSAREAS; ++i) // Initialise the wait queues - { - init_waitqueue_head(&pdx->rTransDef[i].wqEvent); - } - - // Put initialises for our stuff here. Note that all of *pdx is zero, so - // no need to explicitly zero it. - spin_lock_init(&pdx->charOutLock); - spin_lock_init(&pdx->charInLock); - spin_lock_init(&pdx->stagedLock); - - // Initialises from the skeleton stuff - kref_init(&pdx->kref); - mutex_init(&pdx->io_mutex); - spin_lock_init(&pdx->err_lock); - init_usb_anchor(&pdx->submitted); - - pdx->udev = usb_get_dev(interface_to_usbdev(interface)); - pdx->interface = interface; - - // Attempt to identify the device - bcdDevice = pdx->udev->descriptor.bcdDevice; - i = (bcdDevice >> 8); - if (i == 0) - pdx->s1401Type = TYPEU1401; - else if ((i>=1) && (i<=23)) - pdx->s1401Type = i+2; - else - { - dev_err(&interface->dev, "%s Unknown device. bcdDevice = %d", __func__, bcdDevice); - goto error; - } - // set up the endpoint information. We only care about the number of EP as - // we know that we are dealing with a 1401 device. - iface_desc = interface->cur_altsetting; - pdx->nPipes = iface_desc->desc.bNumEndpoints; - dev_info(&interface->dev, "1401Type=%d with %d End Points", pdx->s1401Type, pdx->nPipes); - if ((pdx->nPipes < 3) || (pdx->nPipes > 4)) - goto error; - - // Allocate the URBs we hold for performing transfers - pdx->pUrbCharOut = usb_alloc_urb(0, GFP_KERNEL); // character output URB - pdx->pUrbCharIn = usb_alloc_urb(0, GFP_KERNEL); // character input URB - pdx->pStagedUrb = usb_alloc_urb(0, GFP_KERNEL); // block transfer URB - if (!pdx->pUrbCharOut || !pdx->pUrbCharIn || !pdx->pStagedUrb) - { - dev_err(&interface->dev, "%s URB alloc failed", __func__); - goto error; - } - - pdx->pCoherStagedIO = usb_alloc_coherent(pdx->udev, STAGED_SZ, GFP_KERNEL, &pdx->pStagedUrb->transfer_dma); - pdx->pCoherCharOut = usb_alloc_coherent(pdx->udev, OUTBUF_SZ, GFP_KERNEL, &pdx->pUrbCharOut->transfer_dma); - pdx->pCoherCharIn = usb_alloc_coherent(pdx->udev, INBUF_SZ, GFP_KERNEL, &pdx->pUrbCharIn->transfer_dma); - if (!pdx->pCoherCharOut || !pdx->pCoherCharIn || !pdx->pCoherStagedIO) - { - dev_err(&interface->dev, "%s Coherent buffer alloc failed", __func__); - goto error; - } - - for (i = 0; i < pdx->nPipes; ++i) - { - endpoint = &iface_desc->endpoint[i].desc; - pdx->epAddr[i] = endpoint->bEndpointAddress; - dev_info(&interface->dev, "Pipe %d, ep address %02x", i, pdx->epAddr[i]); - if (((pdx->nPipes==3) && (i==0)) || // if char input end point - ((pdx->nPipes==4) && (i==1))) - { - pdx->bInterval = endpoint->bInterval; // save the endpoint interrupt interval - dev_info(&interface->dev, "Pipe %d, bInterval = %d", i, pdx->bInterval); - } - - // Detect USB2 by checking last ep size (64 if USB1) - if (i == pdx->nPipes-1) // if this is the last ep (bulk) - { - pdx->bIsUSB2 = le16_to_cpu(endpoint->wMaxPacketSize) > 64; - dev_info(&pdx->interface->dev, "USB%d", pdx->bIsUSB2 + 1); - } - } - - /* save our data pointer in this interface device */ - usb_set_intfdata(interface, pdx); - - /* we can register the device now, as it is ready */ - retval = usb_register_dev(interface, &ced_class); - if (retval) - { - /* something prevented us from registering this driver */ - dev_err(&interface->dev, "Not able to get a minor for this device.\n"); - usb_set_intfdata(interface, NULL); - goto error; - } - - /* let the user know what node this device is now attached to */ - dev_info(&interface->dev, - "USB CEDUSB device now attached to cedusb #%d", - interface->minor); - return 0; + DEVICE_EXTENSION *pdx; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i, bcdDevice; + int retval = -ENOMEM; + + // allocate memory for our device extension and initialize it + pdx = kzalloc(sizeof(*pdx), GFP_KERNEL); + if (!pdx) { + dev_err(&interface->dev, "Out of memory\n"); + goto error; + } + + for (i = 0; i < MAX_TRANSAREAS; ++i) // Initialise the wait queues + { + init_waitqueue_head(&pdx->rTransDef[i].wqEvent); + } + + // Put initialises for our stuff here. Note that all of *pdx is zero, so + // no need to explicitly zero it. + spin_lock_init(&pdx->charOutLock); + spin_lock_init(&pdx->charInLock); + spin_lock_init(&pdx->stagedLock); + + // Initialises from the skeleton stuff + kref_init(&pdx->kref); + mutex_init(&pdx->io_mutex); + spin_lock_init(&pdx->err_lock); + init_usb_anchor(&pdx->submitted); + + pdx->udev = usb_get_dev(interface_to_usbdev(interface)); + pdx->interface = interface; + + // Attempt to identify the device + bcdDevice = pdx->udev->descriptor.bcdDevice; + i = (bcdDevice >> 8); + if (i == 0) + pdx->s1401Type = TYPEU1401; + else if ((i >= 1) && (i <= 23)) + pdx->s1401Type = i + 2; + else { + dev_err(&interface->dev, "%s Unknown device. bcdDevice = %d", + __func__, bcdDevice); + goto error; + } + // set up the endpoint information. We only care about the number of EP as + // we know that we are dealing with a 1401 device. + iface_desc = interface->cur_altsetting; + pdx->nPipes = iface_desc->desc.bNumEndpoints; + dev_info(&interface->dev, "1401Type=%d with %d End Points", + pdx->s1401Type, pdx->nPipes); + if ((pdx->nPipes < 3) || (pdx->nPipes > 4)) + goto error; + + // Allocate the URBs we hold for performing transfers + pdx->pUrbCharOut = usb_alloc_urb(0, GFP_KERNEL); // character output URB + pdx->pUrbCharIn = usb_alloc_urb(0, GFP_KERNEL); // character input URB + pdx->pStagedUrb = usb_alloc_urb(0, GFP_KERNEL); // block transfer URB + if (!pdx->pUrbCharOut || !pdx->pUrbCharIn || !pdx->pStagedUrb) { + dev_err(&interface->dev, "%s URB alloc failed", __func__); + goto error; + } + + pdx->pCoherStagedIO = + usb_alloc_coherent(pdx->udev, STAGED_SZ, GFP_KERNEL, + &pdx->pStagedUrb->transfer_dma); + pdx->pCoherCharOut = + usb_alloc_coherent(pdx->udev, OUTBUF_SZ, GFP_KERNEL, + &pdx->pUrbCharOut->transfer_dma); + pdx->pCoherCharIn = + usb_alloc_coherent(pdx->udev, INBUF_SZ, GFP_KERNEL, + &pdx->pUrbCharIn->transfer_dma); + if (!pdx->pCoherCharOut || !pdx->pCoherCharIn || !pdx->pCoherStagedIO) { + dev_err(&interface->dev, "%s Coherent buffer alloc failed", + __func__); + goto error; + } + + for (i = 0; i < pdx->nPipes; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + pdx->epAddr[i] = endpoint->bEndpointAddress; + dev_info(&interface->dev, "Pipe %d, ep address %02x", i, + pdx->epAddr[i]); + if (((pdx->nPipes == 3) && (i == 0)) || // if char input end point + ((pdx->nPipes == 4) && (i == 1))) { + pdx->bInterval = endpoint->bInterval; // save the endpoint interrupt interval + dev_info(&interface->dev, "Pipe %d, bInterval = %d", i, + pdx->bInterval); + } + // Detect USB2 by checking last ep size (64 if USB1) + if (i == pdx->nPipes - 1) // if this is the last ep (bulk) + { + pdx->bIsUSB2 = + le16_to_cpu(endpoint->wMaxPacketSize) > 64; + dev_info(&pdx->interface->dev, "USB%d", + pdx->bIsUSB2 + 1); + } + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, pdx); + + /* we can register the device now, as it is ready */ + retval = usb_register_dev(interface, &ced_class); + if (retval) { + /* something prevented us from registering this driver */ + dev_err(&interface->dev, + "Not able to get a minor for this device.\n"); + usb_set_intfdata(interface, NULL); + goto error; + } + + /* let the user know what node this device is now attached to */ + dev_info(&interface->dev, + "USB CEDUSB device now attached to cedusb #%d", + interface->minor); + return 0; error: - if (pdx) - kref_put(&pdx->kref, ced_delete); // frees allocated memory - return retval; + if (pdx) + kref_put(&pdx->kref, ced_delete); // frees allocated memory + return retval; } static void ced_disconnect(struct usb_interface *interface) { - DEVICE_EXTENSION *pdx = usb_get_intfdata(interface); - int minor = interface->minor; // save for message at the end - int i; - - usb_set_intfdata(interface, NULL); // remove the pdx from the interface - usb_deregister_dev(interface, &ced_class); // give back our minor device number - - mutex_lock(&pdx->io_mutex); // stop more I/O starting while... - ced_draw_down(pdx); // ...wait for then kill any io - for (i=0; i<MAX_TRANSAREAS; ++i) - { - int iErr = ClearArea(pdx, i); // ...release any used memory - if (iErr == U14ERR_UNLOCKFAIL) - dev_err(&pdx->interface->dev, "%s Area %d was in used", __func__, i); - } - pdx->interface = NULL; // ...we kill off link to interface - mutex_unlock(&pdx->io_mutex); + DEVICE_EXTENSION *pdx = usb_get_intfdata(interface); + int minor = interface->minor; // save for message at the end + int i; + + usb_set_intfdata(interface, NULL); // remove the pdx from the interface + usb_deregister_dev(interface, &ced_class); // give back our minor device number + + mutex_lock(&pdx->io_mutex); // stop more I/O starting while... + ced_draw_down(pdx); // ...wait for then kill any io + for (i = 0; i < MAX_TRANSAREAS; ++i) { + int iErr = ClearArea(pdx, i); // ...release any used memory + if (iErr == U14ERR_UNLOCKFAIL) + dev_err(&pdx->interface->dev, "%s Area %d was in used", + __func__, i); + } + pdx->interface = NULL; // ...we kill off link to interface + mutex_unlock(&pdx->io_mutex); - usb_kill_anchored_urbs(&pdx->submitted); + usb_kill_anchored_urbs(&pdx->submitted); - kref_put(&pdx->kref, ced_delete); // decrement our usage count + kref_put(&pdx->kref, ced_delete); // decrement our usage count - dev_info(&interface->dev, "USB cedusb #%d now disconnected", minor); + dev_info(&interface->dev, "USB cedusb #%d now disconnected", minor); } // Wait for all the urbs we know of to be done with, then kill off any that // are left. NBNB we will need to have a mechanism to stop circular xfers // from trying to fire off more urbs. We will wait up to 3 seconds for Urbs // to be done. -void ced_draw_down(DEVICE_EXTENSION *pdx) +void ced_draw_down(DEVICE_EXTENSION * pdx) { - int time; - dev_dbg(&pdx->interface->dev,"%s called", __func__); - - pdx->bInDrawDown = true; - time = usb_wait_anchor_empty_timeout(&pdx->submitted, 3000); - if (!time) // if we timed out we kill the urbs - { - usb_kill_anchored_urbs(&pdx->submitted); - dev_err(&pdx->interface->dev,"%s timed out", __func__); - } - pdx->bInDrawDown = false; - } + int time; + dev_dbg(&pdx->interface->dev, "%s called", __func__); + + pdx->bInDrawDown = true; + time = usb_wait_anchor_empty_timeout(&pdx->submitted, 3000); + if (!time) // if we timed out we kill the urbs + { + usb_kill_anchored_urbs(&pdx->submitted); + dev_err(&pdx->interface->dev, "%s timed out", __func__); + } + pdx->bInDrawDown = false; +} static int ced_suspend(struct usb_interface *intf, pm_message_t message) { - DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); - if (!pdx) - return 0; - ced_draw_down(pdx); + DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); + if (!pdx) + return 0; + ced_draw_down(pdx); - dev_dbg(&pdx->interface->dev,"%s called", __func__); - return 0; + dev_dbg(&pdx->interface->dev, "%s called", __func__); + return 0; } static int ced_resume(struct usb_interface *intf) { - DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); - if (!pdx) - return 0; - dev_dbg(&pdx->interface->dev,"%s called", __func__); - return 0; + DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); + if (!pdx) + return 0; + dev_dbg(&pdx->interface->dev, "%s called", __func__); + return 0; } static int ced_pre_reset(struct usb_interface *intf) { - DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); - dev_dbg(&pdx->interface->dev, "%s", __func__); - mutex_lock(&pdx->io_mutex); - ced_draw_down(pdx); - return 0; + DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); + dev_dbg(&pdx->interface->dev, "%s", __func__); + mutex_lock(&pdx->io_mutex); + ced_draw_down(pdx); + return 0; } static int ced_post_reset(struct usb_interface *intf) { - DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); - dev_dbg(&pdx->interface->dev, "%s", __func__); + DEVICE_EXTENSION *pdx = usb_get_intfdata(intf); + dev_dbg(&pdx->interface->dev, "%s", __func__); - /* we are sure no URBs are active - no locking needed */ - pdx->errors = -EPIPE; - mutex_unlock(&pdx->io_mutex); + /* we are sure no URBs are active - no locking needed */ + pdx->errors = -EPIPE; + mutex_unlock(&pdx->io_mutex); - return 0; + return 0; } -static struct usb_driver ced_driver = -{ - .name = "cedusb", - .probe = ced_probe, - .disconnect = ced_disconnect, - .suspend = ced_suspend, - .resume = ced_resume, - .pre_reset = ced_pre_reset, - .post_reset = ced_post_reset, - .id_table = ced_table, - .supports_autosuspend = 1, +static struct usb_driver ced_driver = { + .name = "cedusb", + .probe = ced_probe, + .disconnect = ced_disconnect, + .suspend = ced_suspend, + .resume = ced_resume, + .pre_reset = ced_pre_reset, + .post_reset = ced_post_reset, + .id_table = ced_table, + .supports_autosuspend = 1, }; static int __init usb_skel_init(void) { - /* register this driver with the USB subsystem */ - return usb_register(&ced_driver); + /* register this driver with the USB subsystem */ + return usb_register(&ced_driver); } static void __exit usb_skel_exit(void) { - /* deregister this driver with the USB subsystem */ - usb_deregister(&ced_driver); + /* deregister this driver with the USB subsystem */ + usb_deregister(&ced_driver); } module_init(usb_skel_init); |