From: Henk <[email-masked]>

- Added a rwsemaphore to yealink.c
  Hopefully this does sysfs write serialisation & fixes potential unload races

- Fixed a small typo in drinvers/usb/input/Kconfig

Cc: Dmitry Torokhov <[email-masked]>
Cc: Greg KH <[email-masked]>
Cc: Vojtech Pavlik <[email-masked]>
Signed-off-by: Henk <[email-masked]>
Signed-off-by: Andrew Morton <[email-masked]>
---

 drivers/usb/input/Kconfig   |    2 -
 drivers/usb/input/yealink.c |   72 ++++++++++++++++++++++++++++++++------------
 2 files changed, 54 insertions(+), 20 deletions(-)

diff -puN drivers/usb/input/Kconfig~yealink-updates-0701 drivers/usb/input/Kconfig
--- devel/drivers/usb/input/Kconfig~yealink-updates-0701	2005-07-06 01:19:50.000000000 -0700
+++ devel-akpm/drivers/usb/input/Kconfig	2005-07-06 01:19:50.000000000 -0700
@@ -234,7 +234,7 @@ config USB_YEALINK
 	tristate "Yealink usb-p1k voip phone"
 	depends on USB && INPUT && EXPERIMENTAL
 	---help---
-	  Say Y here if you want to enable keyboard and LDC functions of the
+	  Say Y here if you want to enable keyboard and LCD functions of the
 	  Yealink usb-p1k usb phones. The audio part is enabled by the generic
 	  usb sound driver, so you might want to enable that as well.
 
diff -puN drivers/usb/input/yealink.c~yealink-updates-0701 drivers/usb/input/yealink.c
--- devel/drivers/usb/input/yealink.c~yealink-updates-0701	2005-07-06 01:19:50.000000000 -0700
+++ devel-akpm/drivers/usb/input/yealink.c	2005-07-06 01:19:50.000000000 -0700
@@ -44,6 +44,7 @@
  *   20050531 henk	Added led, LCD, dialtone and sysfs interface.
  *   20050610 henk	Cleanups, make it ready for public consumption.
  *   20050630 henk	Cleanups, fixes in response to comments.
+ *   20050701 henk	sysfs write serialisation, fix potential unload races
  */
 
 #include <linux/config.h>
@@ -52,12 +53,12 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
-#include <linux/smp_lock.h>
+#include <linux/rwsem.h>
 #include <linux/usb.h>
 
 #include "map_to_7segment.h"
 
-#define DRIVER_VERSION "yld-20050630"
+#define DRIVER_VERSION "yld-20050701"
 #define DRIVER_AUTHOR "Henk Vergonet"
 #define DRIVER_DESC "Yealink phone driver"
 
@@ -502,6 +503,8 @@ static void input_close(struct input_dev
  * sysfs interface
  ******************************************************************************/
 
+static DECLARE_RWSEM(sysfs_rwsema);
+
 /* Interface to the 7-segments translation table aka. char set.
  */
 static ssize_t show_map(struct device *dev, struct device_attribute *attr,
@@ -530,9 +533,16 @@ static ssize_t store_map(struct device *
  */
 static ssize_t show_line(struct device *dev, char *buf, int a, int b)
 {
-	struct yealink_dev *yld = dev_get_drvdata(dev);
+	struct yealink_dev *yld;
 	int i = 0;
 
+	down_read(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld == NULL) {
+		up_read(&sysfs_rwsema);
+		return 0;
+	}
+
 	for (i = a; i < b; i++)
 		*buf++ = lcdMap[i].type;
 	*buf++ = '\n';
@@ -540,6 +550,8 @@ static ssize_t show_line(struct device *
 		*buf++ = yld->lcdMap[i];
 	*buf++ = '\n';
 	*buf = 0;
+
+	up_read(&sysfs_rwsema);
 	return 3 + ((b - a) << 1);
 }
 
@@ -571,13 +583,19 @@ static ssize_t show_line3(struct device 
 static ssize_t store_line(struct device *dev, const char *buf, size_t count,
 		int el, size_t len)
 {
-	struct yealink_dev *yld = dev_get_drvdata(dev);
+	struct yealink_dev *yld;
 	int i;
 
-	if (len > count)
-		len = count;
-	for (i = 0; i < len; i++)
-		setChar(yld, el++, buf[i]);
+	down_write(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld) {
+		if (len > count)
+			len = count;
+		for (i = 0; i < len; i++)
+			setChar(yld, el++, buf[i]);
+	}
+
+	up_write(&sysfs_rwsema);
 	return count;
 }
 
@@ -607,8 +625,16 @@ static ssize_t store_line3(struct device
 static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
 			char *buf)
 {
-	struct yealink_dev *yld = dev_get_drvdata(dev);
+	struct yealink_dev *yld;
 	int i, ret = 1;
+
+	down_read(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (!yld) {
+		up_read(&sysfs_rwsema);
+		return 0;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
 		if (lcdMap[i].type != '.')
 			continue;
@@ -616,6 +642,7 @@ static ssize_t get_icons(struct device *
 				yld->lcdMap[i] == ' ' ? "  " : "on",
 				lcdMap[i].u.p.name);
 	}
+	up_read(&sysfs_rwsema);
 	return ret;
 }
 
@@ -623,19 +650,22 @@ static ssize_t get_icons(struct device *
 static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
 			int chr)
 {
-	struct yealink_dev *yld = dev_get_drvdata(dev);
+	struct yealink_dev *yld;
 	int i;
 
-	if (yld == NULL)
-		return count;
-	for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
-		if (lcdMap[i].type != '.')
-			continue;
-		if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) {
-			setChar(yld, i, chr);
-			break;
+	down_write(&sysfs_rwsema);
+	yld = dev_get_drvdata(dev);
+	if (yld) {
+		for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+			if (lcdMap[i].type != '.')
+				continue;
+			if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) {
+				setChar(yld, i, chr);
+				break;
+			}
 		}
 	}
+	up_write(&sysfs_rwsema);
 	return count;
 }
 
@@ -735,10 +765,14 @@ static int usb_cleanup(struct yealink_de
 
 static void usb_disconnect(struct usb_interface *intf)
 {
-	struct yealink_dev *yld = usb_get_intfdata(intf);
+	struct yealink_dev *yld;
 
+	down_write(&sysfs_rwsema);
+	yld = usb_get_intfdata(intf);
 	sysfs_remove_group(&intf->dev.kobj, &yld_attr_group);
 	usb_set_intfdata(intf, NULL);
+	up_write(&sysfs_rwsema);
+
 	usb_cleanup(yld, 0);
 }
 
_