Linux内核 I2C子系统代码跟读

听了这个课程感觉讲的很好,很有必要自己跟读一遍Kernel中I2C的代码,以加深对Kernel中I2C子系统的理解。

相关文件

mach-smdkv210.c : 开机注册方法

at24_drv.c : 设备驱动层

i2c-core.c : i2c总线核心层

i2c-s3c2410.c : i2c_adapter驱动层(i2c控制器驱动层)

设备注册

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// mach-smdkv210.c
static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
    { I2C_BOARD_INFO("24c08", 0x50), },     
    { I2C_BOARD_INFO("wm8580", 0x1b), },
};
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) //求数组中元素个数
#define I2C_BOARD_INFO(dev_type, dev_addr) .type = dev_type, .addr = (dev_addr) 

smdkv210_machine_init 
	|
	i2c_register_board_info(0, smdkv210_i2c_devs0,ARRAY_SIZE(smdkv210_i2c_devs0));
		|
		int i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
		{
			for (status = 0; len; len--, info++) {
				struct i2c_devinfo	*devinfo;
                /*
                	struct i2c_devinfo {
                        struct list_head	list;
                        int			busnum;
                        struct i2c_board_info	board_info;
                    };
                */
				devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
				devinfo->busnum = busnum;
				devinfo->board_info = *info;
				list_add_tail(&devinfo->list, &__i2c_board_list);
			}
		}
		/*
			总结: i2c_register_board_info()函数构造了一个链表__i2c_board_list,并将i2c的board_info加入此链表,并加入了控制器编号。
		*/
	i2c_register_board_info(1, smdkv210_i2c_devs1,ARRAY_SIZE(smdkv210_i2c_devs1));
	i2c_register_board_info(2, smdkv210_i2c_devs2,ARRAY_SIZE(smdkv210_i2c_devs2));
	platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
        /*
            static struct platform_device *smdkv210_devices[] __initdata = {
                &s3c_device_i2c0,
                &s3c_device_i2c1,
                &s3c_device_i2c2,
            };
            struct platform_device s3c_device_i2c0 = {
				.name		  = "s3c2410-i2c",
				.id		  = 0,
				.num_resources	  = ARRAY_SIZE(s3c_i2c_resource),
				.resource	  = s3c_i2c_resource,
			};
			static struct resource s3c_i2c_resource[] = {
            [0] = {
                .start	= S3C_PA_IIC5,	// #define S3C_PA_IIC5			EXYNOS4_PA_IIC(5)
                .end	= S3C_PA_IIC5 + SZ_4K - 1,
                .flags	= IORESOURCE_MEM,
            },
            [1] = {
                .start	= IRQ_IIC5,
                .end	= IRQ_IIC5,
                .flags	= IORESOURCE_IRQ,
            },
        };
        */
		|
        int platform_add_devices(struct platform_device **devs, int num)
            for (i = 0; i < num; i++) 
                ret = platform_device_register(devs[i]);
            			|
                        platform_device_add() //此函数定义在drivers/base/platform.c中
                            |
                            pdev->dev.bus = &platform_bus_type;
								|
                                struct bus_type platform_bus_type = {
                                    .name		= "platform",
                                    .match		= platform_match,
                                };
                            device_add(&pdev->dev); //此函数定义在drivers/base/core.c

        /*
        	总结:
        		此函数向platform平台中批量注册platform_device, i2c控制器也抽象为platform_device,并包含寄存器地址信息。
        */

向platform中注册设备属于另外一篇文章,此处不再深究。

i2c总线核心驱动层

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//i2c-core.c (drivers/i2c/i2c-core.c)
postcore_initcall(i2c_init);
module_exit(i2c_exit);


static int __init i2c_init(void)
    |
	bus_register(&i2c_bus_type); // 构建了i2c总线,并定义了匹配的方法
		/*
			struct bus_type i2c_bus_type = {
                .name		= "i2c",
                .match		= i2c_device_match,
                .probe		= i2c_device_probe,
                .remove		= i2c_device_remove,
            };
		*/
		|
		i2c_add_driver(&dummy_driver); //只增加了一个设备


