|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
我们在 ubuntu 的 home/nfs/07 目录下新建 led.c 文件,可以在上次实验的驱动代码基础上进行修改,以 # q6 w, N8 ]9 `, T# m6 b
下代码为完整的驱动代码。
! U9 O6 }1 p2 J1 Z我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设 + B/ w$ t' V f' c- N
备,并生成了设备节点。接下来我们要完成控制 BEEP 的逻辑操作,那么控制 BEEP 就涉及到了对寄存器的
5 I% y! q6 M# }. P. q 操作,但是对寄存器的操作我们是不能直接访问的,因为 linux 不能直接访问我们的物理地址,需要把物理 . H2 }. n$ {: Z4 \7 w) M0 \4 }
地址先映射成虚拟地址,我们完成这一步转换需要用到 ioremap 函数。
. P. W/ y# D$ y1 y6 h 完整的驱动文件如下所示: 8 O- [/ D; X7 q% c
/*
& ?2 j4 Y8 `) l2 l' u' R% q * @Descripttion: 基于杂项设备的 LED 驱动
, E% j9 B, |9 r V' t f* @version:
+ u8 O* A' k8 G& A, T * @Author:
H& J/ m$ t5 G * @Date: 2021-02-23 13:54:49
9 u3 h" L! o/ C% l) t1 |; H */
# I/ P7 r# g& r, y. y( p) g/ S, e3 h #include <linux/init.h> - A" a% [0 F& f5 s
//初始化头文件 , L2 |% ]6 k$ U: T4 n" T8 j
#include <linux/module.h> $ m" c8 ^! Z, ^8 T0 M! G @- h6 u
//最基本的文件,支持动态添加和卸载模块。 # x: ~, }& @+ Y6 w! M
#include <linux/miscdevice.h> //包含了 miscdevice 结构的定义及相关的操作函数。
) c Y1 o, L( S# e! ]0 V' _9 D#include <linux/fs.h>
* B3 j% ^% n9 N' M6 R" _ //文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等) " m6 _- Q5 k/ B
#include <linux/uaccess.h>
0 K- i# m+ _0 J2 z b6 [( a //包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函 6 Z! {8 z# e0 s
数定义。
) ?4 Q, u' ]$ F1 r- K9 q) i#include <linux/io.h>
- L8 T) m' L7 `! G //包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。
4 A/ q: j+ E$ i) t8 j#include <linux/kernel.h>
% T7 u7 d' y5 T7 h. m% v //驱动要写入内核,与内核相关的头文件
# D% t* V7 Q# V" `- ]#define GPIO_DR 0xfdd60000 //LED 物理地址,通过查看原理图得知 8 I- U/ x9 o4 W% a' F& _
unsigned int *vir_gpio_dr; //存放映射完的虚拟地址的首地址 . [* K9 i# L6 t; Z+ n2 ~
/**
" P4 t8 v+ J" v; I2 [ * @name: misc_read 0 o. `+ i4 G; J+ x2 K9 J
* @test: 从设备中读取数据,当用户层调用函数 read 时,对应的,内核驱动就会调用这个函数。
' u3 a) ^3 q; f1 e Z( I* @msg:
: ` P3 k3 P! h+ d% Q3 a: ? * @param {structfile} *file file 结构体
5 l5 k/ @- f+ Q8 h. V* @param {char__user} *ubuf 这是对应用户层的 read 函数的第二个参数 void *buf $ W$ _, j; d3 U6 r% r" [* t
* @param {size_t} size 对应应用层的 read 函数的第三个参数 * ^ Y. l- B) j* j6 O$ e3 ?
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使 , L% i5 [ ~- C5 o ]
偏移量往后移。
( ?5 E! q) }3 A5 V2 v; J. A* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取
7 }6 Q) K! ^1 T: t 的字节数。
. E# ?: \. ~5 [+ h! s1 C 如果返回负数,内核就会认为这是错误,应用程序返回-1
2 d& W4 G: c1 {' j3 I8 ? */
* s4 Q" J- C0 p$ G ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t) - \$ \- g" R- j R( m+ R4 k# h
{ . l. D% L1 z/ l# z9 a, f/ B5 q5 W' g
printk("misc_read\n "); * p# ^1 b. U7 I" X8 T! m
return 0; 6 i$ v" B. W; r% }9 Z3 g0 `
}5 D8 n% j9 [/ C3 g. K# }0 y3 X; `
/**
! X8 p/ K% \$ N3 A6 c- D$ [1 O: Z * @name: misc_write% A! d# [- f: k5 ?1 Y: r. S, Z h0 T
* @test: 往设备写入数据,当用户层调用函数 write 时,对应的,内核驱动就会调用这个函数。 6 T; J" k0 P6 @8 X# ]9 c
* @msg: ; `+ l3 v/ e( r7 @2 u: R9 E
* @param {structfile} * filefile 结构体
4 C9 V9 a( |+ V- J6 C* @param {constchar__user} *ubuf 这是对应用户层的 write 函数的第二个参数 const void *buf
% `& \6 t. \# t8 x$ `) x * @param {size_t} size 对应用户层的 write 函数的第三个参数 count。 & L- W( s8 z+ v' _2 |: @
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使
6 ?( z$ l4 f4 x7 ]" m# w0 T3 V 偏移量往后移。
% `% m* l& U' i4 M* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取 6 N, C8 e8 c) M. }/ L2 ^% L
的字节数。
9 n1 w4 l: J( }5 F5 K: B 如果返回负数,内核就会认为这是错误,应用程序返回-1。 / G8 g( t8 C: M5 n: }8 M
*/
L `' x2 ~2 z ~$ N* s% @0 O: gssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t) 5 V# i0 }; u9 N' f! }
{ + X: w/ W5 J' v
/*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/
& p1 z; l! z0 F0 w // kbuf 保存的是从应用层读取到的数据
% r H# v/ G8 [$ \0 Echar kbuf[64] = {0}; * u; F- q7 G3 |& J* w
// copy_from_user 从应用层传递数据给内核层
2 F3 K# V& v# ~: N6 Jif(copy_from_user(kbuf,ubuf,size)!= 0)
6 A8 ~& x4 o# k {
) E9 Q. A$ m- P0 l // copy_from_user 传递失败打印 : f) r# D ^; G* X
printk("copy_from_user error \n ");
* c; h* f5 n% s; ~$ { return -1;
x! J" \4 _& D( ] }' c) ~; T# G. [, U
//打印传递进内核的数据
$ E- p/ S1 i) Q/ Cprintk("kbuf is %d\n ",kbuf[0]);
' F. S3 z) A K- L6 B K if(kbuf[0]==1) //传入数据为 1 ,LED 亮
' \) v0 S6 b' ~: F% p{ f3 O* o3 _' P2 q$ |8 T/ R7 i
*vir_gpio_dr = 0x80008000; / f4 @& B$ {0 u7 r0 c
}6 m, k3 c, t1 I6 N: g
else if(kbuf[0]==0) //传入数据为 0,LED 灭 - r0 ^4 `# [. }3 M% Y) D
*vir_gpio_dr = 0x80000000; * D: @& L, Z* k% i+ `
return 0;
" p; O% a3 c1 l' I7 ?: p }9 S( J# D ?7 P: [
/** 0 f, O; S+ P) a
* @name: misc_release 3 r+ G9 @$ w7 U
* @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为 NULL。关闭设
. H1 S3 E, i$ e 备永远成功。
, v7 ?0 |. G+ {* @msg:
. w/ c- N2 O2 r) j5 ^ * @param {structinode} *inode 设备节点 ' j* m) A& L- F& U3 @/ ^
* @param {structfile} *file filefile 结构体 & ?$ Q9 k2 \$ V, q: W
* @return {0}
2 r4 q9 T* \: e */
9 Q) N7 ^, H7 D7 q int misc_release(struct inode *inode,struct file *file){) ], J! b( O( s, G! r3 i% m3 e
printk("hello misc_relaease bye bye \n ");
/ A4 e7 e- A' ]* x7 X' w" M return 0;
. ~0 Y9 w$ P0 w0 F( E }- w; h( ?/ v2 k! t7 |" m) ]
/**
+ Z/ z' t- n& Z$ z * @name: misc_open 2 y& i3 F8 c$ o* w( p/ G1 R1 J+ i
* @test: 在操作设备前必须先调用 open 函数打开文件,可以干一些需要的初始化操作。 ( A. N ~9 \, v+ R+ L. m' x8 T! _
* @msg:
/ m$ H) }% x, o7 m! g+ b9 { * @param {structinode} *inode 设备节点 6 l" e: ]9 Z7 m" w! _9 s; Q
* @param {structfile} *file filefile 结构体
1 R2 e% W8 }2 @. U9 M* @return {0}
8 l- y6 v8 R' l# [* d& ^7 v */
, M3 \0 n6 n. [& C# S t) h int misc_open(struct inode *inode,struct file *file){
$ z* R8 L7 l& a- U, Z printk("hello misc_open\n "); 7 t7 H* a' D' C1 B% n8 p! {
return 0;
G7 o4 ?" K! T3 U2 k }; z7 E$ s% {' ?" g2 F
//文件操作集
9 A M( t b# U; Mstruct file_operations misc_fops={ 7 h2 i$ [7 J0 m. p/ d5 p1 |
.owner = THIS_MODULE, 1 w0 W+ s" W- ] z' B
.open = misc_open,
3 E: s1 ^8 B" E .release = misc_release, / I3 R$ q7 W- E" L3 J1 d+ R6 M
.read = misc_read, ' G, F: J+ o) u' n5 E
.write = misc_write, ' G# ]" G4 M7 x+ Y
};! l. G T* P' }* [2 m
//miscdevice 结构体
; O O5 ~& X+ T+ `9 B8 j! kstruct miscdevice misc_dev = { 1 c1 C, u/ v5 o: t+ o8 l
.minor = MISC_DYNAMIC_MINOR,
1 {! X; `) J, B! R( @" {0 J8 h .name = "hello_misc", ! d% X) d8 \$ T0 G$ s
.fops = &misc_fops,
8 D- Z# N9 I* V& e0 I" w% m };% }& Y0 D3 S3 D- S
static int misc_init(void)
# F$ l+ k( R x2 ~; B1 X" q {
* J+ w0 d$ y0 }5 a4 q int ret;
% |- l8 r5 ?9 w' G/ r3 M- J+ N //注册杂项设备
; X' T) F \# |% r% {ret = misc_register(&misc_dev); 9 a, A) R/ J! |. Z: o
if(ret<0)
* U) t4 r6 j6 @; K+ O { + @1 j+ o. M; C7 z. M- \
printk("misc registe is error \n");
0 ]# J6 u% q. F" M, \* K# k }
5 M8 d, ?$ l! g0 f8 _ printk("misc registe is succeed \n"); ' `' \) n1 L y3 R$ ]+ ^' A6 z0 E
//将物理地址转化为虚拟地址 . l- [1 }; e/ C* K5 f
vir_gpio_dr = ioremap(GPIO_DR,4);
* S$ |5 u* I" R/ S if(vir_gpio_dr == NULL) ! G0 c; U$ l" Z
{; n8 `4 z) G' v7 ^
printk("GPIO_DR ioremap is error \n"); % o/ \5 A/ y& f
return EBUSY;
6 P7 R; l/ h' z( V+ K }
8 y, m! p) x4 a) y& e printk("GPIO_DR ioremap is ok \n");
9 I; ~( t# k7 [( K% E4 U0 ^ return 0; . V) W1 w! X& |: B+ _. K
}
. D( Y/ S8 ?6 l, P static void misc_exit(void){
/ w8 C6 ~1 v7 T //卸载杂项设备
) Z* V5 R: h2 a( b/ P6 ^misc_deregister(&misc_dev); ( E8 D, i& M1 n! z6 f6 E
iounmap(vir_gpio_dr); ) o2 |( F. G D5 c9 n& ?. J9 ], g
printk(" misc gooodbye! \n");
h B. `9 n* t }! K( s# a1 j }/ H
module_init(misc_init);
! [$ k1 u; U5 Y7 `2 z module_exit(misc_exit);
' ?7 n8 q' E2 J# `4 {) E MODULE_LICENSE("GPL");: ?/ r" a" T ?: n+ l9 P
更多内容请关注:北京迅为6 J6 Y) o/ L+ [3 e
1 P# A s- N5 ^2 P2 J* B
|
|