基於Linux內核的IIC驅動


此例是在一個模塊中註冊了設備和驅動,可以作爲一個快速調試i2c的模塊來用;
 但在一般工程中,有兩種實現方法:
 1,在 drivers/i2c/chips/下創建一個i2c設備,生成一個i2c_client,然後 EXPORT_SYMBOL()導出,在其他地方直接使用,也就是讀寫操作;
 2,在 arch/arm/boot/dts 下創建一個i2c設備,然後在某個地方使用i2c,創建i2c_driver,然後註冊i2c_add_driver,這樣就會在 下找到i2c設備,然後調用i2c_probe;

#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>


//分配初始化設備ID信息
static struct i2c_device_id at24c02_id[] = {
{"at24c02", 0} //這個名稱很重要,用於匹配
};


static struct i2c_client *g_client;


static int major;
static struct class *cls;


/*
unsigned char addr = 0x10;
unsigned char buf[2];
buf[0] = addr;
read(fd, buf, 1);
*/
static ssize_t at24c02_read(struct file *file,
char __user *buf,
size_t count,
loff_t *ppos)
{
/*1.獲取用戶要訪問的地址內容*/
unsigned char addr;
unsigned char data;
unsigned char buffer[2];


copy_from_user(&addr, buf, 1);


/*2.利用SMBUS協議提供的訪問接口,將地址信息
傳遞給I2C總線驅動實現數據傳輸。SMBUS協議
規定的操作接口和使用方法參看文檔:
smbus-protocol,還要利用匹配成功的i2c_client,
因爲i2c_client中有總線驅動的信息還有
設備地址信息
*/
data = i2c_smbus_read_byte_data(g_client, addr);
if (data < 0) {
printk("read error!\n");
return -EIO;
} else {
copy_to_user(buf, &data, 1);
}
return count;
}


/*
unsigned char buf[2];
buf[0] = 0x10;
buf[1] = 89;
write(fd, buf, 2);
*/
static ssize_t at24c02_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
/*1.獲取用戶要操作的地址和數據*/
unsigned char buffer[2];
unsigned char addr;
unsigned char data;
int ret;

copy_from_user(buffer, buf, 2);
addr = buffer[0];
data = buffer[1];

/*2.利用SMBUS協議提供的訪問接口,將地址信息
傳遞給I2C總線驅動實現數據傳輸。SMBUS協議
規定的操作接口和使用方法參看文檔:
smbus-protocol,還要利用匹配成功的i2c_client,
因爲i2c_client中有總線驅動的信息還有
設備地址信息
*/
ret = i2c_smbus_write_byte_data(g_client, addr, data);
if (ret < 0)
return -EIO;

return count;
}


//驅動操作集合
static struct file_operations at24c02_fops = {
.owner = THIS_MODULE, 
.read = at24c02_read,
.write = at24c02_write
};


//如果一旦匹配成功,調用probe函數
//client指針指向匹配成功的設備結構i2c_client,
//可以從中獲取總線驅動和設備地址等信息
static int at24c02_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
//註冊字符設備驅動
major = register_chrdev(major, "at24c02",
&at24c02_fops);


//自動創建設備節點
cls = class_create(THIS_MODULE, "at24c02");
device_create(cls, NULL, MKDEV(major, 0),
NULL, "at24c02");
return 0;
}


static int at24c02_remove(struct i2c_client *client)
{
/*1.刪除設備節點*/
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);


unregister_chrdev(major, "at24c02");
return 0;
}


//分配初始化i2c_driver
static struct i2c_driver at24c02_drv = {
.driver = {
.owner = THIS_MODULE,
.name = "tarena"
},
.probe = at24c02_probe,
.remove = at24c02_remove,
.id_table = at24c02_id
};


static int at24c02_init(void)
{
//存放at24c02設備信息
struct i2c_board_info info;
//獲取總線驅動信息
struct i2c_adapter *adapter;

/*1.註冊i2c_driver*/
/*1.1向總線的drv鏈表添加節點*/
/*1.2遍歷dev鏈表,取出i2c_client,進行匹配*/
i2c_add_driver(&at24c02_drv);


/*2.採用內核方法2來實現i2c_client*/
/*2.1初始化at24c02的硬件信息*/
memset(&info, 0, sizeof(info));
//指定設備名稱,很重要,匹配靠它
strlcpy(info.type, "at24c02", I2C_NAME_SIZE);
//指定設備地址
info.addr = 0x50;


/*2.2 獲取總線驅動信息*/
//參數是at24c02設備所在的總線編號,看原理圖
adapter = i2c_get_adapter(0); 


/*3.實例化i2c_client(分配,初始化,註冊)*/
/*3.1 添加節點i2c_client到dev鏈表*/
/*3.2 遍歷drv鏈表,進行匹配,如果匹配成功,
調用i2c_driver.probe函數,並且
將註冊的i2c_client指針給probe函數
*/
g_client = i2c_new_device(adapter, &info);

return 0;
}


static void at24c02_exit(void)
{
//卸載
i2c_del_driver(&at24c02_drv);
//釋放i2c_client
i2c_unregister_device(g_client);
}


module_init(at24c02_init);
module_exit(at24c02_exit);

MODULE_LICENSE("GPL v2");

**************************************************************************************

                                             測試代碼:

**************************************************************************************

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


/*
./at24c02_test r 0x10 
./at24c02_test w 0x10 97
*/
int main(int argc, char *argv[])
{
unsigned char buf[2];
int fd;


if((argc != 3) && (argc != 4)) {
printf("usage:\n %s r addr\n", argv[0]);
printf("%s w addr data\n", argv[0]);
return -1;
}


fd = open("/dev/at24c02", O_RDWR);
if (fd < 0) {
printf("open at24c02 failed.\n");
return -1;
}


if (strcmp(argv[1], "r") == 0) {
buf[0] = strtoul(argv[2], NULL, 0);
read(fd, &buf[0], 1);
printf("data: %c %d %#x\n", buf[0], buf[0],
buf[0]);
} else if (strcmp(argv[1], "w") == 0) {
buf[0] = strtoul(argv[2], NULL, 0);
buf[1] = strtoul(argv[3], NULL, 0);
write(fd, buf, 2);
}
close(fd);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章