05-SA8155 QNX I2C框架及代码分析

1. 描述

本文主要描述QNX I2C Drvier的相关内容,并以SA8155处理器为例讲解。
I2C 是经常用到的一种总线协议,它只占用两个IO口资源,分别是SCL时钟信号线与SDA数据线,
两根线就能将连接与总线上的设备实现数据通信,由于它的简便的构造设计,于是成为一种较为
常用的通信方式。在QNX系统里,也提供了I2C驱动框架,整体框架与SPI类似。

I2C总线数据传输和应答 

2. 目录结构

2.1 QUB配置部分

TODO

请查阅 https://blog.csdn.net/liaochaoyun/article/details/127317225

2.2. 驱动部分:

qnx_ap/AMSS/platform/hwdrivers/wired_peripherals/i2c

├── common
│   ├── aarch64
│   │   ├── a-le
│   │   ├── Makefile
│   │   └── so-le
│   ├── arm
│   │   ├── a-le-v7
│   │   ├── Makefile
│   │   └── so-le-v7
│   ├── common.mk
│   ├── Makefile
│   └── src
│       ├── I2cDeviceQup.c
│       └── I2cSys.c
├── inc
│   ├── ddii2c.h
│   ├── I2cDevice.h
│   ├── I2cError.h
│   ├── I2cTransferCfg.h
│   ├── I2cTransfer.h
│   └── I2cTypes.h
├── Makefile
└── platsvc_i2c
    ├── aarch64
    │   ├── a-le
    │   ├── Makefile
    │   └── so-le
    ├── arm
    │   ├── a-le-v7
    │   ├── Makefile
    │   └── so-le-v7
    ├── common.mk
    ├── inc
    │   ├── I2cLog.h
    │   ├── I2cPlatBam.h
    │   ├── I2cPlatBsp.h
    │   ├── I2cPlatSvc.h
    │   └── I2cSys.h
    ├── Makefile
    └── src
        ├── I2cPlatBsp.c
        └── I2cPlatSvc.c

2.3. 资源管理器Res部分

qnx_ap/AMSS/platform/resources/i2c_drv

├── aarch64
│   ├── a-le
│   │   └── Makefile
│   ├── Makefile
│   ├── so-le
│   │   └── Makefile
│   └── so-le-g
│       └── Makefile
├── arm
│   ├── a-le-v7
│   │   └── Makefile
│   ├── Makefile
│   └── so-le-v7
│       └── Makefile
├── common.mk
├── i2c_drv.c
├── Makefile
└── protected
    └── i2c_devctls.h

2.4  I2C Service(Demo 进程)

qnx_ap/AMSS/platform/services/daemons/i2c_service

├── aarch64
│   ├── Makefile
│   └── o-le
│       └── Makefile
├── arm
│   ├── Makefile
│   └── o-le-v7
│       └── Makefile
├── common.mk
├── Makefile
├── src
│   └── i2cservice_main.c
└── usefile

2.5  I2c API(Client)

qnx_ap/AMSS/platform/qal/clients/i2c_client

├── aarch64
│   ├── Makefile
│   ├── so-le
│   │   └── Makefile
│   └── so-le-g
│       └── Makefile
├── arm
│   ├── Makefile
│   └── so-le-v7
│       └── Makefile
├── common.mk
├── i2c_client.c
├── Makefile
└── public
    └── amss
        └── i2c_client.h

3. I2C API 

qnx_ap/AMSS/platform/qal/clients/i2c_client/public/amss/i2c_client.h

安装:qnx_ap/install/usr/include/amss/i2c_client.h

/*
 * gain access to controller/bus driver
 * Returns a handle that is passed to all other functions.
 * Parameters:
 * (in)        devname        Resource Manager connection(ex. "/dev/i2c1")
 *
 * Returns:
 * >0    success
 * -1     failure
 */
int i2c_open(void* devname);

/*
 * releases access to controller/bus driver
 * Frees memory associated with "fd".
 * Parameters:
 * (in)     fd         Handle returned from init()
 *
 */
void i2c_close(int fd);

/*
 * Specify the bus clock frequency
 * If an invalid bus speed is requested, this function should return
 * failure and leave the bus speed unchanged.
 * Parameters:
 * (in)     addr    The slave address
 * (in)     fmt     The slave address format
 * Returns:
 * 0    success
 * -1   failure
 */
int i2c_set_slave_addr(int fd, uint32_t addr, uint32_t fmt);

/*
 * Specify the bus clock frequency
 * If an invalid bus speed is requested, this function should return
 * failure and leave the bus speed unchanged.
 * Parameters:
 * (in)     bus_clock_freq.
 * Returns:
 * 0    success
 * -1   failure
 */
