137 IIC核心函数与“万能”驱动

编程入门 行业动态 更新时间:2024-10-23 18:32:49

137 IIC核心<a href=https://www.elefans.com/category/jswz/34/1771370.html style=函数与“万能”驱动"/>

137 IIC核心函数与“万能”驱动

文章目录

    • 一、i2c_add_adapter()函数
    • 二、i2c_add_driver()宏
    • 三、i2c_register_driver()函数
    • 四、i2c_transfer()函数
    • 五、i2c_msg结构体
    • 六、i2c_master_send()函数
    • 七、i2c_master_recv()函数
    • 八、i2c_transfer_buffer_flags()函数
    • 九、"万能"的i2c驱动--i2c-dev.c分析
      • 1、i2c_dev_init()函数
      • 2、i2cdev_notifier定义
      • 3、i2cdev_notifier_call()函数
      • 4、I2C_MAJOR
      • 5、I2C_MINORS
      • 6、i2c_for_each_dev()函数
      • 7、bus_for_each_dev()函数
      • 8、i2cdev_attach_adapter()函数
      • 9、i2cdev_fops定义
      • 10、i2cdev_open()函数
      • 11、i2cdev_read()函数
      • 12、i2cdev_write()函数

一、i2c_add_adapter()函数

drivers/i2c/i2c-core-base.c

注册一个i2c适配器

// 由kernel分配适配器的编号
int i2c_add_adapter(struct i2c_adapter *adapter)
// 自己指定适配器的编号
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)

adapter->nr:适配器的编号

参数:

  • adapter:i2c物理控制器对应的适配器,对应一个结构体

返回值:

  • 成功:0
  • 失败:负数

二、i2c_add_driver()宏

include/linux/i2c.h

注册一个i2c驱动

// struct i2c_driver
#define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver)

三、i2c_register_driver()函数

drivers/i2c/i2c-core-base.c

注册一个i2c驱动

// 第二个参数对应的驱动结构体由我们自己来实现
int i2c_register_driver(struct module *owner,struct i2c_driver *driver)

参数:

  • owner: :一般为 THIS_MODULE
  • driver:要注册的 i2c_driver.

返回值:

  • 成功:0
  • 失败:负数

四、i2c_transfer()函数

drivers/i2c/i2c-core-base.c

收发iic消息(可收可发)

// 收发消息都要用到i2c_adapter适配器
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

参数:

  • adap :所使用的 I2C 适配器,i2c_client 会保存其对应的 i2c_adapter
  • msgs:I2C 要发送的一个或多个消息,每个消息对应一个消息结构体
  • num :消息数量,也就是 msgs 的数量

返回值:

  • 成功:发送的msgs 的数量
  • 失败:负数

五、i2c_msg结构体

include/uapi/linux/i2c.h

描述一个iic消息

struct i2c_msg
{// 从机地址,发送到哪个从设备地址__u16 addr;	/* slave address			*/// 读写等其他特性,见下面的宏__u16 flags;// 此宏表示读,0表示写#define I2C_M_RD		0x0001	/* read data, from slave to master *//* I2C_M_RD is guaranteed to be 0x0001! */#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */#define I2C_M_DMA_SAFE		0x0200	/* the buffer of this message is DMA safe *//* makes only sense in kernelspace *//* userspace buffers are copied anyway */#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */// 消息数据的长度__u16 len;		/* msg length				*/// 发送或接收的消息缓冲区__u8 *buf;		/* pointer to msg data			*/
};
  • addr:iic设备地址

  • flags:消息传输方向和特性。

    • I2C_M_RD:表示读取消息
    • 0:表示发送消息
  • len:消息数据的长度

  • buf:消息缓冲区

六、i2c_master_send()函数

include/linux/i2c.h

发送一个i2c消息,注意只能是一个。
其实最终调用的还是 i2c_transfer

static inline int i2c_master_send(const struct i2c_client *client,const char *buf, int count)
{return i2c_transfer_buffer_flags(client, (char *)buf, count, 0);
};

