www.pudn.com > sdio-2.6.18-full.rar > hif.c
/*
* Copyright (c) 2004-2005 Atheros Communications Inc.
*
* Wireless Network driver for Atheros AR6001
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
*
* This file contains the routines handling the interaction with the SDIO
* driver
*/
#include "hif_internal.h"
/* ------ Static Variables ------ */
/* ------ Global Variable Declarations ------- */
SD_PNP_INFO Ids[] = {
{
.SDIO_ManufacturerID = MANUFACTURER_ID_BASE | 0xB,
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
.SDIO_FunctionClass = FUNCTION_CLASS,
.SDIO_FunctionNo = 1
},
{
.SDIO_ManufacturerID = MANUFACTURER_ID_BASE | 0xA,
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
.SDIO_FunctionClass = FUNCTION_CLASS,
.SDIO_FunctionNo = 1
},
{
.SDIO_ManufacturerID = MANUFACTURER_ID_BASE | 0x9,
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
.SDIO_FunctionClass = FUNCTION_CLASS,
.SDIO_FunctionNo = 1
},
{
.SDIO_ManufacturerID = MANUFACTURER_ID_BASE | 0x8,
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
.SDIO_FunctionClass = FUNCTION_CLASS,
.SDIO_FunctionNo = 1
},
{
} //list is null termintaed
};
TARGET_FUNCTION_CONTEXT FunctionContext = {
.function.Version = CT_SDIO_STACK_VERSION_CODE,
.function.pName = "sdio_wlan",
.function.MaxDevices = 1,
.function.NumDevices = 0,
.function.pIds = Ids,
.function.pProbe = hifDeviceInserted,
.function.pRemove = hifDeviceRemoved,
.function.pSuspend = NULL,
.function.pResume = NULL,
.function.pWake = NULL,
.function.pContext = &FunctionContext,
};
HIF_DEVICE hifDevice[HIF_MAX_DEVICES];
HTC_CALLBACKS htcCallbacks;
BUS_REQUEST busRequest[BUS_REQUEST_MAX_NUM];
OS_CRITICALSECTION lock;
extern A_UINT32 onebitmode;
extern A_UINT32 busspeedlow;
extern A_UINT32 debughif;
#ifdef DEBUG
#define ATH_DEBUG_ERROR 1
#define ATH_DEBUG_WARN 2
#define ATH_DEBUG_TRACE 3
#define _AR_DEBUG_PRINTX_ARG(arg...) arg
#define AR_DEBUG_PRINTF(lvl, args)\
{if (lvl <= debughif)\
A_PRINTF(KERN_ALERT _AR_DEBUG_PRINTX_ARG args);\
}
#else
#define AR_DEBUG_PRINTF(lvl, args)
#endif
/* ------ Functions ------ */
void
HIFRegisterCallbacks(HTC_CALLBACKS *callbacks)
{
SDIO_STATUS status;
DBG_ASSERT(callbacks != NULL);
/* Store the callback and event handlers */
htcCallbacks.deviceInsertedHandler = callbacks->deviceInsertedHandler;
htcCallbacks.deviceRemovedHandler = callbacks->deviceRemovedHandler;
htcCallbacks.deviceSuspendHandler = callbacks->deviceSuspendHandler;
htcCallbacks.deviceResumeHandler = callbacks->deviceResumeHandler;
htcCallbacks.deviceWakeupHandler = callbacks->deviceWakeupHandler;
htcCallbacks.rwCompletionHandler = callbacks->rwCompletionHandler;
htcCallbacks.dsrHandler = callbacks->dsrHandler;
CriticalSectionInit(&lock);
/* Register with bus driver core */
status = SDIO_RegisterFunction(&FunctionContext.function);
DBG_ASSERT(SDIO_SUCCESS(status));
}
A_STATUS
HIFReadWrite(HIF_DEVICE *device,
A_UINT32 address,
A_UCHAR *buffer,
A_UINT32 length,
HIF_REQUEST *request,
void *context)
{
A_UINT8 rw;
A_UINT8 mode;
A_UINT8 funcNo;
A_UINT8 opcode;
A_UINT16 count;
SDREQUEST *sdrequest;
SDIO_STATUS status;
DBG_ASSERT(device != NULL);
DBG_ASSERT(device->handle != NULL);
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
sdrequest = hifAllocateDeviceRequest(device->handle);
if (sdrequest == NULL) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("Unable to allocate bus request\n"));
return A_ERROR;
}
sdrequest->pDataBuffer = buffer;
if (request->emode == HIF_SYNCHRONOUS) {
sdrequest->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS;
sdrequest->pCompleteContext = NULL;
sdrequest->pCompletion = NULL;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Execution mode: Synchronous\n"));
} else if (request->emode == HIF_ASYNCHRONOUS) {
sdrequest->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS |
SDREQ_FLAGS_TRANS_ASYNC;
sdrequest->pCompleteContext = context;
sdrequest->pCompletion = hifRWCompletionHandler;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Execution mode: Asynchronous\n"));
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Invalid execution mode: %d\n", request->emode));
return A_ERROR;
}
if (request->type == HIF_EXTENDED_IO) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Command type: CMD53\n"));
sdrequest->Command = CMD53;
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Invalid command type: %d\n", request->type));
return A_ERROR;
}
if (request->dmode == HIF_BLOCK_BASIS) {
mode = CMD53_BLOCK_BASIS;
sdrequest->BlockLen = HIF_MBOX_BLOCK_SIZE;
sdrequest->BlockCount = length / HIF_MBOX_BLOCK_SIZE;
count = sdrequest->BlockCount;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
("Block mode (BlockLen: %d, BlockCount: %d)\n",
sdrequest->BlockLen, sdrequest->BlockCount));
} else if (request->dmode == HIF_BYTE_BASIS) {
mode = CMD53_BYTE_BASIS;
sdrequest->BlockLen = length;
sdrequest->BlockCount = 1;
count = sdrequest->BlockLen;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
("Byte mode (BlockLen: %d, BlockCount: %d)\n",
sdrequest->BlockLen, sdrequest->BlockCount));
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Invalid data mode: %d\n", request->dmode));
return A_ERROR;
}
if ((address >= HIF_MBOX_START_ADDR(0)) &&
(address <= HIF_MBOX_END_ADDR(3)))
{
/*
* Mailbox write. Adjust the address so that the last byte
* falls on the EOM address.
*/
address += (HIF_MBOX_WIDTH - length);
}
if (request->direction == HIF_WRITE) {
rw = CMD53_WRITE;
sdrequest->Flags |= SDREQ_FLAGS_DATA_WRITE;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Direction: Write\n"));
} else if (request->direction == HIF_READ) {
rw = CMD53_READ;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Direction: Read\n"));
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Invalid direction: %d\n", request->direction));
return A_ERROR;
}
if (request->amode == HIF_FIXED_ADDRESS) {
opcode = CMD53_FIXED_ADDRESS;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Address mode: Fixed\n"));
} else if (request->amode == HIF_INCREMENTAL_ADDRESS) {
opcode = CMD53_INCR_ADDRESS;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Address mode: Incremental\n"));
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Invalid address mode: %d\n", request->amode));
return A_ERROR;
}
funcNo = SDDEVICE_GET_SDIO_FUNCNO(device->handle);
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Function number: %d\n", funcNo));
SDIO_SET_CMD53_ARG(sdrequest->Argument, rw, funcNo,
mode, opcode, address, count);
/* Send the command out */
status = SDDEVICE_CALL_REQUEST_FUNC(device->handle, sdrequest);
if (status == SDIO_STATUS_PENDING) {
DBG_ASSERT(request->emode == HIF_ASYNCHRONOUS);
return A_OK;
} else if (status == SDIO_STATUS_SUCCESS) {
DBG_ASSERT(request->emode == HIF_SYNCHRONOUS);
hifFreeDeviceRequest(sdrequest);
return A_OK;
}
return A_ERROR;
}
A_STATUS
HIFConfigureDevice(HIF_DEVICE *device, HIF_DEVICE_CONFIG_OPCODE opcode,
void *config, A_UINT32 configLen)
{
A_UINT32 count;
switch(opcode) {
case HIF_DEVICE_GET_MBOX_BLOCK_SIZE:
((A_UINT32 *)config)[0] = HIF_MBOX0_BLOCK_SIZE;
((A_UINT32 *)config)[1] = HIF_MBOX1_BLOCK_SIZE;
((A_UINT32 *)config)[2] = HIF_MBOX2_BLOCK_SIZE;
((A_UINT32 *)config)[3] = HIF_MBOX3_BLOCK_SIZE;
break;
case HIF_DEVICE_GET_MBOX_ADDR:
for (count = 0; count < 4; count ++) {
((A_UINT32 *)config)[count] = HIF_MBOX_START_ADDR(count);
}
break;
default:
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Invalid configuration opcode: %d\n", opcode));
return A_ERROR;
}
return A_OK;
}
void
HIFShutDownDevice(HIF_DEVICE *device)
{
A_UINT8 data;
A_UINT32 count;
SDIO_STATUS status;
SDCONFIG_BUS_MODE_DATA busSettings;
SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData;
if (device != NULL) {
DBG_ASSERT(device->handle != NULL);
/* Remove the allocated current if any */
status = SDLIB_IssueConfig(device->handle,
SDCONFIG_FUNC_FREE_SLOT_CURRENT, NULL, 0);
DBG_ASSERT(SDIO_SUCCESS(status));
/* Disable the card */
fData.EnableFlags = SDCONFIG_DISABLE_FUNC;
fData.TimeOut = 1;
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_ENABLE_DISABLE,
&fData, sizeof(fData));
DBG_ASSERT(SDIO_SUCCESS(status));
/* Perform a soft I/O reset */
data = SDIO_IO_RESET;
status = SDLIB_IssueCMD52(device->handle, 0, SDIO_IO_ABORT_REG,
&data, 1, 1);
DBG_ASSERT(SDIO_SUCCESS(status));
/*
* WAR - Codetelligence driver does not seem to shutdown correctly in 1
* bit mode. By default it configures the HC in the 4 bit. Its later in
* our driver that we switch to 1 bit mode. If we try to shutdown, the
* driver hangs so we revert to 4 bit mode, to be transparent to the
* underlying bus driver.
*/
if (onebitmode) {
ZERO_OBJECT(busSettings);
busSettings.BusModeFlags =
device->handle->pHcd->CardProperties.BusMode;
SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
SDCONFIG_BUS_WIDTH_4_BIT);
/* Issue config request to change the bus width to 4 bit */
status = SDLIB_IssueConfig(device->handle, SDCONFIG_BUS_MODE_CTRL,
&busSettings,
sizeof(SDCONFIG_BUS_MODE_DATA));
DBG_ASSERT(SDIO_SUCCESS(status));
}
/* Free the bus requests */
for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) {
SDDeviceFreeRequest(device->handle, busRequest[count].request);
busRequest[count].free = FALSE;
}
} else {
/* Unregister with bus driver core */
status = SDIO_UnregisterFunction(&FunctionContext.function);
DBG_ASSERT(SDIO_SUCCESS(status));
}
}
void
hifRWCompletionHandler(SDREQUEST *request)
{
A_STATUS status;
void *context;
if (SDIO_SUCCESS(request->Status)) {
status = A_OK;
} else {
status = A_ERROR;
}
context = (void *)request->pCompleteContext;
htcCallbacks.rwCompletionHandler(context, status);
DBG_ASSERT(status == A_OK);
hifFreeDeviceRequest(request);
}
void
hifIRQHandler(void *context)
{
A_STATUS status;
HIF_DEVICE *device;
device = (HIF_DEVICE *)context;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
status = htcCallbacks.dsrHandler(device);
DBG_ASSERT(status == A_OK);
}
BOOL
hifDeviceInserted(SDFUNCTION *function, SDDEVICE *handle)
{
BOOL enabled;
A_UINT8 data;
A_UINT32 count;
HIF_DEVICE *device;
SDIO_STATUS status;
A_UINT16 maxBlocks;
A_UINT16 maxBlockSize;
SDCONFIG_BUS_MODE_DATA busSettings;
SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData;
TARGET_FUNCTION_CONTEXT *functionContext;
SDCONFIG_FUNC_SLOT_CURRENT_DATA slotCurrent;
DBG_ASSERT(function != NULL);
DBG_ASSERT(handle != NULL);
device = addHifDevice(handle);
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
functionContext = (TARGET_FUNCTION_CONTEXT *)function->pContext;
/*
* Issue commands to get the manufacturer ID and stuff and compare it
* against the rev Id derived from the ID registered during the
* initialization process. Report the device only in the case there
* is a match. In the case od SDIO, the bus driver has already queried
* these details so we just need to use their data structures to get the
* relevant values. Infact, the driver has already matched it against
* the Ids that we registered with it so we dont need to the step here.
*/
/* Configure the SDIO Bus Width */
if (onebitmode) {
data = SDIO_BUS_WIDTH_1_BIT;
status = SDLIB_IssueCMD52(handle, 0, SDIO_BUS_IF_REG, &data, 1, 1);
if (!SDIO_SUCCESS(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Unable to set the bus width to 1 bit\n"));
return FALSE;
}
}
/* Get current bus flags */
ZERO_OBJECT(busSettings);
busSettings.BusModeFlags = handle->pHcd->CardProperties.BusMode;
if (onebitmode) {
SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
SDCONFIG_BUS_WIDTH_1_BIT);
}
busSettings.ClockRate = (busspeedlow ? SDIO_CLOCK_FREQUENCY_REDUCED :
SDIO_CLOCK_FREQUENCY_DEFAULT);
/* Issue config request to override clock rate */
status = SDLIB_IssueConfig(handle, SDCONFIG_BUS_MODE_CTRL, &busSettings,
sizeof(SDCONFIG_BUS_MODE_DATA));
if (!SDIO_SUCCESS(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Unable to configure the host clock\n"));
return FALSE;
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
("Configured clock: %d, Maximum clock: %d\n",
busSettings.ActualClockRate,
SDDEVICE_GET_MAX_CLOCK(handle)));
}
/*
* Check if the target supports block mode. This result of this check
* can be used to implement the HIFReadWrite API.
*/
if (SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle)) {
/* Limit block size to operational block limit or card function
capability */
maxBlockSize = min(SDDEVICE_GET_OPER_BLOCK_LEN(handle),
SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle));
/* check if the card support multi-block transfers */
if (!(SDDEVICE_GET_SDIOCARD_CAPS(handle) & SDIO_CAPS_MULTI_BLOCK)) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Byte basis only\n"));
/* Limit block size to max byte basis */
maxBlockSize = min(maxBlockSize,
(A_UINT16)SDIO_MAX_LENGTH_BYTE_BASIS);
maxBlocks = 1;
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Multi-block capable\n"));
maxBlocks = SDDEVICE_GET_OPER_BLOCKS(handle);
status = SDLIB_SetFunctionBlockSize(handle, HIF_MBOX_BLOCK_SIZE);
if (!SDIO_SUCCESS(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Failed to set block size. Err:%d\n", status));
return FALSE;
}
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
("Bytes Per Block: %d bytes, Block Count:%d \n",
maxBlockSize, maxBlocks));
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Function does not support Block Mode!\n"));
return FALSE;
}
/* Allocate the slot current */
status = SDLIB_GetDefaultOpCurrent(handle, &slotCurrent.SlotCurrent);
if (SDIO_SUCCESS(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Allocating Slot current: %d mA\n",
slotCurrent.SlotCurrent));
status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_ALLOC_SLOT_CURRENT,
&slotCurrent, sizeof(slotCurrent));
if (!SDIO_SUCCESS(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Failed to allocate slot current %d\n", status));
return FALSE;
}
}
/* Enable the dragon function */
count = 0;
enabled = FALSE;
fData.TimeOut = 1;
fData.EnableFlags = SDCONFIG_ENABLE_FUNC;
while ((count++ < SDWLAN_ENABLE_DISABLE_TIMEOUT) && !enabled)
{
/* Enable dragon */
status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_ENABLE_DISABLE,
&fData, sizeof(fData));
if (!SDIO_SUCCESS(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
("Attempting to enable the card again\n"));
continue;
}
/* Mark the status as enabled */
enabled = TRUE;
}
/* Check if we were succesful in enabling the target */
if (!enabled) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("Failed to communicate with the target\n"));
return FALSE;
}
/* Allocate the bus requests to be used later */
for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) {
if ((busRequest[count].request = SDDeviceAllocRequest(handle)) == NULL){
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("Unable to allocate memory\n"));
/* TODO: Free the memory that has already been allocated */
return FALSE;
}
busRequest[count].free = TRUE;
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
("busRequest[%d].request = 0x%p, busRequest[%d].free = %d\n", count, busRequest[count].request, count, busRequest[count].free));
}
/*
* Adding a wait of around a second before we issue the very first
* command to dragon. During the process of loading/unloading the
* driver repeatedly it was observed that we get a data timeout
* while accessing function 1 registers in the chip. The theory at
* this point is that some initialization delay in dragon is
* causing the SDIO state in dragon core to be not ready even after
* the ready bit indicates that function 1 is ready. Accomodating
* for this behavior by adding some delay in the driver before it
* issues the first command after switching on dragon. Need to
* investigate this a bit more - TODO
*/
A_MDELAY(1000);
/* Inform HTC */
if ((htcCallbacks.deviceInsertedHandler(device)) != A_OK) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device rejected\n"));
return FALSE;
}
return TRUE;
}
void
HIFAckInterrupt(HIF_DEVICE *device)
{
SDIO_STATUS status;
DBG_ASSERT(device != NULL);
DBG_ASSERT(device->handle != NULL);
/* Acknowledge our function IRQ */
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_ACK_IRQ,
NULL, 0);
DBG_ASSERT(SDIO_SUCCESS(status));
}
void
HIFUnMaskInterrupt(HIF_DEVICE *device)
{
SDIO_STATUS status;
DBG_ASSERT(device != NULL);
DBG_ASSERT(device->handle != NULL);
/* Register the IRQ Handler */
SDDEVICE_SET_IRQ_HANDLER(device->handle, hifIRQHandler, device);
/* Unmask our function IRQ */
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_UNMASK_IRQ,
NULL, 0);
DBG_ASSERT(SDIO_SUCCESS(status));
}
void HIFMaskInterrupt(HIF_DEVICE *device)
{
SDIO_STATUS status;
DBG_ASSERT(device != NULL);
DBG_ASSERT(device->handle != NULL);
/* Mask our function IRQ */
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_MASK_IRQ,
NULL, 0);
DBG_ASSERT(SDIO_SUCCESS(status));
/* Unregister the IRQ Handler */
SDDEVICE_SET_IRQ_HANDLER(device->handle, NULL, NULL);
}
SDREQUEST *
hifAllocateDeviceRequest(SDDEVICE *device)
{
SDREQUEST *request;
A_UINT32 count;
DBG_ASSERT(device != NULL);
/* Acquire lock */
CriticalSectionAcquire(&lock);
request = NULL;
for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
("busRequest[%d].request = 0x%p, busRequest[%d].free = %d\n", count, busRequest[count].request, count, busRequest[count].free));
if (busRequest[count].free) {
request = busRequest[count].request;
busRequest[count].free = FALSE;
break;
}
}
/* Release lock */
CriticalSectionRelease(&lock);
return request;
}
void
hifFreeDeviceRequest(SDREQUEST *request)
{
A_UINT32 count;
DBG_ASSERT(request != NULL);
/* Acquire lock */
CriticalSectionAcquire(&lock);
for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) {
if (busRequest[count].request == request) {
busRequest[count].free = TRUE;
break;
}
}
/* Release lock */
CriticalSectionRelease(&lock);
}
void
hifDeviceRemoved(SDFUNCTION *function, SDDEVICE *handle)
{
A_STATUS status;
HIF_DEVICE *device;
DBG_ASSERT(function != NULL);
DBG_ASSERT(handle != NULL);
device = getHifDevice(handle);
status = htcCallbacks.deviceRemovedHandler(device);
delHifDevice(handle);
DBG_ASSERT(status == A_OK);
}
HIF_DEVICE *
addHifDevice(SDDEVICE *handle)
{
DBG_ASSERT(handle != NULL);
hifDevice[0].handle = handle;
return &hifDevice[0];
}
HIF_DEVICE *
getHifDevice(SDDEVICE *handle)
{
DBG_ASSERT(handle != NULL);
return &hifDevice[0];
}
void
delHifDevice(SDDEVICE *handle)
{
DBG_ASSERT(handle != NULL);
hifDevice[0].handle = NULL;
}