int i2c_set_bus_speed(int fd, uint32_t speed, uint32_t * ospeed);

/*
 * Lock this I2C controller/bus
 * Parameters:
 * (in)     fd         Handle returned from init()
 * Returns:
 * 0    success
 * -1    failure
 */
int i2c_bus_lock(int fd);

/*
 * Unlock this I2C  controller/bus
 * Parameters:
 * (in)     fd         Handle returned from init()
 * Returns:
 * 0    success
 * -1    failure
 */
int i2c_bus_unlock(int fd);

/*
 * Write
 * Parameters:
 * (in)     fd          Handle returned from init()
 * (in)     buf         Buffer of data to write
 * (in)     len         Length in bytes of buf
 * Returns:
 * Number of bytes read
 */
int i2c_write(int fd, void *buf, uint32_t len);

/*
 * Read
 * Parameters:
 * (in)     fd         Handle returned from init()
 * (in)     buf        Buffer for read data
 * (in)     len        Length in bytes of buf
 * Returns:
 * >=0    Number of bytes read
 * <0    Error # from errno.h
 */
int i2c_read(int fd, void *buf, uint32_t len);

/*
 * Combined Write Read
 * Parameters:
 * (in)     fd          Handle returned from init()
 * (in)     wbuf        Buffer of data to send
 * (in)     wlen        Length in bytes of wbuf
 * (in)        rbuff           Buffer for received data
 * (in)        rlen           Length in bytes of wbuf
 * Returns:
 * >=0    Number of bytes read
 * <0    Error # from errno.h
 */
int i2c_combined_writeread(int fd, void * wbuff, uint32_t wlen, void* rbuff, uint32_t rlen );


/*
 * Request info about the driver.
 * Returns:
 * 0    success
 * -1   failure
 */
int i2c_driver_info(int fd, i2c_driver_info_t *info);

文件引用:#include <amss/i2c_client.h>

4.  I2C 资源管理器设计

4.1 优点

资源管理器接口的主要优点是:

  • 它为应用程序开发人员提供了一个清晰、易于理解的思路。
  • 它作为一个中介,在多个应用程序对一个或多个从设备之间进行访问,强制不同I2C接口之间的一致性。
  • 对于专用的i2c总线应用程序,硬件访问库更有效;硬件接口定义了这个库的接口,有助于维护和代码可移植性。

4.2 通用架构

通用架构如下如:

4.3 代码分析

4.3.1 服务进程

核心文件: qnx_ap/AMSS/platform/services/daemons/i2c_service/src/i2cservice_main.c

int i2c_service_base_init(void)
{
    //调用i2c_drv 资源管理接口实现i2c资源管理器初始化
    if (-1 == i2c_drv_init()) {
        logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR, 
                   QCLOG_ERROR, "i2c_drv_init Failed");
    }

    drop_abilities_i2c();
    /* init sysctrl */
    if(DAL_SUCCESS != sysctrl_init()) {
        logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR, 
                   QCLOG_ERROR, "sysctrl_init failed, errno: %s", strerror(errno));
        return DAL_ERROR;
    }

    return DAL_SUCCESS;
}

int main(int argc, char *argv[]) {

    int c;
    ...

    /* QNX IO operation privilege */
    if (ThreadCtl(_NTO_TCTL_IO, 0) == -1)
    {
        PROCESS_ERROR_CRITICAL("i2c_service: ThreadCtl Error");
        return EXIT_FAILURE;
    }
    
    //信号SIGTERM处理
    signal(SIGTERM,handle_sigterm);

    // Check if process is running /dev/i2c_service
    if( 0 < open(DEVICE_NAME, O_RDONLY))
    {
        PROCESS_ERROR_CRITICAL("Process [%s] already running.. exiting.", DEVICE_NAME);
        return EXIT_FAILURE;
    }

    // Run process in background,进程后台运行
    Resource_Daemonize();

    //资源管理器实现接口
    if(i2c_service_base_init() != DAL_SUCCESS)
    {
        PROCESS_ERROR_CRITICAL("i2c_service: i2c_service_core_init failed");
        return EXIT_FAILURE;
    }
    //启动I2C资源管理器服务
    /* the i2c resource manager main loop is going to run after here */
    if(DAL_SUCCESS != sysctrl_start(DEVICE_NAME)) {
        PROCESS_ERROR_CRITICAL("i2c_service: service_init failed");
        return EXIT_FAILURE;
    }

    // Code should never reach here
    PROCESS_ERROR_CRITICAL("Exiting i2c_service..");
    return EXIT_SUCCESS;
}

 查看系统进程:

