|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
我们在 ubuntu 的 home/nfs/07 目录下新建 led.c 文件,可以在上次实验的驱动代码基础上进行修改,以
% D: R$ _" w! ^- q- l下代码为完整的驱动代码。
" b# d- }, D. u( N) m' F8 b我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设
2 i5 p y) H" x* p* ] 备,并生成了设备节点。接下来我们要完成控制 BEEP 的逻辑操作,那么控制 BEEP 就涉及到了对寄存器的
+ @- h7 b: b; h+ h0 e/ I" G 操作,但是对寄存器的操作我们是不能直接访问的,因为 linux 不能直接访问我们的物理地址,需要把物理
, }5 W' i% a7 }: H! N4 l 地址先映射成虚拟地址,我们完成这一步转换需要用到 ioremap 函数。
% s: S4 H. c; P4 \ e( k' B 完整的驱动文件如下所示:
5 [4 z! M2 c0 m, T. X, u4 t/*
! N1 b6 |; p r. j/ R * @Descripttion: 基于杂项设备的 LED 驱动 * ]1 c* o7 _4 m) z4 O
* @version:
* ^8 x a& t; e# c7 m * @Author:
4 \5 Y" j7 q1 u y1 m * @Date: 2021-02-23 13:54:49 / A% D9 |# b% z& M0 R8 F* _
*/ 8 e. y% j( ^+ w( y, @0 C
#include <linux/init.h> ! e: g1 W6 k* n: K! y4 F* n
//初始化头文件
: Z" h( @% A* J0 r8 V#include <linux/module.h> 5 s6 j! Y$ K& @$ B
//最基本的文件,支持动态添加和卸载模块。
( e5 T6 l- H- V$ P5 \#include <linux/miscdevice.h> //包含了 miscdevice 结构的定义及相关的操作函数。 ; x& \6 S' y4 |5 t# S4 o. f
#include <linux/fs.h>
9 y t0 e' a: t: i* s# |' \8 J; F+ v4 f //文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等)
1 T. h6 h% l) m% \* K#include <linux/uaccess.h>
8 |, E9 ]1 R' |6 ~% o9 `7 ? //包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函
$ j1 q0 N5 T8 r5 {( ? 数定义。
- p( B3 r! W! {#include <linux/io.h>
. T0 s& {4 h6 Q: K" e //包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。
t( ?8 G( k- i' \" _#include <linux/kernel.h>
% n! ?6 B9 m% D3 ^ //驱动要写入内核,与内核相关的头文件
6 q9 y2 k# z; Z! v/ ~; k8 ]#define GPIO_DR 0xfdd60000 //LED 物理地址,通过查看原理图得知
3 `% Z' E' m# y; _- ]& m# Sunsigned int *vir_gpio_dr; //存放映射完的虚拟地址的首地址
) D ?3 H \' J. J9 C( n" ?# M/**
4 n& u. I. F, T% b" i * @name: misc_read
|+ h3 R3 f( d' T * @test: 从设备中读取数据,当用户层调用函数 read 时,对应的,内核驱动就会调用这个函数。
2 O6 q; T$ G: H. z* @msg:
) o6 @1 Y# P5 A% k H * @param {structfile} *file file 结构体 1 t& D' E/ K" U0 t
* @param {char__user} *ubuf 这是对应用户层的 read 函数的第二个参数 void *buf ) \/ m1 @7 k7 M" F
* @param {size_t} size 对应应用层的 read 函数的第三个参数
$ k# [8 N: a$ G# m! ]. g0 M* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使
" d: n' u* Y( n* v5 N' x \, J 偏移量往后移。
0 e0 \' ~; q2 _" L; c* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取 % S( [ p0 @$ O- [1 S2 T# A3 W
的字节数。 9 f* P/ V4 m" l9 ^6 U
如果返回负数,内核就会认为这是错误,应用程序返回-1
3 k# w; } U# I" g% W+ X) Y */ " R. ?4 g- _* K0 }5 v' g
ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
0 |2 ]: W2 h* K9 x. ^ { 4 p2 u% M( ^. P8 y3 y* |7 B
printk("misc_read\n "); # j; l' @% T; Y- c9 d" Y# n
return 0; & k( k, \6 A! m( G1 c- q
}8 B H$ g& T! k1 @1 P5 k
/**
8 c( A4 D& d* t) O * @name: misc_write7 A6 m, E! {- K, t% \$ j
* @test: 往设备写入数据,当用户层调用函数 write 时,对应的,内核驱动就会调用这个函数。
7 F# _& h, ]. H0 I* @msg:
/ v7 a _6 k- V; Y: Z * @param {structfile} * filefile 结构体
. p/ t3 \, r% z& [/ N8 H* N* @param {constchar__user} *ubuf 这是对应用户层的 write 函数的第二个参数 const void *buf $ R; P+ q5 u/ D3 ]. Z4 D# m. F
* @param {size_t} size 对应用户层的 write 函数的第三个参数 count。 ( N6 q0 P; ?4 t
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使
) i8 o0 ~5 z( g, N$ o) L% N5 x 偏移量往后移。 3 r( G9 m' t0 w& V' @+ `# e- A/ _
* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取
: D) g1 \ B( q/ o* C4 ^ 的字节数。
6 [3 }; ]7 ?+ g. b, H- Y8 K7 u 如果返回负数,内核就会认为这是错误,应用程序返回-1。
3 Q0 s: H" d' W+ D6 O" T1 ?*/
+ J- K, z* l: Z' vssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t) : I5 D: b2 |+ C/ v+ ?5 a* m
{ , D$ Z+ O# J) B, v8 X
/*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/
& y) F, t$ J: I2 S- x3 M+ P // kbuf 保存的是从应用层读取到的数据 $ T9 b6 c# J% Q% K8 \5 X; q6 y
char kbuf[64] = {0};
4 b3 N6 N3 Y6 K* ^, c // copy_from_user 从应用层传递数据给内核层
0 A0 `+ i: Z% H# q- M- vif(copy_from_user(kbuf,ubuf,size)!= 0)
' _. x3 x) r2 M { , |" C* s# Q# Y! B3 R% n1 R
// copy_from_user 传递失败打印 : ~* W m' ^! {& s. y7 z: P4 p! x
printk("copy_from_user error \n "); - q! q/ }( S' ^
return -1;
' e; d8 Y1 W. f* N. S# _. _ }
$ W! X; {: k3 {& E //打印传递进内核的数据
- R& [9 A0 F! g" F8 ?* e5 ~% Z/ E2 mprintk("kbuf is %d\n ",kbuf[0]); 0 p. s8 {& v4 C) ?( r5 X) L% r
if(kbuf[0]==1) //传入数据为 1 ,LED 亮 : }; `4 h$ G+ {, W
{
2 a/ I/ A' F( @8 X *vir_gpio_dr = 0x80008000; ' I4 b) g: K. H# L, d+ I+ g
}3 T4 Q# K" `; n7 L. J
else if(kbuf[0]==0) //传入数据为 0,LED 灭
& J& T( i) V" K. w' r @, i* j*vir_gpio_dr = 0x80000000; ' y x/ Q( w3 O) D' v9 s) k
return 0;
8 p) I# z! f$ X& v }
% L( M8 Q5 E5 v( X" d /**
5 f O8 d8 ~: Z" S g4 m( u; J * @name: misc_release ; N6 q Q) ~# Z% q6 Y
* @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为 NULL。关闭设
4 \6 q( {6 y2 i, } c 备永远成功。
1 ]1 g- Z9 _* v9 C6 q0 ^! C* @msg:
6 |6 T/ s0 S& s, _! e0 X( _5 {. N9 H2 A * @param {structinode} *inode 设备节点 3 b! l+ _. d* k; g
* @param {structfile} *file filefile 结构体
9 i( N0 f) t0 ?5 ~1 D% X* ^* @return {0} ' N1 [& L. U( p
*/
; y$ j4 V1 D3 M- v% ~4 Y int misc_release(struct inode *inode,struct file *file){; v" M1 ^% v' O9 Y
printk("hello misc_relaease bye bye \n "); + g' _* w" y% {3 |! e! z M* ]
return 0; 8 ]( X* T% V& @0 Q5 }5 }% e
} P# M% }/ N8 n' g& b
/** 2 v; P ?$ U' O' p2 U+ ~9 W
* @name: misc_open
+ R# t T, H2 y) t1 X. S * @test: 在操作设备前必须先调用 open 函数打开文件,可以干一些需要的初始化操作。 % G+ t, r) y# ]3 }: q
* @msg: 3 l! b7 M) v+ n/ Y; i( ^( e3 Y
* @param {structinode} *inode 设备节点
; i" n9 q* s2 s! g& F* @param {structfile} *file filefile 结构体
* T, I) u, ^( C5 @7 n* @return {0} ! s$ z: e" y' R; \1 L
*/
8 L! L1 _5 h$ o+ y. G& L9 R int misc_open(struct inode *inode,struct file *file){ - j" g% v* y4 ~0 m t$ U T7 j. m
printk("hello misc_open\n "); 0 U: c. V$ V$ ]3 b) V
return 0;
z$ {+ D7 u+ @' [1 x }6 ?3 U, F: R K" a! y
//文件操作集
`0 @! n% A: B* P' hstruct file_operations misc_fops={
2 C! {1 X8 s$ f( ~% ]- k7 } .owner = THIS_MODULE,
% P" c7 g7 R F+ ^, F$ K .open = misc_open,
: ]9 s0 U9 |) N7 R. {1 X; M# n .release = misc_release, : k5 m1 ^2 x4 A3 C# G
.read = misc_read, ) `3 ^. ]' l& t
.write = misc_write,
2 U+ `* p( e* f. J1 E* q };: b- ^; P" i# b- M1 V( G2 F
//miscdevice 结构体
$ ^2 N) U N6 [struct miscdevice misc_dev = {
) w' D# s. K: \, i/ m, p# g, k .minor = MISC_DYNAMIC_MINOR,
6 X; H7 |8 c0 Q% \, a; r; J" _+ u .name = "hello_misc",
( D% r9 n4 z1 @$ @) O6 M3 h7 K1 K/ F .fops = &misc_fops,
: ~1 u. s' g6 V( C+ H7 _. d: d& u; b1 O };
& i- Q4 G2 x8 t: z/ t static int misc_init(void)
0 u2 w/ B+ g2 ] f# Q {
3 ?- M& n6 h9 }1 S+ I; @. L int ret; s6 c0 u' `/ M2 r5 O9 q
//注册杂项设备
9 d3 ?3 {; `8 ?. o" ?1 ^/ A- ^ret = misc_register(&misc_dev);
' X' o! g) A4 K$ k if(ret<0) , g* w7 Q# d) |+ y4 P1 x
{ - ~" O0 @4 f- B) y7 M* B3 K
printk("misc registe is error \n");
6 e9 P, A$ O% A% w% ` D }% r9 F( v/ Z2 x! x- j& M
printk("misc registe is succeed \n");
$ t* K6 s; o% U //将物理地址转化为虚拟地址 7 w R6 ^" x) f" F
vir_gpio_dr = ioremap(GPIO_DR,4); ! [9 h7 s' a$ V8 E
if(vir_gpio_dr == NULL) ; u5 a' e8 y% N6 ^" ^9 p* w+ `$ j
{% s7 p- ^5 ]9 r2 u7 S( U
printk("GPIO_DR ioremap is error \n"); 7 j0 T X/ A; G9 L# T3 A
return EBUSY; 8 k. m2 ]; b) c3 l. Q
}3 y1 q% L4 R$ T
printk("GPIO_DR ioremap is ok \n");
2 ^( x; K; x9 e9 E return 0; 9 q/ R" k2 d$ L
}8 X, G4 e" H Q3 B7 e
static void misc_exit(void){ % P' Y* @/ {! U/ J, D
//卸载杂项设备
, K/ c1 V# `( y5 jmisc_deregister(&misc_dev);
7 f$ z4 F8 }9 J0 A8 V" F: W iounmap(vir_gpio_dr); - i1 Q+ m! q5 p& C6 X
printk(" misc gooodbye! \n");
8 s% ^2 X1 [: K' a% H. r }1 C$ A7 B7 c( R; D$ ^1 m
module_init(misc_init); & r5 n7 @. `2 M$ f
module_exit(misc_exit);
5 C) c0 Z7 Q Y% D, F7 H MODULE_LICENSE("GPL");
) u4 s, r5 l- `6 m* `9 p更多内容请关注:北京迅为7 B& l6 f# P3 R5 a; N( ^3 T
, S% S5 D: R* A/ ?* C
|
|