drivers/pps/pps.c

Source file repositories/reference/linux-study-clean/drivers/pps/pps.c

File Facts

System
Linux kernel
Corpus path
drivers/pps/pps.c
Extension
.c
Size
11601 bytes
Lines
504
Domain
Driver Families
Bucket
drivers/pps
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct file_operations pps_cdev_fops = {
	.owner		= THIS_MODULE,
	.poll		= pps_cdev_poll,
	.fasync		= pps_cdev_fasync,
	.compat_ioctl	= pps_cdev_compat_ioctl,
	.unlocked_ioctl	= pps_cdev_ioctl,
	.open		= pps_cdev_open,
	.release	= pps_cdev_release,
};

static void pps_device_destruct(struct device *dev)
{
	struct pps_device *pps = dev_get_drvdata(dev);

	pr_debug("deallocating pps%d\n", pps->id);
	kfree(pps);
}

int pps_register_cdev(struct pps_device *pps)
{
	int err;

	mutex_lock(&pps_idr_lock);
	/*
	 * Get new ID for the new PPS source.  After idr_alloc() calling
	 * the new source will be freely available into the kernel.
	 */
	err = idr_alloc(&pps_idr, pps, 0, PPS_MAX_SOURCES, GFP_KERNEL);
	if (err < 0) {
		if (err == -ENOSPC) {
			pr_err("%s: too many PPS sources in the system\n",
			       pps->info.name);
			err = -EBUSY;
		}
		kfree(pps);
		goto out_unlock;
	}
	pps->id = err;

	pps->dev.class = &pps_class;
	pps->dev.parent = pps->info.dev;
	pps->dev.devt = MKDEV(pps_major, pps->id);
	dev_set_drvdata(&pps->dev, pps);
	dev_set_name(&pps->dev, "pps%d", pps->id);
	pps->dev.release = pps_device_destruct;
	err = device_register(&pps->dev);
	if (err)
		goto free_idr;

	pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, pps_major,
		 pps->id);

	get_device(&pps->dev);
	mutex_unlock(&pps_idr_lock);
	return 0;

free_idr:
	idr_remove(&pps_idr, pps->id);
	put_device(&pps->dev);
out_unlock:
	mutex_unlock(&pps_idr_lock);
	return err;
}

void pps_unregister_cdev(struct pps_device *pps)
{
	pr_debug("unregistering pps%d\n", pps->id);
	pps->lookup_cookie = NULL;
	device_destroy(&pps_class, pps->dev.devt);

	/* Now we can release the ID for re-use */
	mutex_lock(&pps_idr_lock);
	idr_remove(&pps_idr, pps->id);
	put_device(&pps->dev);
	mutex_unlock(&pps_idr_lock);
}

/*
 * Look up a pps device by magic cookie.
 * The cookie is usually a pointer to some enclosing device, but this
 * code doesn't care; you should never be dereferencing it.
 *
 * This is a bit of a kludge that is currently used only by the PPS
 * serial line discipline.  It may need to be tweaked when a second user
 * is found.
 *
 * There is no function interface for setting the lookup_cookie field.
 * It's initialized to NULL when the pps device is created, and if a
 * client wants to use it, just fill it in afterward.
 *

Annotation

Implementation Notes