diff options
Diffstat (limited to 'drivers/input/keyboard/atkbd.c')
-rw-r--r-- | drivers/input/keyboard/atkbd.c | 74 |
1 files changed, 36 insertions, 38 deletions
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index a3573570c52f..7b4056292eaf 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c | |||
@@ -134,7 +134,8 @@ static const unsigned short atkbd_unxlate_table[128] = { | |||
134 | #define ATKBD_CMD_GETID 0x02f2 | 134 | #define ATKBD_CMD_GETID 0x02f2 |
135 | #define ATKBD_CMD_SETREP 0x10f3 | 135 | #define ATKBD_CMD_SETREP 0x10f3 |
136 | #define ATKBD_CMD_ENABLE 0x00f4 | 136 | #define ATKBD_CMD_ENABLE 0x00f4 |
137 | #define ATKBD_CMD_RESET_DIS 0x00f5 | 137 | #define ATKBD_CMD_RESET_DIS 0x00f5 /* Reset to defaults and disable */ |
138 | #define ATKBD_CMD_RESET_DEF 0x00f6 /* Reset to defaults */ | ||
138 | #define ATKBD_CMD_SETALL_MBR 0x00fa | 139 | #define ATKBD_CMD_SETALL_MBR 0x00fa |
139 | #define ATKBD_CMD_RESET_BAT 0x02ff | 140 | #define ATKBD_CMD_RESET_BAT 0x02ff |
140 | #define ATKBD_CMD_RESEND 0x00fe | 141 | #define ATKBD_CMD_RESEND 0x00fe |
@@ -224,8 +225,10 @@ struct atkbd { | |||
224 | 225 | ||
225 | struct delayed_work event_work; | 226 | struct delayed_work event_work; |
226 | unsigned long event_jiffies; | 227 | unsigned long event_jiffies; |
227 | struct mutex event_mutex; | ||
228 | unsigned long event_mask; | 228 | unsigned long event_mask; |
229 | |||
230 | /* Serializes reconnect(), attr->set() and event work */ | ||
231 | struct mutex mutex; | ||
229 | }; | 232 | }; |
230 | 233 | ||
231 | /* | 234 | /* |
@@ -576,7 +579,7 @@ static void atkbd_event_work(struct work_struct *work) | |||
576 | { | 579 | { |
577 | struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work); | 580 | struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work); |
578 | 581 | ||
579 | mutex_lock(&atkbd->event_mutex); | 582 | mutex_lock(&atkbd->mutex); |
580 | 583 | ||
581 | if (!atkbd->enabled) { | 584 | if (!atkbd->enabled) { |
582 | /* | 585 | /* |
@@ -595,7 +598,7 @@ static void atkbd_event_work(struct work_struct *work) | |||
595 | atkbd_set_repeat_rate(atkbd); | 598 | atkbd_set_repeat_rate(atkbd); |
596 | } | 599 | } |
597 | 600 | ||
598 | mutex_unlock(&atkbd->event_mutex); | 601 | mutex_unlock(&atkbd->mutex); |
599 | } | 602 | } |
600 | 603 | ||
601 | /* | 604 | /* |
@@ -611,7 +614,7 @@ static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) | |||
611 | 614 | ||
612 | atkbd->event_jiffies = jiffies; | 615 | atkbd->event_jiffies = jiffies; |
613 | set_bit(event_bit, &atkbd->event_mask); | 616 | set_bit(event_bit, &atkbd->event_mask); |
614 | wmb(); | 617 | mb(); |
615 | schedule_delayed_work(&atkbd->event_work, delay); | 618 | schedule_delayed_work(&atkbd->event_work, delay); |
616 | } | 619 | } |
617 | 620 | ||
@@ -836,7 +839,7 @@ static void atkbd_cleanup(struct serio *serio) | |||
836 | struct atkbd *atkbd = serio_get_drvdata(serio); | 839 | struct atkbd *atkbd = serio_get_drvdata(serio); |
837 | 840 | ||
838 | atkbd_disable(atkbd); | 841 | atkbd_disable(atkbd); |
839 | ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_BAT); | 842 | ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_DEF); |
840 | } | 843 | } |
841 | 844 | ||
842 | 845 | ||
@@ -848,13 +851,20 @@ static void atkbd_disconnect(struct serio *serio) | |||
848 | { | 851 | { |
849 | struct atkbd *atkbd = serio_get_drvdata(serio); | 852 | struct atkbd *atkbd = serio_get_drvdata(serio); |
850 | 853 | ||
854 | sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); | ||
855 | |||
851 | atkbd_disable(atkbd); | 856 | atkbd_disable(atkbd); |
852 | 857 | ||
853 | /* 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 | */ | ||
854 | cancel_delayed_work_sync(&atkbd->event_work); | 866 | cancel_delayed_work_sync(&atkbd->event_work); |
855 | 867 | ||
856 | sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); | ||
857 | input_unregister_device(atkbd->dev); | ||
858 | serio_close(serio); | 868 | serio_close(serio); |
859 | serio_set_drvdata(serio, NULL); | 869 | serio_set_drvdata(serio, NULL); |
860 | kfree(atkbd); | 870 | kfree(atkbd); |
@@ -1086,7 +1096,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) | |||
1086 | atkbd->dev = dev; | 1096 | atkbd->dev = dev; |
1087 | ps2_init(&atkbd->ps2dev, serio); | 1097 | ps2_init(&atkbd->ps2dev, serio); |
1088 | INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); | 1098 | INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); |
1089 | mutex_init(&atkbd->event_mutex); | 1099 | mutex_init(&atkbd->mutex); |
1090 | 1100 | ||
1091 | switch (serio->id.type) { | 1101 | switch (serio->id.type) { |
1092 | 1102 | ||
@@ -1159,19 +1169,23 @@ static int atkbd_reconnect(struct serio *serio) | |||
1159 | { | 1169 | { |
1160 | struct atkbd *atkbd = serio_get_drvdata(serio); | 1170 | struct atkbd *atkbd = serio_get_drvdata(serio); |
1161 | struct serio_driver *drv = serio->drv; | 1171 | struct serio_driver *drv = serio->drv; |
1172 | int retval = -1; | ||
1162 | 1173 | ||
1163 | if (!atkbd || !drv) { | 1174 | if (!atkbd || !drv) { |
1164 | 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"); |
1165 | return -1; | 1176 | return -1; |
1166 | } | 1177 | } |
1167 | 1178 | ||
1179 | mutex_lock(&atkbd->mutex); | ||
1180 | |||
1168 | atkbd_disable(atkbd); | 1181 | atkbd_disable(atkbd); |
1169 | 1182 | ||
1170 | if (atkbd->write) { | 1183 | if (atkbd->write) { |
1171 | if (atkbd_probe(atkbd)) | 1184 | if (atkbd_probe(atkbd)) |
1172 | return -1; | 1185 | goto out; |
1186 | |||
1173 | if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) | 1187 | if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) |
1174 | return -1; | 1188 | goto out; |
1175 | 1189 | ||
1176 | atkbd_activate(atkbd); | 1190 | atkbd_activate(atkbd); |
1177 | 1191 | ||
@@ -1189,8 +1203,11 @@ static int atkbd_reconnect(struct serio *serio) | |||
1189 | } | 1203 | } |
1190 | 1204 | ||
1191 | atkbd_enable(atkbd); | 1205 | atkbd_enable(atkbd); |
1206 | retval = 0; | ||
1192 | 1207 | ||
1193 | return 0; | 1208 | out: |
1209 | mutex_unlock(&atkbd->mutex); | ||
1210 | return retval; | ||
1194 | } | 1211 | } |
1195 | 1212 | ||
1196 | static struct serio_device_id atkbd_serio_ids[] = { | 1213 | static struct serio_device_id atkbd_serio_ids[] = { |
@@ -1234,47 +1251,28 @@ static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, | |||
1234 | ssize_t (*handler)(struct atkbd *, char *)) | 1251 | ssize_t (*handler)(struct atkbd *, char *)) |
1235 | { | 1252 | { |
1236 | struct serio *serio = to_serio_port(dev); | 1253 | struct serio *serio = to_serio_port(dev); |
1237 | int retval; | 1254 | struct atkbd *atkbd = serio_get_drvdata(serio); |
1238 | |||
1239 | retval = serio_pin_driver(serio); | ||
1240 | if (retval) | ||
1241 | return retval; | ||
1242 | |||
1243 | if (serio->drv != &atkbd_drv) { | ||
1244 | retval = -ENODEV; | ||
1245 | goto out; | ||
1246 | } | ||
1247 | |||
1248 | retval = handler((struct atkbd *)serio_get_drvdata(serio), buf); | ||
1249 | 1255 | ||
1250 | out: | 1256 | return handler(atkbd, buf); |
1251 | serio_unpin_driver(serio); | ||
1252 | return retval; | ||
1253 | } | 1257 | } |
1254 | 1258 | ||
1255 | 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, |
1256 | ssize_t (*handler)(struct atkbd *, const char *, size_t)) | 1260 | ssize_t (*handler)(struct atkbd *, const char *, size_t)) |
1257 | { | 1261 | { |
1258 | struct serio *serio = to_serio_port(dev); | 1262 | struct serio *serio = to_serio_port(dev); |
1259 | struct atkbd *atkbd; | 1263 | struct atkbd *atkbd = serio_get_drvdata(serio); |
1260 | int retval; | 1264 | int retval; |
1261 | 1265 | ||
1262 | retval = serio_pin_driver(serio); | 1266 | retval = mutex_lock_interruptible(&atkbd->mutex); |
1263 | if (retval) | 1267 | if (retval) |
1264 | return retval; | 1268 | return retval; |
1265 | 1269 | ||
1266 | if (serio->drv != &atkbd_drv) { | ||
1267 | retval = -ENODEV; | ||
1268 | goto out; | ||
1269 | } | ||
1270 | |||
1271 | atkbd = serio_get_drvdata(serio); | ||
1272 | atkbd_disable(atkbd); | 1270 | atkbd_disable(atkbd); |
1273 | retval = handler(atkbd, buf, count); | 1271 | retval = handler(atkbd, buf, count); |
1274 | atkbd_enable(atkbd); | 1272 | atkbd_enable(atkbd); |
1275 | 1273 | ||
1276 | out: | 1274 | mutex_unlock(&atkbd->mutex); |
1277 | serio_unpin_driver(serio); | 1275 | |
1278 | return retval; | 1276 | return retval; |
1279 | } | 1277 | } |
1280 | 1278 | ||