aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2014-04-28 13:49:51 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2014-05-14 19:40:04 -0400
commitbf1de9761c21f56d5b0c6a0acd3b792d801c61e6 (patch)
tree41f79fa2df689dd2a15f680078fa4f82e7f759c5
parentd1fefd5b73ba9fe301441e84ebea06044d8f5ea4 (diff)
Input: implement managed polled input devices
Managed resources are becoming more and more popular in drivers. Let's implement managed polled input devices, to complement managed regular input devices. Similarly to managed regular input devices only one new call devm_input_allocate_polled_device() is added and the rest of APIs is modified to work with both managed and non-managed devices. Reviewed-by: David Herrmann <dh.herrmann@gmail.com> Tested-by: Alexander Shiyan <shc_work@mail.ru> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r--drivers/input/input-polldev.c118
-rw-r--r--include/linux/input-polldev.h3
2 files changed, 119 insertions, 2 deletions
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index 4b191908d5de..3664f81655ca 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -176,6 +176,91 @@ struct input_polled_dev *input_allocate_polled_device(void)
176} 176}
177EXPORT_SYMBOL(input_allocate_polled_device); 177EXPORT_SYMBOL(input_allocate_polled_device);
178 178
179struct input_polled_devres {
180 struct input_polled_dev *polldev;
181};
182
183static 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
190static 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
202static 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 */
238struct 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}
262EXPORT_SYMBOL(devm_input_allocate_polled_device);
263
179/** 264/**
180 * input_free_polled_device - free memory allocated for polled device 265 * input_free_polled_device - free memory allocated for polled device
181 * @dev: device to free 266 * @dev: device to free
@@ -186,7 +271,12 @@ EXPORT_SYMBOL(input_allocate_polled_device);
186void input_free_polled_device(struct input_polled_dev *dev) 271void input_free_polled_device(struct input_polled_dev *dev)
187{ 272{
188 if (dev) { 273 if (dev) {
189 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);
190 kfree(dev); 280 kfree(dev);
191 } 281 }
192} 282}
@@ -204,9 +294,19 @@ EXPORT_SYMBOL(input_free_polled_device);
204 */ 294 */
205int input_register_polled_device(struct input_polled_dev *dev) 295int input_register_polled_device(struct input_polled_dev *dev)
206{ 296{
297 struct input_polled_devres *devres = NULL;
207 struct input_dev *input = dev->input; 298 struct input_dev *input = dev->input;
208 int error; 299 int error;
209 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
210 input_set_drvdata(input, dev); 310 input_set_drvdata(input, dev);
211 INIT_DELAYED_WORK(&dev->work, input_polled_device_work); 311 INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
212 312
@@ -221,8 +321,10 @@ int input_register_polled_device(struct input_polled_dev *dev)
221 input->dev.groups = input_polldev_attribute_groups; 321 input->dev.groups = input_polldev_attribute_groups;
222 322
223 error = input_register_device(input); 323 error = input_register_device(input);
224 if (error) 324 if (error) {
325 devres_free(devres);
225 return error; 326 return error;
327 }
226 328
227 /* 329 /*
228 * Take extra reference to the underlying input device so 330 * Take extra reference to the underlying input device so
@@ -233,6 +335,12 @@ int input_register_polled_device(struct input_polled_dev *dev)
233 */ 335 */
234 input_get_device(input); 336 input_get_device(input);
235 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
236 return 0; 344 return 0;
237} 345}
238EXPORT_SYMBOL(input_register_polled_device); 346EXPORT_SYMBOL(input_register_polled_device);
@@ -247,6 +355,12 @@ EXPORT_SYMBOL(input_register_polled_device);
247 */ 355 */
248void input_unregister_polled_device(struct input_polled_dev *dev) 356void input_unregister_polled_device(struct input_polled_dev *dev)
249{ 357{
358 if (dev->devres_managed)
359 WARN_ON(devres_destroy(dev->input->dev.parent,
360 devm_input_polldev_unregister,
361 devm_input_polldev_match,
362 dev));
363
250 input_unregister_device(dev->input); 364 input_unregister_device(dev->input);
251} 365}
252EXPORT_SYMBOL(input_unregister_polled_device); 366EXPORT_SYMBOL(input_unregister_polled_device);
diff --git a/include/linux/input-polldev.h b/include/linux/input-polldev.h
index ce0b72464eb8..2465182670db 100644
--- a/include/linux/input-polldev.h
+++ b/include/linux/input-polldev.h
@@ -48,9 +48,12 @@ struct input_polled_dev {
48 48
49/* private: */ 49/* private: */
50 struct delayed_work work; 50 struct delayed_work work;
51
52 bool devres_managed;
51}; 53};
52 54
53struct input_polled_dev *input_allocate_polled_device(void); 55struct input_polled_dev *input_allocate_polled_device(void);
56struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev);
54void input_free_polled_device(struct input_polled_dev *dev); 57void input_free_polled_device(struct input_polled_dev *dev);
55int input_register_polled_device(struct input_polled_dev *dev); 58int input_register_polled_device(struct input_polled_dev *dev);
56void input_unregister_polled_device(struct input_polled_dev *dev); 59void input_unregister_polled_device(struct input_polled_dev *dev);