#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>
#define MAJOR_NUM 100 //主设备号
#define MINOR_NUM 0 //次设备号
#define GLOBALMEM_SIZE 0x400 //内存空间大小
#define CMD_1 0x01
#define CMD_2 0x02
#define CMD_3 0x03
static dev_t dev; //dev_t类型是unsigned int 类型,32位,用于在驱动程序中定义设备编号
static struct cdev mychardev; //字符设备
static struct class *myclass; //class
//static char mem[GLOBALMEM_SIZE] = {0}; //全局内存空间
static int mychardev_open(struct inode *inode, struct file *file);
static int mychardev_release(struct inode *inode, struct file *file);
static ssize_t mychardev_read(struct file *filp, char *buf, size_t len, loff_t *ppos);
static ssize_t mychardev_write(struct file *filp, const char *buf, size_t len, loff_t *ppos);
static long mychardev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
struct file_operations mychardev_fops =
{
.owner = THIS_MODULE,
.open = mychardev_open,
.release = mychardev_release,
.read = mychardev_read,
.write = mychardev_write,
.unlocked_ioctl = mychardev_ioctl,
};
static int mychardev_open(struct inode *inode, struct file *file)
{
try_module_get(THIS_MODULE); //模块计数加一
printk("test:This chrdev is open\n");
return 0;
}
static int mychardev_release(struct inode *inode, struct file *file)
{
module_put(THIS_MODULE); //模块计数减一
printk("test:This chrdev is release\n");
return 0;
}
static ssize_t mychardev_read(struct file *filp, char *buf, size_t len, loff_t *ppos)
{
int ret;
unsigned long p = *ppos;
char mem[GLOBALMEM_SIZE] = "mychardev_read";
printk("test:This chrdev is reading\n");
if(p >= GLOBALMEM_SIZE)
return 0;
if(len > GLOBALMEM_SIZE - p)
len = GLOBALMEM_SIZE - p;
ret = copy_to_user(buf, mem + p, len); //copy_to_user(to, from, n)
if(ret)
return -EFAULT;
*ppos += len;
ret = len;
printk("test:Read %u bytes from %lu, Read:%s\n", len, p, mem);
return ret;
}
static ssize_t mychardev_write(struct file *filp, const char *buf, size_t len, loff_t *ppos)
{
int ret;
unsigned long p = *ppos;
char mem[GLOBALMEM_SIZE] = {0};
printk("test:This chrdev is writting\n");
if(p >= GLOBALMEM_SIZE)
return 0;
if(len > GLOBALMEM_SIZE - p)
len = GLOBALMEM_SIZE - p;
ret = copy_from_user(mem + p, buf, len); //copy_from_user(to, from, n)
if(ret)
return -EFAULT;
*ppos += len;
ret = len;
printk("test:Write %u bytes from %lu, write:%s\n", len, p, mem);
return ret;
}
static long mychardev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
char mem[GLOBALMEM_SIZE] = {0};
printk("test:mychardev_ioctl start\n");
switch(cmd){
case CMD_1:
//memset(mem, 0, GLOBALMEM_SIZE);
printk("test:CMD1: Mem has been reset\n");
break;
case CMD_2:
ret = copy_from_user(mem, (char *)arg, GLOBALMEM_SIZE); //copy_from_user(to, from, n)
printk("test:CMD2: Mem has been write = %s\n", mem);
break;
case CMD_3:
strcpy(mem, "hello");
ret = copy_to_user((char *)arg, mem, GLOBALMEM_SIZE);
printk("test:CMD3: Mem has been read = %s\n", mem);
break;
default:
return -EFAULT;
}
return 0;
}
static int __init test_init(void)
{
int major = MAJOR_NUM;
int minor = MINOR_NUM;
int ret;
dev = MKDEV(MAJOR_NUM, MINOR_NUM); //MKDEV宏可以通过主设备号和次设备号生成dev_t
printk("----------------[test_init]----------------");
printk("[test_init] start");
printk("----------------[test_init]----------------");
/* 先申请设备号 */
if(major)
{
//如果主设备号存在,则静态申请设备号
ret = register_chrdev_region(dev, 1, "sunlh"); //dev_t 次设备号个数 编号对应设备名称(cat /proc/devices可以查看设备号及名称)
}
else
{
//如果主设备号不存在,则动态申请
ret = alloc_chrdev_region(&dev, 0, 1, "sunlh"); //dev_t 次设备号起始 次设备号个数 编号对应设备名称(cat /proc/devices可以查看设备号及名称)
major = MAJOR(dev); //获取分配的主设备号
minor = MINOR(dev); //获取分配的次设备号
}
if(ret < 0)
{
printk("register chrdev region failed");
return -EFAULT;
}
/* 其次注册字符设备驱动,将设备与file_operation绑定 */
cdev_init(&mychardev, &mychardev_fops);
mychardev.owner = THIS_MODULE;
/* 最后向系统添加设备,将申请的设备号与字符设备绑定 */
ret = cdev_add(&mychardev, dev, 1); //字符设备 dev_t 次设备号个数
if(ret)
{
printk("fail to add cdev");
return -EFAULT;
}
/**
* device_create - creates a device and registers it with sysfs
* @class: pointer to the struct class that this device should be registered to
* @parent: pointer to the parent struct device of this new device, if any
* @devt: the dev_t for the char device to be added
* @drvdata: the data to be added to the device for callbacks
* @fmt: string for the device's name
**/
/* 创建class并将class注册到内核中,返回值为class结构指针
在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用device_create在/dev/下创建对应的设备。 */
myclass = class_create(THIS_MODULE, "sunlh_class"); // ls /sys/class 可以查看设备类型名称
device_create(myclass, NULL, dev, NULL, "sunlh_dev"); // ls /dev/ 可以查看设备名称
printk("----------------[test_init]----------------");
printk("[test_init] end");
printk("----------------[test_init]----------------");
return 0;
}
static void __exit test_exit(void)
{
printk("----------------[test_exit]----------------");
printk("[test_exit] start");
printk("----------------[test_exit]----------------");
device_destroy(myclass, dev);
class_destroy(myclass);
cdev_del(&mychardev);
unregister_chrdev_region(dev, 1);
printk("----------------[test_exit]----------------");
printk("[test_exit] end");
printk("----------------[test_exit]----------------");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL v2");
10-28
856
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)