/** * \file * \brief ATCA Hardware abstraction layer for PIC32MZ2048 * * \copyright (c) 2015-2020 Microchip Technology Inc. and its subsidiaries. * * \page License * * Subject to your compliance with these terms, you may use Microchip software * and any derivatives exclusively with Microchip products. It is your * responsibility to comply with third party license terms applicable to your * use of third party software (including open source software) that may * accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, * SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE * OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF * MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE * FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL * LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED * THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR * THIS SOFTWARE. */ #include #include #include #include "atca_hal.h" #include "atca_device.h" #include "hal/hal_pic32mz2048efm_i2c.h" #include "driver/i2c/drv_i2c.h" #include "system_definitions.h" #include "driver/i2c/src/drv_i2c_local.h" DRV_HANDLE drvI2CMasterHandle; //Returned from DRV_I2C_Open for I2C Master DRV_HANDLE drvI2CMasterHandle1; //Returned from DRV_I2C_Open for I2C Master DRV_I2C_BUFFER_HANDLE write_bufHandle; //Returned from calling a Data Transfer function DRV_I2C_BUFFER_HANDLE read_bufHandle; //Returned from calling a Data Transfer function /** * \brief * Logical to physical bus mapping structure */ static ATCAI2CMaster_t i2c_hal_data[MAX_I2C_BUSES]; // map logical, 0-based bus number to index /** \brief discover any CryptoAuth devices on a given logical bus number * \param[in] bus_num logical bus number on which to look for CryptoAuth devices * \param[out] cfg pointer to head of an array of interface config structures which get filled in by this method * \param[out] found number of devices found on this bus * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_discover_devices(int bus_num, ATCAIfaceCfg cfg[], int *found) { return ATCA_UNIMPLEMENTED; } /** \brief discover i2c buses available for this hardware * this maintains a list of logical to physical bus mappings freeing the application * of the a-priori knowledge * \param[in] i2c_buses an array of logical bus numbers * \param[in] max_buses maximum number of buses the app wants to attempt to discover * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_discover_buses(int i2c_buses[], int max_buses) { i2c_buses[0] = 0; return ATCA_SUCCESS; } /** \brief initialize an I2C interface using given config * \param[in] hal opaque ptr to HAL data * \param[in] cfg interface configuration * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_init(void *hal, ATCAIfaceCfg *cfg) { DRV_I2C_Object *tempI2cObj; if (cfg->atcai2c.bus >= MAX_I2C_BUSES) { return ATCA_COMM_FAIL; } ATCAI2CMaster_t* data = &i2c_hal_data[cfg->atcai2c.bus]; if (data->ref_ct <= 0) { // Bus isn't being used, enable it tempI2cObj = (DRV_I2C_Object*)(cfg->cfg_data); drvI2CMasterHandle = DRV_I2C_Open(tempI2cObj->i2cDriverInstanceIndex, DRV_IO_INTENT_NONBLOCKING | DRV_IO_INTENT_READWRITE); if (drvI2CMasterHandle == DRV_HANDLE_INVALID) { return ATCA_COMM_FAIL; } // store this for use during the release phase data->bus_index = cfg->atcai2c.bus; // buses are shared, this is the first instance data->ref_ct = 1; } else { // Bus is already is use, increment reference counter data->ref_ct++; } ((ATCAHAL_t*)hal)->hal_data = data; return ATCA_SUCCESS; } /** \brief HAL implementation of I2C post init * \param[in] iface instance * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_post_init(ATCAIface iface) { return ATCA_SUCCESS; } /** \brief HAL implementation of I2C send over ASF * \param[in] iface instance * \param[in] txdata pointer to space to bytes to send * \param[in] txlength number of bytes to send * \return ATCA_SUCCESS on success, otherwise an error code. */ uint32_t Debug_count = 0; uint32_t bytes_transferred = 0; ATCA_STATUS hal_i2c_send(ATCAIface iface, uint8_t *txdata, int txlength) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); DRV_I2C_BUFFER_EVENT Transaction; // for this implementation of I2C with CryptoAuth chips, txdata is assumed to have ATCAPacket format // other device types that don't require i/o tokens on the front end of a command need a different hal_i2c_send and wire it up instead of this one // this covers devices such as ATSHA204A and ATECCx08A that require a word address value pre-pended to the packet // txdata[0] is using _reserved byte of the ATCAPacket txdata[0] = 0x03; // insert the Word Address Value, Command token txlength++; // account for word address value byte. uint8_t tx = cfg->atcai2c.slave_address; write_bufHandle = DRV_I2C_Transmit(drvI2CMasterHandle, cfg->atcai2c.slave_address, txdata, txlength, NULL); if ( ((DRV_I2C_BUFFER_HANDLE)(-1)) == write_bufHandle) { while (1) { ; } } do { Transaction = DRV_I2C_TransferStatusGet(drvI2CMasterHandle, write_bufHandle); if (Transaction != DRV_I2C_BUFFER_EVENT_COMPLETE) { Debug_count++; bytes_transferred = DRV_I2C_BytesTransferred(drvI2CMasterHandle, write_bufHandle); } if (Transaction == DRV_I2C_BUFFER_EVENT_ERROR) { return ATCA_COMM_FAIL; } } while (Transaction != DRV_I2C_BUFFER_EVENT_COMPLETE); return ATCA_SUCCESS; } /** \brief HAL implementation of I2C receive function * \param[in] iface Device to interact with. * \param[out] rxdata Data received will be returned here. * \param[inout] rxlength As input, the size of the rxdata buffer. * As output, the number of bytes received. * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_receive(ATCAIface iface, uint8_t *rxdata, uint16_t *rxlength) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); DRV_I2C_BUFFER_EVENT Transaction = DRV_I2C_BUFFER_EVENT_ERROR; uint16_t count; uint16_t rxdata_max_size = *rxlength; *rxlength = 0; if (rxdata_max_size < 1) { return ATCA_SMALL_BUFFER; } read_bufHandle = DRV_I2C_Receive(drvI2CMasterHandle, cfg->atcai2c.slave_address, rxdata, 1, NULL); do { Transaction = DRV_I2C_TransferStatusGet(drvI2CMasterHandle, read_bufHandle); if (Transaction == DRV_I2C_BUFFER_EVENT_ERROR) { return ATCA_COMM_FAIL; } } while (Transaction != DRV_I2C_BUFFER_EVENT_COMPLETE && Transaction != DRV_I2C_BUFFER_EVENT_ERROR); if (rxdata[0] < ATCA_RSP_SIZE_MIN) { return ATCA_INVALID_SIZE; } if (rxdata[0] > rxdata_max_size) { return ATCA_SMALL_BUFFER; } count = rxdata[0] - 1; read_bufHandle = DRV_I2C_Receive(drvI2CMasterHandle, cfg->atcai2c.slave_address, &rxdata[1], count, NULL); do { Transaction = DRV_I2C_TransferStatusGet(drvI2CMasterHandle, read_bufHandle); if (Transaction == DRV_I2C_BUFFER_EVENT_ERROR) { return ATCA_COMM_FAIL; } } while (Transaction != DRV_I2C_BUFFER_EVENT_COMPLETE && Transaction != DRV_I2C_BUFFER_EVENT_ERROR); *rxlength = rxdata[0]; return ATCA_SUCCESS; } /** \brief wake up CryptoAuth device using I2C bus * \param[in] iface interface to logical device to wakeup * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_wake(ATCAIface iface) { DRV_I2C_Object *tempI2cObj; ATCAIfaceCfg *cfg = atgetifacecfg(iface); //int bus = cfg->atcai2c.bus; //int retries = cfg->rx_retries; uint32_t bdrt = cfg->atcai2c.baud; uint8_t data[4]; DRV_I2C_BUFFER_EVENT Rx_Transaction = DRV_I2C_BUFFER_EVENT_ERROR; data[0] = 0x00; if (bdrt != 100000) // if not already at 100KHz, change it { uint32_t backupBaudRate; DRV_I2C_INIT *tempi2cDriverInit; DRV_I2C_Close(drvI2CMasterHandle); /* DeInitialize Drivers */ tempI2cObj = (DRV_I2C_Object*)(cfg->cfg_data); DRV_I2C_Deinitialize(tempI2cObj->i2cDriverInstance); /* Re-Initialize Drivers for ECC508A sleep configuration - I2C speed at 100KHZ */ tempi2cDriverInit = (DRV_I2C_INIT*)tempI2cObj->i2cDriverInit; backupBaudRate = tempi2cDriverInit->baudRate; tempi2cDriverInit->baudRate = 100000; tempI2cObj->i2cDriverInstance = DRV_I2C_Initialize(tempI2cObj->i2cDriverInstanceIndex, (SYS_MODULE_INIT*)tempi2cDriverInit); drvI2CMasterHandle1 = DRV_I2C_Open(tempI2cObj->i2cDriverInstanceIndex, DRV_IO_INTENT_NONBLOCKING | DRV_IO_INTENT_READWRITE); write_bufHandle = DRV_I2C_Transmit(drvI2CMasterHandle1, cfg->atcai2c.slave_address, &data, 1, NULL); if ( ((DRV_I2C_BUFFER_HANDLE)(-1)) == write_bufHandle) { while (1) { ; } } atca_delay_us(cfg->wake_delay); // wait tWHI + tWLO which is configured based on device type and configuration structure read_bufHandle = DRV_I2C_Receive(drvI2CMasterHandle1, cfg->atcai2c.slave_address, &data, 4, NULL); do { Rx_Transaction = DRV_I2C_TransferStatusGet(drvI2CMasterHandle1, read_bufHandle); } while (Rx_Transaction != DRV_I2C_BUFFER_EVENT_COMPLETE && Rx_Transaction != DRV_I2C_BUFFER_EVENT_ERROR); DRV_I2C_Close(drvI2CMasterHandle1); /* DeInitialize Drivers */ DRV_I2C_Deinitialize(tempI2cObj->i2cDriverInstance); /* Re-Initialize Drivers back for ECC508A default configuration*/ tempi2cDriverInit->baudRate = backupBaudRate; tempI2cObj->i2cDriverInstance = DRV_I2C_Initialize(tempI2cObj->i2cDriverInstanceIndex, (SYS_MODULE_INIT*)tempi2cDriverInit); drvI2CMasterHandle = DRV_I2C_Open(tempI2cObj->i2cDriverInstanceIndex, DRV_IO_INTENT_NONBLOCKING | DRV_IO_INTENT_READWRITE); } else { DRV_I2C_BUFFER_EVENT statusevent; // Send the wake by writing to an address of 0x00 write_bufHandle = DRV_I2C_Transmit(drvI2CMasterHandle, cfg->atcai2c.slave_address, &data, 1, NULL); if ( ((DRV_I2C_BUFFER_HANDLE)(-1)) == write_bufHandle) { while (1) { ; } } atca_delay_us(cfg->wake_delay); // wait tWHI + tWLO which is configured based on device type and configuration structure read_bufHandle = DRV_I2C_Receive(drvI2CMasterHandle, cfg->atcai2c.slave_address, &data, 4, NULL); do { Rx_Transaction = DRV_I2C_TransferStatusGet(drvI2CMasterHandle, read_bufHandle); } while (Rx_Transaction != DRV_I2C_BUFFER_EVENT_COMPLETE && Rx_Transaction != DRV_I2C_BUFFER_EVENT_ERROR); } return hal_check_wake(data, 4); } /** \brief idle CryptoAuth device using I2C bus * \param[in] iface interface to logical device to idle * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_idle(ATCAIface iface) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); uint8_t data[1]; data[0] = 0x02; // idle word address value write_bufHandle = DRV_I2C_Transmit(drvI2CMasterHandle, cfg->atcai2c.slave_address, &data, 1, NULL); return ATCA_SUCCESS; } /** \brief sleep CryptoAuth device using I2C bus * \param[in] iface interface to logical device to sleep * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_sleep(ATCAIface iface) { ATCAIfaceCfg *cfg = atgetifacecfg(iface); uint8_t data[4]; data[0] = 0x01; // sleep word address value write_bufHandle = DRV_I2C_Transmit(drvI2CMasterHandle, cfg->atcai2c.slave_address, &data, 1, NULL); return ATCA_SUCCESS; } /** \brief manages reference count on given bus and releases resource if no more refences exist * \param[in] hal_data opaque pointer to hal data structure - known only to the HAL implementation * \return ATCA_SUCCESS on success, otherwise an error code. */ ATCA_STATUS hal_i2c_release(void *hal_data) { ATCAI2CMaster_t *hal = (ATCAI2CMaster_t*)hal_data; // if the use count for this bus has gone to 0 references, disable it. protect against an unbracketed release if (hal && --(hal->ref_ct) <= 0) { DRV_I2C_Close(drvI2CMasterHandle); hal->ref_ct = 0; } return ATCA_SUCCESS; }