Linux I2C Protocol

Keywords: Linux less

brief introduction

I2C bus only uses SCL and SDA signal lines to realize data interaction between devices, which greatly simplifies the occupancy of hardware resources and PCB board wiring space. Therefore, I2C bus is widely used in the interface between EEPROM, real-time clock, small LCD and CPU.

Linux I2C GPIO driver simulates I2C bus timing with GPIO port without dedicated I2C chip, and completes the communication process between Linux and I2C devices. Two GPIOs were used to simulate SDA and SCL respectively. Different from I2C driver, GPIO has its own transmission algorithm in I2C driver simulation. GPIO simulates I2C to occupy CPU resources, while I2C chips do not occupy CPU resources. Using I2C subsystem instead of common character device has the following advantages:

  1. Using the Linux I2C subsystem, you don't need to understand the I2C operation in too much detail.
  2. Writing driver is portable.
  3. Kernel resources can be used, and when faced with complex I2C devices, the workload is relatively much less.

Working Principle of I2C

Two transmission lines of I2C bus standard, SDA is data line and Scl is clock line. When SCL is high and SDA is low from high to low, start-up information is sent and nine pulses are sent. 1-7 is address, 8 is read and write control bit, and 9 is ACK response bit. Therefore, the controlled devices hanging on I2C receive the transmitted information and compare the received seven-bit address with their own address. Feedback will be provided. When the SCL is low and the SDA is high from low to high, the stop signal is sent.

Framework

Linux's I2C architecture is divided into three parts

1. I2C core framework

The definition of core data structure and related interface functions are provided to implement I2C adapter. The registration and cancellation management of driver and device drivers, as well as the upper level code of I2C communication method, which is independent of specific adapters, add corresponding reading and writing methods for each I2C bus in the system.

> I2C core framework implements i2c-core.c and i2c-dev.c in / drivers/i2c directory

2. I2C Bus Driver

The i2c_adapter data structure describing the specific I2C bus adapter is defined, and the I2C bus communication method on the specific I2C bus adapter is implemented. The i2c_algorithm data structure is described. Through I2C bus-driven code, we can control I2C to generate start bits, stop bits, read and write cycles, read and write from devices, generate ACK and so on.

> I2C bus driver is implemented in busses folder under / drivers/i2c directory. For example: Linux I2C GPIO bus driver is i2c_gpio.c. I2C bus algorithm in the / drivers/i2c directory algos folder. For example, Linux I2C GPIO bus driver algorithm is implemented in i2c_algo_bit.c.

3. I2C Device Driver

It is the realization of specific I2C hardware driver. I2C device driver communicates with CPU through I2C adapter. It mainly includes i2c_driver and i2c_client data structure. i2c_driver structure corresponds to a set of specific driving methods, such as probe, remove, suspend and so on, which need to be declared by oneself. I2c_client data structure is automatically generated by the kernel according to the specific device registration information, and the device driver is filled according to the specific hardware conditions. Specific use is described below.

> I2C device driver is implemented in chips folder under / drivers/i2c directory.

Equipment registration

Next, take GPIO analog I2C bus driver as an example to introduce device registration. For I2C chip drivers, there are many similarities and differences, mainly in the transmission algorithm. First make menuconfig select i2c-gpio so that it can be programmed into the kernel. Device registration includes two kinds of device registration, i2c-gpio bus and I2C device driver.

1. i2c-gpio Bus Registration / drivers/i2c/buses/i2c_gpio.c is the i2c-gpio bus driver source code. Here you can see the i2c-gpio registration

static struct platform_driver i2c_gpio_driver = {
       .driver = {
              .name = "i2c-gpio",           //Drive name
              .owner = THIS_MODULE,
       },
       .probe = i2c_gpio_probe,
       .remove = __devexit_p(i2c_gpio_remove),
};

static int __init i2c_gpio_init(void)
{
       int ret;
       ret = platform_driver_register(&i2c_gpio_driver);//Register as Platform Equipment
       if (ret)
              printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);
       return ret;
}
module_init(i2c_gpio_init);

