miscキャラクタデバイス(実装)
miscキャラクタデバイスは、メジャ番号10、コールバック関数がmisc_fopsで登録され、一般のキャラクタデバイスと同じ管理下に置かれます。proc_create()は/proc/misc、class_create()は/sys/class/miscを作成します。
取得できれば、スペシャルファイルのオペレーションコールバックを取得したオペレーションコールバックに差し替え、そのopen()をコールすることで、該当するデバイスのオープンとなります。
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.llseek = noop_llseek,
};
#define MISC_MAJOR 10
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
misc_class = class_create(THIS_MODULE, "misc");
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk:
printk("unable to get major %d for misc devices\n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
}
miscデバスファイル配下のスペシャルファイルがオープンされた時のコールバック関数で、misc_register()で登録されたmisc_listにリストされているキャラクタデバイスを走査して、マイナ番号の一致するデバイスを取得します。取得できなければ、char-major-10-マイナとするlkmをinsmodして再走査します。これで取得できなければエラーです。取得できれば、スペシャルファイルのオペレーションコールバックを取得したオペレーションコールバックに差し替え、そのopen()をコールすることで、該当するデバイスのオープンとなります。
static int misc_open(struct inode * inode, struct file * file)
{
int minor = iminor(inode);
struct miscdevice *c;
int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops);
break;
}
}
if (!new_fops) {
mutex_unlock(&misc_mtx);
request_module("char-major-%d-%d", MISC_MAJOR, minor);
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops);
break;
}
}
if (!new_fops)
goto fail;
}
err = 0;
old_fops = file->f_op;
file->f_op = new_fops;
if (file->f_op->open) {
file->private_data = c;
err=file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
}
fops_put(old_fops);
fail:
mutex_unlock(&misc_mtx);
return err;
}
/proc/miscのコールバックで、misc_listを走査して、そのデバイスのマイナ番号とデバイス名を表示します。
static int misc_seq_show(struct seq_file *seq, void *v)
{
const struct miscdevice *p = list_entry(v, struct miscdevice, list);
seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
return 0;
}
lkm等でキャラクタデバイスをmiscデバイスに登録する時にコールし、マイナ番号が255なら空き番号を割り当て、misc_listに該当するデバイスのmiscを登録します。device_create()は、sysfsに登録し、スペシャルファイルを作成します。
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
#define MISC_DYNAMIC_MINOR 255
int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
}
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
err = PTR_ERR(misc->this_device);
goto out;
}
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
}




