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