diff options
Diffstat (limited to 'drivers/usb/class/usbtmc.c')
| -rw-r--r-- | drivers/usb/class/usbtmc.c | 320 | 
1 files changed, 279 insertions, 41 deletions
| diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 529295a17579..83ffa5a14c3d 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -18,6 +18,7 @@  #include <linux/poll.h>  #include <linux/mutex.h>  #include <linux/usb.h> +#include <linux/compat.h>  #include <linux/usb/tmc.h> @@ -30,6 +31,8 @@   */  #define USBTMC_SIZE_IOBUFFER	2048 +/* Minimum USB timeout (in milliseconds) */ +#define USBTMC_MIN_TIMEOUT	100  /* Default USB timeout (in milliseconds) */  #define USBTMC_TIMEOUT		5000 @@ -67,6 +70,7 @@ struct usbtmc_device_data {  	const struct usb_device_id *id;  	struct usb_device *usb_dev;  	struct usb_interface *intf; +	struct list_head file_list;  	unsigned int bulk_in;  	unsigned int bulk_out; @@ -87,7 +91,6 @@ struct usbtmc_device_data {  	int            iin_interval;  	struct urb    *iin_urb;  	u16            iin_wMaxPacketSize; -	atomic_t       srq_asserted;  	/* coalesced usb488_caps from usbtmc_dev_capabilities */  	__u8 usb488_caps; @@ -104,9 +107,25 @@ struct usbtmc_device_data {  	struct mutex io_mutex;	/* only one i/o function running at a time */  	wait_queue_head_t waitq;  	struct fasync_struct *fasync; +	spinlock_t dev_lock; /* lock for file_list */  };  #define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) +/* + * This structure holds private data for each USBTMC file handle. + */ +struct usbtmc_file_data { +	struct usbtmc_device_data *data; +	struct list_head file_elem; + +	u32            timeout; +	u8             srq_byte; +	atomic_t       srq_asserted; +	u8             eom_val; +	u8             term_char; +	bool           term_char_enabled; +}; +  /* Forward declarations */  static struct usb_driver usbtmc_driver; @@ -122,7 +141,7 @@ static int usbtmc_open(struct inode *inode, struct file *filp)  {  	struct usb_interface *intf;  	struct usbtmc_device_data *data; -	int retval = 0; +	struct usbtmc_file_data *file_data;  	intf = usb_find_interface(&usbtmc_driver, iminor(inode));  	if (!intf) { @@ -130,21 +149,51 @@ static int usbtmc_open(struct inode *inode, struct file *filp)  		return -ENODEV;  	} +	file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); +	if (!file_data) +		return -ENOMEM; +  	data = usb_get_intfdata(intf);  	/* Protect reference to data from file structure until release */  	kref_get(&data->kref); +	mutex_lock(&data->io_mutex); +	file_data->data = data; + +	/* copy default values from device settings */ +	file_data->timeout = USBTMC_TIMEOUT; +	file_data->term_char = data->TermChar; +	file_data->term_char_enabled = data->TermCharEnabled; +	file_data->eom_val = 1; + +	INIT_LIST_HEAD(&file_data->file_elem); +	spin_lock_irq(&data->dev_lock); +	list_add_tail(&file_data->file_elem, &data->file_list); +	spin_unlock_irq(&data->dev_lock); +	mutex_unlock(&data->io_mutex); +  	/* Store pointer in file structure's private data field */ -	filp->private_data = data; +	filp->private_data = file_data; -	return retval; +	return 0;  }  static int usbtmc_release(struct inode *inode, struct file *file)  { -	struct usbtmc_device_data *data = file->private_data; +	struct usbtmc_file_data *file_data = file->private_data; -	kref_put(&data->kref, usbtmc_delete); +	/* prevent IO _AND_ usbtmc_interrupt */ +	mutex_lock(&file_data->data->io_mutex); +	spin_lock_irq(&file_data->data->dev_lock); + +	list_del(&file_data->file_elem); + +	spin_unlock_irq(&file_data->data->dev_lock); +	mutex_unlock(&file_data->data->io_mutex); + +	kref_put(&file_data->data->kref, usbtmc_delete); +	file_data->data = NULL; +	kfree(file_data);  	return 0;  } @@ -369,10 +418,12 @@ exit:  	return rv;  } -static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, +static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,  				void __user *arg)  { +	struct usbtmc_device_data *data = file_data->data;  	struct device *dev = &data->intf->dev; +	int srq_asserted = 0;  	u8 *buffer;  	u8 tag;  	__u8 stb; @@ -381,15 +432,25 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,  	dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",  		data->iin_ep_present); +	spin_lock_irq(&data->dev_lock); +	srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted); +	if (srq_asserted) { +		/* a STB with SRQ is already received */ +		stb = file_data->srq_byte; +		spin_unlock_irq(&data->dev_lock); +		rv = put_user(stb, (__u8 __user *)arg); +		dev_dbg(dev, "stb:0x%02x with srq received %d\n", +			(unsigned int)stb, rv); +		return rv; +	} +	spin_unlock_irq(&data->dev_lock); +  	buffer = kmalloc(8, GFP_KERNEL);  	if (!buffer)  		return -ENOMEM;  	atomic_set(&data->iin_data_valid, 0); -	/* must issue read_stb before using poll or select */ -	atomic_set(&data->srq_asserted, 0); -  	rv = usb_control_msg(data->usb_dev,  			usb_rcvctrlpipe(data->usb_dev, 0),  			USBTMC488_REQUEST_READ_STATUS_BYTE, @@ -412,7 +473,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,  		rv = wait_event_interruptible_timeout(  			data->waitq,  			atomic_read(&data->iin_data_valid) != 0, -			USBTMC_TIMEOUT); +			file_data->timeout);  		if (rv < 0) {  			dev_dbg(dev, "wait interrupted %d\n", rv);  			goto exit; @@ -420,7 +481,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,  		if (rv == 0) {  			dev_dbg(dev, "wait timed out\n"); -			rv = -ETIME; +			rv = -ETIMEDOUT;  			goto exit;  		} @@ -435,9 +496,8 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,  		stb = buffer[2];  	} -	rv = copy_to_user(arg, &stb, sizeof(stb)); -	if (rv) -		rv = -EFAULT; +	rv = put_user(stb, (__u8 __user *)arg); +	dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)stb, rv);   exit:  	/* bump interrupt bTag */ @@ -506,6 +566,51 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,  }  /* + * Sends a TRIGGER Bulk-OUT command message + * See the USBTMC-USB488 specification, Table 2. + * + * Also updates bTag_last_write. + */ +static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data) +{ +	struct usbtmc_device_data *data = file_data->data; +	int retval; +	u8 *buffer; +	int actual; + +	buffer = kzalloc(USBTMC_HEADER_SIZE, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	buffer[0] = 128; +	buffer[1] = data->bTag; +	buffer[2] = ~data->bTag; + +	retval = usb_bulk_msg(data->usb_dev, +			      usb_sndbulkpipe(data->usb_dev, +					      data->bulk_out), +			      buffer, USBTMC_HEADER_SIZE, +			      &actual, file_data->timeout); + +	/* Store bTag (in case we need to abort) */ +	data->bTag_last_write = data->bTag; + +	/* Increment bTag -- and increment again if zero */ +	data->bTag++; +	if (!data->bTag) +		data->bTag++; + +	kfree(buffer); +	if (retval < 0) { +		dev_err(&data->intf->dev, "%s returned %d\n", +			__func__, retval); +		return retval; +	} + +	return 0; +} + +/*   * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-OUT endpoint.   * @transfer_size: number of bytes to request from the device.   * @@ -513,8 +618,10 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,   *   * Also updates bTag_last_write.   */ -static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t transfer_size) +static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data, +				       size_t transfer_size)  { +	struct usbtmc_device_data *data = file_data->data;  	int retval;  	u8 *buffer;  	int actual; @@ -533,9 +640,9 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t  	buffer[5] = transfer_size >> 8;  	buffer[6] = transfer_size >> 16;  	buffer[7] = transfer_size >> 24; -	buffer[8] = data->TermCharEnabled * 2; +	buffer[8] = file_data->term_char_enabled * 2;  	/* Use term character? */ -	buffer[9] = data->TermChar; +	buffer[9] = file_data->term_char;  	buffer[10] = 0; /* Reserved */  	buffer[11] = 0; /* Reserved */ @@ -543,7 +650,8 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t  	retval = usb_bulk_msg(data->usb_dev,  			      usb_sndbulkpipe(data->usb_dev,  					      data->bulk_out), -			      buffer, USBTMC_HEADER_SIZE, &actual, USBTMC_TIMEOUT); +			      buffer, USBTMC_HEADER_SIZE, +			      &actual, file_data->timeout);  	/* Store bTag (in case we need to abort) */  	data->bTag_last_write = data->bTag; @@ -565,6 +673,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t  static ssize_t usbtmc_read(struct file *filp, char __user *buf,  			   size_t count, loff_t *f_pos)  { +	struct usbtmc_file_data *file_data;  	struct usbtmc_device_data *data;  	struct device *dev;  	u32 n_characters; @@ -576,7 +685,8 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,  	size_t this_part;  	/* Get pointer to private data structure */ -	data = filp->private_data; +	file_data = filp->private_data; +	data = file_data->data;  	dev = &data->intf->dev;  	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); @@ -591,7 +701,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,  	dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count); -	retval = send_request_dev_dep_msg_in(data, count); +	retval = send_request_dev_dep_msg_in(file_data, count);  	if (retval < 0) {  		if (data->auto_abort) @@ -610,7 +720,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,  				      usb_rcvbulkpipe(data->usb_dev,  						      data->bulk_in),  				      buffer, USBTMC_SIZE_IOBUFFER, &actual, -				      USBTMC_TIMEOUT); +				      file_data->timeout);  		dev_dbg(dev, "usb_bulk_msg: retval(%u), done(%zu), remaining(%zu), actual(%d)\n", retval, done, remaining, actual); @@ -721,6 +831,7 @@ exit:  static ssize_t usbtmc_write(struct file *filp, const char __user *buf,  			    size_t count, loff_t *f_pos)  { +	struct usbtmc_file_data *file_data;  	struct usbtmc_device_data *data;  	u8 *buffer;  	int retval; @@ -730,7 +841,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,  	int done;  	int this_part; -	data = filp->private_data; +	file_data = filp->private_data; +	data = file_data->data;  	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);  	if (!buffer) @@ -751,7 +863,7 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,  			buffer[8] = 0;  		} else {  			this_part = remaining; -			buffer[8] = 1; +			buffer[8] = file_data->eom_val;  		}  		/* Setup IO buffer for DEV_DEP_MSG_OUT message */ @@ -781,7 +893,7 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,  					      usb_sndbulkpipe(data->usb_dev,  							      data->bulk_out),  					      buffer, n_bytes, -					      &actual, USBTMC_TIMEOUT); +					      &actual, file_data->timeout);  			if (retval != 0)  				break;  			n_bytes -= actual; @@ -1138,12 +1250,91 @@ exit:  	return rv;  } +/* + * Get the usb timeout value + */ +static int usbtmc_ioctl_get_timeout(struct usbtmc_file_data *file_data, +				void __user *arg) +{ +	u32 timeout; + +	timeout = file_data->timeout; + +	return put_user(timeout, (__u32 __user *)arg); +} + +/* + * Set the usb timeout value + */ +static int usbtmc_ioctl_set_timeout(struct usbtmc_file_data *file_data, +				void __user *arg) +{ +	u32 timeout; + +	if (get_user(timeout, (__u32 __user *)arg)) +		return -EFAULT; + +	/* Note that timeout = 0 means +	 * MAX_SCHEDULE_TIMEOUT in usb_control_msg +	 */ +	if (timeout < USBTMC_MIN_TIMEOUT) +		return -EINVAL; + +	file_data->timeout = timeout; + +	return 0; +} + +/* + * enables/disables sending EOM on write + */ +static int usbtmc_ioctl_eom_enable(struct usbtmc_file_data *file_data, +				void __user *arg) +{ +	u8 eom_enable; + +	if (copy_from_user(&eom_enable, arg, sizeof(eom_enable))) +		return -EFAULT; + +	if (eom_enable > 1) +		return -EINVAL; + +	file_data->eom_val = eom_enable; + +	return 0; +} + +/* + * Configure termination character for read() + */ +static int usbtmc_ioctl_config_termc(struct usbtmc_file_data *file_data, +				void __user *arg) +{ +	struct usbtmc_termchar termc; + +	if (copy_from_user(&termc, arg, sizeof(termc))) +		return -EFAULT; + +	if ((termc.term_char_enabled > 1) || +		(termc.term_char_enabled && +		!(file_data->data->capabilities.device_capabilities & 1))) +		return -EINVAL; + +	file_data->term_char = termc.term_char; +	file_data->term_char_enabled = termc.term_char_enabled; + +	return 0; +} +  static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  { +	struct usbtmc_file_data *file_data;  	struct usbtmc_device_data *data;  	int retval = -EBADRQC; -	data = file->private_data; +	file_data = file->private_data; +	data = file_data->data; +  	mutex_lock(&data->io_mutex);  	if (data->zombie) {  		retval = -ENODEV; @@ -1175,6 +1366,26 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  		retval = usbtmc_ioctl_abort_bulk_in(data);  		break; +	case USBTMC_IOCTL_GET_TIMEOUT: +		retval = usbtmc_ioctl_get_timeout(file_data, +						  (void __user *)arg); +		break; + +	case USBTMC_IOCTL_SET_TIMEOUT: +		retval = usbtmc_ioctl_set_timeout(file_data, +						  (void __user *)arg); +		break; + +	case USBTMC_IOCTL_EOM_ENABLE: +		retval = usbtmc_ioctl_eom_enable(file_data, +						 (void __user *)arg); +		break; + +	case USBTMC_IOCTL_CONFIG_TERMCHAR: +		retval = usbtmc_ioctl_config_termc(file_data, +						   (void __user *)arg); +		break; +  	case USBTMC488_IOCTL_GET_CAPS:  		retval = copy_to_user((void __user *)arg,  				&data->usb488_caps, @@ -1184,7 +1395,8 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  		break;  	case USBTMC488_IOCTL_READ_STB: -		retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg); +		retval = usbtmc488_ioctl_read_stb(file_data, +						  (void __user *)arg);  		break;  	case USBTMC488_IOCTL_REN_CONTROL: @@ -1201,6 +1413,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  		retval = usbtmc488_ioctl_simple(data, (void __user *)arg,  						USBTMC488_REQUEST_LOCAL_LOCKOUT);  		break; + +	case USBTMC488_IOCTL_TRIGGER: +		retval = usbtmc488_ioctl_trigger(file_data); +		break;  	}  skip_io_on_zombie: @@ -1210,14 +1426,15 @@ skip_io_on_zombie:  static int usbtmc_fasync(int fd, struct file *file, int on)  { -	struct usbtmc_device_data *data = file->private_data; +	struct usbtmc_file_data *file_data = file->private_data; -	return fasync_helper(fd, file, on, &data->fasync); +	return fasync_helper(fd, file, on, &file_data->data->fasync);  }  static __poll_t usbtmc_poll(struct file *file, poll_table *wait)  { -	struct usbtmc_device_data *data = file->private_data; +	struct usbtmc_file_data *file_data = file->private_data; +	struct usbtmc_device_data *data = file_data->data;  	__poll_t mask;  	mutex_lock(&data->io_mutex); @@ -1229,7 +1446,7 @@ static __poll_t usbtmc_poll(struct file *file, poll_table *wait)  	poll_wait(file, &data->waitq, wait); -	mask = (atomic_read(&data->srq_asserted)) ? EPOLLIN | EPOLLRDNORM : 0; +	mask = (atomic_read(&file_data->srq_asserted)) ? EPOLLPRI : 0;  no_poll:  	mutex_unlock(&data->io_mutex); @@ -1243,6 +1460,9 @@ static const struct file_operations fops = {  	.open		= usbtmc_open,  	.release	= usbtmc_release,  	.unlocked_ioctl	= usbtmc_ioctl, +#ifdef CONFIG_COMPAT +	.compat_ioctl	= usbtmc_ioctl, +#endif  	.fasync         = usbtmc_fasync,  	.poll           = usbtmc_poll,  	.llseek		= default_llseek, @@ -1276,15 +1496,33 @@ static void usbtmc_interrupt(struct urb *urb)  		}  		/* check for SRQ notification */  		if (data->iin_buffer[0] == 0x81) { +			unsigned long flags; +			struct list_head *elem; +  			if (data->fasync)  				kill_fasync(&data->fasync, -					SIGIO, POLL_IN); +					SIGIO, POLL_PRI); -			atomic_set(&data->srq_asserted, 1); -			wake_up_interruptible(&data->waitq); +			spin_lock_irqsave(&data->dev_lock, flags); +			list_for_each(elem, &data->file_list) { +				struct usbtmc_file_data *file_data; + +				file_data = list_entry(elem, +						       struct usbtmc_file_data, +						       file_elem); +				file_data->srq_byte = data->iin_buffer[1]; +				atomic_set(&file_data->srq_asserted, 1); +			} +			spin_unlock_irqrestore(&data->dev_lock, flags); + +			dev_dbg(dev, "srq received bTag %x stb %x\n", +				(unsigned int)data->iin_buffer[0], +				(unsigned int)data->iin_buffer[1]); +			wake_up_interruptible_all(&data->waitq);  			goto exit;  		} -		dev_warn(dev, "invalid notification: %x\n", data->iin_buffer[0]); +		dev_warn(dev, "invalid notification: %x\n", +			 data->iin_buffer[0]);  		break;  	case -EOVERFLOW:  		dev_err(dev, "overflow with length %d, actual length is %d\n", @@ -1295,6 +1533,7 @@ static void usbtmc_interrupt(struct urb *urb)  	case -ESHUTDOWN:  	case -EILSEQ:  	case -ETIME: +	case -EPIPE:  		/* urb terminated, clean up */  		dev_dbg(dev, "urb terminated, status: %d\n", status);  		return; @@ -1339,7 +1578,9 @@ static int usbtmc_probe(struct usb_interface *intf,  	mutex_init(&data->io_mutex);  	init_waitqueue_head(&data->waitq);  	atomic_set(&data->iin_data_valid, 0); -	atomic_set(&data->srq_asserted, 0); +	INIT_LIST_HEAD(&data->file_list); +	spin_lock_init(&data->dev_lock); +  	data->zombie = 0;  	/* Initialize USBTMC bTag and other fields */ @@ -1442,17 +1683,14 @@ err_put:  static void usbtmc_disconnect(struct usb_interface *intf)  { -	struct usbtmc_device_data *data; - -	dev_dbg(&intf->dev, "usbtmc_disconnect called\n"); +	struct usbtmc_device_data *data  = usb_get_intfdata(intf); -	data = usb_get_intfdata(intf);  	usb_deregister_dev(intf, &usbtmc_class);  	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);  	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);  	mutex_lock(&data->io_mutex);  	data->zombie = 1; -	wake_up_all(&data->waitq); +	wake_up_interruptible_all(&data->waitq);  	mutex_unlock(&data->io_mutex);  	usbtmc_free_int(data);  	kref_put(&data->kref, usbtmc_delete); |