📄 wmdevice.c
字号:
/*-----------------------------------------------------------------------------
* Copyright (c) Wolfson Microelectronics plc. All rights reserved.
*
* This software as well as any related documentation is furnished under
* license and may only be used or copied in accordance with the terms of the
* license. The information in this file is furnished for informational use
* only, is subject to change without notice, and should not be construed as
* a commitment by Wolfson Microelectronics plc. Wolfson Microelectronics plc
* assumes no responsibility or liability for any errors or inaccuracies that
* may appear in this document or any software that may be provided in
* association with this document.
*
* Except as permitted by such license, no part of this document may be
* reproduced, stored in a retrieval system, or transmitted in any form or by
* any means without the express written consent of Wolfson Microelectronics plc.
*
* $Id: WMDevice.c 2717 2006-02-02 12:26:48Z ib $
*
* This file contains functionality for opening and closing a device.
*
* Warning:
* This driver is specifically written for Wolfson Codecs. It is not a
* general CODEC device driver.
*
* -----------------------------------------------------------------------------*/
/*
* IMPORTANT
*
* This is one of the few files which can allocate memory. All functions in
* this file must still follow the following rules and guidelines:
*
* 1. They do not make system calls, unless WMSystemCallsAllowed returns TRUE.
*
* All functions take a context parameter, which passes in pointers to the virtual
* addresses of memory-mapped registers, and stores what type of device is being
* accessed.
*
* Some client functions provide shared memory via the v_pWMData member of the
* context structure. This cannot be relied on.
*/
/*
* Include files
*/
#include "WMCommon.h"
#include "WMGlobals.h"
#include "WMControlLink.h"
#include "WM2WireLink.h"
#include "WMPlatformACLink.h"
#include "WMPlatform2Wire.h"
#include "WMAudio.h"
#include "WMTouch.h"
#include "WMAuxADC.h"
#include "WMPlatform_OS.h"
#include "WMACLink.h"
#include "WMPower.h"
#include "WM8753Power.h"
#include "WMDevice.h"
/*
* Global definitions
*/
#if WM_MUTEX_LOCK_DEBUG
# define WM_LOCK_TRACE WM_TRACE
#else
# define WM_LOCK_TRACE( _hDevice, _args )
#endif /* WM_MUTEX_LOCK_DEBUG */
/*
* Private data
*/
/* The maximum length of time (in ms) to wait for the lock */
#define LINK_LOCK_TIMEOUT 50
/* The maximum length of time (in ms) to wait for the lock */
#define GLOBALS_LOCK_TIMEOUT 50
/* Mutex for the AC Link */
#ifdef WIN32
# define WM_LINK_MUTEX_NAME _T("WMLinkMutex")
#else
# define WM_LINK_MUTEX_NAME 'WMLink'
#endif
/* Mutex for protecting the global shared memory */
#define WM_GLOBALS_MEMORY_NAME _T("WMGlobalsMemory")
#ifdef WIN32
# define WM_GLOBALS_MUTEX_NAME _T("WMGlobalsMutex")
#else
# define WM_GLOBALS_MUTEX_NAME 'WMDT'
#endif
/*
* Function prototypes
*/
static WMSTATUS private_InitDeviceContext( WM_DRIVER_ID driverID,
WM_DEVICE_ID devID,
WM_DEVICE_CONTEXT **ppContext
);
static void private_FreeDeviceContext( WM_DEVICE_HANDLE hDevice );
#if WM_AC97
static WM_BOOL private_WMLockLink( WM_DEVICE_HANDLE hDevice,
const char *file,
int line
);
static void private_WMUnlockLink( WM_DEVICE_HANDLE hDevice,
const char *file,
int line
);
#endif /* WM_AC97 */
static WM_BOOL private_WMLockGlobalData( WM_DEVICE_HANDLE hDevice,
const char *file,
int line
);
static void private_WMUnlockGlobalData( WM_DEVICE_HANDLE hDevice,
const char *file,
int line
);
/*-----------------------------------------------------------------------------
* Function: WMOpenDevice
*
* Initialises the use of the Wolfson device by a given driver.
*
* Parameters:
* devID The device ID (e.g. WM_DEV_AC97_PRIMARY)
* driverID The driver ID (e.g. WM_DRIVER_TOUCH)
* phDevice A variable to receive the handle to the device
*
* Returns: WMSTATUS
* See WMStatus.h.
*---------------------------------------------------------------------------*/
WMSTATUS WMOpenDevice( WM_DEVICE_ID devID,
WM_DRIVER_ID driverID,
WM_DEVICE_HANDLE *phDevice
)
{
WM_DEVICE_CONTEXT *pDeviceContext;
WM_DEVICE_HANDLE hDevice;
WMSTATUS status;
/*
* Check we're using a supported combination of driver and device.
*/
switch ( driverID )
{
case WM_DRIVER_RAW:
#if WM_AUDIO
case WM_DRIVER_AUDIO:
#endif
/* All codecs support these */
break;
#if WM_TOUCH || WM_AUXADC
case WM_DRIVER_TOUCH:
case WM_DRIVER_AUXADC:
case WM_DRIVER_BATTERY:
/* Only AC'97 devices support these at present */
if ( !WM_IS_AC97_DEVICE_ID( devID ) )
{
status = WMS_NO_SUPPORTED_DEVICE;
goto error0;
}
break;
#endif /* WM_TOUCH || WM_AUXADC */
default:
status = WMS_INVALID_PARAMETER;
goto error0;
}
status = private_InitDeviceContext( driverID, devID, &pDeviceContext );
if ( WM_ERROR( status ) )
{
goto error0;
}
hDevice = WMDEVICE_TO_HANDLE( pDeviceContext );
status = WMLinkInit( hDevice, driverID );
if ( WM_ERROR( status ) && WMS_UNSUPPORTED != status)
{
goto error0;
}
if ( WMS_UNSUPPORTED == status )
{
/*
* The generic I2S drivers do not support full Link initialisation,
* for _any_ other drivers this is a fatal error
*/
if ( ! WM_IS_I2S_GENERIC_ID( devID ) )
{
goto error0;
}
}
/*
* Now initialise the subsystem.
*/
switch ( driverID )
{
case WM_DRIVER_RAW:
/* Nothing to do here */
break;
#if WM_AUDIO
case WM_DRIVER_AUDIO:
/* Initialise the audio subsystem */
status = WMAudioInit( hDevice );
if ( WM_ERROR( status ) )
{
goto error1;
}
break;
#endif
#if WM_TOUCH
case WM_DRIVER_TOUCH:
/* Initialise the touch subsystem */
status = WMTouchInit( hDevice );
if ( WM_ERROR( status ) )
{
goto error1;
}
break;
#endif
#if WM_AUXADC
case WM_DRIVER_AUXADC:
case WM_DRIVER_BATTERY:
/* Initialise the AuxADC subsystem */
status = WMAuxADCInit( hDevice );
if ( WM_ERROR( status ) )
{
goto error1;
}
break;
#endif
default:
status = WMS_INVALID_PARAMETER;
goto error0;
}
/*
* And we're done.
*/
*phDevice = hDevice;
return WMS_SUCCESS;
/*
* Error cleanup.
*/
error1:
WMLinkShutdown( hDevice, driverID );
error0:
*phDevice = NULL;
return status;
}
/*-----------------------------------------------------------------------------
* Function: WMCloseDevice
*
* Marks the end of use of the codec by this driver.
*
* Parameters:
* hDevice handle to the device (from WMOpenDevice)
* driverID The driver ID (e.g. WM_DRIVER_TOUCH)
*
* Returns: void
*---------------------------------------------------------------------------*/
void WMCloseDevice( WM_DEVICE_HANDLE hDevice, WM_DRIVER_ID driverID )
{
WMLinkShutdown( hDevice, driverID );
private_FreeDeviceContext( hDevice );
}
/*-----------------------------------------------------------------------------
* Function: private_InitDeviceContext
*
* Initialises and returns a pointer to the global device context for passing
* to the Raw functions.
*
* This must in some way allocate space for the global data area g_pWMData
* (definition in WMGlobals.h) so that the memory is common to all instances
* of drivers using the library.
*
* Parameters:
* driverID The driver ID (e.g. WM_DRIVER_AUDIO)
* devID The device ID (e.g. WM_DEV_AC97_PRIMARY)
* ppContext a variable to receive the pointer to the context structure.
*
* Returns: WMSTATUS
* See WMStatus.h
*---------------------------------------------------------------------------*/
static WMSTATUS private_InitDeviceContext( WM_DRIVER_ID driverID,
WM_DEVICE_ID devID,
WM_DEVICE_CONTEXT **ppContext
)
{
WMSTATUS status;
WM_DEVICE_CONTEXT *pDeviceContext;
WM_DEVICE_HANDLE hDevice; /* for WM_TRACE, MAP_MEMORY and WM_ASSERT */
volatile WM_SHARED_DATA *pWMData = NULL;
WM_DEVICE_ID deviceID;
WM_DEVICE_ID devOrdinal;
WM_CHIPTYPE chipType;
/*
* Decode the device ID.
*/
chipType = WM_DEV_GET_CHIP( devID );
devOrdinal = WM_DEV_GET_ORDINAL( devID );
/*
* Convert to a generic ID.
*/
if ( WM_IS_AC97_DEVICE_ID( devID ) )
{
deviceID = devID;
}
else if ( WM_IS_I2S_DEVICE_ID( devID ) )
{
deviceID = WM_DEV_MAKE_DEVICE( WM_DEV_I2S, devOrdinal );
}
else
{
status = WMS_NO_SUPPORTED_DEVICE;
goto error0;
}
/*
* Get ourselves a device context.
*/
status = WMPlatformAllocateDeviceContext( deviceID, &pDeviceContext );
if ( WM_ERROR( status ) )
{
goto error0;
}
if ( WM_IS_I2S_DEVICE_ID( deviceID ) )
{
/*
* Select our codec addresses as appropriate for I2S.
*/
switch ( devOrdinal )
{
case WM_DEV_PRIMARY:
pDeviceContext->codecAddress = WM_2WIRE_ADDR_1;
break;
case WM_DEV_SECONDARY:
pDeviceContext->codecAddress = WM_2WIRE_ADDR_2;
break;
default:
status = WMS_NO_SUPPORTED_DEVICE;
goto error0;
}
}
/*
* And remember our device ID.
*/
pDeviceContext->deviceID = deviceID;
/*
* Create our device handle.
*/
hDevice = WMDEVICE_TO_HANDLE( pDeviceContext );
/*
* Allocate our global data.
*/
if ( !pDeviceContext->v_pWMData )
{
pWMData = ALLOC_SHARED_MEMORY( hDevice,
volatile WM_SHARED_DATA,
sizeof( WM_SHARED_DATA ),
WM_GLOBALS_MEMORY_NAME
);
if ( !pWMData )
{
status = WMS_RESOURCE_FAIL;
goto error1;
}
pDeviceContext->v_pWMData = pWMData;
}
#if WM_AUXADC
/*
* Our digitiser memory is part of the global memory.
*/
pDeviceContext->v_pADCData = &pDeviceContext->v_pWMData->adcData;
#endif
/*
* Allocate platform memory. Note we always need to do this in
* case this is a second driver on the same device context which
* needs different areas of memory mapped. It shouldn't allocate
* the same area twice.
*/
status = WMPlatformAllocatePlatformMemory( hDevice, driverID );
if ( WM_ERROR( status ) )
{
goto error1;
}
/* Check whether we're initialised */
if ( pDeviceContext->flags & DEVICE_CONTEXT_INITIALISED )
{
goto InitDone;
}
/*
* Create the global data mutex.
* Note this will return the existing handle if another thread
* got in first.
*/
status = CREATE_MUTEX( &pDeviceContext->globalsMutex, WM_GLOBALS_MUTEX_NAME );
if ( WM_ERROR( status ) )
{
goto error1;
}
if ( WM_IS_AC97_DEVICE_ID( deviceID ) )
{
/*
* And now the AC link mutex.
*/
status = CREATE_MUTEX( &pDeviceContext->linkMutex, WM_LINK_MUTEX_NAME );
if ( WM_ERROR( status ) )
{
goto error1;
}
}
/*
* And we can do locking and system calls.
*/
pDeviceContext->preventSyscallCount = 0;
/*
* Now set up our device context with as much info as we can at this point.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -