当前位置:网站首页>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;
}
边栏推荐
- Leetcode list summary
- 整理混乱的头文件,我用include what you use
- 【Proteus仿真】基于VSM 串口printf调试输出示例
- gatling 之性能测试
- [template] [Luogu p4630] duathlon Triathlon (round square tree)
- Win32 API 访问路由的加密网页
- The Block:USDD增长势头强劲
- Is it safe for CITIC Securities to open an account online? Is the account opening fee charged
- 【HCIA持续更新】广域网技术
- Rainfall warning broadcast automatic data platform bwii broadcast warning monitor
猜你喜欢
Vscode modification indentation failed, indent four spaces as soon as it is saved
超标量处理器设计 姚永斌 第6章 指令解码 摘录
[HCIA continuous update] WLAN overview and basic concepts
上网成瘾改变大脑结构:语言功能受影响,让人话都说不利索
Zebras are recognized as dogs, and the reason for AI's mistakes is found by Stanford
To sort out messy header files, I use include what you use
【测试开发】软件测试——基础篇
Rainfall warning broadcast automatic data platform bwii broadcast warning monitor
Superscalar processor design yaoyongbin Chapter 5 instruction set excerpt
wuzhicms代码审计
随机推荐
智捷云——元宇宙综合解决方案服务商
中断的顶半部和底半部介绍以及实现方式(tasklet 和 工作队列)
Superscalar processor design yaoyongbin Chapter 5 instruction set excerpt
High school physics: force, object and balance
La 18e Conférence internationale de l'IET sur le transport d'électricité en courant alternatif et en courant continu (acdc2022) s'est tenue avec succès en ligne.
Is it safe for CITIC Securities to open an account online? Is the account opening fee charged
数学分析_笔记_第7章:多元函数的微分学
R语言plotly可视化:plotly可视化多分类变量小提琴图(multiple variable violin plot in R with plotly)
【HCIA持续更新】WLAN概述与基本概念
Cocoscreator event dispatch use
TP configuring multiple databases
Blood spitting finishing nanny level series tutorial - play Fiddler bag grabbing tutorial (2) - first meet fiddler, let you have a rational understanding
超标量处理器设计 姚永斌 第5章 指令集体系 摘录
完美融入 Win11 风格,微软全新 OneDrive 客户端抢先看
Analysis of abnormal frequency of minor GC in container environment
CocosCreator事件派發使用
Dynamic programming stock problem comparison
R语言plotly可视化:plotly可视化互相重叠的直方图(historgram)、并在直方图的顶部边缘使用geom_rug函数添加边缘轴须图Marginal rug plots
网页游戏引擎
【测试开发】软件测试——基础篇