#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/jiffies.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/delay.h>
#define DEVICE_NAME "at24c02"
#define I2C_MINORS 250
static int at24c08b_major = I2C_MINORS;
static struct class *i2c_dev_class;
static struct i2c_client *my_client;
struct class *at24_class;
struct at24c08b_dev
{
struct i2c_client *client;
char name[30];
unsigned short current_pointer;
struct cdev cdev;
};
struct at24c08b_dev *at24c08b_devp;
static struct i2c_device_id at24xx_ids[]=
{
{"at24c01",0x50},
{"at24c02",0x50},
{"at24c04",0x50},
{"at24c08",0x50},
{},
};
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
char *tmp;
char reg_addr=0;
int ret;
printk(KERN_ERR "i2cdev_read\n");
if (count > 8192)
count = 8192;
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
struct i2c_msg msg[2];
struct i2c_client *client= (struct i2c_client *)file->private_data;
struct i2c_adapter *adap = client->adapter;
reg_addr = *offset;
printk(KERN_ERR"read:count = %d,offset = %ld\n",count,*offset);
msg[0].addr = client->addr;
msg[0].flags = client->flags & I2C_M_TEN;
msg[0].len = 1;
msg[0].buf = ®_addr;//read addr
msg[1].addr = client->addr;
msg[1].flags = client->flags & I2C_M_TEN;
msg[1].flags |= I2C_M_RD;
msg[1].len = count;
msg[1].buf = tmp;//read data
ret = i2c_transfer(adap, msg, 2);
printk(KERN_ERR "i2c_transfer:ret=%d\n",ret);
if (ret != 2)
goto out;
ret = copy_to_user(buf, tmp, count);
printk(KERN_ERR "copy_to_user:ret=%d\n",ret);
if(ret)
goto out;
kfree(tmp);
return count;
out:
kfree(tmp);
printk(KERN_ERR "i2cdev_read out:\n");
return -1;
}
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
int ret;
int i;
char *tmp;
char tmp_data[2];
struct i2c_client *client = file->private_data;
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
printk(KERN_ERR "i2cdev_write\n");
if (count > 8192)
count = 8192;
printk(KERN_ERR"write:count = %d,offset = %ld\n",count,*offset);
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
if (copy_from_user(tmp, buf, count)) {
kfree(tmp);
printk(KERN_ERR"copy_from_user falied\n");
return -EFAULT;
}
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
for(i=0; i< count; i++)
{
tmp_data[0] = *offset +i;
tmp_data[1] = *(tmp+i);
msg.len = 2;
msg.buf = tmp_data;
ret = i2c_transfer(adap, &msg, 1);
if(1 != ret)
{
printk(KERN_ERR"i2cdev_write:i2c_transfer !=1 ret=%d\n",ret);
goto out;
}
}
kfree(tmp);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
out:
kfree(tmp);
printk(KERN_ERR"in out ret=%d\n",ret);
return -1;
}
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data;
printk(KERN_ERR "i2cdev_ioctl, cmd=0x%02x, arg=0x%02lx\n",cmd, arg);
switch (cmd) {
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
/* For historical reasons, user-space sets the timeout
* value in units of 10 ms.
*/
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
/* NOTE: returning a fault code here could cause trouble
* in buggy userspace code. Some old kernel bugs returned
* zero in this case, and userspace code might accidentally
* have depended on that bug.
*/
return -ENOTTY;
}
return 0;
}
static int i2cdev_open(struct inode *inode, struct file *file)
{
printk(KERN_ERR "i2cdev_open\n");
file->private_data = my_client;
return 0;
}
static int i2cdev_release(struct inode *inode, struct file *file)
{
//struct i2c_client *client = file->private_data;
printk(KERN_ERR "i2cdev_release\n");
file->private_data = NULL;
return 0;
}
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
static void at24c08b_setup_cdev (struct at24c08b_dev *dev, int index)
{
int err, devnum = MKDEV (I2C_MINORS, index);
cdev_init (&dev->cdev, &i2cdev_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add (&dev->cdev, devnum, 1);
if (err)
printk (KERN_NOTICE "Error %d adding at24c08b %d", err, index);
}
static int at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int res;
int ret;
printk (KERN_NOTICE "at24c08b probe is start\n"); //调试用,看是否执行了probe 函数
dev_t devnum = MKDEV (at24c08b_major, 0);
if (I2C_MINORS)
ret = register_chrdev_region (devnum, 1, "lei24c08");
else
{
ret = alloc_chrdev_region (&devnum, 0, 1, "lei24c08");
at24c08b_major = MAJOR (devnum);
}
if (ret < 0)
return ret;
at24c08b_devp = kmalloc (sizeof (struct at24c08b_dev), GFP_KERNEL);
if (!at24c08b_devp)
{
ret = -ENOMEM;
goto fail_malloc;
}
memset (at24c08b_devp, 0, sizeof (struct at24c08b_dev));
at24c08b_devp->client = client;
at24c08b_setup_cdev (at24c08b_devp, 0);
at24_class = class_create(THIS_MODULE, "at24_class");
if (IS_ERR(at24_class)) {
printk(DEVICE_NAME " failed in creating class./n");
return -1;
}
device_create(at24_class, NULL, devnum, NULL, "at24c02-%d", 0);
return 0;
fail_malloc:
unregister_chrdev_region (devnum, 1);
return ret;
}
static int at24xx_remove(struct i2c_client *client)
{
printk(KERN_ERR "at24xx_remove\n");
device_destroy(i2c_dev_class, MKDEV(I2C_MINORS, 0));
return 0;
}
static struct i2c_driver at24cxx_driver = {
.driver = {
.name = "lei24c08",
},
.probe = at24cxx_probe,
.id_table = at24xx_ids,
.remove = at24xx_remove,
};
/*
*at24cxx init
*/
static int __init at24cxx_init(void)
{/*
int res;
printk(KERN_ERR "at24cxx_init\n");
res = register_chrdev(I2C_MINORS, DEV_NAME, &i2cdev_fops);
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
res = i2c_add_driver(&at24cxx_driver);
if (res)
goto out_unreg_class;
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MINORS, "at24c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
*/
printk (KERN_NOTICE "at24c08b is insmod\n");
return i2c_add_driver (&at24cxx_driver);
}
static void __exit at24cxx_exit(void)
{
printk (KERN_NOTICE "at24c08b is rmmod\n");
i2c_del_driver (&at24cxx_driver);
}
MODULE_AUTHOR("yiheng <yihengzuji@163.com>");
MODULE_DESCRIPTION("I2C at24cxx entries driver");
MODULE_LICENSE("GPL");
module_init(at24cxx_init);
module_exit(at24cxx_exit);