diff options
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/uinput.c | 94 |
1 files changed, 71 insertions, 23 deletions
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 46b7caeb2817..c5a49aba418f 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c | |||
@@ -54,27 +54,28 @@ static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned i | |||
54 | return 0; | 54 | return 0; |
55 | } | 55 | } |
56 | 56 | ||
57 | /* Atomically allocate an ID for the given request. Returns 0 on success. */ | ||
57 | static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) | 58 | static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) |
58 | { | 59 | { |
59 | /* Atomically allocate an ID for the given request. Returns 0 on success. */ | ||
60 | int id; | 60 | int id; |
61 | int err = -1; | 61 | int err = -1; |
62 | 62 | ||
63 | spin_lock(&udev->requests_lock); | 63 | spin_lock(&udev->requests_lock); |
64 | 64 | ||
65 | for (id = 0; id < UINPUT_NUM_REQUESTS; id++) | 65 | for (id = 0; id < UINPUT_NUM_REQUESTS; id++) { |
66 | if (!udev->requests[id]) { | 66 | if (!udev->requests[id]) { |
67 | request->id = id; | 67 | request->id = id; |
68 | udev->requests[id] = request; | 68 | udev->requests[id] = request; |
69 | err = 0; | 69 | err = 0; |
70 | break; | 70 | break; |
71 | } | 71 | } |
72 | } | ||
72 | 73 | ||
73 | spin_unlock(&udev->requests_lock); | 74 | spin_unlock(&udev->requests_lock); |
74 | return err; | 75 | return err; |
75 | } | 76 | } |
76 | 77 | ||
77 | static struct uinput_request* uinput_request_find(struct uinput_device *udev, int id) | 78 | static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id) |
78 | { | 79 | { |
79 | /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ | 80 | /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ |
80 | if (id >= UINPUT_NUM_REQUESTS || id < 0) | 81 | if (id >= UINPUT_NUM_REQUESTS || id < 0) |
@@ -99,14 +100,51 @@ static void uinput_request_done(struct uinput_device *udev, struct uinput_reques | |||
99 | complete(&request->done); | 100 | complete(&request->done); |
100 | } | 101 | } |
101 | 102 | ||
102 | static int uinput_request_submit(struct input_dev *dev, struct uinput_request *request) | 103 | static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request) |
103 | { | 104 | { |
105 | int retval; | ||
106 | |||
107 | retval = uinput_request_reserve_slot(udev, request); | ||
108 | if (retval) | ||
109 | return retval; | ||
110 | |||
111 | retval = mutex_lock_interruptible(&udev->mutex); | ||
112 | if (retval) | ||
113 | return retval; | ||
114 | |||
115 | if (udev->state != UIST_CREATED) { | ||
116 | retval = -ENODEV; | ||
117 | goto out; | ||
118 | } | ||
119 | |||
104 | /* Tell our userspace app about this new request by queueing an input event */ | 120 | /* Tell our userspace app about this new request by queueing an input event */ |
105 | uinput_dev_event(dev, EV_UINPUT, request->code, request->id); | 121 | uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); |
122 | |||
123 | out: | ||
124 | mutex_unlock(&udev->mutex); | ||
125 | return retval; | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * Fail all ouitstanding requests so handlers don't wait for the userspace | ||
130 | * to finish processing them. | ||
131 | */ | ||
132 | static void uinput_flush_requests(struct uinput_device *udev) | ||
133 | { | ||
134 | struct uinput_request *request; | ||
135 | int i; | ||
136 | |||
137 | spin_lock(&udev->requests_lock); | ||
138 | |||
139 | for (i = 0; i < UINPUT_NUM_REQUESTS; i++) { | ||
140 | request = udev->requests[i]; | ||
141 | if (request) { | ||
142 | request->retval = -ENODEV; | ||
143 | uinput_request_done(udev, request); | ||
144 | } | ||
145 | } | ||
106 | 146 | ||
107 | /* Wait for the request to complete */ | 147 | spin_unlock(&udev->requests_lock); |
108 | wait_for_completion(&request->done); | ||
109 | return request->retval; | ||
110 | } | 148 | } |
111 | 149 | ||
112 | static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) | 150 | static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) |
@@ -126,6 +164,7 @@ static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) | |||
126 | 164 | ||
127 | static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) | 165 | static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) |
128 | { | 166 | { |
167 | struct uinput_device *udev = input_get_drvdata(dev); | ||
129 | struct uinput_request request; | 168 | struct uinput_request request; |
130 | int retval; | 169 | int retval; |
131 | 170 | ||
@@ -146,15 +185,18 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff | |||
146 | request.u.upload.effect = effect; | 185 | request.u.upload.effect = effect; |
147 | request.u.upload.old = old; | 186 | request.u.upload.old = old; |
148 | 187 | ||
149 | retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); | 188 | retval = uinput_request_submit(udev, &request); |
150 | if (!retval) | 189 | if (!retval) { |
151 | retval = uinput_request_submit(dev, &request); | 190 | wait_for_completion(&request.done); |
191 | retval = request.retval; | ||
192 | } | ||
152 | 193 | ||
153 | return retval; | 194 | return retval; |
154 | } | 195 | } |
155 | 196 | ||
156 | static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) | 197 | static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) |
157 | { | 198 | { |
199 | struct uinput_device *udev = input_get_drvdata(dev); | ||
158 | struct uinput_request request; | 200 | struct uinput_request request; |
159 | int retval; | 201 | int retval; |
160 | 202 | ||
@@ -166,9 +208,11 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) | |||
166 | request.code = UI_FF_ERASE; | 208 | request.code = UI_FF_ERASE; |
167 | request.u.effect_id = effect_id; | 209 | request.u.effect_id = effect_id; |
168 | 210 | ||
169 | retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); | 211 | retval = uinput_request_submit(udev, &request); |
170 | if (!retval) | 212 | if (!retval) { |
171 | retval = uinput_request_submit(dev, &request); | 213 | wait_for_completion(&request.done); |
214 | retval = request.retval; | ||
215 | } | ||
172 | 216 | ||
173 | return retval; | 217 | return retval; |
174 | } | 218 | } |
@@ -176,20 +220,24 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) | |||
176 | static void uinput_destroy_device(struct uinput_device *udev) | 220 | static void uinput_destroy_device(struct uinput_device *udev) |
177 | { | 221 | { |
178 | const char *name, *phys; | 222 | const char *name, *phys; |
223 | struct input_dev *dev = udev->dev; | ||
224 | enum uinput_state old_state = udev->state; | ||
179 | 225 | ||
180 | if (udev->dev) { | 226 | udev->state = UIST_NEW_DEVICE; |
181 | name = udev->dev->name; | 227 | |
182 | phys = udev->dev->phys; | 228 | if (dev) { |
183 | if (udev->state == UIST_CREATED) | 229 | name = dev->name; |
184 | input_unregister_device(udev->dev); | 230 | phys = dev->phys; |
185 | else | 231 | if (old_state == UIST_CREATED) { |
186 | input_free_device(udev->dev); | 232 | uinput_flush_requests(udev); |
233 | input_unregister_device(dev); | ||
234 | } else { | ||
235 | input_free_device(dev); | ||
236 | } | ||
187 | kfree(name); | 237 | kfree(name); |
188 | kfree(phys); | 238 | kfree(phys); |
189 | udev->dev = NULL; | 239 | udev->dev = NULL; |
190 | } | 240 | } |
191 | |||
192 | udev->state = UIST_NEW_DEVICE; | ||
193 | } | 241 | } |
194 | 242 | ||
195 | static int uinput_create_device(struct uinput_device *udev) | 243 | static int uinput_create_device(struct uinput_device *udev) |