当前位置:   article > 正文

05-SA8155 QNX I2C框架及代码分析_8155跑qnx系统

8155跑qnx系统

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

  1. int i2c_service_base_init(void)
  2. {
  3. //调用i2c_drv 资源管理接口实现i2c资源管理器初始化
  4. if (-1 == i2c_drv_init()) {
  5. logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR,
  6. QCLOG_ERROR, "i2c_drv_init Failed");
  7. }
  8. drop_abilities_i2c();
  9. /* init sysctrl */
  10. if(DAL_SUCCESS != sysctrl_init()) {
  11. logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR,
  12. QCLOG_ERROR, "sysctrl_init failed, errno: %s", strerror(errno));
  13. return DAL_ERROR;
  14. }
  15. return DAL_SUCCESS;
  16. }
  17. int main(int argc, char *argv[]) {
  18. int c;
  19. ...
  20. /* QNX IO operation privilege */
  21. if (ThreadCtl(_NTO_TCTL_IO, 0) == -1)
  22. {
  23. PROCESS_ERROR_CRITICAL("i2c_service: ThreadCtl Error");
  24. return EXIT_FAILURE;
  25. }
  26. //信号SIGTERM处理
  27. signal(SIGTERM,handle_sigterm);
  28. // Check if process is running /dev/i2c_service
  29. if( 0 < open(DEVICE_NAME, O_RDONLY))
  30. {
  31. PROCESS_ERROR_CRITICAL("Process [%s] already running.. exiting.", DEVICE_NAME);
  32. return EXIT_FAILURE;
  33. }
  34. // Run process in background,进程后台运行
  35. Resource_Daemonize();
  36. //资源管理器实现接口
  37. if(i2c_service_base_init() != DAL_SUCCESS)
  38. {
  39. PROCESS_ERROR_CRITICAL("i2c_service: i2c_service_core_init failed");
  40. return EXIT_FAILURE;
  41. }
  42. //启动I2C资源管理器服务
  43. /* the i2c resource manager main loop is going to run after here */
  44. if(DAL_SUCCESS != sysctrl_start(DEVICE_NAME)) {
  45. PROCESS_ERROR_CRITICAL("i2c_service: service_init failed");
  46. return EXIT_FAILURE;
  47. }
  48. // Code should never reach here
  49. PROCESS_ERROR_CRITICAL("Exiting i2c_service..");
  50. return EXIT_SUCCESS;
  51. }

 查看系统进程:

