キャラクタデバイスドライバのkernel moduleの個人的なテンプレート
kernelは4あたり
#include <linux/module.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/device.h> #include <asm/uaccess.h> #define DEVNAME "hello" #define MINOR_COUNT 1 MODULE_LICENSE("Dual BSD/GPL"); struct hello_dev { int major; int minor; dev_t dev_id; struct cdev *pcdev; struct class* pclass; struct device* pdevice; }; static struct hello_dev *pdev; static int hello_open(struct inode *inode, struct file* flip) { pr_info("dev open\n"); return 0; } static int hello_release(struct inode *inode, struct file* flip) { pr_info("dev close\n"); return 0; } static ssize_t hello_read(struct file *flip, char __user *buf, size_t count, loff_t *offset) { pr_info("dev read\n"); return 0; } static ssize_t hello_write(struct file *flip, const char __user *buf, size_t count, loff_t *offset) { pr_info("dev write\n"); return 0; } static long hello_ioctl(struct file *flip, unsigned int cmd, unsigned long arg) { pr_info("dev ioctl\n"); return 0; } static struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .release = hello_release, .read = hello_read, .write = hello_write, .unlocked_ioctl = hello_ioctl, }; static int hello_init(void) { int ret = -ENOMEM; pr_info("hello\n"); pdev = kmalloc(sizeof(struct hello_dev), GFP_KERNEL); if(pdev == NULL) { pr_err("cannot alloc kern mem\n"); return -ENOMEM; } pdev->pcdev = kmalloc(sizeof(struct cdev), GFP_KERNEL); if(pdev->pcdev == NULL) { pr_err("cannot alloc kern mem for cdev\n"); goto pdev_free; } ret = alloc_chrdev_region(&(pdev->dev_id), 0, MINOR_COUNT, DEVNAME); if(ret < 0) { pr_err("cannot alloc chrdev region\n"); goto cdev_free; } cdev_init(pdev->pcdev, &hello_fops); pdev->pcdev->owner = THIS_MODULE; ret = cdev_add(pdev->pcdev, pdev->dev_id, MINOR_COUNT); if(ret < 0) { pr_err("fail to add cdev\n"); goto cdev_unregist; } pr_info("mojor:%d\n", MAJOR(pdev->dev_id)); pr_info("minor:%d\n", MINOR(pdev->dev_id)); pdev->pclass = class_create(THIS_MODULE, DEVNAME); if(IS_ERR(pdev->pclass)) { pr_err("fail to create class\n"); ret = PTR_ERR(pdev->pclass); goto cdev_del; } pdev->pdevice = device_create(pdev->pclass, NULL, pdev->dev_id, NULL, DEVNAME); if(IS_ERR(pdev->pdevice)) { pr_err("fail to create device\n"); ret = PTR_ERR(pdev->pdevice); goto class_destroy; } pr_info("create success\n"); return 0; class_destroy: class_destroy(pdev->pclass); cdev_del: cdev_del(pdev->pcdev); cdev_unregist: unregister_chrdev_region(pdev->dev_id, MINOR_COUNT); cdev_free: kfree(pdev->pcdev); pdev_free: kfree(pdev); return ret; } static void hello_exit(void) { device_destroy(pdev->pclass, pdev->dev_id); class_destroy(pdev->pclass); cdev_del(pdev->pcdev); unregister_chrdev_region(pdev->dev_id, MINOR_COUNT); kfree(pdev->pcdev); kfree(pdev); pr_info("Goodbye...\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_DESCRIPTION("kernel module template");
書き方を調べていたところ
cdevを登録するのに2つの方法があるらしい。
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
と
struct cdev my_cdev ;
...
cdev_init(&my_cdev,&my_fops);
cdev_add(&my_cdev,num, count)
という方法らしいが、前者はcdevを意識する必要がない。
stackoverflow.com
これは前者でいいじゃんみたいな感じ。。
しかしここで(ちょっと古い)
The cdev interface [LWN.net]
cdev_addが行われるとすぐにkernelによってファイルオペレーションを呼ぶことが可能なので、
呼関連付けられているデバイスの完全な初期化が終わるまではcdev_addを呼ぶべきではない。
と書かれている。
これを踏まえるとデバイスがちゃんと見つかって、probe関数が呼ばれたときに、デバイスを初期化して、
最後にcdev_addするのが良いのだろうか・・・