Stefan Thom / SPITIS_TPM20

SPITIS_TPM20.cpp

Committer:
LordOfDorks
Date:
2015-03-23
Revision:
0:b11c8971edd9
Child:
1:fd0a59e55a85

File content as of revision 0:b11c8971edd9:

/* mbed TCG TIS 1.3 SPI TPM 2.0 Library,
 * Copyright (c) 2015, Microsoft Coprporation Inc.
 * by Stefan Thom (LordOfDorks) StefanTh@Microsoft.com, Stefan@ThomsR.Us
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
 
 #include "SPITIS_TPM20.h"
 
TISTPM20::TISTPM20(
    PinName mosi,
    PinName miso,
    PinName clk,
    PinName cs
    )
{
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.Init: ");
#endif
    m_SPITpmDev = new SPI(mosi, miso, clk);
    m_SPITpmDev->format(8, 0);
    m_SPITpmDev->frequency(4000000);
    m_SPICSTpmDev = new DigitalOut(cs);
    *m_SPICSTpmDev = 1;
    m_ExclusiveAccess = false;
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("OK.\n\r");
#endif
}

// Release all held resources
TISTPM20::~TISTPM20()
{
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.Destroy: ");
#endif
    if(m_SPITpmDev != NULL)
    {
        delete m_SPITpmDev;
        m_SPITpmDev = NULL;
    }
    if(m_SPICSTpmDev != NULL)
    {
        delete m_SPICSTpmDev;
        m_SPICSTpmDev = NULL;
    }
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("OK.\n\r");
#endif
}

uint32_t
TISTPM20::Execute(
    uint8_t locality,
    uint8_t* pbCmd,
    uint32_t cbCmd,
    uint8_t* pbRsp,
    uint32_t cbRsp,
    uint32_t timeout
    )
{
    uint32_t result = 0;
    Timeout watchdog;

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.ExecuteWaitForAccess.");
#endif

    // Only one caller should be talking to the TPM at any given time
    while(m_ExclusiveAccess)
    {
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf(".");
#endif
        wait_us(500);
    }
    m_ExclusiveAccess = true;
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("OK\n\r");
#endif

    // Set the requested locality for the call
    m_Locality = locality;
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.Locality = %d\n\r", m_Locality);
#endif

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.SetupTimeout\n\r");
#endif
    // Setup TPM timeout
    m_TimeoutTriggered = false;
    watchdog.attach(this, &TISTPM20::TimeoutTrigger, 0.0001 * timeout);

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.Execute: ");
    for(uint32_t n = 0; n < cbCmd; n++) printf("%02x ", pbCmd[n]);
    printf("\n\r");
#endif
    
    // Execute command on the TPM
    if((result = ExecuteTIS(pbCmd, cbCmd, pbRsp, cbRsp)) == 0)
    {
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.Failed\n\r");
#endif
        goto Cleanup;
    }

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.Response: ");
    for(uint32_t n = 0; n < result; n++) printf("%02x ", pbRsp[n]);
    printf("\n\r");
#endif

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.CancelTimeout\n\r");
#endif

Cleanup:
    watchdog.detach();
    m_ExclusiveAccess = false;
    return result;
}

uint32_t
TISTPM20::ParseResponseHeader(
    uint8_t* pbRsp,
    uint32_t rspLen,
    uint16_t* rspTag,
    uint32_t* rspSize,
    uint32_t* cursor
    )
{
    uint32_t rspResponseCode = 0;

    // Check that the response header is well formatted
    if(rspLen < (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t)))
    {
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.ResponseHdr.rspLen = 0x%08x\n\r", rspLen);
#endif
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }
    
    // Read the header components
    *rspTag = BYTEARRAY_TO_UINT16(pbRsp, *cursor);
    *cursor += sizeof(uint16_t);
    *rspSize = BYTEARRAY_TO_UINT32(pbRsp, *cursor);
    *cursor += sizeof(uint32_t);
    rspResponseCode = BYTEARRAY_TO_UINT32(pbRsp, *cursor);
    *cursor += sizeof(uint32_t);

    // Check the components
    if(((*rspTag != TPM_ST_NO_SESSIONS) && (*rspTag != TPM_ST_SESSIONS)) ||
       (*rspSize != rspLen))
    {
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.ResponseHdr.rspTag = 0x%04x.rspLen=0x%08x\n\r", *rspTag, rspLen);
#endif
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }

Cleanup:    
    return rspResponseCode;
}

uint32_t
TISTPM20::TPM2_Startup(
    uint8_t locality, 
    uint16_t startupType
    )
{
    uint32_t rspLen = 0;
    uint16_t rspTag = 0;
    uint32_t rspSize = 0;
    uint32_t rspResponseCode = 0;
    uint32_t cursor = 0;
    uint8_t tpmCmd[] = {0x80, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00};
#ifndef TPM_TIS_DEBUG_OUTPUT
    uint32_t timeout = 2000;
#else
    uint32_t timeout = 20000;
#endif
    UINT16_TO_BYTEARRAY(startupType, tpmCmd, sizeof(tpmCmd) - 2);  

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_Startup(0x%04x)\n\r", startupType);
#endif

    if((rspLen = Execute(locality, tpmCmd, sizeof(tpmCmd), tpmCmd, sizeof(tpmCmd), timeout)) == 0)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }
    if((rspResponseCode = ParseResponseHeader(tpmCmd, rspLen, &rspTag, &rspSize, &cursor)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }
    
    if(rspSize != 0x0000000a)
    {
        rspResponseCode = TPM_RC_FAILURE;
    }

Cleanup:    
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_Startup.ResponseCode = 0x%08x\n\r", rspResponseCode);
#endif
    return rspResponseCode;
}

uint32_t
TISTPM20::TPM2_Shutdown(
    uint8_t locality, 
    uint16_t shutdownType
    )
{
    uint32_t rspLen = 0;
    uint16_t rspTag = 0;
    uint32_t rspSize = 0;
    uint32_t rspResponseCode = 0;
    uint32_t cursor = 0;
    uint8_t tpmCmd[] = {0x80, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00};
#ifndef TPM_TIS_DEBUG_OUTPUT
    uint32_t timeout = 2000;
#else
    uint32_t timeout = 20000;
#endif
    UINT16_TO_BYTEARRAY(shutdownType, tpmCmd, sizeof(tpmCmd) - 2);

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_Shutdown(0x%04x)\n\r", shutdownType);
#endif

    if((rspLen = Execute(locality, tpmCmd, sizeof(tpmCmd), tpmCmd, sizeof(tpmCmd), timeout)) == 0)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }
    if((rspResponseCode = ParseResponseHeader(tpmCmd, rspLen, &rspTag, &rspSize, &cursor)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }

    if(rspSize != 0x0000000a)
    {
        rspResponseCode = TPM_RC_FAILURE;
    }

Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_Shutdown.ResponseCode = 0x%08x\n\r", rspResponseCode);
#endif
    return rspResponseCode;
}

uint32_t
TISTPM20::TPM2_SelfTest(
    uint8_t locality, 
    uint8_t fullTest
    )
{
    uint32_t rspLen = 0;
    uint16_t rspTag = 0;
    uint32_t rspSize = 0;
    uint32_t rspResponseCode = 0;
    uint32_t cursor = 0;
    uint8_t tpmCmd[] = {0x80, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x01, 0x43, 0x00};
#ifndef TPM_TIS_DEBUG_OUTPUT
    uint32_t timeout = 2000;
#else
    uint32_t timeout = 20000;
#endif
    tpmCmd[sizeof(tpmCmd) - 1] = fullTest;

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_SelfTest(0x%02x)\n\r", fullTest);
#endif

    if((rspLen = Execute(locality, tpmCmd, sizeof(tpmCmd), tpmCmd, sizeof(tpmCmd), timeout)) == 0)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }
    if((rspResponseCode = ParseResponseHeader(tpmCmd, rspLen, &rspTag, &rspSize, &cursor)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }

    if(rspSize != 0x0000000a)
    {
        rspResponseCode = TPM_RC_FAILURE;
    }

Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_SelfTest.ResponseCode = 0x%08x\n\r", rspResponseCode);
#endif
    return rspResponseCode;
}

uint32_t
TISTPM20::TPM2_GetRandom(
    uint8_t locality, 
    uint16_t bytesRequested,
    uint8_t* randomBytes
    )
{
    uint32_t cursor = 0;
    uint32_t rspLen = 0;
    uint16_t rspTag = 0;
    uint32_t rspSize = 0;
    uint32_t rspResponseCode = 0;
    uint32_t tpmMax = sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + bytesRequested;
    uint8_t* tpmCmd = new uint8_t[tpmMax];
    uint16_t bytesReturned = 0;
#ifndef TPM_TIS_DEBUG_OUTPUT
    uint32_t timeout = 2000;
#else
    uint32_t timeout = 20000;
#endif
    
    if(tpmCmd == NULL)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Build command
    UINT16_TO_BYTEARRAY(TPM_ST_NO_SESSIONS, tpmCmd, cursor);
    cursor += sizeof(uint16_t) + sizeof(cursor);
    UINT32_TO_BYTEARRAY(TPM_CC_GetRandom, tpmCmd, cursor);
    cursor += sizeof(TPM_CC_GetRandom);
    UINT16_TO_BYTEARRAY(bytesRequested, tpmCmd, cursor);
    cursor += sizeof(bytesRequested);
    UINT32_TO_BYTEARRAY(cursor, tpmCmd, sizeof(uint16_t));

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_GetRandom(%d)\n\r", bytesRequested);
#endif

    if((rspLen = Execute(locality, tpmCmd, cursor, tpmCmd, tpmMax, timeout)) == 0)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }
    cursor = 0;
    if((rspResponseCode = ParseResponseHeader(tpmCmd, rspLen, &rspTag, &rspSize, &cursor)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }

    if(rspSize != tpmMax)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Copy the random bytes out
    bytesReturned = BYTEARRAY_TO_UINT16(tpmCmd, cursor);
    cursor += sizeof(uint16_t);
    memcpy(randomBytes, &tpmCmd[cursor], (size_t)min(bytesReturned, bytesRequested));
 
 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_GetRandom.ResponseCode = 0x%08x\n\r", rspResponseCode);
#endif
    if(tpmCmd != NULL)
    {
        delete[] tpmCmd;
        tpmCmd = NULL;
    }
    return rspResponseCode;
}

uint32_t
TISTPM20::TPM2_StirRandom(
    uint8_t locality, 
    uint16_t inDataLen,
    uint8_t* inData
    )
{
    uint32_t cursor = 0;
    uint32_t rspLen = 0;
    uint16_t rspTag = 0;
    uint32_t rspSize = 0;
    uint32_t rspResponseCode = 0;
    uint32_t tpmMax = sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + inDataLen;
    uint8_t* tpmCmd = new uint8_t[tpmMax];
 #ifndef TPM_TIS_DEBUG_OUTPUT
    uint32_t timeout = 2000;
#else
    uint32_t timeout = 20000;
#endif
   
    if(tpmCmd == NULL)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }

    // Build command
    UINT16_TO_BYTEARRAY(TPM_ST_NO_SESSIONS, tpmCmd, cursor);
    cursor += sizeof(uint16_t) + sizeof(cursor);
    UINT32_TO_BYTEARRAY(TPM_CC_StirRandom, tpmCmd, cursor);
    cursor += sizeof(TPM_CC_GetRandom);
    UINT16_TO_BYTEARRAY(inDataLen, tpmCmd, cursor);
    cursor += sizeof(inDataLen);
    memcpy(&tpmCmd[cursor], inData, inDataLen);
    cursor += inDataLen;
    UINT32_TO_BYTEARRAY(cursor, tpmCmd, sizeof(uint16_t));

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_StirRandom(%d)\n\r", inDataLen);
#endif

    if((rspLen = Execute(locality, tpmCmd, cursor, tpmCmd, tpmMax, timeout)) == 0)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }
    cursor = 0;
    if((rspResponseCode = ParseResponseHeader(tpmCmd, rspLen, &rspTag, &rspSize, &cursor)) != TPM_RC_SUCCESS)
    {
        goto Cleanup;
    }

    if(rspSize != 0x0000000a)
    {
        rspResponseCode = TPM_RC_FAILURE;
        goto Cleanup;
    }

 Cleanup:
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.TPM2_StirRandom.ResponseCode = 0x%08x\n\r", rspResponseCode);
#endif
    if(tpmCmd != NULL)
    {
        delete[] tpmCmd;
        tpmCmd = NULL;
    }
    return rspResponseCode;
}

void
TISTPM20::TimeoutTrigger(
    void
    )
{
    m_TimeoutTriggered = true;
}

uint32_t
TISTPM20::ExecuteTIS(
    uint8_t* pbCmd,
    uint32_t cbCmd,
    uint8_t* pbRsp,
    uint32_t cbRsp
    )
{
    uint32_t result = 0;
    uint8_t tisStatus = 0xFF;
    uint32_t index = 0;
    uint32_t rspSize = 0;
    uint16_t tisBurstCount = 0;

    // Lock the requested locality
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.RequestLocality\r\n");
#endif
    if(!RequestLocality())
    {
        goto Cleanup;
    }
    
    // Check the status before writing the command
    if(!GetStatus(&tisStatus))
    {
        goto Cleanup;
    }
    // Abort anything that may still be stuck in the TPM
    if((tisStatus & TIS_STS_COMMAND_READY) == 0)
    {
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.GetReady\r\n");
#endif
        uint8_t mask = TIS_STS_COMMAND_READY;
        if(!Abort() || !WaitForStatus(mask))
        {
            goto Cleanup;
        }
    }
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.ReadyForCommand\r\n");
#endif

    // Send the command to the TPM
    do
    {
        uint16_t cbIteration = 0;

        // Get the maximum bytes we can send for this iteration
        if((!GetBurstCount(&tisBurstCount)) || (tisBurstCount == 0))
        {
            goto Cleanup;
        }
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.GetBurstCount = %d\r\n", tisBurstCount);
#endif

        // Assemble the buffer for transmission
        cbIteration = min((cbCmd - index), min(tisBurstCount, TIS_MAX_HW_FRAME_SIZE));
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.SendingBurst = %d\r\n", cbIteration);
#endif
        if(!SPIFullDuplex(false, TIS_DATA_FIFO, &pbCmd[index], cbIteration))
        {
            goto Cleanup;
        }

        // Update the counts
        index += cbIteration;
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.Remaining = %d\r\n", (cbCmd - index));
#endif
    } while((cbCmd - index) > 0);

    // Wait for the TPM to get ready and kick the command off
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.StatusValid\r\n");
#endif
    if(!WaitForStatus(TIS_STS_VALID) || !Go())
    {
        goto Cleanup;
    }

#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.Go\r\n");
#endif

    // Wait for the TPM to finish the command execution
    do
    {
        wait_us(1000);
        tisStatus = 0xFF;
        if(!GetStatus(&tisStatus) || ((tisStatus & TIS_STS_VALID) == 0))
        {
            goto Cleanup;
        }
    }
    while(((tisStatus & TIS_STS_DATA_AVAIL) == 0) && (!m_TimeoutTriggered));

    if(m_TimeoutTriggered)
    {
        goto Cleanup;
    }

    // Get the response header from the TPM
    index = 0;
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("Tis.ReadResponseHeader\r\n");
#endif
    if(!GetBurstCount(&tisBurstCount) ||
       (tisBurstCount < (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t))) ||
       !SPIFullDuplex(true, TIS_DATA_FIFO, &pbRsp[index], (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t))))
    {
        goto Cleanup;
    }
    index += (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t));
   
    rspSize = BYTEARRAY_TO_UINT32(pbRsp, sizeof(uint16_t));
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("Tis.ResponseSize = %d\r\n", rspSize);
#endif
    
    if(rspSize > index)
    {
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("Tis.ReadResponse\r\n");
#endif
        do
        {
            uint16_t cbIteration = 0;

            // Check to make sure the TPM has still data for us
            if(!GetStatus(&tisStatus) || ((tisStatus & TIS_STS_DATA_AVAIL) == 0))
            {
                goto Cleanup;
            }

            // Get the number of available bytes for reading in this iteration
            if(!GetBurstCount(&tisBurstCount))
            {
                goto Cleanup;
            }
#ifdef TPM_TIS_DEBUG_OUTPUT
            printf("TIS.GetBurstCount = %d\r\n", tisBurstCount);
#endif

            cbIteration = min((rspSize - index), min((cbRsp - index), min(tisBurstCount, TIS_MAX_HW_FRAME_SIZE)));
#ifdef TPM_TIS_DEBUG_OUTPUT
            printf("TIS.ReceivingBurst = %d\r\n", cbIteration);
#endif

            // Read the data for this iteration
            if(!SPIFullDuplex(true, TIS_DATA_FIFO, &pbRsp[index], cbIteration))
            {
                goto Cleanup;
            }
            index += cbIteration;
#ifdef TPM_TIS_DEBUG_OUTPUT
            printf("TIS.Remaining = %d\r\n", (min(rspSize, cbRsp) - index));
#endif
        }
        while(index < min(rspSize, cbRsp));
    }
    
    result = index;
    
Cleanup:
    // Always leave the TPM in a clean state if somethign fishy happened
    if(result == 0)
    {
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("TIS.Abort\r\n");
#endif
        Abort();
    }

    // Release the locality again
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS.ReleaseLocality\r\n");
#endif
    ReleaseLocality();
    return result;
}

bool
TISTPM20::SPIFullDuplex(
    bool readCycle,
    uint8_t tisRegister,
    uint8_t* pbBuffer,
    uint16_t cbBuffer
    )
{
    bool result = false;
    uint8_t dataByteIn;
    uint8_t dataByteOut;

    // Lock the bus for this operation
    *m_SPICSTpmDev = 0;
    
    // Send the TIS header
    uint32_t tisHdr = TIS_HEADER(m_Locality, readCycle, tisRegister, cbBuffer);
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("TIS(LC:%d,RG:%02x,SZ:%02x,%s):", m_Locality, tisRegister, cbBuffer, (readCycle) ? "RD" : "WR");
#endif

    for(uint8_t n = 0; n < sizeof(tisHdr); n++)
    {
         dataByteOut = tisHdr >> (8 * (3 - n));
         dataByteIn = m_SPITpmDev->write(dataByteOut);
    }
        
    // The last read bit is the wait state indicator
    if((dataByteIn & 0x01) == 0)
    {
        do
        {
#ifdef TPM_TIS_DEBUG_OUTPUT
            printf(".");
#endif
            // Do we still have to wait?
            if((dataByteIn = m_SPITpmDev->write(0xFF)) != 0x01)
            {
                wait_us(10);
            }
        }
        while((dataByteIn != 0x01) && (!m_TimeoutTriggered));
        
        // Check if we timed out
        if((dataByteIn != 0x01) && (m_TimeoutTriggered))
        {
            goto Cleanup;
        }
    }
    
    // Do the remaining bytes now
    for(uint8_t n = 0; n < cbBuffer; n++)
    {
        dataByteOut = (readCycle) ? 0xFF : pbBuffer[n];
        dataByteIn = m_SPITpmDev->write(dataByteOut);
        if(readCycle) pbBuffer[n] = dataByteIn;
#ifdef TPM_TIS_DEBUG_OUTPUT
        printf("0x%02x ", (readCycle) ? dataByteIn : dataByteOut);
#endif
    }
    result = true;
#ifdef TPM_TIS_DEBUG_OUTPUT
    printf("\r\n");
#endif

Cleanup:
    // Make sure to release the bus before we leave
    *m_SPICSTpmDev = 1;
    return result;
}

bool
TISTPM20::RequestLocality()
{
    do
    {
        // Do we have access already?
        uint8_t dataByte = 0xFF;
        if(!SPIFullDuplex(true, TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte)))
        {
            return false;
        }
        if((dataByte & TIS_ACCESS_ACTIVE_LOCALITY) &&
           (dataByte & TIS_ACCESS_VALID))
        {
            break;
        }

        // Request access
        dataByte = TIS_ACCESS_REQUEST_USE;
        if(!SPIFullDuplex(false, TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte)))
        {
            return false;
        }
        if(m_TimeoutTriggered)
        {
            // We give up eventually
            return false;
        }
        wait_us(10);
    }
    while(1);
    return true;
}

bool TISTPM20::ReleaseLocality()
{
    do
    {
        // Do we even have access?
        uint8_t dataByte = 0xFF;
        if(!SPIFullDuplex(true, TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte)))
        {
            return false;
        }
        if(!(dataByte & TIS_ACCESS_ACTIVE_LOCALITY) &&
            (dataByte & TIS_ACCESS_VALID))
        {
            break;
        }

        // Release access
        dataByte = TIS_ACCESS_ACTIVE_LOCALITY;
        if(!SPIFullDuplex(false, TIS_ACCESS_REGISTER, &dataByte, sizeof(dataByte)))
        {
            return false;
        }
        if(m_TimeoutTriggered)
        {
            // We give up eventually
            return false;
        }
        wait_us(10);
    }
    while(1);
    return true;
}

bool
TISTPM20::GetBurstCount(
    uint16_t* pBurstCount
    )
{
    uint8_t dataBytes[sizeof(*pBurstCount)] = {0};
    do
    {
        if(!SPIFullDuplex(true, TIS_BURSTCOUNT_REGISTER, dataBytes, sizeof(dataBytes)))
        {
            return false;
        }
        *pBurstCount = (uint16_t)dataBytes[0] | (uint16_t)(dataBytes[1] << 8);

        // Burst count can be 0 if we are too fast
        if(*pBurstCount != 0)
        {
            break;
        }
        if(m_TimeoutTriggered)
        {
            // We give up eventually
            return false;
        }
        wait_us(10);
     }
    while(1);
    return true;  
}

bool
TISTPM20::GetStatus(
    uint8_t* pStatus
    )
{
    bool result = SPIFullDuplex(true, TIS_STATUS_REGISTER, pStatus, sizeof(*pStatus));
    return result;
}

bool
TISTPM20::WaitForStatus(
    uint8_t statusMask
    )
{
    bool result = false;
    uint8_t tisStatus = 0xFF;

    do
    {
        if(!GetStatus(&tisStatus))
        {
            goto Cleanup;
        }
        if((tisStatus & statusMask) == statusMask)
        {
            result = true;
            break;
        }
        wait_us(10);
    }
    while(!m_TimeoutTriggered);

Cleanup:    
    return result;
}

bool
TISTPM20::Abort()
{
    uint8_t dataByte = TIS_STS_COMMAND_READY;
    return SPIFullDuplex(false, TIS_STATUS_REGISTER, &dataByte, sizeof(dataByte));
}

bool
TISTPM20::Go()
{
    uint8_t dataByte = TIS_STS_GO;
    return SPIFullDuplex(false, TIS_STATUS_REGISTER, &dataByte, sizeof(dataByte));
}