现在的位置: 首页 > 综合 > 正文

《Essential Linux Device Drivers》第8章

2012年07月11日 ⁄ 综合 ⁄ 共 5254字 ⁄ 字号 评论关闭
 



8





I2
C


协议


















I2
C


The Inter-Integrated Circuit
)及其
子集
SMBus

System Management Bus
)均为同步串行接口,普遍存在于桌面电脑和嵌入式设备中。
本章通过实现访问
I2
C

EEPROM

I2
C

RTC
的驱动实例,让我们理解内核如何支持
I2
C

/SMBus
主机适配器和客户设备。在结束本章之前,让我们也浏览一下内核支持的两种其它的串行接口:串行外围接口(
SPI
)总线和
1-wire
总线。

所有这些串行接口(
I2
C


SMBus

SPI

1-wire
),都有两个共同的特性:

·        


交换的数据总量较少。

·        


数据传输率较低。





I2
C/SMBus

是什么?

I2
C

是广泛用于桌面和笔记本电脑中的串行总线,用于处理器和一些外设之间的接口,这些外设包括
EEPROM
音频编解码器
以及监控温度、供电电压等参数的专用芯片。此外,

I2
C

也在嵌入式设备中大行其道,用于和
RTC
,智能电池电路、多路复用器,端口扩展卡,光收发器,以及其它类似设备之间的通信。由于
I2
C

被大量的微控制器所支持,在当前的市场上可找到大量便宜的
I2
C

设备。

I2
C


SMBus
为主-从协议,其通信双方为主机适配器(主控制器)和客户设备(从设备)。主机控制器在桌面电脑上通常为南桥芯片组的一部分;而在嵌入式设备上,通常为微控制器的一部分。图
8.1
显示了在
PC
兼容硬件上
I2
C

总线的例子。



8.1. PC
兼容硬件上

I2
C/SMBus.










I2
C

及其子集
SMBus
最初分别为
Philips


Intel
所开发,均为
2
线接口
。这
2
根线为时钟线和双向数据线,分别被称为串行时钟(
Serial Clock

SCL
)和串行数据(
Serial Data

SDA
)。由于
I2
C

总线仅需要一对总线,因此在电路板上占用了更少的空间。因此带来的问题是带宽较窄。
I2
C

在标准模式下支持最高
100Kbps
的传输率,在快速模式下最高可达
400Kbps
(然而,
SMBus
最高仅支持
100Kbps
)。因此它们仅适用于慢速设备。即使
I2
C

支持双向数据交换,因为仅有一根数据线,故通信是半双工的。

I2
C


SMBus
设备使用
7
位地址。协议也支持
10
位地址,但很多设备仅响应
7
位地址,故在总线上最多有
127
个设备。源于协议的主-从特性,设备地址也称为从地址。


I2
C

核心


I2
C

核心由主机适配器驱动和客户驱动可利用的函数和数据结构组成。核心中的公共代码简化了驱动开发者的工作。核心也间接使客户驱动独立于主机适配器,以使客户设备即使用于采用不同
I2
C

主机适配器的电路板上,亦可保持客户驱动运行如常。核心层的此机制及其好处也可在内核中其它的很多设备驱动类中发现,如
PCMCIA

PCI

USB
等。

除了核心,内核的
I2
C

底层由如下组成:

·        


I2
C

主机适配器的设备驱动。属于总线驱动,通常由适配器驱动和算法
algorithm
)驱动组成。前者利用后者和
I2
C

总线交互。

·        


I2C
客户设备的设备驱动。

·        


i2c-dev
,允许在用户模式下实现
I2
C

客户驱动。


你更可能的是实现客户驱动,而不是适配器或
algorithm
驱动,因为相比于
I2
C

主机适配器,有多得多的
I2C
设备。因此在本章中,我们将主要讨论客户驱动。


8.2
展示了
Linux

I2
C

子系统。它显示了
I2
C

内核模块和
I2
C

总线上的主机适配器和客户设备的交互。




8.2. Linux I2
C

子系统


由于
SMBus

I2
C

的子集,因此仅使用
SMBus
指令和你的设备交互的驱动可工作于
SMBus

I2
C

适配器。表
8.1
列出了
I2
C

核心提供的和
SMBus
兼容的数据传输流程。







8.1. I2C
核心提供的和
SMBus
兼容的数据
访问函数

函数


作用


i2c_smbus_read_byte()


从设备读取一个字节(不定义位置偏移,使用以前发起的命令的偏移)

i2c_smbus_write_byte()


从设备写入一个字节(使用以前发起的命令的偏移)

i2c_smbus_write_quick()


向设备发送一个比特
(
取代清单
8.1
中的
Rd/Wr

).

i2c_smbus_read_byte_data()


从设备指定偏移处读取一个字节

i2c_smbus_write_byte_data()


向设备指定偏移处写入一个字节

i2c_smbus_read_word_data()


从设备指定偏移处读取二个字节

i2c_smbus_write_word_data()

向设备指定偏移处写入二个字节

i2c_smbus_read_block_data()

从设备指定偏移处读取一块数据
.

i2c_smbus_write_block_data()

向设备指定偏移处写入一块数据
. (<= 32
字节
)


总线事务

在实现驱动例子之前,通过放大镜观察导线
,让我们来更好的理解

I2
C

协议。清单
8.1
展示了和
I2
C EEPROM

