blob: 0e298dba9fc80cc3d54c304e8e77cd674a21dbf4 [file] [log] [blame]
/*
* 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;
}