a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 249|回复: 3

[综合辅导] linux指导:LinuxI2C驱动分析

[复制链接]
发表于 2012-8-4 12:07:07 | 显示全部楼层 |阅读模式
最近在看Linux 2.6.21内核的I2C驱动,也在网上查了一下资料,有错也有对,有些心得,记录下来吧。里面认识或许多有不当之处,还恳请指正。/ U  ^5 Z' \* [; Q2 B7 @7 I
    0 V7 m6 Z) A& c2 L. {  o5 ?
    1. I2C 协议
! P- J  L, ^1 g6 z8 C, V( U7 ^2 C    : f1 u& N0 Y7 v7 f" J) L  o/ u
    1.1  I2C总线工作原理
  P! {: w/ v, k   
1 A  X! l0 Y4 X8 B    I2C总线是由数据线SDA和时钟SCL构成的串行总线,各种被控制器件均并联在这条总线上,每个器件都有一个唯一的地址识别,可以作为总线上的一个发送器件或接收器件(具体由器件的功能决定)
! E* p' U, C1 [/ x: F    : S# b1 k' `- L2 _: R. D+ z
    1.2  I2C总线的几种信号状态
! P) ^0 L' q1 z. G! s) ^7 h   
" k6 p1 P) }4 z2 }$ x3 l/ t    1.  空闲状态:SDA和SCL都为高电平。
9 c1 v9 q1 e, R1 K; z+ Z  y2 r    $ F9 [# [* l. c$ ^
    2.  开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
# T! t6 |2 Q, z   
  F# v7 l, H$ \: w( N% j" x; M    3.  结束条件(P):SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。2 ~/ J8 A' \  S1 e8 e$ c
   
  x8 [2 d" e( r! u/ }) ^) l    4.  数据有效:在SCL的高电平期间, SDA保持稳定,数据有效。SDA的改变只能发生在SCL的底电平期间。+ C8 j* |. F, i
    % w6 l  G7 i" J& A* I/ p5 h
    5.  ACK信号: 数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。/ R2 h3 ]+ L9 @5 \- M: h6 r
   
. h- S: a  b/ `    1.3  I2C总线基本操作6 Q. j, r6 ]- A) y6 T9 t: J
    4 y, s. U6 q% x# [# s1 X+ r9 M$ F2 V
    I2C总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL),同时控制总线的传输方向,并产生开始和停止条件。
" `$ ?3 ^7 Q0 q# R    ! [! g( J8 s  W# ?3 W
    数据传输中,首先主器件产生开始条件,随后是器件的控制字节(前七位是从器件的地址,最后一位为读写位 )。接下来是读写操作的数据,以及 ACK响应信号。数据传输结束时,主器件产生停止条件7 X, V& R* _( r) {
   
9 q, _) Z3 q2 ~0 _% i! m    2. Linux I2C 结构分析8 f" ?2 o; ]- |, j% s
    , ^/ J4 q; g# g
    2.1 层次分析
