linux-bk/fs/char_dev.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/char_dev.c
   3 *
   4 *  Copyright (C) 1991, 1992  Linus Torvalds
   5 */
   6
   7#include <linux/config.h>
   8#include <linux/init.h>
   9#include <linux/fs.h>
  10#include <linux/slab.h>
  11#include <linux/string.h>
  12
  13#include <linux/major.h>
  14#include <linux/errno.h>
  15#include <linux/module.h>
  16#include <linux/smp_lock.h>
  17#include <linux/devfs_fs_kernel.h>
  18
  19#include <linux/kobject.h>
  20#include <linux/kobj_map.h>
  21#include <linux/cdev.h>
  22
  23#ifdef CONFIG_KMOD
  24#include <linux/kmod.h>
  25#endif
  26
  27static struct kobj_map *cdev_map;
  28
  29#define MAX_PROBE_HASH 255      /* random */
  30
  31static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
  32
  33static struct char_device_struct {
  34        struct char_device_struct *next;
  35        unsigned int major;
  36        unsigned int baseminor;
  37        int minorct;
  38        const char *name;
  39        struct file_operations *fops;
  40        struct cdev *cdev;              /* will die */
  41} *chrdevs[MAX_PROBE_HASH];
  42
  43/* index in the above */
  44static inline int major_to_index(int major)
  45{
  46        return major % MAX_PROBE_HASH;
  47}
  48
  49/* get char device names in somewhat random order */
  50int get_chrdev_list(char *page)
  51{
  52        struct char_device_struct *cd;
  53        int i, len;
  54
  55        len = sprintf(page, "Character devices:\n");
  56
  57        read_lock(&chrdevs_lock);
  58        for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) {
  59                for (cd = chrdevs[i]; cd; cd = cd->next)
  60                        len += sprintf(page+len, "%3d %s\n",
  61                                       cd->major, cd->name);
  62        }
  63        read_unlock(&chrdevs_lock);
  64
  65        return len;
  66}
  67
  68/*
  69 * Register a single major with a specified minor range.
  70 *
  71 * If major == 0 this functions will dynamically allocate a major and return
  72 * its number.
  73 *
  74 * If major > 0 this function will attempt to reserve the passed range of
  75 * minors and will return zero on success.
  76 *
  77 * Returns a -ve errno on failure.
  78 */
  79static struct char_device_struct *
  80__register_chrdev_region(unsigned int major, unsigned int baseminor,
  81                           int minorct, const char *name)
  82{
  83        struct char_device_struct *cd, **cp;
  84        int ret = 0;
  85        int i;
  86
  87        cd = kmalloc(sizeof(struct char_device_struct), GFP_KERNEL);
  88        if (cd == NULL)
  89                return ERR_PTR(-ENOMEM);
  90
  91        memset(cd, 0, sizeof(struct char_device_struct));
  92
  93        write_lock_irq(&chrdevs_lock);
  94
  95        /* temporary */
  96        if (major == 0) {
  97                for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
  98                        if (chrdevs[i] == NULL)
  99                                break;
 100                }
 101
 102                if (i == 0) {
 103                        ret = -EBUSY;
 104                        goto out;
 105                }
 106                major = i;
 107                ret = major;
 108        }
 109
 110        cd->major = major;
 111        cd->baseminor = baseminor;
 112        cd->minorct = minorct;
 113        cd->name = name;
 114
 115        i = major_to_index(major);
 116
 117        for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
 118                if ((*cp)->major > major ||
 119                    ((*cp)->major == major && (*cp)->baseminor >= baseminor))
 120                        break;
 121        if (*cp && (*cp)->major == major &&
 122            (*cp)->baseminor < baseminor + minorct) {
 123                ret = -EBUSY;
 124                goto out;
 125        }
 126        cd->next = *cp;
 127        *cp = cd;
 128        write_unlock_irq(&chrdevs_lock);
 129        return cd;
 130out:
 131        write_unlock_irq(&chrdevs_lock);
 132        kfree(cd);
 133        return ERR_PTR(ret);
 134}
 135
 136static struct char_device_struct *
 137__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
 138{
 139        struct char_device_struct *cd = NULL, **cp;
 140        int i = major_to_index(major);
 141
 142        write_lock_irq(&chrdevs_lock);
 143        for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
 144                if ((*cp)->major == major &&
 145                    (*cp)->baseminor == baseminor &&
 146                    (*cp)->minorct == minorct)
 147                        break;
 148        if (*cp) {
 149                cd = *cp;
 150                *cp = cd->next;
 151        }
 152        write_unlock_irq(&chrdevs_lock);
 153        return cd;
 154}
 155
 156int register_chrdev_region(dev_t from, unsigned count, char *name)
 157{
 158        struct char_device_struct *cd;
 159        dev_t to = from + count;
 160        dev_t n, next;
 161
 162        for (n = from; n < to; n = next) {
 163                next = MKDEV(MAJOR(n)+1, 0);
 164                if (next > to)
 165                        next = to;
 166                cd = __register_chrdev_region(MAJOR(n), MINOR(n),
 167                               next - n, name);
 168                if (IS_ERR(cd))
 169                        goto fail;
 170        }
 171        return 0;
 172fail:
 173        to = n;
 174        for (n = from; n < to; n = next) {
 175                next = MKDEV(MAJOR(n)+1, 0);
 176                kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
 177        }
 178        return PTR_ERR(cd);
 179}
 180
 181int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, char *name)
 182{
 183        struct char_device_struct *cd;
 184        cd = __register_chrdev_region(0, baseminor, count, name);
 185        if (IS_ERR(cd))
 186                return PTR_ERR(cd);
 187        *dev = MKDEV(cd->major, cd->baseminor);
 188        return 0;
 189}
 190
 191int register_chrdev(unsigned int major, const char *name,
 192                    struct file_operations *fops)
 193{
 194        struct char_device_struct *cd;
 195        struct cdev *cdev;
 196        char *s;
 197        int err = -ENOMEM;
 198
 199        cd = __register_chrdev_region(major, 0, 256, name);
 200        if (IS_ERR(cd))
 201                return PTR_ERR(cd);
 202        
 203        cdev = cdev_alloc();
 204        if (!cdev)
 205                goto out2;
 206
 207        cdev->owner = fops->owner;
 208        cdev->ops = fops;
 209        strcpy(cdev->kobj.name, name);
 210        for (s = strchr(cdev->kobj.name, '/'); s; s = strchr(s, '/'))
 211                *s = '!';
 212                
 213        err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
 214        if (err)
 215                goto out;
 216
 217        cd->cdev = cdev;
 218
 219        return major ? 0 : cd->major;
 220out:
 221        kobject_put(&cdev->kobj);
 222out2:
 223        kfree(__unregister_chrdev_region(cd->major, 0, 256));
 224        return err;
 225}
 226
 227void unregister_chrdev_region(dev_t from, unsigned count)
 228{
 229        dev_t to = from + count;
 230        dev_t n, next;
 231
 232        for (n = from; n < to; n = next) {
 233                next = MKDEV(MAJOR(n)+1, 0);
 234                if (next > to)
 235                        next = to;
 236                kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
 237        }
 238}
 239
 240int unregister_chrdev(unsigned int major, const char *name)
 241{
 242        struct char_device_struct *cd;
 243        cdev_unmap(MKDEV(major, 0), 256);
 244        cd = __unregister_chrdev_region(major, 0, 256);
 245        if (cd && cd->cdev)
 246                cdev_del(cd->cdev);
 247        kfree(cd);
 248        return 0;
 249}
 250
 251static spinlock_t cdev_lock = SPIN_LOCK_UNLOCKED;
 252/*
 253 * Called every time a character special file is opened
 254 */
 255int chrdev_open(struct inode * inode, struct file * filp)
 256{
 257        struct cdev *p;
 258        struct cdev *new = NULL;
 259        int ret = 0;
 260
 261        spin_lock(&cdev_lock);
 262        p = inode->i_cdev;
 263        if (!p) {
 264                struct kobject *kobj;
 265                int idx;
 266                spin_unlock(&cdev_lock);
 267                kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
 268                if (!kobj)
 269                        return -ENODEV;
 270                new = container_of(kobj, struct cdev, kobj);
 271                spin_lock(&cdev_lock);
 272                p = inode->i_cdev;
 273                if (!p) {
 274                        inode->i_cdev = p = new;
 275                        inode->i_cindex = idx;
 276                        list_add(&inode->i_devices, &p->list);
 277                        new = NULL;
 278                } else if (!cdev_get(p))
 279                        ret = -ENODEV;
 280        } else if (!cdev_get(p))
 281                ret = -ENODEV;
 282        spin_unlock(&cdev_lock);
 283        cdev_put(new);
 284        if (ret)
 285                return ret;
 286        filp->f_op = fops_get(p->ops);
 287        if (!filp->f_op) {
 288                cdev_put(p);
 289                return -ENODEV;
 290        }
 291        if (filp->f_op->open) {
 292                lock_kernel();
 293                ret = filp->f_op->open(inode,filp);
 294                unlock_kernel();
 295        }
 296        if (ret)
 297                cdev_put(p);
 298        return ret;
 299}
 300
 301void cd_forget(struct inode *inode)
 302{
 303        spin_lock(&cdev_lock);
 304        list_del_init(&inode->i_devices);
 305        inode->i_cdev = NULL;
 306        spin_unlock(&cdev_lock);
 307}
 308
 309void cdev_purge(struct cdev *cdev)
 310{
 311        spin_lock(&cdev_lock);
 312        while (!list_empty(&cdev->list)) {
 313                struct inode *inode;
 314                inode = container_of(cdev->list.next, struct inode, i_devices);
 315                list_del_init(&inode->i_devices);
 316                inode->i_cdev = NULL;
 317        }
 318        spin_unlock(&cdev_lock);
 319}
 320
 321/*
 322 * Dummy default file-operations: the only thing this does
 323 * is contain the open that then fills in the correct operations
 324 * depending on the special file...
 325 */
 326struct file_operations def_chr_fops = {
 327        .open = chrdev_open,
 328};
 329
 330static struct kobject *exact_match(dev_t dev, int *part, void *data)
 331{
 332        struct cdev *p = data;
 333        return &p->kobj;
 334}
 335
 336static int exact_lock(dev_t dev, void *data)
 337{
 338        struct cdev *p = data;
 339        return cdev_get(p) ? 0 : -1;
 340}
 341
 342int cdev_add(struct cdev *p, dev_t dev, unsigned count)
 343{
 344        int err = kobject_add(&p->kobj);
 345        if (err)
 346                return err;
 347        err = kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
 348        if (err)
 349                kobject_del(&p->kobj);
 350        return err;
 351}
 352
 353void cdev_unmap(dev_t dev, unsigned count)
 354{
 355        kobj_unmap(cdev_map, dev, count);
 356}
 357
 358void cdev_del(struct cdev *p)
 359{
 360        kobject_del(&p->kobj);
 361        kobject_put(&p->kobj);
 362}
 363
 364struct kobject *cdev_get(struct cdev *p)
 365{
 366        struct module *owner = p->owner;
 367        struct kobject *kobj;
 368
 369        if (owner && !try_module_get(owner))
 370                return NULL;
 371        kobj = kobject_get(&p->kobj);
 372        if (!kobj)
 373                module_put(owner);
 374        return kobj;
 375}
 376
 377void cdev_put(struct cdev *p)
 378{
 379        if (p) {
 380                kobject_put(&p->kobj);
 381                module_put(p->owner);
 382        }
 383}
 384
 385static decl_subsys(cdev, NULL, NULL);
 386
 387static void cdev_default_release(struct kobject *kobj)
 388{
 389        struct cdev *p = container_of(kobj, struct cdev, kobj);
 390        cdev_purge(p);
 391}
 392
 393static void cdev_dynamic_release(struct kobject *kobj)
 394{
 395        struct cdev *p = container_of(kobj, struct cdev, kobj);
 396        cdev_purge(p);
 397        kfree(p);
 398}
 399
 400static struct kobj_type ktype_cdev_default = {
 401        .release        = cdev_default_release,
 402};
 403
 404static struct kobj_type ktype_cdev_dynamic = {
 405        .release        = cdev_dynamic_release,
 406};
 407
 408static struct kset kset_dynamic = {
 409        .subsys = &cdev_subsys,
 410        .kobj = {.name = "major",},
 411        .ktype = &ktype_cdev_dynamic,
 412};
 413
 414struct cdev *cdev_alloc(void)
 415{
 416        struct cdev *p = kmalloc(sizeof(struct cdev), GFP_KERNEL);
 417        if (p) {
 418                memset(p, 0, sizeof(struct cdev));
 419                p->kobj.kset = &kset_dynamic;
 420                INIT_LIST_HEAD(&p->list);
 421                kobject_init(&p->kobj);
 422        }
 423        return p;
 424}
 425
 426void cdev_init(struct cdev *cdev, struct file_operations *fops)
 427{
 428        INIT_LIST_HEAD(&cdev->list);
 429        kobj_set_kset_s(cdev, cdev_subsys);
 430        cdev->kobj.ktype = &ktype_cdev_default;
 431        kobject_init(&cdev->kobj);
 432        cdev->ops = fops;
 433}
 434
 435static struct kobject *base_probe(dev_t dev, int *part, void *data)
 436{
 437        if (request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0)
 438                /* Make old-style 2.4 aliases work */
 439                request_module("char-major-%d", MAJOR(dev));
 440        return NULL;
 441}
 442
 443void __init chrdev_init(void)
 444{
 445        subsystem_register(&cdev_subsys);
 446        kset_register(&kset_dynamic);
 447        cdev_map = kobj_map_init(base_probe, &cdev_subsys);
 448}
 449
 450
 451/* Let modules do char dev stuff */
 452EXPORT_SYMBOL(register_chrdev_region);
 453EXPORT_SYMBOL(unregister_chrdev_region);
 454EXPORT_SYMBOL(alloc_chrdev_region);
 455EXPORT_SYMBOL(cdev_init);
 456EXPORT_SYMBOL(cdev_alloc);
 457EXPORT_SYMBOL(cdev_get);
 458EXPORT_SYMBOL(cdev_put);
 459EXPORT_SYMBOL(cdev_del);
 460EXPORT_SYMBOL(cdev_add);
 461EXPORT_SYMBOL(cdev_unmap);
 462EXPORT_SYMBOL(register_chrdev);
 463EXPORT_SYMBOL(unregister_chrdev);
 464
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.