キャラクタデバイスドライバ
chr_drv.c
キャラクタデバイスファイルをオープンすると、メジャー/マイナ番号が設定されているinode->i_rdevからcdev_mapのデバイスを取得し、かかるデバイスのコールバックがファイルコールバックとして設定されます。
#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:babakaka255register_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); }