Platform is the virtual bus of linux, which is called platform bus. The corresponding device is called platform_device, and the corresponding driver is called platform_driver. We know that I2C bus also corresponds to a device. Here is the corresponding i2c_adapte structure, which will be described in detail later. Here you can see that it registers I2C bus driver as platform device driver platform_driver_register (& i2c_gpio_driver) To register i2c_gpio devices as platform devices, it is necessary to add the definition of resources needed by i2c-gpio in mach_xxx board-level files (devices.c). That is to say, I2C bus devices are encapsulated as platform devices. Firstly, the system resources occupied by bus are defined.

static struct i2c_gpio_platform_data i2c3_data = {
        .sda_pin = CONFIG_SDA_PIN;
        .scl_pin = CONFIG_SCL_PIN; //Set up the gpio pins you need
             .udelay = 0,  //Set I2C working frequency, if no default value is 50
             .timeout = 0, //Set I2C work timeout if there is no default value of 10
};

Because the i2c_gpio driver needs to be registered on the platform bus, it also needs to add the platform_device structure of i2c-gpio to the board-level file of mach_xxx.

static struct platform_device i2c3_device = {
    .name       = "i2c-gpio", //Must have the same name as the i2c-gpio driver
    .id         = 2,          //Bus No. ID
    .dev = {
        .platform_data  = &i2c3_data,
    },
};

Before registering i2c-gpio driver, there must be a GPIO setup process. The setup process is as follows:

{
//SDA
Pnx_gpio_set_mode(GPIO_F8,GPIO_MODE_MUX1)
Pnx_gpio_set_direction(GPIO_F8,GPIO_DIR_OUTPUT)
//SCL
Pnx_gpio_set_mode(GPIO_F7,GPIO_MODE_MUX1)
Pnx_gpio_set_direction(GPIO_F7,GPIO_DIR_OUTPUT)

};

Finally, the i2c-gpio device is registered into the platform bus. platform_device_register(&i2c3_device);

2. Register I2C device driver to i2c-gpio bus For example, the device driver source code is in / drivers/i2c/chips/lis35de.c, which is required to register with the i2c bus. The practice is as follows. First, define the device ID:

static const struct i2c_device_id lis35de_id[] = {
                     { "lis35de", 0 },//Device name and device have data length
                     { }
};

Then declare the i2c_driver structure

static struct i2c_driver st_lis35de_driver = {
               .probe     = st_lis35de_probe,
               .remove        = st_lis35de_remove,
               .suspend   = st_lis35de_suspend,
               .resume        = st_lis35de_resume,//The above four functions are chosen according to the specific situation.
               .id_table = lis35de_id,
               .driver        = {
                     .name    = "lis35de",  //Drive name
                },
};

Finally, call static inline int i2c_add_driver(struct i2c_driver *driver) to register lis35de driver to I2C bus, as follows

static int __init st_lis35de_init(void)
{
         return i2c_add_driver(&st_lis35de_driver);//Register st_lis35de_driver
};
module_init(st_lis35de_init);

But up to now, it is not known to register with the I2C bus. Now add the lis35de device driver to the i2c-gpio bus we want. Using the function i2c_register_board_info provided by the kernel, the device information is registered on the I2C bus which needs to be registered in the board-level file of mach_xxx.

int __init i2c_register_board_info(int busnum,//Bus ID to be registered by the device
         struct i2c_board_info const *info,//Equipment information includes device name, address, etc.
unsigned len)

For example, the lis35de driver is registered with the i2c-gpio bus, and the bus ID is 2.

static struct i2c_board_info i2c_devices_lis35de[] = {
    {
        I2C_BOARD_INFO("lis35de", 0x1C), //Equipment name and address
    },
};
i2c_register_board_info(2,i2c_devices_lis35de,ARRAY_SIZE(i2c_devices_lis35de));

unsigned int pnx_modem_gpio_reserved in arch/arm/mach-pnx67xx/board_pnx67xx_wavex.c

Note GPIO_F7 and GPIO_F8 below to prevent the kernel from thinking that F8 and F7 have been used. So far, the i2c-gpio bus has been registered with the system and the device driver has been registered with the i2c-gpio bus.

So much has been said before. Is it a bit confusing? Let's take care of it here?