七、i2c_master_recv()函数

include/linux/i2c.h
接收一个i2c消息,注意只能是一个。
其实最终调用的还是 i2c_transfer

static inline int i2c_master_recv(const struct i2c_client *client,char *buf, int count)
{return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD);
};

八、i2c_transfer_buffer_flags()函数

drivers/i2c/i2c-core-base.c

发送一个i2c消息
其实最终调用的还是 i2c_transfer

int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,int count, u16 flags)
{int ret;// 根据传入参数构建消息结构体,此结构体定义详见上struct i2c_msg msg ={.addr = client->addr,.flags = flags | (client->flags & I2C_M_TEN),.len = count,.buf = buf,};ret = i2c_transfer(client->adapter, &msg, 1);/** If everything went ok (i.e. 1 msg transferred), return #bytes* transferred, else error code.*/return (ret == 1) ? count : ret;
}

九、"万能"的i2c驱动–i2c-dev.c分析

drivers/i2c/i2c-dev.c

  • 内核集成i2c_dev驱动模块,开机自动加载
  • 为每个i2c_adapter生成一个设备文件,通过该设备文件间接使用IIC核心函数收发数据
  • 注册i2c总线的通知函数,解决加载顺序问题

1、i2c_dev_init()函数

drivers/i2c/i2c-dev.c

static int __init i2c_dev_init(void)
{int res;printk(KERN_INFO "i2c /dev entries driver\n");// 申请设备号,I2C_MAJOR为89,次设备号为0,I2C_MINORS为1<<20-1,表示次设备号的数量。// 就是把这个主设备号对应的次设备号都申请了。res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");if (res)goto out;// 创建一个同名类,在 /sys/class中可以看到i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");if (IS_ERR(i2c_dev_class)) {res = PTR_ERR(i2c_dev_class);goto out_unreg_chrdev;}...// 注册i2c总线的通知函数// 参数2详见下res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);if (res)goto out_unreg_class;/* Bind to already existing adapters right away */// 遍历i2c总线上的所有设备,每次都执行第二个参数对应的函数i2c_for_each_dev(NULL, i2cdev_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:printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);return res;
}

2、i2cdev_notifier定义

static struct notifier_block i2cdev_notifier =
{// 详见下.notifier_call = i2cdev_notifier_call,
};

3、i2cdev_notifier_call()函数

static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,void *data)
{struct device *dev = data;// 发生的事件类型switch (action) {// 此i2c总线下发生添加设备事件case BUS_NOTIFY_ADD_DEVICE:// 创建设备文件之类的操作return i2cdev_attach_adapter(dev, NULL);// 此i2c总线下发生删除设备事件case BUS_NOTIFY_DEL_DEVICE:return i2cdev_detach_adapter(dev, NULL);}return 0;
}

4、I2C_MAJOR

include/linux/i2c-dev.h

#define I2C_MAJOR	89		/* Device major number		*/

5、I2C_MINORS

drivers/i2c/i2c-dev.c

#define MINORBITS	20
#define MINORMASK	((1U << MINORBITS) - 1)
#define I2C_MINORS	MINORMASK

6、i2c_for_each_dev()函数

drivers/i2c/i2c-core-base.c

int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{int res;mutex_lock(&core_lock);res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);mutex_unlock(&core_lock);return res;
}

7、bus_for_each_dev()函数

drivers/base/bus.c

int bus_for_each_dev(struct bus_type *bus, struct device *start,void *data, int (*fn)(struct device *, void *))
{struct klist_iter i;struct device *dev;int error = 0;if (!bus || !bus->p)return -EINVAL;klist_iter_init_node(&bus->p->klist_devices, &i,(start ? &start->p->knode_bus : NULL));while (!error && (dev = next_device(&i)))error = fn(dev, data);klist_iter_exit(&i);return error;
}

8、i2cdev_attach_adapter()函数

i2c总线添加设备后回调执行的函数
drivers/i2c/i2c-dev.c

