当前位置:网站首页>Device interface analysis of the adapter of I2C subsystem (I2C dev.c file analysis)
Device interface analysis of the adapter of I2C subsystem (I2C dev.c file analysis)
2022-07-04 17:43:00 【Snail taking off】
1、 The principle of driving implementation
(1) Ideas for driving implementation : take I2C The operation method of the controller is directly provided to the application , The driver itself only implements the simplest I2C The method of bus sending and receiving data , Specifically I2C The operation sequence of the interface hardware is controlled by the application ;
(2) The driver only does the most basic operation of sending and receiving data , The difficulty of driver code development is reduced , But the difficulty of application development has increased , And applications need to be aware of hardware differences , But most of us need to shield the hardware differences of applications when writing programs , So this driver framework is not commonly used ;
2、 The effect of driving
(1) stay /dev Create under directory "i2c-n(n=0,1,2······)" Device node , Each device node represents Soc One of the I2C controller ;
(2) Application passed open、read、write、ioctl Such as function , Go to mount in corresponding I2C On the bus I2C Equipment communication ;
Add : One I2C The controller is equivalent to a I2C Bus ;I2C The controller is in I2C It is also called adapter in subsystem ;
3、 Driver loading :i2c_dev_init( )
// Operation method of equipment node
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
//I2C drive
static struct i2c_driver i2cdev_driver = {
.driver = {
.name = "dev_driver",
},
.attach_adapter = i2cdev_attach_adapter,
.detach_adapter = i2cdev_detach_adapter,
};
#define I2C_MAJOR 89 /* Device major number */
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
// The registered device number is 89, The range of secondary equipment number is 0-255、 The set of file operations is i2cdev_fops Character devices
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
// The registered name is i2c-dev The device class of
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;
}
// register i2c Adapter device driver i2cdev_driver
res = i2c_add_driver(&i2cdev_driver);
if (res)
goto out_unreg_class;
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
(1) The registered device number is 89, The range of secondary equipment number is 0-255、 The set of file operations is i2cdev_fops Character devices ;
(2) The registered name is i2c-dev The device class of ;
(3) register i2c Adapter device driver i2cdev_driver, Every future kernel registration I2C Adapters will be exposed to applications by the driver in the form of device nodes ;
summary :I2C All drivers belong to i2c-dev Equipment class , Common main equipment number 89; In the future "/sys/class/i2c-dev" You can see the registered I2C drive ;
4、 Open the device node
4.1、i2cdev_open() function
static int i2cdev_open(struct inode *inode, struct file *file)
{
// adopt inode Get the secondary device number
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
// From the linked list according to the secondary equipment number i2c_dev_list Get the corresponding i2c_dev object
i2c_dev = i2c_dev_get_by_minor(minor);
if (!i2c_dev)
return -ENODEV;
// get ID by i2c_dev->adap->nr The adapter object for
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap)
return -ENODEV;
/* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. * * This client is ** NEVER REGISTERED ** with the driver model * or I2C core code!! It just holds private copies of addressing * information and maybe a PEC flag. */
// Create a i2c_client Object and initialize
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->driver = &i2cdev_driver;
client->adapter = adap;
// Save to struct file Private data of structure , I will use
file->private_data = client;
return 0;
}
(1) from inode Get secondary device number from , Then from the linked list according to the secondary equipment number i2c_dev_list Get the corresponding i2c_dev object ;
(2) get ID by i2c_dev->adap->nr The adapter object for , Adapter objects are used in the kernel to represent I2C Controller ;
(3) Create a i2c_client Object and initialize , take adap、i2cdev_driver、 The names are saved to client in ;
(4) The final will be i2c_client Object as file Private data for subsequent operations ;
5、 from I2C The device reads data
5.1、 Function call
i2cdev_read()
i2c_master_recv()
i2c_transfer()
adap->algo->master_xfer() //adap How the adapter sends and receives data
copy_to_user()
5.2、i2cdev_read() function
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
// Analysis Division open Built when struct i2c_client Structure
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->f_path.dentry->d_inode), count);
// receive data , Then copy the data to the user space cache
ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;
}
(1) Analysis Division open Built when struct i2c_client Structure ;
(2) call i2c_master_recv() Function to receive data , Then copy the data to the user space cache ;
5.3、i2c_master_recv() function
int i2c_master_recv(struct i2c_client *client, char *buf, int count)
{
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
int ret;
// stay open() There is no right in the method client->addr assignment , Therefore, you cannot call directly after opening the device read() Method
msg.addr = client->addr; // From the device in I2C Address on the bus
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
//i2c_transfer() By calling the adapter communication method master_xfer Send a message
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes transmitted, else error code. */
return (ret == 1) ? count : ret;
}
(1)i2c_master_recv() By filling i2c_msg Structure , And then call i2c_transfer() Function to realize transmission ;
(2)i2c_transfer() Then call the adapter communication method master_xfer() Send a message ;
Add : In filling msg The slave device is assigned at I2C Address on the bus , But in open Time is not right addr Assign a value , So we can't open Directly after read Operation of the , You need to call ioctl Function to set the slave device address addr, Indicates to operate I2C Which device on the bus ;
6、 Go to I2C Device write data
6.1、 Function call relationship
i2cdev_write()
copy_from_user()
i2c_master_send()
i2c_transfer()
adap->algo->master_xfer()
6.2、i2cdev_write() function
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;
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
// Copy the data to be sent from user space to kernel space
if (copy_from_user(tmp, buf, count)) {
kfree(tmp);
return -EFAULT;
}
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
iminor(file->f_path.dentry->d_inode), count);
ret = i2c_master_send(client, tmp, count);
kfree(tmp);
return ret;
}
(1) Analysis Division open Built when struct i2c_client Structure ;
(2) call i2c_master_send() Function to send data , Ultimately, the adapter communication method is called master_xfer(), It is basically the same as the process of reading data ;
7、ioctl function :i2cdev_ioctl()
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
// Analysis Division open() Created when client
struct i2c_client *client = file->private_data;
unsigned long funcs;
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
switch (cmd) {
case I2C_SLAVE: // Assign slave device address , If the address has been assigned , Then return to EBUSY error
case I2C_SLAVE_FORCE: // Whether assigned or not , Force this address
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return 0;
case I2C_TENBIT: // Set up I2C_M_TEN identification . Enable 10bit Address mode
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC: // start-up SMBus Package error checking
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS: // Read the functions supported by the adapter
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);
case I2C_RDWR: // Read 、 write in I2C news
return i2cdev_ioctl_rdrw(client, arg);
case I2C_SMBUS: // Handle SMBus Message transmission
return i2cdev_ioctl_smbus(client, arg);
case I2C_RETRIES: // Set number of retries
client->adapter->retries = arg;
break;
case I2C_TIMEOUT: // Set timeout
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
return -ENOTTY;
}
return 0;
}
8、 When is the device node created
8.1、 Function call relationship
i2c_dev_init()
i2c_add_driver()
i2c_register_driver()
bus_for_each_dev()
__process_new_driver()
driver->attach_adapter()
i2cdev_attach_adapter()
device_create()
device_create_file()
8.2、i2cdev_attach_adapter() function
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
int res;
// Allocate one i2c_dev object , To add to i2c_dev_list In the list
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
/* Create a device object and create it in sysfs Register in , stay /dev The equipment number created under the directory is MKDEV(I2C_MAJOR, adap->nr), The name is "i2c-%d" Character device node */
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;
}
// establish "/sys/class/i2c-dev/"i2c-%d"/name" file
res = device_create_file(i2c_dev->dev, &dev_attr_name);
if (res)
goto error_destroy;
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error_destroy:
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
return_i2c_dev(i2c_dev);
return res;
}
边栏推荐
- RecastNavigation 之 Recast
- Load test practice of pingcode performance test
- 《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(2)-初识Fiddler让你理性认识一下
- Pytorch深度学习之环境搭建
- 【Unity UGUI】ScrollRect 动态缩放格子大小,自动定位到中间的格子
- 新享科技发布小程序UniPro小优 满足客户移动办公场景
- Cann operator: using iterators to efficiently realize tensor data cutting and blocking processing
- curl 命令妙用
- 防火墙基础透明模式部署和双机热备
- 离线、开源版的 Notion—— 笔记软件Anytype 综合评测
猜你喜欢
CANN算子:利用迭代器高效实现Tensor数据切割分块处理
Zebras are recognized as dogs, and the reason for AI's mistakes is found by Stanford
使用3DMAX制作一枚手雷
Rainfall warning broadcast automatic data platform bwii broadcast warning monitor
解读数据安全治理能力评估框架2.0,第四批DSG评估征集中
To sort out messy header files, I use include what you use
Cann operator: using iterators to efficiently realize tensor data cutting and blocking processing
NFT liquidity market security issues occur frequently - Analysis of the black incident of NFT trading platform quixotic
Recast of recastnavigation
Vb无法访问数据库stocks
随机推荐
Master the use of auto analyze in data warehouse
The 18th IET AC / DC transmission International Conference (acdc2022) was successfully held online
补能的争议路线:快充会走向大一统吗?
What if Kaili can't input Chinese???
kaili不能输入中文怎么办???
I2C子系统之适配器的设备接口分析(i2c-dev.c文件分析)
7 RSA Cryptosystem
shell脚本的替换功能实现
[HCIA continuous update] overview of WLAN workflow
NFT liquidity market security issues occur frequently - Analysis of the black incident of NFT trading platform quixotic
Load test practice of pingcode performance test
Go micro tutorial - Chapter 2 go micro V3 using gin and etcd
数学分析_笔记_第7章:多元函数的微分学
Chow Tai Fook fulfills the "centenary commitment" and sincerely serves to promote green environmental protection
《吐血整理》保姆级系列教程-玩转Fiddler抓包教程(2)-初识Fiddler让你理性认识一下
超标量处理器设计 姚永斌 第6章 指令解码 摘录
将Opencv绘制图片显示在MFC Picture Control控件上
雨量预警广播自动化数据平台BWII 型广播预警监测仪
Zebras are recognized as dogs, and the reason for AI's mistakes is found by Stanford
MVC模式和三层架构