mutex_lock(&chrdevs_lock); for (cd = chrdevs[major_to_index(offset)]; cd; cd = cd->next) { if (cd->major == offset)
seq_printf(f, "%3d %s\n", cd->major, cd->name);
}
mutex_unlock(&chrdevs_lock);
}
#endif/* CONFIG_PROC_FS */
staticint find_dynamic_major(void)
{ int i; struct char_device_struct *cd;
for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) { if (chrdevs[i] == NULL) return i;
}
for (i = CHRDEV_MAJOR_DYN_EXT_START;
i >= CHRDEV_MAJOR_DYN_EXT_END; i--) { for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next) if (cd->major == i) break;
if (cd == NULL) return i;
}
return -EBUSY;
}
/* * Register a single major with a specified minor range. * * If major == 0 this function will dynamically allocate an unused major. * If major > 0 this function will attempt to reserve the range of minors * with given major. *
*/ staticstruct char_device_struct *
__register_chrdev_region(unsignedint major, unsignedint baseminor, int minorct, constchar *name)
{ struct char_device_struct *cd, *curr, *prev = NULL; int ret; int i;
if (major >= CHRDEV_MAJOR_MAX) {
pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
name, major, CHRDEV_MAJOR_MAX-1); return ERR_PTR(-EINVAL);
}
if (minorct > MINORMASK + 1 - baseminor) {
pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n",
name, baseminor, baseminor + minorct - 1, 0, MINORMASK); return ERR_PTR(-EINVAL);
}
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); if (cd == NULL) return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock);
if (major == 0) {
ret = find_dynamic_major(); if (ret < 0) {
pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",
name); goto out;
}
major = ret;
}
ret = -EBUSY;
i = major_to_index(major); for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) { if (curr->major < major) continue;
if (curr->major > major) break;
if (curr->baseminor + curr->minorct <= baseminor) continue;
if (curr->baseminor >= baseminor + minorct) break;
staticstruct char_device_struct *
__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
{ struct char_device_struct *cd = NULL, **cp; int i = major_to_index(major);
mutex_lock(&chrdevs_lock); for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major == major &&
(*cp)->baseminor == baseminor &&
(*cp)->minorct == minorct) break; if (*cp) {
cd = *cp;
*cp = cd->next;
}
mutex_unlock(&chrdevs_lock); return cd;
}
/** * register_chrdev_region() - register a range of device numbers * @from: the first in the desired range of device numbers; must include * the major number. * @count: the number of consecutive device numbers required * @name: the name of the device or driver. * * Return value is zero on success, a negative error code on failure.
*/ int register_chrdev_region(dev_t from, unsigned count, constchar *name)
{ struct char_device_struct *cd;
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0); if (next > to)
next = to;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name); if (IS_ERR(cd)) goto fail;
} return 0;
fail:
to = n; for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
} return PTR_ERR(cd);
}
/** * alloc_chrdev_region() - register a range of char device numbers * @dev: output parameter for first assigned number * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: the name of the associated device or driver * * Allocates a range of char device numbers. The major number will be * chosen dynamically, and returned (along with the first minor number) * in @dev. Returns zero or a negative error code.
*/ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, constchar *name)
{ struct char_device_struct *cd;
cd = __register_chrdev_region(0, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd);
*dev = MKDEV(cd->major, cd->baseminor); return 0;
}
/** * __register_chrdev() - create and register a cdev occupying a range of minors * @major: major device number or 0 for dynamic allocation * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: name of this range of devices * @fops: file operations associated with this devices * * If @major == 0 this functions will dynamically allocate a major and return * its number. * * If @major > 0 this function will attempt to reserve a device with the given * major number and will return zero on success. * * Returns a -ve errno on failure. * * The name of this device has nothing to do with the name of the device in * /dev. It only helps to keep track of the different owners of devices. If * your module name has only one type of devices it's ok to use e.g. the name * of the module here.
*/ int __register_chrdev(unsignedint major, unsignedint baseminor, unsignedint count, constchar *name, conststruct file_operations *fops)
{ struct char_device_struct *cd; struct cdev *cdev; int err = -ENOMEM;
cd = __register_chrdev_region(major, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd);
/** * unregister_chrdev_region() - unregister a range of device numbers * @from: the first in the range of numbers to unregister * @count: the number of device numbers to unregister * * This function will unregister a range of @count device numbers, * starting with @from. The caller should normally be the one who * allocated those numbers in the first place...
*/ void unregister_chrdev_region(dev_t from, unsigned count)
{
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0); if (next > to)
next = to;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
}
/** * __unregister_chrdev - unregister and destroy a cdev * @major: major device number * @baseminor: first of the range of minor numbers * @count: the number of minor numbers this cdev is occupying * @name: name of this range of devices * * Unregister and destroy the cdev occupying the region described by * @major, @baseminor and @count. This function undoes what * __register_chrdev() did.
*/ void __unregister_chrdev(unsignedint major, unsignedint baseminor, unsignedint count, constchar *name)
{ struct char_device_struct *cd;
cd = __unregister_chrdev_region(major, baseminor, count); if (cd && cd->cdev)
cdev_del(cd->cdev);
kfree(cd);
}
/* * Called every time a character special file is opened
*/ staticint chrdev_open(struct inode *inode, struct file *filp)
{ conststruct file_operations *fops; struct cdev *p; struct cdev *new = NULL; int ret = 0;
spin_lock(&cdev_lock);
p = inode->i_cdev; if (!p) { struct kobject *kobj; int idx;
spin_unlock(&cdev_lock);
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); if (!kobj) return -ENXIO; new = container_of(kobj, struct cdev, kobj);
spin_lock(&cdev_lock); /* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p = inode->i_cdev; if (!p) {
inode->i_cdev = p = new;
list_add(&inode->i_devices, &p->list); new = NULL;
} elseif (!cdev_get(p))
ret = -ENXIO;
} elseif (!cdev_get(p))
ret = -ENXIO;
spin_unlock(&cdev_lock);
cdev_put(new); if (ret) return ret;
ret = -ENXIO;
fops = fops_get(p->ops); if (!fops) goto out_cdev_put;
replace_fops(filp, fops); if (filp->f_op->open) {
ret = filp->f_op->open(inode, filp); if (ret) goto out_cdev_put;
}
/* * Dummy default file-operations: the only thing this does * is contain the open that then fills in the correct operations * depending on the special file...
*/ conststruct file_operations def_chr_fops = {
.open = chrdev_open,
.llseek = noop_llseek,
};
/** * cdev_add() - add a char device to the system * @p: the cdev structure for the device * @dev: the first device number for which this device is responsible * @count: the number of consecutive minor numbers corresponding to this * device * * cdev_add() adds the device represented by @p to the system, making it * live immediately. A negative error code is returned on failure.
*/ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{ int error;
/** * cdev_set_parent() - set the parent kobject for a char device * @p: the cdev structure * @kobj: the kobject to take a reference to * * cdev_set_parent() sets a parent kobject which will be referenced * appropriately so the parent is not freed before the cdev. This * should be called before cdev_add.
*/ void cdev_set_parent(struct cdev *p, struct kobject *kobj)
{
WARN_ON(!kobj->state_initialized);
p->kobj.parent = kobj;
}
/** * cdev_device_add() - add a char device and it's corresponding * struct device, linkink * @dev: the device structure * @cdev: the cdev structure * * cdev_device_add() adds the char device represented by @cdev to the system, * just as cdev_add does. It then adds @dev to the system using device_add * The dev_t for the char device will be taken from the struct device which * needs to be initialized first. This helper function correctly takes a * reference to the parent device so the parent will not get released until * all references to the cdev are released. * * This helper uses dev->devt for the device number. If it is not set * it will not add the cdev and it will be equivalent to device_add. * * This function should be used whenever the struct cdev and the * struct device are members of the same structure whose lifetime is * managed by the struct device. * * NOTE: Callers must assume that userspace was able to open the cdev and * can call cdev fops callbacks at any time, even if this function fails.
*/ int cdev_device_add(struct cdev *cdev, struct device *dev)
{ int rc = 0;
if (dev->devt) {
cdev_set_parent(cdev, &dev->kobj);
rc = cdev_add(cdev, dev->devt, 1); if (rc) return rc;
}
rc = device_add(dev); if (rc && dev->devt)
cdev_del(cdev);
return rc;
}
/** * cdev_device_del() - inverse of cdev_device_add * @cdev: the cdev structure * @dev: the device structure * * cdev_device_del() is a helper function to call cdev_del and device_del. * It should be used whenever cdev_device_add is used. * * If dev->devt is not set it will not remove the cdev and will be equivalent * to device_del. * * NOTE: This guarantees that associated sysfs callbacks are not running * or runnable, however any cdevs already open will remain and their fops * will still be callable even after this function returns.
*/ void cdev_device_del(struct cdev *cdev, struct device *dev)
{
device_del(dev); if (dev->devt)
cdev_del(cdev);
}
/** * cdev_del() - remove a cdev from the system * @p: the cdev structure to be removed * * cdev_del() removes @p from the system, possibly freeing the structure * itself. * * NOTE: This guarantees that cdev device will no longer be able to be * opened, however any cdevs already open will remain and their fops will * still be callable even after cdev_del returns.
*/ void cdev_del(struct cdev *p)
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj);
}
/** * cdev_alloc() - allocate a cdev structure * * Allocates and returns a cdev structure, or NULL on failure.
*/ struct cdev *cdev_alloc(void)
{ struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
} return p;
}
/** * cdev_init() - initialize a cdev structure * @cdev: the structure to initialize * @fops: the file_operations for this device * * Initializes @cdev, remembering @fops, making it ready to add to the * system with cdev_add().
*/ void cdev_init(struct cdev *cdev, conststruct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
staticstruct kobject *base_probe(dev_t dev, int *part, void *data)
{ if (request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0) /* Make old-style 2.4 aliases work */
request_module("char-major-%d", MAJOR(dev)); return NULL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.