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




















    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    地方坐标系
    地图常用坐标系
    地图投影【百度百科】
    地图投影
    坐标系、坐标参照系、坐标变换、投影变换
    Scale和Resolution的含义及转换算法
    OpenStreetMap/Google/百度/Bing瓦片地图服务(TMS)
    面向对象的一些方法
    对象构造函数的原型图
    bookstarp的初体验
  • 原文地址:https://www.cnblogs.com/ch122633/p/7363293.html
Copyright © 2011-2022 走看看