Example of driving module using I2C bus

Source: Internet
Author: User

In embedded systems, I2C interfaces are of great significance. Many peripheral chip control interfaces use I2C. Therefore, it is necessary to understand how to implement I2C bus communication in the driver module. Let's first look at the tree structure of the I2C driver code:

.|-- Kconfig|-- Makefile|-- algos|   |-- Kconfig|   |-- Makefile|   |-- i2c-algo-bit.c|   |-- i2c-algo-pca.c|   |-- i2c-algo-pcf.c|   `-- i2c-algo-pcf.h|-- busses|   |-- Kconfig|   |-- Makefile|   |-- i2c-acorn.c|   |-- ...|   |-- i2c-gpio.c|   |-- ...|   |-- i2c-pxa.c|   |-- i2c-s3c2410.c|   `-- ...|-- chips|   |-- Kconfig|   |-- Makefile|   |-- ds1682.c|   |-- pca963x.c|   `-- tsl2550.c|-- i2c-boardinfo.c|-- i2c-core.c|-- i2c-core.h`-- i2c-dev.c3 directories, 86 files

The i2c-core.c is the core of I2C and provides interface functions. i2c_adapter and i2c_driver call i2c_add_numbered_adapter () and i2c_add_driver () to register them into I2C core respectively during module initialization.

I2c-dev.c is I2C device part, realize character device access interface, specific access to hardware is achieved through i2c_adapter. During initialization, it needs to register an i2c_driver with I2C core. I think this part only allows the user layer to access the I2C bus. If the kernel mode accesses the I2C bus, it is unnecessary to ignore it. But we need to refer to it to implement our own i2c_driver, the i2c_driver must be registered to the I2C core.

There are many files in the busses Directory, which are I2C bus drivers for various platforms, such as s2c2410, PXA, and nmpc, which are closely related to hardware spec. A bus driver requires two modules, which are described by i2c_adapter and i2c_algorithm.

The algos directory is a supplement to i2c_algorithm in the bus. The special i2c_algorithm can be implemented here. Chips is the I2C slave chip driver code, such as the I2C control interface's EEPROM, led segment code display chip, etc.

For more details about the I2C driver architecture, see: http://blog.chinaunix.net/space.php? Uid = 11134731 & Do = Blog & id = 33193

 

The following is the main question: attach the Demo code and copy it for use directly when it comes to related occasions.

#define AZURE_I2C_ADDR   0x1a#define AZURE_I2C_BUS_NO 0static struct i2c_client *azure_i2c;static int azure_i2c_probe(struct i2c_client *i2c,const struct i2c_device_id *id){azure_i2c = i2c;i2c->addr = AZURE_I2C_ADDR;return 0;}static int azure_i2c_remove(struct i2c_client *client){return 0;}static const struct i2c_device_id azure_i2c_id[] = {{ "azure", 0 },{ }};MODULE_DEVICE_TABLE(i2c, azure_i2c_id);static struct i2c_driver azure_i2c_driver = {.driver  = {.name  = "AZURE I2C",.owner = THIS_MODULE,},.probe    = azure_i2c_probe,.remove   = azure_i2c_remove,.id_table = azure_i2c_id,};static int azure_add_i2c_device(){struct i2c_board_info info;struct i2c_adapter *adapter;struct i2c_client *client;int ret;memset(&info, 0, sizeof(struct i2c_board_info));info.addr = AZURE_I2C_ADDR;strlcpy(info.type, "azure", I2C_NAME_SIZE);adapter = i2c_get_adapter(AZURE_I2C_BUS_NO);if (!adapter) {printk(KERN_ERR "can't get i2c adapter %d/n", AZURE_I2C_BUS_NO);goto err_driver;}client = i2c_new_device(adapter, &info);i2c_put_adapter(adapter);if (!client) {printk(KERN_ERR "can't add i2c device at 0x%x/n", (unsigned int)info.addr);goto err_driver;}ret = i2c_add_driver(&azure_i2c_driver);if (ret != 0) {printk(KERN_ERR "can't add i2c driver/n");return ret;}return 0;err_driver:i2c_del_driver(&azure_i2c_driver);return -ENODEV;}

Then, when the module is initialized, call azure_add_i2c_device () to register an i2c_driver to the I2C core, and then call i2c_master_recv () and i2c_master_send () to perform I2C bus read and write operations. The first client parameter of i2c_master_send ()/i2c_master_recv () is passed in by azure_i2c.

These are not smart practices. If there are n modules that require I2C, then the same code should be added to N modules. A better solution is to write an independent module for the kernel to call I2C. In other modules, you only need to call these read/write interface functions. The following is the module code:

