Skip to contentSkip to conversation

USB: add USB test and measurement class driver - round 2

PATCH In review Greg KH 5 participants 23 messages Aug 27, 2008 – Aug 30, 2008

Subsystems (2)
lore
Share & tools
lore

Greg KH

author
#0 lore raw

PATCH 1/1 USB: add USB test and measurement class driver - round 2

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

thanks,

greg k-h

----------
From: Greg Kroah-Hartman <[email protected]>
Subject: USB: add USB test and measurement class driver

This driver was originaly written by Stefan Kopp, but massively reworked by Greg for submission.

Thanks to Felipe Balbi <[email protected]> for lots of work in cleaning up this driver.

Thanks to Oliver Neukum <[email protected]> for reviewing previous versions and pointing out problems.

Cc Stefan Kopp <[email protected]>
Cc Marcel Janssen <[email protected]>
Cc Felipe Balbi <[email protected]>
Cc Oliver Neukum <[email protected]>
---
 Documentation/ABI/stable/sysfs-driver-usb-usbtmc |   62 +
 Documentation/devices.txt                        |    3 
 Documentation/ioctl-number.txt                   |    2 
 drivers/usb/class/Kconfig                        |   10 
 drivers/usb/class/Makefile                       |    1 
 drivers/usb/class/usbtmc.c                       | 1095 +++++++++++++++++++++++
 include/linux/usb/Kbuild                         |    2 
 include/linux/usb/tmc.h                          |   43 
 8 files changed, 1217 insertions(+), 1 deletion(-)
Documentation/ABI/stable/sysfs-driver-usb-usbtmc new file +62 -0
@@ -0,0 +1,62 @@
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		These files show the various USB TMC capabilities as described
+		by the device itself.  The full description of the bitfields
+		can be found in the USB TMC documents from the USB-IF entitled
+		"Universal Serial Bus Test and Measurement Class Specification
+		(USBTMC) Revision 1.0" section 4.2.1.8.
+
+		The files are read only.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		These files show the various USB TMC capabilities as described
+		by the device itself.  The full description of the bitfields
+		can be found in the USB TMC documents from the USB-IF entitled
+		"Universal Serial Bus Test and Measurement Class, Subclass
+		USB488 Specification (USBTMC-USB488) Revision 1.0" section
+		4.2.2.
+
+		The files are read only.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/TermChar
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		This file is the TermChar value to be sent to the USB TMC
+		device as described by the document, "Universal Serial Bus Test
+		and Measurement Class Specification
+		(USBTMC) Revision 1.0" as published by the USB-IF.
+
+		Note that the TermCharEnabled file determines if this value is
+		sent to the device or not.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		This file determines if the TermChar is to be sent to the
+		device on every transaction or not.  For more details about
+		this, please see the document, "Universal Serial Bus Test and
+		Measurement Class Specification (USBTMC) Revision 1.0" as
+		published by the USB-IF.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/auto_abort
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		This file determines if the the transaction of the USB TMC
+		device is to be automatically aborted if there is any error.
+		For more details about this, please see the document,
+		"Universal Serial Bus Test and Measurement Class Specification
+		(USBTMC) Revision 1.0" as published by the USB-IF.
Documentation/devices.txt +3 -0
@@ -2571,6 +2571,9 @@ Your cooperation is appreciated.
 		160 = /dev/usb/legousbtower0	1st USB Legotower device
 		    ...
 		175 = /dev/usb/legousbtower15	16th USB Legotower device
+		176 = /dev/usb/usbtmc1	First USB TMC device
+		   ...
+		192 = /dev/usb/usbtmc16	16th USB TMC device
 		240 = /dev/usb/dabusb0	First daubusb device
 		    ...
 		243 = /dev/usb/dabusb3	Fourth dabusb device
Documentation/ioctl-number.txt +2 -0
@@ -110,6 +110,8 @@ Code	Seq#	Include File		Comments
 'W'	00-1F	linux/wanrouter.h	conflict!
 'X'	all	linux/xfs_fs.h
 'Y'	all	linux/cyclades.h
+'['	00-07	linux/usb/usbtmc.h	USB Test and Measurement Devices
+					<mailto:[email protected]>
 'a'	all				ATM on linux
 					<http://lrcwww.epfl.ch/linux-atm/magic.html>
 'b'	00-FF				bit3 vme host bridge
drivers/usb/class/Kconfig +10 -0
@@ -40,3 +40,13 @@ config USB_WDM
 	  To compile this driver as a module, choose M here: the
 	  module will be called cdc-wdm.
 
+config USB_TMC
+	tristate "USB Test and Measurement Class support"
+	depends on USB
+	help
+	  Say Y here if you want to connect a USB device that follows
+	  the USB.org specification for USB Test and Measurement devices
+	  to your computer's USB port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbtmc.
drivers/usb/class/Makefile +1 -0
@@ -6,3 +6,4 @@
 obj-$(CONFIG_USB_ACM)		+= cdc-acm.o
 obj-$(CONFIG_USB_PRINTER)	+= usblp.o
 obj-$(CONFIG_USB_WDM)		+= cdc-wdm.o
+obj-$(CONFIG_USB_TMC)		+= usbtmc.o
drivers/usb/class/usbtmc.c new file +1095 -0

1,095 lines not on this page · raw message

include/linux/usb/Kbuild +1 -1
@@ -4,4 +4,4 @@ header-y += ch9.h
 header-y += gadgetfs.h
 header-y += midi.h
 header-y += g_printer.h
-
+header-y += tmc.h
include/linux/usb/tmc.h new file +43 -0

43 lines not on this page · raw message

#1 lore raw
Quoted 2 lines · Greg KH

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

This is an example of what I was discussing with Oliver. In all likelihood you simply don't need usbtmc_mutex, and using it will cause a lockdep violation.

That's why so many of the other USB class drivers don't have an analogous static mutex.

drivers/usb/class/usbtmc.c line 111

