📄 ssp.cpp
字号:
// -----------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// Copyright (c) 2002 Silicon Motion, Inc.
//
// Module Name: ssp.cpp
//
// Abstract: SSP API
//
// -----------------------------------------------------------------------------
#include "precomp.h"
///////////////////////////////////////////////////////////////////////////////
// Macro for printing debug messages
#if 1
// Print messages only in DEBUG mode
#define MESSAGE DEBUGMSG
#define MESSAGE_ZONE GPE_ZONE_WARNING
#else
// Force messages even in RELEASE mode
#define MESSAGE RETAILMSG
#define MESSAGE_ZONE 0
#endif
#ifndef DISABLE_INTERRUPT_MANAGEMENT
///////////////////////////////////////////////////////////////////////////////
// SSP read buffer class.
CDataBuffer::CDataBuffer(DWORD dwSize, PWORD pBuffer)
: m_bOwnBuffer(FALSE)
, m_pBuffer(NULL)
{
SetBuffer(dwSize, pBuffer);
}
CDataBuffer::~CDataBuffer()
{
if (m_bOwnBuffer && m_pBuffer)
delete m_pBuffer;
}
BOOL CDataBuffer::PutWord(WORD data)
{
if (IsFull())
{
m_bOverrun = TRUE;
return FALSE;
}
m_head = next(m_head);
m_pBuffer[m_head] = data;
return TRUE;
}
BOOL CDataBuffer::GetWord(WORD& data, DWORD timeout)
{
DWORD start = GetTickCount();
while (IsEmpty())
{
if (timeout != INFINITE)
{
DWORD elapsed = GetTickCount() - start;
if (elapsed > timeout)
return FALSE;
}
}
m_tail = next(m_tail);
data = m_pBuffer[m_tail];
return TRUE;
}
BOOL CDataBuffer::IsFull()
{
return (next(m_head) == m_tail);
}
BOOL CDataBuffer::IsEmpty()
{
return (m_head == m_tail);
}
DWORD CDataBuffer::GetLength()
{
if (m_head >= m_tail)
return m_head - m_tail;
else
return m_size + m_head - m_tail;
}
VOID CDataBuffer::Reset()
{
m_head = 0;
m_tail = 0;
m_bOverrun = FALSE;
}
VOID CDataBuffer::SetBuffer(DWORD dwSize, PWORD pBuffer)
{
// Delete existing own if any
if (m_bOwnBuffer && m_pBuffer)
{
delete m_pBuffer;
m_pBuffer = NULL;
m_bOwnBuffer = FALSE;
}
// Determine the type of the buffer
if (pBuffer)
{
m_pBuffer = pBuffer;
m_size = dwSize;
m_bOwnBuffer = FALSE;
}
else
{
if (dwSize != 0)
{
m_pBuffer = new WORD[dwSize];
m_size = dwSize;
m_bOwnBuffer = TRUE;
}
}
// Reset pointers
Reset();
}
DWORD CDataBuffer::next(DWORD pos)
{
pos++;
if (pos >= m_size)
pos = 0;
return pos;
}
///////////////////////////////////////////////////////////////////////////////
// SSP Interrupt Handler Entries.
VOID ssp0IntHandlerEntry(SMI* pSMI)
{
pSMI->sspIntHandler(SSP0);
}
VOID ssp1IntHandlerEntry(SMI* pSMI)
{
pSMI->sspIntHandler(SSP1);
}
#endif // DISABLE_INTERRUPT_MANAGEMENT
///////////////////////////////////////////////////////////////////////////////
// SSP API Command Handler.
ULONG SMI::HandleSMISSPAPI(ULONG cjIn, PVOID pvIn, ULONG cjOut, PVOID pvOut)
{
MESSAGE(MESSAGE_ZONE, (TEXT("+SMI::HandleSMISSPAPI\r\n")));
ULONG ulRetVal = VGXI2C_INVALID_PARAMETER;
if ((cjIn >= sizeof(VGXSSP_STRUCT)) && (pvIn != NULL))
{
PVGXSSP_STRUCT pIn = (PVGXSSP_STRUCT)pvIn;
switch (pIn->command)
{
case VGXSSPCMD_OPEN:
if ((cjIn == sizeof(VGXSSP_OPEN_STRUCT)) &&
(cjOut == 0) && (pvOut == NULL))
{
ulRetVal = sspOpen((PVGXSSP_OPEN_STRUCT)pvIn);
}
break;
case VGXSSPCMD_CLOSE:
if ((cjIn == sizeof(VGXSSP_STRUCT)) &&
(cjOut == 0) && (pvOut == NULL))
{
ulRetVal = sspClose(((PVGXSSP_STRUCT)pvIn)->ssp);
}
break;
case VGXSSPCMD_READ:
if ((cjIn == sizeof(VGXSSP_STRUCT)) &&
(cjOut > sizeof(VGXSSP_TRANSMIT_RESULT)) && (pvOut != NULL))
{
ulRetVal = sspRead(
((PVGXSSP_STRUCT)pvIn)->ssp, // SSP number
(PBYTE)pvOut + sizeof(VGXSSP_TRANSMIT_RESULT), // Read buffer pointer
cjOut - sizeof(VGXSSP_TRANSMIT_RESULT), // Number of bytes to read
((PVGXSSP_TRANSMIT_RESULT)pvOut)->read); // Number of data units read
}
break;
case VGXSSPCMD_WRITE:
if ((cjIn > sizeof(VGXSSP_STRUCT)) &&
(cjOut == sizeof(VGXSSP_TRANSMIT_RESULT)) && (pvOut != NULL))
{
ulRetVal = sspWrite(
((PVGXSSP_STRUCT)pvIn)->ssp, // SSP number
(PBYTE)pvIn + sizeof(VGXSSP_STRUCT), // Write buffer
cjIn - sizeof(VGXSSP_STRUCT), // Number of bytes to write
((PVGXSSP_TRANSMIT_RESULT)pvOut)->written); // Number of data units written
}
break;
case VGXSSPCMD_RESET_BUFFER:
if ((cjIn == sizeof(VGXSSP_STRUCT)) &&
(cjOut == 0) && (pvOut == NULL))
{
ulRetVal = sspResetBuffer(((PVGXSSP_STRUCT)pvIn)->ssp);
}
break;
default:
MESSAGE(MESSAGE_ZONE, (TEXT("SMI::HandleSMISSPAPI: invalid command has been specified\r\n")));
}
}
else
{
MESSAGE(MESSAGE_ZONE,
(TEXT("SMI::HandleSMISSPAPI: invalid input structure\r\n")));
}
MESSAGE(MESSAGE_ZONE, (TEXT("-SMI::HandleSMISSPAPI\r\n")));
return ulRetVal;
}
///////////////////////////////////////////////////////////////////////////////
// SSP Start-up Initialization.
VOID SMI::InitSSP()
{
#ifndef DISABLE_INTERRUPT_MANAGEMENT
for (INT i = 0; i < SSP_COUNT; i++)
{
m_SSPInfo[i].Active = FALSE;
m_SSPInfo[i].writeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
m_SSPInfo[SSP0].IntHandler = ssp0IntHandlerEntry;
m_SSPInfo[SSP0].dwIntMask = _F_MASK(INT_MASK_SSP0);
m_SSPInfo[SSP0].dwControl0Reg = SSP_0_CONTROL_0;
m_SSPInfo[SSP0].dwControl1Reg = SSP_0_CONTROL_1;
m_SSPInfo[SSP0].dwDataReg = SSP_0_DATA;
m_SSPInfo[SSP0].dwStatusReg = SSP_0_STATUS;
m_SSPInfo[SSP0].dwPrescaleReg = SSP_0_CLOCK_PRESCALE;
m_SSPInfo[SSP0].dwIntStatusReg = SSP_0_INTERRUPT_STATUS;
m_SSPInfo[SSP1].IntHandler = ssp1IntHandlerEntry;
m_SSPInfo[SSP1].dwIntMask = _F_MASK(INT_MASK_SSP1);
m_SSPInfo[SSP1].dwControl0Reg = SSP_1_CONTROL_0;
m_SSPInfo[SSP1].dwControl1Reg = SSP_1_CONTROL_1;
m_SSPInfo[SSP1].dwDataReg = SSP_1_DATA;
m_SSPInfo[SSP1].dwStatusReg = SSP_1_STATUS;
m_SSPInfo[SSP1].dwPrescaleReg = SSP_1_CLOCK_PRESCALE;
m_SSPInfo[SSP1].dwIntStatusReg = SSP_1_INTERRUPT_STATUS;
#endif // DISABLE_INTERRUPT_MANAGEMENT
}
#ifndef DISABLE_INTERRUPT_MANAGEMENT
///////////////////////////////////////////////////////////////////////////////
// SSP Interrupt Handler.
VOID SMI::sspIntHandler(VGXSSP_ENUM ssp)
{
// Read interrupt status.
DWORD dwIntStatus = peekRegisterDWord(m_SSPInfo[ssp].dwIntStatusReg);
// Check for "transmit FIFO" interrupt.
if (FIELD_GET(dwIntStatus, SSP_0_INTERRUPT_STATUS, TRANSMIT)
== SSP_0_INTERRUPT_STATUS_TRANSMIT_ACTIVE)
{
while (FIELD_GET(peekRegisterDWord(m_SSPInfo[ssp].dwStatusReg), SSP_0_STATUS, TRANSMIT_FIFO) !=
SSP_0_STATUS_TRANSMIT_FIFO_FULL)
{
WORD data;
if (!m_SSPInfo[ssp].writeBuffer.GetWord(data))
{
// No more data to transmit - disable "transmit FIFO" interrupt
pokeRegisterDWord(m_SSPInfo[ssp].dwControl1Reg,
FIELD_SET(peekRegisterDWord(m_SSPInfo[ssp].dwControl1Reg),
SSP_0_CONTROL_1, TRANSMIT_INTERRUPT, DISABLE));
// Set the event
SetEvent(m_SSPInfo[ssp].writeEvent);
// Stop the cycle
break;
}
// Send the data out
pokeRegisterDWord(m_SSPInfo[ssp].dwDataReg, data);
}
}
// Check for "receive FIFO" interrupt.
if (FIELD_GET(dwIntStatus, SSP_0_INTERRUPT_STATUS, RECEIVE)
== SSP_0_INTERRUPT_STATUS_RECEIVE_ACTIVE)
{
while (FIELD_GET(peekRegisterDWord(m_SSPInfo[ssp].dwStatusReg), SSP_0_STATUS, RECEIVE_FIFO) !=
SSP_0_STATUS_RECEIVE_FIFO_EMPTY)
{
m_SSPInfo[ssp].readBuffer.PutWord(
(WORD)peekRegisterDWord(m_SSPInfo[ssp].dwDataReg));
}
}
// Check for "read FIFO" overrun.
if (FIELD_GET(dwIntStatus, SSP_0_INTERRUPT_STATUS, OVERRUN)
== SSP_0_INTERRUPT_STATUS_OVERRUN_ACTIVE)
{
// Clear overrun interrupt.
pokeRegisterDWord(m_SSPInfo[ssp].dwIntStatusReg, dwIntStatus);
// Set overrun flag
m_SSPInfo[ssp].readBuffer.m_bOverrun = TRUE;
}
}
#endif // DISABLE_INTERRUPT_MANAGEMENT
///////////////////////////////////////////////////////////////////////////////
// Opens specified SSP channel.
ULONG SMI::sspOpen(PVGXSSP_OPEN_STRUCT pInit)
{
MESSAGE(MESSAGE_ZONE, (TEXT("+SMI::sspOpen\r\n")));
#ifdef DISABLE_INTERRUPT_MANAGEMENT
ULONG nResult = VGXSSP_NOT_SUPPORTED;
#else
ULONG nResult = VGXSSP_SUCCESS;
do
{
// Validate "ssp"
if ((pInit->ssp != SSP0) &&
(pInit->ssp != SSP1))
{
nResult = VGXSSP_INVALID_SSP;
break;
}
// Make sure UART1 is not active
if ((pInit->ssp == SSP1) && IsUART1Active())
{
nResult = VGXSSP_UART1_ACTIVE;
break;
}
if (m_SSPInfo[pInit->ssp].Active)
{
// Already "open"
break;
}
// Validate "clock_prescale"
if ((pInit->clock_prescale < 2) ||
(pInit->clock_prescale > 254) ||
(pInit->clock_prescale & 1))
{
nResult = VGXSSP_INVALID_PRESCALE;
break;
}
// Validate "data_size"
if ((pInit->data_size < 4) ||
(pInit->data_size > 16))
{
nResult = VGXSSP_INVALID_DATA_SIZE;
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -