|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
我们在 ubuntu 的 home/nfs/07 目录下新建 led.c 文件,可以在上次实验的驱动代码基础上进行修改,以 , `+ Z1 }8 b ]# k3 Y! V
下代码为完整的驱动代码。
. f0 P( H4 i: M; `% ]8 n我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设
5 X+ M& D7 x' z1 @9 }" @ 备,并生成了设备节点。接下来我们要完成控制 BEEP 的逻辑操作,那么控制 BEEP 就涉及到了对寄存器的
0 V3 I; p2 S0 R4 Y# i 操作,但是对寄存器的操作我们是不能直接访问的,因为 linux 不能直接访问我们的物理地址,需要把物理
( t' L6 F. b- @$ `; U9 w0 L4 e 地址先映射成虚拟地址,我们完成这一步转换需要用到 ioremap 函数。
+ x* r+ Q2 o- i4 r% N% K 完整的驱动文件如下所示: D; D# h) u6 P' L; c
/*
% p9 s0 s+ Y% a& p6 a" ]$ g% h1 H- P * @Descripttion: 基于杂项设备的 LED 驱动
' O- @9 Z/ E) h q* @version: 0 O' \2 h" H1 W
* @Author:
0 T* Y- V4 C! d8 ^4 U M/ }( M * @Date: 2021-02-23 13:54:49
; x9 m0 c+ h9 f! \; S" Y */
2 F3 }8 n) v5 ?( h8 H #include <linux/init.h> - K* d% [4 k. g0 M' S
//初始化头文件
) t: ~ T! Z! M/ q% s( A# U#include <linux/module.h>
" h* v2 z2 t3 |* u8 I6 B4 y5 T //最基本的文件,支持动态添加和卸载模块。 2 K" e# R+ w1 F5 ?& |! o0 ?' J' x
#include <linux/miscdevice.h> //包含了 miscdevice 结构的定义及相关的操作函数。
d$ P8 n# {2 ]% S$ u, @( }#include <linux/fs.h> % j/ ^* ^ t, z3 _* V: k( g
//文件系统头文件,定义文件表结构(file,buffer_head,m_inode 等) 7 J& B- s9 D3 s& W- E9 t. E! i- x, d9 l
#include <linux/uaccess.h>
6 t- G2 z6 p5 S; i$ }( B' h$ a //包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函
/ b3 B9 Z" s) K 数定义。6 @. [' H$ S* V4 g1 ^
#include <linux/io.h> ( u4 c3 m8 X/ l
//包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。
- H) l+ ]3 {* W6 B5 r#include <linux/kernel.h> 6 `4 b l& c4 P, W
//驱动要写入内核,与内核相关的头文件 6 S& N4 `+ E. v# z6 ^
#define GPIO_DR 0xfdd60000 //LED 物理地址,通过查看原理图得知 0 D1 M! c3 e9 H W6 t
unsigned int *vir_gpio_dr; //存放映射完的虚拟地址的首地址
: k& \! {9 d' F. v: o/**
6 v K# q5 Y* X& T * @name: misc_read
6 x/ W9 e1 `/ ^3 }' V * @test: 从设备中读取数据,当用户层调用函数 read 时,对应的,内核驱动就会调用这个函数。
+ L* ? j& C$ o+ z! X& w* @msg:
' x U' h# m G7 o8 ~ * @param {structfile} *file file 结构体
2 _/ a5 y# U I1 u) Q- ^* @param {char__user} *ubuf 这是对应用户层的 read 函数的第二个参数 void *buf - r9 M0 G5 g1 ^) s2 @
* @param {size_t} size 对应应用层的 read 函数的第三个参数
6 M- Z5 X+ N$ i5 K( ?9 `+ J8 i* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使 ) h! {* t, R, V) `/ W, s w
偏移量往后移。 ) w9 c6 a2 Y0 I. i9 f
* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取 # R V8 `; d0 `4 \
的字节数。 " O1 g8 e9 }) D1 y# q
如果返回负数,内核就会认为这是错误,应用程序返回-1 % [: m- Q5 g6 o0 M) I4 x; x. D" D
*/
0 x- W0 t& n8 @7 A r7 X7 `0 H4 X ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t) v+ f: o0 L* x( }; B$ O
{ u3 g( h) j5 D
printk("misc_read\n ");
1 `0 _ `5 ~5 R0 H- a return 0;
% L: s1 [4 L7 b7 d# d q v8 [ }
% u: N8 ^/ n* B% o /** 1 x+ `3 h2 U3 h0 M) R, I
* @name: misc_write
# T/ n$ H- p% R4 V! [* w1 M; f * @test: 往设备写入数据,当用户层调用函数 write 时,对应的,内核驱动就会调用这个函数。 7 H% ^3 K2 d( N$ K- ^
* @msg:
' ]8 F9 a5 P& q1 a * @param {structfile} * filefile 结构体
' o! t) z' R+ x& l* @param {constchar__user} *ubuf 这是对应用户层的 write 函数的第二个参数 const void *buf
+ b: e V6 g5 [; m4 T4 V- m/ ] * @param {size_t} size 对应用户层的 write 函数的第三个参数 count。 , ?. i& J4 |. L, P- l+ q
* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使
8 ^; E k8 q" [0 U 偏移量往后移。
- o' T2 u2 J3 r2 k% u* A; L* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取 1 y$ J8 X$ p7 m7 F% O$ D4 j
的字节数。 ! e: T7 G" Q1 v
如果返回负数,内核就会认为这是错误,应用程序返回-1。 : a# G+ u, g+ W% x8 Q5 q$ U" R
*/
% ]* A. v G: |. n( B6 Hssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
' E: y l6 Q7 e) }4 U6 F4 ` {
0 b e; L. l/ y9 `( y /*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/ $ J2 G- W# Z% u9 H: Y7 }
// kbuf 保存的是从应用层读取到的数据
: b1 \9 }# ~$ q* g- n6 M, s- B' dchar kbuf[64] = {0}; . s5 S! z2 i, f/ B t5 I
// copy_from_user 从应用层传递数据给内核层 % F# d1 E( ~" Z% l' G
if(copy_from_user(kbuf,ubuf,size)!= 0) / H) t+ r4 Z3 t
{ " D" T5 l" N# m0 F; c
// copy_from_user 传递失败打印 + K* i+ e# d9 f
printk("copy_from_user error \n ");
' ]4 Z D# V7 M1 m6 u4 Q- E4 |$ x return -1;
, G5 \6 v+ f3 F& P }+ x# H1 h2 Y# b( w" q5 N
//打印传递进内核的数据
- d. Z' q- q9 Kprintk("kbuf is %d\n ",kbuf[0]);
) u3 U- X/ T4 B" F$ ^ if(kbuf[0]==1) //传入数据为 1 ,LED 亮 " B# B8 O' f0 J
{
6 r- D* e- B& S% m V5 B *vir_gpio_dr = 0x80008000;
# v( f+ W* J0 y! |1 x }
+ b7 \0 k2 o) x* @; a) E0 m* _8 K else if(kbuf[0]==0) //传入数据为 0,LED 灭
) g7 u% U& t& r) m8 i6 d*vir_gpio_dr = 0x80000000; 1 J- M% ~+ K! A& ]; r/ P6 S
return 0;
9 C5 _" k% p0 w. C }
( p# z$ n& n; S+ I /**
+ _6 h' }" w; {9 R7 \8 T * @name: misc_release 3 O; A+ D, h: F
* @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为 NULL。关闭设
- m- J# U" w- _! l6 x7 L$ d N 备永远成功。 2 M) y/ A) `6 ?/ H* f) B+ U
* @msg: $ Q2 M& n( M% P+ \4 l5 N4 [
* @param {structinode} *inode 设备节点 / i) V* a1 P: K3 [
* @param {structfile} *file filefile 结构体 " N A, T9 C# y
* @return {0} 9 X# A z3 a, a7 T
*/ \4 M7 i2 r, W& b2 M# P- T) J" v8 I" z
int misc_release(struct inode *inode,struct file *file){
& A% P3 m# v, D% `9 ^6 s2 P0 V printk("hello misc_relaease bye bye \n "); 1 G( S; d9 G r' n
return 0; . a* i- B: m/ ~2 f6 z6 F* I
}
, G1 e8 }% @ V. I* H /** ' A3 ~, z3 Z8 n
* @name: misc_open 2 _; y6 r" n0 c4 O) v' m8 B) g
* @test: 在操作设备前必须先调用 open 函数打开文件,可以干一些需要的初始化操作。
, |# ], ^) m% o! {- R/ S* @msg: 2 q: m) I4 @6 C* }* }
* @param {structinode} *inode 设备节点
7 f" a9 b1 \, s) B5 V* @param {structfile} *file filefile 结构体 . i3 f# L2 E9 b: a/ H
* @return {0}
8 j% H5 q( ]5 Q, x7 y- j */
0 f4 I! O( [; O% T/ w" k+ Z int misc_open(struct inode *inode,struct file *file){
) H/ `: `2 f, I- s9 e: @" K& E printk("hello misc_open\n ");
' ]8 _6 A( Y- q! ` return 0;
3 e9 C: U) E) e: M }- m/ X, c+ P3 ^4 Y
//文件操作集 4 L; n; F$ P3 M" c8 x# f
struct file_operations misc_fops={
$ v# Q! O) D+ m: k! L J) ^ .owner = THIS_MODULE,
# @/ s' s: o" m8 ? .open = misc_open,
4 m! n- H% W8 ~; y$ @0 V; X) r .release = misc_release,
1 e6 J% }0 k5 C3 U .read = misc_read,
# J, l8 B) L9 b- f+ R+ |( Z .write = misc_write, n, y4 _6 S, M' u$ Q- f
};; O; n% g2 \% L5 y! B
//miscdevice 结构体 , t" O+ g5 A! M: U* i4 |8 ~7 d1 r
struct miscdevice misc_dev = {
$ \- ^7 i+ y$ `$ X' U+ F4 z .minor = MISC_DYNAMIC_MINOR, 0 ^. [7 K: V" Z0 a
.name = "hello_misc",
& u' j0 U/ W( Z2 v2 O .fops = &misc_fops,
- a7 |2 y! G/ |) A };
. n/ Q0 M+ f+ P1 k7 B+ R, [ static int misc_init(void) ' {8 D% A) D# M+ B) I& [8 t, Q
{
w1 c1 ]! x! I" R% u1 v$ I int ret; ' b6 n! [0 l7 M" r ^- Z, Q
//注册杂项设备
$ n k5 @4 G8 H& ]! g+ e% ?ret = misc_register(&misc_dev);
& Z' X+ d1 H3 l6 A% `. U$ l# n; b if(ret<0) $ ?6 u) s3 [9 ?& @6 o) h
{
. U; C* O' [# d, g/ @ printk("misc registe is error \n");
# e5 _" }9 K$ K- T+ F6 o }: ^6 D2 P! v+ o4 ~: B8 p! o+ L6 s7 y
printk("misc registe is succeed \n");
: ]1 E) D: T* h. S //将物理地址转化为虚拟地址 ! o' N; t, n. U2 u
vir_gpio_dr = ioremap(GPIO_DR,4);
& l2 R% w3 z5 k if(vir_gpio_dr == NULL) % A1 u b8 d' H
{
) D% `/ Q; d, v) ~$ g- k printk("GPIO_DR ioremap is error \n"); ; U' u5 A& Y0 h5 X \
return EBUSY;
/ t- X2 Q8 O% z" Q8 B }
% \/ F3 E. ]: N printk("GPIO_DR ioremap is ok \n"); T6 ?& z/ s `$ ?2 [6 \' l
return 0;
: W5 Q( f" H9 j( r+ T }
( L) n6 A+ }3 H1 `0 S' C: g2 t$ m! f static void misc_exit(void){ 8 H6 W3 g% j1 x+ _5 ]9 z
//卸载杂项设备 0 c! k9 K7 E" B) `* n
misc_deregister(&misc_dev); 5 T9 m0 j# w( T- m2 V+ K
iounmap(vir_gpio_dr);
0 T8 C, x4 d, `4 E printk(" misc gooodbye! \n");
! N8 t9 m) u2 R6 m* e }& N+ a' [ F: a; i- O
module_init(misc_init); ' F) u8 ?8 k# k* l
module_exit(misc_exit); ! i7 u! ?1 x2 E. z
MODULE_LICENSE("GPL");
% i# X7 s: ^! A, r' W* M& Y4 i更多内容请关注:北京迅为8 I8 d& K3 ~+ c; z a" V6 {" o
0 t4 h9 |3 Q* q8 M! _) g |
|