Quoted 7 lines
drivers/usb/class/usbtmc.c new file +7 -0
+static int usbtmc_open(struct inode *inode, struct file *filp)
+{
+	struct usb_interface *intf;
+	struct usbtmc_device_data *data;
+	int retval = -ENODEV;
+
+	mutex_lock(&usbtmc_mutex);

You must never acquire a lock in your open method if it will be held by your disconnect method while unregistering the minor.

Quoted 3 lines
drivers/usb/class/usbtmc.c new file +3 -0
+static int usbtmc_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{

...

drivers/usb/class/usbtmc.c line 1044

Quoted 8 lines
drivers/usb/class/usbtmc.c new file +8 -0
+	retcode = usb_register_dev(intf, &usbtmc_class);
+	if (retcode) {
+		dev_err(&intf->dev, "Not able to get a minor"
+			" (base %u, slice default): %d\n", USBTMC_MINOR_BASE,
+			retcode);
+		goto error_register;
+	}
+	dev_dbg(&intf->dev, "Using minor number %d\n", intf->minor);

You do call usb_register_dev() during probe...

Quoted 13 lines
drivers/usb/class/usbtmc.c new file +13 -0
+
+	return 0;
+
+error_register:
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+	kref_put(&data->kref, usbtmc_delete);
+	return retcode;
+}
+
+static void usbtmc_disconnect(struct usb_interface *intf)
+{
+	struct usbtmc_device_data *data;

drivers/usb/class/usbtmc.c line 1067

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+
+	dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
+
+	mutex_lock(&usbtmc_mutex);
+	data = usb_get_intfdata(intf);
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+	kref_put(&data->kref, usbtmc_delete);
+	mutex_unlock(&usbtmc_mutex);
+}

But you don't call usb_unregister_dev() during disconnect. An oversight?

Alan Stern

#2 lore raw

Am Mittwoch 27 August 2008 20:28:22 schrieb Alan Stern:

Quoted 12 lines · Greg KH
Quoted 3 lines

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

This is an example of what I was discussing with Oliver.  In all likelihood you simply don't need usbtmc_mutex, and using it will cause a lockdep violation.

That's why so many of the other USB class drivers don't have an analogous static mutex.

Quoted 10 lines

drivers/usb/class/usbtmc.c line 111

Quoted 7 lines
drivers/usb/class/usbtmc.c new file +7 -0
+static int usbtmc_open(struct inode *inode, struct file *filp)
+{
+     struct usb_interface *intf;
+     struct usbtmc_device_data *data;
+     int retval = -ENODEV;
+
+     mutex_lock(&usbtmc_mutex);

You must never acquire a lock in your open method if it will be held by your disconnect method while unregistering the minor.

And this rule depends on sharing the USB major or not. This needs a big fat mention in Documentation.

Regards
Oliver

author
#3 lore raw
In reply to Alan Stern
Quoted 11 lines · Alan Stern
Quoted 2 lines · Greg KH

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

This is an example of what I was discussing with Oliver. In all likelihood you simply don't need usbtmc_mutex, and using it will cause a lockdep violation.

That's why so many of the other USB class drivers don't have an analogous static mutex.

Ok, then it's just safe to drop this static mutex entirely, right?

Quoted 44 lines

drivers/usb/class/usbtmc.c line 111

Quoted 7 lines
drivers/usb/class/usbtmc.c new file +7 -0
+static int usbtmc_open(struct inode *inode, struct file *filp)
+{
+	struct usb_interface *intf;
+	struct usbtmc_device_data *data;
+	int retval = -ENODEV;
+
+	mutex_lock(&usbtmc_mutex);

You must never acquire a lock in your open method if it will be held by your disconnect method while unregistering the minor.

Quoted 3 lines
drivers/usb/class/usbtmc.c new file +3 -0
+static int usbtmc_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{

...

drivers/usb/class/usbtmc.c line 1044

Quoted 8 lines
drivers/usb/class/usbtmc.c new file +8 -0
+	retcode = usb_register_dev(intf, &usbtmc_class);
+	if (retcode) {
+		dev_err(&intf->dev, "Not able to get a minor"
+			" (base %u, slice default): %d\n", USBTMC_MINOR_BASE,
+			retcode);
+		goto error_register;
+	}
+	dev_dbg(&intf->dev, "Using minor number %d\n", intf->minor);

You do call usb_register_dev() during probe...

Quoted 18 lines
drivers/usb/class/usbtmc.c new file +18 -0
+
+	return 0;
+
+error_register:
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+	kref_put(&data->kref, usbtmc_delete);
+	return retcode;
+}
+
+static void usbtmc_disconnect(struct usb_interface *intf)
+{
+	struct usbtmc_device_data *data;
+
+	dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
+
+	mutex_lock(&usbtmc_mutex);
+	data = usb_get_intfdata(intf);
Quoted 8 lines

drivers/usb/class/usbtmc.c line 1067

Quoted 5 lines
drivers/usb/class/usbtmc.c new file +5 -0
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+	kref_put(&data->kref, usbtmc_delete);
+	mutex_unlock(&usbtmc_mutex);
+}

But you don't call usb_unregister_dev() during disconnect. An oversight?

Ah, yes, my fault, I converted this from using a private cdev to using the usb_register_dev() call and forgot about this. Thanks for catching it, I'll go fix this.

Oh, it's usb_deregister_dev(), stupid programmers with inconsitant interfaces :)

thanks,

greg k-h

#4 lore raw
Quoted 6 lines · Greg KH
Quoted 5 lines · Alan Stern
Quoted 2 lines · Greg KH

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

Quoted 8 lines
Quoted 6 lines

This is an example of what I was discussing with Oliver. In all likelihood you simply don't need usbtmc_mutex, and using it will cause a lockdep violation.

That's why so many of the other USB class drivers don't have an analogous static mutex.

Ok, then it's just safe to drop this static mutex entirely, right?

Yes, once you add the call to usb_deregister_dev.

Quoted 2 lines · Oliver Neukum

And this rule depends on sharing the USB major or not. This needs a big fat mention in Documentation.

You mean that the open/disconnect locking rule applies only to drivers that call usb_register_dev, i.e, drivers using the USB major. Right?

I agree that it deserves to be mentioned in the documentation somewhere. Where would be a good place? None of the existing files in Documentation/usb seem appropriate.

Alan Stern

#5 lore raw

Am Mittwoch 27 August 2008 20:58:08 schrieb Alan Stern:

Quoted 5 lines
Quoted 2 lines

And this rule depends on sharing the USB major or not. This needs a big fat mention in Documentation.

You mean that the open/disconnect locking rule applies only to drivers that call usb_register_dev, i.e, drivers using the USB major.  Right?

Yes. In fact drivers not using the USB major but their own char devices will need such a lock. This is tricky.

Quoted 3 lines

I agree that it deserves to be mentioned in the documentation somewhere.  Where would be a good place?  None of the existing files in Documentation/usb seem appropriate.

The USB major merits a file of its own.

Regards
Oliver

#6 lore raw
Quoted 9 lines · Oliver Neukum

Am Mittwoch 27 August 2008 20:58:08 schrieb Alan Stern:

Quoted 5 lines
Quoted 2 lines

And this rule depends on sharing the USB major or not. This needs a big fat mention in Documentation.

You mean that the open/disconnect locking rule applies only to drivers that call usb_register_dev, i.e, drivers using the USB major.  Right?

Yes. In fact drivers not using the USB major but their own char devices will need such a lock. This is tricky.

IMO all char-device registration/deregistration routines should use a similar rwsem. Then device drivers wouldn't need to worry about it.

Quoted 5 lines
Quoted 3 lines

I agree that it deserves to be mentioned in the documentation somewhere.  Where would be a good place?  None of the existing files in Documentation/usb seem appropriate.

The USB major merits a file of its own.

I don't feel competent to start writing such a file. Greg, what do you think? Maybe all that's needed to get going is to "borrow" some of the material in LDD3. :-)

Alan Stern

author
#7 lore raw
Quoted 19 lines · Alan Stern
Quoted 9 lines · Oliver Neukum

Am Mittwoch 27 August 2008 20:58:08 schrieb Alan Stern:

Quoted 5 lines
Quoted 2 lines

And this rule depends on sharing the USB major or not. This needs a big fat mention in Documentation.

You mean that the open/disconnect locking rule applies only to drivers that call usb_register_dev, i.e, drivers using the USB major.  Right?

Yes. In fact drivers not using the USB major but their own char devices will need such a lock. This is tricky.

IMO all char-device registration/deregistration routines should use a similar rwsem. Then device drivers wouldn't need to worry about it.

Quoted 4 lines
Quoted 3 lines

I agree that it deserves to be mentioned in the documentation somewhere.  Where would be a good place?  None of the existing files in Documentation/usb seem appropriate.

Quoted 5 lines
Quoted 1 line

The USB major merits a file of its own.

I don't feel competent to start writing such a file. Greg, what do you think? Maybe all that's needed to get going is to "borrow" some of the material in LDD3. :-)

Well, as the USB chapter in LDD3 "borrowed" heavily from the comments in the usb code itself, that would only be fair :)

