Linux下的简单字符驱动--改自宋宝华《Linux驱动开发详解第2版》在kernel2.6.38通过

2024-02-03 17:38

本文主要是介绍Linux下的简单字符驱动--改自宋宝华《Linux驱动开发详解第2版》在kernel2.6.38通过,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux下的简单字符驱动--改自宋宝华《Linux驱动开发详解第2版》在kernel2.6.38通过,
实验过程:在fedora9虚拟机上完成交叉编译,生成mini6410_globalmem.ko, 复制到tiny6410开发板上。
可以用insmod及rmmod 添加删除

/* globalmem driver as an example of char device drivers

   mini6410_globalmem.c
    
The
 
initial
 
developer
 
of
 
the
 
original
 
code
 
is
 
Baohua
 
Song
    
<author@linuxdriver.cn>.
 
All
 
Rights
 
Reserved.
======================================================================*/
#include
 
<linux/module.h>
#include
 
<linux/types.h>
#include
 
<linux/fs.h>
#include
 
<linux/errno.h>
#include
 
<linux/mm.h>
#include
 
<linux/sched.h>
#include
 
<linux/init.h>
#include
 
<linux/cdev.h>
#include
 
<asm/io.h>
#include
 
<asm/system.h>
#include
 
<asm/uaccess.h>
#include
 
<linux/device.h>
#include
 
<linux/slab.h>
#define
 
GLOBALMEM_SIZE 
0x1000
 
/*全局内存最大4K字节*/
#define
 
MEM_CLEAR 
0x1
  
/*清0全局内存*/
#define
 
GLOBALMEM_MAJOR 
233
    
/*预设的globalmem的主设备号*/
struct
 
class
 
*
my_class;
static
 
globalmem_major 
=
 
GLOBALMEM_MAJOR;
/*globalmem设备结构体*/
struct
 
globalmem_dev
                                     
{
                                                        
  
struct
 
cdev
 
cdev
;
 
/*cdev结构体*/
                       
  
unsigned
 
char
 
mem
[
GLOBALMEM_SIZE];
 
/*全局内存*/
     
  
struct
 
semaphore
 
sem
;
   
};
struct
 
globalmem_dev
 
*
globalmem_devp;
 
/*设备结构体指针*/
/*文件打开函数*/
int
 
globalmem_open(
struct
 
inode
 
*
inode
,
 
struct
 
file
 
*
filp
)
{
  
/*将设备结构体指针赋值给文件私有数据指针*/
  
filp
->
private_data 
=
 
globalmem_devp;
  
return
 
0
;
}
/*文件释放函数*/
int
 
globalmem_release(
struct
 
inode
 
*
inode
,
 
struct
 
file
 
*
filp
)
{
  
return
 
0
;
}
/*
 
ioctl设备控制函数
 
after
 
kernel2.6.36
 
remove
 
ioctl,add
 
unlocked_ioctl*/
static
 
int
 
globalmem_unlocked_ioctl(
struct
 
inode
 
*
inodep
,
 
struct
 
file
 
*
filp
,
 
unsigned
  
int
 
cmd
,
 
unsigned
 
long
 
arg
)
{
  
struct
 
globalmem_dev
 
*
dev
 
=
 
filp
->
private_data;
//获得设备结构体指针
  
switch
 
(
cmd
)
  
{
    
case
 
MEM_CLEAR:
 
if
(
down_interruptible(&
dev
->
sem
))
  
return
 
-
ERESTARTSYS;
      
memset(
dev
->
mem
,
 
0
,
 
GLOBALMEM_SIZE);
      
 
up(&
dev
->
sem
);
      
printk(
KERN_INFO 
"globalmem
 
is
 
set
 
to
 
zero/n"
);
      
break
;
    
default
:
      
return
  
-
 
EINVAL;
  
}
  
return
 
0
;
}
/*读函数*/
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
 
count 
?
  
-
 
ENXIO:
 
0
;
  
if
 
(
count 
>
 
GLOBALMEM_SIZE 
-
 
p)
    
count 
=
 
GLOBALMEM_SIZE 
-
 
p;
  
if
(
down_interruptible(&
dev->
sem))
 
return
 
-
ERESTARTSYS;
  
/*内核空间->用户空间*/
  
if
 
(
copy_to_user(
buf,
 
(
void
*)(
dev->
mem 
+
 
p),
 
count))
  
{
    
ret 
=
  
-
 
EFAULT;
  
}
  
