キャラクタデバイスドライバ


chr_drv.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");

#define DEVICE_NAME "babakaka"
#define MAJOR_NO    901

static  ssize_t chdrv_write(struct file *file, const char __user *buf,
                                size_t size, loff_t *pos)
{
       char    read_buf[32];
       int     len;
       unsigned int minor;

       len = (size > 32)? 32: size;
       minor = iminor(file->f_path.dentry->d_inode);

       if(!_copy_from_user(read_buf, buf,len)) {
               read_buf[len] = 0;
               printk("minor no %d:%s",minor, read_buf);
       }
       return  len;
}

static const struct file_operations fileops = {
       .owner = THIS_MODULE,
       .write = chdrv_write
};

static int __init chdrv_init(void)
{
       int     ret = 0;

      if (register_chrdev(MAJOR_NO, DEVICE_NAME, &fileops) < 0) {
               ret = -1;
       }
       return ret;
}

static void __exit chdrv_exit(void)
{
       unregister_chrdev(MAJOR_NO, DEVICE_NAME);
}

module_init(chdrv_init);
module_exit(chdrv_exit);
動作
[root@localhost north]# insmod chr_drv.ko
[root@localhost north]#
[root@localhost north]# cat /proc/devices | grep babakaka
901 babakaka

[root@localhost north]# mknod abcd0 c 901 0
[root@localhost north]# mknod abcd1 c 901 1
[root@localhost north]# mknod abcd2 c 901 2
[root@localhost north]# mknod abcd255 c 901 255
[root@localhost north]# mknod abcd256 c 901 256
[root@localhost north]# ls -l abcd*
crw-r--r-- 1 root root 901,   0  4月 17 00:12 abcd0
crw-r--r-- 1 root root 901,   1  4月 17 00:12 abcd1
crw-r--r-- 1 root root 901,   2  4月 17 00:13 abcd2
crw-r--r-- 1 root root 901, 255  4月 17 00:13 abcd255
crw-r--r-- 1 root root 901, 256  4月 17 00:13 abcd256

[root@localhost north]# echo babakaka0 > abcd0
[root@localhost north]# echo babakaka1 > abcd1
[root@localhost north]# echo babakaka2 > abcd2
[root@localhost north]# echo babakaka255 > abcd255
[root@localhost north]# echo babakaka256 > abcd256
-bash: abcd256: そのようなデバイスやアドレスはありません

[root@localhost north]# dmesg
  :
[ 3569.445479] minor no 0:babakaka0
[ 3574.389954] minor no 1:babakaka1
[ 3580.124261] minor no 2:babakaka2
[ 3585.070913] minor no 255:babakaka255
register_chrdev()はメジャー番号をインデックスとしてstatic struct kobj_map *cdev_mapにマイナー番号0から255までのキャラクタデバイスを登録します。majorが0だと、未使用のメジャー番号を取得し登録されます。特定のマイナー番号による設定なら、直接__register_chrdev()をコールする事で実装できます。

キャラクタデバイスファイルをオープンすると、メジャー/マイナ番号が設定されているinode->i_rdevからcdev_mapのデバイスを取得し、かかるデバイスのコールバックがファイルコールバックとして設定されます。
static inline int register_chrdev(unsigned int major, const char *name,
                                 const struct file_operations *fops)
{
       return __register_chrdev(major, 0, 256, name, fops);
}

int __register_chrdev(unsigned int major, unsigned int baseminor,
                     unsigned int count, const char *name,
                     const struct 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);

       cdev = cdev_alloc();
       if (!cdev)
               goto out2;

       cdev->owner = fops->owner;
       cdev->ops = fops;
       kobject_set_name(&cdev->kobj, "%s", name);

       err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
       if (err)
               goto out;

       cd->cdev = cdev;

       return major ? 0 : cd->major;
out:
       kobject_put(&cdev->kobj);
out2:
       kfree(__unregister_chrdev_region(cd->major, baseminor, count));
       return err;
}

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;

       /* Check for overlapping minor ranges.  */
       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;

               /* New driver overlaps from the left.  */
               if (new_max >= old_min && new_max <= old_max) {
                       ret = -EBUSY;
                       goto out;
               }

               /* New driver overlaps from the right.  */
               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);
}

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;
}

static struct kobj_map *cdev_map;

void __init chrdev_init(void)
{
       cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
       bdi_init(&directly_mappable_cdev_bdi);
}
キャラクタデバイスのopenされるとchrdev_open()がコールされ、inode->i_rdevでcdev_mapからkojを取得し、そのオペレーションコールバックをfilpのコールバックとし、そのopenをコールします。
static int chrdev_open(struct inode *inode, struct file *filp)
{
       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);

               p = inode->i_cdev;
               if (!p) {
                       inode->i_cdev = p = new;
                       list_add(&inode->i_devices, &p->list);
                       new = NULL;
               } else if (!cdev_get(p))
                       ret = -ENXIO;
       } else if (!cdev_get(p))
               ret = -ENXIO;
       spin_unlock(&cdev_lock);
       cdev_put(new);
       if (ret)
               return ret;

       ret = -ENXIO;
       filp->f_op = fops_get(p->ops);
       if (!filp->f_op)
               goto out_cdev_put;

       if (filp->f_op->open) {
               ret = filp->f_op->open(inode, filp);
               if (ret)
                       goto out_cdev_put;
       }

       return 0;

out_cdev_put:
       cdev_put(p);
       return ret;
}

struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
{
       struct kobject *kobj;
       struct probe *p;
       unsigned long best = ~0UL;

retry:
       mutex_lock(domain->lock);
       for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
               struct kobject *(*probe)(dev_t, int *, void *);
               struct module *owner;
               void *data;

               if (p->dev > dev || p->dev + p->range - 1 < dev)
                       continue;
               if (p->range - 1 >= best)
                       break;
               if (!try_module_get(p->owner))
                       continue;
               owner = p->owner;
               data = p->data;
               probe = p->get;
               best = p->range - 1;
               *index = dev - p->dev;
               if (p->lock && p->lock(dev, data) < 0) {
                       module_put(owner);
                       continue;
               }
               mutex_unlock(domain->lock);
               kobj = probe(dev, index, data);

               module_put(owner);
               if (kobj)
                       return kobj;
               goto retry;
       }
       mutex_unlock(domain->lock);
       return NULL;
}

#define MAJOR(dev)      ((dev)>>8)
#define MINOR(dev)      ((dev) & 0xff)
#define MKDEV(ma,mi)    ((ma)<<8 | (mi))

static inline unsigned iminor(const struct inode *inode)
{
       return MINOR(inode->i_rdev);
}


最終更新 2016/04/16 17:36:06 - north
(2016/04/16 17:23:08 作成)


検索

アクセス数
3585191
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。