#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/smp_lock.h>
#include <linux/jiffies.h>
#include <linux/slab.h> /* kzalloc() */
#include <linux/sysfs.h> /* sysfs_create_group() */
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/string.h>
struct e2prom_priv_t {
u8 dev_addr;
u8 reg;
u8 data;
};
struct e2prom_priv_t *e2prom_priv=NULL;
#define E2PROM_ADDR 0x50
#define EEPROM_RDWR 0X4401
static struct i2c_client *this_client = NULL;
static struct cdev *e2prom_dev ;
static const struct i2c_device_id e2prom_i2c_id[] = {
{ "e2prom", 0 }
};
#define DEV_MAJOR 254
static int major=DEV_MAJOR;
static int e2prom_open(struct inode *inode,struct file *filp )
{
struct e2prom_priv_t *data=&e2prom_priv;
filp->private_data = data ;
try_module_get(THIS_MODULE);
return 0;
}
static int e2prom_release(struct inode *inode,struct file *filp)
{
filp->private_data = NULL;
module_put(THIS_MODULE);
return 0;
}
static int e2prom_write_read(struct i2c_client client,u8 reg, u8 *value,int mode)
{
u8 data[2];
BUG_ON(client);
struct i2c_msg msg[] = {
{
.addr = client->addr,
//.flags = 0;//I2C_M_RD,
.len = 2,
.buf[0] = reg,
.buf[1] = *value,
},
};
if(mode==0){
msg[0].flags = I2C_M_WR;;
ret = i2c_transfer(client->adapter, msg, 1);
if (ret < 0)
printk("%s i2c read error: %d\n", __func__, ret);
return (ret == 1) ? length : ret;
}
else
{
msg[0].flags = I2C_M_RD;;
ret = i2c_transfer(client->adapter, msg, 1);
if (ret < 0)
printk("%s i2c read error: %d\n", __func__, ret);
return (ret == 1) ? length : ret;
}
// return 0;
}
static int e2prom_wr(struct file * filp, const char * buffer, size_t count, loff_t * ppos)
{
//struct e2prom_priv_t *wm8731=(struct e2prom_priv_t *)filp->private_data;
char *tmp;
int ret;
u16 val;
tmp = kzalloc(count,GFP_KERNEL);
if (tmp==NULL)
return -ENOMEM;
if (copy_from_user(tmp,buffer,count)) {
kfree(tmp);
return -EFAULT;
}
ret = e2prom_write_read(this_client,tmp[0],&val,0);
kfree(tmp);
return ret;
}
static int e2prom_rd(struct file * filp, const char * buffer, size_t count, loff_t * ppos)
{
// struct e2prom_priv_t *wm8731=(struct e2prom_priv_t *)filp->private_data;
char *tmp;
int ret;
u16 val;
tmp = kzalloc(count,GFP_KERNEL);
if (tmp==NULL)
return -ENOMEM;
ret = e2prom_write_read(this_client,tmp[0],&val,1);
tmp[0]= val;
if (copy_to_user(buffer,tmp,count)) {
kfree(tmp);
return -EFAULT;
}
kfree(tmp);
return ret;
}
static int e2prom_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long ioctl_param)
{
int ret=-1;
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_smbus_ioctl_data data_arg;
struct i2c_msg *rdwr_pa;
u8 __user **data_ptrs;
switch(cmd){
case EEPROM_RDWR:
// do it yourself
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
/* Put an arbitrary limit on the number of messages that can
* be sent at once */
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;
rdwr_pa = (struct i2c_msg *)
kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
GFP_KERNEL);
if (rdwr_pa == NULL) return -ENOMEM;
if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return -EFAULT;
}
data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}
ret = 0;
for( i=0; i<rdwr_arg.nmsgs; i++ ) {
/* Limit the size of the message to a sane amount */
if (rdwr_pa[i].len > 8192) {
ret = -EINVAL;
break;
}
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
if(rdwr_pa[i].buf == NULL) {
ret = -ENOMEM;
break;
}
if(copy_from_user(rdwr_pa[i].buf,
data_ptrs[i],
rdwr_pa[i].len)) {
++i; /* Needs to be kfreed too */
ret = -EFAULT;
break;
}
}
if (ret < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return ret;
}
ret = i2c_transfer(client->adapter,
rdwr_pa,
rdwr_arg.nmsgs);
while(i-- > 0) {
if( ret>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if(copy_to_user(
data_ptrs[i],
rdwr_pa[i].buf,
rdwr_pa[i].len)) {
ret = -EFAULT;
}
}
kfree(rdwr_pa[i].buf);
}
kfree(data_ptrs);
kfree(rdwr_pa);
return ret;
break;
default:
break;
}
return ret;
}
static struct file_operations e2prom_fops = {
owner: THIS_MODULE,
read: e2prom_rd,
write: e2prom_wr,
ioctl: e2prom_ioctl,
open: e2prom_open,
release: e2prom_release,
};
static int e2prom_register()
{
int ret;
static dev_t dev= MKDEV(DEV_MAJOR,0);
if(major)
{
ret= register_chrdev_region(dev,1,"eeprom");
if(ret){
return -EFAULT;
}
}
else{
ret= alloc_chrdev_region(dev, 0, 1,"eeprom");
if(ret){
return -EFAULT;
}
}
e2prom_dev =cdev_alloc();
cdev_init(e2prom_dev,e2prom_fops);
e2prom_dev->owner= THIS_MODULE;
ret= cdev_add(e2prom_dev,dev,1);
return ret;
}
static int e2prom_unregister()
{
static dev_t dev= MKDEV(DEV_MAJOR,0);
if( e2prom_dev)
cdev_del(e2prom_dev);
unregister_chrdev_region(dev,1);
return 0;
}
#define E2PROM_ADDR 0x50
static struct i2c_client *this_client = NULL;
static int e2prom_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
e2prom_priv = kzalloc(sizeof(struct e2prom_priv_t), GFP_KERNEL);
if (e2prom_priv == NULL)
return -ENOMEM;
e2prom_priv->addr = E2PROM_ADDR;
if(client->addr != e2prom_priv->addr)
client->addr = e2prom_priv->addr;
this_client = client;
i2c_set_clientdata(client, e2prom_priv);
return 0;
}
static int e2prom_i2c_remove(struct i2c_client *client)
{
struct e2prom_priv_t *e2prom_priv = i2c_get_clientdata(client);
i2c_set_clientdata(client, NULL);
if(e2prom_priv) kfree(e2prom_priv);
return 0;
}
static struct i2c_driver e2prom_i2c_driver = {
.probe = e2prom_i2c_probe,
.remove = e2prom_i2c_remove,
.id_table = e2prom_i2c_id,
.driver = {
.name = "EEPROM",
.owner = THIS_MODULE,
},
};
static int __init e2prom_drv_init(void)
{
int ret;
e2prom_register();
ret = i2c_add_driver(&e2prom_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", ret);
}
return 0;
}
static void __exit e2prom_drv_exit(void)
{
i2c_del_driver(&e2prom_i2c_driver);
e2prom_unregister();
}
MODULE_LICENSE("GPL");
module_init(e2prom_drv_init);
module_exit(e2prom_drv_exit);