static void __exit i2c_exit(void)
{
	i2c_del_driver(&dummy_driver);
	bus_unregister(&i2c_bus_type);
}

i2c adapter层

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// i2c-s3c2410.c (drivers/i2c/busses/i2c-s3c2410.c)

i2c_adap_s3c_init()
    |
    platform_driver_register(&s3c24xx_i2c_driver) // 向platform总线注册了platform_driver
            static struct platform_driver s3c24xx_i2c_driver = {
                .probe		= s3c24xx_i2c_probe,
                .id_table	= s3c24xx_driver_ids,
                .driver		= {
                    .name	= "s3c-i2c",
                },
            };
					/*
						其中
							static struct platform_device_id s3c24xx_driver_ids[] = {
                            {
                                .name		= "s3c2410-i2c",
                                .driver_data	= TYPE_S3C2410,
                            }, {
                                .name		= "s3c2440-i2c",
                                .driver_data	= TYPE_S3C2440,
                            }, { },
                        };
					*/
	// platform_driver注册进platform总线后会调用platform的match函数,匹配成功后会调用platform_driver的probe函数,匹配的过程这里暂时不分析,先看probe函数的内容。

s3c24xx_i2c_probe(struct platform_device *pdev)
    |
    struct s3c24xx_i2c *i2c;
	struct s3c2410_platform_i2c *pdata;
	struct resource *res;
	pdata = pdev->dev.platform_data; // 获取到平台自定义数据

	i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); // 分配一个adapter
			/*
                struct s3c24xx_i2c {
                    struct i2c_msg		*msg;
                    unsigned int		irq;
                    void __iomem		*regs;
                    struct device		*dev;
                    struct resource		*ioarea;
                    struct i2c_adapter	adap;
                };
                
                struct i2c_adapter {
                    struct module *owner; 
                    const struct i2c_algorithm *algo; 
                    void *algo_data;
                    struct device dev;		
                    int nr;
                    char name[48];
                }
			*/
	/* 初始化adapter */
	strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
	i2c->adap.owner   = THIS_MODULE;
	i2c->adap.algo    = &s3c24xx_i2c_algorithm;
	i2c->dev = &pdev->dev;
	
	/* 获得内存资源 */
	platform_get_resource(pdev, IORESOURCE_MEM, 0);
	i2c->regs = ioremap(res->start, resource_size(res));

	i2c->adap.algo_data = i2c;
	i2c->adap.dev.parent = &pdev->dev;
	
	s3c24xx_i2c_init(i2c);  // 硬件初始化

	/* 获得中断资源 */
	i2c->irq = platform_get_irq(pdev, 0);
	request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);

	i2c->adap.nr = pdata->bus_num;
	i2c_add_numbered_adapter(&i2c->adap);  // i2c-core.c
		static int i2c_register_adapter(struct i2c_adapter *adap)
		|
        i2c_register_adapter(adap);
			|
	        dev_set_name(&adap->dev, "i2c-%d", adap->nr); // 设备名字 i2c-控制器序号
    	    adap->dev.bus = &i2c_bus_type;
        	adap->dev.type = &i2c_adapter_type;
	        res = device_register(&adap->dev);
			i2c_scan_static_board_info(adap);
				static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
                    struct i2c_devinfo	*devinfo;
				|
                list_for_each_entry(devinfo, &__i2c_board_list, list) {
                    if (devinfo->busnum == adapter->nr
                        && !i2c_new_device(adapter, &devinfo->board_info))
                }
						|
                        struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
                            	|
                            	struct i2c_client	*client;
										/*
											struct i2c_client {
                                                unsigned short flags;		//传输方向
                                                unsigned short addr;		//设备地址
                                                char name[I2C_NAME_SIZE];
                                                struct i2c_adapter *adapter;	//与之对应的适配器
                                                struct i2c_driver *driver;	//父类
                                                struct device dev;		//父类
                                        	};
                                        
                                            struct i2c_driver {
                                                //Standard driver model interfaces 
                                                int (*probe)(struct i2c_client *, const struct i2c_device_id *);
                                                int (*remove)(struct i2c_client *);
                                                struct device_driver driver;
                                                const struct i2c_device_id *id_table;
                                            };
										*/
								client = kzalloc(sizeof *client, GFP_KERNEL);
                                client->adapter = adap;
                                client->dev.platform_data = info->platform_data;
                                client->flags = info->flags;
                                client->addr = info->addr;
                                client->irq = info->irq;
								strlcpy(client->name, info->type, sizeof(client->name));
                                client->dev.parent = &client->adapter->dev;
                                client->dev.bus = &i2c_bus_type;
                                client->dev.type = &i2c_client_type;
                                dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), client->addr); // 0-0x50 0号控制器上的地址为0x50的从设备
                                device_register(&client->dev);
				/*
					总结:
						adapter在注册的时候,会遍历__i2c_board_list链表
						如果adapter的编号和链表中节点的号码一致,就会构建i2c client并注册client
						i2c client中成员的值来自于board_info
				*/

	platform_set_drvdata(pdev, i2c);
         

