当前位置:网站首页>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;
}
边栏推荐
- Superscalar processor design yaoyongbin Chapter 7 register rename excerpt
- Wuzhicms code audit
- 7 RSA密码体制
- New technology releases a small program UNIPRO to meet customers' mobile office scenarios
- Master the use of auto analyze in data warehouse
- 超标量处理器设计 姚永斌 第6章 指令解码 摘录
- 大规模服务异常日志检索
- S5PV210芯片I2C适配器驱动分析(i2c-s3c2410.c)
- 缓存穿透、缓存击穿、缓存雪崩分别是什么
- 高中物理:力、物体和平衡
猜你喜欢
To sort out messy header files, I use include what you use
整理混乱的头文件,我用include what you use
Analysis of abnormal frequency of minor GC in container environment
To sort out messy header files, I use include what you use
超大规模数仓集群在大型商业银行的落地实践
整理混乱的头文件,我用include what you use
RecastNavigation 之 Recast
CocosCreator事件派发使用
Ks007 realizes personal blog system based on JSP
Using win10 scheduling task program to automatically run jar package at fixed time
随机推荐
Win32 API 访问路由的加密网页
数学分析_笔记_第7章:多元函数的微分学
Is it safe for Great Wall Securities to open an account? How to open a securities account
Mathematical analysis_ Notes_ Chapter 7: differential calculus of multivariate functions
Summary of tx.origin security issues
Go micro tutorial - Chapter 2 go micro V3 using gin and etcd
7 RSA Cryptosystem
缓存穿透、缓存击穿、缓存雪崩分别是什么
VSCode修改缩进不成功,一保存就缩进四个空格
完美融入 Win11 风格,微软全新 OneDrive 客户端抢先看
【HCIA持续更新】网络管理与运维
【Hot100】32. 最长有效括号
Vscode modification indentation failed, indent four spaces as soon as it is saved
Easy to use map visualization
Electronic pet dog - what is the internal structure?
图像检索(image retrieval)
gatling 之性能测试
Zebras are recognized as dogs, and the reason for AI's mistakes is found by Stanford
Vb无法访问数据库stocks
关于nacos启动时防火墙开启8848的坑