交互的代码片断,以及在总线上发生的相应的事务。这些事务是在运行代码片断时通过相连的
I2
C

总线分析仪捕获的。这些代码使用的时用户模式的
I2
C

函数(在第
19


用户空间的驱动

中,我们将讨论更多的用户模式的
I2
C

编程)。



清单
8.1. I2
C

总线上的事务

Code View:

/* ... */

/*

 
* Connect to the EEPROM. 0x50 is the device address.

 
* smbus_fp is a file pointer into the SMBus device.

 
*/

ioctl(smbus_fp, 0x50, slave);

 

/* Write a byte (0xAB) at memory offset 0 on the EEPROM */

i2c_smbus_write_byte_data(smbus_fp, 0, 0xAB);

 

/*

 
* This is the corresponding transaction observed

 
* on the bus after the write:

 
* S 0x50 Wr [A] 0 [A] 0xAB [A] P

 
*

 
* S is the start bit, 0x50 is the 7-bit slave address (0101000b),

 
* Wr is the write command (0b), A is the Accept bit (or

 
* acknowledgment) received by the host from the slave, 0 is the

 
* address offset on the slave device where the byte is to be

 
* written, 0xAB is the data to be written, and P is the stop bit.

 
* The data enclosed within [] is sent from the slave to the

 
* host, while the rest of the bits are sent by the host to the

 
* slave.

 
*/

/* Read a byte from offset 0 on the EEPROM */

res = i2c_smbus_read_byte_data(smbus_fp, 0);

 

/*

 
* This is the corresponding transaction observed

 
* on the bus after the read:

 
* S 0x50

Wr [A] 0 [A] S 0x50 Rd [A] [0xAB] NA P

 
*

 
* The explanation of the bits is the same as before, except that

 
* Rd stands for the Read command (1b), 0xAB is the data received

 
* from the slave, and NA is the Reverse Accept bit (or the

 
* acknowledgment sent by the host to the slave).

 
*/

 

                                  
 

 


设备例子:
EEPROM

我们的第一个客户驱动例子时
I2
C

总线上的
EEPROM
设置,如图
8.1
所示。几乎所有的笔记本和桌面电脑都有类似的
EEPROM
,用于存储
BIOS
配置信息。例子中的
EEPROM
有两个
memory bank
。相对应于每个
memory bank
,驱动提供
/dev
接口:
/dev/eep/0


/dev/eep/1
。应用程序在这些节点上操作,和
EEPROM
交换数据。

每个
I2
C/SMBus

客户设备都分配有一个从地址,作为设备标识。例子中的
EEPROM
有两个从地址,
SLAVE_ADDR1

SLAVE_ADDR2
,分别对应于两个
memory bank

例子中驱动所使用的
I2
C

指令和
SMBus
兼容,因此它可以工作于
I2
C


SMBus EEPROM












初始化


正如所有的驱动类那样,
I2
C

客户驱动也有
init()
入口点。初始化用于分配数据结构,向
I2
C

核心注册驱动,将
sysfs

Linux
设备模型联系在一起。这些在清单
8.2
中完成。


清单
8.2.
初始化
EEPROM
驱动

Code View:

/* Driver entry points */

static struct file_operations eep_fops = {

 
.owner  
= THIS_MODULE,

 
.llseek 
= eep_llseek,

 
 
.read   
= eep_read,

 
.ioctl  
= eep_ioctl,

 
.open   
= eep_open,

 
.release = eep_release,

 
.write  
= eep_write,

};

 

static dev_t dev_number;         
/* Allotted Device Number */

static struct class *eep_class;  
/* Device class */

 

/* Per-device client data structure for each

 
* memory bank supported by the driver

 
*/

 

struct eep_bank {

 
struct i2c_client *client;     
/* I2C client for this bank */

 
unsigned int addr;             
/* Slave address of this bank */

 
unsigned short current_pointer; /* File pointer */

 
int bank_number;               
/* Actual memory bank number

*/

 
/* ... */                      
/* Spinlocks, data cache for

                                    
slow devices,.. */

};

 

#define NUM_BANKS 2              
/* Two supported banks */

#define BANK_SIZE 2048           
/* Size of each bank */

 

struct ee_bank *ee_bank_list;    
/* List of private data

                                    
structures, one per bank */

 

 

/*

 
* Device Initialization

 
*/

int __init

eep_init(void)

{

 

 
int err, i;

 

 
/* Allocate the per-device data structure, ee_bank */

 
ee_bank_list = kmalloc(sizeof(struct ee_bank)*NUM_BANKS,

                        
GFP_KERNEL);

 
memset(ee_bank_list, 0, sizeof(struct ee_bank)*NUM_BANKS);

 
/* Register and create the /dev interfaces to access the EEPROM

    
banks. Refer back to Chapter 5, "Character Drivers" for

    
more details */

 
if (alloc_chrdev_region(&dev_number, 0,

                         
NUM_BANKS, "eep") < 0) {

   
printk(KERN_DEBUG "Can't register device/n");

   
return -1;

 
}

 

 
eep_class = class_create(THIS_MODULE, DEVICE_NAME);

 
for (i=0; i < NUM_BANKS;i++) {

 

   
/* Connect the file operations with cdev */

   
cdev_init(&ee_bank[i].cdev, &ee_fops);

 

抱歉!评论已关闭.