diff options
Diffstat (limited to 'drivers/input/keyboard/atkbd.c')
-rw-r--r-- | drivers/input/keyboard/atkbd.c | 69 |
1 files changed, 33 insertions, 36 deletions
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 1f5e2ce327d6..7b4056292eaf 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c | |||
@@ -225,8 +225,10 @@ struct atkbd { | |||
225 | 225 | ||
226 | struct delayed_work event_work; | 226 | struct delayed_work event_work; |
227 | unsigned long event_jiffies; | 227 | unsigned long event_jiffies; |
228 | struct mutex event_mutex; | ||
229 | unsigned long event_mask; | 228 | unsigned long event_mask; |
229 | |||
230 | /* Serializes reconnect(), attr->set() and event work */ | ||
231 | struct mutex mutex; | ||
230 | }; | 232 | }; |
231 | 233 | ||
232 | /* | 234 | /* |
@@ -577,7 +579,7 @@ static void atkbd_event_work(struct work_struct *work) | |||
577 | { | 579 | { |
578 | struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work); | 580 | struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work); |
579 | 581 | ||
580 | mutex_lock(&atkbd->event_mutex); | 582 | mutex_lock(&atkbd->mutex); |
581 | 583 | ||
582 | if (!atkbd->enabled) { | 584 | if (!atkbd->enabled) { |
583 | /* | 585 | /* |
@@ -596,7 +598,7 @@ static void atkbd_event_work(struct work_struct *work) | |||
596 | atkbd_set_repeat_rate(atkbd); | 598 | atkbd_set_repeat_rate(atkbd); |
597 | } | 599 | } |
598 | 600 | ||
599 | mutex_unlock(&atkbd->event_mutex); | 601 | mutex_unlock(&atkbd->mutex); |
600 | } | 602 | } |
601 | 603 | ||
602 | /* | 604 | /* |
@@ -612,7 +614,7 @@ static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) | |||
612 | 614 | ||
613 | atkbd->event_jiffies = jiffies; | 615 | atkbd->event_jiffies = jiffies; |
614 | set_bit(event_bit, &atkbd->event_mask); | 616 | set_bit(event_bit, &atkbd->event_mask); |
615 | wmb(); | 617 | mb(); |
616 | schedule_delayed_work(&atkbd->event_work, delay); | 618 | schedule_delayed_work(&atkbd->event_work, delay); |
617 | } | 619 | } |
618 | 620 | ||
@@ -849,13 +851,20 @@ static void atkbd_disconnect(struct serio *serio) | |||
849 | { | 851 | { |
850 | struct atkbd *atkbd = serio_get_drvdata(serio); | 852 | struct atkbd *atkbd = serio_get_drvdata(serio); |
851 | 853 | ||
854 | sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); | ||
855 | |||
852 | atkbd_disable(atkbd); | 856 | atkbd_disable(atkbd); |
853 | 857 | ||
854 | /* make sure we don't have a command in flight */ | 858 | input_unregister_device(atkbd->dev); |
859 | |||
860 | /* | ||
861 | * Make sure we don't have a command in flight. | ||
862 | * Note that since atkbd->enabled is false event work will keep | ||
863 | * rescheduling itself until it gets canceled and will not try | ||
864 | * accessing freed input device or serio port. | ||
865 | */ | ||
855 | cancel_delayed_work_sync(&atkbd->event_work); | 866 | cancel_delayed_work_sync(&atkbd->event_work); |
856 | 867 | ||
857 | sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); | ||
858 | input_unregister_device(atkbd->dev); | ||
859 | serio_close(serio); | 868 | serio_close(serio); |
860 | serio_set_drvdata(serio, NULL); | 869 | serio_set_drvdata(serio, NULL); |
861 | kfree(atkbd); | 870 | kfree(atkbd); |
@@ -1087,7 +1096,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) | |||
1087 | atkbd->dev = dev; | 1096 | atkbd->dev = dev; |
1088 | ps2_init(&atkbd->ps2dev, serio); | 1097 | ps2_init(&atkbd->ps2dev, serio); |
1089 | INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); | 1098 | INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); |
1090 | mutex_init(&atkbd->event_mutex); | 1099 | mutex_init(&atkbd->mutex); |
1091 | 1100 | ||
1092 | switch (serio->id.type) { | 1101 | switch (serio->id.type) { |
1093 | 1102 | ||
@@ -1160,19 +1169,23 @@ static int atkbd_reconnect(struct serio *serio) | |||
1160 | { | 1169 | { |
1161 | struct atkbd *atkbd = serio_get_drvdata(serio); | 1170 | struct atkbd *atkbd = serio_get_drvdata(serio); |
1162 | struct serio_driver *drv = serio->drv; | 1171 | struct serio_driver *drv = serio->drv; |
1172 | int retval = -1; | ||
1163 | 1173 | ||
1164 | if (!atkbd || !drv) { | 1174 | if (!atkbd || !drv) { |
1165 | printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n"); | 1175 | printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n"); |
1166 | return -1; | 1176 | return -1; |
1167 | } | 1177 | } |
1168 | 1178 | ||
1179 | mutex_lock(&atkbd->mutex); | ||
1180 | |||
1169 | atkbd_disable(atkbd); | 1181 | atkbd_disable(atkbd); |
1170 | 1182 | ||
1171 | if (atkbd->write) { | 1183 | if (atkbd->write) { |
1172 | if (atkbd_probe(atkbd)) | 1184 | if (atkbd_probe(atkbd)) |
1173 | return -1; | 1185 | goto out; |
1186 | |||
1174 | if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) | 1187 | if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) |
1175 | return -1; | 1188 | goto out; |
1176 | 1189 | ||
1177 | atkbd_activate(atkbd); | 1190 | atkbd_activate(atkbd); |
1178 | 1191 | ||
@@ -1190,8 +1203,11 @@ static int atkbd_reconnect(struct serio *serio) | |||
1190 | } | 1203 | } |
1191 | 1204 | ||
1192 | atkbd_enable(atkbd); | 1205 | atkbd_enable(atkbd); |
1206 | retval = 0; | ||
1193 | 1207 | ||
1194 | return 0; | 1208 | out: |
1209 | mutex_unlock(&atkbd->mutex); | ||
1210 | return retval; | ||
1195 | } | 1211 | } |
1196 | 1212 | ||
1197 | static struct serio_device_id atkbd_serio_ids[] = { | 1213 | static struct serio_device_id atkbd_serio_ids[] = { |
@@ -1235,47 +1251,28 @@ static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, | |||
1235 | ssize_t (*handler)(struct atkbd *, char *)) | 1251 | ssize_t (*handler)(struct atkbd *, char *)) |
1236 | { | 1252 | { |
1237 | struct serio *serio = to_serio_port(dev); | 1253 | struct serio *serio = to_serio_port(dev); |
1238 | int retval; | 1254 | struct atkbd *atkbd = serio_get_drvdata(serio); |
1239 | |||
1240 | retval = serio_pin_driver(serio); | ||
1241 | if (retval) | ||
1242 | return retval; | ||
1243 | |||
1244 | if (serio->drv != &atkbd_drv) { | ||
1245 | retval = -ENODEV; | ||
1246 | goto out; | ||
1247 | } | ||
1248 | |||
1249 | retval = handler((struct atkbd *)serio_get_drvdata(serio), buf); | ||
1250 | 1255 | ||
1251 | out: | 1256 | return handler(atkbd, buf); |
1252 | serio_unpin_driver(serio); | ||
1253 | return retval; | ||
1254 | } | 1257 | } |
1255 | 1258 | ||
1256 | static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, | 1259 | static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, |
1257 | ssize_t (*handler)(struct atkbd *, const char *, size_t)) | 1260 | ssize_t (*handler)(struct atkbd *, const char *, size_t)) |
1258 | { | 1261 | { |
1259 | struct serio *serio = to_serio_port(dev); | 1262 | struct serio *serio = to_serio_port(dev); |
1260 | struct atkbd *atkbd; | 1263 | struct atkbd *atkbd = serio_get_drvdata(serio); |
1261 | int retval; | 1264 | int retval; |
1262 | 1265 | ||
1263 | retval = serio_pin_driver(serio); | 1266 | retval = mutex_lock_interruptible(&atkbd->mutex); |
1264 | if (retval) | 1267 | if (retval) |
1265 | return retval; | 1268 | return retval; |
1266 | 1269 | ||
1267 | if (serio->drv != &atkbd_drv) { | ||
1268 | retval = -ENODEV; | ||
1269 | goto out; | ||
1270 | } | ||
1271 | |||
1272 | atkbd = serio_get_drvdata(serio); | ||
1273 | atkbd_disable(atkbd); | 1270 | atkbd_disable(atkbd); |
1274 | retval = handler(atkbd, buf, count); | 1271 | retval = handler(atkbd, buf, count); |
1275 | atkbd_enable(atkbd); | 1272 | atkbd_enable(atkbd); |
1276 | 1273 | ||
1277 | out: | 1274 | mutex_unlock(&atkbd->mutex); |
1278 | serio_unpin_driver(serio); | 1275 | |
1279 | return retval; | 1276 | return retval; |
1280 | } | 1277 | } |
1281 | 1278 | ||