That chapter needs to be reworked. The authors and Oreilly and I are currently talking about how to do this all in a framework that makes sense (the current one of a book every few years that quickly gets out of date doesn't make sense.)

thanks,

greg k-h

author
#8 lore raw
In reply to Alan Stern
Quoted 12 lines · Alan Stern
Quoted 10 lines · Greg KH
Quoted 9 lines · Alan Stern
Quoted 2 lines · Greg KH

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

This is an example of what I was discussing with Oliver. In all likelihood you simply don't need usbtmc_mutex, and using it will cause a lockdep violation.

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

That's why so many of the other USB class drivers don't have an analogous static mutex.

Ok, then it's just safe to drop this static mutex entirely, right?

Yes, once you add the call to usb_deregister_dev.

Great, all done now.

Here's the updated version.

thanks,

greg k-h
----------
From: Greg Kroah-Hartman <[email protected]>
Subject: USB: add USB test and measurement class driver

This driver was originaly written by Stefan Kopp, but massively reworked by Greg for submission.

Thanks to Felipe Balbi <[email protected]> for lots of work in cleaning up this driver.

Thanks to Oliver Neukum <[email protected]> for reviewing previous versions and pointing out problems.

Cc Stefan Kopp <[email protected]>
Cc Marcel Janssen <[email protected]>
Cc Felipe Balbi <[email protected]>
Cc Oliver Neukum <[email protected]>
---
 Documentation/ABI/stable/sysfs-driver-usb-usbtmc |   62 +
 Documentation/devices.txt                        |    3 
 Documentation/ioctl-number.txt                   |    2 
 drivers/usb/class/Kconfig                        |   10 
 drivers/usb/class/Makefile                       |    1 
 drivers/usb/class/usbtmc.c                       | 1087 +++++++++++++++++++++++
 include/linux/usb/Kbuild                         |    2 
 include/linux/usb/tmc.h                          |   43 
 8 files changed, 1209 insertions(+), 1 deletion(-)
Documentation/ABI/stable/sysfs-driver-usb-usbtmc new file +62 -0
@@ -0,0 +1,62 @@
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		These files show the various USB TMC capabilities as described
+		by the device itself.  The full description of the bitfields
+		can be found in the USB TMC documents from the USB-IF entitled
+		"Universal Serial Bus Test and Measurement Class Specification
+		(USBTMC) Revision 1.0" section 4.2.1.8.
+
+		The files are read only.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		These files show the various USB TMC capabilities as described
+		by the device itself.  The full description of the bitfields
+		can be found in the USB TMC documents from the USB-IF entitled
+		"Universal Serial Bus Test and Measurement Class, Subclass
+		USB488 Specification (USBTMC-USB488) Revision 1.0" section
+		4.2.2.
+
+		The files are read only.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/TermChar
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		This file is the TermChar value to be sent to the USB TMC
+		device as described by the document, "Universal Serial Bus Test
+		and Measurement Class Specification
+		(USBTMC) Revision 1.0" as published by the USB-IF.
+
+		Note that the TermCharEnabled file determines if this value is
+		sent to the device or not.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		This file determines if the TermChar is to be sent to the
+		device on every transaction or not.  For more details about
+		this, please see the document, "Universal Serial Bus Test and
+		Measurement Class Specification (USBTMC) Revision 1.0" as
+		published by the USB-IF.
+
+
+What:		/sys/bus/usb/drivers/usbtmc/devices/*/auto_abort
+Date:		August 2008
+Contact:	Greg Kroah-Hartman <[email protected]>
+Description:
+		This file determines if the the transaction of the USB TMC
+		device is to be automatically aborted if there is any error.
+		For more details about this, please see the document,
+		"Universal Serial Bus Test and Measurement Class Specification
+		(USBTMC) Revision 1.0" as published by the USB-IF.
Documentation/devices.txt +3 -0
@@ -2571,6 +2571,9 @@ Your cooperation is appreciated.
 		160 = /dev/usb/legousbtower0	1st USB Legotower device
 		    ...
 		175 = /dev/usb/legousbtower15	16th USB Legotower device
+		176 = /dev/usb/usbtmc1	First USB TMC device
+		   ...
+		192 = /dev/usb/usbtmc16	16th USB TMC device
 		240 = /dev/usb/dabusb0	First daubusb device
 		    ...
 		243 = /dev/usb/dabusb3	Fourth dabusb device
Documentation/ioctl-number.txt +2 -0
@@ -110,6 +110,8 @@ Code	Seq#	Include File		Comments
 'W'	00-1F	linux/wanrouter.h	conflict!
 'X'	all	linux/xfs_fs.h
 'Y'	all	linux/cyclades.h
+'['	00-07	linux/usb/usbtmc.h	USB Test and Measurement Devices
+					<mailto:[email protected]>
 'a'	all				ATM on linux
 					<http://lrcwww.epfl.ch/linux-atm/magic.html>
 'b'	00-FF				bit3 vme host bridge
drivers/usb/class/Kconfig +10 -0
@@ -40,3 +40,13 @@ config USB_WDM
 	  To compile this driver as a module, choose M here: the
 	  module will be called cdc-wdm.
 
+config USB_TMC
+	tristate "USB Test and Measurement Class support"
+	depends on USB
+	help
+	  Say Y here if you want to connect a USB device that follows
+	  the USB.org specification for USB Test and Measurement devices
+	  to your computer's USB port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbtmc.
drivers/usb/class/Makefile +1 -0
@@ -6,3 +6,4 @@
 obj-$(CONFIG_USB_ACM)		+= cdc-acm.o
 obj-$(CONFIG_USB_PRINTER)	+= usblp.o
 obj-$(CONFIG_USB_WDM)		+= cdc-wdm.o
+obj-$(CONFIG_USB_TMC)		+= usbtmc.o
drivers/usb/class/usbtmc.c new file +1087 -0

1,087 lines not on this page · raw message

include/linux/usb/Kbuild +1 -1
@@ -4,4 +4,4 @@ header-y += ch9.h
 header-y += gadgetfs.h
 header-y += midi.h
 header-y += g_printer.h
-
+header-y += tmc.h
include/linux/usb/tmc.h new file +43 -0

43 lines not on this page · raw message

#9 lore raw

Am Donnerstag 28 August 2008 01:47:20 schrieb Greg KH:

Quoted 16 lines · Alan Stern
Quoted 15 lines · Greg KH
Quoted 14 lines · Alan Stern
Quoted 11 lines · Greg KH
Quoted 3 lines

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

This is an example of what I was discussing with Oliver. In all likelihood you simply don't need usbtmc_mutex, and using it will cause a lockdep violation.

That's why so many of the other USB class drivers don't have an analogous static mutex.

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

Ok, then it's just safe to drop this static mutex entirely, right?

Yes, once you add the call to usb_deregister_dev.

Great, all done now.

Here's the updated version.

OK, here we go again.

Quoted 3 lines
drivers/usb/class/usbtmc.c new file +3 -0
+static ssize_t usbtmc_read(struct file *filp, char __user *buf,
+			   size_t count, loff_t *f_pos)
+{

[..]

drivers/usb/class/usbtmc.c line 403

Quoted 11 lines
drivers/usb/class/usbtmc.c new file +11 -0
+		/* Setup IO buffer for DEV_DEP_MSG_IN message
+		 * Refer to class specs for details
+		 */
+		buffer[0] = 2;
+		buffer[1] = data->bTag;
+		buffer[2] = ~(data->bTag);
+		buffer[3] = 0; /* Reserved */
+		buffer[4] = (this_part - 12 - 3) & 255;
+		buffer[5] = ((this_part - 12 - 3) >> 8) & 255;
+		buffer[6] = ((this_part - 12 - 3) >> 16) & 255;
+		buffer[7] = ((this_part - 12 - 3) >> 24) & 255;

We have excellent endianness conversion macros.

drivers/usb/class/usbtmc.c line 405

Quoted 2 lines
drivers/usb/class/usbtmc.c new file +2 -0
+		buffer[8] = data->TermCharEnabled * 2;
+		/* Use term character? */

smp_rmb(); /* we must make sure we don't read a stale terminator */

drivers/usb/class/usbtmc.c line 417

Quoted 12 lines
drivers/usb/class/usbtmc.c new file +12 -0
+		buffer[9] = data->TermChar;
+		buffer[10] = 0; /* Reserved */
+		buffer[11] = 0; /* Reserved */
+
+		/* Send bulk URB */
+		retval = usb_bulk_msg(data->usb_dev,
+				      usb_sndbulkpipe(data->usb_dev,
+						      data->bulk_out),
+				      buffer, 12, &actual, USBTMC_TIMEOUT);
+
+		/* Store bTag (in case we need to abort) */
+		data->bTag_last_write = data->bTag;

even if usb_bulk_msg() failed?

Quoted 25 lines
drivers/usb/class/usbtmc.c new file +25 -0
+
+		/* Increment bTag -- and increment again if zero */
+		data->bTag++;
+		if (!data->bTag)
+			(data->bTag)++;
+
+		if (retval < 0) {
+			dev_err(dev, "usb_bulk_msg returned %d\n", retval);
+			if (data->auto_abort)
+				usbtmc_ioctl_abort_bulk_out(data);
+			goto exit;
+		}
+
+		/* Send bulk URB */
+		retval = usb_bulk_msg(data->usb_dev,
+				      usb_rcvbulkpipe(data->usb_dev,
+						      data->bulk_in),
+				      buffer, USBTMC_SIZE_IOBUFFER, &actual,
+				      USBTMC_TIMEOUT);
+
+		/* Store bTag (in case we need to abort) */
+		data->bTag_last_read = data->bTag;
+
+		if (retval < 0) {
+			dev_err(dev, "Unable to read data, error %d\n", retval);

drivers/usb/class/usbtmc.c line 452

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+			if (data->auto_abort)
+				usbtmc_ioctl_abort_bulk_in(data);
+			goto exit;
+		}
+
+		/* How many characters did the instrument send? */
+		n_characters = buffer[4] +
+			       (buffer[5] << 8) +
+			       (buffer[6] << 16) +
+			       (buffer[7] << 24);

endianness macro

Quoted 5 lines
drivers/usb/class/usbtmc.c new file +5 -0
+
+		/* Copy buffer to user space */
+		if (copy_to_user(buf + done, &buffer[12], n_characters)) {
+			/* There must have been an addressing problem */
+			retval = -EFAULT;

drivers/usb/class/usbtmc.c line 467

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+			goto exit;
+		}
+
+		done += n_characters;
+		if (n_characters < USBTMC_SIZE_IOBUFFER)
+			remaining = 0;
+	}
+
+	/* Update file position value */
+	*f_pos = *f_pos + done;

