sscanf函数 linux 物理cpu信息,《Linux4.0设备驱动开发详解》笔记--第十五章:Linux I2C核心、总线与设备驱动...

15.1 Linux I2C体系结构

I2C核心

I2C核心提供了I2C总线驱动和设备驱动的注册、注销的方法,I2C通信(Algorithm)方法上层的与具体适配器无关代码以及探测设备、检测设备地址的上层代码等 I2C总线驱动

是对I2C体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部 总线驱动包含I2C适配器数据结构i2c_adapter、I2C适配器的Algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数 I2C设备驱动

它是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU通信 I2C驱动主要包含数据结构i2c_driver和i2c_client,需要根据的设备实现其中的成员函数 所有的I2C设备都在sysfs文件系统中显示,存在/sys/bus/i2c目录下,以适配器地址和芯片地址的形式列出 /drivers/i2c/下的文件介绍

i2c-core.c:实现了I2C核心的功能以及/proc/bus/i2c*接口 i2c-dev.c:实现I2C适配器设备文件的功能,每一个适配器被分配一个设备

设配器的主设备号位89,次设备号位:0-255 i2c_dev.c并不是根据具体的设备而设计的,只是提供了同用的read()、write()和ioctl()等接口,应用层可以通过这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式 busses文件夹:包含一些I2C主机控制器驱动,如i2c_omap.c、i2c_s3c2440c.等 algos文件夹:实现了一些I2C总线适配器的通信方法 i2c_adapter、i2c_algorithm、i2c_driver和i2c_client数据结构的作用及其之间的关系

i2c_adapter与i2c_algorithm

i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应于一套通信方法 一个i2c_adapter需要i2c_algorithm提供的通信函数来控制适配器产生特定的访问周期 i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位 i2c_driver与i2c_client

i2c_driver对应于一套驱动方法,struct i2c_device_id形式的id_table是该驱动所支持的I2C设备的ID表 i2c_client对应于真实的物理设备,每个I2C设备都需一个i2c_client来描述 一个i2c_driver支持多个同类型的i2c_client i2c_client的信息通常在BSP的板文件中通过i2c_board_info填充,包括设备的ID号、地址、中断号等信息 在I2C总线驱动i2c_bus_type的match()函数i2c_device_match()中,会调用i2c_match_id()函数匹配在板文件中定义的ID和i2c_driver所支持的ID表 i2c_adapter与i2c_client

其关系与I2C设备体系中适配器与设备的关系一致,即i2c_client依附于i2c_driver 一个i2c_driver可以被多个i2c_client依附,i2c_driver中包含有依附它的i2c_client的链表

15.2 I2C核心

增加、删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adap);

int i2c_del_adapter(struct i2c_adapter *adap);

增加、删除i2c_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver);

int i2c_del_driver(struct i2c_driver *driver);

inline int i2c_add_driver(struct i2c_driver *driver);

i2c_client依附/脱离

int i2c_attach_client(struct i2c_client *client);

int i2c_detach_client(struct i2c_client *client);

当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。相反地,在client被取消关联的时候,sysfs文件和设备也被注销

代码清单15.6 I2C核心client attach/detach函数

1 int i2c_attach_client(struct i2c_client *client)

2 {

3 ...

4 device_register(&client->dev);

5 device_create_file(&client->dev, &dev_attr_client_name);

6

7 return 0;

8 }

9

10 int i2c_detach_client(struct i2c_client *client)

11 {

12 ...

13 device_remove_file(&client->dev, &dev_attr_client_name);

14 device_unregister(&client->dev);

15 ...

16 }

I2C传输、发送和接收

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);

int i2c_master_send(struct i2c_client *client,const char *buf ,int count);

int i2c_master_recv(struct i2c_client *client, char *buf ,int count);

i2c_transfer ()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会 调用i2c_transfer()函数分别完成一条写消息和一条读消

例如:

代码清单15.7 I2C核心i2c_master_send函数

1 int i2c_master_send(struct i2c_client *client,const char *buf ,int count)

