blob: 39e5823794a8088f621d3d914ba03f1e191801bb [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2004-2010 Atheros Communications Inc.
// All rights reserved.
//
//
//
// 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 (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 (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 (!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 */