Linux裝置驅動入門----I2C裝置驅動
阿新 • • 發佈:2019-02-04
/* * I2C驅動的一些模板: * (1)、I2C匯流排驅動的的模組載入和解除安裝函式模板 * (2)、I2C匯流排通訊方法 * (3)、I2C裝置驅動模組的載入和解除安裝 * (4)、I2C裝置驅動的檔案操作介面 * (與普通驅動的檔案操作一致,只是要使用 * i2c_client,i2c_driver,i2c_adapter,i2c_algorithm * 結構體和I2C核心,需要協同合作) */ /* * (1) : 初始化I2C介面卡所使用的硬體資源, * 如申請I/O地址,中斷號等。通過i2c_add_adapter() * 新增i2c_adapter的資料結構,當然這個i2c_adapter * 資料結構的成員已經被XXX設配器的相應函式指標所初始化 * 說明:xxx_adapter_hw_init();xxx_adapter_hw_free(); * 函式的實現都與具體的CPU和I2C裝置硬體直接相關。 */ static int __init i2c_adapter_xxx_init(void) { xxx_adapter_hw_init(); i2c_add_adapter(&xxx_adapter); } static void __exit i2c_adapter_xxx_exit(void) { xxx_adapter_hw_free(); i2c_del_adapter(&xxx_adapter); } /* * (2): 主要實現i2c_algorithm 的master_xfer() * 函式和 functionality()函式。 * functionality()函式用於返回algorithm所支援的通訊協議 * master_xfer()函式在I2C介面卡上完成傳遞給它的 * i2c_msg陣列中的每個I2C訊息。 * * 說明:模板中的i2c_adapter_xxx_start(),setaddr(), * wait_ack(),readbytes(),writebytes(),stop()函式用於 * 介面卡的底層硬體操作,與I2C介面卡和CPU的具體硬體直接 * 相關,需要由工程師根據晶片的資料手冊來實現。 * */ static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { ... for(i = 0; i < num; i++) { i2c_adapter_xxx_start(); /* 產生開始位 */ /* 是讀訊息 */ if (msgs[i]->flags & I2C_M_RD) { /* 傳送從裝置讀地址 */ i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); i2c_adapter_xxx_wait_ack(); /* 獲取從裝置的ack */ /* 讀取msgs[i]->len長的資料到msgs[i]->buf */ i2c_adapter_xxx_readbytes(msgs[i]->buf, msgs[i]->len); } else { /* 寫訊息 */ /* 傳送從裝置寫地址 */ i2c_adapter_xxx_setaddr(msg->addr << 1); i2c_adapter_xxx_wait_ack; //獲取從裝置的ack /* 讀取msgs[i]->len長的資料到msgs[i]->buf */ i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len); } } i2c_adapter_xxx_stop(); /* 產生停止位 */ } /* * (3): 通過register_chrdev()函式將I2C設備註冊為一個字元裝置; * 通過I2C核心的i2c_add_driver()函式新增i2c_driver; * */ static int __init yyy_init(void) { int res; /* 註冊字元裝置 */ res = register_chrdev(YYY_MAJOR, "YYY", &yyy_fops);//老核心介面 if (res) goto out; /* 新增i2c_driver */ res = i2c_add_driver(&yyy_driver); if(res) { goto out_unreg_class; } return 0; out_unreg_class: unregister_chrdev(I2C_MAJOR, "i2c"); out: printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); return res; } static void __exit yyy_exit(void) { i2c_del_driver(&i2cdev_driver); unregiister_chrdev(YYY_MAJOR, "YYY"); } /* * i2c_add_driver(&yyy_driver)的執行會引發i2c_driver * 結構體中yyy_attach_adapter()函式的執行,可以在該函 * 數立探測物理裝置,只需要呼叫I2C核心的i2c_probe() * */ static int yyy_attach_adapter(struct i2c_adapter * adapter) { /* * param1:i2c_adapter指標 * param2:要探測的地址資料 * param3:具體的探測函式 */ return i2c_probe(adapter, &addr_data, yyy_detect); } /* * i2c_probe()函式會引發yyy_detect()函式的呼叫, * 可以在yyy_detect()函式中初始化i2c_client */ static int yyy_detect(struct i2c_adapter *adapter, int address, int kind) { struct i2c_client * new_client; struct yyy_data *data; int err = 0; if(!i2c_check_functionality(adapter, I2C_FUNC_XXX)) goto exit; if (!(data = kzalloc(sizeof(struct yyy_data), GFP_KERBEL))) { err = - ENOMEM; goto exit; } new_client = &data->client; new_client->addr = address; new_client->adapter = adapter; new_client->driver = &yyy_driver; new_client->flags = 0; /* 新的client將依附於adapter */ if ((err = i2c_attach_client(new_client))) goto exit_kfree; yyy_init_client(new_client);//此函式是與硬體相關的 return 0; exit_kfree: kfree(data); exit: return err; } /* i2c_detach_client函式 */ static int yyy_detach_client(struct i2c_client *client) { int err; struct yyy_data *data = i2c_get_clientdata(client); if((err = i2c_detach_client(client))) return err; kfree(data); return 0; } /* * yyy_command()的實現,它實現了針對裝置的控制命令; * 具體的控制命令是裝置相關的,對於實時鐘而言,命令 * 將是設定時間和獲取時間, 而對於視訊AD裝置而言, * 命令會是設定取樣方式,選擇通道等。 */ static int yyy_command(struct i2c_client *client, unsigned int cmd, void * arg) { switch(cmd) { case YYY_CMD1: return yyy_cmd1(client, arg); case YYY_CMD2: return yyy_cmd2(client, arg); default: return -EINVAL; } } /* * I2C裝置具體命令處理函式模板 */ static int yyy_cmd1(struct i2c_client *client, struct rtc_time *dt) { struct i2c_msg msg[2]; /* 第一條是寫訊息 */ msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 1; msg[0].buf = &offs; /* 第二條訊息是讀訊息 */ msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].len = sizeof(buf); msg[1].buf = &buf[0]; i2c_transfer(client->adapter, msg, 2); ...; } /* * (4)、I2C裝置檔案的介面寫函式範例 * 具體的寫操作與裝置密切相關 * I2C裝置驅動的檔案操作介面也不是必須的; * 大多數情況下,只需要通過i2c-dev.c檔案 * 提供的I2C介面卡裝置檔案介面久可完成對I2C * 裝置的讀寫。 * */ static ssize_t yyy_write(struct file * file, char *buf, size_t count, loff_t off) { struct i2c_client *client = (struct i2c_client*)file->private_data; i2c_msg msg[1]; char *tmp; int ret; tmp = kmalloc(count, GFP_KERNEL); if (tmp == NULL) return -ENOMEM; if(copy_from_user(tmp, buf, count)) { kfree(tmp); return -EFAULT; } msg[0].addr = client->addr; //地址 msg[0].flags = 0; // 0為寫 msg[0].len = count; //要寫的位元組數 msg[0].buf = tmp; //要寫的資料 ret = i2c_transfer(client->adapter, msg, 1); //傳輸I2C訊息 return (ret == 1) ? count : ret; }