diff options
Diffstat (limited to 'drivers/input/input-polldev.c')
-rw-r--r-- | drivers/input/input-polldev.c | 131 |
1 files changed, 122 insertions, 9 deletions
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 7f161d93203c..3664f81655ca 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c | |||
@@ -147,6 +147,11 @@ static struct attribute_group input_polldev_attribute_group = { | |||
147 | .attrs = sysfs_attrs | 147 | .attrs = sysfs_attrs |
148 | }; | 148 | }; |
149 | 149 | ||
150 | static const struct attribute_group *input_polldev_attribute_groups[] = { | ||
151 | &input_polldev_attribute_group, | ||
152 | NULL | ||
153 | }; | ||
154 | |||
150 | /** | 155 | /** |
151 | * input_allocate_polled_device - allocate memory for polled device | 156 | * input_allocate_polled_device - allocate memory for polled device |
152 | * | 157 | * |
@@ -171,6 +176,91 @@ struct input_polled_dev *input_allocate_polled_device(void) | |||
171 | } | 176 | } |
172 | EXPORT_SYMBOL(input_allocate_polled_device); | 177 | EXPORT_SYMBOL(input_allocate_polled_device); |
173 | 178 | ||
179 | struct input_polled_devres { | ||
180 | struct input_polled_dev *polldev; | ||
181 | }; | ||
182 | |||
183 | static int devm_input_polldev_match(struct device *dev, void *res, void *data) | ||
184 | { | ||
185 | struct input_polled_devres *devres = res; | ||
186 | |||
187 | return devres->polldev == data; | ||
188 | } | ||
189 | |||
190 | static void devm_input_polldev_release(struct device *dev, void *res) | ||
191 | { | ||
192 | struct input_polled_devres *devres = res; | ||
193 | struct input_polled_dev *polldev = devres->polldev; | ||
194 | |||
195 | dev_dbg(dev, "%s: dropping reference/freeing %s\n", | ||
196 | __func__, dev_name(&polldev->input->dev)); | ||
197 | |||
198 | input_put_device(polldev->input); | ||
199 | kfree(polldev); | ||
200 | } | ||
201 | |||
202 | static void devm_input_polldev_unregister(struct device *dev, void *res) | ||
203 | { | ||
204 | struct input_polled_devres *devres = res; | ||
205 | struct input_polled_dev *polldev = devres->polldev; | ||
206 | |||
207 | dev_dbg(dev, "%s: unregistering device %s\n", | ||
208 | __func__, dev_name(&polldev->input->dev)); | ||
209 | input_unregister_device(polldev->input); | ||
210 | |||
211 | /* | ||
212 | * Note that we are still holding extra reference to the input | ||
213 | * device so it will stick around until devm_input_polldev_release() | ||
214 | * is called. | ||
215 | */ | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * devm_input_allocate_polled_device - allocate managed polled device | ||
220 | * @dev: device owning the polled device being created | ||
221 | * | ||
222 | * Returns prepared &struct input_polled_dev or %NULL. | ||
223 | * | ||
224 | * Managed polled input devices do not need to be explicitly unregistered | ||
225 | * or freed as it will be done automatically when owner device unbinds | ||
226 | * from * its driver (or binding fails). Once such managed polled device | ||
227 | * is allocated, it is ready to be set up and registered in the same | ||
228 | * fashion as regular polled input devices (using | ||
229 | * input_register_polled_device() function). | ||
230 | * | ||
231 | * If you want to manually unregister and free such managed polled devices, | ||
232 | * it can be still done by calling input_unregister_polled_device() and | ||
233 | * input_free_polled_device(), although it is rarely needed. | ||
234 | * | ||
235 | * NOTE: the owner device is set up as parent of input device and users | ||
236 | * should not override it. | ||
237 | */ | ||
238 | struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev) | ||
239 | { | ||
240 | struct input_polled_dev *polldev; | ||
241 | struct input_polled_devres *devres; | ||
242 | |||
243 | devres = devres_alloc(devm_input_polldev_release, sizeof(*devres), | ||
244 | GFP_KERNEL); | ||
245 | if (!devres) | ||
246 | return NULL; | ||
247 | |||
248 | polldev = input_allocate_polled_device(); | ||
249 | if (!polldev) { | ||
250 | devres_free(devres); | ||
251 | return NULL; | ||
252 | } | ||
253 | |||
254 | polldev->input->dev.parent = dev; | ||
255 | polldev->devres_managed = true; | ||
256 | |||
257 | devres->polldev = polldev; | ||
258 | devres_add(dev, devres); | ||
259 | |||
260 | return polldev; | ||
261 | } | ||
262 | EXPORT_SYMBOL(devm_input_allocate_polled_device); | ||
263 | |||
174 | /** | 264 | /** |
175 | * input_free_polled_device - free memory allocated for polled device | 265 | * input_free_polled_device - free memory allocated for polled device |
176 | * @dev: device to free | 266 | * @dev: device to free |
@@ -181,7 +271,12 @@ EXPORT_SYMBOL(input_allocate_polled_device); | |||
181 | void input_free_polled_device(struct input_polled_dev *dev) | 271 | void input_free_polled_device(struct input_polled_dev *dev) |
182 | { | 272 | { |
183 | if (dev) { | 273 | if (dev) { |
184 | input_free_device(dev->input); | 274 | if (dev->devres_managed) |
275 | WARN_ON(devres_destroy(dev->input->dev.parent, | ||
276 | devm_input_polldev_release, | ||
277 | devm_input_polldev_match, | ||
278 | dev)); | ||
279 | input_put_device(dev->input); | ||
185 | kfree(dev); | 280 | kfree(dev); |
186 | } | 281 | } |
187 | } | 282 | } |
@@ -199,26 +294,35 @@ EXPORT_SYMBOL(input_free_polled_device); | |||
199 | */ | 294 | */ |
200 | int input_register_polled_device(struct input_polled_dev *dev) | 295 | int input_register_polled_device(struct input_polled_dev *dev) |
201 | { | 296 | { |
297 | struct input_polled_devres *devres = NULL; | ||
202 | struct input_dev *input = dev->input; | 298 | struct input_dev *input = dev->input; |
203 | int error; | 299 | int error; |
204 | 300 | ||
301 | if (dev->devres_managed) { | ||
302 | devres = devres_alloc(devm_input_polldev_unregister, | ||
303 | sizeof(*devres), GFP_KERNEL); | ||
304 | if (!devres) | ||
305 | return -ENOMEM; | ||
306 | |||
307 | devres->polldev = dev; | ||
308 | } | ||
309 | |||
205 | input_set_drvdata(input, dev); | 310 | input_set_drvdata(input, dev); |
206 | INIT_DELAYED_WORK(&dev->work, input_polled_device_work); | 311 | INIT_DELAYED_WORK(&dev->work, input_polled_device_work); |
312 | |||
207 | if (!dev->poll_interval) | 313 | if (!dev->poll_interval) |
208 | dev->poll_interval = 500; | 314 | dev->poll_interval = 500; |
209 | if (!dev->poll_interval_max) | 315 | if (!dev->poll_interval_max) |
210 | dev->poll_interval_max = dev->poll_interval; | 316 | dev->poll_interval_max = dev->poll_interval; |
317 | |||
211 | input->open = input_open_polled_device; | 318 | input->open = input_open_polled_device; |
212 | input->close = input_close_polled_device; | 319 | input->close = input_close_polled_device; |
213 | 320 | ||
214 | error = input_register_device(input); | 321 | input->dev.groups = input_polldev_attribute_groups; |
215 | if (error) | ||
216 | return error; | ||
217 | 322 | ||
218 | error = sysfs_create_group(&input->dev.kobj, | 323 | error = input_register_device(input); |
219 | &input_polldev_attribute_group); | ||
220 | if (error) { | 324 | if (error) { |
221 | input_unregister_device(input); | 325 | devres_free(devres); |
222 | return error; | 326 | return error; |
223 | } | 327 | } |
224 | 328 | ||
@@ -231,6 +335,12 @@ int input_register_polled_device(struct input_polled_dev *dev) | |||
231 | */ | 335 | */ |
232 | input_get_device(input); | 336 | input_get_device(input); |
233 | 337 | ||
338 | if (dev->devres_managed) { | ||
339 | dev_dbg(input->dev.parent, "%s: registering %s with devres.\n", | ||
340 | __func__, dev_name(&input->dev)); | ||
341 | devres_add(input->dev.parent, devres); | ||
342 | } | ||
343 | |||
234 | return 0; | 344 | return 0; |
235 | } | 345 | } |
236 | EXPORT_SYMBOL(input_register_polled_device); | 346 | EXPORT_SYMBOL(input_register_polled_device); |
@@ -245,8 +355,11 @@ EXPORT_SYMBOL(input_register_polled_device); | |||
245 | */ | 355 | */ |
246 | void input_unregister_polled_device(struct input_polled_dev *dev) | 356 | void input_unregister_polled_device(struct input_polled_dev *dev) |
247 | { | 357 | { |
248 | sysfs_remove_group(&dev->input->dev.kobj, | 358 | if (dev->devres_managed) |
249 | &input_polldev_attribute_group); | 359 | WARN_ON(devres_destroy(dev->input->dev.parent, |
360 | devm_input_polldev_unregister, | ||
361 | devm_input_polldev_match, | ||
362 | dev)); | ||
250 | 363 | ||
251 | input_unregister_device(dev->input); | 364 | input_unregister_device(dev->input); |
252 | } | 365 | } |