天天看點

字元裝置初始化流程



字元裝置初始化流程:

1.

register_chrdev_region() - register a range of device numbers

alloc_chrdev_region() - register a range of char device numbers

1.1

Register a single major with a specified minor range.

2.

cdev_init() - initialize a cdev structure

3.

cdev_add() - add a char device to the system

static struct char_device_struct {

struct char_device_struct *next;

unsigned int major;

unsigned int baseminor;

int minorct;

char name[64];

struct cdev *cdev;

} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

static int __init s3c2440_leds_init(void)

{

int result;

dev_t ledno = MKDEV(leds_major, 0);

char dev_name[] = "leds";

if (leds_major)

{

result = register_chrdev_region(ledno, 1, dev_name);

}

else

{

result = alloc_chrdev_region(&ledno, 0, 1, dev_name);

leds_major = MAJOR(ledno);

}

if (result < 0)

{

printk(KERN_WARNING "leds:unable to get major %d\n", leds_major);

return result;

}

cdev_init(&leds_dev, &leds_fops);

leds_dev.owner = THIS_MODULE;

leds_dev.ops = &leds_fops;

result = cdev_add(&leds_dev, MKDEV(leds_major, 0), 1);

if (result)

printk(KERN_NOTICE "Error %d adding leds\n", result);

printk("Leds device installed,with major %d\n", leds_major);

printk("The device name is :%s\n", dev_name);

leds_class = class_create(THIS_MODULE, "leds_driver");

device_create(leds_class, NULL, MKDEV(leds_major, 0), NULL, "leds");

return 0;

}

int register_chrdev_region(dev_t from, unsigned count, const char *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);

}

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,

const char *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;

}

static struct char_device_struct *

__register_chrdev_region(unsigned int major, unsigned int baseminor,

   int minorct, const char *name)

{

struct char_device_struct *cd, **cp;

int ret = 0;

int i;

cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);

if (cd == NULL)

return ERR_PTR(-ENOMEM);

mutex_lock(&chrdevs_lock);

if (major == 0) {

for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {

if (chrdevs[i] == NULL)

break;

}

if (i == 0) {

ret = -EBUSY;

goto out;

}

major = i;

ret = major;

}

cd->major = major;

cd->baseminor = baseminor;

cd->minorct = minorct;

strlcpy(cd->name, name, sizeof(cd->name));

i = major_to_index(major);

for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)

if ((*cp)->major > major ||

    ((*cp)->major == major &&

     (((*cp)->baseminor >= baseminor) ||

      ((*cp)->baseminor + (*cp)->minorct > baseminor))))

break;

if (*cp && (*cp)->major == major) {

int old_min = (*cp)->baseminor;

int old_max = (*cp)->baseminor + (*cp)->minorct - 1;

int new_min = baseminor;

int new_max = baseminor + minorct - 1;

if (new_max >= old_min && new_max <= old_max) {

ret = -EBUSY;

goto out;

}

if (new_min <= old_max && new_min >= old_min) {

ret = -EBUSY;

goto out;

}

}

cd->next = *cp;

*cp = cd;

mutex_unlock(&chrdevs_lock);

return cd;

out:

mutex_unlock(&chrdevs_lock);

kfree(cd);

return ERR_PTR(ret);

}

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

{

memset(cdev, 0, sizeof *cdev);

INIT_LIST_HEAD(&cdev->list);

kobject_init(&cdev->kobj, &ktype_cdev_default);

cdev->ops = fops;

}

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char *err_str;

if (!kobj) {

err_str = "invalid kobject pointer!";

goto error;

}

if (!ktype) {

err_str = "must have a ktype to be initialized properly!\n";

goto error;

}

if (kobj->state_initialized) {

printk(KERN_ERR "kobject (%p): tried to init an initialized "

       "object, something is seriously wrong.\n", kobj);

dump_stack();

}

kobject_init_internal(kobj);

kobj->ktype = ktype;

return;

error:

printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);

dump_stack();

}

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

{

p->dev = dev;

p->count = count;

return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);

}

int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,

     struct module *module, kobj_probe_t *probe,

     int (*lock)(dev_t, void *), void *data)

{

unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;

unsigned index = MAJOR(dev);

unsigned i;

struct probe *p;

if (n > 255)

n = 255;

p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);

if (p == NULL)

return -ENOMEM;

for (i = 0; i < n; i++, p++) {

p->owner = module;

p->get = probe;

p->lock = lock;

p->dev = dev;

p->range = range;

p->data = data;

}

mutex_lock(domain->lock);

for (i = 0, p -= n; i < n; i++, p++, index++) {

struct probe **s = &domain->probes[index % 255];

while (*s && (*s)->range < range)

s = &(*s)->next;

p->next = *s;

*s = p;

}

mutex_unlock(domain->lock);

return 0;

}