diff options
Diffstat (limited to 'drivers/media/rc/imon.c')
| -rw-r--r-- | drivers/media/rc/imon.c | 31 | 
1 files changed, 27 insertions, 4 deletions
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index ebd68edf5b2..8fc0f081b47 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -46,7 +46,7 @@  #define MOD_AUTHOR	"Jarod Wilson <jarod@wilsonet.com>"  #define MOD_DESC	"Driver for SoundGraph iMON MultiMedia IR/Display"  #define MOD_NAME	"imon" -#define MOD_VERSION	"0.9.2" +#define MOD_VERSION	"0.9.3"  #define DISPLAY_MINOR_BASE	144  #define DEVICE_NAME	"lcd%d" @@ -460,8 +460,9 @@ static int display_close(struct inode *inode, struct file *file)  }  /** - * Sends a packet to the device -- this function must be called - * with ictx->lock held. + * Sends a packet to the device -- this function must be called with + * ictx->lock held, or its unlock/lock sequence while waiting for tx + * to complete can/will lead to a deadlock.   */  static int send_packet(struct imon_context *ictx)  { @@ -991,12 +992,21 @@ static void imon_touch_display_timeout(unsigned long data)   * the iMON remotes, and those used by the Windows MCE remotes (which is   * really just RC-6), but only one or the other at a time, as the signals   * are decoded onboard the receiver. + * + * This function gets called two different ways, one way is from + * rc_register_device, for initial protocol selection/setup, and the other is + * via a userspace-initiated protocol change request, either by direct sysfs + * prodding or by something like ir-keytable. In the rc_register_device case, + * the imon context lock is already held, but when initiated from userspace, + * it is not, so we must acquire it prior to calling send_packet, which + * requires that the lock is held.   */  static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)  {  	int retval;  	struct imon_context *ictx = rc->priv;  	struct device *dev = ictx->dev; +	bool unlock = false;  	unsigned char ir_proto_packet[] = {  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 }; @@ -1029,6 +1039,11 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)  	memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet)); +	if (!mutex_is_locked(&ictx->lock)) { +		unlock = true; +		mutex_lock(&ictx->lock); +	} +  	retval = send_packet(ictx);  	if (retval)  		goto out; @@ -1037,6 +1052,9 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)  	ictx->pad_mouse = false;  out: +	if (unlock) +		mutex_unlock(&ictx->lock); +  	return retval;  } @@ -2134,6 +2152,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)  		goto rdev_setup_failed;  	} +	mutex_unlock(&ictx->lock);  	return ictx;  rdev_setup_failed: @@ -2205,6 +2224,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,  		goto urb_submit_failed;  	} +	mutex_unlock(&ictx->lock);  	return ictx;  urb_submit_failed: @@ -2299,6 +2319,8 @@ static int __devinit imon_probe(struct usb_interface *interface,  	usb_set_intfdata(interface, ictx);  	if (ifnum == 0) { +		mutex_lock(&ictx->lock); +  		if (product == 0xffdc && ictx->rf_device) {  			sysfs_err = sysfs_create_group(&interface->dev.kobj,  						       &imon_rf_attr_group); @@ -2309,13 +2331,14 @@ static int __devinit imon_probe(struct usb_interface *interface,  		if (ictx->display_supported)  			imon_init_display(ictx, interface); + +		mutex_unlock(&ictx->lock);  	}  	dev_info(dev, "iMON device (%04x:%04x, intf%d) on "  		 "usb<%d:%d> initialized\n", vendor, product, ifnum,  		 usbdev->bus->busnum, usbdev->devnum); -	mutex_unlock(&ictx->lock);  	mutex_unlock(&driver_lock);  	return 0;  |