i2c bus driver

1. In the device. C file, declare the system resources occupied by the platform device, then define a platform device, and register the platform device to the platform bus.

2. In the i2c-gpio.c file, declare the list of devices supported by the driver, then define a platform driver structure, and register the platform driver to the platform bus.

i2c device driver

1. Also in devices.c file, declare an I2C device structure in the device list of the corresponding bus, and then register the device list to the I2C bus by the i2c_register_board_info() function.

2. In the lis35de.c file, the list of I2C devices declared to be supported and an I2C device driver structure i2c_driver are registered on the I2C bus.

> Note: Whether the device or driver is registered on the bus first, they automatically request to match all the drivers or devices on the bus.

I2C Key Data Structure and Detailed Registration Process

The above descriptions are the framework of i2c system. The detailed data structure registration process will be described in detail below.

Key data structures

In the i2c.h header file, four key data structures are defined: i2c_adapter, i2c_algorithm, i2c_driver and i2c_client.

1. i2c_algorithm corresponds to a set of communication methods.

This data structure is very important for the implementation of the specific transceiver algorithm. The transceiver function calls the specific hardware transceiver operation. For the communication method of i2c-gpio bus, the algos folder i2c_algo_bit.c in the / drivers/i2c directory is implemented.

struct i2c_algorithm {
    int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);
     //i2c transfer function pointer
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data);
    //smbus transfer function pointer
u32 (*functionality) (struct i2c_adapter *);
 //Return adapter support
};

i2c_adapter

Used to define each adapter (adapter) on the bus, each adapter needs the communication function provided in i2c_algorithm to control the access cycle of the adapter, so the i2c_algorithm pointer is included in i2c_adapter. The key function of i2c_algorithm, master_xfer, is used to generate the I2C access signal in i2c_msg.

struct i2c_adapter {
struct module *owner;  //Subordinate module
unsigned int id;      //The algorithm type, defined in i2c-id.h, begins with I2C_ALGO_
unsigned int class;       /* classes to allow probing for */
const struct i2c_algorithm *algo;
void *algo_data;   //algorithm data

int (*client_register)(struct i2c_client *); //Called when client registers
int (*client_unregister)(struct i2c_client *);

/* data fields that are valid for all devices   */
u8 level;           /* nesting level for lockdep */
struct mutex bus_lock;
struct mutex clist_lock;

int timeout;            /* in jiffies */
int retries;
struct device dev;      /* Adapter device */

int nr;
struct list_head clients;   /* DEPRECATED */
char name[48];
struct completion dev_released;
};

struct i2c_msg {
__u16 addr; /* Slave address         */
__u16 flags;
#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 received byte */
__u16 len;      /* msg length               */
__u8 *buf;      /* pointer to msg data          */
};

i2c_driver structure

struct i2c_driver {
    int id;
    unsigned int class;
    int (*attach_adapter)(struct i2c_adapter *);//Dependent on i2c_adapter
    int (*detach_adapter)(struct i2c_adapter *);//Departure from i2c_adapter
int (*detach_client)(struct i2c_client *) __deprecated; //Departure from i2c_ client

    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);
    void (*shutdown)(struct i2c_client *);
    int (*suspend)(struct i2c_client *, pm_message_t mesg);
    int (*resume)(struct i2c_client *);

    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
    //Similar to ioctl
    struct device_driver driver;
    const struct i2c_device_id *id_table;
    int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
    /* Device detection callback for automatic device creation */
    const struct i2c_client_address_data *address_data;
    struct list_head clients;
};
4)i2c_client Structure.
struct i2c_client {
    unsigned short flags;       /* sign */
    unsigned short addr;        /* Low 7 bits for chip address */

    char name[I2C_NAME_SIZE]; //Device name
    struct i2c_adapter *adapter;    /*Dependent on i2c_adapter   */
    struct i2c_driver *driver;  /*Dependence on i2c_ driver   */
    struct device dev;      /* the device structure     */
    int irq;            /* irq issued by device     */
    struct list_head list;      /* DEPRECATED */
    struct list_head detected;
    struct completion released;
};

Detailed registration process