else
  
{
    
*
ppos 
+=
 
count;
    
ret 
=
 
count;
    
    
printk(
KERN_INFO 
"read
 
%d
 
bytes(s)
 
from
 
%d/n"
,
 
count,
 
p);
  
}
  
up(&
dev->
sem
);
  
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)
    
return
 
count 
?
  
-
 
ENXIO:
 
0
;
  
if
 
(
count 
>
 
GLOBALMEM_SIZE 
-
 
p)
    
count 
=
 
GLOBALMEM_SIZE 
-
 
p;
    
  
if
(
down_interruptible(&
dev->
sem))
 
return
 
-
ERESTARTSYS;
  
/*用户空间->内核空间*/
  
if
 
(
copy_from_user(
dev->
mem 
+
 
p,
 
buf,
 
count))
//p是偏移,也就是写入的起始地址
    
ret 
=
  
-
 
EFAULT;
  
else
  
{
    
*
ppos 
+=
 
count;
    
ret 
=
 
count;
    
    
printk(
KERN_INFO 
"written
 
%d
 
bytes(s)
 
from
 
%d/n"
,
 
count,
 
p);
  
}
  
up(&
dev->
sem
);
  
return
 
ret;
}
/*
 
seek文件定位函数
 
*/
static
 
loff_t 
globalmem_llseek(
struct
 
file
 
*
filp
,
 
loff_t 
offset
,
 
int
 
orig
)
{
  
loff_t 
ret
 
=
 
0
;
  
switch
 
(
orig
)
  
{
    
case
 
0
:
   
/*相对文件开始位置偏移*/
      
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
:
   
/*相对文件当前位置偏移*/
      
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
 
const
 
struct
 
file_operations 
globalmem_fops 
=
{
  
.
owner 
=
 
THIS_MODULE,
  
.
llseek 
=
 
globalmem_llseek,
  
.
read 
=
 
globalmem_read,
  
.
write 
=
 
globalmem_write,
  
.
unlocked_ioctl 
=
 
globalmem_unlocked_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;
  
dev
->
cdev
.
ops 
=
 
&
globalmem_fops;
  
err
 
=
 
cdev_add(&
dev
->
cdev
,
 
devno
,
 
1
);
  
if
 
(
err
)
    
printk(
KERN_NOTICE 
"Error
 
%d
 
adding
 
globalmem%d"
,
 
err
,
 
index
);
}
/*设备驱动模块加载函数*/
int
 
globalmem_init(
void
)
{
  
int
 
result
;
  
dev_t 
devno
 
=
 
MKDEV(
globalmem_major,
 
0
);
  
/*
 
申请设备号*/
  
if
 
(
globalmem_major)
    
result
 
=
 
register_chrdev_region(
devno
,
 
1
,
 
"globalmem"
);
  
else
  
/*
 
动态申请设备号
 
*/
  
{
    
result
 
=
 
alloc_chrdev_region(&
devno
,
 
0
,
 
1
,
 
"globalmem"
);
    
globalmem_major 
=
 
MAJOR(
devno
);
  
}
  
  
if
 
(
result
 
<
 
0
)
    
return
 
result
;
 
/*kmalloc*/
 
globalmem_devp 
=
 
kmalloc(
sizeof
(
struct
 
globalmem_dev
),
 
GFP_KERNEL);
  
if
 
(!
globalmem_devp)
    
/*申请失败*/
  
{
    
result
 
=
  
-
 
ENOMEM;
    
goto
 
fail_malloc;
  
}
  
memset(
globalmem_devp,
 
0
,
 
sizeof
(
struct
 
globalmem_dev
));
  
  
globalmem_setup_cdev(
globalmem_devp,
 
0
);
  
sema_init(&
globalmem_devp->
sem
,
1
);
//init_MUTEX(&globalmem_devp->sem);
 
  
/*
 
create
 
your
 
own
 
class
 
under
 
/sysfs
 
*/
  
/*class_create
 
-->
 
class_register()*/
  
my_class 
=
 
class_create(
THIS_MODULE,
 
"globalmem"
);
  
if
(
IS_ERR(
my_class))
 
{
             
printk(
"Err:
 
failed
 
in
 
creating
 
class./n"
);
             
goto
 
fail_malloc;
  
}
 
  
/*
 
register
 
your
 
own
 
device
 
in
 
sysfs,
 
and
 
this
 
will
 
cause
 
udev
 
to
 
create
 
corresponding
 
device
 
node
 
*/
  
device_create(
my_class,
 
NULL,
 
MKDEV(
globalmem_major,
 
0
),
 
NULL,
 
"globalmem"
);
  
printk 
(
KERN_INFO 
"Registered
 
character
 
driver/n"
);
  
return
 
0
;
  
fail_malloc
:
 
unregister_chrdev_region(
devno
,
 
1
);
  
return
 
result
;
}
/*模块卸载函数*/
void
 
globalmem_exit(
void
)
{
  
cdev_del(&
globalmem_devp->
cdev
);
   
/*注销cdev*/
  
kfree(
globalmem_devp);
     
/*释放设备结构体内存*/
  
unregister_chrdev_region(
MKDEV(
globalmem_major,
 
0
),
 
1
);
 
/*释放设备号*/
}
MODULE_AUTHOR(
"Song
 
Baohua"
);
MODULE_LICENSE(
"Dual
 
BSD/GPL"
);
module_param(
globalmem_major,
 
int
,
 
S_IRUGO);
module_init(
globalmem_init);
module_exit(
globalmem_exit);

这篇关于Linux下的简单字符驱动--改自宋宝华《Linux驱动开发详解第2版》在kernel2.6.38通过的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/674899

相关文章

Java Lambda表达式的使用详解

《JavaLambda表达式的使用详解》:本文主要介绍JavaLambda表达式的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言二、Lambda表达式概述1. 什么是Lambda表达式?三、Lambda表达式的语法规则1. 无参数的Lambda表

详解如何使用Python构建从数据到文档的自动化工作流

《详解如何使用Python构建从数据到文档的自动化工作流》这篇文章将通过真实工作场景拆解,为大家展示如何用Python构建自动化工作流,让工具代替人力完成这些数字苦力活,感兴趣的小伙伴可以跟随小编一起... 目录一、Excel处理:从数据搬运工到智能分析师二、PDF处理:文档工厂的智能生产线三、邮件自动化:

Spring @RequestMapping 注解及使用技巧详解

《Spring@RequestMapping注解及使用技巧详解》@RequestMapping是SpringMVC中定义请求映射规则的核心注解,用于将HTTP请求映射到Controller处理方法... 目录一、核心作用二、关键参数说明三、快捷组合注解四、动态路径参数(@PathVariable)五、匹配请

git stash命令基本用法详解

《gitstash命令基本用法详解》gitstash是Git中一个非常有用的命令,它可以临时保存当前工作区的修改,让你可以切换到其他分支或者处理其他任务,而不需要提交这些还未完成的修改,这篇文章主要... 目录一、基本用法1. 保存当前修改(包括暂存区和工作区的内容)2. 查看保存了哪些 stash3. 恢

java String.join()方法实例详解

《javaString.join()方法实例详解》String.join()是Java提供的一个实用方法,用于将多个字符串按照指定的分隔符连接成一个字符串,这一方法是Java8中引入的,极大地简化了... 目录bVARxMJava String.join() 方法详解1. 方法定义2. 基本用法2.1 拼接

Java中的record使用详解

《Java中的record使用详解》record是Java14引入的一种新语法(在Java16中成为正式功能),用于定义不可变的数据类,这篇文章给大家介绍Java中的record相关知识,感兴趣的朋友... 目录1. 什么是 record?2. 基本语法3. record 的核心特性4. 使用场景5. 自定

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

Python struct.unpack() 用法及常见错误详解

《Pythonstruct.unpack()用法及常见错误详解》struct.unpack()是Python中用于将二进制数据(字节序列)解析为Python数据类型的函数,通常与struct.pa... 目录一、函数语法二、格式字符串详解三、使用示例示例 1:解析整数和浮点数示例 2:解析字符串示例 3:解

基于Python开发一个有趣的工作时长计算器

《基于Python开发一个有趣的工作时长计算器》随着远程办公和弹性工作制的兴起,个人及团队对于工作时长的准确统计需求日益增长,本文将使用Python和PyQt5打造一个工作时长计算器,感兴趣的小伙伴可... 目录概述功能介绍界面展示php软件使用步骤说明代码详解1.窗口初始化与布局2.工作时长计算核心逻辑3

C/C++ chrono简单使用场景示例详解

《C/C++chrono简单使用场景示例详解》:本文主要介绍C/C++chrono简单使用场景示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录chrono使用场景举例1 输出格式化字符串chrono使用场景China编程举例1 输出格式化字符串示