1、globalmem虚拟设备实例

globalmem为“全局内存”的意思,在globalmem字符设备中会分配一片大小为GLOBALMEM_SIZE(4KB)的内存空间,并在驱动中提供对这片内存的读写、控制和定位函数,供用户空间的进程能通过Linux系统调用获取和设置这片内存。

(1)头文件、宏以及设备结构体

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h> #define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 230 static int globalmem_major = GLOBALMEM_MAJOR;
module_param(globalmem_major, int, S_IRUGO); struct globalmem_dev {
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
}; struct globalmem_dev *globalmem_devp;

定义的globalmem_dev结构体中,包含了对应于globalmem字符设备的cdev,使用的内存mem[GLOBALMEM_SIZE]。

(2)globalmem设备驱动模块的加载和卸载函数

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, );
if (err) {
printk(KERN_NOTICE "Error %d adding globalmem %d", err, index);
}
} static int __init globalmem_init(void)
{
int ret;
dev_t devno = MKDEV(globalmem_major, ); if (globalmem_major) {
ret = register_chrdev_region(devno, , "globalmem");
} else {
ret = alloc_chrdev_region(&devno, , , "globalmem");
globalmem_major = MAJOR(devno);
}
if (ret < )
return ret; globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if (!globalmem_devp) {
ret = -ENOMEM;
goto fail_malloc;
} globalmem_setup_cdev(globalmem_devp, );
return ; fail_malloc:
unregister_chrdev_region(devno, );
return ret;
} static void __exit globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major, ), );
}

globalmem_setup_cdev()函数完成cdev的初始化化和添加,kzalloc()申请了一份globalmem_dev结构体的内存,并将其清0,在cdev_init()函数中,与globalmem的cdev关联的file_operations结构体如下所示:

static const struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
.open = globalmem_open,
.release = globalmem_release,
.read = globalmem_read,
.write = globalmem_write,
.llseek = globalmem_llseek,
.unlocked_ioctl = globalmem_ioctl,
};

(3)读写函数的实现

首先是读函数,函数的实现如下所示:

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 = ;
struct globalmem_dev *dev = filp->private_data; if (p > GLOBALMEM_SIZE)
return ;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p; if (copy_to_user(buf, dev->mem + p, count)) {
ret = -EFAULT;
} else {
*ppos += count;
ret = count; printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
} return ret;
}

其中*ppos是读的位置相对于文件开头的漂移,如果该漂移大于或等于GLOBALMEM_SIZE,表示文件已经到了末尾,返回0(EOF)。

写函数的实现如下所示:

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 = ;
struct globalmem_dev *dev = filp->private_data; if (p > GLOBALMEM_SIZE)
return ;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p; if (copy_from_user(dev->mem + p, buf, count)) {
return -EFAULT;
} else {
*ppos += count;
ret = count; printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
} return ret;
}

(4)seek函数的实现

seek()函数对文件定位的起始地址可以是文件开头(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 = ;
switch (orig) {
case :
if (offset < ) {
ret = -EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case :
if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
if ((filp->f_pos + offset) < ) {
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}

(5)ioctl函数实现

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, , GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero\n");
break;
default:
return -EINVAL;
} return ;
}

(6)使用文件的私有数据

将文件的私有数据private_data指向设备的结构体,然后使用read()、write()、ioctl()、llseek()等函数通过private_data访问设备结构体,如下所示:

static int globalmem_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalmem_devp;
return ;
} static int globalmem_release(struct inode *inode, struct file *filp)
{
return ;
}