#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/i2c.h>#include <linux/smp_lock.h>#define I2C_API_FAKE_ADDR 0x7f#define I2C_MINORS      256int  i2c_api_attach(struct i2c_adapter *adapter);int  i2c_api_detach(struct i2c_adapter *adapter);struct i2c_api {struct list_head list;struct i2c_client *client;};static LIST_HEAD(i2c_api_list);static DEFINE_SPINLOCK(i2c_api_list_lock);static const unsigned short normal_addr[] = { I2C_API_FAKE_ADDR, I2C_CLIENT_END };static const unsigned short ignore[]      = { I2C_CLIENT_END };static struct i2c_client_address_data addr_data = {.normal_i2c = normal_addr,.probe      = ignore,.ignore     = ignore,.forces     = NULL,};static const struct i2c_device_id id[] = {{"I2C-API", 0},{} };      MODULE_DEVICE_TABLE(i2c, id);static struct i2c_driver i2c_api_driver = {.id_table       = id,.attach_adapter = i2c_api_attach,.detach_adapter= i2c_api_detach, .command        = NULL,.driver         = {.name  = "I2C-API",.owner = THIS_MODULE,},.address_data   = &addr_data,};static struct i2c_api *get_i2c_api(int bus_id){struct i2c_api *i2c_api;spin_lock(&i2c_api_list_lock);list_for_each_entry(i2c_api, &i2c_api_list, list) {if (i2c_api->client->adapter->nr == bus_id)goto found;}i2c_api = NULL;found:spin_unlock(&i2c_api_list_lock);return i2c_api;}static struct i2c_api *add_i2c_api(struct i2c_client *client){struct i2c_api *i2c_api;if (client->adapter->nr >= I2C_MINORS) {printk(KERN_ERR "i2c_api: Out of device minors (%d)/n",client->adapter->nr);return NULL;}i2c_api = kzalloc(sizeof(*i2c_api), GFP_KERNEL);if (!i2c_api)return NULL;i2c_api->client = client;spin_lock(&i2c_api_list_lock);list_add_tail(&i2c_api->list, &i2c_api_list);spin_unlock(&i2c_api_list_lock);return i2c_api;}static void del_i2c_api(struct i2c_api *i2c_api){spin_lock(&i2c_api_list_lock);list_del(&i2c_api->list);spin_unlock(&i2c_api_list_lock);kfree(i2c_api);}static int i2c_api_do_xfer(int bus_id, char chip_addr, char sub_addr, int mode, char *buf, unsigned int size){/** you could define more transfer mode here, implement it following. */#define I2C_API_XFER_MODE_SEND            0x0 /* standard send */#define I2C_API_XFER_MODE_RECV            0x1 /* standard receive */#define I2C_API_XFER_MODE_SEND_NO_SUBADDR 0x2 /* send without sub-address */#define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive without sub-address */int ret = 0;char *tmp;struct i2c_api *i2c_api = get_i2c_api(bus_id);if (!i2c_api)return -ENODEV;i2c_api->client->addr = chip_addr;switch (mode) {case I2C_API_XFER_MODE_SEND:tmp = kmalloc(size + 1,GFP_KERNEL);if (tmp == NULL)return -ENOMEM;tmp[0] = sub_addr;memcpy(&tmp[1], buf, size);ret = i2c_master_send(i2c_api->client, tmp, size + 1);ret = (ret == size + 1) ? size : ret;break;case I2C_API_XFER_MODE_RECV:ret = i2c_master_send(i2c_api->client, &sub_addr, 1);if (ret < 0)return ret;ret = i2c_master_recv(i2c_api->client, buf, size);break;case I2C_API_XFER_MODE_SEND_NO_SUBADDR:ret = i2c_master_send(i2c_api->client, buf, size);break;case I2C_API_XFER_MODE_RECV_NO_SUBADDR:ret = i2c_master_recv(i2c_api->client, buf, size);break;default:return -EINVAL;}return ret;}int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size){return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_SEND, buf, size);}int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size){return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_RECV, buf, size);}int i2c_api_attach(struct i2c_adapter *adap){struct i2c_board_info info;struct i2c_client *client;memset(&info, 0, sizeof(struct i2c_board_info));strlcpy(info.type, "i2c_api", I2C_NAME_SIZE);info.addr = I2C_API_FAKE_ADDR;client = i2c_new_device(adap, &info);if (client)add_i2c_api(client);printk(KERN_INFO "i2c_api_attach adap[%d]/n", adap->nr);return 0;}int i2c_api_detach(struct i2c_adapter *adap){struct i2c_api *i2c_api;i2c_api = get_i2c_api(adap->nr);if (i2c_api)del_i2c_api(i2c_api);return 0;}static int __init i2c_api_init(void){int ret = i2c_add_driver(&i2c_api_driver);if (ret) {printk(KERN_ERR "[%s] Driver registration failed, module not inserted./n", __func__);return ret;}return 0 ;}static void __exit i2c_api_exit(void){i2c_del_driver(&i2c_api_driver);}MODULE_AUTHOR("Loon, <sepnic@gmail.com>");MODULE_DESCRIPTION("I2C i2c_api Driver");MODULE_LICENSE("GPL");module_init(i2c_api_init);module_exit(i2c_api_exit);EXPORT_SYMBOL_GPL(i2c_api_do_send);EXPORT_SYMBOL_GPL(i2c_api_do_recv);

The kernel version is 2.6.32. The i2c_driver struct members attach_adapter and detach_adapter may be added or deleted later. Then, you need to adjust i2c_api_attach () and i2c_api_detach () in the module.

After this module is loaded, other modules can call i2c_api_do_recv ()/i2c_api_do_send () to read and write the I2C bus.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.