diff options
Diffstat (limited to 'drivers/pps/pps.c')
-rw-r--r-- | drivers/pps/pps.c | 50 |
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 @@ | |||
38 | static dev_t pps_devt; | 39 | static dev_t pps_devt; |
39 | static struct class *pps_class; | 40 | static struct class *pps_class; |
40 | 41 | ||
42 | static DEFINE_SPINLOCK(pps_idr_lock); | ||
43 | static 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 | ||
236 | static 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 | |||
232 | int pps_register_cdev(struct pps_device *pps) | 250 | int 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) | |||
258 | del_cdev: | 301 | del_cdev: |
259 | cdev_del(&pps->cdev); | 302 | cdev_del(&pps->cdev); |
260 | 303 | ||
304 | free_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 | ||