That'll get out of sync if -EFAULT is returned

Quoted 18 lines
drivers/usb/class/usbtmc.c new file +18 -0
+	retval = done;
+
+exit:
+	mutex_unlock(&data->io_mutex);
+	kfree(buffer);
+	return retval;
+}
+
+static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
+			    size_t count, loff_t *f_pos)
+{
+	struct usbtmc_device_data *data;
+	u8 *buffer;
+	int retval;
+	int actual;
+	unsigned long int n_bytes;
+	int n;
+	int remaining;

drivers/usb/class/usbtmc.c line 495

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+	int done;
+	int this_part;
+
+	data = filp->private_data;
+
+	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	mutex_lock(&data->io_mutex);

This and usbtmc_read() need a test for disconnection. Open() and disconnect are guarded in usbcore, read & write are not. By reference count you've made sure you have a valid device descriptor, but the device may have been reprobed.

Quoted 12 lines
drivers/usb/class/usbtmc.c new file +12 -0
+
+	remaining = count;
+	done = 0;
+
+	while (remaining > 0) {
+		if (remaining > USBTMC_SIZE_IOBUFFER - 12) {
+			this_part = USBTMC_SIZE_IOBUFFER - 12;
+			buffer[8] = 0;
+		} else {
+			this_part = remaining;
+			buffer[8] = 1;
+		}

drivers/usb/class/usbtmc.c line 517

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+
+		/* Setup IO buffer for DEV_DEP_MSG_OUT message */
+		buffer[0] = 1;
+		buffer[1] = data->bTag;
+		buffer[2] = ~(data->bTag);
+		buffer[3] = 0; /* Reserved */
+		buffer[4] = this_part & 255;
+		buffer[5] = (this_part >> 8) & 255;
+		buffer[6] = (this_part >> 16) & 255;
+		buffer[7] = (this_part >> 24) & 255;

endianness macro

Quoted 12 lines
drivers/usb/class/usbtmc.c new file +12 -0
+		/* buffer[8] is set above... */
+		buffer[9] = 0; /* Reserved */
+		buffer[10] = 0; /* Reserved */
+		buffer[11] = 0; /* Reserved */
+
+		if (copy_from_user(&buffer[12], buf + done, this_part)) {
+			retval = -EFAULT;
+			goto exit;
+		}
+
+		n_bytes = 12 + this_part;
+		if (this_part % 4)

drivers/usb/class/usbtmc.c line 539

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+			n_bytes += 4 - this_part % 4;
+			for (n = 12 + this_part; n < n_bytes; n++)
+				buffer[n] = 0;
+
+		retval = usb_bulk_msg(data->usb_dev,
+				      usb_sndbulkpipe(data->usb_dev,
+						      data->bulk_out),
+				      buffer, n_bytes, &actual, USBTMC_TIMEOUT);
+
+		data->bTag_last_write = data->bTag;

error case?

[..]

Quoted 7 lines
drivers/usb/class/usbtmc.c new file +7 -0
+static int get_capabilities(struct usbtmc_device_data *data)
+{
+	struct device *dev = &data->usb_dev->dev;
+	char *buffer;
+	int rv;
+
+	buffer = kmalloc(0x18, GFP_KERNEL);

drivers/usb/class/usbtmc.c line 766

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+	if (!buffer)
+		return -ENOMEM;
+
+	rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_GET_CAPABILITIES,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			     0, 0, buffer, 0x18, USBTMC_TIMEOUT);
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		return rv;

memory leak

drivers/usb/class/usbtmc.c line 776

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+	}
+
+	dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
+	dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]);
+	dev_dbg(dev, "Device capabilities are %x\n", buffer[5]);
+	dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]);
+	dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]);
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
+		return -EPERM;

memory leak

[..]

drivers/usb/class/usbtmc.c line 834

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+static ssize_t store_TermChar(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);
+
+	if (count < 1)
+		return -EINVAL;
+	data->TermChar = buf[0];

smp_wmb(); /* must hit RAM before enablement */

Quoted 15 lines
drivers/usb/class/usbtmc.c new file +15 -0
+	return count;
+}
+static DEVICE_ATTR(TermChar, S_IRUGO, show_TermChar, store_TermChar);
+
+#define data_attribute(name)						\
+static ssize_t show_##name(struct device *dev,				\
+			   struct device_attribute *attr, char *buf)	\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\
+									\
+	return sprintf(buf, "%d\n", data->name);			\
+}									\
+static ssize_t store_##name(struct device *dev,				\
+			    struct device_attribute *attr,		\

drivers/usb/class/usbtmc.c line 859

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+			    const char *buf, size_t count)		\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\
+	ssize_t result;							\
+	unsigned val;							\
+									\
+	result = sscanf(buf, "%u\n", &val);				\
+	if (result != 1)						\
+		result = -EINVAL;					\

smp_rmb(); /* must hit RAM after the terminator */ \

There are subtle penalties to avoiding ioctl(). Among them is the loss of atomicity.
[..]

drivers/usb/class/usbtmc.c line 925

Quoted 7 lines
drivers/usb/class/usbtmc.c new file +7 -0
+static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usbtmc_device_data *data;
+	int retval = -EBADRQC;
+
+	data = file->private_data;
+	mutex_lock(&data->io_mutex);

check for disconnect as in read & write

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+
+	switch (cmd) {
+	case USBTMC_IOCTL_CLEAR_OUT_HALT:
+		retval = usbtmc_ioctl_clear_out_halt(data);
+
+	case USBTMC_IOCTL_CLEAR_IN_HALT:
+		retval = usbtmc_ioctl_clear_in_halt(data);
+
+	case USBTMC_IOCTL_INDICATOR_PULSE:
+		retval = usbtmc_ioctl_indicator_pulse(data);

drivers/usb/class/usbtmc.c line 945

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+
+	case USBTMC_IOCTL_CLEAR:
+		retval = usbtmc_ioctl_clear(data);
+
+	case USBTMC_IOCTL_ABORT_BULK_OUT:
+		retval = usbtmc_ioctl_abort_bulk_out(data);
+
+	case USBTMC_IOCTL_ABORT_BULK_IN:
+		retval = usbtmc_ioctl_abort_bulk_in(data);
+	}

This is missing a whole lot of "break;"