(7)完整的globalmem驱动代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h> #define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 230 static int globalmem_major = GLOBALMEM_MAJOR;
module_param(globalmem_major, int, S_IRUGO); struct globalmem_dev {
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
}; struct globalmem_dev *globalmem_devp; static int globalmem_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalmem_devp;
return ;
} static int globalmem_release(struct inode *inode, struct file *filp)
{
return ;
} 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, , GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero\n");
break;
default:
return -EINVAL;
} return ;
} 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 = ;
struct globalmem_dev *dev = filp->private_data; if (p > GLOBALMEM_SIZE)
return ;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p; if (copy_to_user(buf, dev->mem + p, count)) {
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 = ;
struct globalmem_dev *dev = filp->private_data; if (p > GLOBALMEM_SIZE)
return ;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p; if (copy_from_user(dev->mem + p, buf, count)) {
return -EFAULT;
} else {
*ppos += count;
ret = count; printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
} return ret;
} static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = ;
switch (orig) {
case :
if (offset < ) {
ret = -EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case :
if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
if ((filp->f_pos + offset) < ) {
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
} static const 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,
}; 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, );
if (err) {
printk(KERN_NOTICE "Error %d adding globalmem %d", err, index);
}
} static int __init globalmem_init(void)
{
int ret;
dev_t devno = MKDEV(globalmem_major, ); if (globalmem_major) {
ret = register_chrdev_region(devno, , "globalmem");
} else {
ret = alloc_chrdev_region(&devno, , , "globalmem");
globalmem_major = MAJOR(devno);
}
if (ret < )
return ret; globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if (!globalmem_devp) {
ret = -ENOMEM;
goto fail_malloc;
} globalmem_setup_cdev(globalmem_devp, );
return ; fail_malloc:
unregister_chrdev_region(devno, );
return ret;
} static void __exit globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major, ), );
} module_init(globalmem_init);
module_exit(globalmem_exit); MODULE_AUTHOR("HLY");
MODULE_LICENSE("GPL");

(8)globalmem驱动验证

使用make命令将源文件编译出驱动模块globalmem.ko文件,编译需要的Makefile如下所示:

# Makefile for globalmem driver

obj-m += globalmem.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

然后使用驱动模块命令加载模块,如下:

$ sudo insmod globalmem.ko
$ lsmod

然后使用下面的命令查看globalmem虚拟设备的设备号:

$ cat /proc/devices

然后,使用mknod创建设备节点:

# mknod /dev/globalmem c
# ls -al /dev/globalmem

接下来使用命令对该文件进行读写以测试:

# echo “Hello World” > /dev/globalmem
# cat /dev/globalmem

也可以使用系统调用函数open、write和read进行该虚拟设备的测试,测试的app.c文件如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h> #define LENGTH 100 int main(int argc, char *argv[])
{
int fd,len;
char str[LENGTH]; fd = open("/dev/globalmem", O_RDWR);
if (fd) {
write(fd, "Hello World", strlen("Hello World"));
close(fd);
} fd = open("/dev/globalmem", O_RDWR);
len = read(fd, str, LENGTH);
str[len] = '\0';
printf("str:%s\n", str);
close(fd); return ;
}

编写该app.c的Makefile文件,如下:

# Makefile by HLY

all: myapp

# Which compiler
CC = gcc # Where are include files
INCLUDE = . # Options for development
CFLAGS = -g -Wall -ansi myapp: app.o
$(CC) -o myapp app.o clean:
rm -rf *.o myapp

使用make命令将app.c编译成可执行文件myapp,然后执行程序,即可完成globalmem虚拟设备/dev/globalmem的读写测试。

参考:

《Linux设备驱动开发详解:基于最新的Linux 4.0内核》

最新文章

  1. MVC Request.IsAuthenticated一直false Request.Cookie获取不到cookie
  2. Android Fragment应用实战,使用碎片向ActivityGroup说再见
  3. eclipse下Android无法自动生成apk文件怎么办?
  4. input type=checkbox checked disabled
  5. 如何编写Iveely搜索引擎插件
  6. VS - 实用技巧
  7. Python的类实例方法,类方法,类静态方法
  8. play app to war
  9. 射频识别技术漫谈(7)——ID卡
  10. mysql数据库的安装以及常见优化设置
  11. GPIO寄存器
  12. stringsteam使用之整型转字符串
  13. 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD)
  14. Scrapy爬虫遇到 ‘Forbidden by robots.txt’的问题
  15. 使用Visual Studio Installer 2015打包WPF程序
  16. LOJ.6073.[2017山东一轮集训Day5]距离(可持久化线段树 树链剖分)
  17. Lodash学习笔记
  18. ElasticSearch 2 (13) - 深入搜索系列之结构化搜索
  19. 如何使用SubtitleWorkshop制作字幕
  20. 关于 App.config文件出错,配置系统未能初始化。 问题解决方案

热门文章

  1. stm32中的型号对比——为什么很少用STM32F2,F3?
  2. Oracle执行过程中出现的问题
  3. 学习:CMP/TEST比较指令
  4. SpringBoot上传文件到本服务器 目录与jar包同级问题
  5. Kubernetes中如何让Deployment更新镜像
  6. kafka(五) 流式处理 kafka stream
  7. abp 中log4net 集成Kafka
  8. MySQL索引原理(二)
  9. essay sundry
  10. PowerMock框架讲解及使用