After i2c-gpio bus is registered, the first function to be executed is i2c_gpio_probe: three main things are done here. First, an i2c_adapter is constructed, then a GPIO application is applied, and finally, the key call is i2c_bit_add_bus. Although the I2C bus is registered as platform device, here we mainly explain the part of I2C bus. We do not elaborate on the registration process of platform device and driver. If you are interested, you can refer to the part of platform driver.

static int __devinit i2c_gpio_probe(struct platform_device *pdev)
{
       pdata = pdev->dev.platform_data;  //Obtain specific hardware structure
       if (!pdata)
              return -ENXIO;
       ret = -ENOMEM;
       adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);//Here you get the adapter
       if (!adap)
              goto err_alloc_adap;
       bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);//The implementation of specific hardware is defined here.
       if (!bit_data)
              goto err_alloc_bit_data;
       ret = gpio_request(pdata->sda_pin, "sda");
  if (ret)
              goto err_request_sda;
       ret = gpio_request(pdata->scl_pin, "scl");
       if (ret)
              goto err_request_scl;
       if (pdata->sda_is_open_drain) {              //If the collector is open
              gpio_direction_output(pdata->sda_pin, 1);//Set Direction to Output
              bit_data->setsda = i2c_gpio_setsda_val;//Setting setsda implementation function
       } else {
              gpio_direction_input(pdata->sda_pin);//If the collector is not open, set the direction as input
              bit_data->setsda = i2c_gpio_setsda_dir;//Setting the implementation function of setsda
       }
       if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {//Open collector and output only
              gpio_direction_output(pdata->scl_pin, 1);//Set direction
              bit_data->setscl = i2c_gpio_setscl_val; //Setting the implementation function of setscl
       } else {        //Collector is not open-circuit
              gpio_direction_input(pdata->scl_pin);
              bit_data->setscl = i2c_gpio_setscl_dir;
       }
       if (!pdata->scl_is_output_only) //Just as output
              bit_data->getscl = i2c_gpio_getscl;
       bit_data->getsda = i2c_gpio_getsda;
       if (pdata->udelay)        //Setting of Delay
              bit_data->udelay = pdata->udelay;
       else if (pdata->scl_is_output_only)
              bit_data->udelay = 50;                 /* 10 kHz */
       else
              bit_data->udelay = 5;                   /* 100 kHz */
       if (pdata->timeout)
              bit_data->timeout = pdata->timeout;
       else
              bit_data->timeout = HZ / 10;         /* 100 ms */
       bit_data->data = pdata;     //Associate bit_data with platform_data
       adap->owner = THIS_MODULE;
       snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
       adap->algo_data = bit_data;  //Specific implementation methods are added to algo_data and called by functions in algo
       adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
       adap->dev.parent = &pdev->dev;
       /*
        * If "dev->id" is negative we consider it as zero.
        * The reason to do so is to avoid sysfs names that only make
        * sense when there are multiple adapters.
        */
       adap->nr = (pdev->id != -1) ? pdev->id : 0;
       ret = i2c_bit_add_numbered_bus(adap);  //Add Entry Bus, defined in i2c-algo-bit.c
       if (ret)
              goto err_add_bus;
       platform_set_drvdata(pdev, adap);//Add adapters to platform s
       return 0;
}

This function is the key to the whole i2c-gpio.c. Almost all the registration of file functions and related functions in algo are related to him. i2c_bit_add_bus: Call i2c_bit_prepare_bus here.

int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
{
       int err;
       err = i2c_bit_prepare_bus(adap); //Here is the correlation function between the algorithm and the adapter, which has been implemented elsewhere.
       if (err)
              return err;
       return i2c_add_numbered_adapter(adap);
}

i2c_bit_prepare_bus: You can see that the original adapter allocated here is combined with the i2c_bit_algo algorithm.

static int i2c_bit_prepare_bus(struct i2c_adapter *adap)
{
       struct i2c_algo_bit_data *bit_adap = adap->algo_data;
       if (bit_test) {
              int ret = test_bus(bit_adap, adap->name);
              if (ret < 0)
                     return -ENODEV;
       }
       /* register new adapter to i2c module... */
       adap->algo = &i2c_bit_algo; //Yes, that's the connection here.
       adap->timeout = 100;     /* default values, should */
       adap->retries = 3;   /* be replaced by defines       */
       return 0;
}

