如何在kernel中访问Eeprom中内容

要在kernel中访问Eeprom中内容,最常规的做法就是open这个device,在read/write,但是在kernel中,有的drvier init的时候有可能
eeprom这个设备节点还没有生成,这样的话就没办法用上面的方法来完成了,下面介绍一种使用at24.c(i2c接口的Eeprom)内部的
macc_read函数来完成.
下面以Ethernet driver 要从eeprom中获取mac address 为例来介绍用法.
1. 首先在Ethernet driver 中添加如下代码
#include
void omap4_get_mac_addr(struct memory_accessor *mem_acc, void *context)

    char mac_addr[6];
    int i;

    mem_acc->read(mem_acc, mac_addr, ETHERNET_MACADDR_OFFSET, ETH_ALEN);
    for (i = 0; i < 6; i++) {
            printk("mac_addr[%d]=0x%x\n",i,mac_addr[i]);
        }
}

2. 然后在board file 中添加如下代码
extern void omap4_get_mac_addr(struct memory_accessor *mem_acc, void *context);
static struct at24_platform_data pca9500_eeprom_pdata = {
 .byte_len   = 256,
 .page_size  = 4,
 .setup      = omap4_get_mac_addr,
    .context    = (void *)0x7f00,
};

3. 我们来看在at24的probe 函数中, 我们可以看到
 /* export data to kernel code */
 if (chip.setup)
  chip.setup(&at24->macc, chip.context);
这样at24 probe 的时候就会调用setup 函数,也就是我们在Ethernet driver 中的函数从eeprom中获取了内容.

static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
 struct at24_platform_data chip;
 bool writable;
 int use_smbus = 0;
 struct at24_data *at24;
 int err;
 unsigned i, num_addresses;
 kernel_ulong_t magic;

 if (client->dev.platform_data) {
  chip = *(struct at24_platform_data *)client->dev.platform_data;
 } else {
  if (!id->driver_data) {
   err = -ENODEV;
   goto err_out;
  }
  magic = id->driver_data;
  chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
  magic >>= AT24_SIZE_BYTELEN;
  chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
  /*
   * This is slow, but we can't know all eeproms, so we better
   * play safe. Specifying custom eeprom-types via platform_data
   * is recommended anyhow.
   */
  chip.page_size = 1;

  /* update chipdata if OF is present */
  at24_get_ofdata(client, &chip);

  chip.setup = NULL;
  chip.context = NULL;
 }

 if (!is_power_of_2(chip.byte_len))
  dev_warn(&client->dev,
   "byte_len looks suspicious (no power of 2)!\n");
 if (!chip.page_size) {
  dev_err(&client->dev, "page_size must not be 0!\n");
  err = -EINVAL;
  goto err_out;
 }
 if (!is_power_of_2(chip.page_size))
  dev_warn(&client->dev,
   "page_size looks suspicious (no power of 2)!\n");

 /* Use I2C operations unless we're stuck with SMBus extensions. */
 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
  if (chip.flags & AT24_FLAG_ADDR16) {
   err = -EPFNOSUPPORT;
   goto err_out;
  }
  if (i2c_check_functionality(client->adapter,
    I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
   use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
  } else if (i2c_check_functionality(client->adapter,
    I2C_FUNC_SMBUS_READ_WORD_DATA)) {
   use_smbus = I2C_SMBUS_WORD_DATA;
  } else if (i2c_check_functionality(client->adapter,
    I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
   use_smbus = I2C_SMBUS_BYTE_DATA;
  } else {
   err = -EPFNOSUPPORT;
   goto err_out;
  }
 }

 if (chip.flags & AT24_FLAG_TAKE8ADDR)
  num_addresses = 8;
 else
  num_addresses = DIV_ROUND_UP(chip.byte_len,
   (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);

 at24 = kzalloc(sizeof(struct at24_data) +
  num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
 if (!at24) {
  err = -ENOMEM;
  goto err_out;
 }

 mutex_init(&at24->lock);
 at24->use_smbus = use_smbus;
 at24->chip = chip;
 at24->num_addresses = num_addresses;

 /*
  * Export the EEPROM bytes through sysfs, since that's convenient.
  * By default, only root should see the data (maybe passwords etc)
  */
 sysfs_bin_attr_init(&at24->bin);
 at24->bin.attr.name = "eeprom";
 at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
 at24->bin.read = at24_bin_read;
 at24->bin.size = chip.byte_len;

 at24->macc.read = at24_macc_read;

 writable = !(chip.flags & AT24_FLAG_READONLY);
 if (writable) {
  if (!use_smbus || i2c_check_functionality(client->adapter,
    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {

   unsigned write_max = chip.page_size;

   at24->macc.write = at24_macc_write;

   at24->bin.write = at24_bin_write;
   at24->bin.attr.mode |= S_IWUSR;

   if (write_max > io_limit)
    write_max = io_limit;
   if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
    write_max = I2C_SMBUS_BLOCK_MAX;
   at24->write_max = write_max;

   /* buffer (data + address at the beginning) */
   at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
   if (!at24->writebuf) {
    err = -ENOMEM;
    goto err_struct;
   }
  } else {
   dev_warn(&client->dev,
    "cannot write due to controller restrictions.");
  }
 }

 at24->client[0] = client;

 /* use dummy devices for multiple-address chips */
 for (i = 1; i < num_addresses; i++) {
  at24->client[i] = i2c_new_dummy(client->adapter,
     client->addr + i);
  if (!at24->client[i]) {
   dev_err(&client->dev, "address 0x%02x unavailable\n",
     client->addr + i);
   err = -EADDRINUSE;
   goto err_clients;
  }
 }

 err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
 if (err)
  goto err_clients;

 i2c_set_clientdata(client, at24);

 dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
  at24->bin.size, client->name,
  writable ? "writable" : "read-only", at24->write_max);
 if (use_smbus == I2C_SMBUS_WORD_DATA ||
     use_smbus == I2C_SMBUS_BYTE_DATA) {
  dev_notice(&client->dev, "Falling back to %s reads, "
      "performance will suffer\n", use_smbus ==
      I2C_SMBUS_WORD_DATA ? "word" : "byte");
 }

 /* export data to kernel code */
 if (chip.setup)
  chip.setup(&at24->macc, chip.context);

 ret = gpio_request(EEPROM_WE_GPIO, "eeprom_wr_gpio");
 if(ret < 0)
 {
  printk(KERN_INFO "Could not get the eeprom write enable gpio\n");
  err = ret;
  goto err_clients;
 }

 gpio_direction_output(EEPROM_WE_GPIO, 0);


 return 0;

err_clients:
 for (i = 1; i < num_addresses; i++)
  if (at24->client[i])
   i2c_unregister_device(at24->client[i]);

 kfree(at24->writebuf);
err_struct:
 kfree(at24);
err_out:
 dev_dbg(&client->dev, "probe error %d\n", err);
 return err;
}

你可能感兴趣的:(linux,eeprom,driver,struct,accessor,byte,passwords,c,performance)