2 {

3 int ret;

4 struct i2c_adapter *adap=client->adapter;

5 struct i2c_msg msg;

6 /*构造一个写消息*/

7 msg.addr = client->addr;

8 msg.flags = client->flags & I2C_M_TEN;

9 msg.len = count;

10 msg.buf = (char *)buf;

11 /*传输消息*/

12 ret = i2c_transfer(adap, &msg, 1);

13

14 return (ret == 1) ? count : ret;

15 }

代码清单15.8 I2C核心i 2c_master_recv函数

1 int i2c_master_recv(struct i2c_client *client, char *buf ,int count)

2 {

3 struct i2c_adapter *adap=client->adapter;

4 struct i2c_msg msg;

5 int ret;

6 /*构造一个读消息*/

7 msg.addr = client->addr;

8 msg.flags = client->flags & I2C_M_TEN;

9 msg.flags |= I2C_M_RD;

10 msg.len = count;

11 msg.buf = buf;

12 /*传输消息*/

13 ret = i2c_transfer(adap, &msg, 1);

14

15 /* 成功(1条消息被处理), 返回读的字节数 */

16 return (ret == 1) ? count : ret;

17 }

i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程,如下例程:

I2C核心i 2c_transfer函数

1 int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)

2 {

3 int ret;

4

5 if (adap->algo->master_xfer) {

6 down(&adap->bus_lock);

7 ret = adap->algo->master_xfer(adap,msgs,num); /* 消息传输 */

8 up(&adap->bus_lock);

9 return ret;

10 } else {

11 dev_dbg(&adap->dev, "I2C level transfers not supportedn");

12 return -ENOSYS;

13 }

14 }

15.3 Linux I2C总线驱动

15.3.1 I2C适配器驱动加载与卸载

I2C总线驱动模块的加载函数要完成两个工作:

初始化I2C适配器所使用的硬件资源,申请I/O地址、中断号等。 通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。 I2C总线驱动模块的卸载函数要完成的工作与加载函数的相反:

释放I2C适配器所使用的硬件资源,释放I/O地址、中断号等。 通过i2c_del_adapter()删除i2c_adapter的数据结构。 代码清单给出了I2C适配器驱动模块加载和卸载函数的模板。

I2C总线驱动模块加载和卸载函数模板

1 static int __init i2c_adapter_xxx_init(void)

2 {

3 xxx_adpater_hw_init();

4 i2c_add_adapter(&xxx_adapter);

5 }

6

7 static void __exit i2c_adapter_xxx_exit(void)

8 {

9 xxx_adpater_hw_free();

10 i2c_del_adapter(&xxx_adapter);

11 }

- 上述代码中xxx_adpater_hw_init()和xxx_adpater_hw_free()函数的实现都与具体的CPU和I2C设备硬件直接相关。

15.3.2 I2C总线通信方法

我们需要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数和functionality()函数。

functionality()函数用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。 master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息 代码给出了xxx设备的master_xfer()函数模板。

I2C总线驱动master_xfer函数模板

1 static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,

2 int num)

3 {

4 ...

5 for (i = 0; i < num; i++)

6 {

7 i2c_adapter_xxx_start(); /*产生开始位*/

8 /*是读消息*/

9 if (msgs[i]->flags &I2C_M_RD)

10 {

11 i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); /*发送从设备读地址*/

12 i2c_adapter_xxx_wait_ack(); /*获得从设备的ack*/

13 i2c_adapter_xxx_readbytes(msgs[i]->buf, msgs[i]->len); /*读取msgs[i]

14 ->len长的数据到msgs[i]->buf*/

15 }

16 else

17 /*是写消息*/

18 {

19 i2c_adapter_xxx_setaddr(msg->addr << 1); /*发送从设备写地址*/

20 i2c_adapter_xxx_wait_ack(); /*获得从设备的ack*/

21 i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len); /*读取msgs[i]

22 ->len长的数据到msgs[i]->buf*/

23 }

24 }

25 i2c_adapter_xxx_stop(); /*产生停止位*/

26 }

