I2C自编设备驱动设计

xiaoxiao2021-02-28  6

一、自编设备驱动模型 at24.c: static int __init at24_init(void) {     io_limit = rounddown_pow_of_two(io_limit);     return i2c_add_driver(&at24_driver);                                   //注册i2c驱动设备 } at24_driver: static struct i2c_driver at24_driver = {     .driver = {         .name = "at24",         .owner = THIS_MODULE,     },     .probe = at24_probe,                                                  //找到驱动对应的设备调用的函数     .remove = __devexit_p(at24_remove),     .id_table = at24_ids,                                                 //这个表里的设备都支持 }; at24_probe: 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;     } 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;         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 (!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;     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_write,用户的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 0xx unavailable\n",                     client->addr + i);             err = -EADDRINUSE;             goto err_clients;         }     }     err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);          //创建一个文件应用程序实际上是在/sys目录下的文件,而这个文件就是它函数创建的。     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" : "");     /* export data to kernel code */     if (chip.setup)         chip.setup(&at24->macc, chip.context);     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; } at24_bin_write: static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,         char *buf, loff_t off, size_t count) {     struct at24_data *at24;     at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));     return at24_write(at24, buf, off, count);                        //调用at24_write } at24_write: static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,              size_t count) {     ssize_t retval = 0;     if (unlikely(!count))         return count;     mutex_lock(&at24->lock);     while (count) {         ssize_t    status;         status = at24_eeprom_write(at24, buf, off, count);                    //调用at24_eeprom_write         if (status <= 0) {             if (retval == 0)                 retval = status;             break;         }         buf += status;         off += status;         count -= status;         retval += status;     }     mutex_unlock(&at24->lock);     return retval; } at24_eeprom_write: static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,         unsigned offset, size_t count) {     struct i2c_client *client;     struct i2c_msg msg;     ssize_t status;     unsigned long timeout, write_time;     unsigned next_page;     /* Get corresponding I2C address and adjust offset */     client = at24_translate_offset(at24, &offset);     /* write_max is at most a page */     if (count > at24->write_max)         count = at24->write_max;     /* Never roll over backwards, to the start of this page */     next_page = roundup(offset + 1, at24->chip.page_size);     if (offset + count > next_page)         count = next_page - offset;     /* If we'll use I2C calls for I/O, set up the message */                     //I2C的消息     if (!at24->use_smbus) {         int i = 0;         msg.addr = client->addr;         msg.flags = 0;         /* msg.buf is u8 and casts will mask the values */         msg.buf = at24->writebuf;         if (at24->chip.flags & AT24_FLAG_ADDR16)             msg.buf[i++] = offset >> 8;         msg.buf[i++] = offset;                                          //提供偏移地址         memcpy(&msg.buf[i], buf, count);                                //拷贝用户发送数据         msg.len = i + count;                                            //设置长度     }     /*      * Writes fail if the previous one didn't complete yet. We may      * loop a few times until this one succeeds, waiting at least      * long enough for one entire page write to work.      */     timeout = jiffies + msecs_to_jiffies(write_timeout);     do {         write_time = jiffies;         if (at24->use_smbus) {             status = i2c_smbus_write_i2c_block_data(client,                     offset, count, buf);             if (status == 0)                 status = count;         } else {             status = i2c_transfer(client->adapter, &msg, 1);                     //交给I2C控制器完成             if (status == 1)                 status = count;         }         dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",                 count, offset, status, jiffies);         if (status == count)             return count;         /* REVISIT: at HZ=100, this is sloooow */         msleep(1);     } while (time_before(write_time, timeout));     return -ETIMEDOUT; } i2c_transfer: int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) {     int ret;     if (adap->algo->master_xfer) { #ifdef DEBUG         for (ret = 0; ret < num; ret++) {             dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0xx, "                 "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)                 ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,                 (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");         } #endif         if (in_atomic() || irqs_disabled()) {             ret = mutex_trylock(&adap->bus_lock);             if (!ret)                 /* I2C activity is ongoing. */                 return -EAGAIN;         } else {             mutex_lock_nested(&adap->bus_lock, adap->level);         }         ret = adap->algo->master_xfer(adap,msgs,num);                   //调用控制器中的算法         mutex_unlock(&adap->bus_lock);         return ret;     } else {         dev_dbg(&adap->dev, "I2C level transfers not supported\n");         return -EOPNOTSUPP;     } } 然后看一下i2c_bin_read: 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;     at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));     return at24_read(at24, buf, off, count);                               //调用at24_read } at24_read: static ssize_t at24_read(struct at24_data *at24,         char *buf, loff_t off, size_t count) {     ssize_t retval = 0;     if (unlikely(!count))         return count;     mutex_lock(&at24->lock);     while (count) {         ssize_t    status;         status = at24_eeprom_read(at24, buf, off, count);                  //继续调用at24_eeprom_read         if (status <= 0) {             if (retval == 0)                 retval = status;             break;         }         buf += status;         off += status;         count -= status;         retval += status;     }     mutex_unlock(&at24->lock);     return retval; } at24_eeprom_read: 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));     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;     }     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;                                             //flags是读     msg[1].buf = buf;                                                    //提供数据量     msg[1].len = count;     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; } 二、对驱动程序的修改和移植 I2C设备的注册: static void __init tq2440_machine_init(void) {     s3c24xx_fb_set_platdata(&tq2440_fb_info);     s3c_i2c0_set_platdata(NULL);     platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));     EmbedSky_machine_init();     s3c2410_gpio_setpin(S3C2410_GPG12, 0);     s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);     s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg); } 增加设备: static struct at24_platform_data at24c02 = {     .byte_len = 2048 / 8,     .page_size = 8,     .flags = 0, }; static struct i2c_board_info __initdata tq2440_i2c_devices[] = {     {         I2C_BOARD_INFO("24c02", 0x50),         .platform_data = &at24c02,     }, }; 然后在tq2440_machine_init中添加: i2c_register_board_info(0, tq2440_i2c_devices, ARRAY_SIZE(tq2440_i2c_devices)); 然后添加头文件: #include <linux/i2c.h> #include <linux/i2c/at24.h> 然后会在开发板/sys/bus/i2c/devices/有一个0-0050的目录,有一个eeprom文件。 编写i2c-app.c文件: #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() {     char write_data[256],read_data[256];     int fd;     int i = 0;     //打开at24c02对应的sys文件     fd = open("/sys/bus/i2c/devices/0-0050/eeprom", O_RDWR);     //写入数据     for(i=0;i<256;i++)         write_data[i] = i;     lseek(fd, 0, SEEK_SET);     write(fd, write_data, 256);     //读出数据     lseek(fd, 0, SEEK_SET);     read(fd, read_data, 256);     //打印对比     for(i=0;i<256;i++)     {         if(i%16 == 0) printf("\r\n");         printf("= ",read_data[i]);     }     printf("\n");     close(fd); } <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script> 阅读(30) | 评论(0) | 转发(0) | 0

上一篇:I2C用户态驱动设计

下一篇:SPI总线介绍和裸机编程分析

相关热门文章 SHTML是什么_SSI有什么用...查看linux中某个端口(port)...卡尔曼滤波的原理说明...shell中字符串操作关于java中的“错误:找不到或... 给主人留下些什么吧!~~ 评论热议
转载请注明原文地址: https://www.6miu.com/read-450283.html

最新回复(0)