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内核》

Linux字符设备驱动实例—globalmem驱动的更多相关文章

  1. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

  2. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  3. Linux字符设备驱动基本结构

    1.Linux字符设备驱动的基本结构 Linux系统下具有三种设备,分别是字符设备.块设备和网络设备,Linux下的字符设备是指只能一个字节一个字节读写的设备,不能随机读取设备内存中某一数据,读取数据 ...

  4. (57)Linux驱动开发之三Linux字符设备驱动

    1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是: ...

  5. 深入理解Linux字符设备驱动

    文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...

  6. Smart210学习记录----beep linux字符设备驱动

    今天搞定了beep linux字符设备驱动,心里还是很开心的,哈哈...但在完成的过程中却遇到了一个非常棘手的问题,花费了我大量的时间,,,, 还是把问题描述一下吧,好像这个问题很普遍的,网上许多解决 ...

  7. Linux字符设备驱动实现

    Linux字符设备驱动实现 要求 编写一个字符设备驱动,并利用对字符设备的同步操作,设计实现一个聊天程序.可以有一个读,一个写进程共享该字符设备,进行聊天:也可以由多个读和多个写进程共享该字符设备,进 ...

  8. linux字符设备驱动--基本知识介绍

    一.设备驱动的分类 1.字符设备 字符设备是指那些能一个字节一个字节读取数据的设备,如LED灯.键盘.鼠标等.字符设备一般需要在驱动层实现open().close().read().write().i ...

  9. Linux字符设备简单示例

    1. Linux字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现open.close.read和write系统调用.例如:串口.Led.按键等. 2. 通过字符设备文件 ...

随机推荐

  1. mysql导出PDM表结构并带有注释

    分享链接:https://www.cnblogs.com/dennyzhangdd/p/5550868.html

  2. redux学你参考网站

    redux官方网站 http://cn.redux.js.org/docs/api/combineReducers.html https://www.redux.org.cn 从设计的角度看Redux ...

  3. Codeforces Round #552 (Div. 3)-1154E-Two Teams-(模拟+双指针)

    http://codeforces.com/contest/1154/problem/E 解题: 举例n=10,k=1 1,2,10,4,7,6,9,8,5,3 第一次,1队先挑2,10,4这三个人 ...

  4. 学习:多字节编码(ANSI)和UNICODE编码的关系

    Windows 既可以使用 Unicode 字符集又可以使用传统的字符集(如多字节编码)来实现对多种语言的支持,以适应国际市场的要求.与传统的字符集编码相比,Unicode 是世界通用的字符编码标准, ...

  5. LeetCode 325. Maximum Size Subarray Sum Equals k

    原题链接在这里:https://leetcode.com/problems/maximum-size-subarray-sum-equals-k/ 题目: Given an array nums an ...

  6. pycharm中使用2to3

    https://www.jianshu.com/p/abbb005ba002 可用

  7. url的组成部分

    /news/index.asp?boardID=5&ID=24618&page=1#name 协议:http: //为分隔符 域名:www.aspxfans.com :为域名和端口之间 ...

  8. 如何判断当前脚本运行在node还是浏览器中

    判断global对象,如果是window,就是运行在浏览器中,如果global对象是undefined,则运行在node中.

  9. 第4组 Alpha冲刺(1/4)

    队名:斗地组 组长博客:地址 作业博客:Alpha冲刺(1/4) 各组员情况 林涛(组长) 过去两天完成了哪些任务: 1.安排好各个组员的任务 2.收集各个组员的进度 3.写页面 4.写博客 展示Gi ...

  10. SQL Server数据库应用技术

    SQL Server数据库应用技术 SQL是Structured Query Language的缩写.SQL是操作命令集,是一种功能齐全的数据库语言.SQL功能强大.简单.易学.使用方便,已经成为了数 ...