aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pps/pps.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pps/pps.c')
-rw-r--r--drivers/pps/pps.c50
1 files changed, 49 insertions, 1 deletions
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index 9f7c2e858dd0..79b445578132 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -30,6 +30,7 @@
30#include <linux/cdev.h> 30#include <linux/cdev.h>
31#include <linux/poll.h> 31#include <linux/poll.h>
32#include <linux/pps_kernel.h> 32#include <linux/pps_kernel.h>
33#include <linux/slab.h>
33 34
34/* 35/*
35 * Local variables 36 * Local variables
@@ -38,6 +39,9 @@
38static dev_t pps_devt; 39static dev_t pps_devt;
39static struct class *pps_class; 40static struct class *pps_class;
40 41
42static DEFINE_SPINLOCK(pps_idr_lock);
43static DEFINE_IDR(pps_idr);
44
41/* 45/*
42 * Char device methods 46 * Char device methods
43 */ 47 */
@@ -229,11 +233,48 @@ static const struct file_operations pps_cdev_fops = {
229 .release = pps_cdev_release, 233 .release = pps_cdev_release,
230}; 234};
231 235
236static void pps_device_destruct(struct device *dev)
237{
238 struct pps_device *pps = dev_get_drvdata(dev);
239
240 /* release id here to protect others from using it while it's
241 * still in use */
242 spin_lock_irq(&pps_idr_lock);
243 idr_remove(&pps_idr, pps->id);
244 spin_unlock_irq(&pps_idr_lock);
245
246 kfree(dev);
247 kfree(pps);
248}
249
232int pps_register_cdev(struct pps_device *pps) 250int pps_register_cdev(struct pps_device *pps)
233{ 251{
234 int err; 252 int err;
235 dev_t devt; 253 dev_t devt;
236 254
255 /* Get new ID for the new PPS source */
256 if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0)
257 return -ENOMEM;
258
259 /* Now really allocate the PPS source.
260 * After idr_get_new() calling the new source will be freely available
261 * into the kernel.
262 */
263 spin_lock_irq(&pps_idr_lock);
264 err = idr_get_new(&pps_idr, pps, &pps->id);
265 spin_unlock_irq(&pps_idr_lock);
266
267 if (err < 0)
268 return err;
269
270 pps->id &= MAX_ID_MASK;
271 if (pps->id >= PPS_MAX_SOURCES) {
272 pr_err("%s: too many PPS sources in the system\n",
273 pps->info.name);
274 err = -EBUSY;
275 goto free_idr;
276 }
277
237 devt = MKDEV(MAJOR(pps_devt), pps->id); 278 devt = MKDEV(MAJOR(pps_devt), pps->id);
238 279
239 cdev_init(&pps->cdev, &pps_cdev_fops); 280 cdev_init(&pps->cdev, &pps_cdev_fops);
@@ -243,13 +284,15 @@ int pps_register_cdev(struct pps_device *pps)
243 if (err) { 284 if (err) {
244 pr_err("%s: failed to add char device %d:%d\n", 285 pr_err("%s: failed to add char device %d:%d\n",
245 pps->info.name, MAJOR(pps_devt), pps->id); 286 pps->info.name, MAJOR(pps_devt), pps->id);
246 return err; 287 goto free_idr;
247 } 288 }
248 pps->dev = device_create(pps_class, pps->info.dev, devt, pps, 289 pps->dev = device_create(pps_class, pps->info.dev, devt, pps,
249 "pps%d", pps->id); 290 "pps%d", pps->id);
250 if (IS_ERR(pps->dev)) 291 if (IS_ERR(pps->dev))
251 goto del_cdev; 292 goto del_cdev;
252 293
294 pps->dev->release = pps_device_destruct;
295
253 pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, 296 pr_debug("source %s got cdev (%d:%d)\n", pps->info.name,
254 MAJOR(pps_devt), pps->id); 297 MAJOR(pps_devt), pps->id);
255 298
@@ -258,6 +301,11 @@ int pps_register_cdev(struct pps_device *pps)
258del_cdev: 301del_cdev:
259 cdev_del(&pps->cdev); 302 cdev_del(&pps->cdev);
260 303
304free_idr:
305 spin_lock_irq(&pps_idr_lock);
306 idr_remove(&pps_idr, pps->id);
307 spin_unlock_irq(&pps_idr_lock);
308
261 return err; 309 return err;
262} 310}
263 311