Look back at the i2c_add_numbered_adapter(adap) called in i2c_bit_add_bus:

int i2c_add_adapter(struct i2c_adapter *adapter)
{
       int    id, res = 0;
retry:
       if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
              return -ENOMEM;
       mutex_lock(&core_lock);
       /* "above" here means "above or equal to", sigh */
       res = idr_get_new_above(&i2c_adapter_idr, adapter,
                            __i2c_first_dynamic_bus_num, &id);
       mutex_unlock(&core_lock);
       if (res < 0) {
              if (res == -EAGAIN)
                     goto retry;
              return res;
       }
       adapter->nr = id;
       return i2c_register_adapter(adapter);
}
//At the end of i2c_add_numbered_adapter, i2c_register_adapter(adapter) is called.
static int i2c_register_adapter(struct i2c_adapter *adap)
{
       int res = 0, dummy;
       /* Can't register until after driver model init */
       if (unlikely(WARN_ON(!i2c_bus_type.p)))
              return -EAGAIN;
       mutex_init(&adap->bus_lock);
       mutex_init(&adap->clist_lock);
       INIT_LIST_HEAD(&adap->clients);
       mutex_lock(&core_lock);
       /* Add the adapter to the driver core.
        * If the parent pointer is not set up,
        * we add this adapter to the host bus.
        */
       if (adap->dev.parent == NULL) {
              adap->dev.parent = &platform_bus;
              pr_debug("I2C adapter driver [%s] forgot to specify "
                      "physical device\n", adap->name);
       }
       dev_set_name(&adap->dev, "i2c-%d", adap->nr);
       adap->dev.release = &i2c_adapter_dev_release;
       adap->dev.class = &i2c_adapter_class;
       res = device_register(&adap->dev);
       if (res)
              goto out_list;
       dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
       /* create pre-declared device nodes for new-style drivers */
       if (adap->nr < __i2c_first_dynamic_bus_num)
              i2c_scan_static_board_info(adap); //Statically scan all I2C devices on the bus, and then create an i2c_client structure one by one
       /* Notify drivers */
       dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
                             i2c_do_add_adapter);
out_unlock:
       mutex_unlock(&core_lock);
       return res;
out_list:
       idr_remove(&i2c_adapter_idr, adap->nr);
       goto out_unlock;
}
// Where i2c_scan_static_board_info(adap) calls i2c_new_device:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
       struct i2c_devinfo  *devinfo;
       mutex_lock(&__i2c_board_lock);
       list_for_each_entry(devinfo, &__i2c_board_list, list) {
              if (devinfo->busnum == adapter->nr
                            && !i2c_new_device(adapter,
                                          &devinfo->board_info))  //Here, it creates a new i2c_client device structure based on devinfo
                     printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
                            i2c_adapter_id(adapter),
                            devinfo->board_info.addr);
       }
       mutex_unlock(&__i2c_board_lock);
}
i2c_new_device: 
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
       struct i2c_client     *client;
       int                  status;
       client = kzalloc(sizeof *client, GFP_KERNEL);
       if (!client)
              return NULL;
       client->adapter = adap;
       client->dev.platform_data = info->platform_data;
       if (info->archdata)
              client->dev.archdata = *info->archdata;
       client->flags = info->flags;
       client->addr = info->addr;
       client->irq = info->irq;
       strlcpy(client->name, info->type, sizeof(client->name));
       /* a new style driver may be bound to this device when we
        * return from this function, or any later moment (e.g. maybe
        * hotplugging will load the driver module).  and the device
        * refcount model is the standard driver model one.
        */
       status = i2c_attach_client(client);
       if (status < 0) {
              kfree(client);
              client = NULL;
       }
       return client;
}

In i2c_new_device, the kernel assigns us i2c_client, giving us the name and address of the device we declare in the board-level file.

