|
@@ -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);
|