476 lines
15 KiB
C
476 lines
15 KiB
C
|
//------------------------------------------------------------------------------
|
||
|
// ISC License (ISC)
|
||
|
//
|
||
|
// Copyright (c) 2004-2010, The Linux Foundation
|
||
|
// All rights reserved.
|
||
|
// Software was previously licensed under ISC license by Qualcomm Atheros, Inc.
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||
|
// purpose with or without fee is hereby granted, provided that the above
|
||
|
// copyright notice and this permission notice appear in all copies.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
// Author(s): ="Atheros"
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
#include "ar6000_drv.h"
|
||
|
|
||
|
#ifdef HTC_RAW_INTERFACE
|
||
|
|
||
|
static void
|
||
|
ar6000_htc_raw_read_cb(void *Context, HTC_PACKET *pPacket)
|
||
|
{
|
||
|
AR_SOFTC_T *ar = (AR_SOFTC_T *)Context;
|
||
|
raw_htc_buffer *busy;
|
||
|
HTC_RAW_STREAM_ID streamID;
|
||
|
AR_RAW_HTC_T *arRaw = ar->arRawHtc;
|
||
|
|
||
|
busy = (raw_htc_buffer *)pPacket->pPktContext;
|
||
|
A_ASSERT(busy != NULL);
|
||
|
if (busy == NULL)
|
||
|
return;
|
||
|
|
||
|
if (pPacket->Status == A_ECANCELED) {
|
||
|
/*
|
||
|
* HTC provides A_ECANCELED status when it doesn't want to be refilled
|
||
|
* (probably due to a shutdown)
|
||
|
*/
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
streamID = arEndpoint2RawStreamID(ar,pPacket->Endpoint);
|
||
|
A_ASSERT(streamID != HTC_RAW_STREAM_NOT_MAPPED);
|
||
|
if(streamID == HTC_RAW_STREAM_NOT_MAPPED) {
|
||
|
return; /* in case panic_on_assert==0 */
|
||
|
}
|
||
|
|
||
|
#ifdef CF
|
||
|
if (down_trylock(&arRaw->raw_htc_read_sem[streamID])) {
|
||
|
#else
|
||
|
if (down_interruptible(&arRaw->raw_htc_read_sem[streamID])) {
|
||
|
#endif /* CF */
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Unable to down the semaphore\n"));
|
||
|
}
|
||
|
|
||
|
A_ASSERT((pPacket->Status != A_OK) ||
|
||
|
(pPacket->pBuffer == (busy->data + HTC_HEADER_LEN)));
|
||
|
|
||
|
busy->length = pPacket->ActualLength + HTC_HEADER_LEN;
|
||
|
busy->currPtr = HTC_HEADER_LEN;
|
||
|
arRaw->read_buffer_available[streamID] = TRUE;
|
||
|
//AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("raw read cb: 0x%X 0x%X \n", busy->currPtr,busy->length);
|
||
|
up(&arRaw->raw_htc_read_sem[streamID]);
|
||
|
|
||
|
/* Signal the waiting process */
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_HTC_RAW,("Waking up the StreamID(%d) read process\n", streamID));
|
||
|
wake_up_interruptible(&arRaw->raw_htc_read_queue[streamID]);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ar6000_htc_raw_write_cb(void *Context, HTC_PACKET *pPacket)
|
||
|
{
|
||
|
AR_SOFTC_T *ar = (AR_SOFTC_T *)Context;
|
||
|
raw_htc_buffer *free;
|
||
|
HTC_RAW_STREAM_ID streamID;
|
||
|
AR_RAW_HTC_T *arRaw = ar->arRawHtc;
|
||
|
|
||
|
free = (raw_htc_buffer *)pPacket->pPktContext;
|
||
|
A_ASSERT(free != NULL);
|
||
|
if (free == NULL)
|
||
|
return;
|
||
|
|
||
|
if (pPacket->Status == A_ECANCELED) {
|
||
|
/*
|
||
|
* HTC provides A_ECANCELED status when it doesn't want to be refilled
|
||
|
* (probably due to a shutdown)
|
||
|
*/
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
streamID = arEndpoint2RawStreamID(ar,pPacket->Endpoint);
|
||
|
A_ASSERT(streamID != HTC_RAW_STREAM_NOT_MAPPED);
|
||
|
if(streamID == HTC_RAW_STREAM_NOT_MAPPED) {
|
||
|
return; /* in case panic_on_assert==0 */
|
||
|
}
|
||
|
#ifdef CF
|
||
|
if (down_trylock(&arRaw->raw_htc_write_sem[streamID])) {
|
||
|
#else
|
||
|
if (down_interruptible(&arRaw->raw_htc_write_sem[streamID])) {
|
||
|
#endif
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Unable to down the semaphore\n"));
|
||
|
}
|
||
|
|
||
|
A_ASSERT(pPacket->pBuffer == (free->data + HTC_HEADER_LEN));
|
||
|
|
||
|
free->length = 0;
|
||
|
arRaw->write_buffer_available[streamID] = TRUE;
|
||
|
up(&arRaw->raw_htc_write_sem[streamID]);
|
||
|
|
||
|
/* Signal the waiting process */
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_HTC_RAW,("Waking up the StreamID(%d) write process\n", streamID));
|
||
|
wake_up_interruptible(&arRaw->raw_htc_write_queue[streamID]);
|
||
|
}
|
||
|
|
||
|
/* connect to a service */
|
||
|
static A_STATUS ar6000_connect_raw_service(AR_SOFTC_T *ar,
|
||
|
HTC_RAW_STREAM_ID StreamID)
|
||
|
{
|
||
|
A_STATUS status;
|
||
|
HTC_SERVICE_CONNECT_RESP response;
|
||
|
A_UINT8 streamNo;
|
||
|
HTC_SERVICE_CONNECT_REQ connect;
|
||
|
|
||
|
do {
|
||
|
|
||
|
A_MEMZERO(&connect,sizeof(connect));
|
||
|
/* pass the stream ID as meta data to the RAW streams service */
|
||
|
streamNo = (A_UINT8)StreamID;
|
||
|
connect.pMetaData = &streamNo;
|
||
|
connect.MetaDataLength = sizeof(A_UINT8);
|
||
|
/* these fields are the same for all endpoints */
|
||
|
connect.EpCallbacks.pContext = ar;
|
||
|
connect.EpCallbacks.EpTxComplete = ar6000_htc_raw_write_cb;
|
||
|
connect.EpCallbacks.EpRecv = ar6000_htc_raw_read_cb;
|
||
|
/* simple interface, we don't need these optional callbacks */
|
||
|
connect.EpCallbacks.EpRecvRefill = NULL;
|
||
|
connect.EpCallbacks.EpSendFull = NULL;
|
||
|
connect.MaxSendQueueDepth = RAW_HTC_WRITE_BUFFERS_NUM;
|
||
|
|
||
|
/* connect to the raw streams service, we may be able to get 1 or more
|
||
|
* connections, depending on WHAT is running on the target */
|
||
|
connect.ServiceID = HTC_RAW_STREAMS_SVC;
|
||
|
|
||
|
A_MEMZERO(&response,sizeof(response));
|
||
|
|
||
|
/* try to connect to the raw stream, it is okay if this fails with
|
||
|
* status HTC_SERVICE_NO_MORE_EP */
|
||
|
status = HTCConnectService(ar->arHtcTarget,
|
||
|
&connect,
|
||
|
&response);
|
||
|
|
||
|
if (A_FAILED(status)) {
|
||
|
if (response.ConnectRespCode == HTC_SERVICE_NO_MORE_EP) {
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HTC RAW , No more streams allowed \n"));
|
||
|
status = A_OK;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* set endpoint mapping for the RAW HTC streams */
|
||
|
arSetRawStream2EndpointIDMap(ar,StreamID,response.Endpoint);
|
||
|
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_HTC_RAW,("HTC RAW : stream ID: %d, endpoint: %d\n",
|
||
|
StreamID, arRawStream2EndpointID(ar,StreamID)));
|
||
|
|
||
|
} while (FALSE);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
int ar6000_htc_raw_open(AR_SOFTC_T *ar)
|
||
|
{
|
||
|
A_STATUS status;
|
||
|
int streamID, endPt, count2;
|
||
|
raw_htc_buffer *buffer;
|
||
|
HTC_SERVICE_ID servicepriority;
|
||
|
AR_RAW_HTC_T *arRaw = ar->arRawHtc;
|
||
|
if (!arRaw) {
|
||
|
arRaw = ar->arRawHtc = A_MALLOC(sizeof(AR_RAW_HTC_T));
|
||
|
if (arRaw) {
|
||
|
A_MEMZERO(arRaw, sizeof(AR_RAW_HTC_T));
|
||
|
}
|
||
|
}
|
||
|
A_ASSERT(ar->arHtcTarget != NULL);
|
||
|
if (ar->arHtcTarget == NULL)
|
||
|
return -ENODEV;
|
||
|
|
||
|
if (!arRaw) {
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Faile to allocate memory for HTC RAW interface\n"));
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
/* wait for target */
|
||
|
status = HTCWaitTarget(ar->arHtcTarget);
|
||
|
|
||
|
if (A_FAILED(status)) {
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HTCWaitTarget failed (%d)\n", status));
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
for (endPt = 0; endPt < ENDPOINT_MAX; endPt++) {
|
||
|
arRaw->arEp2RawMapping[endPt] = HTC_RAW_STREAM_NOT_MAPPED;
|
||
|
}
|
||
|
|
||
|
for (streamID = HTC_RAW_STREAM_0; streamID < HTC_RAW_STREAM_NUM_MAX; streamID++) {
|
||
|
/* Initialize the data structures */
|
||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
|
||
|
sema_init(&arRaw->raw_htc_read_sem[streamID], 1);
|
||
|
sema_init(&arRaw->raw_htc_write_sem[streamID], 1);
|
||
|
#else
|
||
|
init_MUTEX(&arRaw->raw_htc_read_sem[streamID]);
|
||
|
init_MUTEX(&arRaw->raw_htc_write_sem[streamID]);
|
||
|
#endif
|
||
|
init_waitqueue_head(&arRaw->raw_htc_read_queue[streamID]);
|
||
|
init_waitqueue_head(&arRaw->raw_htc_write_queue[streamID]);
|
||
|
|
||
|
/* try to connect to the raw service */
|
||
|
status = ar6000_connect_raw_service(ar,streamID);
|
||
|
|
||
|
if (A_FAILED(status)) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (arRawStream2EndpointID(ar,streamID) == 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (count2 = 0; count2 < RAW_HTC_READ_BUFFERS_NUM; count2 ++) {
|
||
|
/* Initialize the receive buffers */
|
||
|
buffer = &arRaw->raw_htc_write_buffer[streamID][count2];
|
||
|
memset(buffer, 0, sizeof(raw_htc_buffer));
|
||
|
buffer = &arRaw->raw_htc_read_buffer[streamID][count2];
|
||
|
memset(buffer, 0, sizeof(raw_htc_buffer));
|
||
|
|
||
|
SET_HTC_PACKET_INFO_RX_REFILL(&buffer->HTCPacket,
|
||
|
buffer,
|
||
|
buffer->data,
|
||
|
HTC_RAW_BUFFER_SIZE,
|
||
|
arRawStream2EndpointID(ar,streamID));
|
||
|
|
||
|
/* Queue buffers to HTC for receive */
|
||
|
if ((status = HTCAddReceivePkt(ar->arHtcTarget, &buffer->HTCPacket)) != A_OK)
|
||
|
{
|
||
|
BMIInit();
|
||
|
return -EIO;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (count2 = 0; count2 < RAW_HTC_WRITE_BUFFERS_NUM; count2 ++) {
|
||
|
/* Initialize the receive buffers */
|
||
|
buffer = &arRaw->raw_htc_write_buffer[streamID][count2];
|
||
|
memset(buffer, 0, sizeof(raw_htc_buffer));
|
||
|
}
|
||
|
|
||
|
arRaw->read_buffer_available[streamID] = FALSE;
|
||
|
arRaw->write_buffer_available[streamID] = TRUE;
|
||
|
}
|
||
|
|
||
|
if (A_FAILED(status)) {
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("HTC RAW, number of streams the target supports: %d \n", streamID));
|
||
|
|
||
|
servicepriority = HTC_RAW_STREAMS_SVC; /* only 1 */
|
||
|
|
||
|
/* set callbacks and priority list */
|
||
|
HTCSetCreditDistribution(ar->arHtcTarget,
|
||
|
ar,
|
||
|
NULL, /* use default */
|
||
|
NULL, /* use default */
|
||
|
&servicepriority,
|
||
|
1);
|
||
|
|
||
|
/* Start the HTC component */
|
||
|
if ((status = HTCStart(ar->arHtcTarget)) != A_OK) {
|
||
|
BMIInit();
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
(ar)->arRawIfInit = TRUE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int ar6000_htc_raw_close(AR_SOFTC_T *ar)
|
||
|
{
|
||
|
A_PRINTF("ar6000_htc_raw_close called \n");
|
||
|
HTCStop(ar->arHtcTarget);
|
||
|
|
||
|
/* reset the device */
|
||
|
ar6000_reset_device(ar->arHifDevice, ar->arTargetType, TRUE, FALSE);
|
||
|
/* Initialize the BMI component */
|
||
|
BMIInit();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
raw_htc_buffer *
|
||
|
get_filled_buffer(AR_SOFTC_T *ar, HTC_RAW_STREAM_ID StreamID)
|
||
|
{
|
||
|
int count;
|
||
|
raw_htc_buffer *busy;
|
||
|
AR_RAW_HTC_T *arRaw = ar->arRawHtc;
|
||
|
|
||
|
/* Check for data */
|
||
|
for (count = 0; count < RAW_HTC_READ_BUFFERS_NUM; count ++) {
|
||
|
busy = &arRaw->raw_htc_read_buffer[StreamID][count];
|
||
|
if (busy->length) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (busy->length) {
|
||
|
arRaw->read_buffer_available[StreamID] = TRUE;
|
||
|
} else {
|
||
|
arRaw->read_buffer_available[StreamID] = FALSE;
|
||
|
}
|
||
|
|
||
|
return busy;
|
||
|
}
|
||
|
|
||
|
ssize_t ar6000_htc_raw_read(AR_SOFTC_T *ar, HTC_RAW_STREAM_ID StreamID,
|
||
|
char __user *buffer, size_t length)
|
||
|
{
|
||
|
int readPtr;
|
||
|
raw_htc_buffer *busy;
|
||
|
AR_RAW_HTC_T *arRaw = ar->arRawHtc;
|
||
|
|
||
|
if (arRawStream2EndpointID(ar,StreamID) == 0) {
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("StreamID(%d) not connected! \n", StreamID));
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
if (down_interruptible(&arRaw->raw_htc_read_sem[StreamID])) {
|
||
|
return -ERESTARTSYS;
|
||
|
}
|
||
|
|
||
|
busy = get_filled_buffer(ar,StreamID);
|
||
|
while (!arRaw->read_buffer_available[StreamID]) {
|
||
|
up(&arRaw->raw_htc_read_sem[StreamID]);
|
||
|
|
||
|
/* Wait for the data */
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_HTC_RAW,("Sleeping StreamID(%d) read process\n", StreamID));
|
||
|
if (wait_event_interruptible(arRaw->raw_htc_read_queue[StreamID],
|
||
|
arRaw->read_buffer_available[StreamID]))
|
||
|
{
|
||
|
return -EINTR;
|
||
|
}
|
||
|
if (down_interruptible(&arRaw->raw_htc_read_sem[StreamID])) {
|
||
|
return -ERESTARTSYS;
|
||
|
}
|
||
|
busy = get_filled_buffer(ar,StreamID);
|
||
|
}
|
||
|
|
||
|
/* Read the data */
|
||
|
readPtr = busy->currPtr;
|
||
|
if (length > busy->length - HTC_HEADER_LEN) {
|
||
|
length = busy->length - HTC_HEADER_LEN;
|
||
|
}
|
||
|
if (copy_to_user(buffer, &busy->data[readPtr], length)) {
|
||
|
up(&arRaw->raw_htc_read_sem[StreamID]);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
busy->currPtr += length;
|
||
|
|
||
|
if (busy->currPtr == busy->length)
|
||
|
{
|
||
|
busy->currPtr = 0;
|
||
|
busy->length = 0;
|
||
|
HTC_PACKET_RESET_RX(&busy->HTCPacket);
|
||
|
//AR_DEBUG_PRINTF(ATH_DEBUG_HTC_RAW,("raw read ioctl: ep for packet:%d \n", busy->HTCPacket.Endpoint));
|
||
|
HTCAddReceivePkt(ar->arHtcTarget, &busy->HTCPacket);
|
||
|
}
|
||
|
arRaw->read_buffer_available[StreamID] = FALSE;
|
||
|
up(&arRaw->raw_htc_read_sem[StreamID]);
|
||
|
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
static raw_htc_buffer *
|
||
|
get_free_buffer(AR_SOFTC_T *ar, HTC_ENDPOINT_ID StreamID)
|
||
|
{
|
||
|
int count;
|
||
|
raw_htc_buffer *free;
|
||
|
AR_RAW_HTC_T *arRaw = ar->arRawHtc;
|
||
|
|
||
|
free = NULL;
|
||
|
for (count = 0; count < RAW_HTC_WRITE_BUFFERS_NUM; count ++) {
|
||
|
free = &arRaw->raw_htc_write_buffer[StreamID][count];
|
||
|
if (free->length == 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!free->length) {
|
||
|
arRaw->write_buffer_available[StreamID] = TRUE;
|
||
|
} else {
|
||
|
arRaw->write_buffer_available[StreamID] = FALSE;
|
||
|
}
|
||
|
|
||
|
return free;
|
||
|
}
|
||
|
|
||
|
ssize_t ar6000_htc_raw_write(AR_SOFTC_T *ar, HTC_RAW_STREAM_ID StreamID,
|
||
|
char __user *buffer, size_t length)
|
||
|
{
|
||
|
int writePtr;
|
||
|
raw_htc_buffer *free;
|
||
|
AR_RAW_HTC_T *arRaw = ar->arRawHtc;
|
||
|
if (arRawStream2EndpointID(ar,StreamID) == 0) {
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("StreamID(%d) not connected! \n", StreamID));
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
if (down_interruptible(&arRaw->raw_htc_write_sem[StreamID])) {
|
||
|
return -ERESTARTSYS;
|
||
|
}
|
||
|
|
||
|
/* Search for a free buffer */
|
||
|
free = get_free_buffer(ar,StreamID);
|
||
|
|
||
|
/* Check if there is space to write else wait */
|
||
|
while (!arRaw->write_buffer_available[StreamID]) {
|
||
|
up(&arRaw->raw_htc_write_sem[StreamID]);
|
||
|
|
||
|
/* Wait for buffer to become free */
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_HTC_RAW,("Sleeping StreamID(%d) write process\n", StreamID));
|
||
|
if (wait_event_interruptible(arRaw->raw_htc_write_queue[StreamID],
|
||
|
arRaw->write_buffer_available[StreamID]))
|
||
|
{
|
||
|
return -EINTR;
|
||
|
}
|
||
|
if (down_interruptible(&arRaw->raw_htc_write_sem[StreamID])) {
|
||
|
return -ERESTARTSYS;
|
||
|
}
|
||
|
free = get_free_buffer(ar,StreamID);
|
||
|
}
|
||
|
|
||
|
/* Send the data */
|
||
|
writePtr = HTC_HEADER_LEN;
|
||
|
if (length > (HTC_RAW_BUFFER_SIZE - HTC_HEADER_LEN)) {
|
||
|
length = HTC_RAW_BUFFER_SIZE - HTC_HEADER_LEN;
|
||
|
}
|
||
|
|
||
|
if (copy_from_user(&free->data[writePtr], buffer, length)) {
|
||
|
up(&arRaw->raw_htc_read_sem[StreamID]);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
free->length = length;
|
||
|
|
||
|
SET_HTC_PACKET_INFO_TX(&free->HTCPacket,
|
||
|
free,
|
||
|
&free->data[writePtr],
|
||
|
length,
|
||
|
arRawStream2EndpointID(ar,StreamID),
|
||
|
AR6K_DATA_PKT_TAG);
|
||
|
|
||
|
HTCSendPkt(ar->arHtcTarget,&free->HTCPacket);
|
||
|
|
||
|
arRaw->write_buffer_available[StreamID] = FALSE;
|
||
|
up(&arRaw->raw_htc_write_sem[StreamID]);
|
||
|
|
||
|
return length;
|
||
|
}
|
||
|
#endif /* HTC_RAW_INTERFACE */
|