博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
I2C(二) linux2.6
阅读量:7081 次
发布时间:2019-06-28

本文共 16377 字,大约阅读时间需要 54 分钟。

目录


title: I2C(二) linux2.6

date: 2019/1/28 18:18:42
toc: true
---

I2C(二) linux2.6

总线驱动

官方例子是drivers\i2c\busses\i2c-s3c2410.c

关键结构

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {    .master_xfer        = s3c24xx_i2c_xfer,    .functionality      = s3c24xx_i2c_func,};static struct s3c24xx_i2c s3c24xx_i2c = {    .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),    .wait       = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),    .tx_setup   = 50,    .adap       = {        .name           = "s3c2410-i2c",        .owner          = THIS_MODULE,        .algo           = &s3c24xx_i2c_algorithm,        .retries        = 2,        .class          = I2C_CLASS_HWMON,    },};struct i2c_adapter {   struct module *owner;              //所属模块   unsigned int id;                //algorithm的类型,定义于i2c-id.h,   unsigned int class;       const struct i2c_algorithm *algo;     //总线通信方法结构体指针   void *algo_data;               //algorithm数据   struct rt_mutex bus_lock;        //控制并发访问的自旋锁   int timeout;      int retries;                //重试次数   struct device dev;             //适配器设备    int nr;                          //存放在i2c_adapter_idr里的位置号 char name[48];              //适配器名称   struct completion dev_released;    //用于同步   struct list_head userspace_clients;   //client链表头  };

入口

这里是一个platform总线框架,第一个函数也就是probe

static struct platform_driver s3c2440_i2c_driver = {    .probe      = s3c24xx_i2c_probe,    .remove     = s3c24xx_i2c_remove,    .resume     = s3c24xx_i2c_resume,    .driver     = {        .owner  = THIS_MODULE,        .name   = "s3c2440-i2c",    },};
static int s3c24xx_i2c_probe(struct platform_device *pdev){    struct s3c24xx_i2c *i2c = &s3c24xx_i2c;    struct resource *res;    int ret;    /* find the clock and enable it */    i2c->dev = &pdev->dev;    i2c->clk = clk_get(&pdev->dev, "i2c");    clk_enable(i2c->clk);    /* map the registers */    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,                     pdev->name);    i2c->regs = ioremap(res->start, (res->end-res->start)+1);    // 设置了具体的适配器算法    /* setup info block for the i2c core */    i2c->adap.algo_data = i2c;    i2c->adap.dev.parent = &pdev->dev;    //寄存器 gpio 初始化    /* initialise the i2c controller */    ret = s3c24xx_i2c_init(i2c);        //设置中断函数,中断里执行具体的读写函数    /* find the IRQ for this unit (note, this relies on the init call to     * ensure no current IRQs pending      */    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);    ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,              pdev->name, i2c);    i2c->irq = res;                    //添加adapt           ret = i2c_add_adapter(&i2c->adap);    //pdev.dev.driver_data=i2c    platform_set_drvdata(pdev, i2c);    return 0;}

i2c_add_adapter

  1. 设置这个具体的适配器相关的信息
  2. 添加新的适配器,这个情况下可能有新的client能够挂接到dev链表
  3. 调用这个i2c_driverattach_adapter
int i2c_add_adapter(struct i2c_adapter *adapter){    res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);    //调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体    i2c_register_adapter(adapter)}static int i2c_register_adapter(struct i2c_adapter *adap){    int res = 0;    struct list_head   *item;    struct i2c_driver  *driver;    INIT_LIST_HEAD(&adap->clients);    list_add_tail(&adap->list, &adapters);        // 这个parent 在上面的probe 指向了 资源文件指向的platform_device.dev    /* Add the adapter to the driver core.     * If the parent pointer is not set up,     * we add this adapter to the host bus.     */    if (adap->dev.parent == NULL) {        adap->dev.parent = &platform_bus;        pr_debug("I2C adapter driver [%s] forgot to specify "             "physical device\n", adap->name);    }    sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);    adap->dev.release = &i2c_adapter_dev_release;    //这里设置了具体的class     adap->dev.class = &i2c_adapter_class;        //注册device 在总线框架中,注册dev 会去寻找driver,执行probe    /* for new style drivers, when registration returns the driver core     * will have called probe() for all matching-but-unbound devices.     */    res = device_register(&adap->dev);    // 扫描静态的单板外设信息,有个全局链表__i2c_board_list 保存着未添加到链表的    // 想要加入到的client的相关信息,这个后续可以看 linux3.4    // 这里就是根据这个新的适配器添加这个client到链表    /* create pre-declared device nodes for new-style drivers */    if (adap->nr < __i2c_first_dynamic_bus_num)        i2c_scan_static_board_info(adap);        // 遍历这个drivers ,去执行实际驱动i2c_driver 的attach_adapter    /* let legacy drivers scan this bus for matching devices */    list_for_each(item,&drivers) {        driver = list_entry(item, struct i2c_driver, list);        if (driver->attach_adapter)            /* We ignore the return code; if it fails, too bad */            driver->attach_adapter(adap);    }}

最后执行attach_adapter,这个在后面的设备驱动再分析

硬件操作

硬件操作最后归结到接口master_xfer,I2C的传输到最后可以整理为

  1. 开始信号
  2. 结束信号
  3. ACK/NAK
  4. 数据传输1字节,读写是根据起始信号决定

在官方的驱动中,使用中断来传输,传输中使用休眠唤醒

//休眠timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);//完成后唤醒wake_up(&i2c->wait);// probe 时候申请中断request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);

设备驱动

参考文件driver/i2c/chips/eeprom.c

入口

static int __init eeprom_init(void){    return i2c_add_driver(&eeprom_driver);}static inline int i2c_add_driver(struct i2c_driver *driver){    return i2c_register_driver(THIS_MODULE, driver);}

注册

注册的时候会加入驱动到链表,最后执行驱动的attach_adapter

int i2c_register_driver(struct module *owner, struct i2c_driver *driver){    int res;    /* add the driver to the list of i2c drivers in the driver core */    driver->driver.owner = owner;    driver->driver.bus = &i2c_bus_type;    //注册driver 会去寻找dev,执行probe    /* for new style drivers, when registration returns the driver core     * will have called probe() for all matching-but-unbound devices.     */    res = driver_register(&driver->driver);    //添加链表    list_add_tail(&driver->list,&drivers);    //遍历adapt,执行驱动的attach_adapter    //这个和我们最后注册 adapt的时候,遍历 driver 时是差不多的    /* legacy drivers scan i2c busses directly */    if (driver->attach_adapter) {        struct i2c_adapter *adapter;        list_for_each_entry(adapter, &adapters, list) {            driver->attach_adapter(adapter);        }    }    return 0;}

attach_adapter

这里看下eeprom的是怎样的

static struct i2c_driver eeprom_driver = {    .driver = {        .name   = "eeprom",    },    .id     = I2C_DRIVERID_EEPROM,    .attach_adapter = eeprom_attach_adapter,    .detach_client  = eeprom_detach_client,};

i2c_probe总结起来就是 使用adapter先去检测地址addr_data,成功后调用eeprom_detect,挂接client,注册我们实际的字符设备驱动等操作

static int eeprom_attach_adapter(struct i2c_adapter *adapter){    return i2c_probe(adapter, &addr_data, eeprom_detect);}

我们这里的地址有以下几种,依次处理Force,probe,normal_i2c,ignore

struct i2c_client_address_data {    unsigned short *normal_i2c;    unsigned short *probe;    unsigned short *ignore;    unsigned short **forces;};

具体流程如下

int i2c_probe(struct i2c_adapter *adapter,          struct i2c_client_address_data *address_data,          int (*found_proc) (struct i2c_adapter *, int, int)){    int i, err;    int adap_id = i2c_adapter_id(adapter);    /* Force entries are done first, and are not affected by ignore       entries */    if (address_data->forces) {        unsigned short **forces = address_data->forces;        int kind;        for (kind = 0; forces[kind]; kind++) {            for (i = 0; forces[kind][i] != I2C_CLIENT_END;                 i += 2) {                if (forces[kind][i] == adap_id                 || forces[kind][i] == ANY_I2C_BUS) {                    dev_dbg(&adapter->dev, "found force "                        "parameter for adapter %d, "                        "addr 0x%02x, kind %d\n",                        adap_id, forces[kind][i + 1],                        kind);                    err = i2c_probe_address(adapter,                        forces[kind][i + 1],                        kind, found_proc);                    if (err)                        return err;                }            }        }    }    /* Stop here if we can't use SMBUS_QUICK */    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {        if (address_data->probe[0] == I2C_CLIENT_END         && address_data->normal_i2c[0] == I2C_CLIENT_END)            return 0;        dev_warn(&adapter->dev, "SMBus Quick command not supported, "             "can't probe for chips\n");        return -1;    }    /* Probe entries are done second, and are not affected by ignore       entries either */    for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {        if (address_data->probe[i] == adap_id         || address_data->probe[i] == ANY_I2C_BUS) {            dev_dbg(&adapter->dev, "found probe parameter for "                "adapter %d, addr 0x%02x\n", adap_id,                address_data->probe[i + 1]);            err = i2c_probe_address(adapter,                        address_data->probe[i + 1],                        -1, found_proc);            if (err)                return err;        }    }    /* Normal entries are done last, unless shadowed by an ignore entry */    for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {        int j, ignore;        ignore = 0;        for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;             j += 2) {            if ((address_data->ignore[j] == adap_id ||                 address_data->ignore[j] == ANY_I2C_BUS)             && address_data->ignore[j + 1]                == address_data->normal_i2c[i]) {                dev_dbg(&adapter->dev, "found ignore "                    "parameter for adapter %d, "                    "addr 0x%02x\n", adap_id,                    address_data->ignore[j + 1]);                ignore = 1;                break;            }        }        if (ignore)            continue;        dev_dbg(&adapter->dev, "found normal entry for adapter %d, "            "addr 0x%02x\n", adap_id,            address_data->normal_i2c[i]);        err = i2c_probe_address(adapter, address_data->normal_i2c[i],                    -1, found_proc);        if (err)            return err;    }    return 0;}

eeprom_detect

我们在这个驱动中的found_proc实际上是eeprom_detect,这里设置client,设置具体的adapt,driver,执行依附

/* This function is called by i2c_probe */static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind){    struct i2c_client *new_client;    struct eeprom_data *data;    int err = 0;    /* There are three ways we can read the EEPROM data:       (1) I2C block reads (faster, but unsupported by most adapters)       (2) Consecutive byte reads (100% overhead)       (3) Regular byte data reads (200% overhead)       The third method is not implemented by this driver because all       known adapters support at least the second. */    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA                        | I2C_FUNC_SMBUS_BYTE))        goto exit;    if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {        err = -ENOMEM;        goto exit;    }        // 设置client    new_client = &data->client;    memset(data->data, 0xff, EEPROM_SIZE);    i2c_set_clientdata(new_client, data);    new_client->addr = address;    new_client->adapter = adapter;    new_client->driver = &eeprom_driver;    new_client->flags = 0;    /* Fill in the remaining client fields */    strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);    data->valid = 0;    mutex_init(&data->update_lock);    data->nature = UNKNOWN;    //i2c_client与适配器进行连接    /* Tell the I2C layer a new client has arrived */    if ((err = i2c_attach_client(new_client)))        goto exit_kfree;    // 这个类似字符设备驱动    /* create the sysfs eeprom file */    err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);}

i2c_attach_client

将i2c_client与适配器进行连接

int i2c_attach_client(struct i2c_client *client){    struct i2c_adapter *adapter = client->adapter;    int res = 0;    list_add_tail(&client->list,&adapter->clients);    client->usage_count = 0;    client->dev.parent = &client->adapter->dev;    client->dev.bus = &i2c_bus_type;    if (client->driver)        client->dev.driver = &client->driver->driver;    if (client->driver && !is_newstyle_driver(client->driver)) {        client->dev.release = i2c_client_release;        client->dev.uevent_suppress = 1;    } else        client->dev.release = i2c_client_dev_release;    snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),        "%d-%04x", i2c_adapter_id(adapter), client->addr);    dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",        client->name, client->dev.bus_id);    res = device_register(&client->dev);    if (adapter->client_register)  {        if (adapter->client_register(client)) {            dev_dbg(&adapter->dev, "client_register "                "failed for client [%s] at 0x%02x\n",                client->name, client->addr);        }    }}

设备驱动参考代码

mark

图片来自

#include 
#include
#include
#include
#include
#include
#include
#include
#include
static unsigned short ignore[] = { I2C_CLIENT_END };static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */ /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};static unsigned short * forces[] = {force_addr, NULL}; static struct i2c_client_address_data addr_data = { .normal_i2c = normal_addr, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */ .probe = ignore, .ignore = ignore, //.forces = forces, /* 强制认为存在这个设备 */};static struct i2c_driver at24cxx_driver;static int major;static struct class *cls;struct i2c_client *at24cxx_client;static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset){ unsigned char address; unsigned char data; struct i2c_msg msg[2]; int ret; /* address = buf[0] * data = buf[1] */ if (size != 1) return -EINVAL; copy_from_user(&address, buf, 1); /* 数据传输三要素: 源,目的,长度 */ /* 读AT24CXX时,要先把要读的存储空间的地址发给它 */ msg[0].addr = at24cxx_client->addr; /* 目的 */ msg[0].buf = &address; /* 源 */ msg[0].len = 1; /* 地址=1 byte */ msg[0].flags = 0; /* 表示写 */ /* 然后启动读操作 */ msg[1].addr = at24cxx_client->addr; /* 源 */ msg[1].buf = &data; /* 目的 */ msg[1].len = 1; /* 数据=1 byte */ msg[1].flags = I2C_M_RD; /* 表示读 */ ret = i2c_transfer(at24cxx_client->adapter, msg, 2); if (ret == 2) { copy_to_user(buf, &data, 1); return 1; } else return -EIO;}static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset){ unsigned char val[2]; struct i2c_msg msg[1]; int ret; /* address = buf[0] * data = buf[1] */ if (size != 2) return -EINVAL; copy_from_user(val, buf, 2); /* 数据传输三要素: 源,目的,长度 */ msg[0].addr = at24cxx_client->addr; /* 目的 */ msg[0].buf = val; /* 源 */ msg[0].len = 2; /* 地址+数据=2 byte */ msg[0].flags = 0; /* 表示写 */ ret = i2c_transfer(at24cxx_client->adapter, msg, 1); if (ret == 1) return 2; else return -EIO;}static struct file_operations at24cxx_fops = { .owner = THIS_MODULE, .read = at24cxx_read, .write = at24cxx_write,};static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind){ printk("at24cxx_detect\n"); /* 构构一个i2c_client结构体: 以后收改数据时会用到它 */ at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); at24cxx_client->addr = address; at24cxx_client->adapter = adapter; at24cxx_client->driver = &at24cxx_driver; strcpy(at24cxx_client->name, "at24cxx"); i2c_attach_client(at24cxx_client); major = register_chrdev(0, "at24cxx", &at24cxx_fops); cls = class_create(THIS_MODULE, "at24cxx"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */ return 0;}static int at24cxx_attach(struct i2c_adapter *adapter){ return i2c_probe(adapter, &addr_data, at24cxx_detect);}static int at24cxx_detach(struct i2c_client *client){ printk("at24cxx_detach\n"); class_device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "at24cxx"); i2c_detach_client(client); kfree(i2c_get_clientdata(client)); return 0;}/* 1. 分配一个i2c_driver结构体 *//* 2. 设置i2c_driver结构体 */static struct i2c_driver at24cxx_driver = { .driver = { .name = "at24cxx", }, .attach_adapter = at24cxx_attach, .detach_client = at24cxx_detach,};static int at24cxx_init(void){ i2c_add_driver(&at24cxx_driver); return 0;}static void at24cxx_exit(void){ i2c_del_driver(&at24cxx_driver);}module_init(at24cxx_init);module_exit(at24cxx_exit);MODULE_LICENSE("GPL");

转载于:https://www.cnblogs.com/zongzi10010/p/10334607.html

你可能感兴趣的文章
open***客户端获得固定IP
查看>>
离线安装paramiko
查看>>
IDA文件目录简介
查看>>
求模版函数地址
查看>>
使用 Apache POI 处理 Microsoft Office 文档
查看>>
10个实用的但偏执的Java编程技术
查看>>
bash批量创建目录
查看>>
Windows pscp
查看>>
初学Python的学习笔记8----面向对象、数据封装、访问限制、继承和多态
查看>>
CAS注销后自定义跳转路径
查看>>
[大数据量]布隆过滤器(Bloom Filter)适用类型以及具体示例
查看>>
Linux | OOM机制的理解
查看>>
linux启动nagios无法通过web访问解决
查看>>
OpenSessionInViewFilter原理以及为什么要用OpenSessionInViewFilter
查看>>
决策树
查看>>
微服务实战(六):选择微服务部署策略
查看>>
mybatis入门教程(二)
查看>>
Java NIO(一)
查看>>
UIWebView类的调用
查看>>
MongoVE连接MongoDB 不显示数据问题
查看>>