# 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

 入口:

  1. /******************************************************************************/
  2. /***************************** startup and config ****************************/
  3. /******************************************************************************/
  4. int i2c_drv_init(void)
  5. {
  6. int i;
  7. int idx = 0;
  8. int ret = EOK;
  9. uint64_t chip_id = 0;
  10. const void *fdt_paddr = 0;
  11. pthread_t threadID;
  12. DALSYSPropertyVar PropVar;
  13. DALSYS_PROPERTY_HANDLE_DECLARE(hDALProps);
  14. DALSYS_InitMod(NULL);
  15. DALSYS_RegisterMod(&gDALModDriverInfoList);
  16. int policy;
  17. struct sched_param param;
  18. pthread_attr_t attr;
  19. if (waitfor_attach(QCORE_SERVICE, 5000))
  20. {
  21. I2C_SLOGE("Timed out waiting for %s to be ready", QCORE_SERVICE);
  22. return -1;
  23. }
  24. fdt_paddr = fdt_get_root();
  25. if (!fdt_paddr) {
  26. I2C_SLOGE("Failed to load device tree");
  27. return -1;
  28. }
  29. ret = fdt_foreach_subnode_byname((void*) fdt_paddr , "/chip_info",
  30. &get_chip_info, &chip_id);
  31. if (ret) {
  32. I2C_SLOGE("Failed to find dt chip_info");
  33. return -1;
  34. }
  35. I2C_SLOGI("Found chip id=%d", (int)chip_id);
  36. //线程配置
  37. pthread_attr_init(&attr);
  38. pthread_getschedparam(pthread_self(), &policy, &param);
  39. param.sched_priority = 100;
  40. pthread_attr_setschedparam(&attr, &param);
  41. devs = calloc(MAX_NUM_I2C_DEVS, sizeof(i2c_dev_t));
  42. if(devs == NULL)
  43. return -1;
  44. //创建设备
  45. // create devices , if specified via command line
  46. for(i = 0; i < MAX_NUM_I2C_DEVS; i++)
  47. {
  48. if(DALSYS_GetDALPropertyHandle(DeviceID[i], hDALProps) == DAL_SUCCESS)
  49. {
  50. //解析I2C配置,详情看QUP-I2C配置项
  51. if (DAL_SUCCESS != DALSYS_GetPropertyValue(hDALProps, "I2C_ENABLED", 0, &PropVar)
  52. || PropVar.Val.dwVal == 0)
  53. {
  54. continue;
  55. }
  56. if (DAL_SUCCESS != DALSYS_GetPropertyValue(hDALProps, "CLOCK_SE_NAME", 0, &PropVar)
  57. || PropVar.Val.pszVal == 0)
  58. {
  59. I2C_SLOGE("Failed to get property CLOCK_SE_NAME");
  60. return -1;
  61. }
  62. if (!strncmp(PropVar.Val.pszVal, "scc", 3)) {
  63. devs[idx].is_ssc = true;
  64. }
  65. //设备编号,名称等
  66. snprintf(devs[idx].devname, MAX_NUM_DEVNAME, "/dev/i2c%d", i+1);
  67. devs[idx].DALDeviceID = DeviceID[i];
  68. devs[idx].index = idx;
  69. devs[idx].bus_active = 0;
  70. devs[idx].timer_created = 0;
  71. //QUP I2C设备初始化
  72. if (DAL_SUCCESS != I2CDEV_Init(DeviceID[i], &devs[idx].ahI2cDev)) {
  73. I2C_SLOGE("Failed to initialize I2C %s", devs[idx].devname);
  74. continue;
  75. }
  76. devs[idx].initialized = 1;
  77. //通过线程创建设备,资源管理器及接口。
  78. ret = pthread_create(&threadID, NULL, (void *)&device_main,
  79. (void *)&devs[idx]);
  80. if (EOK == ret) {
  81. pthread_setname_np(threadID, devs[idx].devname);
  82. idx++;
  83. } else {
  84. I2C_SLOGE("Couldn't create RM thread for device-%d:"
  85. "name-%s:ret-%d",
  86. DeviceID[i], devs[idx].devname, ret);
  87. }
  88. }
  89. }
  90. if (ID_6155 == chip_id) {
  91. if ((ret = i2c_register_ssr())) {
  92. I2C_SLOGE("Failed to register for SSR ret=%x", ret);
  93. return -1;
  94. }
  95. }
  96. return 0;
  97. }

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

  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函数

 资源管理器创建实现:

 

  1. int device_main(i2c_dev_t *dev)
  2. {
  3. resmgr_connect_funcs_t connect_funcs;
  4. resmgr_io_funcs_t io_funcs;
  5. resmgr_attr_t rattr;
  6. iofunc_funcs_t ocb_funcs = { _IOFUNC_NFUNCS, _ocb_calloc, _ocb_free,
  7. NULL, NULL, NULL };
  8. iofunc_mount_t mount = { 0, IOFUNC_PC_ACL, 0, 0, &ocb_funcs };
  9. I2CDEV_PowerStates power_state = 0;
  10. pthread_condattr_t cond_attr = { 0 };
  11. /* Set up contiguous buffers */
  12. dev->fd_mmap = posix_typed_mem_open("/ram/dma",
  13. O_RDWR,
  14. POSIX_TYPED_MEM_ALLOCATE_CONTIG);
  15. if (dev->fd_mmap == -1) {
  16. I2C_SLOGE("%s: posix_typed_mem_open(/ram/dma) failed(%d-%s)",
  17. __FUNCTION__, errno, strerror(errno));
  18. exit(1);
  19. }
  20. /* Configure clocks, gpios at init-time*/
  21. I2CDEV_GetPowerState(dev->ahI2cDev, &power_state);
  22. if (POWER_STATE_2 != power_state) {
  23. I2CDEV_SetPowerState(dev->ahI2cDev , POWER_STATE_2);
  24. dev->bus_active = 1;
  25. }
  26. pthread_mutex_init(&dev->mutex, NULL);
  27. pthread_condattr_init(&cond_attr);
  28. pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
  29. pthread_cond_init(&dev->cond, &cond_attr);
  30. //创建channel
  31. dev->chid = ChannelCreate(_NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK);
  32. if(dev->chid == -1) {
  33. I2C_SLOGE("ChannelCreate failed (%s)", strerror(errno));
  34. exit (1);
  35. }
  36. //第一步
  37. /*
  38. * allocate and initialize a dispatch structure for use by our
  39. * main loop
  40. */
  41. I2C_SLOGI("initializing %s\n",dev->devname);
  42. dev->dpp = dispatch_create_channel ( dev->chid, 0);
  43. if (dev->dpp == NULL)
  44. {
  45. I2C_SLOGE("couldn't dispatch_create: %s\n",strerror(errno));
  46. exit (1);
  47. }
  48. /*
  49. * set up the resource manager attributes structure, we'll
  50. * use this as a way of passing information to resmgr_attach().
  51. * For now, we just use defaults.
  52. */
  53. memset (&rattr, 0, sizeof (rattr)); /* using the defaults for rattr */
  54. rattr.nparts_max = 2; /* Max iov used by client is 2 */
  55. rattr.msg_max_size = MAX_NUM_I2C_WRITE;
  56. //第二步 iofunc初始化
  57. /*
  58. * intialize the connect functions and I/O functions tables to
  59. * their defaults by calling iofunc_func_init().
  60. *
  61. * connect_funcs, and io_funcs variables are already declared.
  62. *
  63. */
  64. iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
  65. _RESMGR_IO_NFUNCS, &io_funcs);
  66. /* over-ride the connect_funcs handler for open with our io_open,
  67. * and over-ride the io_funcs handlers for read and write with our
  68. * io_read and io_write handlers
  69. */
  70. connect_funcs.open = io_open;
  71. io_funcs.devctl = io_devctl;
  72. io_funcs.acl = io_acl;
  73. io_funcs.close_ocb = io_close;
  74. /* initialize our device description structure
  75. */
  76. iofunc_attr_init (&dev->hdr, S_IFCHR | 0666, NULL, NULL);
  77. dev->hdr.mount = &mount; // so we can alloc an OCB per open
  78. iofunc_acl_init(&dev->hdr, iofunc_acl_posix_ctrl, 0);
  79. //第三步 注册设备
  80. /*
  81. * call resmgr_attach to register our prefix with the
  82. * process manager, and also to let it know about our connect
  83. * and I/O functions.
  84. *
  85. * On error, returns -1 and errno is set.
  86. */
  87. dev->pathID = resmgr_attach(dev->dpp,
  88. &rattr,
  89. dev->devname,
  90. _FTYPE_ANY,
  91. 0,
  92. &connect_funcs,
  93. &io_funcs,
  94. (RESMGR_HANDLE_T *)dev);
  95. if (dev->pathID == -1)
  96. {
  97. I2C_SLOGE(" couldn't attach pathname: %s\n", strerror (errno));
  98. exit (1);
  99. }
  100. //第四步 为dpp分配句柄空间
  101. dispatch_context_t *ctp;
  102. dev->ctp = dispatch_context_alloc(dev->dpp);
  103. if (dev->ctp == NULL)
  104. {
  105. dispatch_destroy(dev->dpp);
  106. return EXIT_FAILURE;
  107. }
  108. /* Notify bmetrics I2C device is ready */
  109. int fd = open("/dev/bmetrics", O_WRONLY);
  110. if (fd == -1) {
  111. I2C_SLOGE("Failed to open /dev/bmetrics with error:%d\n", errno);
  112. } else {
  113. char buf[BM_KPI_BUF_SIZE];
  114. snprintf(buf, BM_KPI_BUF_SIZE, "bootmarker DRIVER I2C Ready:%s\n",
  115. dev->devname);
  116. if (-1 == write(fd, buf, strlen(buf))) {
  117. I2C_SLOGE("Failed to write /dev/bmetrics with error:%d\n", errno);
  118. }
  119. close(fd);
  120. }
  121. /* register LPM pulses */
  122. if (EOK != i2c_register_pulse(dev)) {
  123. I2C_SLOGE("pulse registration for %s failed", dev->devname);
  124. exit(1);
  125. }
  126. /* register SSR pulses */
  127. dev->ssr_coid = ConnectAttach(ND_LOCAL_NODE, 0 /* pid */,
  128. dev->chid, _NTO_SIDE_CHANNEL, 0);
  129. if (-1 == dev->ssr_coid) {
  130. I2C_SLOGE("I2C_RM SSR ConnectAttach failed (%s)", strerror(errno));
  131. exit(1);
  132. }
  133. I2C_SLOGE("Initialized %s", dev->devname);
  134. //第五步 通过不断循环等待dispatch_block()来调用MsgReceive()使Resource manger处于receive block状态,以接收上层发送来的消息,通过dispatch_handler (ctp)去调用我们自己定义的IO函数
  135. while(1) {
  136. if ((ctp = dispatch_block(dev->ctp)) == NULL) {
  137. I2C_SLOGE("dispatch_block failed");
  138. exit(1);
  139. }
  140. dispatch_handler(dev->ctp);
  141. }
  142. return 0;
  143. }

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

4.4.1 标准框架如下:

4.4.2 具体实现

 TODO

拿devctl为例: 

 

5. 梳理

6. 案例

TODO

//伪代码如下

  1. #include <amss/i2c_client.h>
  2. #define DEV_NAME "/dev/i2c_1"
  3. int fd = -1;
  4. int slaveAddr = 0xAA;
  5. main()
  6. {
  7. unsigned char writedata[3];
  8. writedata[0] = 0x11;
  9. writedata[1] = 0x22;
  10. writedata[2] = 0x22;
  11. fd = i2c_open(DEV_NAME );
  12. i2c_bus_lock ( fd );
  13. i2c_set_slave_addr(fd, slaveAddr, 0))
  14. i2c_write(fd, writedata, sizeof(writedata));
  15. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/100186
推荐阅读
相关标签
  

闽ICP备14008679号