]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsconfig.c
Merge tag 'v2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / staging / ath6kl / miscdrv / ar3kps / ar3kpsconfig.c
diff --git a/drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsconfig.c b/drivers/staging/ath6kl/miscdrv/ar3kps/ar3kpsconfig.c
new file mode 100644 (file)
index 0000000..0e298db
--- /dev/null
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2004-2010 Atheros Communications Inc.
+ * All rights reserved.
+ *
+ * This file implements the Atheros PS and patch downloaded for HCI UART Transport driver.
+ * This file can be used for HCI SDIO transport implementation for AR6002 with HCI_TRANSPORT_SDIO
+ * defined.
+ *
+ *
+ * ar3kcpsconfig.c
+ *
+ *
+ *
+ * The software source and binaries included in this development package are
+ * licensed, not sold. You, or your company, received the package under one
+ * or more license agreements. The rights granted to you are specifically
+ * listed in these license agreement(s). All other rights remain with Atheros
+ * Communications, Inc., its subsidiaries, or the respective owner including
+ * those listed on the included copyright notices..  Distribution of any
+ * portion of this package must be in strict compliance with the license
+ * agreement(s) terms.
+ *
+ *
+ *
+ */
+
+
+
+#include "ar3kpsconfig.h"
+#ifndef HCI_TRANSPORT_SDIO
+#include "hci_ath.h"
+#include "hci_uart.h"
+#endif /* #ifndef HCI_TRANSPORT_SDIO */
+
+#define MAX_FW_PATH_LEN             50
+#define MAX_BDADDR_FORMAT_LENGTH    30
+
+/*
+ *  Structure used to send HCI packet, hci packet length and device info 
+ *  together as parameter to PSThread.
+ */
+typedef struct {
+
+    PSCmdPacket *HciCmdList;
+    A_UINT32  num_packets;
+    AR3K_CONFIG_INFO *dev;
+}HciCommandListParam;
+
+A_STATUS SendHCICommandWaitCommandComplete(AR3K_CONFIG_INFO *pConfig,
+                                           A_UINT8          *pHCICommand,
+                                           int              CmdLength,
+                                           A_UINT8          **ppEventBuffer,
+                                           A_UINT8          **ppBufferToFree);
+
+A_UINT32  Rom_Version;
+A_UINT32  Build_Version;
+extern A_BOOL BDADDR;
+
+A_STATUS getDeviceType(AR3K_CONFIG_INFO *pConfig, A_UINT32 * code);
+A_STATUS ReadVersionInfo(AR3K_CONFIG_INFO *pConfig);
+#ifndef HCI_TRANSPORT_SDIO
+
+DECLARE_WAIT_QUEUE_HEAD(PsCompleteEvent);
+DECLARE_WAIT_QUEUE_HEAD(HciEvent);
+A_UCHAR *HciEventpacket;
+rwlock_t syncLock;
+wait_queue_t Eventwait;
+
+int PSHciWritepacket(struct hci_dev*,A_UCHAR* Data, A_UINT32 len);
+extern char *bdaddr;
+#endif /* HCI_TRANSPORT_SDIO */
+
+A_STATUS write_bdaddr(AR3K_CONFIG_INFO *pConfig,A_UCHAR *bdaddr,int type);
+
+int PSSendOps(void *arg);
+
+#ifdef BT_PS_DEBUG
+void Hci_log(A_UCHAR * log_string,A_UCHAR *data,A_UINT32 len)
+{
+    int i;
+    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s : ",log_string));
+    for (i = 0; i < len; i++) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("0x%02x ", data[i]));
+    }
+    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("\n...................................\n"));
+}
+#else
+#define Hci_log(string,data,len)
+#endif /* BT_PS_DEBUG */
+
+
+
+
+A_STATUS AthPSInitialize(AR3K_CONFIG_INFO *hdev)
+{
+    A_STATUS status = A_OK;
+    if(hdev == NULL) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Invalid Device handle received\n"));
+        return A_ERROR;
+    }
+
+#ifndef HCI_TRANSPORT_SDIO
+    DECLARE_WAITQUEUE(wait, current);
+#endif /* HCI_TRANSPORT_SDIO */
+    
+
+#ifdef HCI_TRANSPORT_SDIO
+    status = PSSendOps((void*)hdev);
+#else
+    if(InitPSState(hdev) == -1) {
+        return A_ERROR;
+    }
+    allow_signal(SIGKILL);
+    add_wait_queue(&PsCompleteEvent,&wait);
+    set_current_state(TASK_INTERRUPTIBLE);
+    if(!kernel_thread(PSSendOps,(void*)hdev,CLONE_FS|CLONE_FILES|CLONE_SIGHAND|SIGCHLD)) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Kthread Failed\n"));
+        remove_wait_queue(&PsCompleteEvent,&wait);
+        return A_ERROR;
+    }
+    wait_event_interruptible(PsCompleteEvent,(PSTagMode == FALSE));
+    set_current_state(TASK_RUNNING);
+    remove_wait_queue(&PsCompleteEvent,&wait);
+
+#endif /* HCI_TRANSPORT_SDIO */
+
+
+    return status;
+    
+}
+
+int PSSendOps(void *arg) 
+{
+    int i;
+    int status = 0;
+    PSCmdPacket *HciCmdList; /* List storing the commands */
+    const struct firmware* firmware;
+    A_UINT32 numCmds;
+    A_UINT8 *event;
+    A_UINT8 *bufferToFree;
+    struct hci_dev *device;
+    A_UCHAR *buffer;
+    A_UINT32 len;
+    A_UINT32 DevType;
+    A_UCHAR *PsFileName;
+    A_UCHAR *patchFileName;
+    A_UCHAR *path = NULL;
+    A_UCHAR *config_path = NULL;
+    A_UCHAR config_bdaddr[MAX_BDADDR_FORMAT_LENGTH];
+    AR3K_CONFIG_INFO *hdev = (AR3K_CONFIG_INFO*)arg;
+    struct device *firmwareDev = NULL;
+    status = 0;
+    HciCmdList = NULL;
+#ifdef HCI_TRANSPORT_SDIO
+    device = hdev->pBtStackHCIDev; 
+    firmwareDev = device->parent;
+#else 
+    device = hdev;
+    firmwareDev = &device->dev;
+    AthEnableSyncCommandOp(TRUE);    
+#endif /* HCI_TRANSPORT_SDIO */
+    /* First verify if the controller is an FPGA or ASIC, so depending on the device type the PS file to be written will be different.
+     */
+
+    path =(A_UCHAR *)A_MALLOC(MAX_FW_PATH_LEN);
+    if(path == NULL) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Malloc failed to allocate %d bytes for path\n", MAX_FW_PATH_LEN));
+        goto complete;
+    }
+    config_path = (A_UCHAR *) A_MALLOC(MAX_FW_PATH_LEN);
+    if(config_path == NULL) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Malloc failed to allocate %d bytes for config_path\n", MAX_FW_PATH_LEN));
+        goto complete;
+    }
+
+    if(A_ERROR == getDeviceType(hdev,&DevType)) {
+        status = 1;
+        goto complete;
+    }
+    if(A_ERROR == ReadVersionInfo(hdev)) {
+        status = 1;
+        goto complete;
+    }
+
+    patchFileName = PATCH_FILE;
+    snprintf(path, MAX_FW_PATH_LEN, "%s/%xcoex/",CONFIG_PATH,Rom_Version);
+    if(DevType){
+        if(DevType == 0xdeadc0de){
+               PsFileName =  PS_ASIC_FILE;
+           } else{
+               AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" FPGA Test Image : %x %x  \n",Rom_Version,Build_Version));
+                if((Rom_Version == 0x99999999) && (Build_Version == 1)){
+                        
+                       AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("FPGA Test Image : Skipping Patch File load\n"));
+                       patchFileName = NULL;
+               }
+               PsFileName =  PS_FPGA_FILE;
+           }
+    }
+    else{
+           PsFileName =  PS_ASIC_FILE;
+    }
+
+    snprintf(config_path, MAX_FW_PATH_LEN, "%s%s",path,PsFileName);
+    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%x: FPGA/ASIC PS File Name %s\n", DevType,config_path));
+    /* Read the PS file to a dynamically allocated buffer */
+    if(A_REQUEST_FIRMWARE(&firmware,config_path,firmwareDev) < 0) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: firmware file open error\n", __FUNCTION__ ));
+        status = 1;
+        goto complete;
+
+    }
+    if(NULL == firmware || firmware->size == 0) {
+        status = 1;
+        goto complete;
+    }
+    buffer = (A_UCHAR *)A_MALLOC(firmware->size);
+    if(buffer != NULL) {
+    /* Copy the read file to a local Dynamic buffer */
+        memcpy(buffer,firmware->data,firmware->size);
+        len = firmware->size;
+        A_RELEASE_FIRMWARE(firmware);
+        /* Parse the PS buffer to a global variable */
+        status = AthDoParsePS(buffer,len);
+        A_FREE(buffer);
+    } else {
+        A_RELEASE_FIRMWARE(firmware);
+    }
+
+
+    /* Read the patch file to a dynamically allocated buffer */
+       if(patchFileName != NULL)
+                snprintf(config_path,
+                         MAX_FW_PATH_LEN, "%s%s",path,patchFileName);
+       else {
+               status = 0;
+       }
+    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Patch File Name %s\n", config_path));
+    if((patchFileName == NULL) || (A_REQUEST_FIRMWARE(&firmware,config_path,firmwareDev) < 0)) {
+        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: firmware file open error\n", __FUNCTION__ ));
+        /* 
+         *  It is not necessary that Patch file be available, continue with PS Operations if.
+         *  failed.
+         */
+        status = 0;
+
+    } else {
+        if(NULL == firmware || firmware->size == 0) {
+            status = 0;
+        } else {
+            buffer = (A_UCHAR *)A_MALLOC(firmware->size);
+            if(buffer != NULL) {
+                /* Copy the read file to a local Dynamic buffer */
+                memcpy(buffer,firmware->data,firmware->size);
+                len = firmware->size;
+                A_RELEASE_FIRMWARE(firmware);
+                /* parse and store the Patch file contents to a global variables */
+                status = AthDoParsePatch(buffer,len);
+                A_FREE(buffer);
+            } else {
+                A_RELEASE_FIRMWARE(firmware);
+            }
+        }
+    }
+
+    /* Create an HCI command list from the parsed PS and patch information */
+    AthCreateCommandList(&HciCmdList,&numCmds);
+
+    /* Form the parameter for PSSendOps() API */
+
+    /*
+     * First Send the CRC packet, 
+     * We have to continue with the PS operations only if the CRC packet has been replied with 
+     * a Command complete event with status Error.
+     */
+
+    if(SendHCICommandWaitCommandComplete
+    (hdev,
+    HciCmdList[0].Hcipacket,
+    HciCmdList[0].packetLen,
+    &event,
+    &bufferToFree) == A_OK) {
+        if(ReadPSEvent(event) == A_OK) { /* Exit if the status is success */
+            if(bufferToFree != NULL) {
+                A_FREE(bufferToFree);
+                }
+       
+#ifndef HCI_TRANSPORT_SDIO
+                       if(bdaddr && bdaddr[0] !='\0') {
+                               write_bdaddr(hdev,bdaddr,BDADDR_TYPE_STRING);
+                       }
+#endif 
+               status = 1;
+               goto complete;
+        }
+        if(bufferToFree != NULL) {
+               A_FREE(bufferToFree);
+        }
+    } else {
+        status = 0;
+        goto complete;
+    }
+    for(i = 1; i <numCmds; i++) {
+    
+        if(SendHCICommandWaitCommandComplete
+        (hdev,
+        HciCmdList[i].Hcipacket,
+        HciCmdList[i].packetLen,
+        &event,
+        &bufferToFree) == A_OK) {
+            if(ReadPSEvent(event) != A_OK) { /* Exit if the status is success */
+                if(bufferToFree != NULL) {
+                    A_FREE(bufferToFree);
+                    }
+                   status = 1;
+                    goto complete;
+            }
+            if(bufferToFree != NULL) {
+                   A_FREE(bufferToFree);
+            }
+        } else {
+            status = 0;
+            goto complete;
+        }
+    }
+#ifdef HCI_TRANSPORT_SDIO
+       if(BDADDR == FALSE)
+               if(hdev->bdaddr[0] !=0x00 ||
+                  hdev->bdaddr[1] !=0x00 ||
+                  hdev->bdaddr[2] !=0x00 ||
+                  hdev->bdaddr[3] !=0x00 ||
+                  hdev->bdaddr[4] !=0x00 ||
+                  hdev->bdaddr[5] !=0x00)
+                       write_bdaddr(hdev,hdev->bdaddr,BDADDR_TYPE_HEX);
+
+#ifndef HCI_TRANSPORT_SDIO
+
+       if(bdaddr && bdaddr[0] != '\0') {
+               write_bdaddr(hdev,bdaddr,BDADDR_TYPE_STRING);
+       } else
+#endif /* HCI_TRANSPORT_SDIO */
+    /* Write BDADDR Read from OTP here */
+
+
+
+#endif
+
+       {
+                /* Read Contents of BDADDR file if user has not provided any option */
+        snprintf(config_path,MAX_FW_PATH_LEN, "%s%s",path,BDADDR_FILE);
+       AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Patch File Name %s\n", config_path));
+       if(A_REQUEST_FIRMWARE(&firmware,config_path,firmwareDev) < 0) {
+               AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: firmware file open error\n", __FUNCTION__ ));
+               status = 1;
+               goto complete;
+       }
+       if(NULL == firmware || firmware->size == 0) {
+               status = 1;
+               goto complete;
+       }
+        len = (firmware->size > MAX_BDADDR_FORMAT_LENGTH)? MAX_BDADDR_FORMAT_LENGTH: firmware->size;
+       memcpy(config_bdaddr, firmware->data,len);
+       config_bdaddr[len] = '\0';
+       write_bdaddr(hdev,config_bdaddr,BDADDR_TYPE_STRING);
+               A_RELEASE_FIRMWARE(firmware);
+       }
+complete:
+#ifndef HCI_TRANSPORT_SDIO
+    AthEnableSyncCommandOp(FALSE);    
+    PSTagMode = FALSE;
+    wake_up_interruptible(&PsCompleteEvent);
+#endif /* HCI_TRANSPORT_SDIO */
+    if(NULL != HciCmdList) {
+        AthFreeCommandList(&HciCmdList,numCmds);
+    }
+    if(path) {
+        A_FREE(path);
+    }
+    if(config_path) {
+        A_FREE(config_path);
+    }
+    return status;
+}
+#ifndef HCI_TRANSPORT_SDIO
+/*
+ *  This API is used to send the HCI command to controller and return
+ *  with a HCI Command Complete event.
+ *  For HCI SDIO transport, this will be internally defined. 
+ */
+A_STATUS SendHCICommandWaitCommandComplete(AR3K_CONFIG_INFO *pConfig,
+                                           A_UINT8          *pHCICommand,
+                                           int              CmdLength,
+                                           A_UINT8          **ppEventBuffer,
+                                           A_UINT8          **ppBufferToFree)
+{
+    if(CmdLength == 0) {
+        return A_ERROR;
+    }
+    Hci_log("COM Write -->",pHCICommand,CmdLength);
+    PSAcked = FALSE;
+    if(PSHciWritepacket(pConfig,pHCICommand,CmdLength) == 0) {
+        /* If the controller is not available, return Error */
+        return A_ERROR;
+    }
+    //add_timer(&psCmdTimer);
+    wait_event_interruptible(HciEvent,(PSAcked == TRUE));
+    if(NULL != HciEventpacket) {
+        *ppEventBuffer = HciEventpacket;
+        *ppBufferToFree = HciEventpacket;
+    } else {
+        /* Did not get an event from controller. return error */
+        *ppBufferToFree = NULL;
+        return A_ERROR;
+    }
+
+    return A_OK;
+}
+#endif /* HCI_TRANSPORT_SDIO */
+
+A_STATUS ReadPSEvent(A_UCHAR* Data){
+    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" PS Event %x %x %x\n",Data[4],Data[5],Data[3]));
+                                
+    if(Data[4] == 0xFC && Data[5] == 0x00)
+    {
+         switch(Data[3]){
+             case 0x0B:
+                     return A_OK;
+                 break;
+                 case 0x0C:
+                    /* Change Baudrate */
+                        return A_OK;    
+                 break;  
+                 case 0x04:
+                     return A_OK;
+                 break;  
+               case 0x1E:
+                       Rom_Version = Data[9];
+                       Rom_Version = ((Rom_Version << 8) |Data[8]);
+                       Rom_Version = ((Rom_Version << 8) |Data[7]);
+                       Rom_Version = ((Rom_Version << 8) |Data[6]);
+
+                       Build_Version = Data[13];
+                       Build_Version = ((Build_Version << 8) |Data[12]);
+                       Build_Version = ((Build_Version << 8) |Data[11]);
+                       Build_Version = ((Build_Version << 8) |Data[10]);
+                       return A_OK;
+               break;
+
+        
+                }
+    }                       
+        
+    return A_ERROR;           
+}
+int str2ba(unsigned char *str_bdaddr,unsigned char *bdaddr)
+{
+       unsigned char bdbyte[3];
+       unsigned char *str_byte = str_bdaddr;
+       int i,j;
+       unsigned char colon_present = 0;
+
+       if(NULL != strstr(str_bdaddr,":")) {
+               colon_present = 1;
+       }
+
+
+       bdbyte[2] = '\0';
+
+       for( i = 0,j = 5; i < 6; i++, j--) {
+               bdbyte[0] = str_byte[0];
+               bdbyte[1] = str_byte[1];
+               bdaddr[j] = A_STRTOL(bdbyte,NULL,16);
+               if(colon_present == 1) {
+                       str_byte+=3;
+               } else {
+                       str_byte+=2;
+               }
+       }
+       return 0; 
+}
+
+A_STATUS write_bdaddr(AR3K_CONFIG_INFO *pConfig,A_UCHAR *bdaddr,int type)
+{
+       A_UCHAR bdaddr_cmd[] = { 0x0B, 0xFC, 0x0A, 0x01, 0x01, 
+                                                       0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+    A_UINT8 *event;
+    A_UINT8 *bufferToFree = NULL;
+    A_STATUS result = A_ERROR;
+       int inc,outc;
+
+       if (type == BDADDR_TYPE_STRING)
+               str2ba(bdaddr,&bdaddr_cmd[7]);
+       else {
+               /* Bdaddr has to be sent as LAP first */
+               for(inc = 5 ,outc = 7; inc >=0; inc--, outc++)
+                       bdaddr_cmd[outc] = bdaddr[inc];
+       }
+
+    if(A_OK == SendHCICommandWaitCommandComplete(pConfig,bdaddr_cmd,
+                                                                                               sizeof(bdaddr_cmd),
+                                                                                               &event,&bufferToFree)) {
+
+        if(event[4] == 0xFC && event[5] == 0x00){
+               if(event[3] == 0x0B){
+                result = A_OK;
+            }
+        }
+
+    }
+    if(bufferToFree != NULL) {
+        A_FREE(bufferToFree);
+   }
+    return result;
+
+}
+A_STATUS ReadVersionInfo(AR3K_CONFIG_INFO *pConfig)
+{
+    A_UINT8   hciCommand[] =  {0x1E,0xfc,0x00};
+    A_UINT8 *event;
+    A_UINT8 *bufferToFree = NULL;
+    A_STATUS result = A_ERROR;
+    if(A_OK == SendHCICommandWaitCommandComplete(pConfig,hciCommand,sizeof(hciCommand),&event,&bufferToFree)) {
+       result = ReadPSEvent(event);
+
+    }
+    if(bufferToFree != NULL) {
+        A_FREE(bufferToFree);
+   }
+    return result;
+}
+A_STATUS getDeviceType(AR3K_CONFIG_INFO *pConfig, A_UINT32 * code)
+{
+    A_UINT8   hciCommand[] =  {0x05,0xfc,0x05,0x00,0x00,0x00,0x00,0x04};
+    A_UINT8 *event;
+    A_UINT8 *bufferToFree = NULL;
+    A_UINT32 reg;
+    A_STATUS result = A_ERROR;
+    *code = 0;
+    hciCommand[3] = (A_UINT8)(FPGA_REGISTER & 0xFF);
+    hciCommand[4] = (A_UINT8)((FPGA_REGISTER >> 8) & 0xFF);
+    hciCommand[5] = (A_UINT8)((FPGA_REGISTER >> 16) & 0xFF);
+    hciCommand[6] = (A_UINT8)((FPGA_REGISTER >> 24) & 0xFF); 
+    if(A_OK == SendHCICommandWaitCommandComplete(pConfig,hciCommand,sizeof(hciCommand),&event,&bufferToFree)) {
+
+        if(event[4] == 0xFC && event[5] == 0x00){
+               switch(event[3]){
+                case 0x05:
+                reg = event[9];
+                reg = ((reg << 8) |event[8]);
+                reg = ((reg << 8) |event[7]);
+                reg = ((reg << 8) |event[6]);
+                *code = reg;
+                result = A_OK;
+
+                break;
+                case 0x06:
+                    //Sleep(500);
+                break;
+            }
+        }
+
+    }
+    if(bufferToFree != NULL) {
+        A_FREE(bufferToFree);
+   }
+    return result;
+}
+
+