static struct i2c_board_info i2c_devices[] = {
    {
        I2C_BOARD_INFO("lis35de", 0x1C), //Equipment name and address
    },

Fill in i2c_client. An i2c_client represents an I2C device driven by a device located on an adapter adapter adapter with the address of client - > addr. Here you can use the functions provided by the Linux I2C core that do not depend on the hardware interface, accept/send functions, etc. Perhaps you wonder where the DEVINFO came from when the i2c_client device was created based on devinfo? Aren't we registering i2c_board_info in devices.c? Yes, the i2c_board_info structure is registered in devices.c. Let's look at the i2c_regester_board_info() function that registers the array of structures.

int __init
i2c_register_board_info(int busnum,
       struct i2c_board_info const *info, unsigned len)
{
       int status;
       down_write(&__i2c_board_lock);
       /* dynamic bus numbers will be assigned after the last static one */
       if (busnum >= __i2c_first_dynamic_bus_num)
              __i2c_first_dynamic_bus_num = busnum + 1;
       for (status = 0; len; len--, info++) {
              struct i2c_devinfo  *devinfo;
              devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
              if (!devinfo) {
                     pr_debug("i2c-core: can't register boardinfo!\n");
                     status = -ENOMEM;
                     break;
              }
              devinfo->busnum = busnum;
              devinfo->board_info = *info;
              list_add_tail(&devinfo->list, &__i2c_board_list);
       }
       up_write(&__i2c_board_lock);
       return status;
}

See, in this registration function, it creates a devinfo structure variable, initializes the devinfo variable with bus number and i2c_board_info structure, and then adds a global devinfo list to see the definition of the devinfo structure:

struct i2c_devinfo {
       struct list_head       list;
       int                  busnum;
       struct i2c_board_info     board_info;
};

This registration function is defined in i2c-boardinfo.c. It maintains a global devinfo list, which is used to create i2c_client one by one.

Using I2C subsystem resource function to operate I2C devices

The functions provided by the Linux I2C core are as follows:

1. Add/delete i2c_adapter

Int  i2c_add_adapter(struct i2c_adapter *adap);
Int  i2c_del_adapter(struct i2c_adapter *adap);

2. Add/delete i2c_driver

Int  i2c_register_driver(struct module  *owner,struct i2c_driver *driver);
Int  i2c_del_driver(struct i2c_driver *driver);

3. i2c_client attachment and detachment

Int i2c_attach_client(struct i2c_client *client);
Int i2c_detach_client(struct i2c_client *client);

4. i2c transmission sending receiving

Int i2c_transfer(struct i2c_adaper *adap,struct i2c_msg *msgs,int num);
Int i2c_master_send(struct i2c_client *client,const char *buf,int count);
Int i2c_master_recv(struct i2c_client *client,const char *buf,int count);

The above three functions must first provide pointers to i2c_client in xxx_probe of device driver, and be careful not to use them before extracting i2c_client, otherwise null pointers will appear. Specific usage method: eg:

struct i2c_client *this_client;//Declare global variables;
static int __init st_lis35de_probe(struct i2c_client *client, const struct i2c_device_id * devid)
{

       I2c_set_clientdata(client,&lis35de);
       Client->addr = 0x1c;
    this_client = client;//Extract the client and use it. These three sentences should be put before acc_init.
       acc_init();//This function eventually calls LIS35DE_init(), annotating the function because the GPIO settings for this function have been placed before devices.c gpio-i2c is registered, and there is no need to repeat them here.

}

void LIS35DE_IICWrite(u_int8_t RegAdd, u_int8_t Data ,u_int8_t *result)
{
    char buffer[2];
     *result = 1;
   // buffer[0]=LIS35DE_AddW; // Using functions such as i2c_master_send, there is no need to transfer addresses
    buffer[0]=RegAdd;
    buffer[1]=Data;
    if(i2c_master_send(this_client, buffer,2)<0)
    {
        printk(KERN_ERR "LIS35DE_IICWrite: i2c_master_send error\n");
        return;
    }
     *result = 0;
    return;
}

int8_t LIS35DE_IICRead(u_int8_t RegAdd,u_int8_t *result)
{
     S8 Data;
     *result = 1;
     char buffer[0];
      //buffer[0]=LIS35DE_AddW;
     //Using functions like i2c_master_send, you no longer need to transfer addresses.
      buffer[0]=RegAdd;
      //buffer[2]=LIS35DE_AddR; // Using functions such as i2c_master_send, there is no need to transfer addresses
      if(i2c_master_send(this_client, buffer,1)<0)
      {
        printk(KERN_ERR "LIS35DE_IICRead: i2c_master_send error\n");
        return -1;
      }
     if( i2c_master_recv(this_client, &Data,1)<0)
      {
        printk(KERN_ERR "LIS35DE_IICRead: i2c_master_recv error\n");
        return -1;
      }
     *result = 0;
      return Data;
}
//The following is an example of using the i2c_transfer() function to implement the above function:
static int LIS35DE_RxData(char *rxData, int length)
{
    struct i2c_msg msgs[] = {
        {
         .addr = this_client->addr,
         .flags = 0,
         .len = 1,
         .buf = rxData,
         },
        {
         .addr = this_client->addr,
         .flags = I2C_M_RD,
         .len = length,
         .buf = rxData,
         },
    };
#if DEBUG
    printk(KERN_INFO "%s\n", __FUNCTION__);
#endif
    if (i2c_transfer(this_client->adapter, msgs, 2) < 0) {
        printk(KERN_ERR "LISI2C_RxData: transfer error\n");
        return -EIO;
    } else
        return 0;
}

static int LIS35DE_TxData(char *txData, int length)
{
    struct i2c_msg msg[] = {
        {
         .addr = this_client->addr,
         .flags = 0,
         .len = length,
         .buf = txData,
         },
    };
#if DEBUG
    printk(KERN_INFO "%s\n", __FUNCTION__);
#endif
    if (i2c_transfer(this_client->adapter, msg, 1) < 0) {
        printk(KERN_ERR "LISI2C_TxData: transfer error\n");
        return -EIO;
    } else
        return 0;
}
void LIS35DE_IICWrite(u_int8_t RegAdd, u_int8_t Data ,u_int8_t *result)
{
    char buff[2];
    *result = 1;
//  client->addr = LIS35DE_AddW;
    buff[0] = RegAdd;
    buff[1] = Data;
    if( LIS35DE_TxData(buff, 2) < 0 ) {
#if DEBUG
        printk(KERN_INFO "%s\n", __FUNCTION__);
        printk("# LIS35IIC Write Error #\r\n");
#endif
        return;
    }
        *result = 0;
}

int8_t LIS35DE_IICRead(u_int8_t RegAdd,u_int8_t *result)
{
    S8 Data;
    *result = 1;
    char buff[2];
//  client->addr = LIS35DE_AddR;
    buff[0] = RegAdd;
    if( LIS35DE_RxData(buff, 1) < 0 ) {
#if DEBUG
        printk("# LIS35IIC Read Error #\r\n");
#endif
        return 1;
    }
    *result = 0;
    Data = *buff;
    return Data;
}

Part of it / is simulated by code to simulate the timing of i2c. 6. General Transfer Algorithms of Gpio Analog i2c Bus /drivers/i2c/i2c-algo-bit.c

int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
{
       int err;
       err = i2c_bit_prepare_bus(adap);
   //Some operations before adding adaoter classes include setting timeouts and retries, and setting specific settings for i2c_algorithm.
       if (err)
              return err;
       return i2c_add_numbered_adapter(adap);
}
 static int bit_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgs[], int num)
