EEPROM 驱动(module)程序

来源:互联网 发布:哪些明星有淘宝店 编辑:程序博客网 时间:2024/05/23 13:55

#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);

原创粉丝点击