Quoted 54 lines
drivers/usb/class/usbtmc.c new file +54 -0
+static int usbtmc_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usbtmc_device_data *data;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int n;
+	int retcode;
+
+	dev_dbg(&intf->dev, "%s called\n", __func__);
+
+	data = kmalloc(sizeof(struct usbtmc_device_data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&intf->dev, "Unable to allocate kernel memory\n");
+		return -ENOMEM;
+	}
+
+	data->intf = intf;
+	data->id = id;
+	data->usb_dev = usb_get_dev(interface_to_usbdev(intf));
+	usb_set_intfdata(intf, data);
+	kref_init(&data->kref);
+	mutex_init(&data->io_mutex);
+
+	/* Initialize USBTMC bTag and other fields */
+	data->bTag	= 1;
+	data->TermCharEnabled = 0;
+	data->TermChar = '\n';
+
+	/* USBTMC devices have only one setting, so use that */
+	iface_desc = data->intf->cur_altsetting;
+
+	/* Find bulk in endpoint */
+	for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
+		endpoint = &iface_desc->endpoint[n].desc;
+
+		if (usb_endpoint_is_bulk_in(endpoint)) {
+			data->bulk_in = endpoint->bEndpointAddress;
+			dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n",
+				data->bulk_in);
+			break;
+		}
+	}
+
+	/* Find bulk out endpoint */
+	for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
+		endpoint = &iface_desc->endpoint[n].desc;
+
+		if (usb_endpoint_is_bulk_out(endpoint)) {
+			data->bulk_out = endpoint->bEndpointAddress;
+			dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n",
+				data->bulk_out);
+			break;
+		}

drivers/usb/class/usbtmc.c line 1030

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+	}
+
+	retcode = get_capabilities(data);
+	if (retcode)
+		dev_err(&intf->dev, "can't read capabilities\n");
+	else
+		retcode = sysfs_create_group(&intf->dev.kobj,
+					     &capability_attr_grp);
+
+	retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp);

If you ignore an error return, be open about it.

Quoted 4 lines
drivers/usb/class/usbtmc.c new file +4 -0
+	retcode = usb_register_dev(intf, &usbtmc_class);
+	if (retcode) {
+		dev_err(&intf->dev, "Not able to get a minor"
+			" (base %u, slice default): %d\n", USBTMC_MINOR_BASE,

drivers/usb/class/usbtmc.c line 1045

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+			retcode);
+		goto error_register;
+	}
+	dev_dbg(&intf->dev, "Using minor number %d\n", intf->minor);
+
+	return 0;
+
+error_register:
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);

The groups might never have been created.

drivers/usb/class/usbtmc.c line 1055

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+	kref_put(&data->kref, usbtmc_delete);
+	return retcode;
+}
+
+static void usbtmc_disconnect(struct usb_interface *intf)
+{
+	struct usbtmc_device_data *data;
+
+	dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
+

You must set a flag for read, write and ioctl.

Regards
Oliver

author
#10 lore raw
Quoted 33 lines · Oliver Neukum

Am Donnerstag 28 August 2008 01:47:20 schrieb Greg KH:

Quoted 16 lines · Alan Stern
Quoted 15 lines · Greg KH
Quoted 14 lines · Alan Stern
Quoted 11 lines · Greg KH
Quoted 3 lines

Here's an updated version of the usbtmc driver, with all of the different issues that have been raised, hopefully addressed.

This is an example of what I was discussing with Oliver. In all likelihood you simply don't need usbtmc_mutex, and using it will cause a lockdep violation.

That's why so many of the other USB class drivers don't have an analogous static mutex.

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

Ok, then it's just safe to drop this static mutex entirely, right?

Yes, once you add the call to usb_deregister_dev.

Great, all done now.

Here's the updated version.

OK, here we go again.

Quoted 3 lines
drivers/usb/class/usbtmc.c new file +3 -0
+static ssize_t usbtmc_read(struct file *filp, char __user *buf,
+			   size_t count, loff_t *f_pos)
+{

[..]

Quoted 13 lines

drivers/usb/class/usbtmc.c line 403

Quoted 11 lines
drivers/usb/class/usbtmc.c new file +11 -0
+		/* Setup IO buffer for DEV_DEP_MSG_IN message
+		 * Refer to class specs for details
+		 */
+		buffer[0] = 2;
+		buffer[1] = data->bTag;
+		buffer[2] = ~(data->bTag);
+		buffer[3] = 0; /* Reserved */
+		buffer[4] = (this_part - 12 - 3) & 255;
+		buffer[5] = ((this_part - 12 - 3) >> 8) & 255;
+		buffer[6] = ((this_part - 12 - 3) >> 16) & 255;
+		buffer[7] = ((this_part - 12 - 3) >> 24) & 255;

We have excellent endianness conversion macros.

For splitting values up into the individual byte portions? I think this is far more obvious as to exactly what is going on, don't you?

Quoted 4 lines

drivers/usb/class/usbtmc.c line 405

Quoted 2 lines
drivers/usb/class/usbtmc.c new file +2 -0
+		buffer[8] = data->TermCharEnabled * 2;
+		/* Use term character? */

smp_rmb(); /* we must make sure we don't read a stale terminator */

I'm not going to worry about races here, that's not a real issue.

Quoted 6 lines
Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+		buffer[9] = data->TermChar;
+		buffer[10] = 0; /* Reserved */
+		buffer[11] = 0; /* Reserved */
+
+		/* Send bulk URB */
+		retval = usb_bulk_msg(data->usb_dev,
Quoted 8 lines

drivers/usb/class/usbtmc.c line 417

Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+				      usb_sndbulkpipe(data->usb_dev,
+						      data->bulk_out),
+				      buffer, 12, &actual, USBTMC_TIMEOUT);
+
+		/* Store bTag (in case we need to abort) */
+		data->bTag_last_write = data->bTag;

even if usb_bulk_msg() failed?

Good point, will fix.

Quoted 7 lines

drivers/usb/class/usbtmc.c line 452

Quoted 5 lines
drivers/usb/class/usbtmc.c new file +5 -0
+		/* How many characters did the instrument send? */
+		n_characters = buffer[4] +
+			       (buffer[5] << 8) +
+			       (buffer[6] << 16) +
+			       (buffer[7] << 24);

endianness macro

I'll leave it (casting is just a big of a mess, this obviously shows what is going on here.)

Quoted 8 lines
Quoted 8 lines
drivers/usb/class/usbtmc.c new file +8 -0
+		/* Copy buffer to user space */
+		if (copy_to_user(buf + done, &buffer[12], n_characters)) {
+			/* There must have been an addressing problem */
+			retval = -EFAULT;
+			goto exit;
+		}
+
+		done += n_characters;
Quoted 8 lines

drivers/usb/class/usbtmc.c line 467

Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+		if (n_characters < USBTMC_SIZE_IOBUFFER)
+			remaining = 0;
+	}
+
+	/* Update file position value */
+	*f_pos = *f_pos + done;

That'll get out of sync if -EFAULT is returned

Hm, I wonder if this is an issue, I'll poke at it.

Quoted 16 lines
Quoted 16 lines
drivers/usb/class/usbtmc.c new file +16 -0
+static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
+			    size_t count, loff_t *f_pos)
+{
+	struct usbtmc_device_data *data;
+	u8 *buffer;
+	int retval;
+	int actual;
+	unsigned long int n_bytes;
+	int n;
+	int remaining;
+	int done;
+	int this_part;
+
+	data = filp->private_data;
+
+	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
Quoted 8 lines

drivers/usb/class/usbtmc.c line 382

Quoted 4 lines
drivers/usb/class/usbtmc.c new file +4 -0
+	if (!buffer)
+		return -ENOMEM;
+
+	mutex_lock(&data->io_mutex);

This and usbtmc_read() need a test for disconnection. Open() and disconnect are guarded in usbcore, read & write are not. By reference count you've made sure you have a valid device descriptor, but the device may have been reprobed.

If so, then struct usb_device would be different, right? Oh, I see, disconnect() using usbfs/sysfs. Bah, is it really something that happens in the real world? Oh well, I'll go fix this...

Quoted 8 lines

drivers/usb/class/usbtmc.c line 539

Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+		retval = usb_bulk_msg(data->usb_dev,
+				      usb_sndbulkpipe(data->usb_dev,
+						      data->bulk_out),
+				      buffer, n_bytes, &actual, USBTMC_TIMEOUT);
+
+		data->bTag_last_write = data->bTag;

error case?

Will do.

Quoted 11 lines
Quoted 11 lines
drivers/usb/class/usbtmc.c new file +11 -0
+static int get_capabilities(struct usbtmc_device_data *data)
+{
+	struct device *dev = &data->usb_dev->dev;
+	char *buffer;
+	int rv;
+
+	buffer = kmalloc(0x18, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0),
Quoted 8 lines

drivers/usb/class/usbtmc.c line 766

Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+			     USBTMC_REQUEST_GET_CAPABILITIES,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			     0, 0, buffer, 0x18, USBTMC_TIMEOUT);
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		return rv;

memory leak

Good find, will fix.

Quoted 10 lines

drivers/usb/class/usbtmc.c line 776

Quoted 8 lines
drivers/usb/class/usbtmc.c new file +8 -0
+	dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
+	dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]);
+	dev_dbg(dev, "Device capabilities are %x\n", buffer[5]);
+	dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]);
+	dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]);
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
+		return -EPERM;

