キャラクタデバイスドライバ
Rev.1を表示中。最新版はこちら。
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: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);
}




