找回密码
 注册
关于网站域名变更的通知
查看: 228|回复: 1
打印 上一主题 下一主题

Linux设备驱动开发总结(一)字符设备驱动结构

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-4-23 09:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
Linux字符设备驱动结构
1 o) o+ z1 T/ y4 [
9 S( O* G/ v4 X1.1 cdev结构体# u9 t) U, m# t3 s- G% F

1 r+ b3 f: K" [3 @      在Linux2.6 内核中,使用cdev结构体来描述一个字符设备,cdev结构体的定义如下:% q' y7 ~" f: W: E# q% F

! f1 R  B0 `, Q- bstruct cdev {* v. j8 K# Y  m# |1 k" Y$ Z+ t, z8 C
* {: a3 N( O/ B$ U
      struct kobject kobj;9 D" I% @4 ?, l+ @$ E( Y7 g4 u
/ p) {& a" D5 e; x- j% B
      struct module *owner;  /*通常为THIS_MODULE*/% l) d  `7 ~# d" @

9 w* V* C0 l- `, \7 G      struct file_operations *ops; /*在cdev_init()这个函数里面与cdev结构联系起来*/
' Z$ _% _1 ?9 h$ R( _+ U
* Y8 G, p! L2 {* r" e      struct  list_head list;# i" |3 N/ _$ e6 q& I0 U& E

/ a! y$ f2 O) n/ ]  m      dev_t  dev;  /*设备号*/( L1 Q+ a$ y9 ]* s4 ~4 w

+ @; d) H/ O5 t- o8 S; T      unsigned int count;# a6 s9 T+ y( {" }$ c) d7 v
4 {0 j5 v7 A; T! g) n' Z
};
) _8 A) P! x: `
$ v/ J. Z- \! F     cdev 结构体的dev_t 成员定义了设备号,为32位,其中12位是主设备号,20位是次设备号,我们只需使用二个简单的宏就可以从dev_t 中获取主设备号和次设备号:
& d3 L- S+ [! @2 E0 C
( b+ s. t. I8 a$ U: d! fMAJOR(dev_t dev)$ m6 C8 g- a- S, l- [
  F! p: i' C( R; w3 |8 l% V
MINOR(dev_t dev)
: T" {/ }# n" y+ k  H1 \' I
, M0 [* i) T/ ~; U* q相反地,可以通过主次设备号来生成dev_t:
/ k4 B: u# D& }9 p6 R8 f- D) Y
5 q5 w+ d5 |6 y6 t4 j" h; FMKDEV(int major,int minor)
3 h4 j3 g1 A8 w4 b" w$ i) h2 b9 H4 J/ Y% M6 l1 @

2 A, k. M+ `# U( v# T
. u4 g% v9 `& V% o& r5 g2 o1.2 Linux 2.6内核提供一组函数用于操作cdev 结构体:
$ u- W! r, k- J; Z& v" [9 a! v
# h1 f& g, a' p9 Y  S& h1:void cdev_init(struct cdev*,struct file_operations *);
# o) j5 D7 s* y/ {/ Q" f+ L4 g& o7 w7 e
9 p. V8 j' j  o$ K( t3 L2:struct cdev *cdev_alloc(void);
4 @$ B6 J: w# j2 q& t7 A$ k1 K1 ^) g4 C' v' t
3:int cdev_add(struct cdev *,dev_t,unsigned);3 v8 \- W. [6 C$ o
5 M+ w, C+ z4 K/ I! U& u
4:void cdev_del(struct cdev *);/ k( O- R+ U. N( I) C9 j# v
2 g. s! o9 M3 S  t: i: E
其中(1)用于初始化cdev结构体,并建立cdev与file_operations 之间的连接。(2)用于动态分配一个cdev结构,(3)向内核注册一个cdev结构,(4)向内核注销一个cdev结构
9 ^' X! M7 Q* p8 |$ X- {- h! k5 H5 z+ P6 P) s" n9 U, Z
2 x' q$ I9 X8 I' J2 [' s, p: F
- [! T" Q- n& R7 w* `; n- g
1.3  Linux 2.6内核分配和释放设备号5 [2 X- y& T) S9 C6 Z7 h0 b! Z0 |! s
9 O6 _( M/ Z" Q0 I6 J+ W8 \& K. F
      在调用cdev_add()函数向系统注册字符设备之前,首先应向系统申请设备号,有二种方法申请设备号,一种是静态申请设备号:
; \  L" q+ M! ~! v2 P8 s
0 {) m. a* U2 ^0 d2 T2 ~  E9 Y1 a: n! \5:int register_chrdev_region(dev_t from,unsigned count,const char *name)# k" K) `5 z$ V' x" r
+ g6 T+ l  `3 N* A& b& M* N* O
另一种是动态申请设备号:
- p5 N' U: S+ r- M# ]( Q! q5 K& Z
0 D4 f$ n: r4 O6:int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);" t( q* Q1 K6 ?( A# O: m& t. U; I
! D8 ]6 ^( ^3 B1 ?- V
       其中,静态申请是已知起始设备号的情况,如先使用cat /proc/devices 命令查得哪个设备号未事先使用(不推荐使用静态申请);动态申请是由系统自动分配,只需设置major = 0即可。
$ W& u' E& e9 y' u# M0 N# B' E1 T- J, y9 i; \8 }
      相反地,在调用cdev_del()函数从系统中注销字符设备之后,应该向系统申请释放原先申请的设备号,使用:
/ r- O$ g* V& U# t1 y8 n" O9 g. o. @& B/ ~! A" ^/ N) `' @
7:void unregister_chrdev_region(dev_t from,unsigned count);
# t6 g( u* ^0 ?; `% M
6 `; y4 `+ b' I, g; v# }" v* r( I- e& @" W% {  t
2 Z$ X% b* i0 T( ]7 t8 g5 l/ K7 U% Y; z
" s9 z( O1 w! `/ K5 G5 P

- j! b; o$ y! K7 E+ F' B1.4 cdev结构的file_operations结构体
3 V4 u% x  c! I
' Z# {3 d. Y4 j      这个结构体是字符设备当中最重要的结构体之一,file_operations 结构体中的成员函数指针是字符设备驱动程序设计的主体内容,这些函数实际上在应用程序进行Linux 的 open()、read()、write()、close()、seek()、ioctl()等系统调用时最终被调用。在include/linux/fs.h文件中定义,这里不一一详解,仅仅解析一些常用的API。( b- h8 p9 D* I0 q+ l; ~; R* R
- f/ A4 ~8 Y$ j/ t$ f4 I
struct file_operations {
; X& G- N: O8 u
3 V4 e7 }" c$ P/*拥有该结构的模块计数,一般为THIS_MODULE*/
  r" Q6 H6 i  q. D struct module *owner;
2 c) L& Q% q) y2 f
# e- ?+ P( ~, B: L$ d6 _8 S/*用于修改文件当前的读写位置*/7 b8 j1 L" |5 P, J, N6 O* Z+ e  W: z& Y
loff_t (*llseek) (struct file *, loff_t, int);
  N6 @: I; ~& \  R) P7 J; K7 K6 s0 T0 @1 ^( Q9 A
/*从设备中同步读取数据*/* e5 |2 s# a' R% T' b
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
9 k- |6 y8 q* l9 b- x1 f9 j2 X4 H- z, r" s; N! S" e  j' g
/*向设备中写数据*/
1 G8 q8 y4 e; \4 s/ A ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);; D  Q+ y: X; Y1 C
) j4 x: I' ^7 ~; B
' U7 e2 P  b# p/ ~! E
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
' P7 j( d: g# O. U4 ?% J ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
. [& z. C: F$ L& m int (*readdir) (struct file *, void *, filldir_t);7 p2 {+ Z/ T$ }. l# l7 H
" K% z1 a# }& h! n" m
/*轮询函数,判断目前是否可以进行非阻塞的读取或写入*/0 u& _/ W6 e: v* f+ E8 K& a7 {* w; D
unsigned int (*poll) (struct file *, struct poll_table_struct *);
" `% |4 S/ _/ g( n: Z3 O6 i/ J3 J' X( R6 g! l3 B# i
/*执行设备的I/O命令*/
6 P" ]$ }1 E+ @5 u4 C. w4 x int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);8 P: e: n! F6 a$ M5 q

- k8 @% f: [* M) _* d/ r
! X( ^! ^% g1 H  V5 q& E long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
8 \- L+ z8 n" s- ]0 Z5 Q1 J long (*compat_ioctl) (struct file *, unsigned int, unsigned long);) @; I0 q5 k% {+ B8 G2 {8 Y
2 t" \7 q8 i5 z) F% `; b
/*用于请求将设备内存映射到进程地址空间*/$ m  L6 @. D1 T8 K( t4 K% I
int (*mmap) (struct file *, struct vm_area_struct *);
- k5 l6 d' ^1 T
  w" k: {% s4 n' {: X7 A/*打开设备文件*/7 R3 o1 N6 l5 i
int (*open) (struct inode *, struct file *);5 R, ^; M. m. c5 i- V! G0 [6 r
int (*flush) (struct file *, fl_owner_t id);  k! t- l9 J% Z  ?, _6 |
6 A4 U7 R# ~5 @, w2 B  _
/*关闭设备文件*/
. a; B4 P* r8 ^0 K- s$ M6 S int (*release) (struct inode *, struct file *);. j* |  t8 ~& t

$ _/ a# T4 H7 y4 g% S6 E2 ?
6 S& j" }" l2 r/ j/ Z5 S0 S9 Z int (*fsync) (struct file *, struct dentry *, int datasync);
, O* h% n. a- l: z9 l+ T! `1 r int (*aio_fsync) (struct kiocb *, int datasync);  `' e2 S' j2 a$ g1 }' _
int (*fasync) (int, struct file *, int);4 s. i2 B  T( N5 J1 @' }
int (*lock) (struct file *, int, struct file_lock *);  L" T4 ?% T% e
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);% V. m5 I3 F+ `. r0 E; G& K- g
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
3 A- k$ g+ ~6 P! o int (*check_flags)(int);
5 O- t' V" Z6 e% o% | int (*flock) (struct file *, int, struct file_lock *);
# r  p$ h# i1 t2 `8 @ ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);7 p) ~3 v4 `! W0 r
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);" g4 J& Y4 {- x+ A6 J
int (*setlease)(struct file *, long, struct file_lock **);
7 }; b9 M- z7 u: g3 A) Z& O};
; o% u+ ?% @& c$ Q4 B! w9 f$ n7 G( i! m
9 Y) T7 Q7 U8 z# b/ b8 S6 i; y

/ Y; C1 f  j4 i1.5 file结构1 W" r/ W( u( b% ~5 x8 g" w

7 f7 \! z! X) V9 b. g2 Y     file  结构代表一个打开的文件,它的特点是一个文件可以对应多个file结构。它由内核再open时创建,并传递给在该文件上操作的所有函数,直到最后close函数,在文件的所有实例都被关闭之后,内核才释放这个数据结构。; K% u1 _: F- [. Z
- Z9 Q5 {/ J5 j2 G
    在内核源代码中,指向 struct file 的指针通常比称为filp,file结构有以下几个重要的成员:
) i$ m  {8 G% c% b6 r5 @
  H" `0 H) E# a4 l' bstruct file{
8 K+ `9 g* o4 [9 ?4 [2 f! ]3 {0 C
  d; e  h  s% o8 e: ?. \; ^7 `, Lmode_t   fmode; /*文件模式,如FMODE_READ,FMODE_WRITE*/
4 n% b) O/ G3 {. m$ s% f$ d  \$ V! Z% q1 o* S9 H
......
' ]+ ^9 z8 x9 |, c# y1 k9 L4 a- M) g
loff_t   f_pos;  /*loff_t 是一个64位的数,需要时,须强制转换为32位*/- K! g  |; z$ K* ?

; H; N/ s1 o8 E, Tunsigned int f_flags;  /*文件标志,如:O_NONBLOCK*/
$ P1 v* S. d+ o2 C  D* Y) n+ a5 a1 `! o) d/ C6 s( a) Y, T9 j3 f
struct  file_operations  *f_op;
8 \, q, [3 w) u2 b- \; B. ~# H) t
# B) B" V# R+ _2 Svoid  *private_data;  /*非常重要,用于存放转换后的设备描述结构指针*/
- h+ y% C; t" a2 |" b9 }3 c+ R- O; d+ J, x- K& V
.......
" X" Q; ?) V) x: e9 x' H! g7 a% g
7 {* f& q, J% k3 b) d' V};
8 s  V6 {! x% Z$ q2 l: o- B, x0 F" j
8 P* P! h7 j, m# q4 s3 ?: u, G$ ^, h& h$ ^

+ A3 c3 R* K1 O" p+ f6 k# z1 o) n; X  g9 p3 h1 S6 O

+ j9 y; J5 Y' g: e' ]1.6 inode 结构
3 x$ Z$ X8 \4 V- c& a2 X& G1 o- ]1 a" m5 Z
      内核用inode 结构在内部表示文件,它是实实在在的表示物理硬件上的某一个文件,且一个文件仅有一个inode与之对应,同样它有二个比较重要的成员:
- u, b' J* b# T8 o# v# e) x# l& _! F- o8 e- \" J& I5 _' b
struct inode{( B* ~: _" d/ q! i: j
6 Z4 W5 M' a) @
dev_t  i_rdev;            /*设备编号*/
$ r2 w: f9 ?! g0 Y: `
" J% Z0 `1 \7 \2 U2 y" a* i" Jstruct cdev *i_cdev;  /*cdev 是表示字符设备的内核的内部结构*/
: W1 h. v+ c1 [" n+ Z; B9 T9 G7 V& u! I9 a' z: {4 N$ }/ Y2 a
};
, k: c" K0 g" @
2 E; X" ]  z  D& F; l  O可以从inode中获取主次设备号,使用下面二个宏:
  l. p4 Q7 d2 f, ^, i
9 Y' y6 j. E9 D6 ]7 w" k: S/*驱动工程师一般不关心这二个宏*/) l- }; T6 {8 g  z- v
3 j7 U# V9 L6 \
unsigned int imajor(struct inode *inode);( ]" y2 Z9 c( \0 e7 K7 z) J

" A7 Z# f& `& G7 |, T6 Nunsigned int iminor(struct inode *inode); 2 t6 J2 t- j0 I; U! R# @. U& `

( B: d6 d; e( W* a. i& n, @7 Y- T/ V+ I% e3 v* m! c1 i

; [$ [$ X3 }  J2 t1 M8 I' z" K1 ~2.1 Linux字符设备驱动的组成! n& @' H1 A' K% O" _$ x% x$ R
1 Y( g; N) U+ u6 I1 C1 X
1、字符设备驱动模块加载与卸载函数" |$ o; C% _$ P$ u: I

4 R9 F/ f3 b$ w& Y8 I2 {2 t      在字符设备驱动模块加载函数中应该实现设备号的申请和cdev 结构的注册,而在卸载函数中应该实现设备号的释放与cdev结构的注销。) v8 W+ b+ u% h, n: X2 ^

: I' k0 o: n% A. b- J      我们一般习惯将cdev内嵌到另外一个设备相关的结构体里面,该设备包含所涉及的cdev、私有数据及信号量等等信息。常见的设备结构体、模块加载函数、模块卸载函数形式如下:# e2 C* G) ~1 {- a
( S9 J; V' ?. T# H3 @* ?
/*设备结构体*/3 s  l8 v( N2 n" {! D& |
9 i0 b" c* r: |' W7 J5 N$ a0 l
struct  xxx_dev{: f: t4 |# b2 N' G9 O5 A
9 D" }9 _  c1 x/ I- n6 q7 F
      struct   cdev   cdev;
. i: h0 E" b" U: ]4 Q/ g. j& T4 W! t& p4 G0 a5 x% ?7 m& T/ H
      char *data;
6 z' S" m' @3 ~+ [( t  l( ]& P: I6 q. [* [
      struct semaphore sem;% b1 Q/ v) R# X$ D/ U, z% V
6 Y0 ]6 z2 ^9 H0 I4 W) {: R9 L8 H
      ......
9 o/ X. i% u7 b7 a& r; p2 ^; ?5 K+ e' L% h* }" [
};
1 s6 k: z3 u9 X" ]+ u: Z2 q6 a4 Y1 |+ L3 j* O& N  A+ n6 A

) r  C8 D7 E0 {
: ~  i0 s2 x5 P7 T/*模块加载函数*/
$ G% ?; x. @5 s
8 o/ G, W( c% g) W! Qstatic int   __init  xxx_init(void)7 h) ~6 l' q: Q, Y: y
) z7 _$ O7 u2 ^/ W3 k2 v1 `
{
/ [3 m: {! p$ ~( k. V  o
  A$ v5 g/ P: {" l! |      .......
, B7 \$ I/ N) n1 n0 z
4 m7 k* ^* N  I2 F2 u) L% H: U      初始化cdev结构;
% ]( P" A- {2 E) j2 ^, X: Z/ C/ H  t4 A( i  Z) I! \% {
      申请设备号;
/ z( Y, h- f" m2 a! @" f  S/ S# a  Y* ~
4 x& r. e4 I  |  Y& ^. |6 Q      注册设备号;, c- V* b/ ]. ]$ P' _+ D' ~7 I# s9 p
* q$ f% X# _" [" P6 `( i
# H! f# d8 [" C# K8 v
" t2 A% u& m6 g0 g  p& ?
       申请分配设备结构体的内存;  /*非必须*/1 \4 R& S( I  w
8 G8 j# y0 G+ O( H
}  ]% v( \) D2 N. P7 v- i

4 p, T8 r) G+ f- \+ l
! L8 M2 q. A# Z+ D4 M1 K6 B! u
- `% e, s6 s  M5 \* T/*模块卸载函数*/
9 }5 V' ^( A  k
. ~4 {  j  v$ E% t% t. Pstatic void  __exit   xxx_exit(void)
8 c; h% o! w6 t5 |! O! |; I: h. F1 L- \% Q, M% T
{. H2 N3 `' V1 J+ N4 N& C: l3 H" Z
! H/ d# ~- r5 i6 C) D
       .......) ~+ S  i9 |! V8 J, [! m/ a
' u' x1 h. m* t8 r2 v- e- o: r
       释放原先申请的设备号;5 J/ L2 R# \% ]7 @9 H
, e, \  g1 J6 M8 A4 e& u
       释放原先申请的内存;
- Q3 {. q7 O( N2 P% y8 o/ V2 n! d3 p! Q
       注销cdev设备;
" Z( f" V  F2 [; [9 S( Y4 Z6 d' A& g* f# |
}$ J1 s+ F/ t  B
8 a7 T$ ?: p: W' `
$ j8 j  Z1 {. v9 a
* j& y1 b! _  O- W7 V

- O. E+ J# D: w2 u% @: ]! G/ v5 C
* c6 g( S) x* ^/ t8 a+ v2、字符设备驱动的 file_operations 结构体重成员函数
, r1 ^% O/ h# ?( ?% L3 E: x! S( z1 Y( B5 U+ e: p) }, V2 g
/*读设备*/3 U# R; O0 V9 C% [) O- g

# U- e: \  ?, ^ssize_t   xxx_read(struct file *filp,  char __user *buf,  size_t  count,  loff_t *f_pos)/ p1 k, w1 x* R% B

+ C2 N' |$ a+ r& z" A$ ^{2 h+ B0 q% q% i9 T* H! T; U) S; A" e

3 r6 ]$ r7 C" R: i4 C1 L        ......
9 U# ~' u$ q9 E9 d( I. J. R6 ^
5 @5 V* L' T5 V- ]: U6 R3 b        使用filp->private_data获取设备结构体指针;9 ^& W8 C7 r1 s! o1 W6 X( d1 t

, ?9 P6 ~6 ^, ]+ n7 \8 p        分析和获取有效的长度;6 g9 {) K  r8 w1 L" ^: X8 E6 @
( Y4 G) N5 u3 f) r
        /*内核空间到用户空间的数据传递*/
8 i' d7 Q7 j; X1 H
" z$ q9 ?! j. `6 E  N5 q/ a% A" d- q        copy_to_user(void __user *to,  const void *from,  unsigned long count);  F+ H. U1 X0 M, z6 m+ c) |$ c3 Q

7 g5 z- `% Y' q9 P  @& C- X        ......
$ _5 v  Q; z  K, }) x4 V" A" r* M+ y- S9 X2 D
}
( C- t8 |! N; @. t8 Q6 m5 H
- ~$ W& u" u$ J2 A6 w1 J( t: T9 l! N/*写设备*/2 n. z/ p* F1 R  _) F% |+ a

: R; c" o1 l' j4 _: n* T/ qssize_t   xxx_write(struct file *filp,  const char  __user *buf,  size_t  count,  loff_t *f_pos)/ L+ }( Z  k* Y. E

# `% I( O* }, y- G/ f: }{
4 f+ K- x. y' j/ U( r) z; n& L' O- a8 D( s* a
        ......
- s- G# @, o' v$ B# r. _) Z8 e, ?8 |$ d5 h$ v% q
        使用filp->private_data获取设备结构体指针;- O' ?( K) t; Q! j. H" q

" y/ Y  P  P) w+ j7 g8 T) i        分析和获取有效的长度;
( R- S3 C" I6 Y: j( ]+ r
/ d$ O6 }3 X0 n2 h# L& z0 v        /*用户空间到内核空间的数据传递*/
6 E# c/ k# @9 `& B8 A" q5 D
  D( Y  I9 T3 Y$ k        copy_from_user(void *to,  const  void   __user *from,  unsigned long count);
* j. ~& h8 L  N: k1 p; Q# c
5 D' |  j8 n$ X" X        ......5 ?7 e& P( T9 I. C

/ J! x# f$ Q7 m+ u6 E' F}) q& f! `5 d+ i+ B

7 n. v( |3 `! w3 u# _) u0 H9 O/*ioctl函数*/
. A1 A' _4 c, b7 n, f+ W9 z' A6 Q' c3 I! _! }0 ]5 D
static int xxx_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)8 z: A3 t# ^3 L/ ]( Y9 ~; G

: U- c; ~, U1 {{, t; K1 R5 Q) T/ d* W- M' |
7 R* R2 ^: c- Z. T! O
      ....../ h' g" r7 V' p% k

) d* d% f4 X" s/ A# h      switch(cmd){2 ~8 E$ W$ [! s/ b

4 Y4 j0 w! }, [( G; @2 G           case  xxx_CMD1:
* s! w% H3 z9 N0 }9 ]; [: J3 l7 M, w1 c( O5 w
                        ......3 G9 P! i8 {/ s6 R/ f0 i3 A# I; X

* W& Q, |" S. E- S0 M4 I                        break;1 M6 U) K. V! o# b$ Q
' |4 \2 B5 E( B( B
           case  xxx_CMD2:9 a4 i2 ]: `& L- H" n9 i/ O
" I4 X: p! \7 _5 t* z; M, ]$ k9 d
                       .......$ r$ t7 p5 ]' [  U' ?# W- a
; ^' M- k" `1 G
                      break;8 h9 l3 Y& D  e8 y' H: V5 P; f8 P
' _! `, l8 @1 s  C; |
           default:" T4 A0 R# h0 O- ]* M$ g  a
5 @7 Z; @, v5 F. I
                      return -ENOTTY;  /*不能支持的命令*/2 L4 Z/ Q2 ^- q
! h: J- T9 ~" h0 S0 J
      }
3 J* R5 H1 b9 f' G, R: c$ O8 N0 j$ }0 ]
      return 0;0 @; G( H' V5 ^. z( ?
( u: m* D$ V' M2 f- m- v9 U
}, Z+ [# G- [6 Y& B: i" Q+ _8 F
& f6 R' I3 [! Q! p9 W/ E
2 F  d7 v1 U# Y# F. D9 }
4 A" v1 H8 X- J* B% C
3、字符设备驱动文件操作结构体模板
1 s8 [  N9 t; p7 T( ?, X& \6 V
: m- G) [  L. F  t, x. Z" W; `struct file_operations xxx_fops = {
; g* M7 h/ T$ w; d1 M6 u" v4 M& V2 D
      .owner = THIS_MODULE,4 L& b, L6 i. \* [' y  i

; o) f8 ?# [3 U2 P( a! U8 B- m      .open = xxx_open,
" R" X( w2 @7 P4 T) q7 f2 r
( Y6 i+ D4 e. N/ z! H) Z      .read = xxx_read,
# F& X! S% ~2 R9 p! J* {; f" Y4 g: I6 k4 ]# O
     .write = xxx_write,
' \3 t* Z( _8 T6 `5 |$ _8 Q: @; ^9 `& E7 \3 a( t/ g# T& D; N
     .close = xxx_release,
0 |) a1 w7 {& s" `% M( Q& h  ^6 M9 U
     .ioctl = xxx_ioctl,( J6 r  L  d! m

9 |0 p' N% h  G1 u1 ?/ _     .lseek = xxx_llseek,5 |8 o8 Q! ?, R6 u: G
$ r" P( j& q) O
};
6 H+ a) ]' T$ C; C$ W
. B5 t$ P. a# N/ ]% S上面的写法需要注意二点,一:结构体成员之间是以逗号分开的而不是分号,结构体字段结束时最后应加上分号。2 p8 b& H# n! J8 H3 O; _

3 |; v2 s" a) o5 G1 \( P  c8 t: _1 F* i" D- [4 u# N" k

+ q4 H6 Z8 H# E结束语:
% W- ?; V# [* l1 {2 |8 y
" O* T/ I" f8 n; O' @) ~            字符驱动的原理分析大概就这么多,下一篇我们详解一个简单字符驱动程序。推荐二本Linux驱动的书给大家,《Linux设备驱动程序》魏永明译,另一本是《Linux设备驱动开发详解》宋宝华著,最后,祝大家学习愉快。$ q8 f( g/ C( b

% ]3 p& S3 i1 D. U" X5 _% x* }0 k0 R6 h2 d- I

$ q/ B3 M+ n5 N) b" `0 B

该用户从未签到

2#
发表于 2020-4-23 13:37 | 只看该作者
字符设备驱动结构
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-7-3 09:02 , Processed in 0.078125 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表