Kojto CC3000 Hostdriver With Mbed Socket interface
Dependents: WiFiDip-KitchenSink WiFiDip-UsbKitchenSink WiFiDipCortexSensor WifiDipCortex-UDPDemo
Fork of cc3000_hostdriver_mbedsocket by
Diff: cc3000_wlan.cpp
- Revision:
- 0:615c697c33b0
- Child:
- 21:30b6ed7bf8fd
diff -r 000000000000 -r 615c697c33b0 cc3000_wlan.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cc3000_wlan.cpp Thu Sep 19 07:55:14 2013 +0000 @@ -0,0 +1,699 @@ +/***************************************************************************** +* +* C++ interface/implementation created by Martin Kojtal (0xc0170). Thanks to +* Jim Carver and Frank Vannieuwkerke for their inital cc3000 mbed port and +* provided help. +* +* This version of "host driver" uses CC3000 Host Driver Implementation. Thus +* read the following copyright: +* +* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the +* distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*****************************************************************************/ +#include "cc3000.h" + +namespace mbed_cc3000 { + +cc3000_wlan::cc3000_wlan(cc3000_simple_link &simple_link, cc3000_event &event, cc3000_spi &spi, cc3000_hci &hci) : + _simple_link(simple_link), _event(event), _spi(spi), _hci(hci) { + +} + +cc3000_wlan::~cc3000_wlan() { + +} + +void cc3000_wlan::simpleLink_init_start(uint16_t patches_available_host) { + uint8_t *ptr; + uint8_t *args; + + ptr = _simple_link.get_transmit_buffer(); + args = (uint8_t *)(ptr + HEADERS_SIZE_CMD); + + UINT8_TO_STREAM(args, ((patches_available_host) ? SL_PATCHES_REQUEST_FORCE_HOST : SL_PATCHES_REQUEST_DEFAULT)); + + // IRQ Line asserted - send HCI_CMND_SIMPLE_LINK_START to CC3000 + _hci.command_send(HCI_CMND_SIMPLE_LINK_START, ptr, WLAN_SL_INIT_START_PARAMS_LEN); + _event.simplelink_wait_event(HCI_CMND_SIMPLE_LINK_START, 0); +} + +void cc3000_wlan::start(uint16_t patches_available_host) { + uint32_t spi_irq_state; + + _simple_link.set_sent_packets(0); + _simple_link.set_number_of_released_packets(0); + _simple_link.set_op_code(0); + _simple_link.set_number_free_buffers(0); + _simple_link.set_buffer_length(0); + _simple_link.set_buffer_size(0); + _simple_link.set_pending_data(0); + _simple_link.set_transmit_error(0); + _simple_link.set_data_received_flag(0); + _simple_link.set_buffer_size(0); + + // init spi + _spi.open(); + // Check the IRQ line + spi_irq_state = _spi.wlan_irq_read(); + // ASIC 1273 chip enable: toggle WLAN EN line + _spi.write_wlan_en(WLAN_ENABLE); + + if (spi_irq_state) + { + // wait till the IRQ line goes low + while(_spi.wlan_irq_read() != 0) + { + } + } + else + { + // wait till the IRQ line goes high and then low + while(_spi.wlan_irq_read() == 0) + { + } + while(_spi.wlan_irq_read() != 0) + { + } + } + simpleLink_init_start(patches_available_host); + + // Read Buffer's size and finish + _hci.command_send(HCI_CMND_READ_BUFFER_SIZE, _simple_link.get_transmit_buffer(), 0); + _event.simplelink_wait_event(HCI_CMND_READ_BUFFER_SIZE, 0); +} + + +void cc3000_wlan::stop() { + // ASIC 1273 chip disable + _spi.write_wlan_en( WLAN_DISABLE ); + + // Wait till IRQ line goes high... + while(_spi.wlan_irq_read() == 0) + { + } + + // Free the used by WLAN Driver memory + if (_simple_link.get_transmit_buffer()) + { + _simple_link.set_transmit_buffer(0); + } + + _spi.close(); +} + + +int32_t cc3000_wlan::disconnect() { + int32_t ret; + uint8_t *ptr; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + + _hci.command_send(HCI_CMND_WLAN_DISCONNECT, ptr, 0); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_DISCONNECT, &ret); + errno = ret; + + return(ret); +} + + +int32_t cc3000_wlan::ioctl_set_connection_policy(uint32_t should_connect_to_open_ap, + uint32_t use_fast_connect, + uint32_t use_profiles) { + int32_t ret; + uint8_t *ptr; + uint8_t *args; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + args = (uint8_t *)(ptr + HEADERS_SIZE_CMD); + + // Fill in HCI packet structure + args = UINT32_TO_STREAM(args, should_connect_to_open_ap); + args = UINT32_TO_STREAM(args, use_fast_connect); + args = UINT32_TO_STREAM(args, use_profiles); + + // Initiate a HCI command + _hci.command_send(HCI_CMND_WLAN_IOCTL_SET_CONNECTION_POLICY, ptr, WLAN_SET_CONNECTION_POLICY_PARAMS_LEN); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_SET_CONNECTION_POLICY, &ret); + + return(ret); +} + + +int32_t cc3000_wlan::ioctl_del_profile(uint32_t index) { + int32_t ret; + uint8_t *ptr; + uint8_t *args; + + ptr = _simple_link.get_transmit_buffer(); + args = (uint8_t *)(ptr + HEADERS_SIZE_CMD); + + // Fill in HCI packet structure + args = UINT32_TO_STREAM(args, index); + ret = EFAIL; + + // Initiate a HCI command + _hci.command_send(HCI_CMND_WLAN_IOCTL_DEL_PROFILE, ptr, WLAN_DEL_PROFILE_PARAMS_LEN); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_DEL_PROFILE, &ret); + + return(ret); +} + +int32_t cc3000_wlan::set_event_mask(uint32_t mask) { + int32_t ret; + uint8_t *ptr; + uint8_t *args; + + + if ((mask & HCI_EVNT_WLAN_TX_COMPLETE) == HCI_EVNT_WLAN_TX_COMPLETE) + { + _simple_link.set_tx_complete_signal(0); + + // Since an event is a virtual event - i.e. it is not coming from CC3000 + // there is no need to send anything to the device if it was an only event + if (mask == HCI_EVNT_WLAN_TX_COMPLETE) + { + return 0; + } + + mask &= ~HCI_EVNT_WLAN_TX_COMPLETE; + mask |= HCI_EVNT_WLAN_UNSOL_BASE; + } + else + { + _simple_link.set_tx_complete_signal(1); + } + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + args = (uint8_t *)(ptr + HEADERS_SIZE_CMD); + + // Fill in HCI packet structure + args = UINT32_TO_STREAM(args, mask); + + // Initiate a HCI command + _hci.command_send(HCI_CMND_EVENT_MASK, ptr, WLAN_SET_MASK_PARAMS_LEN); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_EVENT_MASK, &ret); + + return(ret); +} + + +int32_t cc3000_wlan::smart_config_start(uint32_t encrypted_flag) { + int32_t ret; + uint8_t *ptr; + uint8_t *args; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + args = (uint8_t *)(ptr + HEADERS_SIZE_CMD); + + // Fill in HCI packet structure + args = UINT32_TO_STREAM(args, encrypted_flag); + ret = EFAIL; + + _hci.command_send(HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_START, ptr, WLAN_SMART_CONFIG_START_PARAMS_LEN); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_START, &ret); + + return(ret); +} + + +int32_t cc3000_wlan::smart_config_stop(void) { + int32_t ret; + uint8_t *ptr; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + + _hci.command_send(HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_STOP, ptr, 0); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_STOP, &ret); + + return(ret); +} + +int32_t cc3000_wlan::smart_config_set_prefix(uint8_t *new_prefix) { + int32_t ret; + uint8_t *ptr; + uint8_t *args; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + args = (ptr + HEADERS_SIZE_CMD); + + if (new_prefix == NULL) + return ret; + else // with the new Smart Config, prefix must be TTT + { + *new_prefix = 'T'; + *(new_prefix + 1) = 'T'; + *(new_prefix + 2) = 'T'; + } + + ARRAY_TO_STREAM(args, new_prefix, SL_SIMPLE_CONFIG_PREFIX_LENGTH); + + _hci.command_send(HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_SET_PREFIX, ptr, SL_SIMPLE_CONFIG_PREFIX_LENGTH); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_SIMPLE_CONFIG_SET_PREFIX, &ret); + + return(ret); +} + +#ifndef CC3000_TINY_DRIVER +int32_t cc3000_wlan::connect(uint32_t sec_type, const uint8_t *ssid, int32_t ssid_len, uint8_t *bssid, + uint8_t *key, int32_t key_len) { + int32_t ret; + uint8_t *ptr; + uint8_t *args; + uint8_t bssid_zero[] = {0, 0, 0, 0, 0, 0}; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + args = (ptr + HEADERS_SIZE_CMD); + + // Fill in command buffer + args = UINT32_TO_STREAM(args, 0x0000001c); + args = UINT32_TO_STREAM(args, ssid_len); + args = UINT32_TO_STREAM(args, sec_type); + args = UINT32_TO_STREAM(args, 0x00000010 + ssid_len); + args = UINT32_TO_STREAM(args, key_len); + args = UINT16_TO_STREAM(args, 0); + + // padding shall be zeroed + if(bssid) + { + ARRAY_TO_STREAM(args, bssid, ETH_ALEN); + } + else + { + ARRAY_TO_STREAM(args, bssid_zero, ETH_ALEN); + } + + ARRAY_TO_STREAM(args, ssid, ssid_len); + + if(key_len && key) + { + ARRAY_TO_STREAM(args, key, key_len); + } + + // Initiate a HCI command + _hci.command_send(HCI_CMND_WLAN_CONNECT, ptr, WLAN_CONNECT_PARAM_LEN + ssid_len + key_len - 1); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_CONNECT, &ret); + errno = ret; + + return(ret); +} + +int32_t cc3000_wlan::add_profile(uint32_t sec_type, + uint8_t* ssid, + uint32_t ssid_length, + uint8_t *b_ssid, + uint32_t priority, + uint32_t pairwise_cipher_or_tx_key_len, + uint32_t group_cipher_tx_key_index, + uint32_t key_mgmt, + uint8_t* pf_or_key, + uint32_t pass_phrase_len) { + uint16_t arg_len = 0x00; + int32_t ret; + uint8_t *ptr; + int32_t i = 0; + uint8_t *args; + uint8_t bssid_zero[] = {0, 0, 0, 0, 0, 0}; + + ptr = _simple_link.get_transmit_buffer(); + args = (ptr + HEADERS_SIZE_CMD); + + args = UINT32_TO_STREAM(args, sec_type); + + // Setup arguments in accordance with the security type + switch (sec_type) + { + //OPEN + case WLAN_SEC_UNSEC: + { + args = UINT32_TO_STREAM(args, 0x00000014); + args = UINT32_TO_STREAM(args, ssid_length); + args = UINT16_TO_STREAM(args, 0); + if(b_ssid) + { + ARRAY_TO_STREAM(args, b_ssid, ETH_ALEN); + } + else + { + ARRAY_TO_STREAM(args, bssid_zero, ETH_ALEN); + } + args = UINT32_TO_STREAM(args, priority); + ARRAY_TO_STREAM(args, ssid, ssid_length); + + arg_len = WLAN_ADD_PROFILE_NOSEC_PARAM_LEN + ssid_length; + } + break; + + //WEP + case WLAN_SEC_WEP: + { + args = UINT32_TO_STREAM(args, 0x00000020); + args = UINT32_TO_STREAM(args, ssid_length); + args = UINT16_TO_STREAM(args, 0); + if(b_ssid) + { + ARRAY_TO_STREAM(args, b_ssid, ETH_ALEN); + } + else + { + ARRAY_TO_STREAM(args, bssid_zero, ETH_ALEN); + } + args = UINT32_TO_STREAM(args, priority); + args = UINT32_TO_STREAM(args, 0x0000000C + ssid_length); + args = UINT32_TO_STREAM(args, pairwise_cipher_or_tx_key_len); + args = UINT32_TO_STREAM(args, group_cipher_tx_key_index); + ARRAY_TO_STREAM(args, ssid, ssid_length); + + for(i = 0; i < 4; i++) + { + uint8_t *p = &pf_or_key[i * pairwise_cipher_or_tx_key_len]; + + ARRAY_TO_STREAM(args, p, pairwise_cipher_or_tx_key_len); + } + + arg_len = WLAN_ADD_PROFILE_WEP_PARAM_LEN + ssid_length + + pairwise_cipher_or_tx_key_len * 4; + + } + break; + + //WPA + //WPA2 + case WLAN_SEC_WPA: + case WLAN_SEC_WPA2: + { + args = UINT32_TO_STREAM(args, 0x00000028); + args = UINT32_TO_STREAM(args, ssid_length); + args = UINT16_TO_STREAM(args, 0); + if(b_ssid) + { + ARRAY_TO_STREAM(args, b_ssid, ETH_ALEN); + } + else + { + ARRAY_TO_STREAM(args, bssid_zero, ETH_ALEN); + } + args = UINT32_TO_STREAM(args, priority); + args = UINT32_TO_STREAM(args, pairwise_cipher_or_tx_key_len); + args = UINT32_TO_STREAM(args, group_cipher_tx_key_index); + args = UINT32_TO_STREAM(args, key_mgmt); + args = UINT32_TO_STREAM(args, 0x00000008 + ssid_length); + args = UINT32_TO_STREAM(args, pass_phrase_len); + ARRAY_TO_STREAM(args, ssid, ssid_length); + ARRAY_TO_STREAM(args, pf_or_key, pass_phrase_len); + + arg_len = WLAN_ADD_PROFILE_WPA_PARAM_LEN + ssid_length + pass_phrase_len; + } + + break; + } + + // Initiate a HCI command + _hci.command_send(HCI_CMND_WLAN_IOCTL_ADD_PROFILE, ptr, arg_len); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_ADD_PROFILE, &ret); + + return(ret); +} + +int32_t cc3000_wlan::ioctl_get_scan_results(uint32_t scan_timeout, uint8_t *results) { + uint8_t *ptr; + uint8_t *args; + + ptr = _simple_link.get_transmit_buffer(); + args = (ptr + HEADERS_SIZE_CMD); + + // Fill in temporary command buffer + args = UINT32_TO_STREAM(args, scan_timeout); + + // Initiate a HCI command + _hci.command_send(HCI_CMND_WLAN_IOCTL_GET_SCAN_RESULTS, ptr, WLAN_GET_SCAN_RESULTS_PARAMS_LEN); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_GET_SCAN_RESULTS, results); + + return(0); +} + +int32_t cc3000_wlan::ioctl_set_scan_params(uint32_t enable, + uint32_t min_dwell_time, + uint32_t max_dwell_time, + uint32_t num_probe_requests, + uint32_t channel_mask, + int32_t rssi_threshold, + uint32_t snr_threshold, + uint32_t default_tx_power, + uint32_t *interval_list) { + uint32_t uiRes; + uint8_t *ptr; + uint8_t *args; + + ptr = _simple_link.get_transmit_buffer(); + args = (ptr + HEADERS_SIZE_CMD); + + // Fill in temporary command buffer + args = UINT32_TO_STREAM(args, 36); + args = UINT32_TO_STREAM(args, enable); + args = UINT32_TO_STREAM(args, min_dwell_time); + args = UINT32_TO_STREAM(args, max_dwell_time); + args = UINT32_TO_STREAM(args, num_probe_requests); + args = UINT32_TO_STREAM(args, channel_mask); + args = UINT32_TO_STREAM(args, rssi_threshold); + args = UINT32_TO_STREAM(args, snr_threshold); + args = UINT32_TO_STREAM(args, default_tx_power); + ARRAY_TO_STREAM(args, interval_list, sizeof(uint32_t) * SL_SET_SCAN_PARAMS_INTERVAL_LIST_SIZE); + + // Initiate a HCI command + _hci.command_send(HCI_CMND_WLAN_IOCTL_SET_SCANPARAM, ptr, WLAN_SET_SCAN_PARAMS_LEN); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_SET_SCANPARAM, &uiRes); + + return(uiRes); +} + +int32_t cc3000_wlan::ioctl_statusget(void) { + int32_t ret; + uint8_t *ptr; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + + _hci.command_send(HCI_CMND_WLAN_IOCTL_STATUSGET,ptr, 0); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_IOCTL_STATUSGET, &ret); + + return(ret); +} + +#else +int32_t cc3000_wlan::wlan_add_profile(uint32_t sec_type, + uint8_t *ssid, + uint32_t ssid_length, + uint8_t *b_ssid, + uint32_t priority, + uint32_t pairwise_cipher_or_tx_key_len, + uint32_t group_cipher_tx_key_index, + uint32_t key_mgmt, + uint8_t* pf_or_key, + uint32_t pass_phrase_length) +{ + return -1; +} + +int32_t cc3000_wlan::connect(const uint8_t *ssid, int32_t ssid_len) { + int32_t ret; + uint8_t *ptr; + uint8_t *args; + uint8_t bssid_zero[] = {0, 0, 0, 0, 0, 0}; + + ret = EFAIL; + ptr = _simple_link.get_transmit_buffer(); + args = (ptr + HEADERS_SIZE_CMD); + + // Fill in command buffer + args = UINT32_TO_STREAM(args, 0x0000001c); + args = UINT32_TO_STREAM(args, ssid_len); + args = UINT32_TO_STREAM(args, 0); + args = UINT32_TO_STREAM(args, 0x00000010 + ssid_len); + args = UINT32_TO_STREAM(args, 0); + args = UINT16_TO_STREAM(args, 0); + + // padding shall be zeroed + ARRAY_TO_STREAM(args, bssid_zero, ETH_ALEN); + ARRAY_TO_STREAM(args, ssid, ssid_len); + + // Initiate a HCI command + _hci.command_send(HCI_CMND_WLAN_CONNECT, ptr, WLAN_CONNECT_PARAM_LEN + ssid_len - 1); + + // Wait for command complete event + _event.simplelink_wait_event(HCI_CMND_WLAN_CONNECT, &ret); + errno = ret; + + return(ret); +} +#endif + + + +#ifndef CC3000_UNENCRYPTED_SMART_CONFIG +int32_t cc3000_wlan::smart_config_process(void) { + int32_t returnValue; + uint32_t ssidLen, keyLen; + uint8_t *decKeyPtr; + uint8_t *ssidPtr; + + // read the key from EEPROM - fileID 12 + returnValue = aes_read_key(key); + + if (returnValue != 0) + return returnValue; + + // read the received data from fileID #13 and parse it according to the followings: + // 1) SSID LEN - not encrypted + // 2) SSID - not encrypted + // 3) KEY LEN - not encrypted. always 32 bytes long + // 4) Security type - not encrypted + // 5) KEY - encrypted together with true key length as the first byte in KEY + // to elaborate, there are two corner cases: + // 1) the KEY is 32 bytes long. In this case, the first byte does not represent KEY length + // 2) the KEY is 31 bytes long. In this case, the first byte represent KEY length and equals 31 + returnValue = nvmem_read(NVMEM_SHARED_MEM_FILEID, SMART_CONFIG_PROFILE_SIZE, 0, profileArray); + + if (returnValue != 0) + return returnValue; + + ssidPtr = &profileArray[1]; + + ssidLen = profileArray[0]; + + decKeyPtr = &profileArray[profileArray[0] + 3]; + + aes_decrypt(decKeyPtr, key); + if (profileArray[profileArray[0] + 1] > 16) + aes_decrypt((uint8_t *)(decKeyPtr + 16), key); + + if (*(uint8_t *)(decKeyPtr +31) != 0) + { + if (*decKeyPtr == 31) + { + keyLen = 31; + decKeyPtr++; + } + else + { + keyLen = 32; + } + } + else + { + keyLen = *decKeyPtr; + decKeyPtr++; + } + + // add a profile + switch (profileArray[profileArray[0] + 2]) + { + case WLAN_SEC_UNSEC://None + { + returnValue = wlan_add_profile(profileArray[profileArray[0] + 2], // security type + ssidPtr, // SSID + ssidLen, // SSID length + NULL, // BSSID + 1, // Priority + 0, 0, 0, 0, 0); + + break; + } + + case WLAN_SEC_WEP://WEP + { + returnValue = wlan_add_profile(profileArray[profileArray[0] + 2], // security type + ssidPtr, // SSID + ssidLen, // SSID length + NULL, // BSSID + 1, // Priority + keyLen, // KEY length + 0, // KEY index + 0, + decKeyPtr, // KEY + 0); + + break; + } + + case WLAN_SEC_WPA: //WPA + case WLAN_SEC_WPA2: //WPA2 + { + returnValue = wlan_add_profile(WLAN_SEC_WPA2, // security type + ssidPtr, + ssidLen, + NULL, // BSSID + 1, // Priority + 0x18, // PairwiseCipher + 0x1e, // GroupCipher + 2, // KEY management + decKeyPtr, // KEY + keyLen); // KEY length + + break; + } + } + + return returnValue; +} +#endif + +}