memory leak

will fix.

Quoted 13 lines

[..]

drivers/usb/class/usbtmc.c line 834

Quoted 10 lines
drivers/usb/class/usbtmc.c new file +10 -0
+static ssize_t store_TermChar(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);
+
+	if (count < 1)
+		return -EINVAL;
+	data->TermChar = buf[0];

smp_wmb(); /* must hit RAM before enablement */

Not going to be an issue, it's ok to leave as-is.

Quoted 11 lines

drivers/usb/class/usbtmc.c line 859

Quoted 9 lines
drivers/usb/class/usbtmc.c new file +9 -0
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\
+	ssize_t result;							\
+	unsigned val;							\
+									\
+	result = sscanf(buf, "%u\n", &val);				\
+	if (result != 1)						\
+		result = -EINVAL;					\

smp_rmb(); /* must hit RAM after the terminator */ \

Same as above.

Quoted 2 lines

There are subtle penalties to avoiding ioctl(). Among them is the loss of atomicity.

Sure, but again, this isn't a real issue here.

Quoted 25 lines

[..]

drivers/usb/class/usbtmc.c line 925

Quoted 7 lines
drivers/usb/class/usbtmc.c new file +7 -0
+static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usbtmc_device_data *data;
+	int retval = -EBADRQC;
+
+	data = file->private_data;
+	mutex_lock(&data->io_mutex);

check for disconnect as in read & write

Quoted 14 lines
drivers/usb/class/usbtmc.c new file +14 -0
+
+	switch (cmd) {
+	case USBTMC_IOCTL_CLEAR_OUT_HALT:
+		retval = usbtmc_ioctl_clear_out_halt(data);
+
+	case USBTMC_IOCTL_CLEAR_IN_HALT:
+		retval = usbtmc_ioctl_clear_in_halt(data);
+
+	case USBTMC_IOCTL_INDICATOR_PULSE:
+		retval = usbtmc_ioctl_indicator_pulse(data);
+
+	case USBTMC_IOCTL_CLEAR:
+		retval = usbtmc_ioctl_clear(data);
+
Quoted 8 lines

drivers/usb/class/usbtmc.c line 945

Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+	case USBTMC_IOCTL_ABORT_BULK_OUT:
+		retval = usbtmc_ioctl_abort_bulk_out(data);
+
+	case USBTMC_IOCTL_ABORT_BULK_IN:
+		retval = usbtmc_ioctl_abort_bulk_in(data);
+	}

This is missing a whole lot of "break;"

Bah, good catch, my fault.

Quoted 10 lines

drivers/usb/class/usbtmc.c line 1030

Quoted 8 lines
drivers/usb/class/usbtmc.c new file +8 -0
+	retcode = get_capabilities(data);
+	if (retcode)
+		dev_err(&intf->dev, "can't read capabilities\n");
+	else
+		retcode = sysfs_create_group(&intf->dev.kobj,
+					     &capability_attr_grp);
+
+	retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp);

If you ignore an error return, be open about it.

I'm not? Should I print an error and then just continue on? Would that be sufficient?

Quoted 5 lines

drivers/usb/class/usbtmc.c line 1045

Quoted 3 lines
drivers/usb/class/usbtmc.c new file +3 -0
+error_register:
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);

The groups might never have been created.

That's ok, the sysfs core should handle this safely.

Quoted 8 lines

