キャラクタデバイスドライバの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");
stackoverflow.com
書き方を調べていたところ
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するのが良いのだろうか・・・