static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{struct i2c_adapter *adap;struct i2c_dev *i2c_dev;int res;// 若设备类型不是i2c适配器,直接返回// 也有可能是 i2c_client 设备if (dev->type != &i2c_adapter_type)return 0;// 从i2c设备结构体中获取i2c适配器结构体adap = to_i2c_adapter(dev);// 分配内存i2c_dev = get_free_i2c_dev(adap);if (IS_ERR(i2c_dev))return PTR_ERR(i2c_dev);// 设置文件操作接口cdev_init(&i2c_dev->cdev, &i2cdev_fops);i2c_dev->cdev.owner = THIS_MODULE;// 注意次设备号为适配器对应的编号,可以自己指定,也可以有系统分配// 参数3为此设备哈的数量,此fops只对应此设备号的文件res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);if (res)goto error_cdev;/* register this i2c device with the driver core */// 在/sys下,在i2c_dev_class类(参数1)下创建一个新的目录, "i2c-适配器编号"就是目录名// 然后会通知用户空间的uevent守护进程,此守护进程会创建一个同参数4名的设备文件i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr);if (IS_ERR(i2c_dev->dev)) {res = PTR_ERR(i2c_dev->dev);goto error;}pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",adap->name, adap->nr);return 0;
error:cdev_del(&i2c_dev->cdev);
error_cdev:put_i2c_dev(i2c_dev);return res;
}

9、i2cdev_fops定义

drivers/i2c/i2c-dev.c

static const struct file_operations i2cdev_fops =
{.owner		= THIS_MODULE,.llseek		= no_llseek,//.read		= i2cdev_read,//.write		= i2cdev_write,// .unlocked_ioctl	= i2cdev_ioctl,pat_ioctl	= compat_i2cdev_ioctl,//.open		= i2cdev_open,.release	= i2cdev_release,
};

10、i2cdev_open()函数

drivers/i2c/i2c-dev.c

static int i2cdev_open(struct inode *inode, struct file *file)
{// 从inode获取次设备号,就是适配器的编号unsigned int minor = iminor(inode);struct i2c_client *client;struct i2c_adapter *adap;// 根据次设备号从i2c总线获取对应的适配器adap = i2c_get_adapter(minor);if (!adap)return -ENODEV;// client表示某个具体的i2c设备,为其分配内存client = kzalloc(sizeof(*client), GFP_KERNEL);if (!client) {i2c_put_adapter(adap);return -ENOMEM;}// 设置具体的i2c设备的名字snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);// 设置具体的i2c设备归属的适配器client->adapter = adap;// 以后可以通过file的此成员获取client指针file->private_data = client;return 0;
}

11、i2cdev_read()函数

drivers/i2c/i2c-dev.c

static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,loff_t *offset)
{char *tmp;int ret;struct i2c_client *client = file->private_data;if (count > 8192)count = 8192;// 分配内存,用于接收消息tmp = kmalloc(count, GFP_KERNEL);if (tmp == NULL)return -ENOMEM;pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",iminor(file_inode(file)), count);// 核心函数,接受一个i2c消息ret = i2c_master_recv(client, tmp, count);if (ret >= 0)ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;kfree(tmp);return ret;
}

12、i2cdev_write()函数

drivers/i2c/i2c-dev.c

static ssize_t i2cdev_write(struct file *file, const char __user *buf,size_t count, loff_t *offset)
{int ret;char *tmp;struct i2c_client *client = file->private_data;if (count > 8192)count = 8192;把用户空间的buf拷贝内核空间的tmptmp = memdup_user(buf, count);if (IS_ERR(tmp))return PTR_ERR(tmp);pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",iminor(file_inode(file)), count);// 核心函数,发送一个i2c消息ret = i2c_master_send(client, tmp, count);kfree(tmp);return ret;
}

更多推荐

137 IIC核心函数与“万能”驱动

本文发布于:2024-03-14 03:46:11,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1735555.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:函数   核心   IIC

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!