* [PATCH v2] gpib: agilent_82357a: Support some 82357B clones
@ 2026-09-02 22:41 Tom Keller
2026-09-03 14:51 ` Dave Penkler
0 siblings, 1 reply; 2+ messages in thread
From: Tom Keller @ 2026-09-02 22:41 UTC (permalink / raw)
To: Dave Penkler, Greg Kroah-Hartman; +Cc: linux-kernel, Tom Keller
Some adapters marked as Agilent/Keysight 82357B are clones that
contain their own firmware. They do not support XFER_STATUS and need
slightly different initialization. Detect clone adapters that fail a
firmware load and work around their quirks.
Signed-off-by: Tom Keller <tom@tompkel.net>
---
Changes since v1:
- Keep the exiting init sequence, add a small increase to FAST_TALKER_T1
on clones.
- Clear AWF_NO_FAST_TALKER_FIRST_BYTE on command writes for all
adapters.
drivers/gpib/agilent_82357a/agilent_82357a.c | 43 +++++++++++++++++++-
drivers/gpib/agilent_82357a/agilent_82357a.h | 2 +
2 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c
index 2468a471d..a165f40ac 100644
--- a/drivers/gpib/agilent_82357a/agilent_82357a.c
+++ b/drivers/gpib/agilent_82357a/agilent_82357a.c
@@ -567,7 +567,7 @@ static ssize_t agilent_82357a_generic_write(struct gpib_board *board,
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;
+ out_data[i] = AWF_ATN | AWF_NO_ADDRESS | AWF_NO_FAST_TALKER;
if (send_eoi)
out_data[i] |= AWF_SEND_EOI;
++i;
@@ -648,10 +648,19 @@ static ssize_t agilent_82357a_generic_write(struct gpib_board *board,
return -ECOMM;
}
}
-
return -ETIMEDOUT;
}
+ /*
+ * Clone adapters do not answer to XFER_STATUS, so use the
+ * bytes_written from the write complete interrupt.
+ */
+ if (a_priv->is_clone_82357b) {
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ *bytes_written = a_priv->write_complete_count;
+ return 0;
+ }
+
status_data = kmalloc(STATUS_DATA_LEN, GFP_KERNEL);
if (!status_data) {
mutex_unlock(&a_priv->bulk_transfer_lock);
@@ -1114,6 +1123,12 @@ static void agilent_82357a_interrupt_complete(struct urb *urb)
}
interrupt_flags = transfer_buffer[0];
+ if (urb->actual_length >= 6 && test_bit(AIF_WRITE_COMPLETE_BN, &interrupt_flags)) {
+ a_priv->write_complete_count = (u32)transfer_buffer[2];
+ a_priv->write_complete_count |= (u32)transfer_buffer[3] << 8;
+ a_priv->write_complete_count |= (u32)transfer_buffer[4] << 16;
+ a_priv->write_complete_count |= (u32)transfer_buffer[5] << 24;
+ }
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))
@@ -1213,6 +1228,26 @@ static void agilent_82357a_free_private(struct gpib_board *board)
board->private_data = NULL;
}
+static void agilent_82357a_detect_clone(struct agilent_82357a_priv *a_priv)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ u8 *ret_data;
+ int retval;
+
+ ret_data = kmalloc(1, GFP_KERNEL);
+ if (!ret_data)
+ return;
+ retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0xA0,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0xE600, 0, ret_data, 1, 100);
+ kfree(ret_data);
+ if (retval < 0) {
+ a_priv->is_clone_82357b = 1;
+ dev_info(&usb_dev->dev, "Clone 82357B detected (firmware load returned %i), using vendor quirks\n",
+ retval);
+ }
+}
+
#define INIT_NUM_REG_WRITES 18
static int agilent_82357a_init(struct gpib_board *board)
{
@@ -1223,6 +1258,9 @@ static int agilent_82357a_init(struct gpib_board *board)
int retval;
unsigned int nanosec;
+ if (a_priv->is_clone_82357b)
+ board->t1_nano_sec = 819;
+
writes[0].address = LED_CONTROL;
writes[0].value = FAIL_LED_ON;
writes[1].address = RESET_TO_POWERUP;
@@ -1346,6 +1384,7 @@ static int agilent_82357a_attach(struct gpib_board *board, const struct gpib_boa
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;
+ agilent_82357a_detect_clone(a_priv);
break;
default:
dev_err(&usb_dev->dev, "bug, unhandled product_id in switch?\n");
diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.h b/drivers/gpib/agilent_82357a/agilent_82357a.h
index 33ac558e5..201316344 100644
--- a/drivers/gpib/agilent_82357a/agilent_82357a.h
+++ b/drivers/gpib/agilent_82357a/agilent_82357a.h
@@ -133,12 +133,14 @@ struct agilent_82357a_priv {
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
+ u32 write_complete_count; // bytes transferred in last write
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;
+ unsigned is_clone_82357b : 1;
};
struct agilent_82357a_register_pairlet {
--
2.55.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH v2] gpib: agilent_82357a: Support some 82357B clones
2026-09-02 22:41 [PATCH v2] gpib: agilent_82357a: Support some 82357B clones Tom Keller
@ 2026-09-03 14:51 ` Dave Penkler
0 siblings, 0 replies; 2+ messages in thread
From: Dave Penkler @ 2026-09-03 14:51 UTC (permalink / raw)
To: Tom Keller; +Cc: Greg Kroah-Hartman, linux-kernel
On Wed, Sep 02, 2026 at 10:41:16PM +0000, Tom Keller wrote:
> Some adapters marked as Agilent/Keysight 82357B are clones that
> contain their own firmware. They do not support XFER_STATUS and need
> slightly different initialization. Detect clone adapters that fail a
> firmware load and work around their quirks.
>
> Signed-off-by: Tom Keller <tom@tompkel.net>
> ---
> Changes since v1:
> - Keep the exiting init sequence, add a small increase to FAST_TALKER_T1
> on clones.
> - Clear AWF_NO_FAST_TALKER_FIRST_BYTE on command writes for all
> adapters.
Great, I tested this patch with my 2 82357B compatible
adaptors, one which needs a firmware download and the other not and it
worked fine. The Beiming adaptor, which does not need a firmware
download, is not detected as a "clone" so the shipped code is still
being used for it. I also tested systematically setting the
FAST_TALKER_T1 to 819 and that works too. So we can drop the clone
specific initialisation and just change the 800 ns value to 819. There
is no visible change in userspace and the extra 21 ns should not have
any noticeable impact. Detecting the "clone" as you propose is
preferable to a module option since from the outside and userspace we
can't really distinguish your clone from a genuine or other clone
adaptor.
> drivers/gpib/agilent_82357a/agilent_82357a.c | 43 +++++++++++++++++++-
> drivers/gpib/agilent_82357a/agilent_82357a.h | 2 +
> 2 files changed, 43 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c
> index 2468a471d..a165f40ac 100644
> --- a/drivers/gpib/agilent_82357a/agilent_82357a.c
> +++ b/drivers/gpib/agilent_82357a/agilent_82357a.c
> @@ -567,7 +567,7 @@ static ssize_t agilent_82357a_generic_write(struct gpib_board *board,
> 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;
> + out_data[i] = AWF_ATN | AWF_NO_ADDRESS | AWF_NO_FAST_TALKER;
> if (send_eoi)
> out_data[i] |= AWF_SEND_EOI;
> ++i;
> @@ -648,10 +648,19 @@ static ssize_t agilent_82357a_generic_write(struct gpib_board *board,
> return -ECOMM;
> }
> }
> -
> return -ETIMEDOUT;
> }
>
> + /*
> + * Clone adapters do not answer to XFER_STATUS, so use the
> + * bytes_written from the write complete interrupt.
> + */
> + if (a_priv->is_clone_82357b) {
> + mutex_unlock(&a_priv->bulk_transfer_lock);
> + *bytes_written = a_priv->write_complete_count;
> + return 0;
> + }
> +
> status_data = kmalloc(STATUS_DATA_LEN, GFP_KERNEL);
> if (!status_data) {
> mutex_unlock(&a_priv->bulk_transfer_lock);
> @@ -1114,6 +1123,12 @@ static void agilent_82357a_interrupt_complete(struct urb *urb)
> }
>
> interrupt_flags = transfer_buffer[0];
> + if (urb->actual_length >= 6 && test_bit(AIF_WRITE_COMPLETE_BN, &interrupt_flags)) {
> + a_priv->write_complete_count = (u32)transfer_buffer[2];
> + a_priv->write_complete_count |= (u32)transfer_buffer[3] << 8;
> + a_priv->write_complete_count |= (u32)transfer_buffer[4] << 16;
> + a_priv->write_complete_count |= (u32)transfer_buffer[5] << 24;
> + }
> 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))
> @@ -1213,6 +1228,26 @@ static void agilent_82357a_free_private(struct gpib_board *board)
> board->private_data = NULL;
> }
>
> +static void agilent_82357a_detect_clone(struct agilent_82357a_priv *a_priv)
> +{
> + struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
> + u8 *ret_data;
> + int retval;
> +
> + ret_data = kmalloc(1, GFP_KERNEL);
> + if (!ret_data)
> + return;
> + retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 0xA0,
> + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> + 0xE600, 0, ret_data, 1, 100);
> + kfree(ret_data);
> + if (retval < 0) {
> + a_priv->is_clone_82357b = 1;
> + dev_info(&usb_dev->dev, "Clone 82357B detected (firmware load returned %i), using vendor quirks\n",
> + retval);
> + }
> +}
> +
> #define INIT_NUM_REG_WRITES 18
> static int agilent_82357a_init(struct gpib_board *board)
> {
> @@ -1223,6 +1258,9 @@ static int agilent_82357a_init(struct gpib_board *board)
> int retval;
> unsigned int nanosec;
>
> + if (a_priv->is_clone_82357b)
> + board->t1_nano_sec = 819;
> +
We can drop this and just use 819 in the initial setting.
> writes[0].address = LED_CONTROL;
> writes[0].value = FAIL_LED_ON;
> writes[1].address = RESET_TO_POWERUP;
> @@ -1346,6 +1384,7 @@ static int agilent_82357a_attach(struct gpib_board *board, const struct gpib_boa
> 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;
> + agilent_82357a_detect_clone(a_priv);
> break;
> default:
> dev_err(&usb_dev->dev, "bug, unhandled product_id in switch?\n");
> diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.h b/drivers/gpib/agilent_82357a/agilent_82357a.h
> index 33ac558e5..201316344 100644
> --- a/drivers/gpib/agilent_82357a/agilent_82357a.h
> +++ b/drivers/gpib/agilent_82357a/agilent_82357a.h
> @@ -133,12 +133,14 @@ struct agilent_82357a_priv {
> 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
> + u32 write_complete_count; // bytes transferred in last write
> 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;
> + unsigned is_clone_82357b : 1;
> };
>
> struct agilent_82357a_register_pairlet {
> --
> 2.55.0
>
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-03 14:51 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-02 22:41 [PATCH v2] gpib: agilent_82357a: Support some 82357B clones Tom Keller
2026-09-03 14:51 ` Dave Penkler
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®