Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1147383
  • 博文数量: 241
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2279
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-27 19:53
个人简介

JustForFun

文章分类

全部博文(241)

文章存档

2023年(8)

2022年(2)

2021年(3)

2020年(30)

2019年(11)

2018年(27)

2017年(54)

2016年(83)

2015年(23)

我的朋友

分类: LINUX

2016-11-25 16:15:35

设备与驱动匹配

1.match过程

i2c_add_driver-->i2c_register_driver-->i2c_bus_type-->.match->i2c_device_match-->of_driver_match_device/i2c_match_id(比 较i2c_driver->id_table->name和client->name,如果相同,则匹配上,匹配上之后,运行 driver_register调用driver_probe_device进行设备与驱动绑定。),


图片
////////////
图片






static int __init at24_init(void)
{
    io_limit = rounddown_pow_of_two(io_limit);

//执行完i2c_add_numbered_adapter函数后,内核的i2c总线上已有adapter device和client device
//
.id_table = at24_ids中的名字和i2c_client中的名字 进行匹配
    return i2c_add_driver(&at24_driver);
}
module_init(at24_init);

////////////////////////////////////////////////////////////////////////////////

static struct i2c_driver at24_driver = {
    .driver = {
        .name = "at24",//这个名字用于创建文件,不用于匹配
        .owner = THIS_MODULE,
    },
 ///当i2c_client和i2c_driver(at24_driver)匹配时调用
    .probe = at24_probe,
    .remove = __devexit_p(at24_remove),
//ID表是用来和i2c_client匹配用的,
//  static struct i2c_board_info i2c_devs0[]用来建立i2c_client(相当于device)
//
//int i2c_attach_client(struct i2c_client *client)
//i2c_new_device函数
    .id_table = at24_ids,
};

////////////////////////////////////////////////////////////////////////////////////////////


//
//struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
//   ------> strlcpy(client->name, info->type, sizeof(client->name));// i2c_board_info中的名字给了client
//static struct i2c_board_info i2c_devs0[]
////"24c08"这个名字就是用来创建i2c_client来和里static struct i2c_driver at24_driver进行匹配的。

static const struct i2c_device_id at24_ids[] = {
    /* needs 8 addresses as A0-A2 are ignored */
    /* old variants can't be handled with this generic entry! */
    { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
    { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
    /* spd is a 24c02 in memory DIMMs */
    { "spd", AT24_DEVICE_MAGIC(2048 / 8,
        AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
    { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
    /* 24rf08 quirk is handled at i2c-core */
    { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
    { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
    { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
    { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
    { "at24", 0 },
    { /* END OF LIST */ }
};

////////////////////////////////////////////////////////////////////////////////////////////

/*-------------------------------------------------------------------------*/

static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct at24_platform_data chip;
    bool writable;
    bool use_smbus = false;
    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;
    }
        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;
    }

    if (!is_power_of_2(chip.byte_len))
        dev_warn(&client->dev,
            "byte_len looks suspicious (no power of 2)!\n");
    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)) {
            err = -EPFNOSUPPORT;
            goto err_out;
        }
        use_smbus = true;
    }

    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)
     */
    at24->bin.attr.name = "eeprom";
    at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;

//除了这种操作i2c设备的方法外,
//
//static int __init i2c_dev_init(void)
//res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); 也是一种操作i2c设备的方法
    at24->bin.read = at24_bin_read;
    at24->bin.size = chip.byte_len;

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

    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\n",
        at24->bin.size, client->name,
        writable ? "(writable)" : "(read-only)");
    dev_dbg(&client->dev,
        "page_size %d, num_addresses %d, write_max %d%s\n",
        chip.page_size, num_addresses,
        at24->write_max,
        use_smbus ? ", use_smbus" : "");

    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;
}


////////////////////////////////////////////////////////////////////////////////////////////

static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
        char *buf, loff_t off, size_t count)
{
    struct at24_data *at24;
    ssize_t retval = 0;

    at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    /*
     * Read data from chip, protecting against concurrent updates
     * from this host, but not from other I2C masters.
     */
    mutex_lock(&at24->lock);

    while (count) {
        ssize_t    status;

        status = at24_eeprom_read(at24, buf, off, count);
        if (status <= 0) {
            if (retval == 0)
                retval = status;
            break;
        }
        buf += status;
        off += status;
        count -= status;
        retval += status;
    }

    mutex_unlock(&at24->lock);

    return retval;
}

////////////////////////////////////////////////////////////////////////////////////////////

static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
        unsigned offset, size_t count)
{
    struct i2c_msg msg[2];
    u8 msgbuf[2];
    struct i2c_client *client;
    int status, i;

    memset(msg, 0, sizeof(msg));

    /*
     * REVISIT some multi-address chips don't rollover page reads to
     * the next slave address, so we may need to truncate the count.
     * Those chips might need another quirk flag.
     *
     * If the real hardware used four adjacent 24c02 chips and that
     * were misconfigured as one 24c08, that would be a similar effect:
     * one "eeprom" file not four, but larger reads would fail when
     * they crossed certain pages.
     */

    /*
     * Slave address and byte offset derive from the offset. Always
     * set the byte address; on a multi-master board, another master
     * may have changed the chip's "current" address pointer.
     */
    client = at24_translate_offset(at24, &offset);

    if (count > io_limit)
        count = io_limit;

    /* Smaller eeproms can work given some SMBus extension calls */
    if (at24->use_smbus) {
        if (count > I2C_SMBUS_BLOCK_MAX)
            count = I2C_SMBUS_BLOCK_MAX;
        status = i2c_smbus_read_i2c_block_data(client, offset,
                count, buf);
        dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n",
                count, offset, status);
        return (status < 0) ? -EIO : status;
    }

    /*
     * When we have a better choice than SMBus calls, use a combined
     * I2C message. Write address; then read up to io_limit data bytes.
     * Note that read page rollover helps us here (unlike writes).
     * msgbuf is u8 and will cast to our needs.
     */
    i = 0;
    if (at24->chip.flags & AT24_FLAG_ADDR16)
        msgbuf[i++] = offset >> 8;
    msgbuf[i++] = offset;

    msg[0].addr = client->addr;
    msg[0].buf = msgbuf;
    msg[0].len = i;

    msg[1].addr = client->addr;
    msg[1].flags = I2C_M_RD;
    msg[1].buf = buf;
    msg[1].len = count;
 
中有i2c_transfer
    status = i2c_transfer(client->adapter, msg, 2);
    dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n",
            count, offset, status);

    if (status == 2)
        return count;
    else if (status >= 0)
        return -EIO;
    else
        return status;
}



阅读(2069) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~