zoukankan      html  css  js  c++  java
  • I2C自编设备驱动设计

    一、自编设备驱动模型

    at24.c:
    1. static int __init at24_init(void)
    2. {
    3.     io_limit = rounddown_pow_of_two(io_limit);
    4.     return i2c_add_driver(&at24_driver);                                   //注册i2c驱动设备
    5. }
    at24_driver:
    1. static struct i2c_driver at24_driver = {
    2.     .driver = {
    3.         .name = "at24",
    4.         .owner = THIS_MODULE,
    5.     },
    6.     .probe = at24_probe,                                                  //找到驱动对应的设备调用的函数
    7.     .remove = __devexit_p(at24_remove),
    8.     .id_table = at24_ids,                                                 //这个表里的设备都支持
    9. };
    at24_probe:
    1. static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
    2. {
    3.     struct at24_platform_data chip;
    4.     bool writable;
    5.     bool use_smbus = false;
    6.     struct at24_data *at24;
    7.     int err;
    8.     unsigned i, num_addresses;
    9.     kernel_ulong_t magic;

    10.     if (client->dev.platform_data) {
    11.         chip = *(struct at24_platform_data *)client->dev.platform_data;
    12.     } else {
    13.         if (!id->driver_data) {
    14.             err = -ENODEV;
    15.             goto err_out;
    16.         }
    17.         magic = id->driver_data;
    18.         chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
    19.         magic >>= AT24_SIZE_BYTELEN;
    20.         chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
    21.         /*
    22.          * This is slow, but we can't know all eeproms, so we better
    23.          * play safe. Specifying custom eeprom-types via platform_data
    24.          * is recommended anyhow.
    25.          */
    26.         chip.page_size = 1;

    27.         chip.setup = NULL;
    28.         chip.context = NULL;
    29.     }

    30.     if (!is_power_of_2(chip.byte_len))
    31.         dev_warn(&client->dev,
    32.             "byte_len looks suspicious (no power of 2)! ");
    33.     if (!is_power_of_2(chip.page_size))
    34.         dev_warn(&client->dev,
    35.             "page_size looks suspicious (no power of 2)! ");

    36.     /* Use I2C operations unless we're stuck with SMBus extensions. */
    37.     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    38.         if (chip.flags & AT24_FLAG_ADDR16) {
    39.             err = -EPFNOSUPPORT;
    40.             goto err_out;
    41.         }
    42.         if (!i2c_check_functionality(client->adapter,
    43.                 I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
    44.             err = -EPFNOSUPPORT;
    45.             goto err_out;
    46.         }
    47.         use_smbus = true;
    48.     }

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

    54.     at24 = kzalloc(sizeof(struct at24_data) +
    55.         num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
    56.     if (!at24) {
    57.         err = -ENOMEM;
    58.         goto err_out;
    59.     }

    60.     mutex_init(&at24->lock);
    61.     at24->use_smbus = use_smbus;
    62.     at24->chip = chip;
    63.     at24->num_addresses = num_addresses;

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

    72.     at24->macc.read = at24_macc_read;

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

    77.             unsigned write_max = chip.page_size;

    78.             at24->macc.write = at24_macc_write;

    79.             at24->bin.write = at24_bin_write;                          //这里注册了at24_bin_write,用户的write函数的调用接口
    80.             at24->bin.attr.mode |= S_IWUSR;

    81.             if (write_max > io_limit)
    82.                 write_max = io_limit;
    83.             if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
    84.                 write_max = I2C_SMBUS_BLOCK_MAX;
    85.             at24->write_max = write_max;

    86.             /* buffer (data + address at the beginning) */
    87.             at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
    88.             if (!at24->writebuf) {
    89.                 err = -ENOMEM;
    90.                 goto err_struct;
    91.             }
    92.         } else {
    93.             dev_warn(&client->dev,
    94.                 "cannot write due to controller restrictions.");
    95.         }
    96.     }

    97.     at24->client[0] = client;

    98.     /* use dummy devices for multiple-address chips */
    99.     for (i = 1; i < num_addresses; i++) {
    100.         at24->client[i] = i2c_new_dummy(client->adapter,
    101.                     client->addr + i);
    102.         if (!at24->client[i]) {
    103.             dev_err(&client->dev, "address 0x%02x unavailable ",
    104.                     client->addr + i);
    105.             err = -EADDRINUSE;
    106.             goto err_clients;
    107.         }
    108.     }

    109.     err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);          //创建一个文件应用程序实际上是在/sys目录下的文件,而这个文件就是它函数创建的。
    110.     if (err)
    111.         goto err_clients;

    112.     i2c_set_clientdata(client, at24);

    113.     dev_info(&client->dev, "%zu byte %s EEPROM %s ",
    114.         at24->bin.size, client->name,
    115.         writable ? "(writable)" : "(read-only)");
    116.     dev_dbg(&client->dev,
    117.         "page_size %d, num_addresses %d, write_max %d%s ",
    118.         chip.page_size, num_addresses,
    119.         at24->write_max,
    120.         use_smbus ? ", use_smbus" : "");

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

    124.     return 0;

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

    129.     kfree(at24->writebuf);
    130. err_struct:
    131.     kfree(at24);
    132. err_out:
    133.     dev_dbg(&client->dev, "probe error %d ", err);
    134.     return err;
    135. }
    at24_bin_write:
    1. static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
    2.         char *buf, loff_t off, size_t count)
    3. {
    4.     struct at24_data *at24;

    5.     at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    6.     return at24_write(at24, buf, off, count);                        //调用at24_write
    7. }
    at24_write:
    1. static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
    2.              size_t count)
    3. {
    4.     ssize_t retval = 0;

    5.     if (unlikely(!count))
    6.         return count;

    7.     mutex_lock(&at24->lock);

    8.     while (count) {
    9.         ssize_t    status;

    10.         status = at24_eeprom_write(at24, buf, off, count);                    //调用at24_eeprom_write
    11.         if (status <= 0) {
    12.             if (retval == 0)
    13.                 retval = status;
    14.             break;
    15.         }
    16.         buf += status;
    17.         off += status;
    18.         count -= status;
    19.         retval += status;
    20.     }

    21.     mutex_unlock(&at24->lock);

    22.     return retval;
    23. }
    at24_eeprom_write:
    1. static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
    2.         unsigned offset, size_t count)
    3. {
    4.     struct i2c_client *client;
    5.     struct i2c_msg msg;
    6.     ssize_t status;
    7.     unsigned long timeout, write_time;
    8.     unsigned next_page;

    9.     /* Get corresponding I2C address and adjust offset */
    10.     client = at24_translate_offset(at24, &offset);

    11.     /* write_max is at most a page */
    12.     if (count > at24->write_max)
    13.         count = at24->write_max;

    14.     /* Never roll over backwards, to the start of this page */
    15.     next_page = roundup(offset + 1, at24->chip.page_size);
    16.     if (offset + count > next_page)
    17.         count = next_page - offset;

    18.     /* If we'll use I2C calls for I/O, set up the message */                     //I2C的消息
    19.     if (!at24->use_smbus) {
    20.         int i = 0;

    21.         msg.addr = client->addr;
    22.         msg.flags = 0;

    23.         /* msg.buf is u8 and casts will mask the values */
    24.         msg.buf = at24->writebuf;
    25.         if (at24->chip.flags & AT24_FLAG_ADDR16)
    26.             msg.buf[i++] = offset >> 8;

    27.         msg.buf[i++] = offset;                                          //提供偏移地址
    28.         memcpy(&msg.buf[i], buf, count);                                //拷贝用户发送数据
    29.         msg.len = i + count;                                            //设置长度
    30.     }

    31.     /*
    32.      * Writes fail if the previous one didn't complete yet. We may
    33.      * loop a few times until this one succeeds, waiting at least
    34.      * long enough for one entire page write to work.
    35.      */
    36.     timeout = jiffies + msecs_to_jiffies(write_timeout);
    37.     do {
    38.         write_time = jiffies;
    39.         if (at24->use_smbus) {
    40.             status = i2c_smbus_write_i2c_block_data(client,
    41.                     offset, count, buf);
    42.             if (status == 0)
    43.                 status = count;
    44.         } else {
    45.             status = i2c_transfer(client->adapter, &msg, 1);                     //交给I2C控制器完成
    46.             if (status == 1)
    47.                 status = count;
    48.         }
    49.         dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld) ",
    50.                 count, offset, status, jiffies);

    51.         if (status == count)
    52.             return count;

    53.         /* REVISIT: at HZ=100, this is sloooow */
    54.         msleep(1);
    55.     } while (time_before(write_time, timeout));

    56.     return -ETIMEDOUT;
    57. }
    i2c_transfer:
    1. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    2. {
    3.     int ret;

    4.     if (adap->algo->master_xfer) {
    5. #ifdef DEBUG
    6.         for (ret = 0; ret < num; ret++) {
    7.             dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
    8.                 "len=%d%s ", ret, (msgs[ret].flags & I2C_M_RD)
    9.                 ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
    10.                 (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
    11.         }
    12. #endif

    13.         if (in_atomic() || irqs_disabled()) {
    14.             ret = mutex_trylock(&adap->bus_lock);
    15.             if (!ret)
    16.                 /* I2C activity is ongoing. */
    17.                 return -EAGAIN;
    18.         } else {
    19.             mutex_lock_nested(&adap->bus_lock, adap->level);
    20.         }

    21.         ret = adap->algo->master_xfer(adap,msgs,num);                   //调用控制器中的算法
    22.         mutex_unlock(&adap->bus_lock);

    23.         return ret;
    24.     } else {
    25.         dev_dbg(&adap->dev, "I2C level transfers not supported ");
    26.         return -EOPNOTSUPP;
    27.     }
    28. }
    然后看一下i2c_bin_read:
    1. static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
    2.         char *buf, loff_t off, size_t count)
    3. {
    4.     struct at24_data *at24;

    5.     at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
    6.     return at24_read(at24, buf, off, count);                               //调用at24_read
    7. }
    at24_read:
    1. static ssize_t at24_read(struct at24_data *at24,
    2.         char *buf, loff_t off, size_t count)
    3. {
    4.     ssize_t retval = 0;

    5.     if (unlikely(!count))
    6.         return count;

    7.     mutex_lock(&at24->lock);

    8.     while (count) {
    9.         ssize_t    status;

    10.         status = at24_eeprom_read(at24, buf, off, count);                  //继续调用at24_eeprom_read
    11.         if (status <= 0) {
    12.             if (retval == 0)
    13.                 retval = status;
    14.             break;
    15.         }
    16.         buf += status;
    17.         off += status;
    18.         count -= status;
    19.         retval += status;
    20.     }

    21.     mutex_unlock(&at24->lock);

    22.     return retval;
    23. }
    at24_eeprom_read:
    1. static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
    2.         unsigned offset, size_t count)
    3. {
    4.     struct i2c_msg msg[2];
    5.     u8 msgbuf[2];
    6.     struct i2c_client *client;
    7.     int status, i;

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

    9.     client = at24_translate_offset(at24, &offset);

    10.     if (count > io_limit)
    11.         count = io_limit;

    12.     /* Smaller eeproms can work given some SMBus extension calls */
    13.     if (at24->use_smbus) {
    14.         if (count > I2C_SMBUS_BLOCK_MAX)
    15.             count = I2C_SMBUS_BLOCK_MAX;
    16.         status = i2c_smbus_read_i2c_block_data(client, offset,
    17.                 count, buf);
    18.         dev_dbg(&client->dev, "smbus read %zu@%d --> %d ",
    19.                 count, offset, status);
    20.         return (status < 0) ? -EIO : status;
    21.     }

    22.     i = 0;
    23.     if (at24->chip.flags & AT24_FLAG_ADDR16)
    24.         msgbuf[i++] = offset >> 8;
    25.     msgbuf[i++] = offset;                                                //设置偏移

    26.     msg[0].addr = client->addr;                                          //第一条消息,提供从设备地址
    27.     msg[0].buf = msgbuf;
    28.     msg[0].len = i;

    29.     msg[1].addr = client->addr;                                          //第二条消息
    30.     msg[1].flags = I2C_M_RD;                                             //flags是读
    31.     msg[1].buf = buf;                                                    //提供数据量
    32.     msg[1].len = count;

    33.     status = i2c_transfer(client->adapter, msg, 2);
    34.     dev_dbg(&client->dev, "i2c read %zu@%d --> %d ",
    35.             count, offset, status);

    36.     if (status == 2)
    37.         return count;
    38.     else if (status >= 0)
    39.         return -EIO;
    40.     else
    41.         return status;
    42. }

    二、对驱动程序的修改和移植

    I2C设备的注册:
    1. static void __init tq2440_machine_init(void)
    2. {
    3.     s3c24xx_fb_set_platdata(&tq2440_fb_info);
    4.     s3c_i2c0_set_platdata(NULL);

    5.     platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
    6.     EmbedSky_machine_init();
    7.     s3c2410_gpio_setpin(S3C2410_GPG12, 0);
    8.     s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);
    9.     s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);
    10. }
    增加设备:
    1. static struct at24_platform_data at24c02 = {
    2.     .byte_len = 2048 / 8,
    3.     .page_size = 8,
    4.     .flags = 0,
    5. };

    6. static struct i2c_board_info __initdata tq2440_i2c_devices[] = {
    7.     {
    8.         I2C_BOARD_INFO("24c02", 0x50),
    9.         .platform_data = &at24c02,
    10.     },
    11. };
    然后在tq2440_machine_init中添加:
    1. i2c_register_board_info(0, tq2440_i2c_devices, ARRAY_SIZE(tq2440_i2c_devices));
    然后添加头文件:
    1. #include <linux/i2c.h>
    2. #include <linux/i2c/at24.h>
    然后会在开发板/sys/bus/i2c/devices/有一个0-0050的目录,有一个eeprom文件。

    编写i2c-app.c文件:
    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <unistd.h>

    6. int main()
    7. {
    8.     char write_data[256],read_data[256];
    9.     int fd;
    10.     int i = 0;
    11.     //打开at24c02对应的sys文件
    12.     fd = open("/sys/bus/i2c/devices/0-0050/eeprom", O_RDWR);

    13.     //写入数据
    14.     for(i=0;i<256;i++)
    15.         write_data[i] = i;

    16.     lseek(fd, 0, SEEK_SET);
    17.     write(fd, write_data, 256);
    18.     //读出数据
    19.     lseek(fd, 0, SEEK_SET);
    20.     read(fd, read_data, 256);

    21.     //打印对比
    22.     for(i=0;i<256;i++)
    23.     {
    24.         if(i%16 == 0) printf(" ");
    25.         printf("%3d ",read_data[i]);
    26.     }
    27.     printf(" ");
    28.     close(fd);
    29. }




















    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    HDU 1800 Flying to the Mars 字典树,STL中的map ,哈希树
    字典树 HDU 1075 What Are You Talking About
    字典树 HDU 1251 统计难题
    最小生成树prim算法 POJ2031
    POJ 1287 Networking 最小生成树
    次小生成树 POJ 2728
    最短路N题Tram SPFA
    poj2236 并查集
    POJ 1611并查集
    Number Sequence
  • 原文地址:https://www.cnblogs.com/ch122633/p/7363293.html
Copyright © 2011-2022 走看看