diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-10-17 11:51:12 -0400 |
---|---|---|
committer | Jean Delvare <khali@mahadeva.delvare> | 2008-10-17 11:51:12 -0400 |
commit | ee4cd32ee8a68e22081f698602e4315fb4272853 (patch) | |
tree | e44f309816eeb26727a23180ecede53f372705cd | |
parent | 0a02002268bf624a8b0eaf3b4eb5c4207bd80d8b (diff) |
hwmon: (ams) Fix locking issues
Use a separate mutex to serialize input device creation/removal,
otheriwse we deadlock if we try to remove input device while it is
being polled. Also do not take ams_info.lock when it is not needed.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
-rw-r--r-- | drivers/hwmon/ams/ams-core.c | 40 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-i2c.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-input.c | 59 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-pmu.c | 18 |
4 files changed, 56 insertions, 65 deletions
diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index fbefa82a015c..9b4a0e7a564f 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c | |||
@@ -223,34 +223,28 @@ int __init ams_init(void) | |||
223 | 223 | ||
224 | void ams_exit(void) | 224 | void ams_exit(void) |
225 | { | 225 | { |
226 | mutex_lock(&ams_info.lock); | 226 | /* Remove input device */ |
227 | 227 | ams_input_exit(); | |
228 | if (ams_info.has_device) { | ||
229 | /* Remove input device */ | ||
230 | ams_input_exit(); | ||
231 | 228 | ||
232 | /* Shut down implementation */ | 229 | /* Remove attributes */ |
233 | ams_info.exit(); | 230 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); |
234 | |||
235 | /* Flush interrupt worker | ||
236 | * | ||
237 | * We do this after ams_info.exit(), because an interrupt might | ||
238 | * have arrived before disabling them. | ||
239 | */ | ||
240 | flush_scheduled_work(); | ||
241 | 231 | ||
242 | /* Remove attributes */ | 232 | /* Shut down implementation */ |
243 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); | 233 | ams_info.exit(); |
244 | 234 | ||
245 | /* Remove device */ | 235 | /* Flush interrupt worker |
246 | of_device_unregister(ams_info.of_dev); | 236 | * |
237 | * We do this after ams_info.exit(), because an interrupt might | ||
238 | * have arrived before disabling them. | ||
239 | */ | ||
240 | flush_scheduled_work(); | ||
247 | 241 | ||
248 | /* Remove handler */ | 242 | /* Remove device */ |
249 | pmf_unregister_irq_client(&ams_shock_client); | 243 | of_device_unregister(ams_info.of_dev); |
250 | pmf_unregister_irq_client(&ams_freefall_client); | ||
251 | } | ||
252 | 244 | ||
253 | mutex_unlock(&ams_info.lock); | 245 | /* Remove handler */ |
246 | pmf_unregister_irq_client(&ams_shock_client); | ||
247 | pmf_unregister_irq_client(&ams_freefall_client); | ||
254 | } | 248 | } |
255 | 249 | ||
256 | MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); | 250 | MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); |
diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index da26de01068e..2cbf8a6506c7 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c | |||
@@ -261,8 +261,6 @@ int __init ams_i2c_init(struct device_node *np) | |||
261 | { | 261 | { |
262 | int result; | 262 | int result; |
263 | 263 | ||
264 | mutex_lock(&ams_info.lock); | ||
265 | |||
266 | /* Set implementation stuff */ | 264 | /* Set implementation stuff */ |
267 | ams_info.of_node = np; | 265 | ams_info.of_node = np; |
268 | ams_info.exit = ams_i2c_exit; | 266 | ams_info.exit = ams_i2c_exit; |
@@ -273,7 +271,5 @@ int __init ams_i2c_init(struct device_node *np) | |||
273 | 271 | ||
274 | result = i2c_add_driver(&ams_i2c_driver); | 272 | result = i2c_add_driver(&ams_i2c_driver); |
275 | 273 | ||
276 | mutex_unlock(&ams_info.lock); | ||
277 | |||
278 | return result; | 274 | return result; |
279 | } | 275 | } |
diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c index 48dbf7d6a66d..8a712392cd38 100644 --- a/drivers/hwmon/ams/ams-input.c +++ b/drivers/hwmon/ams/ams-input.c | |||
@@ -27,6 +27,8 @@ static unsigned int invert; | |||
27 | module_param(invert, bool, S_IWUSR | S_IRUGO); | 27 | module_param(invert, bool, S_IWUSR | S_IRUGO); |
28 | MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); | 28 | MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); |
29 | 29 | ||
30 | static DEFINE_MUTEX(ams_input_mutex); | ||
31 | |||
30 | static void ams_idev_poll(struct input_polled_dev *dev) | 32 | static void ams_idev_poll(struct input_polled_dev *dev) |
31 | { | 33 | { |
32 | struct input_dev *idev = dev->input; | 34 | struct input_dev *idev = dev->input; |
@@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev) | |||
50 | } | 52 | } |
51 | 53 | ||
52 | /* Call with ams_info.lock held! */ | 54 | /* Call with ams_info.lock held! */ |
53 | static void ams_input_enable(void) | 55 | static int ams_input_enable(void) |
54 | { | 56 | { |
55 | struct input_dev *input; | 57 | struct input_dev *input; |
56 | s8 x, y, z; | 58 | s8 x, y, z; |
57 | 59 | int error; | |
58 | if (ams_info.idev) | ||
59 | return; | ||
60 | 60 | ||
61 | ams_sensors(&x, &y, &z); | 61 | ams_sensors(&x, &y, &z); |
62 | ams_info.xcalib = x; | 62 | ams_info.xcalib = x; |
@@ -65,7 +65,7 @@ static void ams_input_enable(void) | |||
65 | 65 | ||
66 | ams_info.idev = input_allocate_polled_device(); | 66 | ams_info.idev = input_allocate_polled_device(); |
67 | if (!ams_info.idev) | 67 | if (!ams_info.idev) |
68 | return; | 68 | return -ENOMEM; |
69 | 69 | ||
70 | ams_info.idev->poll = ams_idev_poll; | 70 | ams_info.idev->poll = ams_idev_poll; |
71 | ams_info.idev->poll_interval = 25; | 71 | ams_info.idev->poll_interval = 25; |
@@ -84,14 +84,18 @@ static void ams_input_enable(void) | |||
84 | set_bit(EV_KEY, input->evbit); | 84 | set_bit(EV_KEY, input->evbit); |
85 | set_bit(BTN_TOUCH, input->keybit); | 85 | set_bit(BTN_TOUCH, input->keybit); |
86 | 86 | ||
87 | if (input_register_polled_device(ams_info.idev)) { | 87 | error = input_register_polled_device(ams_info.idev); |
88 | if (error) { | ||
88 | input_free_polled_device(ams_info.idev); | 89 | input_free_polled_device(ams_info.idev); |
89 | ams_info.idev = NULL; | 90 | ams_info.idev = NULL; |
90 | return; | 91 | return error; |
91 | } | 92 | } |
93 | |||
94 | joystick = 1; | ||
95 | |||
96 | return 0; | ||
92 | } | 97 | } |
93 | 98 | ||
94 | /* Call with ams_info.lock held! */ | ||
95 | static void ams_input_disable(void) | 99 | static void ams_input_disable(void) |
96 | { | 100 | { |
97 | if (ams_info.idev) { | 101 | if (ams_info.idev) { |
@@ -99,6 +103,8 @@ static void ams_input_disable(void) | |||
99 | input_free_polled_device(ams_info.idev); | 103 | input_free_polled_device(ams_info.idev); |
100 | ams_info.idev = NULL; | 104 | ams_info.idev = NULL; |
101 | } | 105 | } |
106 | |||
107 | joystick = 0; | ||
102 | } | 108 | } |
103 | 109 | ||
104 | static ssize_t ams_input_show_joystick(struct device *dev, | 110 | static ssize_t ams_input_show_joystick(struct device *dev, |
@@ -110,39 +116,42 @@ static ssize_t ams_input_show_joystick(struct device *dev, | |||
110 | static ssize_t ams_input_store_joystick(struct device *dev, | 116 | static ssize_t ams_input_store_joystick(struct device *dev, |
111 | struct device_attribute *attr, const char *buf, size_t count) | 117 | struct device_attribute *attr, const char *buf, size_t count) |
112 | { | 118 | { |
113 | if (sscanf(buf, "%d\n", &joystick) != 1) | 119 | unsigned long enable; |
120 | int error = 0; | ||
121 | |||
122 | if (strict_strtoul(buf, 0, &enable) || enable > 1) | ||
114 | return -EINVAL; | 123 | return -EINVAL; |
115 | 124 | ||
116 | mutex_lock(&ams_info.lock); | 125 | mutex_lock(&ams_input_mutex); |
117 | 126 | ||
118 | if (joystick) | 127 | if (enable != joystick) { |
119 | ams_input_enable(); | 128 | if (enable) |
120 | else | 129 | error = ams_input_enable(); |
121 | ams_input_disable(); | 130 | else |
131 | ams_input_disable(); | ||
132 | } | ||
122 | 133 | ||
123 | mutex_unlock(&ams_info.lock); | 134 | mutex_unlock(&ams_input_mutex); |
124 | 135 | ||
125 | return count; | 136 | return error ? error : count; |
126 | } | 137 | } |
127 | 138 | ||
128 | static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, | 139 | static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, |
129 | ams_input_show_joystick, ams_input_store_joystick); | 140 | ams_input_show_joystick, ams_input_store_joystick); |
130 | 141 | ||
131 | /* Call with ams_info.lock held! */ | ||
132 | int ams_input_init(void) | 142 | int ams_input_init(void) |
133 | { | 143 | { |
134 | int result; | 144 | if (joystick) |
135 | |||
136 | result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); | ||
137 | |||
138 | if (!result && joystick) | ||
139 | ams_input_enable(); | 145 | ams_input_enable(); |
140 | return result; | 146 | |
147 | return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); | ||
141 | } | 148 | } |
142 | 149 | ||
143 | /* Call with ams_info.lock held! */ | ||
144 | void ams_input_exit(void) | 150 | void ams_input_exit(void) |
145 | { | 151 | { |
146 | ams_input_disable(); | ||
147 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); | 152 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); |
153 | |||
154 | mutex_lock(&ams_input_mutex); | ||
155 | ams_input_disable(); | ||
156 | mutex_unlock(&ams_input_mutex); | ||
148 | } | 157 | } |
diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c index 9463e9768f6f..fb18b3d3162b 100644 --- a/drivers/hwmon/ams/ams-pmu.c +++ b/drivers/hwmon/ams/ams-pmu.c | |||
@@ -149,8 +149,6 @@ int __init ams_pmu_init(struct device_node *np) | |||
149 | const u32 *prop; | 149 | const u32 *prop; |
150 | int result; | 150 | int result; |
151 | 151 | ||
152 | mutex_lock(&ams_info.lock); | ||
153 | |||
154 | /* Set implementation stuff */ | 152 | /* Set implementation stuff */ |
155 | ams_info.of_node = np; | 153 | ams_info.of_node = np; |
156 | ams_info.exit = ams_pmu_exit; | 154 | ams_info.exit = ams_pmu_exit; |
@@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np) | |||
161 | 159 | ||
162 | /* Get PMU command, should be 0x4e, but we can never know */ | 160 | /* Get PMU command, should be 0x4e, but we can never know */ |
163 | prop = of_get_property(ams_info.of_node, "reg", NULL); | 161 | prop = of_get_property(ams_info.of_node, "reg", NULL); |
164 | if (!prop) { | 162 | if (!prop) |
165 | result = -ENODEV; | 163 | return -ENODEV; |
166 | goto exit; | 164 | |
167 | } | ||
168 | ams_pmu_cmd = ((*prop) >> 8) & 0xff; | 165 | ams_pmu_cmd = ((*prop) >> 8) & 0xff; |
169 | 166 | ||
170 | /* Disable interrupts */ | 167 | /* Disable interrupts */ |
@@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np) | |||
175 | 172 | ||
176 | result = ams_sensor_attach(); | 173 | result = ams_sensor_attach(); |
177 | if (result < 0) | 174 | if (result < 0) |
178 | goto exit; | 175 | return result; |
179 | 176 | ||
180 | /* Set default values */ | 177 | /* Set default values */ |
181 | ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); | 178 | ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); |
@@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np) | |||
198 | 195 | ||
199 | printk(KERN_INFO "ams: Found PMU based motion sensor\n"); | 196 | printk(KERN_INFO "ams: Found PMU based motion sensor\n"); |
200 | 197 | ||
201 | result = 0; | 198 | return 0; |
202 | |||
203 | exit: | ||
204 | mutex_unlock(&ams_info.lock); | ||
205 | |||
206 | return result; | ||
207 | } | 199 | } |