# pidin | grep i2c
   40982   1 bin/i2c_service     10r RECEIVE     8
   40982   2 bin/i2c_service     15r RECEIVE     2
   40982   3 bin/i2c_service     21r SEM         fff808a056320f94
   40982   4 bin/i2c_service     41r INTR
   40982   5 bin/i2c_service     10r RECEIVE     3
   40982   6 bin/i2c_service     41r INTR
   40982   7 bin/i2c_service     10r RECEIVE     6
   40982   8 bin/i2c_service     41r INTR
   40982   9 bin/i2c_service     10r RECEIVE     11

4.3.2 资源管理器实现

 核心文件:qnx_ap/AMSS/platform/resources/i2c_drv/i2c_drv.c

 入口:

/******************************************************************************/
/***************************** startup and config  ****************************/
/******************************************************************************/

int i2c_drv_init(void)
{
    int i;
    int idx = 0;
    int ret = EOK;
    uint64_t chip_id = 0;
    const void *fdt_paddr = 0;
    pthread_t threadID;
    DALSYSPropertyVar PropVar;

    DALSYS_PROPERTY_HANDLE_DECLARE(hDALProps);
    DALSYS_InitMod(NULL);
    DALSYS_RegisterMod(&gDALModDriverInfoList);

    int policy;
    struct sched_param param;
    pthread_attr_t attr;

    if (waitfor_attach(QCORE_SERVICE, 5000))
    {
        I2C_SLOGE("Timed out waiting for %s to be ready", QCORE_SERVICE);
        return -1;
    }

    fdt_paddr = fdt_get_root();
    if (!fdt_paddr) {
        I2C_SLOGE("Failed to load device tree");
        return -1;
    }
    ret = fdt_foreach_subnode_byname((void*) fdt_paddr , "/chip_info",
            &get_chip_info, &chip_id);
    if (ret) {
        I2C_SLOGE("Failed to find dt chip_info");
        return -1;
    }

    I2C_SLOGI("Found chip id=%d", (int)chip_id);

    //线程配置
    pthread_attr_init(&attr);
    pthread_getschedparam(pthread_self(), &policy, &param);
    param.sched_priority = 100;
    pthread_attr_setschedparam(&attr, &param);

    devs = calloc(MAX_NUM_I2C_DEVS, sizeof(i2c_dev_t));
    if(devs == NULL)
        return -1;

    //创建设备
    // create devices , if specified via command line
    for(i = 0; i < MAX_NUM_I2C_DEVS; i++)
    {
        if(DALSYS_GetDALPropertyHandle(DeviceID[i], hDALProps) == DAL_SUCCESS)
        {
            //解析I2C配置,详情看QUP-I2C配置项
            if (DAL_SUCCESS != DALSYS_GetPropertyValue(hDALProps, "I2C_ENABLED", 0, &PropVar)
                    || PropVar.Val.dwVal == 0)
            {
                continue;
            }

            if (DAL_SUCCESS != DALSYS_GetPropertyValue(hDALProps, "CLOCK_SE_NAME", 0, &PropVar)
                    || PropVar.Val.pszVal == 0)
            {
                I2C_SLOGE("Failed to get property CLOCK_SE_NAME");
                return -1;
            }

            if (!strncmp(PropVar.Val.pszVal, "scc", 3)) {
                devs[idx].is_ssc = true;
            }
            
            //设备编号,名称等
            snprintf(devs[idx].devname, MAX_NUM_DEVNAME, "/dev/i2c%d", i+1);
            devs[idx].DALDeviceID = DeviceID[i];
            devs[idx].index = idx;
            devs[idx].bus_active = 0;
            devs[idx].timer_created = 0;

            //QUP I2C设备初始化
            if (DAL_SUCCESS != I2CDEV_Init(DeviceID[i], &devs[idx].ahI2cDev)) {
               I2C_SLOGE("Failed to initialize I2C %s", devs[idx].devname);
               continue;
            }
            devs[idx].initialized = 1;

            //通过线程创建设备,资源管理器及接口。
            ret = pthread_create(&threadID, NULL, (void *)&device_main,
                    (void *)&devs[idx]);
            if (EOK == ret) {
                pthread_setname_np(threadID, devs[idx].devname);
                idx++;
            } else {
                I2C_SLOGE("Couldn't create RM thread for device-%d:"
                          "name-%s:ret-%d",
                          DeviceID[i], devs[idx].devname, ret);
            }
        }
    }

    if (ID_6155 == chip_id) {
        if ((ret = i2c_register_ssr())) {
            I2C_SLOGE("Failed to register for SSR ret=%x", ret);
            return -1;
        }
    }

    return 0;
}

 资源管理器创建通用步骤:

  1. 建立一个上下文切换句柄dpp = dispatch_create();这个东东主要用在mainloop中产生一个block特性,可以让我们等待接受消息;
  2. iofunc初始化。这一步是将自己实现的函数与POSIX层函数进行接口,解析从read、write、devctl等函数传来的消息进行解析,以实现底层与应用层函数之间的交互,通过io_funcs.read = io_read,io_funcs.write = io_write,进行函数重载;
  3. 注册设备名,使设备在命名空间中产生相应的名称,这一点是整个过程的关键了,形如 pathID = resmgr_attach (dpp, &rattr, "/dev/Null",_FTYPE_ANY, 0, &connect_funcs,&io_funcs, &ioattr),这样不仅注册了一个设备名,还让系统知道了我们实习的IO函数对应关系;
  4. 为之前创建的上下文句柄分配空间,例如ctp = dispatch_context_alloc (dpp);为了第六步使用;
  5. 通过不断循环等待dispatch_block()来调用MsgReceive()使Resource manger处于receive block状态,以接收上层发送来的消息,通过dispatch_handler (ctp)去调用我们自己定义的IO函数

 资源管理器创建实现:

 


