找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

巢课
电巢直播8月计划
查看: 4|回复: 0
打印 上一主题 下一主题

[硬件] 驱动基础知识学习笔记

[复制链接]

551

主题

1470

帖子

3万

积分

EDA365管理团队

Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9

积分
39487
跳转到指定楼层
1#
发表于 2019-9-27 15:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您!

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

x
一,知识结构  G$ H0 z( f: |$ y. \
* ^, J- _( F1 d; V, ^) H
5 {) S0 W/ r6 I* L
二、驱动分类 可分为 :字符驱动、块设备驱动、网络设备驱动
; S, O# F5 W5 A$ p. ]2 p
3 u. f2 k! S/ h, b: U+ b. p9 f5 y
) b) z- F$ D' B# I* z6 ?8 e字符设备驱动以字符为访问单位(一个字符可能对应多个字节)进行顺序访问,不能随机读取。
! N: G# c8 M$ @' H$ g" S  m
+ N: M/ |. u' {$ {4 V5 C0 {' E8 k' z* w
块设备驱动:以块为访问单位(块可以在内核中进行配置),通常512字节,或者更大的2的N次方。# J% ~4 d# h' e7 D( S

  d! n, q8 ~7 M; W; B
3 F6 |5 }* v% a: q. M块设备可以随机读取。在linux中块设备也可以以字节为单位进行访问,块设备跟字符设备) r5 U5 L+ s% Y  b* z

. X$ ^* R6 c9 F0 W& V9 X& N; y4 V4 z
主要区别是访问接口的不同,并且块设备可以随机访问。/ w4 P- L  Q7 y
" i; S8 p4 w. }2 O- a! L
! a4 F  ^9 O$ D" i, O
网络设备:网络设备是以网络接口为访问对象的。可以是一个物理实体,也可以是存软件,例如linux下的lo设备是个存软件的网络设备。
: D, H( Z$ B5 `' j# P0 T- ~4 J# C! C  `) p8 A

$ X( J. t: }( y三、驱动的安装方式
- l1 ^5 I& v/ t+ E$ C* S# l  F0 L9 s. ?' O6 n- T2 m( t
: X! _+ b9 V* G- Q
1.驱动可以直接被编译内核,也可以以模块的方式安装,驱动设计的模型跟内核模块设计一样,入口都为module_init(),出口为module_exit();如果想要把驱动编译进内核,需要配置相应的Kconfig 跟config还有makefile文件。, n4 z0 T& S) N8 f% {3 ^

( O5 y% h8 e: `
5 A8 J( a) p" G' j! G四、字符设备驱动程序
% {/ D  n  h; z+ H7 D- A! t' h  m# r

- t. l& \  r( b0 v- o$ ?) R/ D字符设备驱动设计流程:1 v6 Q) B. E9 [% k: Z% I4 U& j

+ H! Q7 `+ u. H' _. ?: T5 H4 a8 i3 X4 q( o- J
设备号:设备号是一个unsigned 型,高12位为住设备号,低20位为次设备号,linux系统提供了MAJOR(dev_t num) ,MINOR(),MKDEV(major,minor).来提取跟分离设备号。其中,主设备号是表明设备类型,是建立应用程序跟设备程序的纽带,往往我们有一个产品中有多个一样的设备,那么我们通常只有一套驱动程序,一个主设备号。通过不同的次设备号来区分访问不同的物理接口。一个主+次设备号对应一个设备文件。$ M1 i% C4 z; v2 G$ z3 ]9 O
  h% }& Z6 Y7 W' R4 ~9 U! Y3 l0 y
% s4 ]6 {! T/ y6 {& k6 [/ E
首先申请设备号,可以通过 alloc_chdev_region(dev_t* dev,unsinged from ,unsigned count ,char* name)设备方法动态申请设备号,当然,也可以用函数 register_chdev_region(dev_t num,unsigned count,char* name)来静态注册设备号。在静态注册前需要通过cat /dev/ 来查看当前没有使用的设备号,才能分配给我们的设备,要不然,可能会产生设备号重复,而使我们的设备不能加载进内核。设备注销时需要释放设备号unregister_chdev_region(dev_t num,int cout);
" z+ C; {; w5 S$ L2 k' {9 b4 [/ s& N7 o7 q9 H

) o0 ~4 F( J1 d& O% G9 G4 o4 c2.设备初始化申明结构体 struct cdev cdev(如果申明为指针在使用前要注意分配内存),定义结构体 struct file_operations file_ops={
, I, C; V7 [- y+ m8 B% l2 @( P
2 V6 Q+ U9 }+ G, [; d  L
0 L( l0 A- n! y3 R1 x$ \.open = mem_open,
. C& f2 u: P) |; J/ c+ h2 ]; Z3 B
, q' g* [9 Z6 a/ Z5 O$ h
.read = mem_read,+ O% v$ J; `  s3 f. i6 m6 _

* i: }& N' e; w9 O' k2 {8 J4 M: M! e- o) j( B6 U3 X
.write = mem_write,
- w6 e+ `$ ^" m) M0 F4 r
0 J# U( {% q6 e! u! K0 p
( g3 `$ n) ~+ J1 s, J( A& c6 i) V.ioctl = mem_ioctl,
+ j/ ~2 a7 E7 [) Y& P, T- n, f2 |) z4 o4 F- J

# [! ?: [- q- d1 N$ t2 W+ Y.release = mem_release,
" K% q$ d1 o$ d; F- b, u  j2 E5 s3 M! o5 w0 v! y5 W1 X
, o) C: n( F0 b3 \9 j
.llseek = mem_lseek,; s- L$ G( T& |: q
% j3 i* [3 R9 ?! g
& d! |- L4 r( b. _. V
};并初始化功能函数,为对功能函数初始化的,默认为NULL,) v* F' m" e' L2 l
5 Y, A# |# K* \. i

' E$ g' p- K- E: k5 I, @! ^2 |然后初始化设备 init_cdev(&cdev,&file_ops),指定模块所有者为模块本身 cdev.owner = THIS_MODULE;
/ n# h1 g8 I4 J% |0 P" M9 i, L" J( s
# d" X6 I4 q3 I. B6 l
; p3 \( b% J# |2 L初始化完成后,添加模块:cdev_add(struct cdev* cdev,dev_t dev_num,unsigned count);此时,模块注册已经完成。
- h4 y8 D# \: v4 U4 |% z: h1 |+ X" Y- J

3 ^3 ~& Z. q0 v3 h7 G2 @在设备卸载时需要释放模块cdev_del(struct cdev* cdev);
/ s" M# \$ E$ P  Q# \( z
6 b7 h7 U4 i$ N; ~7 T; r& y- G' k/ w% H# ?) O7 O: ]
3\需要注意的三个重要结构
4 i2 l/ o2 M% k, N: C
$ Z; W3 x% A$ }( g. B- u+ D5 @- q, l7 |1 U9 z0 D+ @
1.struct file 在打开文件时由系统创建,代表当前打开文件的相关读写信息。在文件关闭后释放。重要成员 loff_t f_pos;当前读写位置,struct file_operations* f_op;& F1 a" e+ _6 h# I9 K5 q0 p
6 n8 d$ @; g' q  i

2 [1 l4 m4 e+ F) `, l* R2.struct innode 结构,表示文件的物理信息,一个文件可以对应多个struct file 但只能对应一个 struct innode。重要成员 dev_t ir_dev;
% ~& m% L% A/ G9 B2 J/ A/ n: m3 l4 w9 A. V  i, D6 ~7 O) q
3 u* s+ l* k1 N1 y
3.struct file_operations 是一个函数指针集合,实现驱动相关函数。在设备添加中,我们讲 file_ops结构传给了cdev结构,但如何再传给struct file结构体的,还不明确,需要再深入研究。
) n4 Y1 C+ z. h* T7 S: F' k) l; w9 j9 _: I; @) h4 ]

+ J6 O: @$ V3 g应用程序在调用库函数fread时最后都会使用到系统调用read然后关联vfs_read,最后将file_operation结构里面的函数关联起来。
& O; J7 R- A4 z) R3 T4 C1 w" Q% _3 i' R  I( |" y/ m

# F: S1 S4 n( O/ T- h$ i设备文件的创建:设备文件可以通过两种方式创建,一种是手工创建设备文件 mknod name c major minor2 k4 C% t% e5 ^8 L
$ F7 T- v# }' O0 p: D; r( Z
7 k& b# y! [, w6 g  t' j4 d/ W+ m7 F
另一种是自动创建设备文件,分三部完成:
7 i( D0 x- q$ T- ~' V- \
& O4 }3 `& w5 P8 p# u
2 p# E  K4 D6 P, wstruct class *myclass ;& w$ t8 A. @% |3 ]

& p8 [. {& ?0 u8 y# O( S* o& X2 F# p3 V9 K7 W: t
class_create(THIS_MODULE, “my_device_driver”);: I( {/ Q! K: N: `# A6 J3 P. Y
/ d/ q  P7 ^, `

/ x2 X4 E: D, V% \; R" L4 }) \device_create(myclass, NULL, MKDEV(major_num, minor_num), NULL, “my_device”);
" M$ u2 K* `( \9 Z  D
/ R& ^; x# j% @2 B2 H
& S& s4 @) O, c5 \- s* b% {( w1 |这样的module被加载时,udev daemon(在嵌入式linux中是mdev,自动创建设备节点实际上是应用层面进行的。 需要在busybox里面配置号相关选项才可可以)就会自动在/dev下创建my_device设备文件。% G: }6 S) b! L) z
1 Z" \  h3 W/ j* W9 J6 t8 g" X6 i

8 u7 S/ o& t/ p  e+ o4 v7 o! Q我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在 /dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。' |" }+ n' G; e: J

6 m$ J/ M- A# Y( s! W7 `+ c" B& k
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。6 s, L) P8 Y3 i( c0 M

/ K/ ?9 ?4 }* R7 Z  J9 t% `) i' O' A+ z) W3 n3 B
注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称 不同,而且里面的参数设置也有一些变化。
# e( s! |- I8 j8 K# h9 e' v& @, L' T# ]6 U4 u3 u9 {5 j

. I! m" f. `- b/ R) U其中,读写等,有从用户空间向内核空间传递地址的,地址在使用前必须做有效性检测(因为应用空间使用的是虚拟地址,有可能地址已经被释放了)
3 S7 C1 e! q5 R- @4 J9 }1 L8 I% N. r7 q

/ J- d4 P3 _( L3 z7 L0 e+ Q7 n( e检测方法: _access_ok(void* start,)_access_ok(unsigned long addr, unsigned long size)
! v; q2 Y7 e8 A# t' U: b
  h4 \3 W3 O6 }% o1 e3 C5 j5 J* R( z$ ]. u
其中 copy_from_user(void*to ,void* from,size_t size),copy_to_user 都包含了参数检测功能,但__puts_user(),__get_user(),函数并未做参数有效性检测。
  W) F1 R( H* Z
+ e8 W: ?0 n: a1 N
9 Y  `4 M/ \& k9 m+ d% tfile_operations 重要函数指针原型:
5 c9 S' ]% L$ e. u) c7 u* x) n" F" S3 q# u7 j; X
  j# }+ p) A' Z% V
loff_t (*llseek) (struct file *, loff_t, int);
7 n2 i7 L3 j* G& K% _5 Q, k# U" q$ A4 K# W
; p, U0 P4 q$ i7 R
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
2 j& S. [% B9 u3 Q+ L
  c' o( W; z5 X$ N% {, @) |' ?! A" X* }$ E. Z+ R
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
3 h! ~; u1 J+ @, C7 ^, o
9 l# u5 F" L6 e$ J1 ?$ x1 K5 w1 [- A+ Y2 Z2 C
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);# ]  v0 E# {) ~& U8 h( ~

: J  Q" J" T" H3 w+ g0 C7 e2 w) d% u1 i  ~
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
, ]' M8 G2 _, f. d
8 t0 m) d- M8 v- P7 ^$ K2 v9 l) D8 S% j: }4 O2 ]
int (*readdir) (struct file *, void *, filldir_t);
" u+ u' V  ?$ V+ l/ Z" W' Q
+ w$ i1 i) p" s5 I% q: o1 {5 Y8 ]) N' R* \7 H
unsigned int (*poll) (struct file *, struct poll_table_struct *);
0 K& c, Y9 Y& `. O( z
, B) W% |8 G  L4 P/ @5 C+ _2 M" k' N# n
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);. |+ T5 V# ~, {$ \6 Z* ~( ^  K
7 `. {$ \: g7 V) H7 e( O8 k% J0 H

8 P  _. C2 P9 _7 k( u' C0 K6 ^. |long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
8 ], S% Q3 H6 N. Y- i) Q" E" N+ f- x7 q, Q2 r1 w8 @8 X7 r% e
: m5 N- G2 x7 d0 t2 _4 H$ G
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);, |, G& \- I9 T; J/ w1 k
. [6 ~) n1 f6 t. h2 v4 ]
6 T; c4 H$ n. T+ i
int (*mmap) (struct file *, struct vm_area_struct *);' |. Z/ I( _: D1 Y( m, R9 S
! l( z5 j7 L- |- f

; t8 M3 s. {( `% j$ [1 Gint (*open) (struct inode *, struct file *);
, w! c+ u0 |. t  |' \2 ~
( \; h( S/ b# l9 K8 W
- b6 [& G/ D, K2 gint (*flush) (struct file *, fl_owner_t id);* N- P- G! S& f6 H- h2 V

: ?& Q! I) t- b% I7 h) ]# B0 z3 |6 |3 J+ t
int (*release) (struct inode *, struct file *);
% ^+ _7 y$ V5 u) g4 e) ], ?
; m& V; _& g2 ~4 A1 V: J: I1 R, {$ Y4 N* W/ _/ F3 w
int (*fsync) (struct file *, struct dentry *, int datasync);' D8 Z" v/ G6 h  V  O9 E+ z

3 p1 Z5 c9 a8 n7 N2 k' d7 Q: Y# Q. ~8 I+ a
int (*aio_fsync) (struct kiocb *, int datasync);
! h3 e' m- K+ X. C- ^$ h2 y
: T" a$ E1 q; s" u0 X5 c5 X* l7 x  ^3 t/ {7 l
int (*fasync) (int, struct file *, int);- t7 r8 J2 Z- q2 C6 m& n
$ f! C6 u. p0 S9 k$ b

: ?8 v; j' t8 Sint (*lock) (struct file *, int, struct file_lock *);
5 I  C2 r6 X5 S% t
& O9 p0 X: r7 y+ l$ B0 G
* ^, ?1 ]; W/ A/ sssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
4 H9 a3 [, R- X+ k" P) E4 {4 o9 w3 G3 r6 ^5 t; Q6 v
. d9 r2 Y5 ]- p5 Q. D4 \* R
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);* |# }& Y" p  A$ g: K
+ z& I7 u; h! p/ n/ ~
! Q0 @0 b9 n0 W
int (*check_flags)(int);4 t0 Y( S6 t3 F" G3 U1 e9 E2 b
2 ]; X( K( X6 j9 C( j+ Z& [

8 K5 n6 L# \; z6 m4 r6 c2 i) X. mint (*flock) (struct file *, int, struct file_lock *);
. ]1 P+ l4 ]) w4 w, t" c7 m, z, ~. M# ^/ q+ x, @3 d

+ {! m+ A: {4 E6 ussize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);' L$ z1 I& K: P
! _8 P  e5 O4 v9 u: b7 \
! ], {( T0 t. ^  c, Q; C
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);" A$ r2 P# v" U8 h: ~2 ~

7 m0 s3 F# k2 D; s) O
' I! m( k: y& K6 y- {7 F$ \1 Rint (*setlease)(struct file *, long, struct file_lock **);
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 支持!支持! 反对!反对!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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

巢课

技术风云榜

关于我们|手机版|EDA365 ( 粤ICP备18020198号 )

GMT+8, 2025-4-5 21:52 , Processed in 0.058662 second(s), 32 queries , Gzip On.

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

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

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