嵌入式[基础] Linux下IIC设备驱动描述

171 阅读5分钟

IIC特点

  1. IIC是一种半双工基础协议
  2. IIC一次传输8bit,高位在前,低位在后
  3. 总线空闲状态时 数据线为高电平

IIC总线架构分析

image.png

设备驱动层组件 linux*/drivers/i2c/i2c-dev.c

  1. 提供用户层相关接口
  2. 统一封装发送格式,但不关心数据的发送形式

设备核心层组件 linux*/drivers/i2c/i2c-core.c

  1. 注册I2C总线
  2. 为驱动层提供编程接口

设备总线驱动层组件 linux*/drivers/i2c/busses/i2c-xxx.c 不同芯片实现不同

  1. 初始化硬件相关接口如IIC控制器
  2. 实现数据发送形式,如高位发送,不关心具体发送的数据

这三个文件的目的是为了相互配合实现操作相应的外围设备

IIC核心层分析

核心层文件 i2c-core-base.c

postcore_initcall(i2c_init); i2c注册函数

i2c_init函数分析



/* 注册i2c设备,最终形成一个i2c的驱动链表 */
static int __init i2c_init(void)
{
    // 注册一根i2c物理总线
    retval = bus_register(&i2c_bus_type); ----|
                                              |
    struct bus_type i2c_bus_type = { <--------|
            .name		= "i2c",
            .match		= i2c_device_match, // 匹配函数 ------------------------|
            .probe		= i2c_device_probe,                                    |
            .remove		= i2c_device_remove,                           |
            .shutdown	= i2c_device_shutdown,                                 |
    };                                                                            |
    static int i2c_device_match(struct device *dev, struct device_driver *drv)<---|
    {
            struct i2c_client	*client = i2c_verify_client(dev); // i2c设备 
            struct i2c_driver	*driver; // i2c驱动
     
            // 获取驱动
            driver = to_i2c_driver(drv);
            // 根据id_table名称和设备结构体匹配,如果匹配则返回匹配的id,否则返回空
            /* Finally an I2C match */
            if (i2c_match_id(driver->id_table, client))
                    return 1;

            return 0;
    }
        // 注册i2c驱动
	retval = i2c_add_driver(&dummy_driver);
	if (retval)
		goto class_err;

	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
		WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
	if (IS_ENABLED(CONFIG_ACPI))
		WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));

	return 0;

class_err:
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
	is_registered = false;
	bus_unregister(&i2c_bus_type);
	return retval;
}

Linux驱动涉及到的重要结构体i2c.h

i2c驱动描述结构体 表示一个i2c驱动

struct i2c_driver {
	检测函数
	int (*probe)(struct i2c_client *client);
	void (*remove)(struct i2c_client *client);

	struct device_driver driver; // 表示驱动
	const struct i2c_device_id *id_table; // 记录i2c驱动能服务于哪些设备
};

i2c设备描述结构体 表示一个i2c设备

struct i2c_client {
	unsigned short flags;		/* 唯一标号		*/
	unsigned short addr;		/* 设备地址 7bit	*/
					
	char name[I2C_NAME_SIZE];       /* 设备名称 */
	struct i2c_adapter *adapter;	/* 所属适配器	*/
	struct device dev;		/* 设备结构体		*/
	int init_irq;			/* 设备对应的中断号	*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;      // 链表
};

i2c适配器/控制器

struct i2c_adapter {
	const struct i2c_algorithm *algo; /* 表示i2c的操作方法,如利用i2c的协议传输数据 */
};

i2c数据发送
struct i2c_algorithm {
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
};

i2c_msg i2c数据包

struct i2c_msg {
	__u16 addr;  // 设备地址
	__u16 flags; // 数据包读写标志位 1 读 0 写
	__u16 len;   // 数据包有效数据长度
	__u8 *buf;   // 有效数据指针
};

i2c_board_info i2c板卡信息

struct i2c_board_info {
	char		type[I2C_NAME_SIZE]; // i2c设备名称 如 cv1800
	unsigned short	flags;
	unsigned short	addr;                // 设备地址
	const char	*dev_name;
	void		*platform_data;
	struct device_node *of_node;
	struct fwnode_handle *fwnode;
	const struct software_node *swnode;
	const struct resource *resources;
	unsigned int	num_resources;
	int		irq;
};
操作时使用宏来操作
#define I2C_BOARD_INFO(dev_type, dev_addr) \
	.type = dev_type, .addr = (dev_addr)

IIC设备驱动层分析 i2c-dev.c

初始化函数 i2c_dev_init

  1. 注册主设备号和硬件操作方法
  2. 创建主设备类
  3. 找到设备并关联驱动
  4. 创建设备文件
static int __init i2c_dev_init(void)
{
        /* 注册主设备号,注册硬件操作方法 */
        /* I2C_MAJOR 在系统中主设备号为89 */
	res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
        /* 创建设备类 */
	i2c_dev_class = class_create("i2c-dev");
	if (IS_ERR(i2c_dev_class)) {
		res = PTR_ERR(i2c_dev_class);
		goto out_unreg_chrdev;
	}
	i2c_dev_class->dev_groups = i2c_groups;

	/* Keep track of adapters which will be added or removed later */
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
	if (res)
		goto out_unreg_class;

	/* Bind to already existing adapters right away */
	/* 绑定适配器,绑定具体操作的哪一台设备,搜索I2C总线的设备链表,
           每搜索到一个设备,都会调用i2cdev_attach_adapter */
        i2c_for_each_dev(NULL, i2c_dev_attach_adapter);

	return 0;

out_unreg_class:
	class_destroy(i2c_dev_class);
out_unreg_chrdev:
	unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
	pr_err("Driver Initialisation failed\n");
	return res;
}

static int i2cdev_attach_adapter(struct device *dev)
{
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
        
        /* 判断是否是适配器,如果不是则返回 */
	if (dev->type != &i2c_adapter_type)
		return NOTIFY_DONE;
                
	i2c_dev = get_free_i2c_dev(adap);
        /* 初始化一个i2c设备链表节点 */
        device_initialize(&i2c_dev->dev);
        /* 设置i2c设备名称 /dev/i2c-0 dev/i2c-1... */
	i2c_dev->dev.devt = MKDEV(I2C_MAJOR, adap->nr);
	i2c_dev->dev.class = i2c_dev_class;
	i2c_dev->dev.parent = &adap->dev;
	i2c_dev->dev.release = i2cdev_dev_release;
}

iic开发涉及到的设备层的重要函数

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); 传输一个i2c数据包
int i2c_register_board_info(int busnum, struct i2c_board_info const *info,
			unsigned n); 注册i2c板级信息,如i2c的oled屏幕名称

int i2c_add_adapter(struct i2c_adapter *adap); 注册/控制器
void i2c_del_adapter(struct i2c_adapter *adap); 注销/控制器
int i2c_add_numbered_adapter(struct i2c_adapter *adap);

注册/注销i2c驱动
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
void i2c_del_driver(struct i2c_driver *driver);

注册/注销i2c设备