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

相关文章

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud