找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

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

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

[复制链接]

551

主题

1470

帖子

3万

积分

EDA365管理团队

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

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

EDA365欢迎您!

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

x
一,知识结构1 M; c2 K  u/ ]8 }6 T6 S9 J2 x! A
1 q6 t9 d! q, _  B, Z9 ^

0 z# j& D8 W3 G: A二、驱动分类 可分为 :字符驱动、块设备驱动、网络设备驱动8 p) H' h. C+ Z+ D) Q

' R7 s; O0 Z( f# T5 i: A% a) Q. V& P  s
字符设备驱动以字符为访问单位(一个字符可能对应多个字节)进行顺序访问,不能随机读取。
8 B- n; G+ b# R( a
) ?6 n1 @' j/ p- ]# n1 a3 V2 V4 K& `" ~$ P& r1 n
块设备驱动:以块为访问单位(块可以在内核中进行配置),通常512字节,或者更大的2的N次方。) ^" g8 |; \; A$ i: C, T) j

5 G1 O* |# N+ W2 m/ q( q1 x) Z. u/ ?, O, W- [
块设备可以随机读取。在linux中块设备也可以以字节为单位进行访问,块设备跟字符设备+ `1 H  s# h, Q5 X2 t; r  {

6 |, P7 P' O6 |. D+ H3 x: R! B9 ]/ A1 s  j/ V+ s* k
主要区别是访问接口的不同,并且块设备可以随机访问。1 P( M$ V- S7 w
0 \3 P: i8 w% W- t: I" `* H. M* H
1 K- I! q: Y4 t: u5 Y! h
网络设备:网络设备是以网络接口为访问对象的。可以是一个物理实体,也可以是存软件,例如linux下的lo设备是个存软件的网络设备。' J  L! o& B" }* }: x9 c6 i1 t
) z6 o$ b% g* G7 d: i4 `
. L8 S& [7 ]0 P
三、驱动的安装方式; e% z& I% v, G

( |- `' n& o' b5 u2 Q/ ~. [  L7 O5 R: i, C+ ]3 A+ w" M2 S
1.驱动可以直接被编译内核,也可以以模块的方式安装,驱动设计的模型跟内核模块设计一样,入口都为module_init(),出口为module_exit();如果想要把驱动编译进内核,需要配置相应的Kconfig 跟config还有makefile文件。
# q2 p" F# j3 g
$ Q( r* e4 D( ~" C% X! @7 ]+ z: i8 |( V  T- ~! `4 c4 Q
四、字符设备驱动程序
( Y% d% T& @/ c7 u# p& y+ z
, ^2 I2 G3 l/ s9 X+ ]
) i+ V! L4 m# N0 e字符设备驱动设计流程:
  K6 M, t6 o8 Q0 g# P
! G1 r* g. {. F/ o! ~: ]* A( w
8 I9 w/ G: T+ e" E设备号:设备号是一个unsigned 型,高12位为住设备号,低20位为次设备号,linux系统提供了MAJOR(dev_t num) ,MINOR(),MKDEV(major,minor).来提取跟分离设备号。其中,主设备号是表明设备类型,是建立应用程序跟设备程序的纽带,往往我们有一个产品中有多个一样的设备,那么我们通常只有一套驱动程序,一个主设备号。通过不同的次设备号来区分访问不同的物理接口。一个主+次设备号对应一个设备文件。1 K5 Q) `3 q! |: M' l, W( ~! G& e, D
8 A+ \0 W  {0 \" N2 ?, ]' ?+ d% W2 f
  {0 X3 ]$ o0 {* l: @# [. H
首先申请设备号,可以通过 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);
5 j# x4 k: V( F3 |6 K. f8 S# }( l; \1 c( r

1 v- q4 b) I5 h. H2.设备初始化申明结构体 struct cdev cdev(如果申明为指针在使用前要注意分配内存),定义结构体 struct file_operations file_ops={
  k1 R, f8 U: u. T; m8 j" v; _; P
7 Z: @8 u2 W4 \
3 n8 }5 p2 p3 e* c- g$ w5 {6 B7 G.open = mem_open,; z  d& V+ v/ ~

# I. ?. e1 w$ J$ C2 C
; _+ P! A3 l7 J: }+ Z# r$ J+ \! Y.read = mem_read,
0 I- k1 D: m' I  C: j
! O- m0 h6 D% T3 {  R( g. H3 C' ?& g# C9 P8 M1 q' ]! L
.write = mem_write,
5 e6 T+ ]' c* r8 `( E+ O
6 a% Y2 d& g4 r6 H! i/ d1 Z( W2 I1 X% l2 W/ `
.ioctl = mem_ioctl,
. [5 c+ r, G, _9 H, A( N
" A0 l* M' q- C' R6 C) Y% P( e% L+ j% _  D0 ^
.release = mem_release,
9 l, e2 a' n' h- X& t# O0 e6 k; s- w) ]; J0 U

# Y$ s( ^  X3 ?* I9 Y. K# U( J- ~.llseek = mem_lseek,
9 M4 P9 N8 J: e$ b
* r7 ~7 y; R' X" w  H- a
# _: d  S- B# i0 c% {};并初始化功能函数,为对功能函数初始化的,默认为NULL,) t. J0 s9 L, W/ K
6 [' N9 R) m# m

$ \. T/ \) G' l$ I5 c) |0 R然后初始化设备 init_cdev(&cdev,&file_ops),指定模块所有者为模块本身 cdev.owner = THIS_MODULE;" J$ W9 A7 J; \

/ b4 H* g8 H$ b6 Y6 y9 P* Q1 T
# H+ A9 {8 ?7 Z初始化完成后,添加模块:cdev_add(struct cdev* cdev,dev_t dev_num,unsigned count);此时,模块注册已经完成。; y2 O, A: ]- w  Y

% m/ c& l& r: F1 m  i
6 T! Y. `# k& ~0 `0 z在设备卸载时需要释放模块cdev_del(struct cdev* cdev);2 t4 Q' g* G4 {+ o

! d% z5 ^! n: o! x6 c+ K% M& K- ]
  G! n$ P" B+ o! I3\需要注意的三个重要结构
9 N, ?/ C1 f+ ]6 |: m
$ A% k/ i* S4 Y+ V
, w0 S3 u& C% c& E7 Z1.struct file 在打开文件时由系统创建,代表当前打开文件的相关读写信息。在文件关闭后释放。重要成员 loff_t f_pos;当前读写位置,struct file_operations* f_op;
/ ?- Q3 D+ Z, Z& n/ h
0 x5 ]3 d: z5 M5 l! v. D- G
2 y4 b6 l0 }5 i7 k2 ~# F2.struct innode 结构,表示文件的物理信息,一个文件可以对应多个struct file 但只能对应一个 struct innode。重要成员 dev_t ir_dev;
; ~4 q8 d) j) x: x; s' v' \) k5 {0 @& \7 @8 {

4 r4 s2 c% b4 p* U( l! p6 E3.struct file_operations 是一个函数指针集合,实现驱动相关函数。在设备添加中,我们讲 file_ops结构传给了cdev结构,但如何再传给struct file结构体的,还不明确,需要再深入研究。
# O- V; A+ F  D5 j# @* I$ r' T$ j! }

* e6 v: Q7 G* P* t- @应用程序在调用库函数fread时最后都会使用到系统调用read然后关联vfs_read,最后将file_operation结构里面的函数关联起来。
7 z" W, {0 r  W
+ Z! x& J# s3 c5 r6 Y3 a9 R% S  `5 m) Q0 ^3 c8 o7 s
设备文件的创建:设备文件可以通过两种方式创建,一种是手工创建设备文件 mknod name c major minor
( x/ `( u7 E3 ?% F- T1 q+ n! [( D) t' X( K1 o0 _6 e
+ `2 k: D6 D. w  A( z; k
另一种是自动创建设备文件,分三部完成:+ j) k* ]) o; E4 M8 t0 l
( X, u; V+ j# W% b& ~, K/ V
2 K' m- y# F0 ?9 T: Z7 Z
struct class *myclass ;; z6 }" m4 x; B- V- {

, t( {. N) j# \+ |: k  m" {8 z) W% t$ ?1 E, \4 n( L' V$ T
class_create(THIS_MODULE, “my_device_driver”);
8 |* o4 p: X8 Z& i! |. s6 `
3 ~* V$ a& a. X: s4 T3 u; b5 S7 T/ o
device_create(myclass, NULL, MKDEV(major_num, minor_num), NULL, “my_device”);
* k, d! A: Z; L& k% e
  h/ t# i$ E$ k" J9 }$ `9 L# I% i' l
这样的module被加载时,udev daemon(在嵌入式linux中是mdev,自动创建设备节点实际上是应用层面进行的。 需要在busybox里面配置号相关选项才可可以)就会自动在/dev下创建my_device设备文件。
  d* ^! L8 C1 ?9 n8 h( h6 n
& m) j7 [* n0 w
2 c5 E" M1 x$ D! G" ?! r我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在 /dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。# J; G4 V  ?) O! h
5 G9 b0 p: }; ^! o" z( Z

" [: S- K' W; h' u  v* d内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
' n1 U1 y/ X7 n) j, s0 ~3 N# U2 v3 l

$ c6 F8 |# T3 A: a6 [  i0 X注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称 不同,而且里面的参数设置也有一些变化。$ b6 ]% }  H8 e& C& R
' e: A3 L# ~: l
! [# k  @' p1 i7 U, T# q
其中,读写等,有从用户空间向内核空间传递地址的,地址在使用前必须做有效性检测(因为应用空间使用的是虚拟地址,有可能地址已经被释放了)
: m$ b7 U' U) O) X% x2 W: g- g) U1 f1 N. M$ o& t

: X9 k5 m4 S, y6 e5 L检测方法: _access_ok(void* start,)_access_ok(unsigned long addr, unsigned long size)
! H! x" s% B  i6 s4 M3 |
7 C% |- @7 n9 x0 O4 Y0 p& T, x
" ~4 u% r$ a0 r9 L* N# q& ^其中 copy_from_user(void*to ,void* from,size_t size),copy_to_user 都包含了参数检测功能,但__puts_user(),__get_user(),函数并未做参数有效性检测。
: P1 l% E/ A" u; {! y7 C
. V+ n: f* Z# b8 u5 J' Q; ~  U8 w2 `# o* Q. M
file_operations 重要函数指针原型:
- V- l$ X8 m" N1 X5 E7 _5 x: b! K, ~: g
# t- G  l6 e* x+ X  M0 x8 x
loff_t (*llseek) (struct file *, loff_t, int);
8 ^& O! E& Y$ N& W3 j! w' c3 j5 N" U8 Q1 `. G: `( j0 F

  K* d) C/ n4 \1 N) \5 T3 Pssize_t (*read) (struct file *, char __user *, size_t, loff_t *);* S3 O) ?/ o# E' p1 D

' s0 Z7 _4 p- \" L, X
' U  U/ E- V$ m+ m* r8 l# ^ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
) W0 O* v& H" _& G6 ^/ L* x* w
. f; P4 Z3 D4 U! y2 \9 D/ m
3 R2 \2 m# B2 a! v$ i. ~$ {" Kssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);/ `1 |' `. h: t! h9 b) H

; C9 }- H. |$ p' a7 O* z# S0 u( u* J
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
+ p% @5 O4 v7 A) R" Y0 Q3 ?$ I  P$ _* H7 b; l) ~  m9 B
- ?) k  M  L  z0 p7 ^! n
int (*readdir) (struct file *, void *, filldir_t);6 l( r4 U9 \6 \0 B# o! P
1 B+ ]0 v1 V& i6 D
5 g  U. d, D# m0 ]2 B! h7 K, U
unsigned int (*poll) (struct file *, struct poll_table_struct *);
. p; f8 C3 T) ^+ f2 D! D( x' n# t! _8 i1 |& G) X

- k$ J: r. e' }int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);5 S3 t) ~% J* B- j- f, `
  L" {- Z5 ]# h& L% l

+ v7 R0 J  r8 g- U6 I6 Xlong (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);% F0 s4 s6 p" p* v3 O8 J& _& t

4 q% b) n1 ]6 V9 p) _5 w; W: {8 I4 u4 P1 z# J
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);* _! W/ G! b# F- M$ Z& A: p# g* z
6 V- W" @5 Y" T- Q6 e

& F, e1 Z6 o" oint (*mmap) (struct file *, struct vm_area_struct *);
! N* H1 x4 d$ v+ l3 H! S9 `+ p- d* G2 H. A8 F4 D, j

2 l0 q8 q  ?" M; @int (*open) (struct inode *, struct file *);
9 M( I2 O4 J2 @5 Q+ _6 r
7 {; [! {( |, t; y) r$ I/ {7 w# y
3 r  Z* g+ Q. c( ]8 M7 R1 w7 @int (*flush) (struct file *, fl_owner_t id);
8 K9 `) d6 C5 H! d3 x% a9 Y% ^, l. Q% @; E- N2 Y
+ G2 c) J# A/ w% n' A/ y$ r8 }, ^
int (*release) (struct inode *, struct file *);
1 J- o% \' C, e4 O* b% `8 y* M4 `2 A( h, V- g" ~