- 上述代码实际上给出了一个master_xfer()函数处理I2C消息数组的流程,对于数组中的每个消息,判断消息类型,若为读消息,则赋从设备地址为 (msg->addr << 1) | 1,否则为msg->addr << 1。对每个消息产生1个开始位,紧接着传送从设备地址,然后开始数据的发送或接收,对最后的消息还需产生1个停止位。图15.3描述了整个master_xfer()完成的时序。

多数I2C总线驱动会定义一个xxx_i2c结构体,作为i2c_adapter的algo_data(类似“私有数据”)

xxx_i2c结构体包含I2C消息数组指针、数组索引及I2C适配器algorithm访问控制用的自旋锁、等待队列等 master_xfer()函数完成消息数组中消息的处理也可通过对xxx_i2c结构体相关成员的访问来控制。

1 struct xxx_i2c

2 {

3 spinlock_t lock;

4 wait_queue_head_t wait;

5 struct i2c_msg *msg;

6 unsigned int msg_num;

7 unsigned int msg_idx;

8 unsigned int msg_ptr;

9 ...

10 struct i2c_adapter adap;

11 };

15.4 linux I2C设备驱动

15.4.1 Linux I2C设备驱动模块加载与卸载

I2C设备驱动模块加载函数通用的方法是在I2C设备驱动模块加载函数中完成两件事:

通过register_chrdev()函数将I2C设备注册为一个字符设备。 通过I2C核心的i2c_add_driver()函数添加i2c_driver。 在模块卸载函数中需要做相反的两件事:

通过I2C核心的i2c_del_driver()函数删除i2c_driver。 通过unregister_chrdev()函数注销字符设备。

1 static int __init yyy_init(void)

2 {

3 int res;

4 /*注册字符设备*/

5 res = register_chrdev(YYY_MAJOR, "yyy", &yyy_fops); //老内核接口

6 if (res)

7 goto out;

8 /*添加i2c_driver*/

9 res = i2c_add_driver(&yyy_driver);

10 if (res)

11 goto out_unreg_class;

12 return 0;

13

14 out_unreg_chrdev: unregister_chrdev(I2C_MAJOR, "i2c");

15 out: printk(KERN_ERR "%s: Driver Initialisation failedn", __FILE__);

16 return res;

17 }

18

19 static void __exit yyy_exit(void)

20 {

21 i2c_del_driver(&i2cdev_driver);

22 unregister_chrdev(YYY_MAJOR, "yyy");

23 }

15.4.2 Linux I2C设备驱动的数据传输

I2C设备上的读写数据的时序且数据通常通过i2c_msg消息数组进行组织,最后通过i2c_transfer函数完成

1 static int yyy_cmd1(struct i2c_client *client, struct rtc_time *dt)

2 {

3 struct i2c_msg msg[2];

4 /*第一条消息是写消息*/

5 msg[0].addr = client->addr;

6 msg[0].flags = 0;

7 msg[0].len = 1;

8 msg[0].buf = &offs;

9 /*第二条消息是读消息*/

10 msg[1].addr = client->addr;

11 msg[1].flags = I2C_M_RD;

12 msg[1].len = sizeof(buf);

13 msg[1].buf = &buf[0];

14

15 i2c_transfer(client->adapter, msg, 2);

16 ...

17 }

vc文件分析">15.4.3 Linux i2c-dev.c文件分析

i2c-dev.c文件可以被看作一个I2C设 备驱动,它实现的一个i2c_client是虚拟的、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter的clients链表中。

i2c-dev.c针对每个I2C适配器生成一个主设备为89的设备文件,实现了i2c_driver的成员函数以及文件操作接口

i2c-dev.c的主体是“i2c_driver成员函数 + 字符设备驱动”。

i2c-dev.c中提供i2cdev_read()、i2cdev_write()函数来对应用户空间要使用的read()和 write()文件操作接口,这两个函数分别调用I2C核心的i2c_master_recv()和i2c_master_send()函数来构造1条 I2C消息并引发适配器algorithm通信函数的调用,完成消息的传输