设备驱动层

自己需要写的驱动代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

const struct file_operations at24_fops = {
    .read = at24_e2prom_read,
    .write = at24_e2prom_write,
    .open = at24_e2prom_open,
    .release = at24_e2prom_close,
}; 

int at24_drv_probe(struct i2c_client * client, const struct i2c_device_id  * id)
{
    /*
         	申请设备号
         	创建设备文件
         	硬件初始化
         	实现fops
         */
    at24_dev = kzalloc(sizeof(struct i2c_e2prom), GFP_KERNEL);
    at24_dev->major = register_chrdev(0, "at24_drv", &at24_fops);

    at24_dev->cls = class_create(THIS_MODULE, "at24_cls");
    at24_dev->dev = device_create(at24_dev->cls, NULL, \
                                  MKDEV(at24_dev->major, 0), NULL, "at24_e2prom" );
    /* 硬件初始化 e2prom上电就可以工作了,不需要初始化,其他设备初始化就是指定寄存器地址,写入指定值。
            i2c系统中为从设备传输数据的方法
         	 int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
         	 int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
         	 这两个函数都调用
         	 int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
         */
    // 记录当前的client, 总线中匹配成功后发送过来的
    at24_dev->client = client;

    return 0;
}

const struct i2c_device_id at24_id_table[] = {
    {"at24c02", 0x2222},
    {"at24c04", 0x3333},
    {"at24c08", 0x4444},
};

struct i2c_driver at24_drv = {
    .probe = at24_drv_probe,
    .remove = at24_drv_remove,
    .driver = {
        .name = "at24_e2prom_drv", //不会用于比对, /sys/bus/i2c/drivers/at24_e2prom_drv
    },
    .id_table = at24_id_table,
};

static int __init at24_drv_init(void)
{
    // 注册一个i2c driver
    return i2c_add_driver(&at24_drv);
}

static void __exit at24_drv_exit(void)
{
    i2c_del_driver(&at24_drv);
}

module_init(at24_drv_init);
module_exit(at24_drv_exit);

总结

  1. mach-smdkv210.c中的smdkv210_machine_init() 会在内核初始化时调用

  2. smdkv210_machine_init()中

     a. 调用i2c_register_board_info()函数,初始化了一个保存有从设备名字和地址的链表__i2c_board_list;  
     b. 调用platform_add_devices()函数,将SOC的I2C控制器抽象为的 包含寄存器以及中断等硬件资源的platform_device 注册进了platform总线;  
    
  3. i2c-core.c中调用i2c_init()函数,注册了I2C总线的实现(包括总线上设备和驱动的匹配方法match()等)(即bus_register(&i2c_bus_type))

  4. i2c-s3c2410.c 中i2c_adap_s3c_init()函数调用platform_driver_register(&s3c24xx_i2c_driver)函数向platform总线注册了一个platform_driver对象;

  5. 4中注册platform_driver的操作会调用platform的match()方法,此方法按照所注册的platform_driver(即s3c24xx_i2c_driver)的id_table字段和platform_device(比如mach-smdkv210.c中的s3c_device_i2c0)的name字段进行匹配,如果匹配就调用相应platform_driver的probe()方法。

  6. i2c-s3c2410.c 中s3c24xx_i2c_probe()方法

     a. 分配并初始化了对象struct s3c24xx_i2c, 此对象包含一个 struct i2c_adapter对象,以及I2C控制器需要的硬件资源;  
     b. 然后调用i2c-core.c中的i2c_register_adapter()函数,主要根据__i2c_board_list中的从设备信息创建client并和adapter相关联。  
    

