File still Updating.... many errors have been FOUND , need big change
qnx i2c structure --written by jlm
MSM8996AU/APQ8096AU has two BLSP blocks. Each block includes six Qualcomm® Universal
Peripheral (QUP) and six UART cores.
Bus Access Module (BAM) is used to move data to/from the peripheral buffers. BAM is a
hardware data mover, rather than a legacy data mover (ADM).
Each BLSP:
■ Is statically connected to a pair of BAM pipes.
■ Consists of 24 pipes for data move operations.
■ Supports BAM and non-BAM based data transfers.
BLSP supports the following protocols:
■ UART with data rates up to 4 Mbps. UART core can be configured as either a UART, SIM, or
IrDA interface.
■ I2C with data rates up to 1 Mbps
■ SPI bus with data rates up to 50 Mbps
在qnx上i2c的代码结构感觉有点乱 要理一理
5个地方
Services and resource managers
A resource manager is a user-level server program that accepts messages from other programs and communicates with hardware .
The resource manager sets up a pathname-space mapping by informing the process manager that it is the one responsible for handling requests at a certain mountpoint.
Core services initialize resource managers for each hardware driver. For example, i2c_service initializes I2C resource manager and spi_service initializes SPI resource manager.
In other words resource manager is the i2c driver
i2c server
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\services\daemons\i2c_service\src\i2cservice_main.c
resource manaer:
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\platform\resources\i2c_drv\i2c_drv.c
hardware drivers:
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\platform\hwdrivers\i2c\platsvc_i2c\src\I2cGpioCfg.c
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\platform\hwdrivers\i2c\platsvc_i2c\src\I2cPlatBsp.c
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\platform\hwdrivers\i2c\platsvc_i2c\src\I2cPlatSvc.c
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\platform\hwdrivers\i2c\common\src\I2cDeviceQup.c
qcore is the core service that establishes clock/power resource nodes for drivers. The qcore
service creates devcfg nodes for BSP drivers. Each BSP driver communicates with qcore over
devcfg nodes to enable power and clock resources. npa_client library provides APIs for drivers
to access devcfg nodes.
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\services\daemons\qcore\src\qcore_baseinit.c
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\services\daemons\qcore\src\qcore_main.c
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\platform\hwdrivers\i2c\devcfg_i2c\src\devcfg_i2c.c
QAL are the client libraries that provide APIs for user applications/services to access hardware
resource managers. QAL facilitates file I/O operations on /dev/ nodes to access hardware.
For example, to access SPI resource manager for an SPI device registered at /dev/spi*, QAL
spi_client provides the following APIs:
■ spi_open(…)
■ spi_write(…)
■ spi_read(…)
i2c client
Y:\home\gary\work\qnx_ap\3rdparty\AMSS\qal\clients\i2c_client\i2c_client.c
QNX DAL configuration (dalconfig) libraries are SoC and platform-specific hardware configuration
information libraries used by QNX DAL framework. Each BSP hardware driver extracts hardware
configuration from dalconfig at runtime. The configuration information is packed as a shared
library (dalconfig.so) and linked to drivers at runtime. Each driver is initialized after parsing
hardware configuration information from dalconfig library
QNX builds consist of the following types of configuration files:
■ SoC-specific configurations files at qnx_ap/AMSS/qal/dal/dalconfig/ --->currently not applicable for qnx i2c of this project
■ Board-specific configuration files at qnx_ap/boards/core/dalconfig/mojave_v2_8996/config --->for i2c configuration, here can configure the i2c slave device
i2c service start:
Y:\home\gary\work\qnx_ap\3rdparty\target\filesets\launcher_scripts\i2c.c
How to configure i2c in qnx
当script的参数传入i2c service_main的时候,首先对传入参数进行解析,走到X:\qnx_poc\qnx_workspace\hqx1.1-8.1-qnx\3rdparty\AMSS\services\daemons\i2c_service\src\i2cservice_main.c
int main(int argc, char *argv[]) { int c; uint32_t enable_mask = ; logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR, QCLOG_INFO, "main: %s %s", __DATE__, __TIME__); ) { switch( c ) { case 'v': slog2_set_verbosity ( amss_default_slog2_buffer , SLOG2_DEBUG2 ) ; break; case 'e': enable_mask |= (1u << atoi ( optarg )); break; case 'U': if(_secpol_in_use()) { if(set_ids_from_arg(optarg) != EOK ) { logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR, QCLOG_ERROR, "UID/GID setting failed"); return EXIT_FAILURE; } } break; default : break; } } if ( ! enable_mask ) { logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR, QCLOG_ERROR, "-e mandatory option missing, exiting"); return EXIT_FAILURE; } /* QNX IO operation privilege */ ) == -) { PROCESS_ERROR_CRITICAL("i2c_service: ThreadCtl Error"); return EXIT_FAILURE; } signal(SIGTERM,handle_sigterm); // Check if process is running < 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( enable_mask )!=DAL_SUCCESS) { PROCESS_ERROR_CRITICAL("i2c_service: i2c_service_core_init failed"); return EXIT_FAILURE; } drop_abilities_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; }
int i2c_service_base_init( uint32_t enable_mask ) { == i2c_drv_init(enable_mask)) { logger_log(QCLOG_AMSS_QNP_SERVICES_I2C_SERVICE, QCLOG_AMSS_I2C_SERVICE_MINOR, QCLOG_ERROR, "i2c_drv_init Failed"); } /* 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; }
然后调用X:\qnx_poc\qnx_workspace\hqx1.1-8.1-qnx\3rdparty\AMSS\platform\resources\i2c_drv\i2c_drv.c
/******************************************************************************/ /***************************** startup and config ****************************/ /******************************************************************************/ int i2c_drv_init(uint32_t enable_mask) { int i; ; pthread_t threadID; logger_log(QCLOG_AMSS_CORE, QCLOG_AMSS_QNP_PLATFORM_I2C_DRV, QCLOG_INFO, "I2C RM starting up (enable_mask=0x%x)\n", enable_mask); devs = calloc(MAX_NUM_I2C_DEVS, sizeof(i2c_dev_t)); if(devs == NULL) ; // create devices , if specified via command line ; i <= MAX_NUM_I2C_DEVS; i++) { //Is this device enabled ? if ( enable_mask & (1U << i)) { logger_log(QCLOG_AMSS_CORE, QCLOG_AMSS_QNP_PLATFORM_I2C_DRV, QCLOG_INFO, "Initializing device 0x%x (i2c%d)" , DeviceID[i] , i ) ; devs[idx].DALDeviceID = DeviceID[i]; //if i2c8 =DALDEVICEID_I2C_DEVICE_8 i=7 devs[idx].index = idx; //idx =2 -e 6,-e 7, -e 8, devs[idx].timer_created = ; devs[idx].bus_active = ; I2CDEV_Init(DeviceID[i], &devs[idx].ahI2cDev); snprintf(devs[idx].devname, MAX_NUM_DEVNAME, "/dev/i2c%d", i); pthread_create(&threadID, NULL, (void *) &device_main, (void *)&devs[idx]); pthread_setname_np(threadID,devs[idx].devname); idx++; } } ; }
先跳到函数I2CDEV_Init中X:\qnx_poc\qnx_workspace\hqx1.1-8.1-qnx\3rdparty\AMSS\platform\hwdrivers\i2c\common\src\I2cDeviceQup.c
/** @brief Initializes the device. This Function Initializes the device and creates the necessary data structures to support other calls into the device. @param[in] uPlatDevId Platform Device ID. @param[inout] phDev Pointer to device handle. @return int32 . */ int32 I2CDEV_Init ( uint32 uPlatDevId, I2CDEV_HANDLE *phDev ) { int32 res = I2C_RES_SUCCESS; I2CDEVQUP_Device *pDev = NULL; uint32 uWhileOnce = ; //search if the dev exists in the dev list pDev = I2CDEVQUP_SearchDevice(uPlatDevId); if ( NULL != pDev ) { *phDev = pDev; return I2C_RES_SUCCESS; } res = (int32)I2CSYS_Malloc((void **)&pDev, sizeof(I2CDEVQUP_Device)); if ( I2C_RES_SUCCESS != res ) { return res; } res = I2CSYS_Memset(pDev, , sizeof(I2CDEVQUP_Device)); if ( I2C_RES_SUCCESS != res ) { I2CSYS_Free(pDev); return res; } pDev->uPlatDevId = uPlatDevId; pDev->devInitState = I2CDEVQUP_InitState_Uninitialized; do { res = (int32)I2CPLATDEV_InitTarget(uPlatDevId, &pDev->hDevTgt);//the uPlatDevId =DALDEVICEID_I2C_DEVICE_8 0x020000C7 if ( I2C_RES_SUCCESS != res ) { break; } pDev->devInitState |= I2CDEVQUP_InitState_TargetInit_Done; res = (int32)I2CPLATDEV_ReadProperties(pDev->hDevTgt, &pDev->devProps); if ( I2C_RES_SUCCESS != res ) { break; } pDev->seqInfo.outSeqInfo.pSeq = &pDev->seqInfo; pDev->seqInfo.inSeqInfo.pSeq = &pDev->seqInfo; if ( pDev->devProps.bBamAvailable ) { /* allocate bam buffer. */ pDev->seqInfo.physMem.uSize = *BAM_TMPBUFF_MAX;// input+output res = I2CSYS_PhysMemAlloc(&pDev->seqInfo.physMem); if (I2C_RES_SUCCESS != res) { break; } if ( !I2cCacheCtrlInited ) { res = cache_init(, &I2cCacheCtrl, NULL); if (I2C_RES_SUCCESS != res) { break; } I2cCacheCtrlInited = ; } pDev->devInitState |= I2CDEVQUP_InitState_PhysMemAlloc_Done; /* use top half for output and bottom half for input. */ pDev->seqInfo.outSeqInfo.aOutBamBuff = pDev->seqInfo.physMem.pVirtAddress; pDev->seqInfo.inSeqInfo.aInBamBuff = &pDev->seqInfo.outSeqInfo.aOutBamBuff[BAM_TMPBUFF_MAX]; } if ( pDev->devProps.bInterruptBased ) { res = (int32)I2CSYS_CreateEvent(&pDev->hQupEvt); if ( I2C_RES_SUCCESS != res ) { break; } pDev->devInitState |= I2CDEVQUP_InitState_Events_Done; } res = I2CSYS_CreateCriticalSection(&pDev->hOperationSync); if ( I2C_RES_SUCCESS != res ) { break; } pDev->devInitState |= I2CDEVQUP_InitState_CritSectionCreate_Done; pDev->clntCfg.uBusFreqKhz = I2CDEVQUP_DEFAULT_BUS_FREQ_KHZ; pDev->clntCfg.uByteTransferTimeoutUs = I2CDEVQUP_DEFAULT_BYTE_TRANSFER_TIMEOUT_US; pDev->devInitState |= I2CDEVQUP_InitState_DeviceInit_Done; pDev->devPowerState = I2CDEV_POWER_STATE_0; /* Init device logging. */ I2cLog_Init((uint32)pDev->uPlatDevId, &pDev->pDevLog); I2cLog_AssociateBlockAddress(pDev->pDevLog, (uint64)pDev->devProps.virtBlockAddr); I2CDEVQUP_LinkDevice(pDev); *phDev = pDev; } while ( uWhileOnce ); if ( I2C_RES_SUCCESS != res ) { /* in case of error undo initialization. */ I2CDEV_DeInit(pDev); } return res; }
跳转到X:\qnx_poc\qnx_workspace\hqx1.1-8.1-qnx\3rdparty\AMSS\platform\hwdrivers\i2c\platsvc_i2c\src\I2cPlatSvc.c
/** This Function initializes target support structures and subsystems. @param[in] uPlatDevId Platform specific device identifier. @param[out] phPlat Pointer to platform handle. @return I2C_RES_SUCCESS is successful, otherwise I2CPLATSVC_Error. */ int32 I2CPLATDEV_InitTarget ( uint32 uPlatDevId, I2CPLATDEV_HANDLE *phPlat ) { I2CPLATDEV_TargetCfgType *pDev; int32 res; res = I2CSYS_Malloc((void **)&pDev, sizeof(I2CPLATDEV_TargetCfgType)); if (I2C_RES_SUCCESS != res) { return res; } res = I2CSYS_Memset(pDev, , sizeof(I2CPLATDEV_TargetCfgType)); if ( I2C_RES_SUCCESS != res ) { I2CSYS_Free(pDev); return res; } pDev->initState = I2CPLATDEV_TGT_INIT_NOT_DONE; do { res = I2CPLATDEV_ReadPlatConfig(uPlatDevId, &pDev->platProps);//define DALDEVICEID_I2C_DEVICE_8 0x020000C7 if ( I2C_RES_SUCCESS != res ) { break; } if ( !_secpol_in_use ( ) ) { , (PROCMGR_AID_IO | PROCMGR_AOP_ALLOW | PROCMGR_ADN_NONROOT), (PROCMGR_AID_INTERRUPT | PROCMGR_AOP_ALLOW | PROCMGR_ADN_NONROOT), (PROCMGR_AID_MEM_PHYS | PROCMGR_AOP_ALLOW | PROCMGR_ADN_NONROOT), PROCMGR_AID_EOL)) { res = I2CPLATSVC_ERROR_FAILED_ABILITIES; break; } } == ThreadCtl(_NTO_TCTL_IO, )) { res = I2CPLATSVC_ERROR_FAILED_THREAD_CTRL; break; } res = I2CPLATDEV_InitHwio(pDev); if ( I2C_RES_SUCCESS != res ) { break; } res = I2CPLATDEV_NpaInit(pDev); if(I2C_RES_SUCCESS != res) { break; } pDev->uPlatDevId = uPlatDevId; *phPlat = (I2CPLATDEV_HANDLE) pDev; res = I2C_RES_SUCCESS; } ); if ( I2C_RES_SUCCESS != res ) { I2CPLATDEV_DeInitTarget(pDev); } return res; }
先根据传进来的地址去进行匹配X:\qnx_poc\qnx_workspace\hqx1.1-8.1-qnx\3rdparty\AMSS\platform\hwdrivers\i2c\platsvc_i2c\src\I2cPlatBsp.c
i2c_8996_bsp里面包含了中断号,dev name resourceName, 地址,block size,还有resource name, 这个是和qcore 那边的devcfg_i2c.c 对应的
/*------------------------------------------------------------------------- * Global vars * ----------------------------------------------------------------------*/ I2CPLATDEV_PlatPropertiesType i2c_8996_bsp[] = { { // device 1 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C1_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c1", // pDevName; "/dev/bam0", // pBamName "/devcfg/i2c/1", // resourceName "I2C_CORE_1", // coreName (uint8 *)0x7575000, // uBlkPhysAddr; 0x1000, // uBlkSize , // bBamEnabled; (uint8 *)BAM1_BLOCK_ADDRESS, // uBamPhysAddr; BAM1_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 2 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C2_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c2", // pDevName; "/dev/bam0", // pBamName "/devcfg/i2c/2", // resourceName "I2C_CORE_2", // coreName (uint8 *)0x7576000, // uBlkPhysAddr; 0x1000, // uBlkSize , // bBamEnabled; (uint8 *)BAM1_BLOCK_ADDRESS, // uBamPhysAddr; BAM1_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 3 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C3_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c3", // pDevName; "/dev/bam0", // pBamName "/devcfg/i2c/3", // resourceName "I2C_CORE_3", // coreName (uint8 *)0x7577000, // uBlkPhysAddr; 0x1000, // uBlkSize , // bBamEnabled; (uint8 *)BAM1_BLOCK_ADDRESS, // uBamPhysAddr; BAM1_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 4 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C4_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c4", // pDevName; "/dev/bam0", // pBamName "/devcfg/i2c/4", // resourceName "I2C_CORE_4", // coreName (uint8 *)0x7578000, // uBlkPhysAddr; 0x1000, // uBlkSize , // bBamEnabled; (uint8 *)BAM1_BLOCK_ADDRESS, // uBamPhysAddr; BAM1_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 5 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C5_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c5", // pDevName; "/dev/bam0", // pBamName "/devcfg/i2c/5", // resourceName "I2C_CORE_5", // coreName (uint8 *)0x7579000, // uBlkPhysAddr; 0x1000, // uBlkSize , // bBamEnabled; (uint8 *)BAM1_BLOCK_ADDRESS, // uBamPhysAddr; BAM1_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 6 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C6_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c6", // pDevName; "/dev/bam0", // pBamName "/devcfg/i2c/6", // resourceName "I2C_CORE_6", // coreName (uint8 *)0x757A000, // uBlkPhysAddr; 0x1000, // uBlkSize , // bBamEnabled; (uint8 *)BAM1_BLOCK_ADDRESS, // uBamPhysAddr; BAM1_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 7 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C7_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c7", // pDevName; "/dev/bam1", // pBamName "/devcfg/i2c/7", // resourceName "I2C_CORE_7", // coreName (uint8 *)0x75B5000, // uBlkPhysAddr; 0x1000, // uBlkSize; , // bBamEnabled; (uint8 *)BAM2_BLOCK_ADDRESS, // uBamPhysAddr; BAM2_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 8 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C8_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c8", // pDevName; "/dev/bam1", // pBamName "/devcfg/i2c/8", // resourceName "I2C_CORE_8", // coreName (uint8 *)0x75B6000, // uBlkPhysAddr; 0x1000, // uBlkSize; , // bBamEnabled; (uint8 *)BAM2_BLOCK_ADDRESS, // uBamPhysAddr; BAM2_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 9 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C9_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c9", // pDevName; "/dev/bam1", // pBamName "/devcfg/i2c/9", // resourceName "I2C_CORE_9", // coreName (uint8 *)0x75B7000, // uBlkPhysAddr; 0x1000, // uBlkSize; , // bBamEnabled; (uint8 *)BAM2_BLOCK_ADDRESS, // uBamPhysAddr; BAM2_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 10 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C10_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c10", // pDevName; "/dev/bam1", // pBamName "/devcfg/i2c/10", // resourceName "I2C_CORE_10", // coreName (uint8 *)0x75B8000, // uBlkPhysAddr; 0x1000, // uBlkSize; , // bBamEnabled; (uint8 *)BAM2_BLOCK_ADDRESS, // uBamPhysAddr; BAM2_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 11 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C11_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c11", // pDevName; "/dev/bam1", // pBamName "/devcfg/i2c/11", // resourceName "I2C_CORE_11", // coreName (uint8 *)0x75B9000, // uBlkPhysAddr; 0x1000, // uBlkSize; , // bBamEnabled; (uint8 *)BAM2_BLOCK_ADDRESS, // uBamPhysAddr; BAM2_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, { // device 12 , // bInterruptBased; , // bDisablePm; , // uInterruptId; HAL_QGIC_TRIGGER_LEVEL, // uIrqTriggerCfg; I2C12_CLOCK_RATE_KHZ, // uI2cSrcClkKhz; "/dev/i2c12", // pDevName; "/dev/bam1", // pBamName "/devcfg/i2c/12", // resourceName "I2C_CORE_12", // coreName (uint8 *)0x75BA000, // uBlkPhysAddr; 0x1000, // uBlkSize; , // bBamEnabled; (uint8 *)BAM2_BLOCK_ADDRESS, // uBamPhysAddr; BAM2_IRQ, // uBamIrqId; BAM_THRESHOLD, // uBamThreshold; , // uBamInPipeId; // uBamOutPipeId; }, }; /** @brief Gets device-specific configuration settings. @param[in] uPlatDevId Platform device ID. @param[out] pDev Pointer to platform device structure. @return I2CPLATSVC_Error type: I2C_RES_SUCCESS if successful, otherwise error. */ int32 I2CPLATDEV_ReadPlatConfig ( uint32 uPlatDevId, I2CPLATDEV_PlatPropertiesType *pDev ) { int dev_id_idx; switch(uPlatDevId) { ; break; ; break; ; break; ; break; ; break; ; break; ; break; ; break; ; break; ; break; ; break; ; break; default: return I2CBSP_INVALID_PLAT_ID; } if ( dev_id_idx > (sizeof(i2c_8996_bsp)/sizeof(I2CPLATDEV_PlatPropertiesType)) ) { return I2CBSP_INVALID_PLAT_ID; } // copy element into location *pDev = i2c_8996_bsp[dev_id_idx]; return I2CBSP_SUCCESS; }
Note:
according to the datasheet the IRQ defined above is not the same as described below, due to the reason that : interrupt number 0~31 was reserved by the system so, need to +32 to get the actual interrput numer
like 95+32=127 for device 1and for device 8 (gpio6/7) 102+32=134 (match the number)
you can refer to https://blog.csdn.net/phenix_lord/article/details/45116259 as reference
然后去init hw 和创建npa client X:\qnx_poc\qnx_workspace\hqx1.1-8.1-qnx\3rdparty\AMSS\platform\hwdrivers\i2c\platsvc_i2c\src\I2cPlatSvc.c
/** @brief Initializes the clocks for the qup core. @param[in] pDev Pointer to platform device structure. @return I2C_RES_SUCCESS is successful, otherwise I2CPLATSVC_Error. */ int32 I2CPLATDEV_NpaInit ( I2CPLATDEV_TargetCfgType *pDev ) { const char* resources [] = { pDev->platProps.resourceName }; npa_resources_available_waitfor ( , resources ) ; /*Create a npa handle for the i2c core*/ pDev->npaRes.npaHandle = npa_create_sync_client(pDev->platProps.resourceName, pDev->platProps.coreName, NPA_CLIENT_REQUIRED); if (NULL == pDev->npaRes.npaHandle) { return I2CPLATSVC_ERROR_FAILED_TO_SYNC_CLIENT; } #if 0 do not issue Initialize request here... this would configure gpios for i2c ports that may never be used... instead wait for open. npa_issue_required_request(pDev->npaRes.npaHandle, I2C_DEVCFG_STATE_INITIALIZE); #endif return I2C_RES_SUCCESS; }
So if we want to add or remove or change the device name , modified the file above
另外一条线就是qcore
X:\qnx_poc\qnx_workspace\hqx1.1-8.1-qnx\3rdparty\AMSS\services\daemons\qcore\src\qcore_baseinit.c
int qcore_base_init( uint32 initFlags ) { /* Init Dalsys */ ... logger_log(QCLOG_AMSS_CORE, QCLOG_AMSS_QCORE_BASEINIT, QCLOG_INFO, "i2c_devcfg_init()"); i2c_devcfg_init ( NULL ) ; ... }
/* Initialization function.*/ int i2c_devcfg_init( const char * cmdline ) { int i; ; i < BLSP_NUM; i++) { npa_resource_available_cb(blsp[i].resource_name, uart_init_cb, &blsp[i].blsp_instance); } npa_define_node(&i2c_node, NULL,NULL); ; }
i2c_node 定义
static npa_node_definition i2c_node = { "/node/devcfg/i2c", // Node name - used when making dependency requests i2c_npa_fcn, // Node Driver Function - From code above NPA_NODE_DEFAULT, // Attributes NULL, // User Data - In case you need Node specific data NPA_EMPTY_ARRAY, // Node Dependencies - From code above NPA_ARRAY(i2c_resource) // Node Resources - From code above };
以device_8 为例子
里面定义了i2c data的pin, clk的pin, gpio funciton,
{ DALDEVICEID_I2C_DEVICE_8, "gcc_blsp2_ahb_clk", "gcc_blsp2_qup2_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { "/devcfg/i2c/8", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL },
查找pin assignment
根据上面的表格,pin的funciton就是3
下面是所有的i2c 的definition
static i2c_npa_info i2c_usr_info[I2C_NUM_DEVICES] = { { DALDEVICEID_I2C_DEVICE_1, "gcc_blsp1_ahb_clk", "gcc_blsp1_qup1_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_2, "gcc_blsp1_ahb_clk", "gcc_blsp1_qup2_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_3, "gcc_blsp1_ahb_clk", "gcc_blsp1_qup3_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_4, "gcc_blsp1_ahb_clk", "gcc_blsp1_qup4_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_5, "gcc_blsp1_ahb_clk", "gcc_blsp1_qup5_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_6, "gcc_blsp1_ahb_clk", "gcc_blsp1_qup6_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_7, "gcc_blsp2_ahb_clk", "gcc_blsp2_qup1_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_8, //# define DALDEVICEID_I2C_DEVICE_8 0x020000C7 "gcc_blsp2_ahb_clk", "gcc_blsp2_qup2_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_9, "gcc_blsp2_ahb_clk", "gcc_blsp2_qup3_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_10, "gcc_blsp2_ahb_clk", "gcc_blsp2_qup4_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_11, "gcc_blsp2_ahb_clk", "gcc_blsp2_qup5_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, { DALDEVICEID_I2C_DEVICE_12, "gcc_blsp2_ahb_clk", "gcc_blsp2_qup6_i2c_apps_clk", , , , // data_gpio_number , // data_gpio_function , // clk_gpio_number , // clk_gpio_function I2C_DEVCFG_STATE_DEINITIALIZE }, }; /*Each resource is tied to a single node*/ static npa_resource_definition i2c_resource[] = { { "/devcfg/i2c/1", /*Resource Name*/ "I2C State", /* Resource Units - Informational only*/ I2C_DEVCFG_STATE_ON, /* Resource Max*/ &npa_max_plugin, /* Define how clients are aggregated*/ /* We are using a predefined aggregation*/ NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, /* Default resource behavior*/ &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/2", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/3", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/4", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/5", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/6", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/7", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/8", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/9", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/10", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/11", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, { "/devcfg/i2c/12", "I2C State", I2C_DEVCFG_STATE_ON, &npa_max_plugin, NPA_RESOURCE_DEFAULT|NPA_RESOURCE_INTERPROCESS_ACCESS_ALLOWED, &i2c_usr_info[], i2c_npa_query_fcn, NULL }, };
i2c definition
也就是说一部分是放在了qcore的devcfg_i2c.c中,一部风是在 resource manager里面去定义,
创建npa node的时候,会去调用 /devcfg/i2c/8 去创建
How to add slave in qnx
For this section I need to verify and todo further study
Not like pure android platform, the qnx will define the slave node in the xml file
for example:
the ti_uh949q bridge should be attached to the i2c8 , search the i2c8 and found
for example the bridge attached under i2c8 and the slave address is 0x0c
How to add new i2c
For this section, please see section i2c_service_start
新增加一个i2c1 发现改了 script.c之后还是不能创建,创建i2c1 就core dump, 发现时下面的原因,在\\192.168.242.128\share\home\jinluming\work\qnx_spi\hqx1.1-8.1-qnx\3rdparty\target\filesets\secpol\i2c.txt 中
没有定义i2c1 的中断
blsp2_qup_irq_0,blsp1_qup_irq_5,blsp2_qup_irq_1所以会崩溃改为或者增加一个blsp1_qup_irq_0 -->对应i2c1 就可以了spi估计也一样 要特别注意
# === Rules for type i2c_service_t ========== allow i2c_service_t self:ability { nonroot setuid:I2C_SERVICE_UID setgid:I2C_SERVICE_GID,QCORE_GID #TODO: Interrupt is handled/attached during every write/read. Hence ability is not dropped interruptevent:blsp2_qup_irq_0,blsp1_qup_irq_5,blsp2_qup_irq_1 mem_phys:PERIPH_SS.PERIPH_SS_BLSP1_BLSP.QUP5_DD_4K mem_phys:PERIPH_SS.PERIPH_SS_BLSP2_BLSP.QUP0_DD_4K mem_phys:PERIPH_SS.PERIPH_SS_BLSP2_BLSP.QUP1_DD_4K mem_phys:__DMA_POOL_START__-__DMA_POOL_END__ iofunc/exec io: spawn public_channel xthread_threadctl pathspace channel_connect mem_peer mem_special keydata rlimit }; allow i2c_service_t self:ability { nonroot unlock map_fixed prot_exec prot_write_and_exec }; allow_attach i2c_service_t { /dev/i2c* /dev/i2c_service /dev/npa /dev/pmem/i2c_service }; allow i2c_service_t { qcore_t slogger2_t smmu_service_t bmetrics_t }:channel connect;
the call sequence of probe/remove
use the i2cdbgr as the example:
the i2cdbgr will call function i2c_combined_writeread to read the i2c register
Y:\home\jinluming\work\qnx_ap\3rdparty\AMSS\qal\clients\i2c_client\i2c_client.c
int i2c_combined_writeread(int fd, void * wbuff, uint32_t wlen, void* rbuff, uint32_t rlen ) { ; i2c_sendrecv_t msg; iov_t wiov[]; iov_t riov[]; ){ ; } printf("jlm i2c client i2c_combined_writeread\n"); logger_log(QCLOG_AMSS_QNP_QAL_I2C_CLIENT, __LINE__, QCLOG_ERROR, "jlm i2c_combined_writeread"); msg.send_len = wlen; msg.recv_len = rlen; msg.stop = ; // always stop condition msg.slave.addr = ; // use current slave SETIOV(wiov+, &msg, sizeof(msg)); SETIOV(wiov+, wbuff, wlen); SETIOV(riov+, &msg, sizeof(msg)); SETIOV(riov+, rbuff, rlen); , , wiov, riov, &retval)!=) { ; } return retval; } i2c_combined_writeread
The i2c_client provides the APIs for the applications to call, in the function it will call
The cmd DCMD_I2C_SENDRECV will be transfered to file i2c_drv.c
in the function int i2c_drv_init(uint32_t enable_mask) it will first configure the i2c hw according to the configuration by invoking the function I2CDEV_Init(DeviceID[i], &devs[idx].ahI2cDev); and then create maximum 12 thread to monitor the device cmd in the function int device_main(i2c_dev_t * dev)
check below:
the function io_devctl will act accordingly.
static int io_devctl (resmgr_context_t *ctp, io_devctl_t *msg, iofunc_ocb_t *ocb_t) { int status; #ifdef PUBLIC_FEATURE_RIM_ALL int n; #else uint32_t n; #endif uint32_t * busSpd; i2c_addr_t * addrdata; unsigned char *buf; void * inputPtr; i2c_recv_t* rheader; i2c_send_t* wheader; i2c_masterhdr_t* mheader; i2c_sendrecv_t * wrheader; i2c_ocb_t * ocb = (i2c_ocb_t *)ocb_t; #ifdef PUBLIC_FEATURE_RIM_ALL /* * Validate access permission: * Check the I2C device was opened with both read and write permission (i.e. O_RDWR). */ if ((ctp == NULL) || (msg == NULL) || (ocb == NULL)) { return EINVAL; } if ((ocb->hdr.ioflag & _IO_FLAG_RD) != _IO_FLAG_RD) { return EPERM; } if ((ocb->hdr.ioflag & _IO_FLAG_WR) != _IO_FLAG_WR) { return EPERM; } #endif status = iofunc_devctl_default(ctp, msg, (iofunc_ocb_t *)ocb); if (status != _RESMGR_DEFAULT) return status; //Perform a task based on DCMD value switch (msg->i.dcmd) { case DCMD_I2C_SET_SLAVE_ADDR: if (msg->i.nbytes != sizeof(i2c_addr_t)) return EINVAL; addrdata = _DEVCTL_DATA(msg->i); status = i2clib_set_slave_addr(ocb->handle, addrdata->addr, addrdata->fmt); if(status != DAL_SUCCESS) { return EINVAL; } msg->o.ret_val=; return _RESMGR_PTR(ctp, msg, sizeof(msg->o)); break; case DCMD_I2C_SET_BUS_SPEED: if (msg->i.nbytes != sizeof(uint32_t)) return EINVAL; busSpd = _DEVCTL_DATA(msg->i); msg->o.ret_val=i2clib_set_bus_speed(ocb->handle, *busSpd, busSpd); return _RESMGR_PTR(ctp, msg, sizeof(msg->o)); break; case DCMD_I2C_MASTER_SEND: inputPtr = _DEVCTL_DATA(msg->i); mheader = inputPtr; buf = inputPtr + (sizeof(i2c_masterhdr_t)); //clears the mem buffer memset(&msg->o, , sizeof(msg->o)); // num bytes actually written msg->o.ret_val = i2clib_write(ocb->handle, buf, mheader->len); msg->o.nbytes = ; #ifdef PUBLIC_FEATURE_RIM_ALL ) { return EIO; } #endif SETIOV(&ctp->iov[], msg, sizeof(msg->o)); ); case DCMD_I2C_SEND: inputPtr = _DEVCTL_DATA(msg->i); wheader = inputPtr; buf = inputPtr + (sizeof(i2c_send_t)); ) return EINVAL; //clears the mem buffer memset(&msg->o, , sizeof(msg->o)); // num bytes actually written msg->o.ret_val = i2clib_write(ocb->handle, buf, wheader->len); msg->o.nbytes = ; #ifdef PUBLIC_FEATURE_RIM_ALL ) { return EIO; } #endif SETIOV(&ctp->iov[], msg, sizeof(msg->o)); ); case DCMD_I2C_MASTER_RECV: inputPtr = _DEVCTL_DATA(msg->i); mheader = inputPtr; // num bytes actually written n = i2clib_read(ocb->handle, ocb->xbuf, mheader->len); memset(&msg->o, , sizeof(msg->o)); msg->o.ret_val = n; ) { msg->o.nbytes = sizeof(*mheader)+n; SETIOV(ctp->iov, &msg->o, sizeof(*msg)+sizeof(*mheader)); SETIOV(ctp->iov+, ocb->xbuf, n); } else { #ifdef PUBLIC_FEATURE_RIM_ALL return EIO; #else msg->o.nbytes = sizeof(*mheader); SETIOV(ctp->iov, &msg->o, sizeof(*msg)+sizeof(*mheader)); SETIOV(ctp->iov+, ocb->xbuf, ); #endif } ); case DCMD_I2C_RECV: inputPtr = _DEVCTL_DATA(msg->i); rheader = inputPtr; ) return EINVAL; // num bytes actually written n = i2clib_read(ocb->handle, ocb->xbuf, rheader->len); memset(&msg->o, , sizeof(msg->o)); msg->o.ret_val = n; ) { msg->o.nbytes = sizeof(*rheader)+n; SETIOV(ctp->iov, &msg->o, sizeof(*msg)+sizeof(*rheader)); SETIOV(ctp->iov+, ocb->xbuf, n); } else { #ifdef PUBLIC_FEATURE_RIM_ALL return EIO; #else msg->o.nbytes = sizeof(*rheader); SETIOV(ctp->iov, &msg->o, sizeof(*msg)+sizeof(*rheader)); SETIOV(ctp->iov+, ocb->xbuf, ); #endif } ); case DCMD_I2C_SENDRECV: inputPtr = _DEVCTL_DATA(msg->i); wrheader = inputPtr; printf("jlm i2c_drv DCMD_I2C_SENDRECV\n"); logger_log(QCLOG_AMSS_CORE, QCLOG_AMSS_QNP_PLATFORM_I2C_DRV, QCLOG_ERROR,"jlm DCMD_I2C_SENDRECV %s\n"); if(wrheader->slave.addr) // specifying/changing slave? { ) return EINVAL; } buf = inputPtr + (sizeof(i2c_sendrecv_t)); n = i2clib_combined_writeread(ocb->handle, buf, wrheader->send_len, ocb->xbuf, wrheader->recv_len); memset(&msg->o, , sizeof(msg->o)); msg->o.ret_val = n; ) { msg->o.nbytes = sizeof(*wrheader)+n; SETIOV(ctp->iov, &msg->o, sizeof(msg->o)+sizeof(*wrheader)); SETIOV(ctp->iov+, ocb->xbuf, n); } else { #ifdef PUBLIC_FEATURE_RIM_ALL return EIO; #else msg->o.nbytes = sizeof(*wrheader); SETIOV(ctp->iov, &msg->o, sizeof(msg->o)+sizeof(*wrheader)); SETIOV(ctp->iov+, ocb->xbuf, ); #endif } ); case DCMD_I2C_LOCK: memset(&msg->o, , sizeof(msg->o)); msg->o.ret_val = i2clib_bus_lock(ocb->handle); return _RESMGR_PTR(ctp, msg, sizeof(msg->o)); case DCMD_I2C_UNLOCK: memset(&msg->o, , sizeof(msg->o)); msg->o.ret_val = i2clib_bus_unlock(ocb->handle); return _RESMGR_PTR(ctp, msg, sizeof(msg->o)); case DCMD_I2C_DRIVER_INFO: if (msg->i.nbytes != sizeof(i2c_driver_info_t)) return EINVAL; break; default: return ENOSYS; // The device doesn't support the dcmd command. } return ENOSYS; } io_devctl
New info will coming soon....
How to test/verify i2c
currently we could use i2cdbgr to test/very the i2c . BUT I dont like this tool I am re-coding this test tool , I prefer to do like i2c-tools
i2ctool first version:
%C : Application to perform i2c read / write /detect /dump /scan Author: gary Usage: i2ctool fd slaveAddr read reg [count] i2ctool fd slaveAddr write reg val i2ctool detect dump $fd i2ctool detect i2ctool detect loop $fd where fd is of form "/dev/i2cX". The i2ctool uses -bit addressing, -bit data to read or write from /dev/i2cX (fd) using i2c_client.h using default frequency. Examples: //read . i2ctool $fd $slaveAddr read $reg [$val] //write . i2ctool $fd $slaveAddr write $reg $val //dump all the registers for device $fd and slaveaddr . i2ctool detect dump $fd //list all the i2c device that currently created . i2ctool detect //scan for the i2c device if it has the slaves or not . i2ctool detect loop $fd i2ctool usage
/* ========================================================================= @file i2ctool.c @brief Application, R/W i2c devices using 8-bit address/data, default freq. Copyright (c) 2013 by QUALCOMM Technologies Incorporated. All Rights Reserved. $Header$ ============================================================================ */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/neutrino.h> #include <hw/inout.h> #include <fcntl.h> #include <ctype.h> #include <amss/i2c_client.h> #define IDX_DEV_FD 1 #define IDX_SLAVE_ADDR 2 #define IDX_OP 3 #define IDX_REG 4 #define IDX_VAL 5 /* write: value to write, read: optional no. of consecutive registers to read */ #define IDX_DETECT 1 #define IDX_DETECT_SUBCMD 2 #define IDX_DETECT_FD 3 // // Command line: // 1. i2ctool $fd $slaveAddr read $reg [$val] 6 // 2. i2ctool $fd $slaveAddr write $reg $val 6 // 3. i2ctool detect dump $fd dump all the registers for device $fd and slaveaddr 4 // 4. i2ctool detect list all the i2c device that currently created 2 // 5. i2ctool detect loop $fd scan for the i2c device if it has the slaves or not 3 //detect #define I2C_MAX_COUNT 12 #define INT2CHAR(x) (x - '0') #define PATH_LEN 10 #define I2C_ADDR_FIRST 0x01 #define I2C_ADDR_LAST 0xff #define I2C_ADDR_MAX 0xff int main(int argc, char *argv[]) { ; int error = EXIT_SUCCESS; != argc) && ( != argc) && ( != argc)) { fprintf(stderr, ]); error = EXIT_FAILURE; } != argc)) { fprintf(stderr, ]); error = EXIT_FAILURE; } else if(!strcmp(argv[IDX_DETECT], "detect")) { fprintf(stderr, "detect\n"); error = EXIT_FAILURE; == argc) { char i2c_path[PATH_LEN]; char *i2c_dev_path = "/dev/i2c%d"; ; i < I2C_MAX_COUNT; i++) { memset(i2c_path, , sizeof(char)*PATH_LEN); snprintf(i2c_path, PATH_LEN, i2c_dev_path, i); == (fd = i2c_open(i2c_path))) { error = EXIT_FAILURE; } else { i2c_close(fd); fprintf(stderr," %s\n",i2c_path); } } } // == (fd = i2c_open(argv[IDX_DETECT_FD]))) { fprintf(stderr, ], argv[IDX_DETECT_FD]); error = EXIT_FAILURE; } == argc) { if(!strcmp(argv[IDX_DETECT_SUBCMD], "loop")) { fprintf(stderr, "ready to scan\n"); fprintf(stderr," 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); ; i < ; i += ) { fprintf(stderr,"%02x: ", i); ; j < ; j++) { if( ((i+j) < I2C_ADDR_FIRST) || ((i+j) > I2C_ADDR_LAST)) { fprintf(stderr," "); continue ; } //slave address is i+j != i2c_set_slave_addr(fd, i+j, )) { fprintf(stderr,"UU "); continue; } else { fprintf(stderr,"-- "); } } fprintf(stderr,"\n"); } } else if (!strcmp(argv[IDX_DETECT_SUBCMD], "dump")) { unsigned ; //int count = 0xff; fprintf(stderr, "ready to dump\n"); fprintf(stderr," 0 1 2 3 4 5 6 7 8 9 a b c d e f\n"); ; ; i < I2C_ADDR_MAX; i+=) { fprintf(stderr,"0x%02x: ", i); ; j < ; j++) { reg = i+j; if(reg < I2C_ADDR_FIRST || reg > I2C_ADDR_LAST) { fprintf(stderr," "); continue; } != i2c_combined_writeread(fd, ®, , &data, )) { if (isgraph(data)) { fprintf(stderr, "%02X ", data); } else { fprintf(stderr, "%02X ", data); } } else { fprintf(stderr,"-- "); } } fprintf(stderr,"\n"); } } else { // this can't happen fprintf(stderr, ], argv[IDX_OP]); error = EXIT_FAILURE; } i2c_close(fd); } } == (fd = i2c_open(argv[IDX_DEV_FD]))) { fprintf(stderr, ], argv[IDX_DEV_FD]); error = EXIT_FAILURE; } //read write else { //#if 1 ); ); == i2c_set_slave_addr(fd, slaveAddr, )) { fprintf(stderr, ]); error = EXIT_FAILURE; } if (EXIT_SUCCESS == error) { if (!strcmp(argv[IDX_OP], "write")) { unsigned ]; writedata[] = reg; writedata[] = strtol(argv[IDX_VAL], NULL, ); error = i2c_write(fd, writedata, ); ) || /*wrote zero bytes...*/ error == - /*Driver error*/) { fprintf(stderr, "%s: i2c_write(%s = %d, data[2] = 0x%02x,0x%02x, 2) failed\n", argv[], argv[IDX_DEV_FD], fd, writedata[], writedata[]); error = EXIT_FAILURE; } else { fprintf(stderr, ], writedata[]); } } else if (!strcmp(argv[IDX_OP], "read")) { unsigned ; ; int i; == argc) { count = strtol(argv[IDX_VAL], NULL, ); } ; i<count; i++, reg++) { == i2c_combined_writeread(fd, ®, , &data, )) { fprintf(stderr, "%s: i2c_combined_writeread(%s = %d, slaveAddr = 0x%02x, 1, &data, 1) failed\n", argv[], argv[IDX_DEV_FD], fd, slaveAddr); error = EXIT_FAILURE; } else { if (isgraph(data)) { fprintf(stderr, "0x%02x -> 0x%02X ('%c')\n", reg, data, data); } else { fprintf(stderr, "0x%02x -> 0x%02X\n", reg, data); } } } } else { // this can't happen fprintf(stderr, ], argv[IDX_OP]); error = EXIT_FAILURE; } } i2c_close(fd); } return error; } i2c tool
it can work like i2ctool that we farmiliar with, in qnx, it is very strange that all the input parameters need a condition to handle otherwise it will cor dump, I spend some time on why it will core dump, but failed, this could be a further task.
How to create i2c_client
i2c_client Provides APIs for user applications to communicate to I2C device nodes(/dev/i2c*).
/**=========================================================================== @file i2c_client.c @copyright Copyright (c) 2010-2013 QUALCOMM Technologies Incorporated. All rights reserved. Licensed Material - Restricted Use. Qualcomm Confidential and Proprietary. ===========================================================================*/ /*=========================================================================== EDIT HISTORY FOR FILE $Header$ when who what, where, why -------- --- ---------------------------------------------------------- 02/01/13 jsanpedr Switched slog to logger_log macro 09/08/12 rohitn Close fd on exec to reduce IO_DUP messages to QNX memmgr ===========================================================================*/ /*========================================================================= Include Files ==========================================================================*/ #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include "i2c_client.h" #include "logger_utils.h" #define LOG(sev, format, ...) \ logger_log(QCLOG_AMSS_QNP_QAL_I2C_CLIENT, , sev, format, ##__VA_ARGS__) #define LOGE(format, ...) LOG(QCLOG_ERROR, format, ##__VA_ARGS__) /*========================================================================= Functions ==========================================================================*/ int i2c_open(void* devname){ int fd; fd = open (devname, O_RDWR|O_CLOEXEC); ) { LOGE("Can't open device %s for RDONLY, errno %d", (char*)devname, errno); } return fd; } void i2c_close(int fd){ ) { close(fd); } } int i2c_set_slave_addr(int fd, uint32_t addr, uint32_t fmt){ ; i2c_addr_t msg; msg.addr = addr; msg.fmt = fmt; ){ ; } ){ ; } return retval; } int i2c_set_bus_speed(int fd, uint32_t speed, uint32_t *ospeed){ ; uint32_t msg = speed; ){ ; } ){ ; } return retval; } int i2c_bus_lock(int fd){ ; ){ ; } , &retval)!=) { ; } return retval; } int i2c_bus_unlock(int fd){ ; ){ ; } , &retval)!=){ ; } return retval; } int i2c_driver_info(int fd, i2c_driver_info_t *info){ ){ ; } ){ ; } ; } int i2c_read(int fd, void *buf, uint32_t len) { ; i2c_masterhdr_t msg; iov_t wiov[]; iov_t riov[]; ){ logger_log(QCLOG_AMSS_QNP_QAL_I2C_CLIENT, __LINE__, QCLOG_ERROR, "Device not yet opened"); ; } msg.len = len; msg.stop = ; // always generate stop per bus transaction SETIOV(wiov+, &msg, sizeof(i2c_masterhdr_t)); SETIOV(riov+, &msg, sizeof(i2c_masterhdr_t)); SETIOV(riov+, buf, len); , , wiov, riov , &retval)!=){ ; } return retval; } int i2c_write(int fd, void *buf, uint32_t len){ ; iov_t iov[]; i2c_masterhdr_t msg; ){ logger_log(QCLOG_AMSS_QNP_QAL_I2C_CLIENT, __LINE__, QCLOG_ERROR, "Device not yet opened"); ; } msg.len = len; msg.stop = ; SETIOV(iov+, &msg, sizeof(i2c_masterhdr_t)); SETIOV(iov+, buf, len); , , iov, NULL , &retval)!=) { ; } return retval; } int i2c_combined_writeread(int fd, void * wbuff, uint32_t wlen, void* rbuff, uint32_t rlen ) { ; i2c_sendrecv_t msg; iov_t wiov[]; iov_t riov[]; ){ ; } msg.send_len = wlen; msg.recv_len = rlen; msg.stop = ; // always stop condition msg.slave.addr = ; // use current slave SETIOV(wiov+, &msg, sizeof(msg)); SETIOV(wiov+, wbuff, wlen); SETIOV(riov+, &msg, sizeof(msg)); SETIOV(riov+, rbuff, rlen); , , wiov, riov, &retval)!=) { ; } return retval; } i2c_client
for example: i2cdbgr will use these apis
/* ========================================================================= @file i2cdbgr.c @brief Application, R/W i2c devices using 8-bit address/data, default freq. Copyright (c) 2013 by QUALCOMM Technologies Incorporated. All Rights Reserved. $Header$ ============================================================================ */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/neutrino.h> #include <hw/inout.h> #include <fcntl.h> #include <ctype.h> #include <amss/i2c_client.h> #define IDX_DEV_FD 1 #define IDX_SLAVE_ADDR 2 #define IDX_OP 3 #define IDX_REG 4 #define IDX_VAL 5 /* write: value to write, read: optional no. of consecutive registers to read */ // // Command line: // 1. i2cdbgr $fd $slaveAddr read $reg [$val] // 2. i2cdbgr $fd $slaveAddr write $reg $val // int main(int argc, char *argv[]) { ; int error = EXIT_SUCCESS; != argc) && ( != argc) ) { fprintf(stderr, ]); error = EXIT_FAILURE; } != argc)) { fprintf(stderr, ]); error = EXIT_FAILURE; } == (fd = i2c_open(argv[IDX_DEV_FD]))) { fprintf(stderr, ], argv[IDX_DEV_FD]); error = EXIT_FAILURE; } else { ); ); == i2c_set_slave_addr(fd, slaveAddr, )) { fprintf(stderr, ]); error = EXIT_FAILURE; } if (EXIT_SUCCESS == error) { if (!strcmp(argv[IDX_OP], "write")) { unsigned ]; writedata[] = reg; writedata[] = strtol(argv[IDX_VAL], NULL, ); error = i2c_write(fd, writedata, ); ) || /*wrote zero bytes...*/ error == - /*Driver error*/) { fprintf(stderr, "%s: i2c_write(%s = %d, data[2] = 0x%02x,0x%02x, 2) failed\n", argv[], argv[IDX_DEV_FD], fd, writedata[], writedata[]); error = EXIT_FAILURE; } else { fprintf(stderr, ], writedata[]); } } else if (!strcmp(argv[IDX_OP], "read")) { unsigned ; ; int i; == argc) { count = strtol(argv[IDX_VAL], NULL, ); } ; i<count; i++, reg++) { == i2c_combined_writeread(fd, ®, , &data, )) { fprintf(stderr, "%s: i2c_combined_writeread(%s = %d, slaveAddr = 0x%02x, 1, &data, 1) failed\n", argv[], argv[IDX_DEV_FD], fd, slaveAddr); error = EXIT_FAILURE; } else { if (isgraph(data)) { fprintf(stderr, "0x%02x -> 0x%02X ('%c')\n", reg, data, data); } else { fprintf(stderr, "0x%02x -> 0x%02X\n", reg, data); } } } } else { // this can't happen fprintf(stderr, ], argv[IDX_OP]); error = EXIT_FAILURE; } } i2c_close(fd); } return error; } i2cdbgr
# === Rules for type i2c_service_t ==========
allow i2c_service_t self:ability {
nonroot
setuid:I2C_SERVICE_UID
setgid:I2C_SERVICE_GID,QCORE_GID
#TODO: Interrupt is handled/attached during every write/read. Hence ability is not dropped
interruptevent:blsp2_qup_irq_0,blsp1_qup_irq_5,blsp2_qup_irq_1
mem_phys:PERIPH_SS.PERIPH_SS_BLSP1_BLSP.QUP5_DD_4K
mem_phys:PERIPH_SS.PERIPH_SS_BLSP2_BLSP.QUP0_DD_4K
mem_phys:PERIPH_SS.PERIPH_SS_BLSP2_BLSP.QUP1_DD_4K
mem_phys:__DMA_POOL_START__-__DMA_POOL_END__
iofunc/exec
io:0
spawn
public_channel
xthread_threadctl
pathspace
channel_connect
mem_peer
mem_special
keydata
rlimit
};
allow i2c_service_t self:ability {
nonroot
unlock
map_fixed
prot_exec
prot_write_and_exec
};
allow_attach i2c_service_t {
/dev/i2c*
/dev/i2c_service
/dev/npa
/dev/pmem/i2c_service
};
allow i2c_service_t {
qcore_t
slogger2_t
smmu_service_t
bmetrics_t
}:channel connect;