/*
|
* Copyright (C) 2024 LingYun IoT System Studio
|
* Author: Guo Wenxue <guowenxue@gmail.com>
|
*
|
* A character skeleton driver example in linux kernel.
|
*/
|
|
#include <linux/module.h>
|
#include <linux/init.h>
|
#include <linux/kernel.h> /* printk() */
|
#include <linux/fs.h> /* everything... */
|
#include <linux/errno.h> /* error codes */
|
#include <linux/types.h> /* size_t */
|
#include <linux/cdev.h> /* cdev */
|
#include <linux/version.h> /* kernel version code */
|
#include <linux/moduleparam.h>
|
|
//#define CONFIG_DYNAMIC_ALLOC
|
|
/* device name and major number */
|
#define DEV_NAME "chrdev"
|
|
#ifdef CONFIG_DYNAMIC_ALLOC
|
#define DEV_MAJOR 0
|
#else
|
#define DEV_MAJOR 79
|
#endif
|
|
int dev_major = DEV_MAJOR;
|
module_param(dev_major, int, S_IRUGO);
|
|
#ifdef CONFIG_DYNAMIC_ALLOC
|
struct cdev *cdev;
|
#else
|
struct cdev cdev;
|
#endif
|
|
static int chrdev_open (struct inode *inode, struct file *file)
|
{
|
return 0;
|
}
|
|
static int chrdev_close (struct inode *node, struct file *file)
|
{
|
return 0;
|
}
|
|
static struct file_operations chrdev_fops = {
|
.owner = THIS_MODULE,
|
.open = chrdev_open, /* open() implementation */
|
.release = chrdev_close, /* close() implementation */
|
};
|
|
static int __init chrdev_init(void)
|
{
|
dev_t devno;
|
int rv;
|
|
/* 1. Allocate device number */
|
if(0 != dev_major)
|
{
|
devno = MKDEV(dev_major, 0);
|
rv = register_chrdev_region(devno, 1, DEV_NAME);
|
}
|
else
|
{
|
rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
|
dev_major = MAJOR(devno);
|
}
|
|
if(rv < 0)
|
{
|
printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major);
|
return -ENODEV;
|
}
|
|
/* 2. Allocate and initialize cdev */
|
#ifdef CONFIG_DYNAMIC_ALLOC
|
cdev = cdev_alloc();
|
if( !cdev )
|
{
|
printk(KERN_ERR "Unable to allocate cdev\n");
|
goto failed1;
|
}
|
cdev_init(cdev, &chrdev_fops);
|
#else
|
cdev_init(&cdev, &chrdev_fops);
|
cdev.owner = THIS_MODULE;
|
#endif
|
|
/* 3. Register cdev to linux kernel */
|
#ifdef CONFIG_DYNAMIC_ALLOC
|
rv = cdev_add(cdev, devno, 1);
|
#else
|
rv = cdev_add(&cdev, devno, 1);
|
#endif
|
if( rv )
|
{
|
rv = -ENODEV;
|
printk(KERN_ERR "%s driver regist failed, rv=%d\n", DEV_NAME, rv);
|
goto failed1;
|
}
|
|
printk(KERN_INFO "%s driver on major[%d] installed.\n", DEV_NAME, dev_major);
|
return 0;
|
|
failed1:
|
unregister_chrdev_region(devno, 1);
|
|
printk(KERN_ERR "%s driver installed failed.\n", DEV_NAME);
|
return rv;
|
}
|
|
static void __exit chrdev_exit(void)
|
{
|
#ifdef CONFIG_DYNAMIC_ALLOC
|
cdev_del(cdev);
|
#else
|
cdev_del(&cdev);
|
#endif
|
unregister_chrdev_region(MKDEV(dev_major,0), 1);
|
|
printk(KERN_INFO "%s driver removed!\n", DEV_NAME);
|
return;
|
}
|
|
module_init(chrdev_init);
|
module_exit(chrdev_exit);
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>");
|