drivers/usb/class/usbtmc.c line 1055

Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+static void usbtmc_disconnect(struct usb_interface *intf)
+{
+	struct usbtmc_device_data *data;
+
+	dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
+

You must set a flag for read, write and ioctl.

Will do. Then I need to lock the flag with a mutex, right?

thanks,

greg k-h

#11 lore raw
Quoted 10 lines
Quoted 7 lines

drivers/usb/class/usbtmc.c line 403

Quoted 5 lines
drivers/usb/class/usbtmc.c new file +4 -0
+		buffer[4] = (this_part - 12 - 3) & 255;
+		buffer[5] = ((this_part - 12 - 3) >> 8) & 255;
+		buffer[6] = ((this_part - 12 - 3) >> 16) & 255;
+		buffer[7] = ((this_part - 12 - 3) >> 24) & 255;

We have excellent endianness conversion macros.

For splitting values up into the individual byte portions? I think this is far more obvious as to exactly what is going on, don't you?

No. Kernel code does not exist to show how to do endianness conversion. We have clearly labeled functions.

Quoted 6 lines
Quoted 4 lines

drivers/usb/class/usbtmc.c line 405

Quoted 2 lines
drivers/usb/class/usbtmc.c new file +2 -0
+		buffer[8] = data->TermCharEnabled * 2;
+		/* Use term character? */

smp_rmb(); /* we must make sure we don't read a stale terminator */

I'm not going to worry about races here, that's not a real issue.

There is no such thing as an ignorable race. On second thought you can take the mutex in the sysfs handlers. That will also do the job.

Quoted 7 lines
Quoted 3 lines

This and usbtmc_read() need a test for disconnection. Open() and disconnect are guarded in usbcore, read & write are not. By reference count you've made sure you have a valid device descriptor, but the device may have been reprobed.

If so, then struct usb_device would be different, right? Oh, I see, disconnect() using usbfs/sysfs. Bah, is it really something that happens in the real world? Oh well, I'll go fix this...

This is oopsable from user space.

Quoted 4 lines
Quoted 1 line

If you ignore an error return, be open about it.

I'm not? Should I print an error and then just continue on? Would that be sufficient?

Yes.

Quoted 10 lines
Quoted 8 lines

drivers/usb/class/usbtmc.c line 1055

Quoted 6 lines
drivers/usb/class/usbtmc.c new file +6 -0
+static void usbtmc_disconnect(struct usb_interface *intf)
+{
+	struct usbtmc_device_data *data;
+
+	dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
+

You must set a flag for read, write and ioctl.

Will do. Then I need to lock the flag with a mutex, right?

Yes. You can set the intf pointer to NULL. That's sort of idiomatic. And you should NULL intfdata.

Regards
Oliver

#12 lore raw
In reply to Greg KH
Quoted 3 lines · On Thursday 28 August 2008 01:47:20 Greg KH wrote:

Great, all done now.

Here's the updated version.

I've just installed this version.

Here's what I see so far :

The driver inserts well and when I connect my device it shows /dev/usbtmc0 with major 180 and minor 176.
It only creates one device (Stefan's driver created two) but I'm not sure if that has changed for a reason so just let you know.

When I disconnect my device, usbtmc0 will not be destroyed. After connecting the device a couple of times I have a lot of /dev/usbtmc.. files.

I would expect the following to work :
echo :*IDN?>/dev/usbtmc0
But it returns : No such device

Using echo and cat to test the device is quite convenient, but is this supposed to work yet ?

I checked /sys a bit and found that the endpoints are correctly found.

I also had an issue applying the patch (documentation and makefile). Not sure if that's related to my system though, I'll check that later.

regards,
Marcel

author
#13 lore raw
Quoted 5 lines · Marcel Janssen
Quoted 3 lines · On Thursday 28 August 2008 01:47:20 Greg KH wrote:

Great, all done now.

Here's the updated version.

Quoted 8 lines

I've just installed this version.

Here's what I see so far :

The driver inserts well and when I connect my device it shows /dev/usbtmc0 with major 180 and minor 176.
It only creates one device (Stefan's driver created two) but I'm not sure if that has changed for a reason so just let you know.

Yes, the old driver had a "control and debug " channel at minor 0, which isn't needed anymore.

Quoted 2 lines

When I disconnect my device, usbtmc0 will not be destroyed. After connecting the device a couple of times I have a lot of /dev/usbtmc.. files.

Hm, that's not good, something's wrong here.

Can you enable CONFIG_USB_DEBUG and rebuild the driver and send me the kernel log messages when you plug the device in and then remove it?

Quoted 3 lines

I would expect the following to work :
echo :*IDN?>/dev/usbtmc0
But it returns : No such device

Yeah, I would expect that to work as well. The debug information might be helpful here also.

Quoted 2 lines

Using echo and cat to test the device is quite convenient, but is this supposed to work yet ?

Yes it should.

Quoted 1 line

I checked /sys a bit and found that the endpoints are correctly found.

Good, how about the capability information? That should be in sysfs as well, does that show the proper values?

thanks,

greg k-h

#14 lore raw
In reply to Marcel Janssen

Hi all,

I haven't been able to follow all the enhancements you did to the driver, but I can comment on the ratioale behind the orignal driver.

The original driver used minor number 0 for "driver communication" (in contrast to "device communication"). The main purpose was to get a list of the USBTMC devices attached by doing a "cat /dev/usbtmc0" (which would point to minor number 0). The first device found would then use the next free minor number, usually 1. I believe the concept of "driver communication" has been removed from the driver, so a single minor number for a single device should be it.

The issue with using cat on the shell level is that it uses fread which has the (in this case) ugly behaviour of recalling the driver's read method until the full number of characters requested has been accumulated (or until zero characters are returned, indicating the end of file). With USBTMC instruments, this behavour is bad because the retry will not just return zero characters, it will cause a timeout with the associated error condition in the device. So, to enable the use of echo/cat, I added some fread handling to the driver (which catches the retries). I believe this also has been removed, so I assume cat/fread will not work (?).

BTW, removing these "features" is not a problem IMHO -- actually I have been working on a "scaled down" version of the driver myself. The reason is that I have been adding a user space library which hides the details of the USB driver and adds portability/exchangability between USB/LAN/serial devices. I expect most users to use this level and it is unlikely they will still want to use echo/cat.

Best regards,
Stefan

-----Original Message-----
From: Marcel Janssen [mailto:[email protected]]
Sent: Donnerstag, 28. August 2008 18:59
To: Greg KH
Cc: Alan Stern; Oliver Neukum; USB list; KOPP,STEFAN (A-Germany,ex1); Felipe Balbi; Kernel development list
Subject: Re: [PATCH] USB: add USB test and measurement class driver - round 2

Quoted 3 lines · On Thursday 28 August 2008 01:47:20 Greg KH wrote:

Great, all done now.

Here's the updated version.

I've just installed this version.

Here's what I see so far :

The driver inserts well and when I connect my device it shows /dev/usbtmc0 with major 180 and minor 176.
It only creates one device (Stefan's driver created two) but I'm not sure if that has changed for a reason so just let you know.

When I disconnect my device, usbtmc0 will not be destroyed. After connecting the device a couple of times I have a lot of /dev/usbtmc.. files.

I would expect the following to work :
echo :*IDN?>/dev/usbtmc0
But it returns : No such device

Using echo and cat to test the device is quite convenient, but is this supposed to work yet ?

I checked /sys a bit and found that the endpoints are correctly found.

I also had an issue applying the patch (documentation and makefile). Not sure if that's related to my system though, I'll check that later.

regards,
Marcel

#15 lore raw

Am Freitag 29 August 2008 08:57:54 schrieb [email protected]:

Quoted 1 line

The issue with using cat on the shell level is that it uses fread which has the (in this case) ugly behaviour of recalling the driver's read method until the full number of characters requested has been accumulated (or until zero characters are returned, indicating the end of file). With USBTMC instruments, this behavour is bad because the retry

This may be problematic. The driver is throwing away EOF in other words. Generally this is not a good idea. User space can no longer tell how long the reply was.

Regards
Oliver

#16 lore raw

Hi Oliver,

USBTMC has a different concept, the "termination character". If this is enabled and set to the appropriate character (usually /n), the read operation will be terminated by the device automatically. The last character in the response will be the termination character. Note, however, this capability is optional (not every vendor supports it) and even if it is supported, is might be deactivated (for good reason, e.g. for transfer of binary data).

If you are not using the termination character, the device will typically send its full output buffer contents. Usually, this works just fine because communication with the device is transactional, one query followed by a response, then another query, followed by another response, and so on.

The driver does return the number of characters received in both cases (with or without use of the term character feature).

The issue is more the habit of fread of "insisting" on receiving the full number of characters requested in the call to fread. Usually, you don't know how many characters will be returned by a USBTMC device, so you just ask for plenty and see how many you get. This works just fine with read(2). fread, unfortunately, retries the read by calling the driver's read entry point again. If you don't catch this retry and ask the USBTMC device for data, you will receive a timeout (unless there happens to be additional data in the device's output buffer).

In the original driver, I "simulated" an EOF by just returning 0 in the retry call to the read method. Not sure there's a better way of handling this issue because you can't rely on the termination character... However, I'm very open to ideas... As I said earlier, I avoided this whole issue by using read in my next layer.

Best regards
Stefan

-----Original Message-----
From: Oliver Neukum [mailto:[email protected]]
Sent: Freitag, 29. August 2008 09:47
To: KOPP,STEFAN (A-Germany,ex1)
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH] USB: add USB test and measurement class driver - round 2

Am Freitag 29 August 2008 08:57:54 schrieb [email protected]:

Quoted 6 lines

The issue with using cat on the shell level is that it uses fread which has the (in this case) ugly behaviour of recalling the driver's read method until the full number of characters requested has been accumulated (or until zero characters are returned, indicating the end of file). With USBTMC instruments, this behavour is bad because the retry

This may be problematic. The driver is throwing away EOF in other words. Generally this is not a good idea. User space can no longer tell how long the reply was.

Regards
Oliver

#17 lore raw

Am Freitag 29 August 2008 10:14:46 schrieb [email protected]:

Quoted 5 lines

USBTMC has a different concept, the "termination character". If this is enabled and set to the appropriate character (usually /n), the read operation will be terminated by the device automatically. The last character in the response will be the termination character. Note, however, this capability is optional (not every vendor supports it) and even if it is supported, is might be deactivated (for good reason, e.g. for transfer of binary data).

If you are not using the termination character, the device will typically send its full output buffer contents. Usually, this works just fine because communication with the device is transactional, one query followed by a response, then another query, followed by another response, and so on.

The driver does return the number of characters received in both cases (with or without use of the term character feature).

But how do you learn in user space how many characters the device actually did send? Using read() you'll only learn that the answer was shorter than you asked for. If you get the amount you expected the answer may have been as long as you expected or longer. How do you find out whether the answer was longer?

Regards
Oliver

#18 lore raw

I may be misinterpreting your message, but read(2) does return the number of bytes read, so if you ask for plenty of data, you can be sure you will get less than that. A device's response should only appear in the output buffer when it is complete. I have never seen a partial response being made available by an instrument.

USBTMC instruments are mostly controlled by short commands such as "CONF:FREQ 1.3E3\n", and they usually return short strings such as a measurement result or setting, again as a human-readable string (according to a standard called "SCPI"). It is common practice to ask for, let's say, 1000 bytes, what you really expect is maybe 10...30 bytes. And you can activate the term char handling if the device supports it.

For large amounts of data (e.g. when downloading a waveform), USBTMC instruments often use a "binblock", a concept taken from the good old GPIB (IEEE488) standard, where the data is preceeded by a header and the header indicates how many bytes are following. So you first read and interpret the header and then you know exactly how many bytes to expect in the body of the message.

Best regards,
Stefan

-----Original Message-----
From: Oliver Neukum [mailto:[email protected]]
Sent: Freitag, 29. August 2008 10:34
To: KOPP,STEFAN (A-Germany,ex1)
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH] USB: add USB test and measurement class driver - round 2

Am Freitag 29 August 2008 10:14:46 schrieb [email protected]:

Quoted 5 lines

USBTMC has a different concept, the "termination character". If this is enabled and set to the appropriate character (usually /n), the read operation will be terminated by the device automatically. The last character in the response will be the termination character. Note, however, this capability is optional (not every vendor supports it) and even if it is supported, is might be deactivated (for good reason, e.g. for transfer of binary data).

If you are not using the termination character, the device will typically send its full output buffer contents. Usually, this works just fine because communication with the device is transactional, one query followed by a response, then another query, followed by another response, and so on.

The driver does return the number of characters received in both cases (with or without use of the term character feature).

But how do you learn in user space how many characters the device actually did send? Using read() you'll only learn that the answer was shorter than you asked for. If you get the amount you expected the answer may have been as long as you expected or longer. How do you find out whether the answer was longer?

Regards
Oliver

#19 lore raw

Am Freitag 29 August 2008 11:13:04 schrieb [email protected]:

Quoted 1 line

For large amounts of data (e.g. when downloading a waveform), USBTMC instruments often use a "binblock", a concept taken from the good old GPIB (IEEE488) standard, where the data is preceeded by a header and the header indicates how many bytes are following. So you first read and interpret the header and then you know exactly how many bytes to expect in the body of the message.

Not really ideal. We should tell user space how much really was sent.

Regards
Oliver

author
#20 lore raw
In reply to [email protected]
Quoted 5 lines · On Fri, Aug 29, 2008 at 12:57:54AM -0600, [email protected] wrote:

Hi all,

I haven't been able to follow all the enhancements you did to the driver, but I can comment on the ratioale behind the orignal driver.

Quoted 7 lines

The original driver used minor number 0 for "driver communication" (in contrast to "device communication"). The main purpose was to get a list of the USBTMC devices attached by doing a "cat /dev/usbtmc0" (which would point to minor number 0). The first device found would then use the next free minor number, usually 1. I believe the concept of "driver communication" has been removed from the driver, so a single minor number for a single device should be it.

cating that file was sending out a text file to userspace that needed to be parsed, like a /proc file. Now you can just look at all of the devices in /sys/class/usb/usbtmc* to see all of the different devices in the system.

Quoted 10 lines

The issue with using cat on the shell level is that it uses fread which has the (in this case) ugly behaviour of recalling the driver's read method until the full number of characters requested has been accumulated (or until zero characters are returned, indicating the end of file). With USBTMC instruments, this behavour is bad because the retry will not just return zero characters, it will cause a timeout with the associated error condition in the device. So, to enable the use of echo/cat, I added some fread handling to the driver (which catches the retries). I believe this also has been removed, so I assume cat/fread will not work (?).

I do not know, but we do not do wierd things in the kernel just because of broken userspace programs. This logic should be done in userspace, and programs should look at the return value of read() and handle it properly. Otherwise it is a bug.

thanks,

greg k-h

#21 lore raw
Quoted 11 lines · On Friday 29 August 2008 16:39:02 Greg KH wrote:
Quoted 10 lines

The issue with using cat on the shell level is that it uses fread which has the (in this case) ugly behaviour of recalling the driver's read method until the full number of characters requested has been accumulated (or until zero characters are returned, indicating the end of file). With USBTMC instruments, this behavour is bad because the retry will not just return zero characters, it will cause a timeout with the associated error condition in the device. So, to enable the use of echo/cat, I added some fread handling to the driver (which catches the retries). I believe this also has been removed, so I assume cat/fread will not work (?).

Quoted 4 lines

I do not know, but we do not do wierd things in the kernel just because of broken userspace programs. This logic should be done in userspace, and programs should look at the return value of read() and handle it properly. Otherwise it is a bug.

I don't think this is broken in user space. The problem is that when you issue a measurement command it is not known how many bytes it will return. This is probably due to ASCII output being very common in T&M devices instead of raw data (int, float etc). The ASCII formatting is done in the device and this returns just a string which may or may not be terminated by the term char. This is of course not true for all T&M devices, but the majority works this way.

I admit that the above produces a lot of overhead, but it's just a fact that T&M devices work this way, including ours for most of their data processing (not all though).

I think the USBTMC spec is quite clear on how it should be implemented on messaging level. Basically when you issue the command "*IDN?" the device will process this and return the device ID string. The length of this string is set in the TransferSize of the 12 byte header that the device returns. The problem when you issue a read command is that the read command does not yet know how much data to expect. It should issue the REQUEST_DEV_DEP_MSG_IN first and set the TransferSize value high enough.
In the USBTMC_488 extension you find an example (chapter 3.3.1 page 7) that shows the REQUEST_DEV_DEP_MSG_IN TransferSize being set to 64 although the actual data being returned from the device is less (only 36 bytes).

What you do see in practice is that when someone would issue a read command and asking for less bytes than are available is that the user program may handle this as a warning telling the user that he did not request all available data.

Stefan's driver works exactly the way I would expect from a users point of view. Whether the implementation can be improved is another issue, but the behaviour is correct and compliant with other usbtmc drivers on other platforms.

Regards,
Marcel

author
#22 lore raw
Quoted 25 lines · Marcel Janssen
Quoted 11 lines · On Friday 29 August 2008 16:39:02 Greg KH wrote:
Quoted 10 lines

The issue with using cat on the shell level is that it uses fread which has the (in this case) ugly behaviour of recalling the driver's read method until the full number of characters requested has been accumulated (or until zero characters are returned, indicating the end of file). With USBTMC instruments, this behavour is bad because the retry will not just return zero characters, it will cause a timeout with the associated error condition in the device. So, to enable the use of echo/cat, I added some fread handling to the driver (which catches the retries). I believe this also has been removed, so I assume cat/fread will not work (?).

Quoted 4 lines

I do not know, but we do not do wierd things in the kernel just because of broken userspace programs. This logic should be done in userspace, and programs should look at the return value of read() and handle it properly. Otherwise it is a bug.

I don't think this is broken in user space. The problem is that when you issue a measurement command it is not known how many bytes it will return. This is probably due to ASCII output being very common in T&M devices instead of raw data (int, float etc). The ASCII formatting is done in the device and this returns just a string which may or may not be terminated by the term char. This is of course not true for all T&M devices, but the majority works this way.

Quoted 3 lines

I admit that the above produces a lot of overhead, but it's just a fact that T&M devices work this way, including ours for most of their data processing (not all though).

How is this overhead in userspace, just do something like the following:

	char big_buffer[16000];		/* bigger than any possible request */
	size = read(file_desc, &big_buffer[0], sizeof(big_buffer));

and size is the amount of data you actually read from the device, i.e. one request.

Quoted 11 lines

I think the USBTMC spec is quite clear on how it should be implemented on messaging level. Basically when you issue the command "*IDN?" the device will process this and return the device ID string. The length of this string is set in the TransferSize of the 12 byte header that the device returns. The problem when you issue a read command is that the read command does not yet know how much data to expect. It should issue the REQUEST_DEV_DEP_MSG_IN first and set the TransferSize value high enough.
In the USBTMC_488 extension you find an example (chapter 3.3.1 page 7) that shows the REQUEST_DEV_DEP_MSG_IN TransferSize being set to 64 although the actual data being returned from the device is less (only 36 bytes).

Quoted 4 lines

What you do see in practice is that when someone would issue a read command and asking for less bytes than are available is that the user program may handle this as a warning telling the user that he did not request all available data.

Then that's a userspace bug, don't do that in your program that reads from these types of devices :)

Quoted 4 lines

Stefan's driver works exactly the way I would expect from a users point of view. Whether the implementation can be improved is another issue, but the behaviour is correct and compliant with other usbtmc drivers on other platforms.

But it's not compliant with the "standard" way of using a file descriptor in unix, and might break some POSIX requirements as well (I'm not a good POSIX follower, so I don't know for sure...)

As this is trivial to handle in userspace, and requires no additional wierd code in the kernel driver, I don't see this as something we should change the driver for.

thanks,

greg k-h