2. i2c 總線驅動:實現i2c硬件體系結構中適配器(解決通訊中怎麼發數據)。
3. i2c 設備驅動: 尋找i2c適配器與CPU交換數據(解決通訊中向誰發數據與發什麼數據)。
框架圖:
在linux源碼 driver/i2c目錄下:
i2c-core.c 實現i2c 核心層
i2c-dev.c 實現適配器設備功能
buses文件 實現主機控制器設備驅動
algos文件夾 實現總線適配器通訊方法。
## 幾個重要的數據結構 ##
[code]//i2c_adapter對應一個物理上的適配器 struct i2c_adapter { struct module *owner; unsigned int id; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ struct rt_mutex bus_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ int nr; char name[48]; struct completion dev_released; struct list_head userspace_clients; }; //對應一套i2c總線通訊方法 struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); //一個i2c通訊周期的信號 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); }; //對應一套驅動方法 struct i2c_driver { unsigned int class; /* Notifies the driver that a new bus has appeared or is about to be * removed. You should avoid using this if you can, it will probably * be removed in a near future. */ int (*attach_adapter)(struct i2c_adapter *); int (*detach_adapter)(struct i2c_adapter *); /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ void (*alert)(struct i2c_client *, unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; //驅動支持i2c設備的id表 /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; }; //對應一個真實的i2c物理設備 struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct i2c_driver *driver; /* and our access routines */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head detected; }; //i2c消息結構體 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 */ };
platform_device:
[code]//plat-s2410.c struct platform_device s3c_device_i2c2 = { .name = "s3c2410-i2c", .id = 2, .num_resources = ARRAY_SIZE(s3c_i2c_resource), .resource = s3c_i2c_resource, //i2c寄存器與i2c中斷號 }; //傳給plat_driver的私有數據 static struct s3c2410_platform_i2c default_i2c_data2 __initdata = { .flags = 0, .bus_num = 2, //對應i2c-2 .slave_addr = 0x10, .frequency = 100*1000, .sda_delay = 100, }; platform_driver: static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, //匹配後執行 .remove = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, }, }; //match-id static struct platform_device_id s3c24xx_driver_ids[] = { //id_table { .name = "s3c2410-i2c", .driver_data = TYPE_S3C2410, }, { .name = "s3c2440-i2c", .driver_data = TYPE_S3C2440, }, { }, }; MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);probe函數初始化適配器硬件,時鐘,中斷等資源,最終注冊一個適配器。
[code]static int s3c24xx_i2c_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; //獲取私有數據 i2c->adap.algo = &s3c24xx_i2c_algorithm; //綁定信號傳輸函數 init_waitqueue_head(&i2c->wait); //申請等待隊列 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //獲取i2c控制寄存器 i2c->regs = ioremap(res->start, resource_size(res)); ret = s3c24xx_i2c_init(i2c); //初始化配置寄存器 i2c->irq = ret = platform_get_irq(pdev, 0); //獲取中斷號 ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c); //申請中斷 i2c->adap.nr = pdata->bus_num; //設置適配器的bus_name ret = i2c_add_numbered_adapter(&i2c->adap);//add_adapter到總線,注冊adapter status = i2c_register_adapter(adap); i2c_scan_static_board_info(adap); //掃描總線,適配產生i2c_client list_for_each_entry(devinfo, &__i2c_board_list, list) { if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter,&devinfo->board_info)) //信號傳輸函數 static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, }; //發送iic信號幀 static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num) ret = s3c24xx_i2c_doxfer(i2c, msgs, num); ret = s3c24xx_i2c_set_master(i2c); i2c->msg = msgs; i2c->msg_num = num; s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); //發送start信號 unsigned int addr = (msg->addr & 0x7f) << 1; //注意msg的iic地址 timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); //等待中斷完成 //中斷到來且i2c數據幀傳輸完成,就是喚醒i2c-wait的時候 static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) i2s_s3c_irq_nextbyte(i2c, status); //繼續發送 .......//繼續發送讀寫byte,繼續中斷 s3c24xx_i2c_stop(i2c, 0); //發送stop信號 s3c24xx_i2c_master_complete(i2c, ret); wake_up(&i2c->wait); //喚醒i2c->wait s3c24xx_i2c_disable_irq(i2c);
[code]//在注冊i2c_adapter會掃描__i2c_board_list,根據i2c_devinfo信息 創建帶adapter的適配器,供i2c_driver匹配。 //方法1.在bsp文件中注冊i2c_register_board_info() //方法2.如果內核支持設備樹,在dts文件中添加一個i2c的節點 struct i2c_board_info { char type[I2C_NAME_SIZE]; //名字如 lm75 unsigned short flags; unsigned short addr; //7位地址 void *platform_data; struct dev_archdata *archdata; #ifdef CONFIG_OF struct device_node *of_node; #endif int irq; }; i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); devinfo->busnum = busnum; devinfo->board_info = *info; list_add_tail(&devinfo->list, &__i2c_board_list); //將i2c_board_info加入__i2c_board_list 在注冊i2c_adapter時: static int i2c_register_adapter(struct i2c_adapter *adap) i2c_scan_static_board_info(adap); list_for_each_entry(devinfo, &__i2c_board_list, list) i2c_new_device(adapter,&devinfo->board_info)//產生依附adaper的client struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->adapter = adap; //放adapter到client client->dev.platform_data = info->platform_data; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; status = device_register(&client->dev); //將生成的i2c_client放到i2c_bus上 //在寫i2c_driver時,提供匹配的id_table static struct i2c_device_id id_tables[] = { {"lm75", 0x1111}, {"lm75a", 0x1112}, }; static struct i2c_driver lm75_drv = { .probe = lm75_drv_probe, //在probe中注冊一個fops,來對設備進行讀寫 .remove = lm75_drv_remove, //必須要有 .driver = { .name = "lm75", }, .id_table = id_tables, };