--- drivers/sdio/function/Kconfig | 32 drivers/sdio/function/Makefile | 7 drivers/sdio/function/bluetooth/Makefile | 5 drivers/sdio/function/bluetooth/sdio_bt.c | 769 +++++++++++++++++ drivers/sdio/function/bluetooth/sdio_bt.h | 176 +++ drivers/sdio/function/bluetooth/sdio_bt_linux.h | 70 + drivers/sdio/function/bluetooth/sdio_bt_os.c | 584 ++++++++++++ drivers/sdio/function/gps/Makefile | 5 drivers/sdio/function/gps/gps.c | 392 ++++++++ drivers/sdio/function/gps/gps.h | 194 ++++ drivers/sdio/function/gps/gps_linux.h | 63 + drivers/sdio/function/gps/gps_os.c | 633 ++++++++++++++ drivers/sdio/function/memory/Makefile | 5 drivers/sdio/function/memory/sdio_memory.c | 926 ++++++++++++++++++++ drivers/sdio/function/memory/sdio_memory.h | 98 ++ drivers/sdio/function/memory/sdio_memory_linux.h | 80 + drivers/sdio/function/memory/sdio_memory_os.c | 1036 +++++++++++++++++++++++ 17 files changed, 5075 insertions(+) Index: linux-2.6.22/drivers/sdio/function/gps/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/gps/Makefile 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,5 @@ +# +# SDIO GPS function driver +# +obj-m += sdio_gps_fd.o +sdio_gps_fd-objs := gps.o gps_os.o Index: linux-2.6.22/drivers/sdio/function/gps/gps.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/gps/gps.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,392 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: gps.c + +@abstract: OS independent GPS class SDIO function driver + +#notes: + +@notice: Copyright (c), 2004-2005 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define MODULE_NAME SD_GPS_FD +#include +#include +#include "gps.h" +#include +#include + +void GpsIRQHandler(PVOID pContext); +static void TxCompletion(PSDREQUEST pReq); + + +/* + * GpsInitialize - initialize new device +*/ +SDIO_STATUS GpsInitialize(PSDGPS_DEVICE pDevice) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; + struct SDIO_GPS_TPL gpsTpl; + UINT32 nextTpl; + UINT8 tplLength; + UINT8 temp; + SDCONFIG_FUNC_SLOT_CURRENT_DATA slotCurrent; + + ZERO_OBJECT(fData); + ZERO_OBJECT(slotCurrent); + + do { + status = SemaphoreInitialize(&pDevice->DeviceSem, 0); + if (!SDIO_SUCCESS(status)) { + break; + } + + status = SDLIB_GetDefaultOpCurrent(pDevice->pSDDevice,&slotCurrent.SlotCurrent); + if (!SDIO_SUCCESS(status)) { + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Gps Function: Allocating Slot current: %d mA\n", slotCurrent.SlotCurrent)); + status = SDLIB_IssueConfig(pDevice->pSDDevice, + SDCONFIG_FUNC_ALLOC_SLOT_CURRENT, + &slotCurrent, + sizeof(slotCurrent)); + + if (!SDIO_SUCCESS((status))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Gps Function: failed to allocate slot current %d\n", + status)); + if (status == SDIO_STATUS_NO_RESOURCES) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Gps Function: Remaining Slot Current: %d mA\n", + slotCurrent.SlotCurrent)); + } + break; + } + + fData.EnableFlags = SDCONFIG_ENABLE_FUNC; + fData.TimeOut = 500; + status = SDLIB_IssueConfig(pDevice->pSDDevice, + SDCONFIG_FUNC_ENABLE_DISABLE, + &fData, + sizeof(fData)); + if (!SDIO_SUCCESS((status))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: GpsInitialize, failed to enable function %d\n", + status)); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function Ready!\n")); + pDevice->HwReady = TRUE; + /* setup starting CIS scan */ + nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pDevice->pSDDevice); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Function CIS starts at :0x%X \n", + SDDEVICE_GET_SDIO_FUNC_CISPTR(pDevice->pSDDevice))); + /* look for the GPS TPL */ + while (1) { + /* reset max buffer length */ + tplLength = sizeof(gpsTpl); + /* go get the GPS tuple */ + status = SDLIB_FindTuple(pDevice->pSDDevice, + GPS_TUPLE, + &nextTpl, + (PUINT8)&gpsTpl, + &tplLength); + + if (!SDIO_SUCCESS(status)){ + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: Failed to get GPS Tuple: %d \n",status)); + break; + } + + if (gpsTpl.StdTupleNumber == STD_GPS_TUPLE_SIOREG) { + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Found SDIOREG Tuple \n")); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: RegisterID:%d,RegisterExtID:%d \n", + gpsTpl.Tpd.AsSIOReg.RegisterID, gpsTpl.Tpd.AsSIOReg.RegisterExpID)); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: MaxBaudCode:%d, DRL:%d , DRM:%d\n", + gpsTpl.Tpd.AsSIOReg.MaxBaudRateCode, + gpsTpl.Tpd.AsSIOReg.DRL_4800,gpsTpl.Tpd.AsSIOReg.DRM_4800)); + pDevice->UartRegOffset = gpsTpl.Tpd.AsSIOReg.RegisterOffset[0]; + pDevice->UartRegOffset |= gpsTpl.Tpd.AsSIOReg.RegisterOffset[1] << 8; + pDevice->UartRegOffset |= gpsTpl.Tpd.AsSIOReg.RegisterOffset[2] << 16; + pDevice->UartMaxBaud = (gpsTpl.Tpd.AsSIOReg.MaxBaudRateCode > 0) ? + gpsTpl.Tpd.AsSIOReg.MaxBaudRateCode * 115200 : 115200; + pDevice->UartDivisor = 4 *(gpsTpl.Tpd.AsSIOReg.DRL_4800 | + (gpsTpl.Tpd.AsSIOReg.DRM_4800 << 8) ); + /* if zero, its not setup, guess */ + if (pDevice->UartDivisor == 0) { + pDevice->UartDivisor = 6666; + DBG_PRINT(SDDBG_WARN, ("SDIO GPS Function: no UART divisor, using 1\n")); + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: RegisterOffset:0x%X \n", + pDevice->UartRegOffset)); + break; + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Found Sub-Tuple %d .. continuing search\n", + gpsTpl.StdTupleNumber)); + continue; + } + } + + if (!SDIO_SUCCESS(status)) { + break; + } + + /* allocate a single request for asynch call usage */ + pDevice->pRequest = SDDeviceAllocRequest(pDevice->pSDDevice); + + if (NULL == pDevice->pRequest) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + /* make sure interrupts are off and this also tests to see if we can see the + * hardware */ + pDevice->InterruptEnable = 0; + status = WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); + if (!SDIO_SUCCESS((status))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: GpsInitialize, failed register write, %d\n", + status)); + break; + } + /* read registers to clear status */ + ReadRegister(pDevice, UART_LINE_STATUS_REG, &temp); + ReadRegister(pDevice, UART_RECEIVE_REG, &temp); + ReadRegister(pDevice, UART_INT_IDENT_REG, &temp); + ReadRegister(pDevice, UART_MODEM_STATUS_REG, &temp); + WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE | UART_FIFO_RCV_RESET); + WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE); + SetBaudRate(pDevice, 4800); + WriteRegister(pDevice, UART_LINE_CNTRL_REG, (UART_ONE_STOP | UART_NO_PARITY | UART_DATA_8_BITS)); + + /* set our IRQ handler */ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Registering GpsIrqHandler \n")); + SDDEVICE_SET_IRQ_HANDLER(pDevice->pSDDevice,GpsIRQHandler,pDevice); + /* unmask our interrupt on the card */ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: unmasking IRQ \n")); + status = SDLIB_IssueConfig(pDevice->pSDDevice,SDCONFIG_FUNC_UNMASK_IRQ,NULL,0); + if (!SDIO_SUCCESS((status))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: GpsInitialize, failed to unmask IRQ %d\n", + status)); + break; + } + +//?? DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: enabling InterruptEnable \n")); +//?? pDevice->InterruptEnable = UART_ERBFI | UART_ELSI; +//?? WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); + + } while (FALSE); + + if (!SDIO_SUCCESS(status)) { + GpsDeinitialize(pDevice); + } + SemaphorePost(&pDevice->DeviceSem); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: exiting \n")); + + return status; +} + +/* + * GpsDeinitialize - initialize new device +*/ +void GpsDeinitialize(PSDGPS_DEVICE pDevice) +{ + SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; + + if (!(SDDEVICE_IS_CARD_REMOVED(pDevice->pSDDevice))) { + if (pDevice->HwReady) { + /* try masking our IRQ */ + SDLIB_IssueConfig(pDevice->pSDDevice,SDCONFIG_FUNC_MASK_IRQ,NULL,0); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Unregistering GpsIrqHandler \n")); + SDDEVICE_SET_IRQ_HANDLER(pDevice->pSDDevice, NULL, NULL); + + /* power down the hardware */ + ZERO_OBJECT(fData); + fData.EnableFlags = SDCONFIG_DISABLE_FUNC; + fData.TimeOut = 500; + SDLIB_IssueConfig(pDevice->pSDDevice, + SDCONFIG_FUNC_ENABLE_DISABLE, + &fData, + sizeof(fData)); + } + } + pDevice->HwReady = FALSE; + + SDLIB_IssueConfig(pDevice->pSDDevice, + SDCONFIG_FUNC_FREE_SLOT_CURRENT, + NULL, + 0); + + if (pDevice->pRequest != NULL) { + SDDeviceFreeRequest(pDevice->pSDDevice, pDevice->pRequest); + pDevice->pRequest = NULL; + } + SemaphoreDelete(&pDevice->DeviceSem); +} +/* + * SetBaudRate - set the requested baud rate + */ +SDIO_STATUS SetBaudRate(PSDGPS_DEVICE pDevice, UINT32 BaudRate) +{ + UINT8 lineControl; + UINT8 saveInterrupt; + UINT32 divisor; + + /* disable device interrupts */ + ReadRegister(pDevice, UART_INT_ENABLE_REG, &saveInterrupt); + WriteRegister(pDevice, UART_INT_ENABLE_REG, 0); + /* check for a valid baud rate */ + if ((BaudRate % 1200) || (BaudRate > pDevice->UartMaxBaud)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: SetBaudRate, invalid rate %d\n", BaudRate)); + WriteRegister(pDevice, UART_INT_ENABLE_REG, saveInterrupt); + return SDIO_STATUS_INVALID_PARAMETER; + } + + /* calculate new divisor based on 1200 baud divisor */ + divisor = pDevice->UartDivisor / (BaudRate / 1200); + + ReadRegister(pDevice, UART_LINE_CNTRL_REG, &lineControl); + + WriteRegister(pDevice, UART_LINE_CNTRL_REG, lineControl | UART_CLOCK_ENABLE); + WriteRegister(pDevice, UART_BAUD_HIGH_REG, (UINT8)((divisor >> 8) & 0xFF)); + WriteRegister(pDevice, UART_BAUD_LOW_REG, (UINT8)(divisor & 0xFF)); + WriteRegister(pDevice, UART_LINE_CNTRL_REG, lineControl); + + WriteRegister(pDevice, UART_INT_ENABLE_REG, saveInterrupt); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: SetBaudRate, rate %d, divisor %d\n", + BaudRate, divisor)); + return SDIO_STATUS_SUCCESS; +} + +/* + * GpsIRQHandler - hamdle interrupts +*/ +void GpsIRQHandler(PVOID pContext) +{ + PSDGPS_DEVICE pDevice; + SDIO_STATUS status = SDIO_STATUS_DEVICE_ERROR; + UINT8 temp; + int max = 4; + pDevice = (PSDGPS_DEVICE)pContext; + DBG_PRINT(SDIO_GPS_TRACE_INT, ("+I\n")); + + while(1) { + if (max-- < 0) break; + /* read the ident register */ + status = ReadRegister(pDevice, UART_INT_IDENT_REG, &temp); + if (!SDIO_SUCCESS(status)) { + break; + } + /* INTPEND bit is zero indicates a pending interrupt */ + if (!(temp & UART_INTPEND)) { + /* get the encoded interrupt value */ + switch (temp & UART_IID_MASK) { + case UART_IID_RLS: + /* line status changed */ + ReadRegister(pDevice, UART_LINE_STATUS_REG, &temp); + DBG_PRINT(SDIO_GPS_TRACE_INT, ("+LS 0x%X\n", (UINT)temp)); + if (temp & UART_LSR_DR) { + GpsReceive(pDevice); + } + break; + case UART_IID_RDA: + /* handle receive */ + GpsReceive(pDevice); + break; + case UART_IID_CTI: + /* receiver timeout? */ + GpsReceive(pDevice); + DBG_PRINT(SDIO_GPS_TRACE_INT, ("+CTI\n")); + break; + case UART_IID_THRE: + /* transmitter empty */ + GpsTransmit(pDevice); + break; + case UART_IID_MS: + /* modem status */ + DBG_PRINT(SDIO_GPS_TRACE_INT, ("SDIO GPS Function: modem status change int \n")); + break; + default: + break; + } + } else { + /* no interrupts pending */ + break; + } + } + + /* ack the interrupt */ + status = SDLIB_IssueConfig(pDevice->pSDDevice,SDCONFIG_FUNC_ACK_IRQ,NULL,0); + + DBG_PRINT(SDIO_GPS_TRACE_INT, ("-I\n")); + +} + +/* + * WriteRegister - write an 8-bit register +*/ +SDIO_STATUS WriteRegister(PSDGPS_DEVICE pDevice, UINT reg, UINT8 Data) { + return SDLIB_IssueCMD52(pDevice->pSDDevice,SDDEVICE_GET_SDIO_FUNCNO(pDevice->pSDDevice), + ((pDevice)->UartRegOffset + (reg)),&Data,1,TRUE); +} + +/* + * WriteRegister - write an 8-bit register aysnchronously +*/ +SDIO_STATUS WriteRegisterAsynch(PSDGPS_DEVICE pDevice, UINT reg, UINT8 Data, PSDREQUEST pReq) +{ + if (NULL == pReq) { + return SDIO_STATUS_NO_RESOURCES; + } + + SDLIB_SetupCMD52Request(SDDEVICE_GET_SDIO_FUNCNO(pDevice->pSDDevice), + ((pDevice)->UartRegOffset + (reg)), + TRUE, + Data, + pReq); + pReq->Flags |= SDREQ_FLAGS_TRANS_ASYNC; + pReq->pCompletion = TxCompletion; + pReq->pCompleteContext = (PVOID)pDevice; + + return SDDEVICE_CALL_REQUEST_FUNC(pDevice->pSDDevice, pReq); +} + +/* + * TxCompletion - completion routine for WriteAsynch +*/ +static void TxCompletion(PSDREQUEST pReq) +{ + /* nothing to do + PSDGPS_DEVICE pDevice; + pDevice = (PSDGPS_DEVICE)pReq->pCompleteContext; + */ +} Index: linux-2.6.22/drivers/sdio/function/gps/gps.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/gps/gps.h 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,194 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: gps.h + +@abstract: OS independent include for GPS SDIO function driver + +#notes: + +@notice: Copyright (c), 2004-2005 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __GPS_H___ +#define __GPS_H___ + +#include "gps_linux.h" + +typedef struct _SDGPS_DRIVER_CONTEXT { + SDFUNCTION Function; /* function description for bus driver */ + PSDDEVICE pSDDevice; /* bus driver's device we are supporting (only 1) */ + SDGPS_DEVICE GpsDevice; /* per device info, we only support one device right now */ +}SDGPS_DRIVER_CONTEXT, *PSDGPS_DRIVER_CONTEXT; + +#define SDIO_GPS_TRACE_INT (SDDBG_TRACE+1) + +#define GPS_CMD_52 52 +#define GPS_MAX_BAUD_RATE 115200 +#define GPS_FIFO_SIZE 16 +#define GPS_TYPE "SDIO GPS 16650" +#define GPS_TUPLE 0x91 + +struct SDIO_GPS_SUBTPL_SIOREG { + UINT8 RegisterID; + UINT8 RegisterExpID; + UINT8 RegisterOffset[3]; + UINT8 MaxBaudRateCode; + UINT8 DRL_4800; + UINT8 DRM_4800; +}__attribute__ ((packed)); + +struct SDIO_GPS_SUBTPL_RCVCAPS { + UINT8 Junk; /* defined in some other doc */ +}__attribute__ ((packed)); + +union GPS_TPL_DATA { + struct SDIO_GPS_SUBTPL_SIOREG AsSIOReg; + struct SDIO_GPS_SUBTPL_RCVCAPS AsRcvCaps; +}; + + /* GPS tuple */ +struct SDIO_GPS_TPL { + UINT8 InterfaceCode; + UINT8 StdTupleNumber; +#define STD_GPS_TUPLE_SIOREG 0x00 +#define STD_GPS_TUPLE_RCVCAP 0x01 + union GPS_TPL_DATA Tpd; +}__attribute__ ((packed)); + + /* 166550 UART register offsets */ +#define UART_RECEIVE_REG 0x00 +#define UART_SEND_REG 0x00 +#define UART_BAUD_LOW_REG 0x00 +#define UART_BAUD_HIGH_REG 0x01 +#define UART_INT_ENABLE_REG 0x01 +#define UART_INT_IDENT_REG 0x02 +#define UART_FIFO_CNTRL_REG 0x02 +#define UART_LINE_CNTRL_REG 0x03 +#define UART_MODEM_CONTROL_REG 0x04 +#define UART_LINE_STATUS_REG 0x05 +#define UART_MODEM_STATUS_REG 0x06 +#define UART_SCRATCH_REG 0x07 + + //INTERRUPT_STATUS_REG +#define UART_UARTINT 0x80 +#define UART_RXWKINT 0x02 + + //INT_ENABLE_REG +#define UART_EDSSI 0x08 /* enable modem status interrupt */ +#define UART_ELSI 0x04 /* enable receiver line status interrupt */ +#define UART_ETBEI 0x02 /* enable transmitter holding register empty int. */ +#define UART_ERBFI 0x01 /* enable received data available interrupt */ + + //INT_IDENT_REG +#define UART_FIFO_ENABLED 0xC0 +#define UART_IID_RLS 0x06 +#define UART_IID_RDA 0x04 +#define UART_IID_CTI 0x0C +#define UART_IID_THRE 0x02 +#define UART_IID_MS 0x00 +#define UART_INTPEND 0x01 +#define UART_IID_MASK 0x0F + + //FIFO_CNTRL_REG +#define UART_FIFO_ENABLE 0x01 +#define UART_FIFO_TRIGGER_1 0x00 +#define UART_FIFO_TRIGGER_4 0x40 +#define UART_FIFO_TRIGGER_8 0x80 +#define UART_FIFO_TRIGGER_14 0xC0 +#define UART_FIFO_XMIT_RESET 0x04 +#define UART_FIFO_RCV_RESET 0x02 + + //LINE_CNTRL_REG +#define UART_CLOCK_ENABLE 0x80 +#define UART_SET_BREAK 0x40 +#define UART_EVEN_PARITY_SELECT 0x10 +#define UART_PARITY_ENABLE 0x08 +#define UART_NO_PARITY 0x00 +#define UART_NUM_STOP_BITS 0x04 +#define UART_ONE_STOP 0x00 +#define UART_DATA_8_BITS 0x03 + + + //MODEM_CONTROL_REG +#define UART_RTS_AUTO_FLOW 0x40 +#define UART_CTS_AUTO_FLOW 0x20 +#define UART_LOOP 0x10 +#define UART_TOUT2 0x08 +#define UART_TOUT1 0x04 +#define UART_RTS_ON 0x02 +#define UART_DTR_ON 0x01 + + //LINE_STATUS_REG +#define UART_LSR_EIRF 0x80 /* error receive fifo */ +#define UART_LSR_TEMT 0x40 +#define UART_LSR_THRE 0x20 /* transmitter holding register empty */ +#define UART_LSR_BI 0x10 +#define UART_LSR_FE 0x08 +#define UART_LSR_PE 0x04 +#define UART_LSR_OE 0x02 +#define UART_LSR_DR 0x01 + + //MODEM_STATUS_REG +#define UART_MSR_DCD 0x80 +#define UART_MSR_RI 0x40 +#define UART_MSR_DSR 0x20 +#define UART_MSR_CTS 0x10 +#define UART_MSR_DCTS 0x01 + + //UART_INIT_REG +#define UART_UART_INIT 0xC7 + +#define GPS_FIFO_SIZE 16 + +/* prototypes */ +SDIO_STATUS IssueCMD52(PSDDEVICE pDevice, + UINT8 FuncNo, + UINT32 Address, + PUCHAR pData, + INT ByteCount, + BOOL Write); +#define ReadRegister(pDev,reg,pData) \ + SDLIB_IssueCMD52((pDev)->pSDDevice,SDDEVICE_GET_SDIO_FUNCNO((pDev)->pSDDevice),\ + ((pDev)->UartRegOffset + (reg)),(pData),1,FALSE) +SDIO_STATUS WriteRegister(PSDGPS_DEVICE pDevice, UINT reg, UINT8 Data); +SDIO_STATUS WriteRegisterAsynch(PSDGPS_DEVICE pDevice, UINT reg, UINT8 Data, PSDREQUEST pReq); +SDIO_STATUS GpsInitialize(PSDGPS_DEVICE pDevice); +void GpsDeinitialize(PSDGPS_DEVICE pDevice); +SDIO_STATUS GpsReceive(PSDGPS_DEVICE pDevice); +SDIO_STATUS GpsTransmit(PSDGPS_DEVICE pDevice); +SDIO_STATUS SetBaudRate(PSDGPS_DEVICE pDevice, UINT32 BaudRate); + + +#endif /* __GPS_H___*/ + Index: linux-2.6.22/drivers/sdio/function/gps/gps_linux.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/gps/gps_linux.h 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,63 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: gps_linux.h + +@abstract: OS dedependent include for GPS SDIO function driver + +#notes: + +@notice: Copyright (c), 2004 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __GPS_LINUX_H___ +#define __GPS_LINUX_H___ +#include +#include +#include +#include + +typedef struct _SDGPS_DEVICE { + PSDDEVICE pSDDevice; /* the devices we are supporting */ + BOOL HwReady; /* hardware is setup and ready */ + struct uart_port Port; /* uart port support */ + UINT32 UartRegOffset; /* UART register offset */ + UINT32 UartMaxBaud; /* maximum allowable baud rate */ + UINT16 UartDivisor; /* base divisor at 1200 baud*/ + UINT8 InterruptEnable; /* state of IER register */ + OS_SEMAPHORE DeviceSem; /* semaphore to protect the shadow registers and interrupts */ + PSDREQUEST pRequest; /* allocate a request for asynch use */ +}SDGPS_DEVICE, *PSDGPS_DEVICE; + + +#endif /* __GPS_LINUX_H___*/ Index: linux-2.6.22/drivers/sdio/function/gps/gps_os.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/gps/gps_os.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,633 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: gps_os.c + +@abstract: OS dependent GPS class SDIO function driver + +#notes: + +@notice: Copyright (c), 2004-2005 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* debug level for this module*/ +#define DBG_DECLARE 3; +#include +#include +#include +#include "gps.h" + +#include +#include +#include +#include +#include + +#define DESCRIPTION "SDIO GPS Function Driver" +#define AUTHOR "Atheros Communications, Inc." + + +/* debug print parameter */ +module_param(debuglevel, int, 0644); +MODULE_PARM_DESC(debuglevel, "debuglevel 0-7, controls debug prints"); +int fixedbaud = 1; +module_param(fixedbaud, int, 0644); +MODULE_PARM_DESC(fixedbaud, "fixedbaud, if non-zero then no baud rate changes will be processed"); + + +static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice); +static void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice); +static unsigned int gps_tx_empty(struct uart_port *port); +static void gps_set_mctrl(struct uart_port *port, unsigned int mctrl); +static unsigned int gps_get_mctrl(struct uart_port *port); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) +static void gps_stop_tx(struct uart_port *port); +static void gps_start_tx(struct uart_port *port); +#else +static void gps_stop_tx(struct uart_port *port, unsigned int tty_stop); +static void gps_start_tx(struct uart_port *port, unsigned int tty_start); +#endif + +static void gps_stop_rx(struct uart_port *port); +static void gps_enable_ms(struct uart_port *port); +static void gps_break_ctl(struct uart_port *port, int break_state); +static int gps_startup(struct uart_port *port); +static void gps_shutdown(struct uart_port *port); +static void gps_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old); +static void gps_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); +static const char *gps_type(struct uart_port *port); +static void gps_release_port(struct uart_port *port); +static int gps_request_port(struct uart_port *port); +static void gps_config_port(struct uart_port *port, int flags); +static int gps_verify_port(struct uart_port *port, struct serial_struct *ser); + + +/* devices we support, null terminated */ +#define SDIO_GPS_CLASS 0x04 +static SD_PNP_INFO Ids[] = { + {.SDIO_FunctionClass = SDIO_GPS_CLASS}, /* SDIO-GPS SDIO standard interface code */ + {} +}; + +static struct uart_ops sops = { + .tx_empty = gps_tx_empty, + .set_mctrl = gps_set_mctrl, + .get_mctrl = gps_get_mctrl, + .stop_tx = gps_stop_tx, + .start_tx = gps_start_tx, + .stop_rx = gps_stop_rx, + .enable_ms = gps_enable_ms, + .break_ctl = gps_break_ctl, + .startup = gps_startup, + .shutdown = gps_shutdown, + .set_termios = gps_set_termios, + .pm = gps_pm, + .type = gps_type, + .release_port = gps_release_port, + .request_port = gps_request_port, + .config_port = gps_config_port, + .verify_port = gps_verify_port, +}; + +/* the driver context data */ +static SDGPS_DRIVER_CONTEXT GpsContext = { + .Function.pName = "sdio_gps", + .Function.Version = CT_SDIO_STACK_VERSION_CODE, + .Function.MaxDevices = 1, + .Function.NumDevices = 0, + .Function.pIds = Ids, + .Function.pProbe = Probe, + .Function.pRemove = Remove, + .Function.pSuspend = NULL, + .Function.pResume = NULL, + .Function.pWake = NULL, + .Function.pContext = &GpsContext, + .GpsDevice.Port.type = PORT_16550, + .GpsDevice.Port.uartclk = GPS_MAX_BAUD_RATE*16, + .GpsDevice.Port.fifosize = GPS_FIFO_SIZE, + .GpsDevice.Port.line = 0, + .GpsDevice.Port.ops = &sops, +}; + +static struct uart_driver gps_uart = { + .owner = THIS_MODULE, + .driver_name = "ttyGPS", /* not sure if this is proper naming convention */ + .dev_name = "ttyGPS", +#ifdef CONFIG_DEVFS + .devfs_name = "ttygps", +#endif + .major = 0, /*TTY_MAJOR,*/ + .minor = 0, /*64,*/ + .nr = 1, + + +}; + + + +/* + * Probe - a device potentially for us +*/ +static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice) { + PSDGPS_DRIVER_CONTEXT pFunctionContext = + (PSDGPS_DRIVER_CONTEXT)pFunction->pContext; + SYSTEM_STATUS err = 0; + SDIO_STATUS status; + + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - enter\n")); + + /* make sure this is a device we can handle */ + if (pDevice->pId[0].SDIO_FunctionClass == pFunctionContext->Function.pIds[0].SDIO_FunctionClass) { + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - card matched (0x%X/0x%X/0x%X)\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_ManufacturerCode, + pDevice->pId[0].SDIO_FunctionNo)); + /* connect to the serial port driver */ + pFunctionContext->pSDDevice = pDevice; + pFunctionContext->GpsDevice.pSDDevice = pDevice; + pFunctionContext->GpsDevice.HwReady = FALSE; + + if (!SDIO_SUCCESS((status = GpsInitialize(&pFunctionContext->GpsDevice)))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: Probe - could not initialize, %d\n", status)); + return FALSE; + } + /* the port structure must be reset each time it is re-used */ + ZERO_OBJECT(pFunctionContext->GpsDevice.Port); + pFunctionContext->GpsDevice.Port.type = PORT_16550; + pFunctionContext->GpsDevice.Port.uartclk = GPS_MAX_BAUD_RATE*16; + pFunctionContext->GpsDevice.Port.fifosize = GPS_FIFO_SIZE; + pFunctionContext->GpsDevice.Port.line = 0; + pFunctionContext->GpsDevice.Port.ops = &sops; + pFunctionContext->GpsDevice.Port.dev = SD_GET_OS_DEVICE(pDevice); + pFunctionContext->GpsDevice.Port.uartclk = pFunctionContext->GpsDevice.UartMaxBaud * 16; +//?? snprintf(gps_uart->tty_driver.devfs_name,sizeof(gps_uart->tty_driver.devfs_name), "sdgps_%s", +//?? (pFunctionContext->GpsDevice.Port.dev->bus_id); +//?? /* remove any colons */ +//?? ReplaceChar(gps_uart->devfs_name, ':', '_'); + + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - adding bus_id: %s, driver name: %s\n", + pFunctionContext->GpsDevice.Port.dev->bus_id, pFunctionContext->GpsDevice.Port.dev->driver->name)); + err = uart_add_one_port(&gps_uart, &pFunctionContext->GpsDevice.Port); + if (err < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: Probe - could not add uart port, %d\n", err)); + return FALSE; + } + return TRUE; + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - not our card (0x%X/0x%X/0x%X)\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_ManufacturerCode, + pDevice->pId[0].SDIO_FunctionNo)); + return FALSE; + } +} + +/* + * Remove - our device is being removed +*/ +void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice) { + PSDGPS_DRIVER_CONTEXT pFunctionContext = + (PSDGPS_DRIVER_CONTEXT)pFunction->pContext; + SYSTEM_STATUS err; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO GPS Function: Remove - enter\n")); + + GpsDeinitialize(&pFunctionContext->GpsDevice); + + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Remove - calling uart_remove_one_port\n")); + err = uart_remove_one_port(&gps_uart, &pFunctionContext->GpsDevice.Port); + if (err < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: Remove - could not remove uart port, %d\n", err)); + } + pFunctionContext->pSDDevice = NULL; + DBG_PRINT(SDDBG_TRACE, ("-SDIO GPS Function: Remove - exit\n")); +} + +/* + * gps_tx_empty - transmitter fifo and shifter is empty +*/ +static unsigned int gps_tx_empty(struct uart_port *port) +{ + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + UINT8 data; + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_tx_empty - enter\n")); + ReadRegister(pDevice, UART_LINE_STATUS_REG, &data); + return (data & UART_LSR_TEMT)? TIOCSER_TEMT : 0; +} + +/* + * gps_set_mctrl - set modem control line +*/ +static void gps_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_set_mctrl - enter\n")); + return; /* fixed values */ +} + +/* + * gps_get_mctrl - get modem control line +*/ +static unsigned int gps_get_mctrl(struct uart_port *port) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_get_mctrl - enter\n")); + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; /* fixed */ +} + +/* + * gps_stop_tx - stop output +*/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) +static void gps_stop_tx(struct uart_port *port) +#else +static void gps_stop_tx(struct uart_port *port, unsigned int tty_stop) +#endif +{ + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_stop_tx - enter\n")); + if (pDevice->InterruptEnable & UART_ETBEI) { + /* called with local ints disabled if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { + return; + } */ + pDevice->InterruptEnable &= ~UART_ETBEI; + WriteRegisterAsynch(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable, pDevice->pRequest); + /*SemaphorePost(&pDevice->DeviceSem);*/ + } +} + +/* + * gps_start_tx - start output +*/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) +static void gps_start_tx(struct uart_port *port) +#else +static void gps_start_tx(struct uart_port *port, unsigned int tty_start) +#endif +{ + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + DBG_PRINT(SDDBG_TRACE, ("+SDIO GPS Function: gps_start_tx - enter\n")); + if (!(pDevice->InterruptEnable & UART_ETBEI)) { + /* called with local ints disabledif (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { + return; + } */ + pDevice->InterruptEnable |= UART_ETBEI; + WriteRegisterAsynch(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable, pDevice->pRequest); + /*SemaphorePost(&pDevice->DeviceSem);*/ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO GPS Function: gps_start_tx\n")); +} + +/* + * gps_stop_rx - stop output +*/ +static void gps_stop_rx(struct uart_port *port) +{ + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_stop_rx - enter\n")); + /* called with local ints disabled if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { + return; + } */ + pDevice->InterruptEnable &= ~UART_ELSI; + WriteRegisterAsynch(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable, pDevice->pRequest); + /*SemaphorePost(&pDevice->DeviceSem);*/ +} + +/* + * gps_enable_ms - enable modem status interrupts +*/ +static void gps_enable_ms(struct uart_port *port) +{ + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_enable_ms - enter\n")); + /* called with local ints disabledif (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { + return; + } */ + pDevice->InterruptEnable |= UART_EDSSI; + WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); + /*SemaphorePost(&pDevice->DeviceSem);*/ +} + +/* + * gps_break_ctl - enable/disable breaks +*/ +static void gps_break_ctl(struct uart_port *port, int break_state) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_break_ctl - enter\n")); +} + +/* + * gps_startup - initialization +*/ +static int gps_startup(struct uart_port *port) +{ + UINT8 temp; + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_startup - enter\n")); +//?? if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { +//?? return -EINTR; +//?? } + /* read registers to clear status */ + ReadRegister(pDevice, UART_LINE_STATUS_REG, &temp); + ReadRegister(pDevice, UART_RECEIVE_REG, &temp); + ReadRegister(pDevice, UART_INT_IDENT_REG, &temp); + ReadRegister(pDevice, UART_MODEM_STATUS_REG, &temp); + WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE | UART_FIFO_RCV_RESET); + WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE); + WriteRegister(pDevice, UART_LINE_CNTRL_REG, (UART_ONE_STOP | UART_NO_PARITY | UART_DATA_8_BITS)); + + pDevice->InterruptEnable = UART_ERBFI | UART_ELSI; + WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); +//?? SemaphorePost(&pDevice->DeviceSem); + return 0; +} + +/* + * gps_shutdown - +*/ +static void gps_shutdown(struct uart_port *port) +{ + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + DBG_PRINT(SDDBG_TRACE, ("+SDIO GPS Function: gps_shutdown - enter\n")); + //??if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { + //?? return; + //??} + pDevice->InterruptEnable = 0; + WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); + //??SemaphorePost(&pDevice->DeviceSem); + DBG_PRINT(SDDBG_TRACE, ("-SDIO GPS Function: gps_shutdown\n")); +} + +/* + * gps_set_termios - set data parameters +*/ +static void gps_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); + unsigned char cval = 0; + unsigned int baudrate; + + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_set_termios - enter\n")); + + port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;//??? + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + cval |= (termios->c_cflag & PARENB)? UART_PARITY_ENABLE : 0; + cval |= (termios->c_cflag & CSTOPB)? UART_NUM_STOP_BITS : 0; + cval |= (!(termios->c_cflag & PARODD))? UART_EVEN_PARITY_SELECT : 0; + //?? WriteRegister(pDevice, UART_LINE_CNTRL_REG, cval); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: LCR 0x%X\n", cval)); + + /* get serial port to figure out the baud rate */ + baudrate = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + DBG_ASSERT(baudrate != 0); + if (fixedbaud == 0) { + SetBaudRate(pDevice, baudrate); + uart_update_timeout(port, termios->c_cflag, baudrate); + } + + + //??do something about this locking + /* maybe called with local ints disabledif (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { + return; + } */ +//?? pDevice->InterruptEnable &= ~UART_EDSSI; +//?? if (UART_ENABLE_MS(&port, termios->c_cflag)) { +//?? pDevice->InterruptEnable |= UART_EDSSI; +//?? } + gps_set_mctrl(&pDevice->Port, pDevice->Port.mctrl); + + /* SemaphorePost(&pDevice->DeviceSem);*/ +} + +/* + * gps_pm - power management +*/ +static void gps_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_pm - state: %d\n", state)); + return; +} + +/* + * gps_type - retrieve type string +*/ +static const char *gps_type(struct uart_port *port) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_type - enter\n")); + return GPS_TYPE; +} + +/* + * gps_release_port - release resources +*/ +static void gps_release_port(struct uart_port *port) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_release_port - enter\n")); + return; +} + +/* + * gps_request_port - request resources +*/ +static int gps_request_port(struct uart_port *port) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_request_port - enter\n")); + return 0; +} + +/* + * gps_config_port - auto configuration +*/ +static void gps_config_port(struct uart_port *port, int flags) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_config_port - enter\n")); + return; +} + +/* + * gps_verify_port - verify configuration of port +*/ +static int gps_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_verify_port - enter\n")); + return 0; +} + +/* + * GpsReceive +*/ +SDIO_STATUS GpsReceive(PSDGPS_DEVICE pDevice) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT8 statusReg; + UINT8 inChar; + UINT maxCount = 64; + struct uart_port *port = &pDevice->Port; + struct tty_struct *tty; + + tty = ((port == NULL) || (port->info == NULL)) ? NULL : port->info->tty; + /* if we aren't ready, just clear out the incoming char */ + if ((port == NULL) || (tty == NULL)) { + ReadRegister(pDevice, UART_RECEIVE_REG, &inChar); + WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE | UART_FIFO_RCV_RESET); + WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE); + DBG_PRINT(SDIO_GPS_TRACE_INT, ("+RX%X\n", (UINT)inChar)); + return status; + } + + for (status = ReadRegister(pDevice, UART_LINE_STATUS_REG, &statusReg); + SDIO_SUCCESS(status) && (statusReg & UART_LSR_DR) && + (maxCount-- > 0); + status = ReadRegister(pDevice, UART_LINE_STATUS_REG, &statusReg)) + { + + ReadRegister(pDevice, UART_RECEIVE_REG, &inChar); + + if (status & UART_LSR_OE) { + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + DBG_PRINT(SDDBG_WARN, ("SDIO GPS Function: GpsReceive overrun\n")); + break; + } + + tty_insert_flip_char(tty, inChar, 0); + udelay(1); + } + tty_flip_buffer_push(tty); + + return status; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) +#define GPS_STOP_TX(p) gps_stop_tx((p)) +#else +#define GPS_STOP_TX(p) gps_stop_tx((p),0) +#endif + +/* + * GpsTransmit +*/ +SDIO_STATUS GpsTransmit(PSDGPS_DEVICE pDevice) +{ + UINT ii; + struct uart_port *port = &pDevice->Port; + struct circ_buf *xmit; + + xmit = ((port == NULL) || (port->info == NULL)) ? NULL : &port->info->xmit; + /* if we aren't ready, just clear interrupt */ + if (xmit == NULL) { + GPS_STOP_TX(port); + return SDIO_STATUS_SUCCESS; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + GPS_STOP_TX(port); + return SDIO_STATUS_SUCCESS; + } + + for(ii = 0; ii < GPS_FIFO_SIZE; ii++) { + WriteRegister(pDevice, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) { + break; + } + + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { + uart_write_wakeup(port); + } + if (uart_circ_empty(xmit)) { + GPS_STOP_TX(port); + } + + return SDIO_STATUS_SUCCESS; +} + +/* + * module init +*/ +static int __init sdio_gps_init(void) { + SDIO_STATUS status; + SYSTEM_STATUS err; + + REL_PRINT(SDDBG_TRACE, ("SDIO GPS Function: init\n")); + /* register with the serial driver core */ + err = uart_register_driver(&gps_uart); + if (err < 0) { + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: failed to register with uart driver, %d\n", err)); + return err; + } + /* register with bus driver core */ + if (!SDIO_SUCCESS((status = SDIO_RegisterFunction(&GpsContext.Function)))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: failed to register with bus driver, %d\n", status)); + uart_unregister_driver(&gps_uart); + return SDIOErrorToOSError(status); + } + return err; +} + +/* + * module cleanup +*/ +static void __exit sdio_gps_cleanup(void) { + REL_PRINT(SDDBG_TRACE, ("SDIO GPS Function: : cleanup\n")); + SDIO_UnregisterFunction(&GpsContext.Function); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: SDIO unregistered\n")); + /* unregister with the serial driver core */ + uart_unregister_driver(&gps_uart); + DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: UART unregistered\n")); +} + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); +module_init(sdio_gps_init); +module_exit(sdio_gps_cleanup); Index: linux-2.6.22/drivers/sdio/function/bluetooth/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/bluetooth/Makefile 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,5 @@ +# +# SDIO bluetooth functiondriver +# +obj-m += sdio_bluetooth_fd.o +sdio_bluetooth_fd-objs := sdio_bt.o sdio_bt_os.o Index: linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,769 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_bt.c + +@abstract: Bluetooth SDIO driver + +#notes: includes OS independent portions + +@notice: Copyright (c), 2005-2006 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#include +#include +#include +#include +#include "sdio_bt.h" + +static void BTIRQHandler(PVOID pContext); +#define BLOCK_WRITE TRUE +#define BLOCK_READ FALSE +static SDIO_STATUS ReceiveHciPacket(PBT_HCI_INSTANCE pHci); +static void BtTxCompletion(PSDREQUEST pReq); + +UINT32 SetRequestParam(PBT_HCI_INSTANCE pHci, + UINT32 BytesToSend, + PSDREQUEST pReq, + BOOL Write); + +/* delete an instance */ +void DeleteHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PBT_HCI_INSTANCE pHci) +{ + SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; + SDIO_STATUS status; + + if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pFuncContext->InstanceSem))) { + return; + } + /* pull it out of the list */ + SDListRemove(&pHci->SDList); + SemaphorePost(&pFuncContext->InstanceSem); + + if (!(SDDEVICE_IS_CARD_REMOVED(pHci->pDevice))) { + if (pHci->Flags & FLAGS_CARD_IRQ_UNMSK) { + SDDEVICE_SET_IRQ_HANDLER(pHci->pDevice, NULL, NULL); + /* mask our IRQ */ + status = SDLIB_IssueConfig(pHci->pDevice,SDCONFIG_FUNC_MASK_IRQ,NULL,0); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to unmask IRQ err:%d \n", status)); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: IRQ disabled \n")); + } + } + /* disable the card */ + if (pHci->Flags & FLAGS_CARD_ENAB) { + ZERO_OBJECT(fData); + fData.EnableFlags = SDCONFIG_DISABLE_FUNC; + fData.TimeOut = SDBT_ENABLE_DISABLE_TIMEOUT; + status = SDLIB_IssueConfig(pHci->pDevice, + SDCONFIG_FUNC_ENABLE_DISABLE, + &fData, + sizeof(fData)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to disable card: %d\n", status)); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Card Disabled \n")); + } + } + + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Card removed \n")); + } + + SDLIB_IssueConfig(pHci->pDevice, + SDCONFIG_FUNC_FREE_SLOT_CURRENT, + NULL, + 0); + + KernelFree(pHci); +} + +/* create a Hci instance */ +PBT_HCI_INSTANCE CreateHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PSDDEVICE pDevice) +{ + PBT_HCI_INSTANCE pHci = NULL; + UINT32 nextTpl; + UINT8 tplLength; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + struct PKT_RETRY_CONTROL_TUPLE rtcTuple; + SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; + SDCONFIG_FUNC_SLOT_CURRENT_DATA slotCurrent; + + ZERO_OBJECT(slotCurrent); + ZERO_OBJECT(fData); + + do { + pHci = (PBT_HCI_INSTANCE)KernelAlloc(sizeof(BT_HCI_INSTANCE)); + if (NULL == pHci) { + break; + } + ZERO_POBJECT(pHci); + SDLIST_INIT(&pHci->SDList); + pHci->pDevice = pDevice; + + pHci->FuncNo = SDDEVICE_GET_SDIO_FUNCNO(pHci->pDevice); + + DBG_PRINT(SDDBG_TRACE, ("SDIO BT HCI Function Instance: 0x%Xn Fn:%d \n",(INT)pHci, + pHci->FuncNo)); + + if (SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(pHci->pDevice) == 0) { + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Function does not support Block Mode! \n")); + status = SDIO_STATUS_ERROR; + break; + } + /* limit block size to operational block limit or card function capability*/ + pHci->MaxBlockSize = min(SDDEVICE_GET_OPER_BLOCK_LEN(pHci->pDevice), + SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(pHci->pDevice)); + + /* check if the card support multi-block transfers */ + if (!(SDDEVICE_GET_SDIOCARD_CAPS(pHci->pDevice) & SDIO_CAPS_MULTI_BLOCK)) { + pHci->Flags |= FLAGS_BYTE_BASIS; + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Byte basis only \n")); + /* limit block size to max byte basis */ + pHci->MaxBlockSize = min(pHci->MaxBlockSize, + (UINT16)SDIO_MAX_LENGTH_BYTE_BASIS); + pHci->MaxBlocks = 1; + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Multi-block capable \n")); + pHci->MaxBlocks = SDDEVICE_GET_OPER_BLOCKS(pHci->pDevice); + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Bytes Per Block: %d bytes, Block Count:%d \n", + pHci->MaxBlockSize, + pHci->MaxBlocks)); + + status = SDLIB_GetDefaultOpCurrent(pDevice, &slotCurrent.SlotCurrent); + + if (!SDIO_SUCCESS(status)) { + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Allocating Slot current: %d mA\n", slotCurrent.SlotCurrent)); + status = SDLIB_IssueConfig(pDevice, + SDCONFIG_FUNC_ALLOC_SLOT_CURRENT, + &slotCurrent, + sizeof(slotCurrent)); + + if (!SDIO_SUCCESS((status))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to allocate slot current %d\n", + status)); + if (status == SDIO_STATUS_NO_RESOURCES) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Remaining Slot Current: %d mA\n", + slotCurrent.SlotCurrent)); + } + break; + } + + nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pHci->pDevice); + tplLength = sizeof(rtcTuple); + status = SDLIB_FindTuple(pHci->pDevice, + CISTPL_VENDOR, + &nextTpl, + (PUINT8)&rtcTuple, + &tplLength); + if (SDIO_SUCCESS(status)) { + pHci->Flags |= FLAGS_RTC_SUPPORTED; + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Retry protocol supported \n")); + if (!(rtcTuple.SDIO_RetryControl & RTC_READ_ACK_NOT_REQUIRED)) { + pHci->Flags |= FLAGS_READ_ACK; + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Read Acknowledgements required \n")); + } + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO BT Function: No RTC Tuple, retry not supported \n")); + status = SDIO_STATUS_SUCCESS; + } + + /* now enable the card */ + fData.EnableFlags = SDCONFIG_ENABLE_FUNC; + fData.TimeOut = SDBT_ENABLE_DISABLE_TIMEOUT; + status = SDLIB_IssueConfig(pHci->pDevice, + SDCONFIG_FUNC_ENABLE_DISABLE, + &fData, + sizeof(fData)); + if (!SDIO_SUCCESS((status))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to enable function Err:%d\n", status)); + break; + } + pHci->Flags |= FLAGS_CARD_ENAB; + + if (!(pHci->Flags & FLAGS_BYTE_BASIS)) { + /* for cards that support multi-block transfers, set the block size */ + status = SDLIB_SetFunctionBlockSize(pHci->pDevice, + pHci->MaxBlockSize); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to set block size. Err:%d\n", status)); + break; + } + } + /* if it was Type-B card, put it in Type-A mode */ + if (SDDEVICE_GET_SDIO_FUNC_CLASS(pHci->pDevice) == 0x03) { + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Type B card detected, switch to type A mode \n")); + status = WriteRegister(pHci,MODE_STATUS_REG,MODE_STATUS_TYPE_A); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to switch to type A mode \n")); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: TYPE A Mode switched \n")); + } + + if (pHci->Flags & FLAGS_READ_ACK) { + status = WriteRegister(pHci,RETRY_CONTROL_STATUS_REG,RETRY_CONTROL_STATUS_USE_ACKS); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to set retry control to use acks \n")); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Read acks enabled \n")); + status = WriteRegister(pHci,RECV_PACKET_CONTROL_REG,RECV_PACKET_CONTROL_ACK); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to set issue read ack \n")); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: Read ack issued \n")); + } + + SDDEVICE_SET_IRQ_HANDLER(pHci->pDevice,BTIRQHandler,pHci); + /* unmask our function IRQ */ + status = SDLIB_IssueConfig(pHci->pDevice,SDCONFIG_FUNC_UNMASK_IRQ,NULL,0); + if (!SDIO_SUCCESS((status))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to unmask IRQ Err:%d\n", status)); + break; + } + pHci->Flags |= FLAGS_CARD_IRQ_UNMSK; + + /* enable packet ready interrupt */ + status = WriteRegister(pHci,INTERRUPT_ENABLE_REG,RCV_PKT_PENDING_ENABLE); + if (!SDIO_SUCCESS(status)) { + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO BT Function: IRQ enabled - ready \n")); +#if 0 // test command + { + UCHAR data[7] = {0x07,0x00,0x00,0x01,0x03,0x10,0x00}; + PSDREQUEST pReq = NULL; + + /* allocate request to send to host controller */ + pReq = SDDeviceAllocRequest(pHci->pDevice); + if (NULL == pReq) { + break; + } + + SetRequestParam(pHci, + 7, + pReq, + TRUE); + + pReq->Flags &= ~SDREQ_FLAGS_TRANS_ASYNC; + pReq->pCompletion =NULL; + pReq->pCompleteContext = NULL; + pReq->pDataBuffer = data; + + SDLIB_PrintBuffer(data,7, + "Sending first BT command..."); + status = SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_WARN, ("SDIO BT - TEST: Synch CMD53 write failed %d \n", + status)); + } + + SDDeviceFreeRequest(pHci->pDevice,pReq); + } +#endif + + } while (FALSE); + + if (!SDIO_SUCCESS(status) && (pHci != NULL)) { + DeleteHciInstance(pFuncContext, pHci); + pHci = NULL; + } + + return pHci; +} + +/* find an instance associated with the SD device */ +PBT_HCI_INSTANCE FindHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PSDDEVICE pDevice) +{ + SDIO_STATUS status; + PSDLIST pItem; + PBT_HCI_INSTANCE pHci = NULL; + + status = SemaphorePendInterruptable(&pFuncContext->InstanceSem); + if (!SDIO_SUCCESS(status)) { + return NULL; + } + /* walk the list and find our instance */ + SDITERATE_OVER_LIST(&pFuncContext->InstanceList, pItem) { + pHci = CONTAINING_STRUCT(pItem, BT_HCI_INSTANCE, SDList); + if (pHci->pDevice == pDevice) { + /* found it */ + break; + } + pHci = NULL; + } + + SemaphorePost(&pFuncContext->InstanceSem); + return pHci; +} + +/* add an instance to our list */ +SDIO_STATUS AddHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PBT_HCI_INSTANCE pHci) +{ + SDIO_STATUS status; + + status = SemaphorePendInterruptable(&pFuncContext->InstanceSem); + if (!SDIO_SUCCESS(status)) { + return status; + } + + SDListAdd(&pFuncContext->InstanceList,&pHci->SDList); + SemaphorePost(&pFuncContext->InstanceSem); + + return SDIO_STATUS_SUCCESS; +} + +static void BTIRQHandler(PVOID pContext) +{ + PBT_HCI_INSTANCE pHci; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT8 temp; + + DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT IRQ \n")); + + pHci = (PBT_HCI_INSTANCE)pContext; + + while (1) { + + status = ReadRegister(pHci,INTERRUPT_STATUS_CLEAR_REG,&temp); + if (!SDIO_SUCCESS(status)) { + /* can't read it for some reason */ + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to read int status, err:%d\n", status)); + break; + } + + if (!(temp & RCV_PKT_PENDING)) { + break; + } + /* clear the status */ + status = WriteRegister(pHci,INTERRUPT_STATUS_CLEAR_REG,RCV_PKT_PENDING); + + if (!SDIO_SUCCESS(status)) { + /* can't clear it for some reason */ + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Failed to clear int status, err:%d\n", status)); + break; + } + + status = ReceiveHciPacket(pHci); + if (!SDIO_SUCCESS(status)) { + break; + } + } + + if (!SDIO_SUCCESS(status)) { + /* mask the interrupt if we can't handle them */ + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: FATAL Error detected in IRQ processing , err:%d\n", status)); + WriteRegister(pHci,INTERRUPT_ENABLE_REG,0); + } + /* ack the interrupt */ + SDLIB_IssueConfig(pHci->pDevice,SDCONFIG_FUNC_ACK_IRQ,NULL,0); +} + +/* set the request parameters in a request + * This does not set the pDataBuffer field */ +UINT32 SetRequestParam(PBT_HCI_INSTANCE pHci, + UINT32 BytesToSend, + PSDREQUEST pReq, + BOOL Write) +{ + UINT32 bytesToTransfer; + + pReq->Command = CMD53; + pReq->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS; + if (Write) { + /* do write in non-blocking fashion */ + /* note, that read-operations are performed in our normal IRQ handler which + * allows synchronous operations */ + pReq->Flags |= (SDREQ_FLAGS_DATA_WRITE | SDREQ_FLAGS_TRANS_ASYNC); + pReq->pCompletion = BtTxCompletion; + pReq->pCompleteContext = (PVOID)pHci; + } + + if ((pHci->Flags & FLAGS_BYTE_BASIS) || (BytesToSend < pHci->MaxBlockSize)) { + /* byte basis */ + bytesToTransfer = min((UINT32)pHci->MaxBlockSize, BytesToSend); + SDIO_SET_CMD53_ARG(pReq->Argument, + (Write ? CMD53_WRITE : CMD53_READ), + pHci->FuncNo, + CMD53_BYTE_BASIS, + CMD53_FIXED_ADDRESS, + (Write ? XMIT_DATA_REG : RECV_DATA_REG), + CMD53_CONVERT_BYTE_BASIS_BLK_LENGTH_PARAM(bytesToTransfer)); + pReq->BlockCount = 1; + pReq->BlockLen = bytesToTransfer; + if (pReq->BlockLen < 8) { + pReq->Flags |= SDREQ_FLAGS_DATA_SHORT_TRANSFER; + } + } else { + /* block mode */ + pReq->BlockLen = pHci->MaxBlockSize; + /* get block counts (whole blocks, no partials allowed) */ + pReq->BlockCount = min(BytesToSend / (UINT32)pHci->MaxBlockSize, (UINT32)pHci->MaxBlocks); + DBG_ASSERT(pReq->BlockCount != 0); + /* calculate total transfer to return */ + bytesToTransfer = pReq->BlockCount * pReq->BlockLen; + /* set argument */ + SDIO_SET_CMD53_ARG(pReq->Argument, + (Write ? CMD53_WRITE : CMD53_READ), + pHci->FuncNo, + CMD53_BLOCK_BASIS, + CMD53_FIXED_ADDRESS, + (Write ? XMIT_DATA_REG : RECV_DATA_REG), + CMD53_CONVERT_BLOCK_BASIS_BLK_COUNT_PARAM(pReq->BlockCount)); + } + + return bytesToTransfer; +} + +static INLINE UINT32 AdjustBytesForHC(PBT_HCI_INSTANCE pHci, UINT32 RemainingBytes) { + + if (pHci->BlockTransferFix) { + /* some host controllers (like the PXA25x) do not support 1 and 3 byte transfers + * we are trying to avert a 1 or 3 byte block transfer + * unfortunately not all SDIO BT cards allow CMD52 operations on the data port + * so we have to "look ahead" and prevent a small block transfer by dividing the + * current chunk */ + if (((RemainingBytes % pHci->MaxBlockSize) == 1) || + ((RemainingBytes % pHci->MaxBlockSize) == 3)) { + /* divide by two, this should split the remaining bytes into something + * manageable */ + DBG_PRINT(SDDBG_WARN, ("SDIO BT - adjusting %d bytes (max blocks: %d) \n", + RemainingBytes,pHci->MaxBlockSize)); + return (RemainingBytes >> 1); + } + return RemainingBytes; + } + /* no adjustment required */ + return RemainingBytes; +} + +static INLINE void SetUpNextTxBlocTransfer(PBT_HCI_INSTANCE pHci, + PSDREQUEST pReq) { + pHci->pTxBufferPosition += pHci->TxBytesToTransfer; + pHci->TxRemaining -= pHci->TxBytesToTransfer; + if (pHci->TxRemaining) { + /* set where we are */ + pReq->pDataBuffer = pHci->pTxBufferPosition; + /* set up parameters for the request */ + pHci->TxBytesToTransfer = SetRequestParam(pHci, + AdjustBytesForHC(pHci,pHci->TxRemaining), + pReq, + BLOCK_WRITE); + } +} + +static INLINE void ResetCurrentTxPacketTransfer(PBT_HCI_INSTANCE pHci) { + pHci->pTxBufferPosition = SDBTHCI_GET_PKT_BUFFER(pHci->pCurrentTxPacket); + pHci->TxRemaining = SDBTHCI_GET_PKT_LENGTH(pHci->pCurrentTxPacket); + pHci->TxBytesToTransfer = 0; +} + +static void BtTxCompletion(PSDREQUEST pReq) +{ + PBT_HCI_INSTANCE pHci; + SDIO_STATUS status; + BOOL done = FALSE; + + pHci = (PBT_HCI_INSTANCE)pReq->pCompleteContext; + DBG_ASSERT(pHci != NULL); + status = pReq->Status; + + switch (pHci->TxState) { + case TX_BLOCK_PROCESSING: + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: XMIT Failed, Err:%d \n", status)); + /* check for retry */ + if (pHci->Flags & FLAGS_RTC_SUPPORTED) { + pHci->TxRetry--; + if (pHci->TxRetry) { + pHci->TxState = TX_PACKET_RETRY; + /* setup request for async CMD52 */ + SDLIB_SetupCMD52RequestAsync((pHci)->FuncNo, + XMIT_PACKET_CONTROL_REG, + CMD52_DO_WRITE, + XMIT_PKT_CONTROL_RETRY, + pReq); + /* set completion routine */ + pReq->pCompletion = BtTxCompletion; + pReq->pCompleteContext = (PVOID)pHci; + /* submit */ + SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: XMIT retries exceeded \n")); + done = TRUE; + break; + } + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Card does not support retries! \n")); + done = TRUE; + } + } else { + /* set it up for the next transfer */ + SetUpNextTxBlocTransfer(pHci,pReq); + /* are we done? */ + if (pHci->TxRemaining) { + /* submit request asynchronously */ + SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); + } else { + DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO BT Function: TX Packet 0x%X sent \n", + (INT)pHci->pCurrentTxPacket)); + done = TRUE; + } + } + break; + case TX_PACKET_RETRY: + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO BT Function: Failed to set TX packet retry control, Err:%d \n", status)); + done = TRUE; + break; + } + if (SD_R5_GET_RESP_FLAGS(pReq->Response) & SD_R5_ERRORS) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO BT Function: CMD52 failed in TX retry control \n")); + status = SDIO_STATUS_DEVICE_ERROR; + done = TRUE; + break; + } + ResetCurrentTxPacketTransfer(pHci); + pHci->TxState = TX_BLOCK_PROCESSING; + /* setup block transfer */ + SetUpNextTxBlocTransfer(pHci,pReq); + /* submit request asynchronously */ + SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); + break; + default: + DBG_ASSERT(FALSE); + break; + } + + if (done) { + OSIndicateHCIPacketTransmitDone(pHci,status); + OSFreeSDRequest(pHci, pReq); + } +} + + + +/* send the hci packet asynchronously the caller handles the header and packet queues and sets the + * pCurrentPacket field for the packet ready to go out */ +SDIO_STATUS SendHciPacket(PBT_HCI_INSTANCE pHci) +{ + SDIO_STATUS status = SDIO_STATUS_PENDING; + PSDREQUEST pReq; + + do { + DBG_ASSERT(pHci->pCurrentTxPacket != NULL); + pReq = OSAllocSDRequest(pHci); + + if (NULL == pReq) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: No SD requests remaining \n")); + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + pHci->TxRetry = SDBT_PKT_RETRIES; + ResetCurrentTxPacketTransfer(pHci); + pHci->TxState = TX_BLOCK_PROCESSING; + /* setup block transfer */ + SetUpNextTxBlocTransfer(pHci,pReq); + + DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO BT Function: BlockLen:%d,BlockCount:%d, Arg:0x%X \n", + pReq->BlockLen, pReq->BlockCount,pReq->Argument)); + /* submit request asynchronously */ + SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); + + } while (FALSE); + + if (!SDIO_SUCCESS(status)) { + OSIndicateHCIPacketTransmitDone(pHci,status); + } + + return status; +} + +static SDIO_STATUS ReceiveHciPacket(PBT_HCI_INSTANCE pHci) +{ + SDIO_STATUS status; + PSDBT_HCI_PACKET pPacket = NULL; + UINT8 header[SDIO_BT_TRANSPORT_HEADER_LENGTH]; + PSDREQUEST pReq; + UINT32 bytesToTransfer; + UINT32 remaining; + PUINT8 pBufferLoc; + + pReq = OSAllocSDRequest(pHci); + + if (NULL == pReq) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: No SD requests remaining \n")); + return SDIO_STATUS_NO_RESOURCES; + } + + do{ + pReq->pDataBuffer = header; + SetRequestParam(pHci,AdjustBytesForHC(pHci,sizeof(header)),pReq,BLOCK_READ); + /* go get the header */ + status = SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to get header, Err:%d\n", status)); + break; + } + + remaining = SDIO_BT_GET_LENGTH(header); + if (remaining <= SDIO_BT_TRANSPORT_HEADER_LENGTH) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Invalid Length:%d \n", remaining)); + break; + } + + if (DBG_GET_DEBUG_LEVEL() >= SDBT_DBG_RECEIVE) { + SDLIB_PrintBuffer(header,sizeof(header),"SDIO BT, header dump \n"); + } + switch (SDIO_BT_GET_SERVICEID(header)) { + case SDIO_BT_TYPE_A_HCI_EVT: + case SDIO_BT_TYPE_A_HCI_ACL: + case SDIO_BT_TYPE_A_HCI_SCO: + break; + default: + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Invalid packet type:%d \n", + SDIO_BT_GET_SERVICEID(header))); + status = SDIO_STATUS_ERROR; + break; + } + + if (!SDIO_SUCCESS(status)) { + break; + } + /* subtract off header */ + remaining -= SDIO_BT_TRANSPORT_HEADER_LENGTH; + /* get a buffer for this HCI packet */ + pPacket = OSAllocHCIRcvPacket(pHci, + remaining, + SDIO_BT_GET_SERVICEID(header)); + if (NULL == pPacket) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: failed to allocate packet \n")); + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT Function: Getting HCI Packet (type:%d, Length:%d) \n", + SDIO_BT_GET_SERVICEID(header),remaining)); + + pBufferLoc = SDBTHCI_GET_PKT_BUFFER(pPacket); + + while (remaining) { + /* set where we are */ + pReq->pDataBuffer = pBufferLoc; + /* set up parameters for the request */ + bytesToTransfer = SetRequestParam(pHci,AdjustBytesForHC(pHci,remaining),pReq,BLOCK_READ); + /* submit request synchronously */ + status = SDDEVICE_CALL_REQUEST_FUNC(pHci->pDevice,pReq); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Rcv Failed, Err:%d \n", status)); + break; + } + + pBufferLoc += bytesToTransfer; + remaining -= bytesToTransfer; + } + + if (!SDIO_SUCCESS(status)) { + /* free this packet */ + OSFreeHciRcvPacket(pHci, pPacket); + if (pHci->Flags & FLAGS_RTC_SUPPORTED) { + pHci->RxRetry++; + if (pHci->RxRetry > MAX_RX_RETRY) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO BT Function: RX Retry Exceeded \n")); + break; + } + /* set bit to discard this packet, we need to start over */ + status = WriteRegister(pHci,RECV_PACKET_CONTROL_REG,RCV_PKT_CONTROL_RETRY); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO BT Function: failed to set RCV PKT RETRY, err:%d \n", status)); + } + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO BT Function: Card does not support retries! \n")); + } + break; + } else { + /* reset retry on good packets */ + pHci->RxRetry = 0; + DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT Function: Packet received --\n")); + if (DBG_GET_DEBUG_LEVEL() >= SDBT_DBG_RECEIVE) { + SDLIB_PrintBuffer(SDBTHCI_GET_PKT_BUFFER(pPacket), + (SDIO_BT_GET_LENGTH(header) - SDIO_BT_TRANSPORT_HEADER_LENGTH), + "Received HCI Packet Dump"); + } + /* indicate the packet */ + OSIndicateHCIPacketReceived(pHci, + pPacket, + (SDIO_BT_GET_LENGTH(header) - SDIO_BT_TRANSPORT_HEADER_LENGTH), + SDIO_BT_GET_SERVICEID(header)); + /* we no longer own this packet */ + pPacket = NULL; + if (pHci->Flags & FLAGS_READ_ACK) { + /* ack the hardware indicating we pulled the packet out */ + status = WriteRegister(pHci,RECV_PACKET_CONTROL_REG,RECV_PACKET_CONTROL_ACK); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO BT Function: failed to set ACK Packet, err:%d \n", status)); + break; + } else { + DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO BT Function: RCV Packet Ack'd \n")); + } + } + } + }while(FALSE); + + + OSFreeSDRequest(pHci, pReq); + + return status; +} + Index: linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt.h 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,176 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_bt.h + +@abstract: OS independent include Bluetooth function driver + +#notes: + +@notice: Copyright (c), 2005-2006 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDIO_BT_H___ +#define __SDIO_BT_H___ + +#include "sdio_bt_linux.h" + +#define SDBT_DBG_TRANSMIT (SDDBG_TRACE + 1) +#define SDBT_DBG_RECEIVE (SDDBG_TRACE + 2) + +#define FLAGS_RTC_SUPPORTED 0x01 /* re-try control protocol supported */ +#define FLAGS_BYTE_BASIS 0x02 /* byte basis transfer of packets */ +#define FLAGS_READ_ACK 0x04 /* requires read acknowledgements */ +#define FLAGS_CARD_ENAB 0x08 +#define FLAGS_CARD_IRQ_UNMSK 0x10 + +#define SDBT_ENABLE_DISABLE_TIMEOUT 2000 +#define SDBT_PKT_RETRIES 3 + +typedef enum _BTHCI_TX_STATES { + TX_BLOCK_PROCESSING = 0, + TX_PACKET_RETRY = 1 +}BTHCI_TX_STATES, *PBTHCI_TX_STATES; + +typedef struct _BT_HCI_INSTANCE { + SDLIST SDList; /* hcd list entry */ + PSDDEVICE pDevice; /* bus driver's device we are supporting */ + UINT8 FuncNo; /* function number we are on */ + SDBT_CONFIG Config; /* devices local data */ + UINT8 Flags; /* operational flags */ + UINT16 MaxBlockSize; /* max block size for transfers */ + UINT16 MaxBlocks; /* max blocks for transfers (multi-block) */ + PSDBT_HCI_PACKET pCurrentTxPacket; + PUINT8 pTxBufferPosition; + UINT32 TxRemaining; + UINT32 RxRemaining; + UINT32 TxBytesToTransfer; + UINT8 TxRetry; + BTHCI_TX_STATES TxState; + UINT8 RxRetry; + BOOL BlockTransferFix; +}BT_HCI_INSTANCE, *PBT_HCI_INSTANCE; + +typedef struct _BT_FUNCTION_CONTEXT { + SDFUNCTION Function; /* function description for bus driver */ + OS_SEMAPHORE InstanceSem; /* instance lock */ + SDLIST InstanceList; /* list of instances */ +}BT_FUNCTION_CONTEXT, *PBT_FUNCTION_CONTEXT; + +#define SDIO_BT_TYPE_A_HCI_CMD 0x01 +#define SDIO_BT_TYPE_A_HCI_ACL 0x02 +#define SDIO_BT_TYPE_A_HCI_SCO 0x03 +#define SDIO_BT_TYPE_A_HCI_EVT 0x04 +#define MAX_RX_RETRY 3 +#define SDIO_BT_TRANSPORT_HEADER_LENGTH 4 +#define SDIO_BT_SET_HEADER(pBuf,ServiceID,Length) \ +{ \ + (pBuf)[0] = (UINT8)(Length); \ + (pBuf)[1] = (UINT8)((Length) >> 8); \ + (pBuf)[2] = (UINT8)((Length) >> 16); \ + (pBuf)[3] = (UINT8)(ServiceID); \ +} + +#define SDIO_BT_GET_LENGTH(pHdr) \ + ((UINT32)((pHdr)[0]) | ((UINT32)((pHdr)[1]) << 8) | ((UINT32)((pHdr)[2]) << 16)) + +#define SDIO_BT_GET_SERVICEID(pHdr) (pHdr)[3] + +/* register offsets */ +#define RECV_DATA_REG 0x00 +#define XMIT_DATA_REG 0x00 +#define RECV_PACKET_CONTROL_REG 0x10 +#define XMIT_PACKET_CONTROL_REG 0x11 +#define RETRY_CONTROL_STATUS_REG 0x12 +#define INTERRUPT_STATUS_CLEAR_REG 0x13 +#define INTERRUPT_ENABLE_REG 0x14 +#define MODE_STATUS_REG 0x20 +/* bit masks */ +#define RCV_PKT_PENDING (1 << 0) +#define RETRY_STATUS_BIT_MASK (1 << 0) +#define RCV_PKT_PENDING_ENABLE (1 << 0) +#define RCV_PKT_CONTROL_RETRY (1 << 0) +#define XMIT_PKT_CONTROL_RETRY (1 << 0) +#define MODE_STATUS_TYPE_A 0x00 +#define RECV_PACKET_CONTROL_ACK 0x00 +#define RETRY_CONTROL_STATUS_USE_ACKS 0x00 + +struct PKT_RETRY_CONTROL_TUPLE { + UINT8 SDIO_Interface; /* interface code */ + UINT8 Class; /* standard function class */ +#define RTC_READ_ACK_REQUIRED 0x00 +#define RTC_READ_ACK_NOT_REQUIRED 0x01 + UINT8 SDIO_RetryControl; /* retry control */ +}CT_PACK_STRUCT; + +#define ReadRegister(pHci,reg,pData) \ + SDLIB_IssueCMD52((pHci)->pDevice,(pHci)->FuncNo,\ + (reg),(pData),1,CMD52_DO_READ) +static INLINE SDIO_STATUS WriteRegister(PBT_HCI_INSTANCE pHci, UINT reg, UINT8 Data) +{ + return SDLIB_IssueCMD52((pHci)->pDevice,(pHci)->FuncNo, + (reg),&Data,1,CMD52_DO_WRITE); +} + +PBT_HCI_INSTANCE CreateHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PSDDEVICE pDevice); +void DeleteHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PBT_HCI_INSTANCE pHci); + +SDIO_STATUS AddHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PBT_HCI_INSTANCE pHci); + +PBT_HCI_INSTANCE FindHciInstance(PBT_FUNCTION_CONTEXT pFuncContext, + PSDDEVICE pDevice); + +SDIO_STATUS SendHciPacket(PBT_HCI_INSTANCE pHci); + +PSDBT_HCI_PACKET OSAllocHCIRcvPacket(PBT_HCI_INSTANCE pHci, + UINT32 HCIPacketLength, + UINT8 Type); + +void OSFreeHciRcvPacket(PBT_HCI_INSTANCE pHci, PSDBT_HCI_PACKET pPkt); + +SDIO_STATUS OSIndicateHCIPacketReceived(PBT_HCI_INSTANCE pHci, + PSDBT_HCI_PACKET pPacket, + UINT32 HCIPacketLength, + UINT8 ServiceID); +void OSIndicateHCIPacketTransmitDone(PBT_HCI_INSTANCE pHci, + SDIO_STATUS status); + +void OSFreeSDRequest(PBT_HCI_INSTANCE pHci, PSDREQUEST pReq); + +PSDREQUEST OSAllocSDRequest(PBT_HCI_INSTANCE pHci); + +#endif /* __SDIO_BT_H___*/ + Index: linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt_linux.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt_linux.h 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,70 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_bt_linux.h + +@abstract: OS dependent include Bluetooth function driver + +#notes: + +@notice: Copyright (c), 2004 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDIO_BT_LINUX_H___ +#define __SDIO_BT_LINUX_H___ + + +#include +#include +#include + +#define SDIO_FUNCTION_BASE "sdiofn" +#define SDIO_FUNCTION_MAX_DEVICES 1 +#define SDIO_FUNCTION_MAJOR 0 + +typedef struct _SDBT_CONFIG { + struct hci_dev *pHciDev; /* the HCI device */ + BOOL HciRegistered; + struct sk_buff_head TxList; + spinlock_t TxListLock; + BOOL PktFlush; + spinlock_t RequestListLock; + SDLIST RequestList; +}SDBT_CONFIG, *PSBT_CONFIG; + +typedef struct sk_buff *PSDBT_HCI_PACKET; +#define SDBTHCI_GET_PKT_BUFFER(p) (p)->data +#define SDBTHCI_GET_PKT_LENGTH(p) (p)->len + +#endif /*__SDIO_BT_LINUX_H___*/ + Index: linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt_os.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/bluetooth/sdio_bt_os.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,584 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_bt_os.c + +@abstract: Linux implementation module for SDIO Bluetooth Function driver + +#notes: includes module load and unload functions + +@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* debug level for this module*/ +#define DBG_DECLARE 3; +#include +#include +#include +#include +#include "sdio_bt.h" +#include +#include +#include + +#define DESCRIPTION "SDIO Bluetooth Function Driver" +#define AUTHOR "Atheros Communications, Inc." + +/* debug print parameter */ +module_param(debuglevel, int, 0644); +MODULE_PARM_DESC(debuglevel, "debuglevel 0-7, controls debug prints"); + +static INT blockfix = 0; +module_param(blockfix, int, 0644); +MODULE_PARM_DESC(blockfix, "HCI packet block fix"); + +static INT sdrequests = 8; +module_param(sdrequests, int, 0644); +MODULE_PARM_DESC(sdrequests, "HCI SDRequest list size"); + +static int bt_open(struct hci_dev *hdev); +static int bt_close(struct hci_dev *hdev); +static int bt_send_frame(struct sk_buff *skb); +static int bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); +static int bt_flush(struct hci_dev *hdev); +static void bt_destruct(struct hci_dev *hdev); +static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice); +static void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice); +static void CleanupInstance(PBT_FUNCTION_CONTEXT pFunctionContext, + PBT_HCI_INSTANCE pHci); + +/* devices we support, null terminated */ +static SD_PNP_INFO Ids[] = { + {.SDIO_FunctionClass = 0x02}, /* SDIO-BLUETOOTH SDIO standard interface code */ + {.SDIO_FunctionClass = 0x03}, + {} +}; + +/* driver data */ +static BT_FUNCTION_CONTEXT FunctionContext = { + .Function.Version = CT_SDIO_STACK_VERSION_CODE, + .Function.pName = "sdio_bluetooth", + .Function.MaxDevices = 1, + .Function.NumDevices = 0, + .Function.pIds = Ids, + .Function.pProbe = Probe, + .Function.pRemove = Remove, + .Function.pSuspend = NULL, + .Function.pResume = NULL, + .Function.pWake = NULL, + .Function.pContext = &FunctionContext, +}; + +void OSFreeSDRequest(PBT_HCI_INSTANCE pHci, PSDREQUEST pReq) +{ + spin_lock(&pHci->Config.RequestListLock); + SDListAdd(&pHci->Config.RequestList, &pReq->SDList); + spin_unlock(&pHci->Config.RequestListLock); +} + +PSDREQUEST OSAllocSDRequest(PBT_HCI_INSTANCE pHci) +{ + PSDREQUEST pReq = NULL; + PSDLIST pItem; + + spin_lock(&pHci->Config.RequestListLock); + do { + /* check the list */ + pItem = SDListRemoveItemFromHead(&pHci->Config.RequestList); + if (NULL == pItem) { + break; + } + pReq = CONTAINING_STRUCT(pItem, SDREQUEST, SDList); + } while (FALSE); + + spin_unlock(&pHci->Config.RequestListLock); + + return pReq; +} + +/* allocate a receive packet */ +PSDBT_HCI_PACKET OSAllocHCIRcvPacket(PBT_HCI_INSTANCE pHci, + UINT32 HCIPacketLength, + UINT8 Type) +{ + PSDBT_HCI_PACKET pPacket; + + /* allocate a buffer */ + pPacket = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (pPacket != NULL) { + pPacket->dev = (PVOID)pHci->Config.pHciDev; + } + return pPacket; +} + +void OSFreeHciRcvPacket(PBT_HCI_INSTANCE pHci, PSDBT_HCI_PACKET pPkt) +{ + kfree_skb(pPkt); +} + +/* + * Indicate that a HCI packet was received +*/ +SDIO_STATUS OSIndicateHCIPacketReceived(PBT_HCI_INSTANCE pHci, + PSDBT_HCI_PACKET pPacket, + UINT32 HCIPacketLength, + UINT8 ServiceID) +{ + UINT8 btType; + + switch (ServiceID) { + case SDIO_BT_TYPE_A_HCI_ACL: + btType = HCI_ACLDATA_PKT; + break; + case SDIO_BT_TYPE_A_HCI_SCO: + btType = HCI_SCODATA_PKT; + break; + case SDIO_BT_TYPE_A_HCI_EVT: + btType = HCI_EVENT_PKT; + break; + default: + DBG_ASSERT(FALSE); + return SDIO_STATUS_ERROR; + } + /* set the final type */ + pPacket->pkt_type = btType; + /* adjust packet length to what the caller copied to the buffer */ + skb_put(pPacket,HCIPacketLength); + /* pass receive packet up the stack */ + if (hci_recv_frame(pPacket) != 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bluetooth Function: hci_recv_frame failed \n")); + } else { + DBG_PRINT(SDBT_DBG_RECEIVE, ("SDIO Bluetooth Function: Indicated RCV of type:%d, Length:%d \n", + btType,HCIPacketLength)); + } + return SDIO_STATUS_SUCCESS; +} + +/* + * bt_open - open a handle to the device +*/ +static int bt_open(struct hci_dev *hdev) +{ + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: bt_open - enter - x\n")); + + set_bit(HCI_RUNNING, &hdev->flags); + set_bit(HCI_UP, &hdev->flags); + set_bit(HCI_INIT, &hdev->flags); + ((PBT_HCI_INSTANCE)hdev->driver_data)->Config.PktFlush = FALSE; + + return 0; +} + +/* + * bt_close - close handle to the device +*/ +static int bt_close(struct hci_dev *hdev) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: bt_close - enter\n")); + + clear_bit(HCI_RUNNING, &hdev->flags); + return 0; +} + + +void OSIndicateHCIPacketTransmitDone(PBT_HCI_INSTANCE pHci, + SDIO_STATUS status) +{ + PSDBT_HCI_PACKET pPacket; + + pPacket = pHci->pCurrentTxPacket; + DBG_ASSERT(pPacket != NULL); + + spin_lock(&pHci->Config.TxListLock); + pHci->pCurrentTxPacket = NULL; + if (!pHci->Config.PktFlush) { + /* dequeue HCI packet */ + pHci->pCurrentTxPacket = __skb_dequeue(&pHci->Config.TxList); + spin_unlock(&pHci->Config.TxListLock); + if (pHci->pCurrentTxPacket != NULL) { + /* start next */ + SendHciPacket(pHci); + } + } else { + spin_unlock(&pHci->Config.TxListLock); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth, cleanup in progress \n")); + } + /* free the one that completed */ + kfree_skb(pPacket); +} + + + +/* + * bt_send_frame - send data frames +*/ +static int bt_send_frame(struct sk_buff *skb) +{ + SDIO_STATUS status; + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + PBT_HCI_INSTANCE pHci; + UINT8 serviceID; + UINT8 *pTemp; + + if (!hdev) { + DBG_PRINT(SDDBG_WARN, ("SDIO Bluetooth Function: bt_send_frame - no device\n")); + return -ENODEV; + } + + if (!test_bit(HCI_RUNNING, &hdev->flags)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: bt_send_frame - not open\n")); + return -EBUSY; + } + + pHci = (PBT_HCI_INSTANCE)hdev->driver_data; + + if (pHci->Config.PktFlush) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: bt_send_frame - flush in progress..\n")); + return -EBUSY; + } + + switch (skb->pkt_type) { + case HCI_COMMAND_PKT: + serviceID = SDIO_BT_TYPE_A_HCI_CMD; + hdev->stat.cmd_tx++; + break; + + case HCI_ACLDATA_PKT: + serviceID = SDIO_BT_TYPE_A_HCI_ACL; + hdev->stat.acl_tx++; + break; + + case HCI_SCODATA_PKT: + serviceID = SDIO_BT_TYPE_A_HCI_SCO; + hdev->stat.sco_tx++; + break; + default: + kfree_skb(skb); + return 0; + } + if (DBG_GET_DEBUG_LEVEL() >= SDBT_DBG_TRANSMIT) { + SDLIB_PrintBuffer(SDBTHCI_GET_PKT_BUFFER(skb),SDBTHCI_GET_PKT_LENGTH(skb), + "Linux BT HCI Packet Dump"); + if (skb->pkt_type == HCI_COMMAND_PKT) { + pTemp = SDBTHCI_GET_PKT_BUFFER(skb); + DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO BT HCI Command: OCF:0x%4.4X, OGF:0x%2.2X \n", + (INT)pTemp[0] | (((INT)(pTemp[1] & 0x03)) << 8), pTemp[1] >> 2 )); + } + } + + /* BT HCI packets have 8 bytes of header space, push on 4 bytes for the header + * this bumps up the "len" field */ + pTemp = (PUINT8)skb_push(skb, SDIO_BT_TRANSPORT_HEADER_LENGTH); + /* set the header */ + SDIO_BT_SET_HEADER(pTemp, + serviceID, + SDBTHCI_GET_PKT_LENGTH(skb)); + + + DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO Bluetooth Function: bt_send_frame (hci:0x%X) Packet:0x%X \n", + (INT)pHci, (INT)skb)); + DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO BT Send ServiceID:%d, Total Length:%d Bytes \n", + serviceID,SDBTHCI_GET_PKT_LENGTH(skb))); + + spin_lock(&pHci->Config.TxListLock); + status = SDIO_STATUS_PENDING; + if (pHci->pCurrentTxPacket != NULL) { + /* queue HCI packet */ + __skb_queue_tail(&pHci->Config.TxList,skb); + spin_unlock(&pHci->Config.TxListLock); + DBG_PRINT(SDBT_DBG_TRANSMIT, ("SDIO BT Send , Packet Queued \n")); + } else { + pHci->pCurrentTxPacket = skb; + spin_unlock(&pHci->Config.TxListLock); + status = SendHciPacket(pHci); + } + + if (!SDIO_SUCCESS(status)) { + return SDIOErrorToOSError(status); + } + return 0; +} + +/* + * bt_ioctl - ioctl processing +*/ +static int bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: bt_ioctl - enter\n")); + return -ENOIOCTLCMD; +} + +/* + * bt_flush - flush outstandingbpackets +*/ +static int bt_flush(struct hci_dev *hdev) +{ + PSDBT_HCI_PACKET pPkt; + PBT_HCI_INSTANCE pHci; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: bt_flush - enter\n")); + + pHci = (PBT_HCI_INSTANCE)hdev->driver_data; + pHci->Config.PktFlush = TRUE; + + spin_lock(&pHci->Config.TxListLock); + /* cleanup the queue */ + while (1) { + pPkt = __skb_dequeue(&pHci->Config.TxList); + if (pPkt != NULL) { + kfree_skb(pPkt); + } else { + break; + } + + } + if (pHci->pCurrentTxPacket != NULL) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: Outstanding HCI packet:0x%X \n", + (INT)pHci->pCurrentTxPacket)); + } + spin_unlock(&pHci->Config.TxListLock); + + return 0; +} + + +/* + * bt_destruct - +*/ +static void bt_destruct(struct hci_dev *hdev) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: bt_destruct - enter\n")); + /* currently only supporting a single statically assigned device, nothing to do here */ +} + +/* + * Probe - a device potentially for us +*/ +static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice) { + PBT_FUNCTION_CONTEXT pFunctionContext = + (PBT_FUNCTION_CONTEXT)pFunction->pContext; + SYSTEM_STATUS err = 0; + BOOL okay = FALSE; + struct hci_dev *pHciDev = NULL; + PBT_HCI_INSTANCE pNewHci = NULL; + INT i; + PSDREQUEST pReq; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: Probe - enter\n")); + + /* make sure this is a device we can handle */ + if ((pDevice->pId[0].SDIO_FunctionClass == 0x02) || + (pDevice->pId[0].SDIO_FunctionClass == 0x03)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: Probe - card matched (0x%X/0x%X/0x%X)\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_ManufacturerCode, + pDevice->pId[0].SDIO_FunctionNo)); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: Probe - not our card (0x%X/0x%X/0x%X)\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_ManufacturerCode, + pDevice->pId[0].SDIO_FunctionNo)); + return FALSE; + } + + do { + + pNewHci = CreateHciInstance(pFunctionContext, pDevice); + if (NULL == pNewHci) { + break; + } + + if (blockfix) { + pNewHci->BlockTransferFix = TRUE; + } else { + pNewHci->BlockTransferFix = FALSE; + } + + skb_queue_head_init(&pNewHci->Config.TxList); + spin_lock_init(&pNewHci->Config.TxListLock); + spin_lock_init(&pNewHci->Config.RequestListLock); + SDLIST_INIT(&pNewHci->Config.RequestList); + + /* allocate bus requests for block transfers */ + for (i = 0; i < sdrequests; i++) { + pReq = SDDeviceAllocRequest(pDevice); + if (NULL == pReq) { + break; + } + /* add it to our list */ + OSFreeSDRequest(pNewHci, pReq); + } + + /* allocate a BT HCI struct for this device */ + pHciDev = hci_alloc_dev(); + if (NULL == pHciDev) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bluetooth Function: Probe - failed to allocate bt struct.\n")); + break; + } + SET_HCIDEV_DEV(pHciDev, SD_GET_OS_DEVICE(pDevice)); + + pNewHci->Config.pHciDev = pHciDev; + /* add this instance to our list */ + if (!SDIO_SUCCESS(AddHciInstance(pFunctionContext,pNewHci))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bluetooth : failed to add instance to list \n")); + break; + } + + pHciDev->type = HCI_VHCI; /* we don't really have a type assigned ????*/ + pHciDev->driver_data = pNewHci; + pHciDev->open = bt_open; + pHciDev->close = bt_close; + pHciDev->send = bt_send_frame; + pHciDev->ioctl = bt_ioctl; + pHciDev->flush = bt_flush; + pHciDev->destruct = bt_destruct; + pHciDev->owner = THIS_MODULE; + + /* mark that we are registered */ + pNewHci->Config.HciRegistered = TRUE; + if ((err = hci_register_dev(pHciDev)) < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bluetooth Function: Probe - can't register with bluetooth %d\n", + err)); + pNewHci->Config.HciRegistered = FALSE; + break; + } + + okay = TRUE; + } while (FALSE); + + if (!okay) { + if (pNewHci != NULL) { + CleanupInstance(pFunctionContext, pNewHci); + } + } + + return okay; +} + +static void CleanupInstance(PBT_FUNCTION_CONTEXT pFunctionContext, + PBT_HCI_INSTANCE pHci) +{ + int err; + PSDREQUEST pReq; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bluetooth CleanupInstance \n")); + + if (pHci->Config.pHciDev != NULL) { + if (pHci->Config.HciRegistered) { + /* first unregister */ + if ((err = hci_unregister_dev(pHci->Config.pHciDev)) < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bluetooth Function: Remove - can't unregister with bluetooth %d\n", + err)); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: Remove - HCI Instance:0x%X, unregistered\n", + (INT)pHci)); + } + + if (pHci->pCurrentTxPacket != NULL) { + /* TODO fix this with polling or an event */ + OSSleep(2000); + } + DBG_ASSERT(pHci->pCurrentTxPacket == NULL); + KernelFree(pHci->Config.pHciDev); + } + } + + /* cleanup list */ + while (1) { + pReq = OSAllocSDRequest(pHci); + if (NULL == pReq) { + break; + } + SDDeviceFreeRequest(pHci->pDevice,pReq); + } + /* remove this instance */ + DeleteHciInstance(pFunctionContext, pHci); + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bluetooth CleanupInstance \n")); +} + +/* + * Remove - our device is being removed +*/ +static void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice) +{ + PBT_HCI_INSTANCE pHci; + PBT_FUNCTION_CONTEXT pFunctionContext = + (PBT_FUNCTION_CONTEXT)pFunction->pContext; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: Remove - enter\n")); + + pHci = FindHciInstance(pFunctionContext,pDevice); + if (pHci != NULL) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: Removing instance: 0x%X From Remove() \n", + (INT)pHci)); + CleanupInstance(pFunctionContext, pHci); + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bluetooth Function: could not find matching instance! \n")); + } +} + + +/* + * module init +*/ +static int __init sdio_bt_init(void) { + SDIO_STATUS status; + + REL_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: enter\n")); + + SDLIST_INIT(&FunctionContext.InstanceList); + status = SemaphoreInitialize(&FunctionContext.InstanceSem, 1); + if (!SDIO_SUCCESS(status)) { + return SDIOErrorToOSError(status); + } + /* register with bus driver core */ + return SDIOErrorToOSError(SDIO_RegisterFunction(&FunctionContext.Function)); +} + +/* + * module cleanup +*/ +static void __exit sdio_bt_cleanup(void) { + REL_PRINT(SDDBG_TRACE, ("SDIO Bluetooth Function: exit\n")); + SDIO_UnregisterFunction(&FunctionContext.Function); + SemaphoreDelete(&FunctionContext.InstanceSem); +} + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); +module_init(sdio_bt_init); +module_exit(sdio_bt_cleanup); + Index: linux-2.6.22/drivers/sdio/function/memory/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/memory/Makefile 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,5 @@ +# +# SDIO memory card function driver +# +obj-m += sdio_memory_fd.o +sdio_memory_fd-objs := sdio_memory.o sdio_memory_os.o Index: linux-2.6.22/drivers/sdio/function/memory/sdio_memory.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/function/memory/sdio_memory.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,926 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_memory.c + +@abstract: SDIO Memeory Function driver + +#notes: includes OS independent portions - + +@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. + * + * 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. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define MODULE_NAME SDMEMORYFD +#include + +#include +#include +#include +#include +#include "sdio_memory.h" + + +#define MMC_GET_START_BIT(pCsd) (pCsd[0] & 0x1) +#define MMC_GET_FILE_FORMAT(pCsd) (pCsd[1] & 0xC) +#define MMC_GET_PERM_WRITE_PROTECT(pCsd) (pCsd[1] & 0x20) +#define MMC_GET_TEMP_WRITE_PROTECT(pCsd) (pCsd[1] & 0x10) +#define MMC_GET_FILE_FORMAT_GROUP(pCsd) (pCsd[1] & 0x80) +#define MMC_GET_PARTIAL_WRITE_DATA(pCsd) (pCsd[2] & 0x20) +#define MMC_GET_MAX_WRITE_DATA_BLOCK(pCsd) (((pCsd[2] & 0xC0)>>6) | ((pCsd[3] & 0x3) << 2)) +#define MMC_GET_WRITE_SPEED_FACTOR(pCsd) (pCsd[3] & 0x1C) +#define MMC_GET_MAX_READ_DATA_BLOCK(pCsd) (pCsd[10] & 0x0F) +#define MMC_GET_PARTIAL_READ_DATA(pCsd) (pCsd[9] & 0x80) +#define MMC_GET_C_SIZE_MULT(pCsd) (((pCsd[5] & 0x80)>>7) | ((pCsd[6] & 0x03)<<1)) +#define MMC_GET_C_SIZE(pCsd) (((pCsd[7] & 0xC0)>>6) | (pCsd[8]<<2) |((pCsd[9] & 0x03)<<10)) +#define MMC_GET_SPEC_VERSION(pCsd) ((pCsd[15] & 0x3C) >> 2) /* MMC Only */ +#define MMC_GET_CSD_VERSION(pCsd) ((pCsd[15] & 0xC0) >> 6) +static const INT MMC_POWER_TABLE[12] = { + 1,2,4,8,16,32,64,128,256,512,1024,2048 +}; + +#define MMC_GET_WRITE_CURR_MAX(pCsd) (((pCsd)[6] >> 2) & 0x07) +#define MMC_GET_READ_CURR_MAX(pCsd) ((pCsd)[7] & 0x07) + + /* VDD current table, mA */ +static const UINT8 VDDCurrentTable[8] = { + 1,5,10,25,35,45,80,200 +}; + +#define MMC_CMD_SET_BLOCK_LENGTH CMD16 +#define MMC_CMD_READ_SINGLE_BLOCK CMD17 +#define MMC_CMD_READ_MULTIPLE_BLOCK CMD18 +#define MMC_CMD_WRITE_SINGLE_BLOCK CMD24 +#define MMC_CMD_WRITE_MULTIPLE_BLOCK CMD25 + + +const SD_SLOT_CURRENT MMC_PowerClass_3_6V_To_Current[MMC_EXT_MAX_PWR_CLASSES] = + /* in milliamps */ +{ + 200, /* 0 */ + 220, /* 1 */ + 250, /* 2 */ + 280, /* 3 */ + 300, /* 4 */ + 320, /* 5 */ + 350, /* 6 */ + 400, /* 7 */ + 450, /* 8 */ + 500, /* 9 */ + 550, /* 10 */ + 0,0,0,0,0 /* 11 - 15 are reserved */ +}; + +const SD_SLOT_CURRENT MMC_PowerClass_1_95V_To_Current[MMC_EXT_MAX_PWR_CLASSES] = + /* in milliamps */ +{ + 130, /* 0 */ + 140, /* 1 */ + 160, /* 2 */ + 180, /* 3 */ + 200, /* 4 */ + 220, /* 5 */ + 240, /* 6 */ + 260, /* 7 */ + 280, /* 8 */ + 300, /* 9 */ + 350, /* 10 */ + 0,0,0,0,0 /* 11 - 15 are reserved */ +}; + + + +static SDIO_STATUS IssueDeviceRequest(PSDDEVICE pDevice, + UINT8 Cmd, + UINT32 Argument, + SDREQUEST_FLAGS Flags, + PSDREQUEST pReqToUse, + PVOID pData, + INT Length); +static SDIO_STATUS ReadBlocks(PSDDEVICE pDevice, + PSDIO_MEMORY_INSTANCE pInstance, + UINT32 Address, + PVOID pData, + UINT32 Length); +static SDIO_STATUS WriteBlocks(PSDDEVICE pDevice, + PSDIO_MEMORY_INSTANCE pInstance, + UINT32 Address, + PVOID pData, + UINT32 Length); + + +/* delete an instance */ +void DeleteInstance(PSDIO_MEMORY_CONTEXT pFuncContext, + PSDIO_MEMORY_INSTANCE pInstance) +{ + + if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pFuncContext->InstanceSem))) { + return; + } + /* pull it out of the list */ + SDListRemove(&pInstance->SDList); + SemaphorePost(&pFuncContext->InstanceSem); + + + /* free slot current */ + SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_FREE_SLOT_CURRENT, + NULL, + 0); + + KernelFree(pInstance); +} + +static void ReorderBuffer(UINT8 *pBuffer, INT Bytes) +{ + UINT8 *pEnd; + UINT8 temp; + + DBG_ASSERT(!(Bytes & 1)); + /* point to the end */ + pEnd = &pBuffer[Bytes - 1]; + /* divide in half */ + Bytes = Bytes >> 1; + + while (Bytes) { + temp = *pBuffer; + /* swap bytes */ + *pBuffer = *pEnd; + *pEnd = temp; + pBuffer++; + pEnd--; + Bytes--; + } +} + +SD_SLOT_CURRENT LookupCurrent(PSDDEVICE pDevice, PUINT8 pExtendedCSD) +{ + const SD_SLOT_CURRENT *pTable = NULL; + UINT8 powerClass = 0; + + switch(SDDEVICE_GET_SLOT_VOLTAGE_MASK(pDevice)) { + case SLOT_POWER_3_3V: + case SLOT_POWER_3_0V: + case SLOT_POWER_2_8V: + pTable = MMC_PowerClass_3_6V_To_Current; + if (SDDEVICE_GET_OPER_CLOCK(pDevice) <= 26000000) { + DBG_PRINT(SDDBG_TRACE, (" Using MMC 3.6V and 26Mhz power table \n")); + powerClass = pExtendedCSD[MMC_EXT_PWR_CL_26_360_OFFSET]; + } else { + DBG_PRINT(SDDBG_TRACE, (" Using MMC 3.6V and 52Mhz power table \n")); + powerClass = pExtendedCSD[MMC_EXT_PWR_CL_52_360_OFFSET]; + } + break; + case SLOT_POWER_2_0V: + case SLOT_POWER_1_8V: + case SLOT_POWER_1_6V: + pTable = MMC_PowerClass_1_95V_To_Current; + if (SDDEVICE_GET_OPER_CLOCK(pDevice) <= 26000000) { + DBG_PRINT(SDDBG_TRACE, (" Using MMC 1.95V and 26Mhz power table \n")); + powerClass = pExtendedCSD[MMC_EXT_PWR_CL_26_195_OFFSET]; + } else { + DBG_PRINT(SDDBG_TRACE, (" Using MMC 1.95V and 52Mhz power table \n")); + powerClass = pExtendedCSD[MMC_EXT_PWR_CL_52_195_OFFSET]; + } + break; + default: + DBG_ASSERT(FALSE); + break; + } + + if (pTable != NULL) { + if (SDDEVICE_GET_BUSWIDTH(pDevice) == SDCONFIG_BUS_WIDTH_MMC8_BIT) { + /* use upper nibble for power class */ + powerClass >>= 4; + } + powerClass &= 0xF; + DBG_PRINT(SDDBG_TRACE, (" MMC Power Class %d: \n",powerClass)); + return pTable[powerClass]; + } + + return 0; +} +/* create a memory instance */ +PSDIO_MEMORY_INSTANCE CreateDeviceInstance(PSDIO_MEMORY_CONTEXT pFuncContext, + PSDDEVICE pDevice) +{ + PSDIO_MEMORY_INSTANCE pInstance = NULL; + SDCONFIG_FUNC_SLOT_CURRENT_DATA slotCurrent; + PUINT8 pCSD = SDDEVICE_GET_CARDCSD(pDevice); + SD_SLOT_CURRENT maxReadCurrent = 0; + SD_SLOT_CURRENT maxWriteCurrent = 0; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDREQUEST pReq = NULL; + + ZERO_OBJECT(slotCurrent); + + do { + pInstance = (PSDIO_MEMORY_INSTANCE)KernelAlloc(sizeof(SDIO_MEMORY_INSTANCE)); + if (NULL == pInstance) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + ZERO_POBJECT(pInstance); + SDLIST_INIT(&pInstance->SDList); + pInstance->pDevice = pDevice; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Memory Function Instance: 0x%X \n",(INT)pInstance)); + DBG_PRINT(SDDBG_TRACE, (" Card Flags: 0x%X \n",SDDEVICE_GET_CARD_FLAGS(pDevice))); + DBG_PRINT(SDDBG_TRACE, (" Card RCA: 0x%X \n",SDDEVICE_GET_CARD_RCA(pDevice))); + DBG_PRINT(SDDBG_TRACE, (" Oper Clock: %d Hz \n",SDDEVICE_GET_OPER_CLOCK(pDevice))); + DBG_PRINT(SDDBG_TRACE, (" Max Clock: %d Hz \n",SDDEVICE_GET_MAX_CLOCK(pDevice))); + DBG_PRINT(SDDBG_TRACE, (" Oper BlklenLim: %d bytes \n",SDDEVICE_GET_OPER_BLOCK_LEN(pDevice))); + DBG_PRINT(SDDBG_TRACE, (" Max BlkLen: %d bytes\n",SDDEVICE_GET_MAX_BLOCK_LEN(pDevice))); + DBG_PRINT(SDDBG_TRACE, (" Oper BlksLim: %d blocks per trans \n",SDDEVICE_GET_OPER_BLOCKS(pDevice))); + DBG_PRINT(SDDBG_TRACE, (" Max Blks: %d blocks per trans \n",SDDEVICE_GET_MAX_BLOCKS(pDevice))); + + + pReq = SDDeviceAllocRequest(pDevice); + + if (NULL == pReq) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + /* for SD cards in high speed mode, the power consumption is reported in the switch + * status block */ + if ((SDDEVICE_GET_CARD_FLAGS(pDevice) & CARD_SD) && + (SDDEVICE_GET_BUSMODE_FLAGS(pDevice) & SDCONFIG_BUS_MODE_SD_HS)) { + /* fetch the switch status block */ + pReq->Command = CMD6; + pReq->Argument = SD_SWITCH_FUNC_ARG_GROUP_CHECK(SD_SWITCH_HIGH_SPEED_GROUP, + SD_SWITCH_HIGH_SPEED_FUNC_NO); + pReq->Flags = SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS; + pReq->BlockCount = 1; + pReq->BlockLen = SD_SWITCH_FUNC_STATUS_BLOCK_BYTES; + pReq->pDataBuffer = pInstance->ExtendedData; + + status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq); + + if (SDIO_SUCCESS(status)) { + /* need to reorder this since cards send this MSB first */ + ReorderBuffer(pInstance->ExtendedData,SD_SWITCH_FUNC_STATUS_BLOCK_BYTES); + maxWriteCurrent = SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pInstance->ExtendedData); + if (maxWriteCurrent == 0) { + DBG_PRINT(SDDBG_WARN, ("SDIO Memory: SD Card Switch Status indicates 0 current!, using CSD instead\n")); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Memory: SD High Speed card requires %d mA of current\n", + maxWriteCurrent)); + } + maxReadCurrent = maxWriteCurrent; + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO Memory: Failed to get SD Card Switch Status \n")); + status = SDIO_STATUS_SUCCESS; + /* since we can't figure this out, use the largest value the SD spec says */ + maxWr