#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
//#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk("%s (%d): ",__FUNCTION__,__LINE__);printk(x);}
#else
#define DPRINTK(x...) (void)(0)
#endif
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD_1(eeprom);
/* Size of EEPROM in bytes */
#define EEPROM_SIZE 256
/* Each client has this additional data */
struct eeprom_data {
struct i2c_client client;
struct cdev dev;
};
static int eeprom_attach_adapter(struct i2c_adapter *adapter);
static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind);//detect:探测
static int eeprom_detach_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver eeprom_driver = {
.driver = {
.name = "eeprom",
},
.id = I2C_DRIVERID_EEPROM,
.attach_adapter = eeprom_attach_adapter,//attach:连接 adapter:转换器
.detach_client = eeprom_detach_client,//detach:分离 client:客户端
};
static ssize_t eeprom_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
struct eeprom_data *data = (struct eeprom_data *)file->private_data;
struct i2c_client *client = &data->client;
struct i2c_msg msg;
int ret;
char *tmp;
if(*offset + count > EEPROM_SIZE)
count = EEPROM_SIZE - *offset;
if (count <= 0)
return -EFAULT;
tmp = kmalloc(count, GFP_KERNEL);
if(tmp == NULL)
return -ENOMEM;
tmp[0] = (*offset) & 0xFF ;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = 1;
msg.buf = tmp;
ret = i2c_transfer(client->adapter, &msg, 1);
if(ret != 1) {
kfree(tmp);
return -EFAULT;
}
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = tmp;
ret = i2c_transfer(client->adapter, &msg, 1);
if(ret == 1)
ret = copy_to_user(buf, tmp, count)? -EFAULT:count;
DPRINTK("%d/n", *tmp);
kfree(tmp);
*offset += count;
return ret;
}
static ssize_t eeprom_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
struct eeprom_data *data = (struct eeprom_data *)file->private_data;
struct i2c_client *client = &data->client;
struct i2c_msg msg;
int ret;
char *tmp;
if(*offset + count > EEPROM_SIZE)
count = EEPROM_SIZE - *offset;
if (count <= 0)
return -EFAULT;
tmp = kmalloc(count+1, GFP_KERNEL);
if(tmp == NULL)
return -ENOMEM;
if(copy_from_user(tmp+1, buf, count)){
kfree(tmp);
return -EFAULT;
}
DPRINTK("%d/n",*buf);
tmp[0] = (*offset) & 0xFF ;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count+1;
msg.buf = tmp;
ret = i2c_transfer(client->adapter, &msg, 1);
kfree(tmp);
*offset += count;
return (ret == 1)? count:ret;
}
static int eeprom_open(struct inode *inode, struct file *file)
{
struct cdev *dev;
struct eeprom_data *data;
dev = inode->i_cdev;
data = container_of(dev, struct eeprom_data, dev);
file->private_data = data;
return 0;
}
static int eeprom_release(struct inode *inode, struct file *file)
{
return 0;
}
static loff_t eeprom_llseek(struct file *file, loff_t off, int whence)
{
loff_t newpos;
switch (whence) {
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = file->f_pos + off;
break;
case SEEK_END:
newpos = EEPROM_SIZE + off;
break;
default:
return -EINVAL;
}
if (newpos < 0 || newpos > EEPROM_SIZE)
return -EINVAL;
file->f_pos = newpos;
return newpos;
}
static struct file_operations eeprom_fops = {
.owner = THIS_MODULE,
.llseek = eeprom_llseek,
.read = eeprom_read,
.write = eeprom_write,
.open = eeprom_open,
.release = eeprom_release,
};
static struct class *my_class;
static struct class_device *class_dev;
static int eeprom_setup(struct eeprom_data *data)
{
int ret = 0;
dev_t devno;
ret = alloc_chrdev_region(&devno, 0, 1, "eeprom");
if (ret) {
DPRINTK("Alloc chrdev region error/n");
return ret;
}
cdev_init(&data->dev, &eeprom_fops);
data->dev.owner = THIS_MODULE;
data->dev.dev = devno;
ret = cdev_add(&data->dev, devno, 1);
if (ret) {
DPRINTK("add cdev error erron=%d/n", ret);
unregister_chrdev_region(devno, 1);
return ret;
}
my_class = class_create(THIS_MODULE, "eeprom");
if (IS_ERR(my_class)) {
ret = PTR_ERR(my_class);
goto error_my_class;
}
class_dev = class_device_create(my_class, NULL,
devno,
NULL, "eeprom");
if (IS_ERR(class_dev)) {
printk("cannot create gpio class device/n");
ret = PTR_ERR(class_dev);
goto error_class_reg;
}
return 0;
error_class_reg:
class_destroy(my_class);
error_my_class:
cdev_del(&data->dev);
return ret;
}
static void eeprom_unset(struct eeprom_data *data)
{
class_device_destroy(my_class, data->dev.dev);
class_destroy(my_class);
cdev_del(&data->dev);
unregister_chrdev_region(data->dev.dev, 1);
}
static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, eeprom_detect);
}
/* This function is called by i2c_probe */
int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct eeprom_data *data;
int err = 0;
/* There are three ways we can read the EEPROM data:
(1) I2C block reads (faster, but unsupported by most adapters)
(2) Consecutive byte reads (100% overhead)
(3) Regular byte data reads (200% overhead)
The third method is not implemented by this driver because all
known adapters support at least the second. */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
| I2C_FUNC_SMBUS_BYTE))
goto exit;
if (!(data = kmalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct eeprom_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &eeprom_driver;
new_client->flags = 0;
/* Fill in the remaining client fields */
strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_kfree;
err = eeprom_setup(data);
if (err)
goto exit_detach;
return 0;
exit_detach:
i2c_detach_client(new_client);
exit_kfree:
kfree(data);
exit:
return err;
}
static int eeprom_detach_client(struct i2c_client *client)
{
int err;
struct eeprom_data *data;
err = i2c_detach_client(client);
if (err)
return err;
data = i2c_get_clientdata(client);
eeprom_unset(data);
kfree(data);
return 0;
}
static int __init eeprom_init(void)
{
int ret;
ret = i2c_add_driver(&eeprom_driver);
if(ret)
return ret;
printk("eeprom initial complete/n");
return 0;
}
static void __exit eeprom_exit(void)
{
i2c_del_driver(&eeprom_driver);
printk("_eeprom_exit/n");
}
MODULE_AUTHOR("liren");
MODULE_DESCRIPTION("I2C EEPROM driver");
MODULE_LICENSE("GPL");
module_init(eeprom_init);
module_exit(eeprom_exit);