* S. J# X0 j- `' h   
( L( p' W4 [/ s# @7 R+ ~    1. I2C Core0 G6 u/ n, K2 i2 c" f4 [; m
    ! Z0 f" `3 n+ ^3 m$ c
    I2C Core用于维护Linux的I2C核心部分,其中维护了两个静态的List,分别记录系统中的I2C driver结构和I2C adapter结构。
9 J3 w& a' c( O5 M! q( S" E   
3 ?/ h4 I4 M5 Y& ^    static LIST_HEAD(adapters);/ u+ p4 L4 u6 h/ @
   
4 T; Y; v# i0 C& N9 D5 V    static LIST_HEAD(drivers);7 f$ k  b- h" M( B: T5 }' Y, }
   
- G' R0 b( \1 X8 {8 e) a( ?    I2C core提供接口函数,允许一个I2C adatper,I2C driver和I2C client初始化时在I2C core中进行注册,以及退出时进行注销。具体可以参见i2c_core.c代码。2 o6 j$ D0 u2 B- h3 I# s7 I
    % S- D3 |# p( u8 R7 H- P+ h
    同时还提供了I2C总线读写访问的一般接口(具体的实现在与I2C控制器相关的I2C adapter中实现),主要应用在I2C设备驱动中。: Z( N- y3 s1 `. X$ \
    2 [% I2 u* W. V) x5 n* \
    常用的主要是9 \+ g% o% \5 B" G/ k9 l& P
      D- j. u+ q- G
    i2c_master_send()
: C* V! X3 g4 ?) a( a1 {5 `  e   
6 [% i) x# v5 D) X& U8 V5 s  R, `    i2c_master_recv()
( t; z1 Q3 j4 w0 Z3 ]   
) n: V- Q( u* n& t    i2c_transfer()
. L, T  l9 S" K8 l; G' `2 ~   
) b3 ]0 H4 F( |0 V: o0 V    2. I2C bus driver
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:07:08 | 显示全部楼层

linux指导:LinuxI2C驱动分析

总线驱动的职责,是为系统中每个I2C总线增加相应的读写方法。但是总线驱动本身并不会进行任何的通讯,它只是存在在那里,等待设备驱动调用其函数。( T0 ]( w; v% t" S
    ( h, n' e# E5 u6 G
    在系统开机时,首先装载的是I2C总线驱动。一个总线驱动用于支持一条特定的I2C总线的读写。一个总线驱动通常需要两个模块,一个struct i2c_adapter和一个struct i2c_algorithm来描述:9 _; o& m- \6 B
    : |9 v" @! j1 ]# J. |- S7 n4 w1 W
    在 buses目录下的i2c-pxa.c中实现了PXA的I2C总线适配器,I2C adapter 构造一个对I2C core层接口的数据结构,并通过接口函数向I2C core注册一个控制器。I2C adapter主要实现对I2C总线访问的算法,iic_xfer() 函数就是I2C adapter底层对I2C总线读写方法的实现。同时I2C adpter 中还实现了对I2C控制器中断的处理函数。
4 Q. ^: ?" ]# q- y' O0 V2 c   
8 s# X9 t) I& T# q" w    1) i2c-pxa.c定义了i2c_algorithm,并且实现了master的发送函数i2c_pxa_xfer(),以及设备查询总线的模式的函数i2c_pxa_functionality()
+ U2 ^5 e( q# c7 x    / a  b+ P) d2 p; c
    static const struct i2c_algorithm i2c_pxa_algorithm = {
" a. x, t/ X8 z- R' \- ^4 i5 p    / m! Y5 ?2 f( _- ~  L3 y6 _
    .master_xfer = i2c_pxa_xfer,
7 ?" q& a# y7 R( d' f5 i    ( L) g9 y8 r/ b6 Q* d
    .functionality = i2c_pxa_functionality,
& n1 d6 H" d+ F$ E7 J    " U3 _# }2 D, G# e/ u' Y2 s8 N, T; H" s
    };0 l, \) T4 r9 Q6 b+ o6 d$ [* y
    ; ^  [& v1 \# l, b8 P
    2) i2c-pxa.c中,实现了i2c_adapter,主要是在定义pxa-i2c时进行初始化,并且i2c_pxa_probe()中进行填充parent指针,并且调用6 q: |2 w: j' U  h; u
   
& S% K0 R5 R( |5 {9 X& g) R    ret = i2c_add_adapter(&i2c->adap);3 _, u# p! K: [) e
    1 m8 F# ~$ N4 _
    进行添加。
4 ~5 j3 g) L) o3 j, n6 ?/ G      U- G7 Z/ W( ]( i. [2 n
    static struct pxa_i2c i2c_pxa = {: U! B, v& i2 B* B5 E9 x) T
   
# M; h* F2 \! V  ~    .lock = SPIN_LOCK_UNLOCKED,
+ j' I1 G8 m* k0 Z' c" A    / T" ]. ^* a; r
    .adap = {: b. _: y6 E2 E. n5 s0 H2 D
    ! f; c% Q5 k' l3 M* C$ {8 |! ]
    .owner  = THIS_MODULE,
: s4 |+ f0 F1 a: w0 |- b1 Z    " Q2 R$ X/ b' o0 {3 U
    .algo  = &i2c_pxa_algorithm,
+ e0 m1 |: }" Y- Y! X, j! u, `7 g    ; c$ ]' |# f; o; v5 H$ K- \! q
    .name  = "pxa2xx-i2c.0",
" J7 K$ O+ }! l. f* h& K8 ]   
3 G: A  l6 a+ h    .retries = 5,: n( p- T8 ~* ^; u4 _
   
* ^5 c, N4 t: o7 n' ?$ w    },; T, [! a) t2 t. h
   
+ ~% e/ G2 a) k+ \0 E2 e4 _' H+ S    };
. O; }# B% x; J% b; `  O! b( Y    $ L' I  p- E' v& _7 Q! x
    总的来说,在i2c-pxa中,使用platform驱动模型,完成了i2c的总线两种模块struct i2c_adapter和struct i2c_algorithm
/ ]( ?: ~; l* J' H+ u& P    0 _" h4 o  R2 y. n$ `
    3. I2C device driver
4 @% G# ]$ h7 W1 [   
8 p  S+ q0 Q5 D    I2C只有总线驱动是不够的,必须有设备才能工作。这就是I2C device driver的必要性。I2C的device是有两个模块来描述的,struct i2c_driver和struct i2c_client.$ q/ K7 n' |$ n. f
    . \) h( c$ B1 e+ ^+ w, @- w
    在介绍chips目录下的device driver前有必要介绍一下i2c-dev.c文件。
& K9 G. x; z' @8 \2 @   
5 b2 I- R/ p4 g/ D! `- |( Q1 W0 u6 j    i2c-dev.c中提供了一个通用的I2C设备的驱动程序,实现了字符类型设备的访问接口,对设备的具体访问是通过I2C adapter来实现的。构造一个对I2C core层接口的数据结构,通过接口函数向 I2C Core注册一个I2C设备驱动。同时构造一个对用户层接口的数据结构,并通过接口函数向内核注册为一个主设备号为89的字符类型设备。
1 a, ?$ O* Y. C" g  J    ' t2 k! _* q8 W9 ?
    static struct i2c_driver i2cdev_driver = {7 F: ~0 j. j" u" S
   
/ N: ^! ^2 c+ X6 i    .driver = {, I, @% D: E' }. A. F$ U4 [
    $ f6 O4 P* L" q
    .name = "dev_driver",) y3 m( R0 h( ^' y: Q) s
   
. }  b9 T# o5 Q4 X' r* U    },
2 Y% l, U3 {% Q/ C; c9 v   
3 U  G/ b+ Q/ Q! i- {- O    .id  = I2C_DRIVERID_I2CDEV,, Q8 S6 X* \, Y( @+ F
    3 b3 M2 B$ p3 c9 a
    .attach_adapter = i2cdev_attach_adapter,
/ @! O$ r" J9 ]* [1 y3 M   
& }; N: V. J: v9 F4 V2 J    .detach_adapter = i2cdev_detach_adapter,
. S; @' ~0 Q9 p9 _% b   
* F9 D! Q9 y. K# x( [    .detach_client = i2cdev_detach_client,/ c6 T& ]1 N3 d) Z; k$ P6 x- L
    8 c. Y3 ~8 X; Z& r% A+ Z& `
    };
( K( R& c2 `9 ?   
) `% X! r% w8 W5 G/ j    struct i2c_dev {
$ i* r. Z* ]) H! d6 F) k8 z2 e   
/ `) |# ^1 n8 ?+ \) W9 l' P    struct list_head list;( F+ `) k; g8 v3 s" `1 n
   
2 j0 l6 [0 v+ G    struct i2c_adapter *adap;5 c6 X3 {) N: q
   
5 t* t+ ~0 [3 @: Q    struct device *dev;1 J- j* J$ L- M- Y
   
5 i  o( ]1 m. }. J' |    };
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:07:09 | 显示全部楼层

linux指导:LinuxI2C驱动分析

该文件提供了用户层对I2C设备的访问,包括open,read,write,ioctl,release等常规文件操作,我们可以通过open函数打开 I2C的设备文件,通过ioctl函数设定要访问从设备的地址,然后就可以通过 read和write函数完成对I2C设备的读写操作。
0 i" k% D5 c4 i; c7 i7 N   
" }) d/ b7 O  w8 f    static const struct file_operations i2cdev_fops = {8 ]6 k. q; F' b( t. T
    : K5 y" X2 P. O: x* C1 M. N" j
    .owner  = THIS_MODULE,' Q3 A5 C+ R4 j1 A5 k
   
6 a. _/ A% f8 P' z9 V2 F    .llseek  = no_llseek,
3 c4 X* t9 v& ?0 l, d4 [7 |   
" X5 L( V' A' e9 k$ T0 J    .read  = i2cdev_read,
3 V$ Y/ Q) X7 E   
6 c8 l. S/ n9 B" q6 l2 [    .write  = i2cdev_write,
. m5 \$ i  ]9 L: [      S/ _: y( a$ l( a4 c9 i
    .ioctl  = i2cdev_ioctl," a7 u9 S2 i+ V3 A* ]# d
    % n8 O7 c6 h0 {/ \: q3 T$ S$ j" a
    .open  = i2cdev_open,! k  [) m% R/ k
   
) E" H! t' d: r+ N/ i2 x2 u    .release = i2cdev_release,
3 Q/ `8 A2 r1 H5 `    , ]0 r) l9 l$ f1 u/ _/ e
    };
  o3 k6 d/ b/ v8 h3 ~: `$ I+ O    / R( S, G+ i  M' K6 }9 g
    注:通过I2C driver提供的通用方法可以访问任何一个I2C的设备,但是其中实现的read,write及ioctl等功能完全是基于一般设备的实现,所有的操作数据都是基于字节流,没有明确的格式和意义。为了更方便和有效地使用I2C设备,我们可以为一个具体的I2C设备开发特定的I2C设备驱动程序,在驱动中完成对特定的数据格式的解释以及实现一些专用的功能。
  L6 [& m, {2 Z1 n8 M: [    4 m- t* V8 _7 f0 h; W7 I
    在chips目录下包含着各种device 的driver,完成各种从设备的注册。作为一般的I2C设备,使用i2c-dev.c里的操作足够完成操作了。
6 b- I% F5 Q1 ~; A) D    , R; I% c  p3 u  _( ^+ [5 X5 Q$ G5 Z2 x* v
    当然如果不能完成,则需要独立完成该驱动,这就是chips目录下的代码。因为i2c-dev.c已经实现了I2C设备的文件操作接口,所以只要实现struct i2c_driver就可以了。对于某些特殊的操作,可以使用command接口进行控制。 当然,对于i2接口的fm芯片,则将struct i2c_driver放在i2c的chips目录下,而将另外fm操作相关的代码放在了/media/radio目录下了。在这个目录下需要完成读写接口的完成,这个大部分使用V4L2架构。
. W- O# G1 q8 C) m5 \    : P7 U7 y: Q8 m: j. ~
    i2c总线使用
( o3 ~4 q$ l, }5 H) F9 [   
( U3 C8 }: {1 V5 q( n/ p    了解了i2c总线的主要结构成员及适配器、设备驱动的注册后,现在我们从上而下的来研究一下i2c总线的使用(仍然以i2c-dev.c为例):% X  F! o( q5 U) i) F  r$ r9 Y
    # \+ G0 _1 U+ K& p
    1、这是面向用户的虚拟字符设备所提供的所有i2c总线操作接口函数:: V# ?- r, t9 P- A1 F/ b
    0 S# {! d/ I4 t
    static const struct file_operations i2cdev_fops = {  L% c. ~$ t3 O
   
1 ]9 E/ c7 ^& ~' L, y    .owner          = THIS_MODULE,4 C& A3 L; R: c( H- X4 Z- o6 R
   
2 f* g# N8 C! [0 _    .llseek         = no_llseek,( {. w4 Q! O$ M% e: w
      x5 K/ x  R: X3 A& z0 Q! T# r
    .read           = i2cdev_read,$ }& i, M" k7 b# s5 c2 }# s# _
   
6 T( L# D) r) s0 T- q    .write          = i2cdev_write,
$ Y& e6 ~* v/ u- p* ^+ N# g) F6 N    ; B; a) a1 Q+ }7 [$ l
    .ioctl          = i2cdev_ioctl,  ^( }0 V* p/ C+ M+ y
   
) c0 s7 R; a7 Y- q/ N    .open           = i2cdev_open,' ]5 |/ U% G- `$ a
    % z! U$ Y; q( K/ f
    .release        = i2cdev_release,( X/ a9 d8 M; [/ d
   
: t, n, `7 B: J5 c( a" ~9 e; Y    };6 @6 {! k3 e& e- j; a, B; }* ~% G7 T
    3 C& n( b: u' |8 e$ J
    1)drivers/i2c/i2c-dev.c
) M  Z7 I8 S- e* {+ l    % V2 J" E9 n- E
    static int i2cdev_open(struct inode *inode, struct file *file)( |  l2 i7 B0 \1 W% M& r
   
0 J/ C! @0 G6 \* y) {7 Q- w6 j    {
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:07:10 | 显示全部楼层

linux指导:LinuxI2C驱动分析

unsigned int minor = iminor(inode);
, x8 u* @# y# P; f' L: I   
9 L& |3 v6 {% Y4 K    struct i2c_client *client;
5 [$ t' @: i. ~  \5 h8 d! D0 Y+ V    8 f8 C0 p% k6 |! p
    struct i2c_adapter *adap;
% K" p5 c8 s' D  c) ~) s   
7 {, M7 S: u! S    struct i2c_dev *i2c_dev;
1 X$ o3 A% X( j( Z# W+ E8 a2 f* u    ( \' y* m. H+ }# \3 H3 T2 l
    i2c_dev = i2c_dev_get_by_minor(minor);    //通过设备文件的从设备号查找对应的i2c_dev, f, E# G1 p6 J5 q% o6 S( K
    1 O2 S% k( d: h1 |
    if (!i2c_dev)+ h. \& g! r: i
    ; {. y, `1 Z, c- C2 J4 L( f
    return -ENODEV;
" f3 I+ R. j# X4 s$ i6 A   
& u7 E. i$ v5 b6 i. G" M1 \  i    adap = i2c_get_adapter(i2c_dev->adap->nr);    //查找对于的adap# p7 K4 R8 x% C# Z6 ?& o
    ( R% `7 e% j/ m, l. `
    if (!adap)% C8 f6 J# R5 C- [
    : s" V5 h  D  H/ T  \
    return -ENODEV;: Y3 r0 ]- }3 t& ]9 F! Z
   
* A* o1 I4 u% _; N6 a: m- ^% `8 E7 p4 |    client = kzalloc(sizeof(*client), GFP_KERNEL);    //i2c从设备描述结构体
8 S  F5 ], K2 Y0 Y5 M1 z   
7 m) {6 h# U; \" m) q6 @: s    if (!client) {
, ^% Z/ b) a) @* i  Q1 q   
, q" H# H2 H# a/ c7 V" h; x    i2c_put_adapter(adap);) `' x3 O/ p9 ]3 x
    * Q: d& k2 {- P8 F3 J" _0 @, `
    return -ENOMEM;
2 d4 c7 p- X! m1 n, m: j$ U5 B8 M   
5 |: @# ^9 \3 W# t# I2 `    }3 Q+ G( O+ h& X$ H; a1 x
    ' T- n* |2 F+ g" @$ q2 U) |8 _
    snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);: D& _! G' V2 l- Y% q
   
8 J& h" S" d: q$ _% d2 T    client->driver = &i2cdev_driver;6 x  w1 e& z+ X" [8 h
    ( E! C9 X" Z- ?
    /* registered with adapter, passed as client to user */
3 w5 ?; H3 [; f! V   
5 ^8 i- W/ y. \; F! h    client->adapter = adap;
) `2 F! c! R' e8 d" X. ^+ t7 E   
$ x9 r+ Y- \6 \, _    file->private_data = client;% G" i4 y' P) |8 j& ]
   
2 |/ {8 `* s; z! X    return 0;
, c1 Z! o) v3 g0 `' v    ' V; x/ m' m4 Y  q9 L
    }
; M  i; m) N: N6 s/ o" x/ {  I$ F    ' P/ Q3 X/ Z3 o
    注:% T/ I6 F3 n$ n2 B
   
/ B' m' V1 f) ~! {) u1 u" R: V5 s    i2cdev_open的主要作用是构建并初始化用于描述i2c从设备的结构体struct i2c_client.& v& I9 _7 q7 b* V- T; Q/ i
    7 n. L7 Q0 A" G7 H8 L$ p) N8 y4 A
    2)drivers/i2c/i2c-dev.c
4 P# a+ U$ ?) D# G; }- \    . n8 J" {" g" W, b- q
    i2cdev_read( J! s$ z' P' V
    3 @# W& F* M5 x! H7 Y: p; o) N- C
    -> i2c_master_recv
0 f8 \5 b# ]5 Z0 U# g3 U    , `9 N( b! J+ E' i. `$ M& a1 q: i
    -> i2c_transfer8 W0 t- i- T" J* \0 k% f5 I
    4 g9 x; \/ C! x; l+ O5 [2 G
    -> adap->algo->master_xfer(s3c24xx_i2c_xfer)
5 o- H% ~. y5 j* {" V    5 U7 h( X+ c, u, P
    3)drivers/i2c/i2c-dev.c
+ G5 B" U4 h/ s& A" O5 m' M   
! B1 d; X2 L0 `, x( Y    i2cdev_write9 Y# a0 C! w- u* r' W$ c/ p
   
! e, h# c3 C0 g. u% ?9 w4 Q" B    -> i2c_master_send- a8 n) m& f/ W# _, b6 M/ b
   
. D( p4 }! i3 t* E: E    -> i2c_transfer  k3 w5 B! ]/ J8 y
   
1 ]# }3 V0 A; S5 o  @0 @    -> adap->algo->master_xfer(s3c24xx_i2c_xfer)
. [- m0 H+ k3 I, }5 X    $ g2 A* j1 C  e* M1 h; q- A
    7)drivers/i2c/busses/i2c-s3c2410.c
1 f8 s1 ^- |' z! J    ! u) J1 n! b% c
    s3c24xx_i2c_xfer
: V! }8 L3 F* ~3 R2 D7 J   
4 C/ v2 U- c* N* [  N    -> s3c24xx_i2c_doxfer(wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5))  s3c24xx_i2c_irq                                                               |
1 \& e& y- Z) V$ E5 R( b    7 s& e) j# a2 _& x, L3 ?$ \* K+ R
    -> i2s_s3c_irq_nextbyte                                                      |  H" J& U* u4 R! T# s, u, T0 D
   
, B) N% m- v& ?5 M& _; k5 y    -> s3c24xx_i2c_stop                                                      |
0 w$ T- X' y. \) P   
# [+ n' G$ A! q8 B6 m    -> s3c24xx_i2c_master_complete(wake_up(&i2c->wait))------------------|
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-21 15:36 , Processed in 0.518034 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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