![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
external fota service implementation
Revision 0:bab3be8d31cf, committed 2015-06-19
- Comitter:
- dudnwjs
- Date:
- Fri Jun 19 06:49:20 2015 +0000
- Commit message:
- add GAPM,GAPC header
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/blemsg_handler.cpp Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,78 @@ +#include "blemsg_handler.h" + + +BleMsg_Handler::BleMsg_Handler(void) +{ +} + + +BleMsg_Handler::~BleMsg_Handler(void) +{ +} + + +void BleMsg_Handler::HandleBleMsg(uint8_t *msg) +{ + ble_hdr msg_hdr; + memcpy(msg_hdr, &msg[1], sizeof(msg_hdr)); + + if( msg_hdr.bDstid != TASK_GTL ) + return; + + switch( msg_hdr.bType ) + { + case GAPM_CMP_EVT: + HadleGapmCmpEvt(msg); + break; + // Event triggered to inform that lower layers are ready + case GAPM_DEVICE_READY_IND: + gapm_device_ready_ind_handler(msg); + break; + case GAPM_ADV_REPORT_IND: + gapm_adv_report_ind_handler(msg); + break; + + case GAPC_CMP_EVT: + HadleGapcCmpEvt(msg); + break; + // Indicate that a connection has been established + case GAPC_CONNECTION_REQ_IND: + gapc_connection_req_ind_handler(msg); + break; + // Indicate that a link has been disconnected + case GAPC_DISCONNECT_IND: + gapc_disconnect_ind_handler(msg); + break; + case GAPC_CON_RSSI_IND: + gapc_con_rssi_ind_handler(msg); + break; + case GAPC_BOND_REQ_IND: + gapc_bond_req_ind_handler(msg); + break; + // Bonding information indication message + case GAPC_BOND_IND: + gapc_bond_ind_handler(msg); + break; + // Encryption requested by peer device indication message. + case GAPC_ENCRYPT_REQ_IND: + gapc_encrypt_req_ind_handler(msg); + break; + // Encryption information indication message + case GAPC_ENCRYPT_IND: + gapc_encrypt_ind_handler(msg); + break; + + case DISS_CREATE_DB_CFM: + diss_create_db_cfm_handler(msg); + break; + case DISS_DISABLE_IND: + break; + case DISS_ERROR_IND: + break; + + default: + break; + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/blemsg_handler.h Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,26 @@ +#ifndef __BLEMSG_HANDLER_H__ +#define __BLEMSG_HANDLER_H__ + +typedef struct { + unsigned short bType; + unsigned short bDstid; + unsigned short bSrcid; + unsigned short bLength; +} ble_hdr; + +class BleMsg_Handler +{ +public: + + BleMsg_Handler(void); + ~BleMsg_Handler(void); + void HandleBleMsg(uint8_t *msg); + void HandleGapmCmpEvt(unsigned short msgid, struct gapm_cmp_evt *param, unsigned short dest_id, unsigned short src_id); + +private: + + +}; + + +#endif //__BLEMSG_HANDLER_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/gapc_task.h Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,97 @@ +#ifndef __GAPC_TASK_H__ +#define __GAPC_TASK_H__ + +enum gapc_msg_id +{ + /* Default event */ + /// Command Complete event + GAPC_CMP_EVT = 14336, + + /* Connection state information */ + /// Indicate that a connection has been established + GAPC_CONNECTION_REQ_IND, + /// Set specific link data configuration. + GAPC_CONNECTION_CFM, + + /// Indicate that a link has been disconnected + GAPC_DISCONNECT_IND, + + /* Link management command */ + /// Request disconnection of current link command. + GAPC_DISCONNECT_CMD, + + /* Peer device info */ + /// Retrieve information command + GAPC_GET_INFO_CMD, + /// Name of peer device indication + GAPC_PEER_NAME_IND, + /// Indication of peer version info + GAPC_PEER_VERSION_IND, + /// Indication of peer features info + GAPC_PEER_FEATURES_IND, + + /// Indication of ongoing connection RSSI + GAPC_CON_RSSI_IND, + /// Indication of peer privacy info + GAPC_PRIVACY_IND, + /// Indication of peer reconnection address info + GAPC_RECON_ADDR_IND, + + /* Privacy configuration */ + /// Set Privacy flag command. + GAPC_SET_PRIVACY_CMD, + /// Set Reconnection Address Value command. + GAPC_SET_RECON_ADDR_CMD, + + /* Connection parameters update */ + /// Perform update of connection parameters command + GAPC_PARAM_UPDATE_CMD, + /// Request of updating connection parameters indication + GAPC_PARAM_UPDATE_REQ_IND, + /// Master confirm or not that parameters proposed by slave are accepted or not + GAPC_PARAM_UPDATE_CFM, + /// Connection parameters updated indication + GAPC_PARAM_UPDATED_IND, + + /* Bonding procedure */ + /// Start Bonding command procedure + GAPC_BOND_CMD, + /// Bonding requested by peer device indication message. + GAPC_BOND_REQ_IND, + /// Confirm requested bond information. + GAPC_BOND_CFM, + /// Bonding information indication message + GAPC_BOND_IND, + + /* Encryption procedure */ + /// Start Encryption command procedure + GAPC_ENCRYPT_CMD, + /// Encryption requested by peer device indication message. + GAPC_ENCRYPT_REQ_IND, + /// Confirm requested Encryption information. + GAPC_ENCRYPT_CFM, + /// Encryption information indication message + GAPC_ENCRYPT_IND, + + /* Security request procedure */ + /// Start Security Request command procedure + GAPC_SECURITY_CMD, + /// Security requested by peer device indication message + GAPC_SECURITY_IND, + + /* Signature procedure */ + /// Indicate the current sign counters to the application + GAPC_SIGN_COUNTER_IND, + + /* Device information */ + /// Indication of ongoing connection Channel Map + GAPC_CON_CHANNEL_MAP_IND, + + + /* Internal messages for timer events, not part of API*/ + /// Parameter update procedure timeout indication + GAPC_PARAM_UPDATE_TO_IND, + +}; + +#endif //__GAPC_TASK_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/gapm_task.h Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,108 @@ +#ifndef __GAPM_TASK_H__ +#define __GAPM_TASK_H__ + +/// Command complete event data structure +struct gapm_cmp_evt +{ + /// GAP requested operation + uint8_t operation; + /// Status of the request + uint8_t status; +}; + +enum gapm_msg_id +{ + /* Default event */ + /// Command Complete event + GAPM_CMP_EVT = 13312, + /// Event triggered to inform that lower layers are ready + GAPM_DEVICE_READY_IND, + + /* Default commands */ + /// Reset link layer and the host command + GAPM_RESET_CMD, + /// Cancel ongoing operation + GAPM_CANCEL_CMD, + + /* Device Configuration */ + /// Set device configuration command + GAPM_SET_DEV_CONFIG_CMD, + /// Set device name command + GAPM_SET_DEV_NAME_CMD, + /// Set device channel map + GAPM_SET_CHANNEL_MAP_CMD, + + /* Local device information */ + /// Get local device info command + GAPM_GET_DEV_INFO_CMD, + /// Local device name indication event + GAPM_DEV_NAME_IND, + /// Local device appearance indication event + GAPM_APPEARANCE_IND, + /// Local device version indication event + GAPM_DEV_VERSION_IND, + /// Local device BD Address indication event + GAPM_DEV_BDADDR_IND, + + /* White List */ + /// White List Management Command + GAPM_WHITE_LIST_MGT_CMD, + /// White List Size indication event + GAPM_WHITE_LIST_SIZE_IND, + + /* Air Operations */ + /// Set advertising mode Command + GAPM_START_ADVERTISE_CMD, + + /// Set Scan mode Command + GAPM_START_SCAN_CMD, + /// Advertising or scanning report information event + GAPM_ADV_REPORT_IND, + + /// Set connection initialization Command + GAPM_START_CONNECTION_CMD, + /// Name of peer device indication + GAPM_PEER_NAME_IND, + /// Confirm connection to a specific device (Connection Operation in Selective mode) + GAPM_CONNECTION_CFM, + + /* Privacy update events */ + /// Privacy flag value has been updated + GAPM_UPDATED_PRIVACY_IND, + /// Reconnection address has been updated + GAPM_UPDATED_RECON_ADDR_IND, + + /* Security / Encryption Toolbox */ + /// Resolve address command + GAPM_RESOLV_ADDR_CMD, + /// Indicate that resolvable random address has been solved + GAPM_ADDR_SOLVED_IND, + /// Generate a random address. + GAPM_GEN_RAND_ADDR_CMD, + /// Use the AES-128 block in the controller + GAPM_USE_ENC_BLOCK_CMD, + /// AES-128 block result indication + GAPM_USE_ENC_BLOCK_IND, + /// Generate a 8-byte random number + GAPM_GEN_RAND_NB_CMD, + /// Random Number Indication + GAPM_GEN_RAND_NB_IND, + + /* Debug */ + /// Indication containing information about memory usage. + GAPM_DBG_MEM_INFO_IND, + + /* Local device information -cont */ + /// Advertising channel Tx power level + GAPM_DEV_ADV_TX_POWER_IND, + + /* Internal messages for timer events, not part of API*/ + /// Limited discoverable timeout indication + GAPM_LIM_DISC_TO_IND, + /// Scan timeout indication + GAPM_SCAN_TO_IND, + /// Address renewal timeout indication + GAPM_ADDR_RENEW_TO_IND, +}; + +#endif //__GAPM_TASK_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/msg_queue.cpp Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,62 @@ +#include "msg_queue.h" +#include "mbed.h" + +Msg_Queue::Msg_Queue(int MaxSize):MaxNum(MaxSize) +{ + Front = NULL; + Rear = NULL; + ElemCnt = 0; +} + +Msg_Queue::~Msg_Queue(void) +{ +} + +void Msg_Queue::EnQueue(void *vData) +{ + Element *tmp = new Element; + tmp->Data = vData; + tmp->Next = NULL; + ElemCnt++; + if( Front == NULL) + { + Front = tmp; + Rear = tmp; + }else + { + Rear->Next = tmp; + Rear = tmp; + } +} + +void *Msg_Queue::DeQueue(void) +{ + void *tmp; + Element *tmpElem; + + if(Front == NULL) + return NULL; + + ElemCnt--; + tmpElem = Front; + Front = tmpElem->Next; + tmp = tmpElem->Data; + free(tmpElem); + + return tmp; +} + +int Msg_Queue::GetElementCount(void) +{ + return ElemCnt; +} + +bool Msg_Queue::IsEmpty(void) +{ + if( Front == NULL) + return false; + else + return true; +} + + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/msg_queue.h Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,30 @@ +#ifndef __MSG_QUEUE_H__ +#define __MSG_QUEUE_H__ + +struct Element +{ + void *Data; + struct Element *Next; +}; + +class Msg_Queue +{ +public: + + Msg_Queue(int MaxSize = 512); + ~Msg_Queue(void); + void EnQueue(void *vData); + void *DeQueue(void); + int GetElementCount(void); + bool IsEmpty(void); + +private: + Element *Front; + Element *Rear; + const int MaxNum; + int ElemCnt; + +}; + + +#endif //__MSG_QUEUE_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/serial_communicator.cpp Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,196 @@ +#include "serial_communicator.h" + +Serial_Communicator::Serial_Communicator(Serial* _device) +{ + debug_flag = 0; + device = _device; + queue = new Msg_Queue(512); + FE_MSG_PACKET_TYPE = 0x05; + MAX_PACKET_LENGTH = 350; + bReceiveState = 0; + wDataLength = 0; + wReceivePos = 0; + bHdrBytesRead = 0; + memset(bReceiveElementArr,0,512); +} + +Serial_Communicator::Serial_Communicator(Serial* _device, Serial* _hostpc) +{ + debug_flag = 1; + device = _device; + hostpc = _hostpc; + queue = new Msg_Queue(512); + FE_MSG_PACKET_TYPE = 0x05; + MAX_PACKET_LENGTH = 350; + bReceiveState = 0; + wDataLength = 0; + wReceivePos = 0; + bHdrBytesRead = 0; + memset(bReceiveElementArr,0,512); +} + +Serial_Communicator::~Serial_Communicator(void) +{ + free(queue); +} + +void Serial_Communicator::print_title() +{ + if( debug_flag == 1) + hostpc->printf("FOTA CLASS START!\n"); +} + +void Serial_Communicator::BleMsgAlloc( unsigned short id, + unsigned short dest_id, + unsigned short src_id, + unsigned short data_len, + void *pdata, + uint8_t *msg ) +{ + memset(msg,0,sizeof(msg)); + msg[0] = 0x05; + memcpy(&msg[1],&id,sizeof(unsigned short)); + memcpy(&msg[1+1*sizeof(unsigned short)],&dest_id,sizeof(unsigned short)); + memcpy(&msg[1+2*sizeof(unsigned short)],&src_id,sizeof(unsigned short)); + memcpy(&msg[1+3*sizeof(unsigned short)],&data_len,sizeof(unsigned short)); + memcpy(&msg[1+4*sizeof(unsigned short)],pdata,data_len); +} + +int Serial_Communicator::BleSendMsg(uint8_t *msg,unsigned short size) +{ + int cnt = 0; + if(debug_flag == 1) + hostpc->printf("send size = %hu\n",size); + + for(int i=0; i < size; i++) + { + if( device->writeable()) + { + if(debug_flag == 1) + hostpc->printf("%02X ", msg[i]); + device->putc(msg[i]); + cnt++; + } + } + return cnt; +} + +void Serial_Communicator::BleMsgEnQueue(uint8_t *msg) +{ + queue->EnQueue((void *)msg); +} + +uint8_t *Serial_Communicator::BleMsgDeQueue(void) +{ + return (uint8_t *)queue->DeQueue(); +} + +bool Serial_Communicator::IsQueueEmpty(void) +{ + return queue->IsEmpty(); +} + +void Serial_Communicator::BleReceiveMsg(void) +{ + unsigned char tmp; + + + //while(true) + //{ + if(device->readable()) + { + tmp = device->getc(); + switch(bReceiveState) + { + case 0: + if( tmp == FE_MSG_PACKET_TYPE ) + { + bReceiveState = 1; + wDataLength = 0; + wReceivePos = 0; + bHdrBytesRead = 0; + + bReceiveElementArr[wReceivePos] = tmp; + wReceivePos++; + if(debug_flag == 1) + hostpc->printf("\n[Receiver] Packet Type |: %02X \n", tmp); + }else + { + if(debug_flag == 1) + hostpc->printf("\n[Receiver] Packet Type Error |: %02X \n", tmp); + } + break; + case 1: + if(debug_flag == 1) + hostpc->printf("R-%02X ",tmp); + bHdrBytesRead++; + bReceiveElementArr[wReceivePos] = tmp; + wReceivePos++; + if( bHdrBytesRead == 6 ) + bReceiveState = 2; + break; + case 2: + if(debug_flag == 1) + hostpc->printf("R-%02X ",tmp); + wDataLength += tmp; + if( wDataLength > MAX_PACKET_LENGTH ) + bReceiveState = 0; + else + { + bReceiveElementArr[wReceivePos] = tmp; + wReceivePos++; + bReceiveState = 3; + } + break; + case 3: + if( debug_flag == 1 ) + hostpc->printf("R-%02X ",tmp); + wDataLength += (unsigned short) (tmp*256); + if( wDataLength > MAX_PACKET_LENGTH ) + { + if( debug_flag == 1 ) + hostpc->printf("\n[Receiver] Over SIZE: %d ", wDataLength); + bReceiveState = 0; + }else if (wDataLength == 0) + { + if( debug_flag == 1 ) + hostpc->printf("\n[Receiver] Zero SIZE: %d ", wDataLength); + uint8_t *msg; + msg = new uint8_t[wReceivePos]; + memcpy(msg,bReceiveElementArr,wReceivePos); + BleMsgEnQueue(msg); + bReceiveState = 0; + }else + { + bReceiveElementArr[wReceivePos] = tmp; + wReceivePos++; + bReceiveState = 4; + } + break; + case 4: + if( debug_flag == 1 ) + hostpc->printf("R-%02X ",tmp); + bReceiveElementArr[wReceivePos] = tmp; + wReceivePos++; + // 9 = 1(first byte = FE_MSG_PACKET_TYPE) + 2(type) +2(dstid) +2(srcid) +2(length size) + if(wReceivePos == wDataLength + 9) + { + uint8_t *msg; + msg = new uint8_t[wReceivePos]; + memcpy(msg,bReceiveElementArr,wReceivePos); + BleMsgEnQueue(msg); + bReceiveState = 0; + if( debug_flag == 1 ) + hostpc->printf("\n[Receiver] Rcv Data SIZE: %d ", wDataLength); + } + break; + default: + if(debug_flag == 1) + hostpc->printf("ERROR STRAGE STATE\n"); + break; + } + } + //} +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext_fota/serial_communicator.h Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,37 @@ +#include "mbed.h" +#include "msg_queue.h" + + +class Serial_Communicator +{ +public: + + Serial_Communicator(Serial* _device); + Serial_Communicator(Serial* _device, Serial* _hostpc); + ~Serial_Communicator(void); + + void print_title(void); + + void BleMsgAlloc(unsigned short id,unsigned short dest_id, unsigned short src_id, unsigned short data_len,void *pdata, uint8_t *msg); + int BleSendMsg(uint8_t *msg,unsigned short size); + void BleMsgEnQueue(uint8_t *msg); + uint8_t *BleMsgDeQueue(void); + void BleReceiveMsg(void); + bool IsQueueEmpty(void); + + +private: + bool debug_flag; + Serial *hostpc; + Serial *device; + Msg_Queue *queue; + uint8_t FE_MSG_PACKET_TYPE; + int MAX_PACKET_LENGTH; + unsigned char bReceiveState; + unsigned short wDataLength; + unsigned short wReceivePos; + unsigned char bHdrBytesRead; + unsigned char bReceiveElementArr[512]; + +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,81 @@ +#include "mbed.h" +#include "serial_communicator.h" +#include "mbed_fota_config.h" + + +Serial hostpc(USBTX,USBRX); +Serial device(p13,p14); + +DigitalOut led1(LED1); + +void func1() +{ + while(1) + { + wait(1); + led1 = !led1; + } +} + +int main() { + Serial_Communicator Fota_comm(&device, &hostpc); + // Serial Setting + hostpc.baud(115200); + device.baud(115200); + + // print title + Fota_comm.print_title(); + uint8_t *a,*c; + uint8_t b[1]; + b[0] = 0x01; + unsigned short array_size = 1+sizeof(ble_hdr)+sizeof(gapm_reset_cmd); + a = new uint8_t[array_size]; + hostpc.printf("msg size = %hu\n", array_size); + Fota_comm.BleMsgAlloc(GAPM_RESET_CMD,TASK_GAPM,TASK_GTL,1,b,a); + + ble_hdr test; + gapm_reset_cmd data_test; + + memcpy(&test,&a[1],sizeof(ble_hdr)); + memcpy(&data_test,&a[1+sizeof(ble_hdr)],sizeof(gapm_reset_cmd)); + + hostpc.printf("packet_type = 0x%02X\n", a[0]); + hostpc.printf("id = %hu, dst = %hu, src = %hu, len = %hu\n", test.bType,test.bDstid,test.bSrcid,test.bLength); + hostpc.printf("data = %02X\n",data_test.operation); + + hostpc.printf("send cnt = %d \n",Fota_comm.BleSendMsg(a,array_size)); + + Fota_comm.BleMsgEnQueue(a); + c = Fota_comm.BleMsgDeQueue(); + + memcpy(&test,&c[1],sizeof(ble_hdr)); + memcpy(&data_test,&c[1+sizeof(ble_hdr)],sizeof(gapm_reset_cmd)); + + hostpc.printf("#c packet_type = 0x%02X\n", c[0]); + hostpc.printf("#c id = %hu, dst = %hu, src = %hu, len = %hu\n", test.bType,test.bDstid,test.bSrcid,test.bLength); + hostpc.printf("#c data = %02X\n",data_test.operation); + + free(c); + free(a); + + while(Fota_comm.IsQueueEmpty() == false) + { + Fota_comm.BleReceiveMsg(); + } + c = Fota_comm.BleMsgDeQueue(); + + memcpy(&test,&c[1],sizeof(ble_hdr)); + memcpy(&data_test,&c[1+sizeof(ble_hdr)],sizeof(gapm_reset_cmd)); + + hostpc.printf("#receive packet_type = 0x%02X\n", c[0]); + hostpc.printf("#receive id = %hu, dst = %hu, src = %hu, len = %hu\n", test.bType,test.bDstid,test.bSrcid,test.bLength); + hostpc.printf("#receive data = %02X\n",data_test.operation); + + free(c); + while(1) + { + func1(); + //serial receive process + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/7cff1c4259d7 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_fota_config.h Fri Jun 19 06:49:20 2015 +0000 @@ -0,0 +1,85 @@ +/** + **************************************************************************************** + * + * @file mbed_fota_config.h + * + * @brief SevenCore Firmware Over The Air Service for External Processor Configuration Code + * + * Copyright (C) 2015 SevenCore and its Affiliates, unpublished work + * This computer program includes Confidential, Proprietary Information and is a Trade Secret + * of SevenCore and its Affiliates. All use, disclosure, and/or reproduction is prohibited + * unless authorized in writing. All Rights Reserved. + * + **************************************************************************************** +*/ + +#ifndef MBED_FOTA_CONFIG_H +#define MBED_FOTA_CONFIG_H + +/* + * INCLUDE FILES + **************************************************************************************** + */ + + +/* + * ENUMERATIONS + **************************************************************************************** + */ + + + /* + * DEFINES + **************************************************************************************** + */ + +#define GAPM_CMP_EVT 13312 +#define GAPM_DEVICE_READY_IND 13313 +#define GAPM_RESET_CMD 13314 +#define TASK_GAPM 13 +#define TASK_GTL 63 +#define GAPM_RESET 0x01 +#define TASK_GTL 63 +#define FE_MSG_PACKET_TYPE 0x05 +#define MAX_PACKET_LENGTH 350 +#define MIN_PACKET_LENGTH 9 + + + + +typedef struct { + unsigned short bType; + unsigned short bDstid; + unsigned short bSrcid; + unsigned short bLength; +} ble_hdr; + + + typedef struct { + unsigned short bType; + unsigned short bDstid; + unsigned short bSrcid; + unsigned short bLength; + unsigned char bData[1]; +} ble_msg; + +typedef uint16_t ke_msg_id_t; +typedef uint8_t ke_state_t; +typedef uint16_t ke_task_id_t; + +/// Reset link layer and the host command +struct gapm_reset_cmd +{ + /// GAPM requested operation: + /// - GAPM_RESET: Reset BLE subsystem: LL and HL. + uint8_t operation; +}; + + + /* + * GLOBAL VALUE + **************************************************************************************** + */ + + +#endif //MEBED_FOTA_CONFIG_H \ No newline at end of file