驱动编写

​ at24_drv.c中是自己写的驱动代码,按照一般内核模块写法,自己构造i2c_driver对象,该对象包含probe()方法,该方法在总结3 i2c_init()函数中定义的i2c_bus_type对象的match()方法中匹配i2c_device和i2c_driver成功时被调用。

​ probe()方法完成设备号注册,fops对象定义,以及硬件初始化操作。

关于数据流

client->adapter->i2c_algorithm->master_xfer(client->adapter, msgs, num)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
struct i2c_algorithm {
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
};

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

I2C读写驱动例子

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
int at24_drv_probe(struct i2c_client * client, const struct i2c_device_id  * id)
{
    /*将i2c_bus的match()方法匹配成功时,会将相应的client传递过来,此处保存到全局的设备对象中*/
    at24_dev->client = client; 
}

//  全局的设备对象
struct i2c_e2prom{
	int dev_major;
	struct class *cls;
	struct device *dev;
	struct i2c_client *client;//记录当前匹配的client
};

struct i2c_e2prom *at24_dev;


//编写一个类似i2c_master_recv/i2c_master_send
int at24_i2c_read(struct i2c_client *client, char *buf, int size)
{
	int ret;
	struct i2c_adapter  *adapter = client->adapter;

	struct i2c_msg msg;
	msg.addr = client->addr;
	msg.flags = I2C_M_RD;
	msg.len = size;
	msg.buf = buf;
	// 参数1---适配器
	//参数2--消息包
	// 参数3--消息的个数
	ret = i2c_transfer(adapter, &msg, 1);
	return ret==1?size:ret;
}

int at24_i2c_write(struct i2c_client *client, char *buf, int size)
{

	int ret;
	struct i2c_adapter  *adapter = client->adapter;

	struct i2c_msg msg;
	msg.addr = client->addr;
	msg.flags = 0;
	msg.len = size;
	msg.buf = buf;
	
	ret = i2c_transfer(adapter, &msg, 1);

	return ret==1?size:ret;

}

int at24_i2c_write(struct i2c_client *client, char *buf, int size)
{

	int ret;
	struct i2c_adapter  *adapter = client->adapter;

	struct i2c_msg msg;
	msg.addr = client->addr;
	msg.flags = 0;
	msg.len = size;
	msg.buf = buf;
	
	ret = i2c_transfer(adapter, &msg, 1);

	return ret==1?size:ret;

}

int at24_e2prom_drv_open (struct inode *inode, struct file *filp)
{

	return 0;
}

ssize_t at24_e2prom_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
	int ret;
	
	if(count < 0 || count > 256)
		return -EINVAL;

	char *tmp = kzalloc(count, GFP_KERNEL);
	
	// 1, 从硬件中获取数据
	ret = at24_i2c_read(at24_dev->client, tmp, count);
	if(ret < 0)
	{
		printk("at24_i2c_read error\n");
		goto err_free;
	}

	// 2 ,将数据给用户
	ret = copy_to_user(buf, tmp, count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		goto err_free;
	}
err_free:
	kfree(tmp);
	return ret;
}

ssize_t at24_e2prom_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	printk("-----------%s-----------\n", __FUNCTION__);
	int ret;
	
	if(count < 0 || count > 256)
		return -EINVAL;

	char *tmp = kzalloc(count, GFP_KERNEL);
	
	// 1, 从用户空间将数据获取到
	ret = copy_from_user(tmp, buf, count);
	if(ret > 0)
	{
		printk("copy_from_user error\n");
		goto err_free;
	}
	// 2,  将数据写入硬件中去
	ret = at24_i2c_write(at24_dev->client, tmp, count);
	if(ret < 0)
	{
		printk("at24_i2c_write error\n");
		goto err_free;
	}
err_free:
	kfree(tmp);
	return ret;
}