int device_main(i2c_dev_t *dev)
{
    resmgr_connect_funcs_t  connect_funcs;
    resmgr_io_funcs_t   io_funcs;
    resmgr_attr_t       rattr;
    iofunc_funcs_t      ocb_funcs = { _IOFUNC_NFUNCS, _ocb_calloc, _ocb_free,
                                       NULL, NULL, NULL };
    iofunc_mount_t      mount = { 0, IOFUNC_PC_ACL, 0, 0, &ocb_funcs };
    I2CDEV_PowerStates  power_state = 0;
    pthread_condattr_t cond_attr = { 0 };

    /* Set up contiguous buffers */
    dev->fd_mmap = posix_typed_mem_open("/ram/dma",
                                        O_RDWR,
                                        POSIX_TYPED_MEM_ALLOCATE_CONTIG);
    if (dev->fd_mmap == -1) {
        I2C_SLOGE("%s: posix_typed_mem_open(/ram/dma) failed(%d-%s)",
                  __FUNCTION__, errno, strerror(errno));
        exit(1);
    }

    /* Configure clocks, gpios at init-time*/
    I2CDEV_GetPowerState(dev->ahI2cDev, &power_state);
    if (POWER_STATE_2 != power_state) {
        I2CDEV_SetPowerState(dev->ahI2cDev , POWER_STATE_2);
        dev->bus_active = 1;
    }

    pthread_mutex_init(&dev->mutex, NULL);

    pthread_condattr_init(&cond_attr);
    pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
    pthread_cond_init(&dev->cond, &cond_attr);

    //创建channel
    dev->chid = ChannelCreate(_NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK);
    if(dev->chid == -1) {
        I2C_SLOGE("ChannelCreate failed (%s)", strerror(errno));
        exit (1);
    }

    //第一步
    /*
     * allocate and initialize a dispatch structure for use by our
     * main loop
     */
    I2C_SLOGI("initializing %s\n",dev->devname);
    dev->dpp = dispatch_create_channel ( dev->chid, 0);
    if (dev->dpp == NULL)
    {
        I2C_SLOGE("couldn't dispatch_create: %s\n",strerror(errno));
        exit (1);
    }


    /*
     * set up the resource manager attributes structure, we'll
     * use this as a way of passing information to resmgr_attach().
     * For now, we just use defaults.
     */

    memset (&rattr, 0, sizeof (rattr)); /* using the defaults for rattr */
    rattr.nparts_max = 2; /* Max iov used by client is 2 */
    rattr.msg_max_size = MAX_NUM_I2C_WRITE;

    //第二步 iofunc初始化
    /*
     * intialize the connect functions and I/O functions tables to
     * their defaults by calling iofunc_func_init().
     *
     * connect_funcs, and io_funcs variables are already declared.
     *
     */
    iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
            _RESMGR_IO_NFUNCS, &io_funcs);

    /* over-ride the connect_funcs handler for open with our io_open,
     * and over-ride the io_funcs handlers for read and write with our
     * io_read and io_write handlers
     */
    connect_funcs.open = io_open;
    io_funcs.devctl = io_devctl;
    io_funcs.acl = io_acl;
    io_funcs.close_ocb = io_close;

    /* initialize our device description structure
    */

    iofunc_attr_init (&dev->hdr, S_IFCHR | 0666, NULL, NULL);

    dev->hdr.mount = &mount;   // so we can alloc an OCB per open

    iofunc_acl_init(&dev->hdr, iofunc_acl_posix_ctrl, 0);

    //第三步 注册设备
    /*
     *  call resmgr_attach to register our prefix with the
     *  process manager, and also to let it know about our connect
     *  and I/O functions.
     *
     *  On error, returns -1 and errno is set.
     */
    dev->pathID = resmgr_attach(dev->dpp,
                                &rattr,
                                dev->devname,
                                _FTYPE_ANY,
                                0,
                                &connect_funcs,
                                &io_funcs,
                                (RESMGR_HANDLE_T *)dev);
    if (dev->pathID == -1)
    {
        I2C_SLOGE(" couldn't attach pathname: %s\n", strerror (errno));
        exit (1);
    }

    //第四步 为dpp分配句柄空间
    dispatch_context_t *ctp;
    dev->ctp = dispatch_context_alloc(dev->dpp);
    if (dev->ctp == NULL)
    {
        dispatch_destroy(dev->dpp);
        return EXIT_FAILURE;
    }

    /* Notify bmetrics I2C device is ready */
    int fd = open("/dev/bmetrics", O_WRONLY);
    if (fd == -1) {
        I2C_SLOGE("Failed to open /dev/bmetrics with error:%d\n", errno);
    } else {
        char buf[BM_KPI_BUF_SIZE];
        snprintf(buf, BM_KPI_BUF_SIZE, "bootmarker DRIVER I2C Ready:%s\n",
                dev->devname);
        if (-1 == write(fd, buf, strlen(buf))) {
            I2C_SLOGE("Failed to write /dev/bmetrics with error:%d\n", errno);
        }
        close(fd);
    }

    /* register LPM pulses */
    if (EOK != i2c_register_pulse(dev)) {
        I2C_SLOGE("pulse registration for %s failed", dev->devname);
        exit(1);
    }

    /* register SSR pulses */
    dev->ssr_coid = ConnectAttach(ND_LOCAL_NODE, 0 /* pid */,
                                  dev->chid, _NTO_SIDE_CHANNEL, 0);
    if (-1 == dev->ssr_coid) {
        I2C_SLOGE("I2C_RM SSR ConnectAttach failed (%s)", strerror(errno));
        exit(1);
    }

    I2C_SLOGE("Initialized %s", dev->devname);

    //第五步 通过不断循环等待dispatch_block()来调用MsgReceive()使Resource manger处于receive block状态,以接收上层发送来的消息,通过dispatch_handler (ctp)去调用我们自己定义的IO函数
    while(1) {
        if ((ctp = dispatch_block(dev->ctp)) == NULL) {
            I2C_SLOGE("dispatch_block failed");
            exit(1);
        }
        dispatch_handler(dev->ctp);
    }

    return 0;
}

4.4 API与资源管理器之间的关联 

4.4.1 标准框架如下:

4.4.2 具体实现

 TODO

拿devctl为例: 

 

5. 梳理

6. 案例

TODO

//伪代码如下

#include <amss/i2c_client.h>

#define DEV_NAME "/dev/i2c_1"

int fd = -1;
int slaveAddr = 0xAA;

main()
{
    unsigned char writedata[3];
	writedata[0] = 0x11;
	writedata[1] = 0x22;
	writedata[2] = 0x22;

    fd = i2c_open(DEV_NAME );
    i2c_bus_lock ( fd );
    i2c_set_slave_addr(fd, slaveAddr, 0))
    i2c_write(fd, writedata, sizeof(writedata));
}

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SA8155 Audio框架是基于SA8155芯片的音频框架SA8155芯片是一种具有多种串行接口的可编程模块,包括UART、SPII2C和I3C。该芯片支持访问系统中的多个硬件实体,并通过内部的串行引擎 (SE)/QUP提供多达八个串行接口。每个接口可根据加载到SE的固件决定支持的协议,可以通过修改TZ中的QUPAC_Access文件加载所需的协议,如I2CSPI或UART。在QNX下开发驱动程序时,可以建立一个与操作系统兼容且支持POSIX的Resource manager框架。在开发过程中,可以利用main函数传递的参数int argc和char argv来传递Resource manager的具体参数,以实现不同可选的特性,并提供使用的便利性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [05-SA8155 QNX通过QUB配置GPIO/INT/SPI/I2C/SPI等](https://blog.csdn.net/u011006622/article/details/128231204)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [05-SA8155 QNX SPI框架代码分析](https://blog.csdn.net/liaochaoyun/article/details/127314018)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

村里小码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值