//------------------------------------------------------------------------------ // 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 */