1,mach-x210.c
/* I2C0 */
static struct i2c_board_info i2c_devs0[] __initdata = {
#ifdef CONFIG_SND_SOC_WM8580
{
I2C_BOARD_INFO("wm8580", 0x1b),
},
#endif
#ifdef CONFIG_EEPROM_at240cxx
{
I2C_BOARD_INFO("at240c64", (0xa0>>1)),
},
#endif
};
2, at24cxx_e2prom_i2c_dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define AT24C16 2047
#define AT24C64 8191
#define EE_TYPE AT24C64
#define IIC_ADDR 0x50
//设计一个驱动针对不同的设备的不同数据(私有)
struct e2prom_private{
char *name; //描述
int size; //容量
int version;//版本
};
int at24_i2c_e2prom_open (struct inode *inode, struct file *filp)
{
printk("------%s--------\n", __FUNCTION__);
//一般也是做初始化
return 0;
}
//设计一个全局的设备对象
struct i2c_e2prom_global{
struct i2c_client *client;//描述了一个从设备的信息---从设备地址,名字
struct e2prom_private *private;
struct miscdevice i2c_misc;
};
//全局的指针
struct i2c_e2prom_global *at24_dev;
//i2c_master_recv(const struct i2c_client * client, char * buf, int count);
int at24_i2c_recv(const struct i2c_client * client, char * buf, int count)
{
int ret;
struct i2c_adapter *adapter = client->adapter;//描述控制器, 比如编号,算法
struct i2c_msg msg;//传输的数据包
msg.addr = client->addr;//数据包传输給哪个从设备
msg.flags = I2C_M_RD; //读还是写-- 1读,0写
msg.len = count;//数据大小bytes
msg.buf = buf;
//参数1--适配器,来自于client
//参数2--传输的消息体--数据包
//参数3--消息体的个数
//返回值--正确为消息的个数,错误为负数
//i2c_transfer(struct i2c_adapter * adap, struct i2c_msg * msgs, int num)
ret = i2c_transfer(adapter, &msg, 1);
return ret==1?count:ret;
}
//重写类似于i2c_master_send/i2c_master_recv的方法
int at24_i2c_send(struct i2c_client *client,char *buf, int count)
{
int ret;
//i2c_adapter描述控制器,比如编号,算法
struct i2c_adapter *adapter = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.buf = buf;
msg.flags = 0;
msg.len = count;
//参数1--适配器,来自于client
//参数2--传输的消息体--数据包
//参数3--消息体的个数
ret = i2c_transfer(adapter, &msg, 1);
return ret==1?count:ret;
}
ssize_t at24_i2c_e2prom_read (struct file *filp, char __user *buf, size_t count, loff_t *fops)
{
size_t ee_addr;
printk("------%s--------\n", __FUNCTION__);
int ret;
char temp2[4];
//简单的参数判断
if(count <0 || count > at24_dev->private->size)
return -EINVAL;
//动态的分配一个空间,用于存放从硬件获取到的数据
char *temp = kzalloc(count, GFP_KERNEL);
// 1,从用户空间得到数据
ret = copy_from_user(temp, buf, count);
if(ret > 0)
{
printk("copy_from_user error\n");
ret = -EFAULT;
goto err_free;
}
ee_addr = (temp[0]<<8)+temp[1];
//i2c_master_recv(const struct i2c_client * client, char * buf, int count);
//i2c_master_send(const struct i2c_client * client, const char * buf, int count);
// 1,从硬件中获取数据
//printk("client addr = %X\n", client->addr);
if(EE_TYPE>AT24C16)
{
temp2[0] = ee_addr>>8;
temp2[1] = ee_addr%256;
at24_i2c_send(at24_dev->client,temp2,2);
}
else
{
at24_dev->client->addr = IIC_ADDR+((ee_addr/256));
at24_i2c_send(at24_dev->client,temp2,1);
temp2[0] = ee_addr%256;
at24_dev->client->addr = IIC_ADDR;
}
ret = at24_i2c_recv(at24_dev->client, temp, count);
if(ret < 0)
{
printk("at24_i2c_recv error\n");
goto err_free;
}
// 2, 将数据传递给用户
ret = copy_to_user(buf, temp, count);
if(ret > 0)
{
printk("copy to user error\n");
ret = -EFAULT;
goto err_free;
}
kfree(temp);
return count;
err_free:
kfree(temp);
return ret;
}
ssize_t at24_i2c_e2prom_write (struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
size_t ee_addr;
printk("------%s--------\n", __FUNCTION__);
int ret;
//简单的参数判断
if(count < 0 || count > at24_dev->private->size)
return -EINVAL;
//动态的分配一个空间
char *temp = kzalloc(count, GFP_KERNEL);
// 1,从用户空间得到数据
ret = copy_from_user(temp, buf, count);
if(ret > 0)
{
printk("copy_from_user error\n");
ret = -EFAULT;
goto err_free;
}
ee_addr = (temp[0]<<8)+temp[1];
if(EE_TYPE>AT24C16)
{
ret = at24_i2c_send(at24_dev->client,temp,count);
}
else
{
at24_dev->client->addr = IIC_ADDR+((ee_addr/256));
temp[1] = ee_addr%256;
ret = at24_i2c_send(at24_dev->client,temp+1,count-1);
at24_dev->client->addr = IIC_ADDR;
}
// 2, 将数据写入到硬件
//ret =at24_i2c_send(at24_dev->client, temp+2, count);
if(ret < 0)
{
printk("at24_i2c_send error\n");
goto err_free;
}
kfree(temp);
return count;
err_free:
kfree(temp);
return ret;
}
int at24_i2c_e2prom_close (struct inode *inode, struct file *flip)
{
printk("------%s--------\n", __FUNCTION__);
return 0;
}
const struct file_operations at24_i2c_fops = {
.open = at24_i2c_e2prom_open,
.read = at24_i2c_e2prom_read,
.write = at24_i2c_e2prom_write,
.release = at24_i2c_e2prom_close,
};
int at24_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)//为什么是id
{
printk("-------%s-------\n",__FUNCTION__);
printk("client name = %s\n", id->name);//
printk("client addr = %X\n", client->addr);
struct e2prom_private *p = (struct e2prom_private *)id->driver_data;
printk("name descriptoin : %s \n", p->name);
printk("size : %d \n", p->size);
printk("verison : 0x%x \n", p->version);
at24_dev = kzalloc(sizeof(struct i2c_e2prom_global), GFP_KERNEL);//GFP_KERNEL
//记录当前匹配之后的i2c client
at24_dev->client = client;
at24_dev->private = (struct e2prom_private *)id->driver_data;
//实现fops
at24_dev->i2c_misc.fops = &at24_i2c_fops;
at24_dev->i2c_misc.minor = 199; //MISC_DYNAMIC_MINOR,
at24_dev->i2c_misc.name = "i2c_e2prom"; //指定设备节点的名字 /dev/i2c_e2prom
//注册一个混杂设备驱动,函数里面已经封装了申请设备号,创建设备文件等过程
misc_register(&at24_dev->i2c_misc);
// 4,根据实际情况去初始化从设备硬件---at24 上电就可以工作
return 0;
}
int at24_i2c_remove(struct i2c_client *client)
{
printk("------%s--------\n", __FUNCTION__);
misc_deregister(&at24_dev->i2c_misc);
kfree(at24_dev);
return 0;
}
struct e2prom_private c02_private = {
.name = "at240c02 in test LZH",
.size = 256,
.version = 0x1234,
};
struct e2prom_private c04_private = {
.name = "at240c04 in test LZH",
.size = 512,
.version = 0x4444,
};
struct e2prom_private c08_private = {
.name = "at240c08 in test LZH",
.size = 2014,
.version = 0x4321,
};
struct e2prom_private c64_private = {
.name = "at240c08 in test LZH",
.size = 8192,
.version = 0x1111,
};
//名字必须与内核中的arch/arm/mach-s5pv210/mach-smdkv210.c中i2c_board_info结构体对应
const struct i2c_device_id at24_i2c_id_table[] = {
{"at240c2",(unsigned long)&c02_private},//注意
{"at240c4",(unsigned long)&c04_private},
{"at240c8",(unsigned long)&c08_private},
{"at240c64",(unsigned long)&c64_private},
};
//描述了一个从设备的操作方法
struct i2c_driver at24_i2c_drv = {
.probe = at24_i2c_probe,
.remove = at24_i2c_remove,
.driver = { //父类,一定要初始化
.name = "at24_drv_LZH", //该名字不会用于进行匹配#
}, //主要的作用是用来产生一个文件 /sys/bus/i2c/drivers/at24_drv
.id_table = at24_i2c_id_table, //用于和i2c client进行匹配的名字列表
};
static int __init at24_drv_init(void)
{
//注册一个i2c driver
return i2c_add_driver(&at24_i2c_drv);
}
static void __exit at24_drv_exit(void)
{
i2c_del_driver(&at24_i2c_drv);
}
module_init(at24_drv_init);
module_exit(at24_drv_exit);
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
3, \drivers\misc\eeprom\Makefile
obj-$(CONFIG_EEPROM_at240cxx) += at24cxx_e2prom_i2c_drv.o
4,\drivers\misc\eeprom\Kconfig
config EEPROM_at240cxx
tristate "EEPROM at240cxx support"
help
This is a driver for the EEPROM chipsets at240cxx.
The driver supports both read as well as write commands.
If unsure, say N.
5, 将i2c core层和i2c adapter控制器层编译进内核
make menuconfig
Device Drivers —>
<*> I2C support —> // i2c-core.c
I2C Hardware Bus support —>
<*> S3C2410 I2C Driver
Device Drivers —>
[*] Misc devices --->
EEPROM support --->
<*> EEPROM at240cxx support
6,app应用。
#include <linux/i2c-dev.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void print_usage(char *str)
{
printf("%s r : read at24c02 addresss 0\n", str);
printf("%s w val : write at24c02 addresss 0\n", str);
}
int main(int argc,char **argv)
{
int fd;
unsigned char val;//字节
int i;
int res;
char wbuf[10];
char rbuf[10];
if (argc < 2)
{
print_usage(argv[0]);
exit(1);
}
/*打开设备文件*/
fd = open("/dev/i2c_e2prom", O_RDWR);
if (fd < 0)
{
perror("open failed");
exit(1);
}
if (strcmp(argv[1], "r") == 0)
{
rbuf[0] = 0;//地址高8位
rbuf[1] = 16;//地址第8位
if (read(fd, rbuf, 32) != 32)
{
perror("read failed");
exit(1);
} else {
printf("rbuf:");
for(i=0;i<32;i++)
printf("%x ",rbuf[i]);
printf("\n\n");
}
}
else if ((strcmp(argv[1], "w") == 0) && (argc == 3))
{
// ./test w 0x99
val = strtoul(argv[2], NULL, 0);
wbuf[0] = 0; //地址高8位
wbuf[1] = 16; //地址低8位
wbuf[2] = val; //数据
wbuf[3] = 0x1b;
wbuf[4] = 0x2b;
wbuf[5] = 0x3b;
wbuf[6] = 0x4b;
if (write(fd, wbuf, 7)!=5)
{
perror("write failed");
exit(1);
}
printf("write data ok!\n");
}
close(fd);
return 0;
}