1 G# Q' _2 q; l+ D" i/ hint (*fsync) (struct file *, struct dentry *, int datasync);
* w2 d( ?, M; R9 @7 y8 x
1 Z1 y) }3 c. U. U
, _2 E  u3 p$ K( z3 ^int (*aio_fsync) (struct kiocb *, int datasync);
) x7 f" F4 y5 p5 I& q% M7 c6 _  s# X$ V& d7 ^9 B! ~

& R! q; F" d8 N( W* ^int (*fasync) (int, struct file *, int);  k- X" c8 X( T4 i

0 b5 c5 I$ m9 [+ U* S, X! N4 I  B9 n! o8 C( Q$ E  ~5 N
int (*lock) (struct file *, int, struct file_lock *);+ ]( B/ F8 X7 L6 A2 U
2 {. D7 P: O* Z8 o

7 |  I8 G. j0 q5 pssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
0 N. J) Y7 a' F& x1 P. h( D! J; ^: ~. Y8 ?- o; E2 R
; O( ]6 R+ H- ^8 |4 j' f
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
7 }# F- I$ D- n" Q  s/ @: n" C( H/ l

& f1 v% b/ \7 X: K4 |% A- u5 T% n' Gint (*check_flags)(int);
, z' R  U6 K4 ], U4 W- s
" F  V- P8 O2 k2 p  ^, \2 Y+ \! q* O
int (*flock) (struct file *, int, struct file_lock *);8 S# n2 I5 D! Y3 ]; @  `2 ~9 f

: v/ W" w1 }/ _4 Q8 l4 x# e  U. m
7 C, X& Y: z6 J6 l, _3 s' Assize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);: Z. [/ c5 B5 r* L4 W% Z1 ^

) b( H- g$ d+ D% o8 `6 j+ c) g/ d- S6 x1 `1 m5 c
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
, `& h8 b3 q2 b
6 I9 S) ~$ y, |( f0 n0 |" w& w/ B5 X) S/ ~
int (*setlease)(struct file *, long, struct file_lock **);
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 支持!支持! 反对!反对!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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

巢课

技术风云榜

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

GMT+8, 2025-4-6 11:26 , Processed in 0.060798 second(s), 34 queries , Gzip On.

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

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

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