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