i2c-dev.c中i2cdev_read()和i2cdev_write()函数不具备太强的通用性,没有太大的实用价值,只能适用于非RepStart模式的情况。

对于2条以上消息组成的读写,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWR IOCTL命令。

代码给出了i2cdev_ioctl()函数的框架,其中详细列出了I2C_RDWR命令的处理过程。

i2c-dev.c中的i2cdev_ioctl函数

1 static int i2cdev_ioctl(struct inode *inode, struct file *file,

2 unsigned int cmd, unsigned long arg)

3 {

4 struct i2c_client *client = (struct i2c_client *)file->private_data;

5 ...

6 switch ( cmd ) {

7 case I2C_SLAVE:

8 case I2C_SLAVE_FORCE:

9 ... /*设置从设备地址*/

10 case I2C_TENBIT:

11 ...

12 case I2C_PEC:

13 ...

14 case I2C_FUNCS:

15 ...

16 case I2C_RDWR:

17 if (copy_from_user(&rdwr_arg,

18 (struct i2c_rdwr_ioctl_data __user *)arg,

19 sizeof(rdwr_arg)))

20 return -EFAULT;

21 /* 一次传入的消息太多 */

22 if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)

23 return -EINVAL;

24 /*获得用户空间传入的消息数组

25 rdwr_pa = (struct i2c_msg *)

26 kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),

27 GFP_KERNEL);

28 if (rdwr_pa == NULL) return -ENOMEM;

29 if (copy_from_user(rdwr_pa, rdwr_arg.msgs,

30 rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {

31 kfree(rdwr_pa);

32 return -EFAULT;

33 }

34 data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);

35 if (data_ptrs == NULL) {

36 kfree(rdwr_pa);

37 return -ENOMEM;

38 }

39 res = 0;

40 for( i=0; i41 /* 限制消息的长度 */

42 if (rdwr_pa[i].len > 8192) {

43 res = -EINVAL;

44 break;

45 }

46 data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;

47 rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);

48 if(rdwr_pa[i].buf == NULL) {

49 res = -ENOMEM;

50 break;

51 }

52 if(copy_from_user(rdwr_pa[i].buf,

53 data_ptrs[i],

54 rdwr_pa[i].len)) {

55 ++i; /* Needs to be kfreed too */

56 res = -EFAULT;

57 break;

58 }

59 }

60 if (res < 0) {

61 int j;

62 for (j = 0; j < i; ++j)

63 kfree(rdwr_pa[j].buf);

64 kfree(data_ptrs);

65 kfree(rdwr_pa);

66 return res;

67 }

68 /*把这些消息交给通信方法去处理*/

69 res = i2c_transfer(client->adapter,

70 rdwr_pa,

71 rdwr_arg.nmsgs);

72 while(i-- > 0) { /*如果是读消息,把值拷贝到用户空间*/

73 if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {

74 if(copy_to_user(

75 data_ptrs[i],

76 rdwr_pa[i].buf,

77 rdwr_pa[i].len)) {

78 res = -EFAULT;

79 }

80 }

81 kfree(rdwr_pa[i].buf);

82 }

83 kfree(data_ptrs);

84 kfree(rdwr_pa);

85 return res;

86 case I2C_SMBUS:

87 ...

88 default:

89 return i2c_control(client,cmd,arg);

90 }

91 return 0;

92 }

- 常用的IOCTL包括I2C_SLAVE(设置从设备地址)、I2C_RETRIES(没有收到设备ACK情况下的重试次数,缺省为1)、I2C_TIMEOU(超时)以及I2C_RDWR。

下面两个代码分别演示了直接通过read()、write()接口和O_RDWR IOCTL读写I2C设备的例子。

代码清单15.23 直接通过read()/write()读写I2C设备

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8

9 #define I2C_RETRIES 0x0701

10 #define I2C_TIMEOUT 0x0702

11 #define I2C_SLAVE 0x0703

12

13 int main(int argc, char **argv)