/*Parameters: Specific adapters
        Data to be transmitted
        Data data
*/
static int bit_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgs[], int num)
{
       i2c_start(adap);
   //Start up bus
       for (i = 0; i < num; i++) {
              pmsg = &msgs[i];
              nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; //Detection of Ignorance of Response
              if (!(pmsg->flags & I2C_M_NOSTART)) {
                     if (i) {
                            i2c_repstart(adap);
                           //If in mixed mode, restart the transmission
                     }
                     ret = bit_doAddress(i2c_adap, pmsg);
                     if ((ret != 0) && !nak_ok) {
                      //If something goes wrong, then something goes wrong.
                            goto bailout;
                     }
              }
              if (pmsg->flags & I2C_M_RD) {
                     //Data collection  
              } else {
                     //send data
                     /* write bytes from buffer */
                     ret = sendbytes(i2c_adap, pmsg);
                     if (ret >= 1)
                     if (ret < pmsg->len) {
                            if (ret >= 0)
                                   ret = -EREMOTEIO;
                            goto bailout;
                     }
              }
       }
       ret = i;
bailout:
       bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
       i2c_stop(adap);
       return ret;
}

static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
       while (count > 0) {
              retval = i2c_outb(i2c_adap, *temp);  //Send a byte of data
              /* OK/ACK; or ignored NAK */
        /*Moving backwards from byte to byte*/
              if ((retval > 0) || (nak_ok && (retval == 0))) {
                     count--;
                     temp++;
                     wrcount++;
}

Clear up the Structural Relations in i2c

Through the above explanations, I have basically introduced all aspects of I2C driver. Perhaps you are still confused about the relationship between many of the structures. Now I will analyze the functions of i2c_driver, i2c_client, i2c_adapter and i2c_algorithm and their disjointed relationships.

  1. i2c_adapter and i2c_algorithm I2c_adapter corresponds to a physical adapter, while i2c_algorithm corresponds to a set of communication methods. An I2C adapter requires communication functions provided in i2c_algorithm to control the generation of specific access cycles on the adapter. Without i2c_algorithm's i2c_adapter, nothing can be done, so i2c_adapter contains its i2c_algorithm pointer. The key function master_xfer() in i2c_algorithm is used to generate the signal needed for the I2C access cycle in the unit of i2c_msg (i.e. I2C message). The i2c_msg structure is also critical, and its definition is given in the code listing.
    struct i2c_msg {
    __u16 addr; /* Device address */
    __u16 flags; /* sign */
    __u16 len;  /* Message length */
    __u8 *buf;  /* Message data */
    };

2. i2c_driver and i2c_client

i2c_driver corresponds to a set of driving methods. It is purely a data structure for auxiliary functions, and it does not correspond to any physical entity. I2c_client corresponds to real physical devices, and each I2C device needs an i2c_client to describe. I2c_client is generally included in the private information structure of I2C character devices. When i2c_driver is associated with i2c_client, the attach_adapter() function of i2c_driver is run. Attach_adapter() detects physical devices, and when a client exists, points the adapter pointer of the i2c_client data structure used by the client to the corresponding i2c_adapter. The driver pointer points to the i2c_driver and calls the client_register() function of the i2c_adapter. The reverse process occurs when the detach_client() function of i2c_driver is called.

3. i2c_adpater and i2c_client

The relationship between i2c_adpater and i2c_client is consistent with that between adapter and device in I2C hardware system, that is, i2c_client is attached to i2c_adpater. Since multiple I2C devices can be connected on an adapter, an i2c_adpater can also be attached by multiple i2c_clients, and i2c_adpater includes a list of i2c_clients attached to it.

i2c-driven writing suggestions

So for a driver engineer, how to write his own i2c-related driver, the following is only a reference scheme:

1. Provide hardware driver of I2C adapter, detect and initialize I2C adapter (such as applying for I2C I/O address and interrupt number), drive CPU-controlled I2C adapter to generate various signals from hardware and process I2C interrupt, etc.

2. Provide the algorithm of I2C adapter, fill the master_xfer pointer of i2c_algorithm with the xxx_xfer() function of specific adapter, and assign the i2c_algorithm pointer to the algo pointer of i2c_adapter.

3. Implement the interface between I2C device driver and i2c_driver, assign the attach_adapter, detach_adapter and detach_client pointers of i2c_driver with yyy_attach_adapter() function pointer, yyy_detach_client() function pointer and yyy_command() function pointer of YYY device.

4. Implementing the file operation interface driven by I2C device, that is, implementing yyy_read(), y yyy_write() and y yyy_ioctl() functions of YYY device. In the above work, 1 and 2 belong to I2C bus driver, 3 and 4 belong to I2C device driver. After these works, the system will add two core modules.

Posted by yoda699 on Thu, 18 Apr 2019 09:45:35 -0700