staging: gpib: Add Agilent/Keysight 82357x USB GPIB driver
authorDave Penkler <dpenkler@gmail.com>
Wed, 18 Sep 2024 12:18:56 +0000 (14:18 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 10 Oct 2024 13:28:34 +0000 (15:28 +0200)
Driver for the Agilent/Keysight USB dongles.

Signed-off-by: Dave Penkler <dpenkler@gmail.com>
Link: https://lore.kernel.org/r/20240918121908.19366-10-dpenkler@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/gpib/agilent_82357a/Makefile [new file with mode: 0644]
drivers/staging/gpib/agilent_82357a/agilent_82357a.c [new file with mode: 0644]
drivers/staging/gpib/agilent_82357a/agilent_82357a.h [new file with mode: 0644]

diff --git a/drivers/staging/gpib/agilent_82357a/Makefile b/drivers/staging/gpib/agilent_82357a/Makefile
new file mode 100644 (file)
index 0000000..4a1d940
--- /dev/null
@@ -0,0 +1,4 @@
+
+obj-m += agilent_82357a.o
+
+
diff --git a/drivers/staging/gpib/agilent_82357a/agilent_82357a.c b/drivers/staging/gpib/agilent_82357a/agilent_82357a.c
new file mode 100644 (file)
index 0000000..bbc7e88
--- /dev/null
@@ -0,0 +1,1692 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ *     driver for Agilent 82357A/B usb to gpib adapters                   *
+ *    copyright                   : (C) 2004 by Frank Mori Hess                   *
+ ***************************************************************************/
+
+#define _GNU_SOURCE
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "agilent_82357a.h"
+#include "gpibP.h"
+#include "tms9914.h"
+
+MODULE_LICENSE("GPL");
+
+#define MAX_NUM_82357A_INTERFACES 128
+static struct usb_interface *agilent_82357a_driver_interfaces[MAX_NUM_82357A_INTERFACES];
+DEFINE_MUTEX(agilent_82357a_hotplug_lock);
+
+static unsigned int agilent_82357a_update_status(gpib_board_t *board, unsigned int clear_mask);
+
+static int agilent_82357a_take_control_internal(gpib_board_t *board, int synchronous);
+
+static void agilent_82357a_bulk_complete(struct urb *urb)
+{
+       struct agilent_82357a_urb_ctx *context = urb->context;
+
+//     printk("debug: %s: status=0x%x, error_count=%i, actual_length=%i\n", __func__,
+//             urb->status, urb->error_count, urb->actual_length);
+
+       up(&context->complete);
+}
+
+static void agilent_82357a_timeout_handler(struct timer_list *t)
+{
+       struct agilent_82357a_priv *a_priv = from_timer(a_priv, t, bulk_timer);
+       struct agilent_82357a_urb_ctx *context = &a_priv->context;
+
+       context->timed_out = 1;
+       up(&context->complete);
+}
+
+static int agilent_82357a_send_bulk_msg(struct agilent_82357a_priv *a_priv, void *data,
+                                       int data_length, int *actual_data_length,
+                                       int timeout_msecs)
+{
+       struct usb_device *usb_dev;
+       int retval;
+       unsigned int out_pipe;
+       struct agilent_82357a_urb_ctx *context = &a_priv->context;
+
+       *actual_data_length = 0;
+       retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock);
+       if (retval)
+               return retval;
+       if (!a_priv->bus_interface) {
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               return -ENODEV;
+       }
+       if (a_priv->bulk_urb) {
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               return -EAGAIN;
+       }
+       a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!a_priv->bulk_urb) {
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               return -ENOMEM;
+       }
+       usb_dev = interface_to_usbdev(a_priv->bus_interface);
+       out_pipe = usb_sndbulkpipe(usb_dev, a_priv->bulk_out_endpoint);
+       sema_init(&context->complete, 0);
+       context->timed_out = 0;
+       usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, out_pipe, data, data_length,
+                         &agilent_82357a_bulk_complete, context);
+
+       if (timeout_msecs)
+               mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs));
+
+       //printk("%s: submitting urb\n", __func__);
+       retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL);
+       if (retval) {
+               pr_err("%s: failed to submit bulk out urb, retval=%i\n", __func__, retval);
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               goto cleanup;
+       }
+       mutex_unlock(&a_priv->bulk_alloc_lock);
+       if (down_interruptible(&context->complete)) {
+               pr_err("%s: interrupted\n", __func__);
+               retval = -ERESTARTSYS;
+               goto cleanup;
+       }
+       if (context->timed_out) {
+               retval = -ETIMEDOUT;
+       } else {
+               retval = a_priv->bulk_urb->status;
+               *actual_data_length = a_priv->bulk_urb->actual_length;
+       }
+cleanup:
+       if (timeout_msecs) {
+               if (timer_pending(&a_priv->bulk_timer))
+                       del_timer_sync(&a_priv->bulk_timer);
+       }
+       mutex_lock(&a_priv->bulk_alloc_lock);
+       if (a_priv->bulk_urb) {
+               usb_kill_urb(a_priv->bulk_urb);
+               usb_free_urb(a_priv->bulk_urb);
+               a_priv->bulk_urb = NULL;
+       }
+       mutex_unlock(&a_priv->bulk_alloc_lock);
+       return retval;
+}
+
+static int agilent_82357a_receive_bulk_msg(struct agilent_82357a_priv *a_priv, void *data,
+                                          int data_length, int *actual_data_length,
+                                          int timeout_msecs)
+{
+       struct usb_device *usb_dev;
+       int retval;
+       unsigned int in_pipe;
+       struct agilent_82357a_urb_ctx *context = &a_priv->context;
+
+       *actual_data_length = 0;
+       retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock);
+       if (retval)
+               return retval;
+       if (!a_priv->bus_interface) {
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               return -ENODEV;
+       }
+       if (a_priv->bulk_urb) {
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               return -EAGAIN;
+       }
+       a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!a_priv->bulk_urb) {
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               return -ENOMEM;
+       }
+       usb_dev = interface_to_usbdev(a_priv->bus_interface);
+       in_pipe = usb_rcvbulkpipe(usb_dev, AGILENT_82357_BULK_IN_ENDPOINT);
+       sema_init(&context->complete, 0);
+       context->timed_out = 0;
+       usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, in_pipe, data, data_length,
+                         &agilent_82357a_bulk_complete, context);
+
+       if (timeout_msecs)
+               mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs));
+
+       //printk("%s: submitting urb\n", __func__);
+       retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL);
+       if (retval) {
+               pr_err("%s: failed to submit bulk out urb, retval=%i\n", __func__, retval);
+               mutex_unlock(&a_priv->bulk_alloc_lock);
+               goto cleanup;
+       }
+       mutex_unlock(&a_priv->bulk_alloc_lock);
+       if (down_interruptible(&context->complete)) {
+               pr_err("%s: interrupted\n", __func__);
+               retval = -ERESTARTSYS;
+               goto cleanup;
+       }
+       if (context->timed_out) {
+               retval = -ETIMEDOUT;
+               goto cleanup;
+       }
+       retval = a_priv->bulk_urb->status;
+       *actual_data_length = a_priv->bulk_urb->actual_length;
+cleanup:
+       if (timeout_msecs)
+               del_timer_sync(&a_priv->bulk_timer);
+
+       mutex_lock(&a_priv->bulk_alloc_lock);
+       if (a_priv->bulk_urb) {
+               usb_kill_urb(a_priv->bulk_urb);
+               usb_free_urb(a_priv->bulk_urb);
+               a_priv->bulk_urb = NULL;
+       }
+       mutex_unlock(&a_priv->bulk_alloc_lock);
+       return retval;
+}
+
+static int agilent_82357a_receive_control_msg(struct agilent_82357a_priv *a_priv, __u8 request,
+                                             __u8 requesttype, __u16 value,  __u16 index,
+                                             void *data, __u16 size, int timeout_msecs)
+{
+       struct usb_device *usb_dev;
+       int retval;
+       unsigned int in_pipe;
+
+       retval = mutex_lock_interruptible(&a_priv->control_alloc_lock);
+       if (retval)
+               return retval;
+       if (!a_priv->bus_interface) {
+               mutex_unlock(&a_priv->control_alloc_lock);
+               return -ENODEV;
+       }
+       usb_dev = interface_to_usbdev(a_priv->bus_interface);
+       in_pipe = usb_rcvctrlpipe(usb_dev, AGILENT_82357_CONTROL_ENDPOINT);
+       retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data,
+                                size, timeout_msecs);
+       mutex_unlock(&a_priv->control_alloc_lock);
+       return retval;
+}
+
+static void agilent_82357a_dump_raw_block(const u8 *raw_data, int length)
+{
+#define RAW_BUF_SIZE 256
+       int i, pos = 0;
+       char print_buf[RAW_BUF_SIZE];
+
+       pr_info("hex block dump\n");
+       for (i = 0; i < length; ++i) {
+               if (i && (i % 8 == 0)) {
+                       pr_info("%s\n", print_buf);
+                       pos = 0;
+               }
+               pos += snprintf(&print_buf[pos], RAW_BUF_SIZE, " %02x", raw_data[i]);
+       }
+       if (pos)
+               pr_info("%s\n", print_buf);
+}
+
+static int agilent_82357a_write_registers(struct agilent_82357a_priv *a_priv,
+                                         const struct agilent_82357a_register_pairlet *writes,
+                                         int num_writes)
+{
+       int retval;
+       u8 *out_data, *in_data;
+       int out_data_length, in_data_length;
+       int bytes_written, bytes_read;
+       int i = 0;
+       int j;
+       static const int bytes_per_write = 2;
+       static const int header_length = 2;
+       static const int max_writes = 31;
+
+       if (num_writes > max_writes) {
+               pr_err("%s: bug! num_writes=%i too large\n", __func__, num_writes);
+               return -EIO;
+       }
+       out_data_length = num_writes * bytes_per_write + header_length;
+       out_data = kmalloc(out_data_length, GFP_KERNEL);
+       if (!out_data) {
+               pr_err("%s: kmalloc failed\n", __func__);
+               return -ENOMEM;
+       }
+       out_data[i++] = DATA_PIPE_CMD_WR_REGS;
+       out_data[i++] = num_writes;
+       for (j = 0; j < num_writes; j++)        {
+               out_data[i++] = writes[j].address;
+               out_data[i++] = writes[j].value;
+       }
+       if (i > out_data_length)
+               pr_err("%s: bug! buffer overrun\n", __func__);
+       retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+       if (retval) {
+               kfree(out_data);
+               return retval;
+       }
+       retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000);
+       kfree(out_data);
+       if (retval) {
+               pr_err("%s: agilent_82357a_send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+                      __func__, retval, bytes_written, i);
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               return retval;
+       }
+       in_data_length = 0x20;
+       in_data = kmalloc(in_data_length, GFP_KERNEL);
+       if (!in_data) {
+               pr_err("%s: kmalloc failed\n", __func__);
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               return -ENOMEM;
+       }
+       retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length,
+                                                &bytes_read, 1000);
+       mutex_unlock(&a_priv->bulk_transfer_lock);
+
+       if (retval) {
+               pr_err("%s: agilent_82357a_receive_bulk_msg returned %i, bytes_read=%i\n",
+                      __func__, retval, bytes_read);
+               agilent_82357a_dump_raw_block(in_data, bytes_read);
+               kfree(in_data);
+               return -EIO;
+       }
+       if (in_data[0] != (0xff & ~DATA_PIPE_CMD_WR_REGS)) {
+               pr_err("%s: error, bulk command=0x%x != ~DATA_PIPE_CMD_WR_REGS\n",
+                      __func__, in_data[0]);
+               return -EIO;
+       }
+       if (in_data[1]) {
+               pr_err("%s: nonzero error code 0x%x in DATA_PIPE_CMD_WR_REGS response\n",
+                      __func__, in_data[1]);
+               return -EIO;
+       }
+       kfree(in_data);
+       return 0;
+}
+
+static int agilent_82357a_read_registers(struct agilent_82357a_priv *a_priv,
+                                        struct agilent_82357a_register_pairlet *reads,
+                                        int num_reads, int blocking)
+{
+       int retval;
+       u8 *out_data, *in_data;
+       int out_data_length, in_data_length;
+       int bytes_written, bytes_read;
+       int i = 0;
+       int j;
+       static const int header_length = 2;
+       static const int max_reads = 62;
+
+       if (num_reads > max_reads)
+               pr_err("%s: bug! num_reads=%i too large\n", __func__, num_reads);
+
+       out_data_length = num_reads + header_length;
+       out_data = kmalloc(out_data_length, GFP_KERNEL);
+       if (!out_data) {
+               pr_err("%s: kmalloc failed\n", __func__);
+               return -ENOMEM;
+       }
+       out_data[i++] = DATA_PIPE_CMD_RD_REGS;
+       out_data[i++] = num_reads;
+       for (j = 0; j < num_reads; j++)
+               out_data[i++] = reads[j].address;
+       if (i > out_data_length)
+               pr_err("%s: bug! buffer overrun\n", __func__);
+       if (blocking) {
+               retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+               if (retval) {
+                       kfree(out_data);
+                       return retval;
+               }
+       } else {
+               retval = mutex_trylock(&a_priv->bulk_transfer_lock);
+               if (retval == 0) {
+                       kfree(out_data);
+                       return -EAGAIN;
+               }
+       }
+       retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000);
+       kfree(out_data);
+       if (retval) {
+               pr_err("%s: agilent_82357a_send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+                      __func__, retval, bytes_written, i);
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               return retval;
+       }
+       in_data_length = 0x20;
+       in_data = kmalloc(in_data_length, GFP_KERNEL);
+       if (!in_data) {
+               pr_err("%s: kmalloc failed\n", __func__);
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               return -ENOMEM;
+       }
+       retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length,
+                                                &bytes_read, 10000);
+       mutex_unlock(&a_priv->bulk_transfer_lock);
+
+       if (retval) {
+               pr_err("%s: agilent_82357a_receive_bulk_msg returned %i, bytes_read=%i\n",
+                      __func__, retval, bytes_read);
+               agilent_82357a_dump_raw_block(in_data, bytes_read);
+               kfree(in_data);
+               return -EIO;
+       }
+       i = 0;
+       if (in_data[i++] != (0xff & ~DATA_PIPE_CMD_RD_REGS)) {
+               pr_err("%s: error, bulk command=0x%x != ~DATA_PIPE_CMD_RD_REGS\n",
+                      __func__, in_data[0]);
+               return -EIO;
+       }
+       if (in_data[i++]) {
+               pr_err("%s: nonzero error code 0x%x in DATA_PIPE_CMD_RD_REGS response\n",
+                      __func__, in_data[1]);
+               return -EIO;
+       }
+       for (j = 0; j < num_reads; j++)
+               reads[j].value = in_data[i++];
+       kfree(in_data);
+       return 0;
+}
+
+static int agilent_82357a_abort(struct agilent_82357a_priv *a_priv, int flush)
+{
+       int retval = 0;
+       int receive_control_retval;
+       u16 wIndex = 0;
+       u8 *status_data;
+       static const unsigned int status_data_len = 2;
+
+       status_data = kmalloc(status_data_len, GFP_KERNEL);
+       if (!status_data)
+               return -ENOMEM;
+
+       if (flush)
+               wIndex |= XA_FLUSH;
+       receive_control_retval = agilent_82357a_receive_control_msg(a_priv,
+                                                                   agilent_82357a_control_request,
+                                                                   USB_DIR_IN | USB_TYPE_VENDOR |
+                                                                   USB_RECIP_DEVICE, XFER_ABORT,
+                                                                   wIndex, status_data,
+                                                                   status_data_len, 100);
+       if (receive_control_retval < 0) {
+               pr_err("%s: agilent_82357a_receive_control_msg() returned %i\n",
+                      __func__, receive_control_retval);
+               retval = -EIO;
+               goto cleanup;
+       }
+       if (status_data[0] != (~XFER_ABORT & 0xff)) {
+               pr_err("%s: error, major code=0x%x != ~XFER_ABORT\n", __func__, status_data[0]);
+               retval = -EIO;
+               goto cleanup;
+       }
+       switch (status_data[1]) {
+       case UGP_SUCCESS:
+               retval = 0;
+               break;
+       case UGP_ERR_FLUSHING:
+               if (flush) {
+                       retval = 0;
+                       break;
+               }
+               fallthrough;
+       case UGP_ERR_FLUSHING_ALREADY:
+       default:
+               pr_err("%s: abort returned error code=0x%x\n", __func__, status_data[1]);
+               retval = -EIO;
+               break;
+       }
+
+cleanup:
+       kfree(status_data);
+       return retval;
+}
+
+// interface functions
+int agilent_82357a_command(gpib_board_t *board, uint8_t *buffer, size_t length,
+                          size_t *bytes_written);
+
+static int agilent_82357a_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
+                              size_t *nbytes)
+{
+       int retval;
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       u8 *out_data, *in_data;
+       int out_data_length, in_data_length;
+       int bytes_written, bytes_read;
+       int i = 0;
+       u8 trailing_flags;
+       unsigned long start_jiffies = jiffies;
+       int msec_timeout;
+
+       *nbytes = 0;
+       *end = 0;
+       out_data_length = 0x9;
+       out_data = kmalloc(out_data_length, GFP_KERNEL);
+       if (!out_data)
+               return -ENOMEM;
+       out_data[i++] = DATA_PIPE_CMD_READ;
+       out_data[i++] = 0;      //primary address when ARF_NO_ADDR is not set
+       out_data[i++] = 0;      //secondary address when ARF_NO_ADDR is not set
+       out_data[i] = ARF_NO_ADDRESS | ARF_END_ON_EOI;
+       if (a_priv->eos_mode & REOS)
+               out_data[i] |= ARF_END_ON_EOS_CHAR;
+       ++i;
+       out_data[i++] = length & 0xff;
+       out_data[i++] = (length >> 8) & 0xff;
+       out_data[i++] = (length >> 16) & 0xff;
+       out_data[i++] = (length >> 24) & 0xff;
+       out_data[i++] = a_priv->eos_char;
+       msec_timeout = (board->usec_timeout + 999) / 1000;
+       retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+       if (retval) {
+               kfree(out_data);
+               return retval;
+       }
+       retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, msec_timeout);
+       kfree(out_data);
+       if (retval || bytes_written != i) {
+               pr_err("%s: agilent_82357a_send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+                      __func__, retval, bytes_written, i);
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               if (retval < 0)
+                       return retval;
+               return -EIO;
+       }
+       in_data_length = length + 1;
+       in_data = kmalloc(in_data_length, GFP_KERNEL);
+       if (!in_data) {
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               return -ENOMEM;
+       }
+       if (board->usec_timeout != 0)
+               msec_timeout -= jiffies_to_msecs(jiffies - start_jiffies) - 1;
+       if (msec_timeout >= 0) {
+               retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length,
+                                                        &bytes_read, msec_timeout);
+       } else {
+               retval = -ETIMEDOUT;
+               bytes_read = 0;
+       }
+       if (retval == -ETIMEDOUT) {
+               int extra_bytes_read;
+               int extra_bytes_retval;
+
+               agilent_82357a_abort(a_priv, 1);
+               extra_bytes_retval = agilent_82357a_receive_bulk_msg(a_priv, in_data + bytes_read,
+                                                                    in_data_length - bytes_read,
+                                                                    &extra_bytes_read, 100);
+               //printk("%s: agilent_82357a_receive_bulk_msg timed out, bytes_read=%i,
+               // extra_bytes_read=%i\n",
+               //      __func__, bytes_read, extra_bytes_read);
+               bytes_read += extra_bytes_read;
+               if (extra_bytes_retval) {
+                       pr_err("%s: extra_bytes_retval=%i, bytes_read=%i\n", __func__,
+                              extra_bytes_retval, bytes_read);
+                       agilent_82357a_abort(a_priv, 0);
+               }
+       } else if (retval) {
+               pr_err("%s: agilent_82357a_receive_bulk_msg returned %i, bytes_read=%i\n",
+                      __func__, retval, bytes_read);
+               agilent_82357a_abort(a_priv, 0);
+       }
+       mutex_unlock(&a_priv->bulk_transfer_lock);
+       if (bytes_read > length + 1) {
+               bytes_read = length + 1;
+               pr_warn("%s: bytes_read > length? truncating", __func__);
+       }
+       //printk("%s: received response:\n", __func__);
+       // agilent_82357a_dump_raw_block(in_data, bytes_read);
+       if (bytes_read >= 1) {
+               memcpy(buffer, in_data, bytes_read - 1);
+               trailing_flags = in_data[bytes_read - 1];
+               *nbytes = bytes_read - 1;
+               if (trailing_flags & (ATRF_EOI | ATRF_EOS))
+                       *end = 1;
+       }
+       kfree(in_data);
+
+       /* Fix for a bug in 9914A that does not return the contents of ADSR
+        *  when the board is in listener active state and ATN is not asserted.
+        *  Set ATN here to obtain a valid board level ibsta
+        */
+       agilent_82357a_take_control_internal(board, 0);
+
+       //FIXME check trailing flags for error
+       return retval;
+}
+
+static ssize_t agilent_82357a_generic_write(gpib_board_t *board, uint8_t *buffer, size_t length,
+                                           int send_commands, int send_eoi, size_t *bytes_written)
+{
+       int retval;
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       u8 *out_data = NULL;
+       u8 *status_data = NULL;
+       int out_data_length;
+       int raw_bytes_written;
+       int i = 0, j;
+       int msec_timeout;
+       unsigned short bsr, adsr;
+       struct agilent_82357a_register_pairlet read_reg;
+
+       *bytes_written = 0;
+       out_data_length = length + 0x8;
+       out_data = kmalloc(out_data_length, GFP_KERNEL);
+       if (!out_data)
+               return -ENOMEM;
+       out_data[i++] = DATA_PIPE_CMD_WRITE;
+       out_data[i++] = 0; // primary address when AWF_NO_ADDRESS is not set
+       out_data[i++] = 0; // secondary address when AWF_NO_ADDRESS is not set
+       out_data[i] = AWF_NO_ADDRESS | AWF_NO_FAST_TALKER_FIRST_BYTE;
+       if (send_commands)
+               out_data[i] |= AWF_ATN | AWF_NO_FAST_TALKER;
+       if (send_eoi)
+               out_data[i] |= AWF_SEND_EOI;
+       ++i;
+       out_data[i++] = length & 0xff;
+       out_data[i++] = (length >> 8) & 0xff;
+       out_data[i++] = (length >> 16) & 0xff;
+       out_data[i++] = (length >> 24) & 0xff;
+       for (j = 0; j < length; j++)
+               out_data[i++] = buffer[j];
+       //printk("%s: sending bulk msg(), send_commands=%i\n", __func__, send_commands);
+
+       clear_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags);
+
+       msec_timeout = (board->usec_timeout + 999) / 1000;
+       retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+       if (retval) {
+               kfree(out_data);
+               return retval;
+       }
+       retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &raw_bytes_written,
+                                             msec_timeout);
+       kfree(out_data);
+       if (retval || raw_bytes_written != i) {
+               agilent_82357a_abort(a_priv, 0);
+               pr_err("%s: agilent_82357a_send_bulk_msg returned %i, raw_bytes_written=%i, i=%i\n",
+                      __func__, retval, raw_bytes_written, i);
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               if (retval < 0)
+                       return retval;
+               return -EIO;
+       }
+       //printk("%s: waiting for write complete\n", __func__);
+       retval = wait_event_interruptible(board->wait,
+                                         test_bit(AIF_WRITE_COMPLETE_BN,
+                                                  &a_priv->interrupt_flags) ||
+                                         test_bit(TIMO_NUM, &board->status));
+       if (retval) {
+               pr_err("%s: wait write complete interrupted\n", __func__);
+               agilent_82357a_abort(a_priv, 0);
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               return -ERESTARTSYS;
+       }
+
+       if (test_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags) == 0) {
+               GPIB_DPRINTK("%s: write timed out ibs %i, tmo %i\n", __func__,
+                            test_bit(TIMO_NUM, &board->status), msec_timeout);
+
+               agilent_82357a_abort(a_priv, 0);
+
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+
+               read_reg.address = BSR;
+               retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1);
+               if (retval) {
+                       pr_err("%s: agilent_82357a_read_registers() returned error\n",  __func__);
+                       return -ETIMEDOUT;
+               }
+
+               bsr = read_reg.value;
+               GPIB_DPRINTK("%s: write aborted bsr 0x%hx\n", __func__, bsr);
+
+               if (send_commands) {/* check for no listeners */
+                       if ((bsr & BSR_ATN_BIT) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) {
+                               GPIB_DPRINTK("%s: No listener on command\n", __func__);
+                               clear_bit(TIMO_NUM, &board->status);
+                               return -ENOTCONN; // no listener on bus
+                       }
+               } else {
+                       read_reg.address = ADSR;
+                       retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1);
+                       if (retval) {
+                               pr_err("%s: agilent_82357a_read_registers() returned error\n",
+                                      __func__);
+                               return -ETIMEDOUT;
+                       }
+                       adsr = read_reg.value;
+                       if ((adsr & HR_TA) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) {
+                               GPIB_DPRINTK("%s: No listener on write\n", __func__);
+                               clear_bit(TIMO_NUM, &board->status);
+                               return -ECOMM;
+                       }
+               }
+
+               return -ETIMEDOUT;
+       }
+
+       status_data = kmalloc(STATUS_DATA_LEN, GFP_KERNEL);
+       if (!status_data) {
+               mutex_unlock(&a_priv->bulk_transfer_lock);
+               return -ENOMEM;
+       }
+
+       // printk("%s: receiving control msg\n", __func__);
+       retval = agilent_82357a_receive_control_msg(a_priv, agilent_82357a_control_request,
+                                                   USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                                                   XFER_STATUS, 0, status_data, STATUS_DATA_LEN,
+                                                   100);
+       mutex_unlock(&a_priv->bulk_transfer_lock);
+       if (retval < 0) {
+               pr_err("%s: agilent_82357a_receive_control_msg() returned %i\n", __func__, retval);
+               kfree(status_data);
+               return -EIO;
+       }
+       *bytes_written  = status_data[2];
+       *bytes_written |= status_data[3] << 8;
+       *bytes_written |= status_data[4] << 16;
+       *bytes_written |= status_data[5] << 24;
+
+       kfree(status_data);
+       //printk("%s: write completed, bytes_written=%i\n", __func__, (int)*bytes_written);
+       return 0;
+}
+
+static int agilent_82357a_write(gpib_board_t *board, uint8_t *buffer, size_t length,
+                               int send_eoi, size_t *bytes_written)
+{
+       return agilent_82357a_generic_write(board, buffer, length, 0, send_eoi, bytes_written);
+}
+
+int agilent_82357a_command(gpib_board_t *board, uint8_t *buffer, size_t length,
+                          size_t *bytes_written)
+{
+       return agilent_82357a_generic_write(board, buffer, length, 1, 0, bytes_written);
+}
+
+int agilent_82357a_take_control_internal(gpib_board_t *board, int synchronous)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet write;
+       int retval;
+
+       write.address = AUXCR;
+       if (synchronous)
+               write.value = AUX_TCS;
+       else
+               write.value = AUX_TCA;
+       retval = agilent_82357a_write_registers(a_priv, &write, 1);
+       if (retval)
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+
+       return retval;
+}
+
+static int agilent_82357a_take_control(gpib_board_t *board, int synchronous)
+{
+       const int timeout = 10;
+       int i;
+
+/* It looks like the 9914 does not handle tcs properly.
+ *  See comment above tms9914_take_control_workaround() in
+ *  drivers/gpib/tms9914/tms9914_aux.c
+ */
+       if (synchronous)
+               return -ETIMEDOUT;
+
+       agilent_82357a_take_control_internal(board, synchronous);
+       // busy wait until ATN is asserted
+       for (i = 0; i < timeout; ++i) {
+               agilent_82357a_update_status(board, 0);
+               if (test_bit(ATN_NUM, &board->status))
+                       break;
+               udelay(1);
+       }
+       if (i == timeout)
+               return -ETIMEDOUT;
+       return 0;
+}
+
+static int agilent_82357a_go_to_standby(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet write;
+       int retval;
+
+       write.address = AUXCR;
+       write.value = AUX_GTS;
+       retval = agilent_82357a_write_registers(a_priv, &write, 1);
+       if (retval)
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+       return 0;
+}
+
+//FIXME should change prototype to return int
+static void agilent_82357a_request_system_control(gpib_board_t *board, int request_control)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet writes[2];
+       int retval;
+       int i = 0;
+
+       /* 82357B needs bit to be set in 9914 AUXCR register */
+       writes[i].address = AUXCR;
+       if (request_control) {
+               writes[i].value = AUX_RQC;
+               a_priv->hw_control_bits |= SYSTEM_CONTROLLER;
+       } else {
+               writes[i].value = AUX_RLC;
+               a_priv->is_cic = 0;
+               a_priv->hw_control_bits &= ~SYSTEM_CONTROLLER;
+       }
+       ++i;
+       writes[i].address = HW_CONTROL;
+       writes[i].value = a_priv->hw_control_bits;
+       ++i;
+       retval = agilent_82357a_write_registers(a_priv, writes, i);
+       if (retval)
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+       return;// retval;
+}
+
+static void agilent_82357a_interface_clear(gpib_board_t *board, int assert)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet write;
+       int retval;
+
+       write.address = AUXCR;
+       write.value = AUX_SIC;
+       if (assert) {
+               write.value |= AUX_CS;
+               a_priv->is_cic = 1;
+       }
+       retval = agilent_82357a_write_registers(a_priv, &write, 1);
+       if (retval)
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+}
+
+static void agilent_82357a_remote_enable(gpib_board_t *board, int enable)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet write;
+       int retval;
+
+       write.address = AUXCR;
+       write.value = AUX_SRE;
+       if (enable)
+               write.value |= AUX_CS;
+       retval = agilent_82357a_write_registers(a_priv, &write, 1);
+       if (retval)
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+       a_priv->ren_state = enable;
+       return;// 0;
+}
+
+static int agilent_82357a_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+
+       if (compare_8_bits == 0) {
+               pr_warn("%s: hardware only supports 8-bit EOS compare", __func__);
+               return -EOPNOTSUPP;
+       }
+       a_priv->eos_char = eos_byte;
+       a_priv->eos_mode = REOS | BIN;
+       return 0;
+}
+
+static void agilent_82357a_disable_eos(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+
+       a_priv->eos_mode &= ~REOS;
+}
+
+static unsigned int agilent_82357a_update_status(gpib_board_t *board, unsigned int clear_mask)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet address_status, bus_status;
+       int retval;
+
+       board->status &= ~clear_mask;
+       if (a_priv->is_cic)
+               set_bit(CIC_NUM, &board->status);
+       else
+               clear_bit(CIC_NUM, &board->status);
+       address_status.address = ADSR;
+       retval = agilent_82357a_read_registers(a_priv, &address_status, 1, 0);
+       if (retval) {
+               if (retval != -EAGAIN)
+                       pr_err("%s: agilent_82357a_read_registers() returned error\n", __func__);
+               return board->status;
+       }
+       // check for remote/local
+       if (address_status.value & HR_REM)
+               set_bit(REM_NUM, &board->status);
+       else
+               clear_bit(REM_NUM, &board->status);
+       // check for lockout
+       if (address_status.value & HR_LLO)
+               set_bit(LOK_NUM, &board->status);
+       else
+               clear_bit(LOK_NUM, &board->status);
+       // check for ATN
+       if (address_status.value & HR_ATN)
+               set_bit(ATN_NUM, &board->status);
+       else
+               clear_bit(ATN_NUM, &board->status);
+       // check for talker/listener addressed
+       if (address_status.value & HR_TA)
+               set_bit(TACS_NUM, &board->status);
+       else
+               clear_bit(TACS_NUM, &board->status);
+       if (address_status.value & HR_LA)
+               set_bit(LACS_NUM, &board->status);
+       else
+               clear_bit(LACS_NUM, &board->status);
+
+       bus_status.address = BSR;
+       retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0);
+       if (retval) {
+               if (retval != -EAGAIN)
+                       pr_err("%s: agilent_82357a_read_registers() returned error\n", __func__);
+               return board->status;
+       }
+       if (bus_status.value & BSR_SRQ_BIT)
+               set_bit(SRQI_NUM, &board->status);
+       else
+               clear_bit(SRQI_NUM, &board->status);
+
+       return board->status;
+}
+
+static int agilent_82357a_primary_address(gpib_board_t *board, unsigned int address)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet write;
+       int retval;
+
+       // put primary address in address0
+       write.address = ADR;
+       write.value = address & ADDRESS_MASK;
+       retval = agilent_82357a_write_registers(a_priv, &write, 1);
+       if (retval) {
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+               return retval;
+       }
+       return retval;
+}
+
+static int agilent_82357a_secondary_address(gpib_board_t *board, unsigned int address, int enable)
+{
+       if (enable)
+               pr_warn("%s: warning: assigning a secondary address not supported\n", __func__);
+       return  -EOPNOTSUPP;
+}
+
+static int agilent_82357a_parallel_poll(gpib_board_t *board, uint8_t *result)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet writes[2];
+       struct agilent_82357a_register_pairlet read;
+       int retval;
+
+       // execute parallel poll
+       writes[0].address = AUXCR;
+       writes[0].value = AUX_CS | AUX_RPP;
+       writes[1].address = HW_CONTROL;
+       writes[1].value = a_priv->hw_control_bits & ~NOT_PARALLEL_POLL;
+       retval = agilent_82357a_write_registers(a_priv, writes, 2);
+       if (retval) {
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+               return retval;
+       }
+       udelay(2);      //silly, since usb write will take way longer
+       read.address = CPTR;
+       retval = agilent_82357a_read_registers(a_priv, &read, 1, 1);
+       if (retval) {
+               pr_err("%s: agilent_82357a_read_registers() returned error\n", __func__);
+               return retval;
+       }
+       *result = read.value;
+       // clear parallel poll state
+       writes[0].address = HW_CONTROL;
+       writes[0].value = a_priv->hw_control_bits | NOT_PARALLEL_POLL;
+       writes[1].address = AUXCR;
+       writes[1].value = AUX_RPP;
+       retval = agilent_82357a_write_registers(a_priv, writes, 2);
+       if (retval) {
+               pr_err("%s: agilent_82357a_write_registers() returned error\n",  __func__);
+               return retval;
+       }
+       return 0;
+}
+
+static void agilent_82357a_parallel_poll_configure(gpib_board_t *board, uint8_t config)
+{
+       //board can only be system controller
+       return;// 0;
+}
+
+static void agilent_82357a_parallel_poll_response(gpib_board_t *board, int ist)
+{
+       //board can only be system controller
+       return;// 0;
+}
+
+static void agilent_82357a_serial_poll_response(gpib_board_t *board, uint8_t status)
+{
+       //board can only be system controller
+       return;// 0;
+}
+
+static uint8_t agilent_82357a_serial_poll_status(gpib_board_t *board)
+{
+       //board can only be system controller
+       return 0;
+}
+
+static void agilent_82357a_return_to_local(gpib_board_t *board)
+{
+       //board can only be system controller
+       return;// 0;
+}
+
+static int agilent_82357a_line_status(const gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet bus_status;
+       int retval;
+       int status = ValidALL;
+
+       bus_status.address = BSR;
+       retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0);
+       if (retval) {
+               if (retval != -EAGAIN)
+                       pr_err("%s: agilent_82357a_read_registers() returned error\n", __func__);
+               return retval;
+       }
+       if (bus_status.value & BSR_REN_BIT)
+               status |= BusREN;
+       if (bus_status.value & BSR_IFC_BIT)
+               status |= BusIFC;
+       if (bus_status.value & BSR_SRQ_BIT)
+               status |= BusSRQ;
+       if (bus_status.value & BSR_EOI_BIT)
+               status |= BusEOI;
+       if (bus_status.value & BSR_NRFD_BIT)
+               status |= BusNRFD;
+       if (bus_status.value & BSR_NDAC_BIT)
+               status |= BusNDAC;
+       if (bus_status.value & BSR_DAV_BIT)
+               status |= BusDAV;
+       if (bus_status.value & BSR_ATN_BIT)
+               status |= BusATN;
+       return status;
+}
+
+static unsigned short nanosec_to_fast_talker_bits(unsigned int *nanosec)
+{
+       static const int nanosec_per_bit = 21;
+       static const int max_value = 0x72;
+       static const int min_value = 0x11;
+       unsigned short bits;
+
+       bits = (*nanosec + nanosec_per_bit / 2) / nanosec_per_bit;
+       if (bits < min_value)
+               bits = min_value;
+       if (bits > max_value)
+               bits = max_value;
+       *nanosec = bits * nanosec_per_bit;
+       return bits;
+}
+
+static unsigned int agilent_82357a_t1_delay(gpib_board_t *board, unsigned int nanosec)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet write;
+       int retval;
+
+       write.address = FAST_TALKER_T1;
+       write.value = nanosec_to_fast_talker_bits(&nanosec);
+       retval = agilent_82357a_write_registers(a_priv, &write, 1);
+       if (retval)
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+       return nanosec;
+}
+
+static void agilent_82357a_interrupt_complete(struct urb *urb)
+{
+       gpib_board_t *board = urb->context;
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       int retval;
+       u8 *transfer_buffer = urb->transfer_buffer;
+       unsigned long interrupt_flags;
+
+       switch (urb->status) {
+               /* success */
+       case 0:
+               break;
+               /* unlinked, don't resubmit */
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               return;
+       default: /* other error, resubmit */
+               retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC);
+               if (retval)
+                       pr_err("%s: failed to resubmit interrupt urb\n", __func__);
+               return;
+       }
+
+       interrupt_flags = transfer_buffer[0];
+       if (test_bit(AIF_READ_COMPLETE_BN, &interrupt_flags))
+               set_bit(AIF_READ_COMPLETE_BN, &a_priv->interrupt_flags);
+       if (test_bit(AIF_WRITE_COMPLETE_BN, &interrupt_flags))
+               set_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags);
+       if (test_bit(AIF_SRQ_BN, &interrupt_flags))
+               set_bit(SRQI_NUM, &board->status);
+
+       wake_up_interruptible(&board->wait);
+
+       retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC);
+       if (retval)
+               pr_err("%s: failed to resubmit interrupt urb\n", __func__);
+}
+
+static int agilent_82357a_setup_urbs(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct usb_device *usb_dev;
+       int int_pipe;
+       int retval;
+
+       retval = mutex_lock_interruptible(&a_priv->interrupt_alloc_lock);
+       if (retval)
+               return retval;
+       if (!a_priv->bus_interface) {
+               retval = -ENODEV;
+               goto setup_exit;
+       }
+
+       a_priv->interrupt_buffer = kmalloc(INTERRUPT_BUF_LEN, GFP_KERNEL);
+       if (!a_priv->interrupt_buffer) {
+               retval = -ENOMEM;
+               goto setup_exit;
+       }
+       a_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!a_priv->interrupt_urb) {
+               retval = -ENOMEM;
+               goto setup_exit;
+       }
+       usb_dev = interface_to_usbdev(a_priv->bus_interface);
+       int_pipe = usb_rcvintpipe(usb_dev, a_priv->interrupt_in_endpoint);
+       usb_fill_int_urb(a_priv->interrupt_urb, usb_dev, int_pipe, a_priv->interrupt_buffer,
+                        INTERRUPT_BUF_LEN, &agilent_82357a_interrupt_complete, board, 1);
+       retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL);
+       if (retval) {
+               usb_free_urb(a_priv->interrupt_urb);
+               a_priv->interrupt_urb = NULL;
+               pr_err("%s: failed to submit first interrupt urb, retval=%i\n", __func__, retval);
+               goto setup_exit;
+       }
+       mutex_unlock(&a_priv->interrupt_alloc_lock);
+       return 0;
+
+setup_exit:
+       kfree(a_priv->interrupt_buffer);
+       mutex_unlock(&a_priv->interrupt_alloc_lock);
+       return retval;
+}
+
+#ifdef RESET_USB_CONFIG
+static int agilent_82357a_reset_usb_configuration(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct usb_device *usb_dev;
+       int retval;
+
+       if (!a_priv->bus_interface)
+               return -ENODEV;
+       usb_dev = interface_to_usbdev(a_priv->bus_interface);
+       retval = usb_reset_configuration(usb_dev);
+       if (retval)
+               pr_err("%s: usb_reset_configuration() returned %i\n", __func__, retval);
+       return retval;
+}
+#endif
+
+static void agilent_82357a_cleanup_urbs(struct agilent_82357a_priv *a_priv)
+{
+       if (a_priv && a_priv->bus_interface) {
+               if (a_priv->interrupt_urb)
+                       usb_kill_urb(a_priv->interrupt_urb);
+               if (a_priv->bulk_urb)
+                       usb_kill_urb(a_priv->bulk_urb);
+       }
+};
+
+static int agilent_82357a_allocate_private(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv;
+
+       board->private_data = kmalloc(sizeof(struct agilent_82357a_priv), GFP_KERNEL);
+       if (!board->private_data)
+               return -ENOMEM;
+       a_priv = board->private_data;
+       memset(a_priv, 0, sizeof(struct agilent_82357a_priv));
+       mutex_init(&a_priv->bulk_transfer_lock);
+       mutex_init(&a_priv->bulk_alloc_lock);
+       mutex_init(&a_priv->control_alloc_lock);
+       mutex_init(&a_priv->interrupt_alloc_lock);
+       return 0;
+}
+
+static void agilent_82357a_free_private(struct agilent_82357a_priv *a_priv)
+{
+       usb_free_urb(a_priv->interrupt_urb);
+       kfree(a_priv->interrupt_buffer);
+       kfree(a_priv);
+}
+
+static int agilent_82357a_init(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet hw_control;
+       struct agilent_82357a_register_pairlet writes[0x20];
+       int retval;
+       int i;
+       unsigned int nanosec;
+
+       i = 0;
+       writes[i].address = LED_CONTROL;
+       writes[i].value = FAIL_LED_ON;
+       ++i;
+       writes[i].address = RESET_TO_POWERUP;
+       writes[i].value = RESET_SPACEBALL;
+       ++i;
+       retval = agilent_82357a_write_registers(a_priv, writes, i);
+       if (retval) {
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+               return -EIO;
+       }
+       set_current_state(TASK_INTERRUPTIBLE);
+       if (schedule_timeout(usec_to_jiffies(2000)))
+               return -ERESTARTSYS;
+       i = 0;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_NBAF;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_HLDE;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_TON;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_LON;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_RSV2;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_INVAL;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_RPP;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_STDL;
+       ++i;
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_VSTDL;
+       ++i;
+       writes[i].address = FAST_TALKER_T1;
+       nanosec = board->t1_nano_sec;
+       writes[i].value = nanosec_to_fast_talker_bits(&nanosec);
+       board->t1_nano_sec = nanosec;
+       ++i;
+       writes[i].address = ADR;
+       writes[i].value = board->pad & ADDRESS_MASK;
+       ++i;
+       writes[i].address = PPR;
+       writes[i].value = 0;
+       ++i;
+       writes[i].address = SPMR;
+       writes[i].value = 0;
+       ++i;
+       writes[i].address = PROTOCOL_CONTROL;
+       writes[i].value = WRITE_COMPLETE_INTERRUPT_EN;
+       ++i;
+       writes[i].address = IMR0;
+       writes[i].value = HR_BOIE | HR_BIIE;
+       ++i;
+       writes[i].address = IMR1;
+       writes[i].value = HR_SRQIE;
+       ++i;
+       // turn off reset state
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_CHIP_RESET;
+       ++i;
+       writes[i].address = LED_CONTROL;
+       writes[i].value = FIRMWARE_LED_CONTROL;
+       ++i;
+       if (i > ARRAY_SIZE(writes)) {
+               pr_err("%s: bug! writes[] overflow\n", __func__);
+               return -EFAULT;
+       }
+       retval = agilent_82357a_write_registers(a_priv, writes, i);
+       if (retval) {
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+               return -EIO;
+       }
+       hw_control.address = HW_CONTROL;
+       retval = agilent_82357a_read_registers(a_priv, &hw_control, 1, 1);
+       if (retval) {
+               pr_err("%s: agilent_82357a_read_registers() returned error\n", __func__);
+               return -EIO;
+       }
+       a_priv->hw_control_bits = (hw_control.value & ~0x7) | NOT_TI_RESET | NOT_PARALLEL_POLL;
+
+       return 0;
+}
+
+static inline int agilent_82357a_device_match(struct usb_interface *interface,
+                                             const gpib_board_config_t *config)
+{
+       struct usb_device * const usbdev = interface_to_usbdev(interface);
+
+       if (gpib_match_device_path(&interface->dev, config->device_path) == 0)
+               return 0;
+       if (config->serial_number &&
+           strcmp(usbdev->serial, config->serial_number) != 0)
+               return 0;
+
+       return 1;
+}
+
+static int agilent_82357a_attach(gpib_board_t *board, const gpib_board_config_t *config)
+{
+       int retval;
+       int i;
+       unsigned int product_id;
+       struct agilent_82357a_priv *a_priv;
+       struct usb_device *usb_dev;
+
+       if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock))
+               return -ERESTARTSYS;
+
+       retval = agilent_82357a_allocate_private(board);
+       if (retval < 0) {
+               mutex_unlock(&agilent_82357a_hotplug_lock);
+               return retval;
+       }
+       a_priv = board->private_data;
+       for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+               if (agilent_82357a_driver_interfaces[i] &&
+                   !usb_get_intfdata(agilent_82357a_driver_interfaces[i]) &&
+                   agilent_82357a_device_match(agilent_82357a_driver_interfaces[i], config)) {
+                       a_priv->bus_interface = agilent_82357a_driver_interfaces[i];
+                       usb_set_intfdata(agilent_82357a_driver_interfaces[i], board);
+                       usb_dev = interface_to_usbdev(a_priv->bus_interface);
+                       dev_info(&usb_dev->dev,
+                                "bus %d dev num %d attached to gpib minor %d, agilent usb interface %i\n",
+                                usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
+                       break;
+               }
+       }
+       if (i == MAX_NUM_82357A_INTERFACES) {
+               mutex_unlock(&agilent_82357a_hotplug_lock);
+               pr_err("No Agilent 82357 gpib adapters found, have you loaded its firmware?\n");
+               return -ENODEV;
+       }
+       product_id = le16_to_cpu(interface_to_usbdev(a_priv->bus_interface)->descriptor.idProduct);
+       switch (product_id) {
+       case USB_DEVICE_ID_AGILENT_82357A:
+               a_priv->bulk_out_endpoint = AGILENT_82357A_BULK_OUT_ENDPOINT;
+               a_priv->interrupt_in_endpoint = AGILENT_82357A_INTERRUPT_IN_ENDPOINT;
+               break;
+       case USB_DEVICE_ID_AGILENT_82357B:
+               a_priv->bulk_out_endpoint = AGILENT_82357B_BULK_OUT_ENDPOINT;
+               a_priv->interrupt_in_endpoint = AGILENT_82357B_INTERRUPT_IN_ENDPOINT;
+               break;
+       default:
+               pr_err("bug, unhandled product_id in switch?\n");
+               return -EIO;
+       }
+#ifdef RESET_USB_CONFIG
+       retval = agilent_82357a_reset_usb_configuration(board);
+       if (retval < 0) {
+               mutex_unlock(&agilent_82357a_hotplug_lock);
+               return retval;
+       }
+#endif
+       retval = agilent_82357a_setup_urbs(board);
+       if (retval < 0) {
+               mutex_unlock(&agilent_82357a_hotplug_lock);
+               return retval;
+       }
+
+       timer_setup(&a_priv->bulk_timer, agilent_82357a_timeout_handler, 0);
+
+       board->t1_nano_sec = 800;
+
+       retval = agilent_82357a_init(board);
+
+       if (retval < 0) {
+               mutex_unlock(&agilent_82357a_hotplug_lock);
+               return retval;
+       }
+
+       pr_info("%s: attached\n", __func__);
+       mutex_unlock(&agilent_82357a_hotplug_lock);
+       return retval;
+}
+
+static int agilent_82357a_go_idle(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv = board->private_data;
+       struct agilent_82357a_register_pairlet writes[0x20];
+       int retval;
+       int i;
+
+       i = 0;
+       // turn on tms9914 reset state
+       writes[i].address = AUXCR;
+       writes[i].value = AUX_CS | AUX_CHIP_RESET;
+       ++i;
+       a_priv->hw_control_bits &= ~NOT_TI_RESET;
+       writes[i].address = HW_CONTROL;
+       writes[i].value = a_priv->hw_control_bits;
+       ++i;
+       writes[i].address = PROTOCOL_CONTROL;
+       writes[i].value = 0;
+       ++i;
+       writes[i].address = IMR0;
+       writes[i].value = 0;
+       ++i;
+       writes[i].address = IMR1;
+       writes[i].value = 0;
+       ++i;
+       writes[i].address = LED_CONTROL;
+       writes[i].value = 0;
+       ++i;
+       if (i > ARRAY_SIZE(writes)) {
+               pr_err("%s: bug! writes[] overflow\n", __func__);
+               return -EFAULT;
+       }
+       retval = agilent_82357a_write_registers(a_priv, writes, i);
+       if (retval) {
+               pr_err("%s: agilent_82357a_write_registers() returned error\n", __func__);
+               return -EIO;
+       }
+       return 0;
+}
+
+static void agilent_82357a_detach(gpib_board_t *board)
+{
+       struct agilent_82357a_priv *a_priv;
+
+       mutex_lock(&agilent_82357a_hotplug_lock);
+
+       a_priv = board->private_data;
+       if (a_priv) {
+               if (a_priv->bus_interface) {
+                       agilent_82357a_go_idle(board);
+                       usb_set_intfdata(a_priv->bus_interface, NULL);
+               }
+               mutex_lock(&a_priv->control_alloc_lock);
+               mutex_lock(&a_priv->bulk_alloc_lock);
+               mutex_lock(&a_priv->interrupt_alloc_lock);
+               agilent_82357a_cleanup_urbs(a_priv);
+               agilent_82357a_free_private(a_priv);
+       }
+       pr_info("%s: detached\n", __func__);
+       mutex_unlock(&agilent_82357a_hotplug_lock);
+}
+
+gpib_interface_t agilent_82357a_gpib_interface = {
+name: "agilent_82357a",
+attach : agilent_82357a_attach,
+detach : agilent_82357a_detach,
+read : agilent_82357a_read,
+write : agilent_82357a_write,
+command : agilent_82357a_command,
+take_control : agilent_82357a_take_control,
+go_to_standby : agilent_82357a_go_to_standby,
+request_system_control : agilent_82357a_request_system_control,
+interface_clear : agilent_82357a_interface_clear,
+remote_enable : agilent_82357a_remote_enable,
+enable_eos : agilent_82357a_enable_eos,
+disable_eos : agilent_82357a_disable_eos,
+parallel_poll : agilent_82357a_parallel_poll,
+parallel_poll_configure : agilent_82357a_parallel_poll_configure,
+parallel_poll_response : agilent_82357a_parallel_poll_response,
+local_parallel_poll_mode : NULL, // XXX
+line_status : agilent_82357a_line_status,
+update_status : agilent_82357a_update_status,
+primary_address : agilent_82357a_primary_address,
+secondary_address : agilent_82357a_secondary_address,
+serial_poll_response : agilent_82357a_serial_poll_response,
+serial_poll_status : agilent_82357a_serial_poll_status,
+t1_delay : agilent_82357a_t1_delay,
+return_to_local : agilent_82357a_return_to_local,
+no_7_bit_eos : 1,
+skip_check_for_command_acceptors : 1
+};
+
+// Table with the USB-devices: just now only testing IDs
+static struct usb_device_id agilent_82357a_driver_device_table[] = {
+       {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357A)},
+       {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357B)},
+       {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, agilent_82357a_driver_device_table);
+
+static int agilent_82357a_driver_probe(struct usb_interface *interface,
+                                      const struct usb_device_id *id)
+{
+       int i;
+       char *path;
+       static const int path_length = 1024;
+
+       if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock))
+               return -ERESTARTSYS;
+       usb_get_dev(interface_to_usbdev(interface));
+       for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+               if (!agilent_82357a_driver_interfaces[i]) {
+                       agilent_82357a_driver_interfaces[i] = interface;
+                       usb_set_intfdata(interface, NULL);
+                       GPIB_DPRINTK("set bus interface %i to address 0x%p\n", i, interface);
+                       break;
+               }
+       }
+       if (i == MAX_NUM_82357A_INTERFACES) {
+               usb_put_dev(interface_to_usbdev(interface));
+               mutex_unlock(&agilent_82357a_hotplug_lock);
+               pr_err("%s: out of space in agilent_82357a_driver_interfaces[]\n", __func__);
+               return -1;
+       }
+       path = kmalloc(path_length, GFP_KERNEL);
+       if (!path) {
+               usb_put_dev(interface_to_usbdev(interface));
+               mutex_unlock(&agilent_82357a_hotplug_lock);
+               return -ENOMEM;
+       }
+       usb_make_path(interface_to_usbdev(interface), path, path_length);
+       pr_info("probe succeeded for path: %s\n", path);
+       kfree(path);
+       mutex_unlock(&agilent_82357a_hotplug_lock);
+       return 0;
+}
+
+static void agilent_82357a_driver_disconnect(struct usb_interface *interface)
+{
+       int i;
+
+       mutex_lock(&agilent_82357a_hotplug_lock);
+
+       for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+               if (agilent_82357a_driver_interfaces[i] == interface) {
+                       gpib_board_t *board = usb_get_intfdata(interface);
+
+                       if (board) {
+                               struct agilent_82357a_priv *a_priv = board->private_data;
+
+                               if (a_priv) {
+                                       mutex_lock(&a_priv->control_alloc_lock);
+                                       mutex_lock(&a_priv->bulk_alloc_lock);
+                                       mutex_lock(&a_priv->interrupt_alloc_lock);
+                                       agilent_82357a_cleanup_urbs(a_priv);
+                                       a_priv->bus_interface = NULL;
+                                       mutex_unlock(&a_priv->interrupt_alloc_lock);
+                                       mutex_unlock(&a_priv->bulk_alloc_lock);
+                                       mutex_unlock(&a_priv->control_alloc_lock);
+                               }
+                       }
+                       GPIB_DPRINTK("nulled agilent_82357a_driver_interfaces[%i]\n", i);
+                       agilent_82357a_driver_interfaces[i] = NULL;
+                       break;
+               }
+       }
+       if (i == MAX_NUM_82357A_INTERFACES)
+               pr_err("unable to find interface in agilent_82357a_driver_interfaces[]? bug?\n");
+       usb_put_dev(interface_to_usbdev(interface));
+
+       mutex_unlock(&agilent_82357a_hotplug_lock);
+}
+
+static int agilent_82357a_driver_suspend(struct usb_interface *interface, pm_message_t message)
+{
+       struct usb_device *usb_dev;
+       int i, retval;
+
+       mutex_lock(&agilent_82357a_hotplug_lock);
+
+       for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+               if (agilent_82357a_driver_interfaces[i] == interface)   {
+                       gpib_board_t *board = usb_get_intfdata(interface);
+
+                       if (board) {
+                               struct agilent_82357a_priv *a_priv = board->private_data;
+
+                               if (a_priv) {
+                                       agilent_82357a_abort(a_priv, 0);
+                                       agilent_82357a_abort(a_priv, 0);
+                                       retval = agilent_82357a_go_idle(board);
+                                       if (retval) {
+                                               pr_err("%s: failed to go idle, retval=%i\n",
+                                                      __func__, retval);
+                                               mutex_unlock(&agilent_82357a_hotplug_lock);
+                                               return retval;
+                                       }
+                                       mutex_lock(&a_priv->interrupt_alloc_lock);
+                                       agilent_82357a_cleanup_urbs(a_priv);
+                                       mutex_unlock(&a_priv->interrupt_alloc_lock);
+                                       usb_dev = interface_to_usbdev(a_priv->bus_interface);
+                                       dev_info(&usb_dev->dev,
+                                                "bus %d dev num %d  gpib minor %d, agilent usb interface %i suspended\n",
+                                                usb_dev->bus->busnum, usb_dev->devnum,
+                                                board->minor, i);
+                               }
+                       }
+                       break;
+               }
+       }
+
+       mutex_unlock(&agilent_82357a_hotplug_lock);
+
+       return 0;
+}
+
+static int agilent_82357a_driver_resume(struct usb_interface *interface)
+{
+       struct usb_device *usb_dev;
+       gpib_board_t *board;
+       int i, retval;
+
+       mutex_lock(&agilent_82357a_hotplug_lock);
+
+       for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+               if (agilent_82357a_driver_interfaces[i] == interface) {
+                       board = usb_get_intfdata(interface);
+                       if (board)
+                               break;
+               }
+       }
+       if (i == MAX_NUM_82357A_INTERFACES)
+               goto resume_exit;
+
+       struct agilent_82357a_priv *a_priv = board->private_data;
+
+       if (a_priv) {
+               if (a_priv->interrupt_urb) {
+                       mutex_lock(&a_priv->interrupt_alloc_lock);
+                       retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL);
+                       if (retval) {
+                               pr_err("%s: failed to resubmit interrupt urb, retval=%i\n",
+                                      __func__, retval);
+                               mutex_unlock(&a_priv->interrupt_alloc_lock);
+                               mutex_unlock(&agilent_82357a_hotplug_lock);
+                               return retval;
+                       }
+                       mutex_unlock(&a_priv->interrupt_alloc_lock);
+               }
+               retval = agilent_82357a_init(board);
+               if (retval < 0) {
+                       mutex_unlock(&agilent_82357a_hotplug_lock);
+                       return retval;
+               }
+               // set/unset system controller
+               agilent_82357a_request_system_control(board, board->master);
+               // toggle ifc if master
+               if (board->master) {
+                       agilent_82357a_interface_clear(board, 1);
+                       usleep_range(200, 250);
+                       agilent_82357a_interface_clear(board, 0);
+               }
+               // assert/unassert REN
+               agilent_82357a_remote_enable(board, a_priv->ren_state);
+
+               usb_dev = interface_to_usbdev(a_priv->bus_interface);
+               dev_info(&usb_dev->dev,
+                        "bus %d dev num %d  gpib minor %d, agilent usb interface %i resumed\n",
+                        usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
+       }
+
+resume_exit:
+       mutex_unlock(&agilent_82357a_hotplug_lock);
+
+       return 0;
+}
+
+static struct usb_driver agilent_82357a_bus_driver = {
+       .name = "agilent_82357a_gpib",
+       .probe = agilent_82357a_driver_probe,
+       .disconnect = agilent_82357a_driver_disconnect,
+       .suspend = agilent_82357a_driver_suspend,
+       .resume = agilent_82357a_driver_resume,
+       .id_table = agilent_82357a_driver_device_table,
+};
+
+static int __init agilent_82357a_init_module(void)
+{
+       int i;
+
+       pr_info("agilent_82357a_gpib driver loading");
+       for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i)
+               agilent_82357a_driver_interfaces[i] = NULL;
+       usb_register(&agilent_82357a_bus_driver);
+       gpib_register_driver(&agilent_82357a_gpib_interface, THIS_MODULE);
+
+       return 0;
+}
+
+static void __exit agilent_82357a_exit_module(void)
+{
+       pr_info("agilent_82357a_gpib driver unloading");
+       gpib_unregister_driver(&agilent_82357a_gpib_interface);
+       usb_deregister(&agilent_82357a_bus_driver);
+}
+
+module_init(agilent_82357a_init_module);
+module_exit(agilent_82357a_exit_module);
+
diff --git a/drivers/staging/gpib/agilent_82357a/agilent_82357a.h b/drivers/staging/gpib/agilent_82357a/agilent_82357a.h
new file mode 100644 (file)
index 0000000..cdbc3ec
--- /dev/null
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ *   copyright            : (C) 2004 by Frank Mori Hess                    *
+ ***************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
+#include <linux/compiler_attributes.h>
+#include "gpibP.h"
+#include "tms9914.h"
+
+enum usb_vendor_ids {
+       USB_VENDOR_ID_AGILENT = 0x0957
+};
+
+enum usb_device_ids {
+       USB_DEVICE_ID_AGILENT_82357A = 0x0107,
+       USB_DEVICE_ID_AGILENT_82357A_PREINIT = 0x0007,  // device id before firmware is loaded
+       USB_DEVICE_ID_AGILENT_82357B = 0x0718,  // device id before firmware is loaded
+       USB_DEVICE_ID_AGILENT_82357B_PREINIT = 0x0518,  // device id before firmware is loaded
+};
+
+enum endpoint_addresses {
+       AGILENT_82357_CONTROL_ENDPOINT = 0x0,
+       AGILENT_82357_BULK_IN_ENDPOINT = 0x2,
+       AGILENT_82357A_BULK_OUT_ENDPOINT = 0x4,
+       AGILENT_82357A_INTERRUPT_IN_ENDPOINT = 0x6,
+       AGILENT_82357B_BULK_OUT_ENDPOINT = 0x6,
+       AGILENT_82357B_INTERRUPT_IN_ENDPOINT = 0x8,
+};
+
+enum bulk_commands {
+       DATA_PIPE_CMD_WRITE = 0x1,
+       DATA_PIPE_CMD_READ = 0x3,
+       DATA_PIPE_CMD_WR_REGS = 0x4,
+       DATA_PIPE_CMD_RD_REGS = 0x5
+};
+
+enum agilent_82357a_read_flags {
+       ARF_END_ON_EOI = 0x1,
+       ARF_NO_ADDRESS = 0x2,
+       ARF_END_ON_EOS_CHAR = 0x4,
+       ARF_SPOLL = 0x8
+};
+
+enum agilent_82357a_trailing_read_flags {
+       ATRF_EOI = 0x1,
+       ATRF_ATN = 0x2,
+       ATRF_IFC = 0x4,
+       ATRF_EOS = 0x8,
+       ATRF_ABORT = 0x10,
+       ATRF_COUNT = 0x20,
+       ATRF_DEAD_BUS = 0x40,
+       ATRF_UNADDRESSED = 0x80
+};
+
+enum agilent_82357a_write_flags {
+       AWF_SEND_EOI = 0x1,
+       AWF_NO_FAST_TALKER_FIRST_BYTE = 0x2,
+       AWF_NO_FAST_TALKER = 0x4,
+       AWF_NO_ADDRESS = 0x8,
+       AWF_ATN = 0x10,
+       AWF_SEPARATE_HEADER = 0x80
+};
+
+enum agilent_82357a_interrupt_flag_bit_numbers {
+       AIF_SRQ_BN = 0,
+       AIF_WRITE_COMPLETE_BN = 1,
+       AIF_READ_COMPLETE_BN = 2,
+};
+
+enum agilent_82357_error_codes {
+       UGP_SUCCESS = 0,
+       UGP_ERR_INVALID_CMD = 1,
+       UGP_ERR_INVALID_PARAM = 2,
+       UGP_ERR_INVALID_REG = 3,
+       UGP_ERR_GPIB_READ = 4,
+       UGP_ERR_GPIB_WRITE = 5,
+       UGP_ERR_FLUSHING = 6,
+       UGP_ERR_FLUSHING_ALREADY = 7,
+       UGP_ERR_UNSUPPORTED = 8,
+       UGP_ERR_OTHER  = 9
+};
+
+enum agilent_82357_control_values {
+       XFER_ABORT = 0xa0,
+       XFER_STATUS = 0xb0,
+};
+
+enum xfer_status_bits {
+       XS_COMPLETED = 0x1,
+       XS_READ = 0x2,
+};
+
+enum xfer_status_completion_bits {
+       XSC_EOI = 0x1,
+       XSC_ATN = 0x2,
+       XSC_IFC = 0x4,
+       XSC_EOS = 0x8,
+       XSC_ABORT = 0x10,
+       XSC_COUNT = 0x20,
+       XSC_DEAD_BUS = 0x40,
+       XSC_BUS_NOT_ADDRESSED = 0x80
+};
+
+enum xfer_abort_type {
+       XA_FLUSH = 0x1
+};
+
+#define STATUS_DATA_LEN 8
+#define INTERRUPT_BUF_LEN 8
+
+struct agilent_82357a_urb_ctx {
+       struct semaphore complete;
+       unsigned timed_out : 1;
+};
+
+// struct which defines local data for each 82357 device
+struct agilent_82357a_priv {
+       struct usb_interface *bus_interface;
+       unsigned short eos_char;
+       unsigned short eos_mode;
+       unsigned short hw_control_bits;
+       unsigned long interrupt_flags;
+       struct urb *bulk_urb;
+       struct urb *interrupt_urb;
+       u8 *interrupt_buffer;
+       struct mutex bulk_transfer_lock; // bulk transfer lock
+       struct mutex bulk_alloc_lock;    // bulk transfer allocation lock
+       struct mutex interrupt_alloc_lock; // interrupt allocation lock
+       struct mutex control_alloc_lock;   // control message allocation lock
+       struct timer_list bulk_timer;
+       struct agilent_82357a_urb_ctx context;
+       unsigned int bulk_out_endpoint;
+       unsigned int interrupt_in_endpoint;
+       unsigned is_cic : 1;
+       unsigned ren_state : 1;
+};
+
+struct agilent_82357a_register_pairlet {
+       short address;
+       unsigned short value;
+};
+
+enum firmware_registers {
+       HW_CONTROL = 0xa,
+       LED_CONTROL = 0xb,
+       RESET_TO_POWERUP = 0xc,
+       PROTOCOL_CONTROL = 0xd,
+       FAST_TALKER_T1 = 0xe
+};
+
+enum hardware_control_bits {
+       NOT_TI_RESET = 0x1,
+       SYSTEM_CONTROLLER = 0x2,
+       NOT_PARALLEL_POLL = 0x4,
+       OSCILLATOR_5V_ON = 0x8,
+       OUTPUT_5V_ON = 0x20,
+       CPLD_3V_ON = 0x80,
+};
+
+enum led_control_bits {
+       FIRMWARE_LED_CONTROL = 0x1,
+       FAIL_LED_ON = 0x20,
+       READY_LED_ON = 0x40,
+       ACCESS_LED_ON = 0x80
+};
+
+enum reset_to_powerup_bits {
+       RESET_SPACEBALL = 0x1,  // wait 2 millisec after sending
+};
+
+enum protocol_control_bits {
+       WRITE_COMPLETE_INTERRUPT_EN = 0x1,
+};
+
+static const int agilent_82357a_control_request = 0x4;
+