I2C ok6410 I2C driver analysis-user-mode driver, linuxok6410i2c
3 i2c-dev
3.1 Overview
Previously speaking about the I2C subsystem, I mentioned using i2c-dev.c files to implement our I2C Slave Device Driver in applications. However, it implements a virtual, temporary i2c_client, which is generated as the device file is opened and abolished as the device file is closed. The I2c-dev.c generates a device file named 89 for each I2C adapter, and implements the i2c_driver member function and file operation interface, so the subject of the i2c-dev.c is "i2c_driver member function + character device driver ".
3.2 i2c-dev.c source code analysis
Initialization module
[Cpp]View plaincopy
- Static int _ init i2c_dev_init (void)
- {
- Res = register_chrdev (I2C_MAJOR, "i2c", & i2cdev_fops );
- I2c_dev_class = class_create (THIS_MODULE, "i2c-dev ");
- /* Keep track of adapters which will be added or removed later */
- Res = bus_register_notifier (& i2c_bus_type, & i2cdev_notifier );
- /* Bind an existing adapter */
- I2c_for_each_dev (NULL, i2cdev_attach_adapter );
- }
The I2c-dev initialization function is mainly used to register the character device file named "i2c" and "i2c-dev" Class
I2cdev_read and i2cdev_write
The i2cdev_read and i2cdev_write functions implemented in the I2c-dev.c are not too general and only suitable for the following single start signal:
However, it is not suitable for starting signals:
Therefore, we often use I2C_RDWR of the i2cdev_ioctl function. Before analyzing the i2cdev_ioctl function, we need to understand a struct:
[Cpp]View plaincopy
- /* This is the structure as used in theI2C_RDWR ioctl call */
- Struct i2c_rdwr_ioctl_data {
- Structi2c_msg _ user * msgs;/* pointersto i2c_msgs */
- _ U32nmsgs;/* number ofi2c_msgs */
- };
Msgs indicates the data transmitted by a single start signal;
Nmsgs indicates the number of msgs instances. For example, if a single start signal is used, nmsgs is equal to 1; if multiple start signals are used, nmsgs is equal to 2.
[Cpp]View plaincopy
- Struct i2c_msg {
- _ U16addr;/* slave address */
- _ U16flags;/* write by default */
- # Define I2C_M_TEN 0x0010/* this is a ten bit chip address */
- # Define I2C_M_RD 0x0001/* read data, from slave to master */
- # Define I2C_M_NOSTART 0x4000/* if I2C_FUNC_PROTOCOL_MANGLING */
- # Define I2C_M_REV_DIR_ADDR 0x2000/* if I2C_FUNC_PROTOCOL_MANGLING */
- # Define I2C_M_IGNORE_NAK 0x1000/* if I2C_FUNC_PROTOCOL_MANGLING */
- # Define I2C_M_NO_RD_ACK 0x0800/* if I2C_FUNC_PROTOCOL_MANGLING */
- # Define I2C_M_RECV_LEN 0x0400/* length will be first converted ed byte */
- _ U16len;/* msg length */
- _ U8 * buf;/* pointer to msgdata */
- };
3.3 eeprom instance
Prerequisites
On the ok6410 Development Board, the address of the eeprom is 0x50. In this experiment, you can read and write data first.
The time sequence for writing AT24C02 arbitrary address Bytes:
The time sequence for writing AT24C02 arbitrary address Bytes:
User-driven:
# Include <stdio. h>
# Include <fcntl. h>
# Include <unistd. h>
# Include <sys/ioctl. h>
# Include <stdlib. h>
// # Include <linux/types. h>
# Define I2C_RDWR0x0707
Struct i2c_msg {
Unsigned short addr;/* slave address */
Unsigned short flags;
Unsigned short len;/* msg length */
Unsigned char * buf;/* pointer to msg data */
};
Struct i2c_rdwr_ioctl_data {
Struct i2c_msg * msgs;/* pointers to i2c_msgs */
Unsigned int nmsgs;/* number of i2c_msgs */
};
Int main ()
{
Int fd;
Struct i2c_rdwr_ioctl_data e2prom_data;
// 1. Open a Universal Device File
Fd = open ("/dev/i2c-0", O_RDWR );
// Allocate space for struct i2c_msg * In i2c_rdwr_ioctl_data
E2prom_data.msgs = (struct i2c_msg *) malloc (2 * sizeof (struct i2c_msg); // construct two messages
// 2. Construct the data written to the eeprom
E2prom_data.nmsgs = 1; // only one message exists.
(E2prom_data.msgs [0]). len = 2; // The length is equal to 2. The first byte represents the internal address of the i2c device, and the second byte represents the written data.
(E2prom_data.msgs [0]). addr = 0x50; // the address of the slave device (e2prom address). Note that there is no direction here!
(E2prom_data.msgs [0]). flags = 0; // The direction is indicated by the flag. 0 indicates writing, and 1 indicates reading.
(E2prom_data.msgs [0]). buf = (unsigned char *) malloc (2); // only two bytes are allocated here (one byte of internal offset address and one byte of data)
(E2prom_data.msgs [0]). buf [0] = 0x10; // data will be written to the internal 0x10 address in e2prom.
(E2prom_data.msgs [0]). buf [1] = 0x60; // write the data bit 0x60 in the internal 0x10 Address of e2prom
// 3. Use ioctl to write data
Ioctl (fd, I2C_RDWR, (unsigned long) & e2prom_data, I2C_RDWR); // The Command Parameter corresponds to the parameter in the case statement in the ioctl driver.
// 4. Construct a message to read data from the eeprom
E2prom_data.nmsgs = 2; // two messages are required for Data Reading.
(E2prom_data.msgs [0]). len = 1; // The length is one byte, representing the internal address of the i2c Device
(E2prom_data.msgs [0]). addr = 0x50; // the address of the slave device (e2prom address). Note that there is no direction here!
(E2prom_data.msgs [0]). flags = 0; // The direction is indicated by the flag. 0 indicates writing, and 1 indicates reading.
(E2prom_data.msgs [0]). buf [0] = 0x10; // data will be written to the internal 0x10 address in e2prom.
// The second message (read data)
(E2prom_data.msgs [1]). len = 1; // The length is one byte, representing the internal address of the i2c Device
(E2prom_data.msgs [1]). addr = 0x50; // the address of the slave device (e2prom address). Note that there is no direction here!
(E2prom_data.msgs [1]). flags = 1; // The direction is indicated by the flag. 0 indicates write, and 1 indicates read.
(E2prom_data.msgs [1]). buf = (unsigned char *) malloc (2 );
(E2prom_data.msgs [1]). buf [0] = 0; // The data is read from the internal 0x10 address in e2prom.
// 5. Read data using ioctl
Ioctl (fd, I2C_RDWR, (unsigned long) & e2prom_data );
Printf ("buf [0] = % x \ n", (e2prom_data.msgs [1]). buf [0]);
// 6. Disable the device
Close (fd );
}