14 {

15 unsigned int fd;

16 unsigned short mem_addr;

17 unsigned short size;

18 unsigned short idx;

19 #define BUFF_SIZE 32

20 char buf[BUFF_SIZE];

21 char cswap;

22 union

23 {

24 unsigned short addr;

25 char bytes[2];

26 } tmp;

27

28 if (argc < 3)

29 {

30 printf("Use:n%s /dev/i2c-x mem_addr sizen", argv[0]);

31 return 0;

32 }

33 sscanf(argv[2], "%d", &mem_addr);

34 sscanf(argv[3], "%d", &size);

35

36 if (size > BUFF_SIZE)

37 size = BUFF_SIZE;

38

39 fd = open(argv[1], O_RDWR);

40

41 if (!fd)

42 {

43 printf("Error on opening the device filen");

44 return 0;

45 }

46

47 ioctl(fd, I2C_SLAVE, 0x50); /* 设置eeprom地址 */

48 ioctl(fd, I2C_TIMEOUT, 1); /* 设置超时 */

49 ioctl(fd, I2C_RETRIES, 1); /* 设置重试次数 */

50

51 for (idx = 0; idx < size; ++idx, ++mem_addr)

52 {

53 tmp.addr = mem_addr;

54 cswap = tmp.bytes[0];

55 tmp.bytes[0] = tmp.bytes[1];

56 tmp.bytes[1] = cswap;

57 write(fd, &tmp.addr, 2);

58 read(fd, &buf[idx], 1);

59 }

60 buf[size] = 0;

61 close(fd);

62 printf("Read %d char: %sn", size, buf);

63 return 0;

64 }

代码清单15.24 通过O_RDWR IOCTL读写I2C设备

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8 #include

9 #include

10 #include

11

12 #define MAX_I2C_MSG 2

13

14 #define I2C_RETRIES 0x0701

15 #define I2C_TIMEOUT 0x0702

16 #define I2C_RDWR 0x0707

17

18 struct i2c_msg

19 {

20 __u16 addr; /* 从地址 */

21 __u16 flags;

22 #define I2C_M_RD 0x01

23 __u8 *buf; /* 消息数据指针 */

24 };

25 struct i2c_rdwr_ioctl_data

26 {

27 struct i2c_msg *msgs; /* i2c_msg[]指针 */

28 int nmsgs; /* i2c_msg数量 */

29 };

30

31 int main(int argc, char **argv)

32 {

33 struct i2c_rdwr_ioctl_data work_queue;

34 unsigned int idx;

35 unsigned int fd;

36 unsigned short start_address;

37 int ret;

38

39 if (argc < 4)

40 {

41 printf("Usage:n%s /dev/i2c-x start_addrn", argv[0]);

42 return 0;

43 }

44

45 fd = open(argv[1], O_RDWR);

46

47 if (!fd)

48 {

49 printf("Error on opening the device filen");

50 return 0;

51 }

52 sscanf(argv[2], "%x", &start_address);

53 work_queue.nmsgs = MAX_I2C_MSG; /* 消息数量 */

54

55 work_queue.msgs = (struct i2c_msg*)malloc(work_queue.nmsgs *sizeof(struct

56 i2c_msg));

57 if (!work_queue.msgs)

58 {

59 printf("Memory alloc errorn");

60 close(fd);

61 return 0;

62 }

63

64 for (idx = 0; idx < work_queue.nmsgs; ++idx)

65 {

66 (work_queue.msgs[idx]).len = 0;

67 (work_queue.msgs[idx]).addr = start_address + idx;

68 (work_queue.msgs[idx]).buf = NULL;

69 }

70

71 ioctl(fd, I2C_TIMEOUT, 2); /* 设置超时 */

72 ioctl(fd, I2C_RETRIES, 1); /* 设置重试次数 */

73

74 ret = ioctl(fd, I2C_RDWR, (unsigned long) &work_queue);

75

76 if (ret < 0)

77 {

78 printf("Error during I2C_RDWR ioctl with error code: %dn", ret);

79 }

80

81 close(fd);

82 return ;

83 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值