代码拉取完成,页面将自动刷新
/*
* a simple char device driver: golbalmem without mutex
*
* Copyright (C) 2022 Martin Zhang (zhangming_233@163.com)
*
* Licensed under GPLv2 or later.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <asm-generic/ioctl.h>
// #include <linux/ioctl.h>
#include <linux/device.h>
#define GLOBALMEM_SIZE 0x1000 // 4KB
#define GLOBALMEM_MAGIC 'g'
#define MEM_CLEAR _IO(GLOBALMEM_MAGIC,0) // 0x1
#define GLOBALMEM_MAJOR 230
static int globalmem_major = GLOBALMEM_MAJOR;
struct class* globalmem_class;
// 向当前模块传入的参数
module_param(globalmem_major, int, S_IRUGO); // S_IRUGO = S_IRUSR|S_IRGRP|S_IROTH, 用户读, 用户组读, 其他读
struct globalmem_dev {
struct cdev cdev; // 对应globalmem字符设备的dev
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmem_dev* globalmem_devp;
/*
* 支持SEEK_SET(0), SEEK_CUR(1), 不支持SEEK_END(2)
*/
static loff_t globalmem_llseek (struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig) {
case 0: // SEEK_SET
if (offset < 0) {
ret = -EINVAL; // 非法参数
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1: // SEEK_CUR
if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
if ((filp->f_pos + offset) < 0) {
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static ssize_t globalmem_read (struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct globalmem_dev* dev = filp->private_data;
if (p > GLOBALMEM_SIZE) // 达到文件末尾
return 0; // EOF
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p;
if (copy_to_user(buf, dev->mem + p, count)) { // 将内核空间mem[p..p+count)拷贝到用户空间buf
ret = -EFAULT;
} else {
*ppos += count; // 更新偏移位置
ret = count; // 已读取字节数
printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
}
return ret;
}
static ssize_t globalmem_write (struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct globalmem_dev* dev = filp->private_data;
if (p >= GLOBALMEM_SIZE) // 要写的位置超出mem[]边界
return 0;
if (count > GLOBALMEM_SIZE - p) // 要写的用户缓冲区大小超过mem[]大小, 截断
count = GLOBALMEM_SIZE - p;
if (copy_from_user(dev->mem + p, buf, count)) {
ret= -EFAULT;
} else {
*ppos += count; // 更新偏移位置
ret = count; // 已读取字节数
printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
}
return ret;
}
static long globalmem_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
struct globalmem_dev* dev = filp->private_data;
switch(cmd) {
case MEM_CLEAR:
memset(dev->mem, 0, GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero\n");
break;
default:
return -EINVAL;
}
return 0;
}
/*
* Linux 驱动遵循潜规则: 将文件的私有数据private_data 指向设备结构体
* 供read/write/ioctl/llseek 等文件操作函数通过private_data 访问设备结构体
*/
static int globalmem_open (struct inode *inode, struct file *filp)
{
filp->private_data = globalmem_devp;
#if 0
struct globalmem_dev *dev = container_of(inode->i_cdev, struct globalmem_dev, cdev);
filp->private_data = dev;
#endif
return 0;
}
static int globalmem_release (struct inode *inode, struct file *filp)
{
filp->private_data = NULL;
return 0;
}
struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.unlocked_ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
/*
* 完成cdev的初始化与添加
*/
static void globalmem_setup_cdev(struct globalmem_dev* dev, int index)
{
int err, devno = MKDEV(globalmem_major, index);
cdev_init(&dev->cdev, &globalmem_fops);
dev->cdev.owner = THIS_MODULE; // 所属模块
err = cdev_add(&dev->cdev, devno, 1); // 注册一个设备
if (err)
printk(KERN_NOTICE "Error %d adding globalmem%d\n", err, index);
}
static void globalmem_unsetup_cdev(struct globalmem_dev* dev, int index)
{
cdev_del(&dev->cdev);
}
static int __init globalmem_init(void)
{
int ret;
dev_t devno = MKDEV(globalmem_major, 0); // 指定设备号
// 申请设备号
if (globalmem_major) {
ret = register_chrdev_region(devno, 1, "globalmem"); // 调用者指定设备号
} else {
ret = alloc_chrdev_region(&devno, 0, 1, "globalmem"); // 系统决定设备号
}
if (ret < 0) {
return ret;
}
globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL); // 向内核申请内存
if (!globalmem_devp) {
ret = -ENOMEM;
goto fail_malloc;
}
globalmem_setup_cdev(globalmem_devp, 0);
globalmem_class = class_create(THIS_MODULE, "globalmem_class");
if (IS_ERR(globalmem_class)) {
ret = -1;
goto fail_class;
}
device_create(globalmem_class, NULL, devno, NULL, "globalmem");
return 0;
fail_class: // class_create 异常处理
;
fail_malloc: // kzalloc 异常处理
unregister_chrdev_region(devno, 1);
return ret;
}
module_init(globalmem_init);
static void __exit globalmem_exit(void)
{
device_destroy(globalmem_class, MKDEV(globalmem_major, 0));
class_destroy(globalmem_class);
globalmem_unsetup_cdev(globalmem_devp, 0);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
}
module_exit(globalmem_exit);
MODULE_AUTHOR("Martin Zhang <zhangming_233@163.com>");
MODULE_LICENSE("GPL v2");
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。