[APR-2987]wlbt: NAN transaction Id
Moved transaction ID handling from wifiHal to driver
Change-Id: I3e5493faf3f102b7c7547b92f641aa60e3635deb
SCSC-Bug-Id: HOST-11254
Signed-off-by: Jaya Prakash Sangaru <j.sangaru@samsung.com>
diff --git a/nan_common.h b/nan_common.h
index 44d7f77..7a75a6b 100755
--- a/nan_common.h
+++ b/nan_common.h
@@ -194,7 +194,8 @@
NAN_REQ_ATTR_SERVICE_NAME_LEN,
NAN_REQ_ATTR_SERVICE_NAME,
NAN_REQ_ATTR_NDP_RESPONSE_CODE,
- NAN_REQ_ATTR_USE_NDPE_ATTR
+ NAN_REQ_ATTR_USE_NDPE_ATTR,
+ NAN_REQ_ATTR_HAL_TRANSACTION_ID
} NAN_REQ_ATTRIBUTES;
typedef enum {
@@ -222,7 +223,8 @@
NAN_REPLY_ATTR_CAP_NDP_SECURITY_SUPPORTED,
NAN_REPLY_ATTR_CAP_MAX_SDEA_SERVICE_SPECIFIC_INFO_LEN,
NAN_REPLY_ATTR_CAP_MAX_SUBSCRIBE_ADDRESS,
- NAN_REPLY_ATTR_CAP_NDPE_ATTR_SUPPORTED
+ NAN_REPLY_ATTR_CAP_NDPE_ATTR_SUPPORTED,
+ NAN_REPLY_ATTR_HAL_TRANSACTION_ID
} NAN_RESP_ATTRIBUTES;
typedef enum {
@@ -304,7 +306,8 @@
NAN_EVT_ATTR_APP_INFO,
NAN_EVT_ATTR_CHANNEL,
NAN_EVT_ATTR_CHANNEL_BW,
- NAN_EVT_ATTR_CHANNEL_NSS
+ NAN_EVT_ATTR_CHANNEL_NSS,
+ NAN_EVT_ATTR_HAL_TRANSACTION_ID
} NAN_EVT_ATTRIBUTES;
#endif
\ No newline at end of file
diff --git a/nan_data.h b/nan_data.h
index 5cd3bd7..45c2c8d 100755
--- a/nan_data.h
+++ b/nan_data.h
@@ -12,14 +12,6 @@
int m_max_ndp_sessions;
int m_data_iface_count;
char m_ifaceName[SLSI_NAN_MAX_NDP][IFNAMSIZ+1];
- static const int idx_iface_create = 0;
- static const int idx_iface_delete = 1;
- static const int idx_ndp_initiator = 2;
- static const int idx_ndp_responder = 3;
- static const int idx_ndp_end = 4;
- static const int idx_max = 5; /* should be the end of idx_* */
- u16 transaction_id[idx_max]; /* 5 = no of reqs: */
-
nlattr *newNlVendorMsg(int subcmd, WifiRequest &request);
@@ -29,10 +21,10 @@
void dataIndicationResponseSuccess(NanDataPathIndicationResponse *msg);
void dataEndSuccess(NanDataPathEndRequest *msg);
- int dataInterfaceCreateDelete(char *ifaceName, int subcmd, WifiRequest &request);
- int dataRequestInitiate(NanDataPathInitiatorRequest *msg, WifiRequest &request);
- int dataIndicationResponse(NanDataPathIndicationResponse *msg, WifiRequest &request);
- int dataEnd(NanDataPathEndRequest *msg, WifiRequest &request);
+ int dataInterfaceCreateDelete(u16 id, char *ifaceName, int subcmd, WifiRequest &request);
+ int dataRequestInitiate(u16 id, NanDataPathInitiatorRequest *msg, WifiRequest &request);
+ int dataIndicationResponse(u16 id, NanDataPathIndicationResponse *msg, WifiRequest &request);
+ int dataEnd(u16 id, NanDataPathEndRequest *msg, WifiRequest &request);
void processNdpChannelInfo(nlattr *nl_data, NanChannelInfo &channel_info);
int processNdpReqEvent(WifiEvent &event, NanCallbackHandler &callbackEventHandler);
@@ -46,7 +38,6 @@
int getDataPathNLMsg(u16 id, void *data, int subcmd, WifiRequest &request);
void setMaxNdpSessions(int max_ndp);
int handleEvent(WifiEvent &event, NanCallbackHandler &callbackEventHandler);
- int getResponseTransactionId(NanResponseMsg *res);
static int putSecurityInfo(u32 cipher, NanSecurityKeyInfo *key_info, u32 scid_len,
u8 *scid, WifiRequest *request);
static const u8 *getCmdName(int cmd);
diff --git a/wifi_nan.cpp b/wifi_nan.cpp
index ebb0762..5867749 100755
--- a/wifi_nan.cpp
+++ b/wifi_nan.cpp
@@ -33,15 +33,6 @@
int subscribeID[2];
int publishID[2];
int followupID[2];
- transaction_id followupTid;
- transaction_id publishTid;
- transaction_id publishCancelTid;
- transaction_id subscribeTid;
- transaction_id subscribeCancelTid;
- transaction_id enableTid;
- transaction_id disableTid;
- transaction_id configTid;
- transaction_id capabilitiesTid;
int version;
NanCapabilities capabilities;
NanDataCommand datacmd;
@@ -108,6 +99,7 @@
NanCapabilities *capabilities = &response->body.nan_capabilities;
nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA);
unsigned int val;
+ transaction_id id = 0;
for(nl_iterator nl_itr(vendor_data); nl_itr.has_next(); nl_itr.next()) {
switch(nl_itr.get_type()) {
@@ -190,13 +182,16 @@
case NAN_REPLY_ATTR_CAP_NDPE_ATTR_SUPPORTED:
capabilities->ndpe_attr_supported = nl_itr.get_u32();
break;
+ case NAN_REPLY_ATTR_HAL_TRANSACTION_ID:
+ id = nl_itr.get_u16();
+ break;
default :
ALOGE("received unknown type(%d) in response", nl_itr.get_type());
- return NL_SKIP;
+ return -1;
}
}
this->capabilities = *capabilities;
- return NL_OK;
+ return id;
}
int processMatchEvent(WifiEvent &event) {
@@ -555,13 +550,13 @@
int processNanFollowupStatus(WifiEvent &event) {
NanTransmitFollowupInd ind;
memset(&ind,0,sizeof(ind));
- ind.id = followupTid;
- followupTid = 0;
nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA);
for(nl_iterator nl_itr(vendor_data); nl_itr.has_next(); nl_itr.next()) {
if (nl_itr.get_type() == NAN_EVT_ATTR_STATUS) {
ind.reason = (NanStatusType)nl_itr.get_u16();
- } else {
+ } else if(nl_itr.get_type() == NAN_EVT_ATTR_HAL_TRANSACTION_ID) {
+ ind.id = nl_itr.get_u16();
+ }else {
ALOGE("processNanFollowupStatus: unknown attribute(%d)", nl_itr.get_type());
return NL_SKIP;
}
@@ -647,16 +642,6 @@
followupID[0] = 0;
followupID[1] = 0;
- followupTid = 0;
- publishTid = 0;
- publishCancelTid = 0;
- subscribeTid = 0;
- subscribeCancelTid = 0;
- enableTid = 0;
- disableTid = 0;
- configTid = 0;
- capabilitiesTid = 0;
-
version = 0;
memset(&capabilities, 0, sizeof(capabilities));
}
@@ -777,13 +762,13 @@
CHECK_CONFIG_PUT_32_RETURN_FAIL(msg->config_ndpe_attr, msg->use_ndpe_attr,
NAN_REQ_ATTR_USE_NDPE_ATTR, request, result, "enable:Failed to put use_ndpe_attr");
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "enable:Failed to put transaction id");
+
request.attr_end(data);
registerNanEvents();
- enableTid = id;
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- enableTid = 0;
ALOGE("failed to NAN; result = %d", result);
unregisterNanEvents();
} else {
@@ -801,7 +786,14 @@
int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_NAN_DISABLE);
CHECK_WIFI_STATUS_RETURN_FAIL(result, "disable:Failed to create WifiRequest");
- disableTid = id;
+
+ nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA);
+ if (!data) {
+ ALOGE("enable: request.attr_start fail");
+ return WIFI_ERROR_OUT_OF_MEMORY;
+ }
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "disable:Failed to put transaction id");
+ request.attr_end(data);
result = requestResponse(request);
CHECK_WIFI_STATUS_RETURN_FAIL(result, "disable:Failed to requestResponse");
return result;
@@ -934,13 +926,13 @@
NAN_REQ_ATTR_DISC_MAC_ADDR_RANDOM_INTERVAL, request, result, "config:Failed to put disc_mac_addr_rand_interval_sec");
CHECK_CONFIG_PUT_32_RETURN_FAIL(msg->config_ndpe_attr, msg->use_ndpe_attr,
- NAN_REQ_ATTR_USE_NDPE_ATTR, request, result, "enable:Failed to put use_ndpe_attr");
+ NAN_REQ_ATTR_USE_NDPE_ATTR, request, result, "config:Failed to put use_ndpe_attr");
+
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "config:Failed to put transaction id");
request.attr_end(data);
- configTid = id;
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- configTid = 0;
ALOGE("failed to set_config; result = %d", result);
} else {
ALOGD("NAN config...success");
@@ -1032,7 +1024,7 @@
NAN_REQ_ATTR_PUBLISH_SDEA, request, result, "publish:Failed to put msg->sdea_service_specific_info");
result = request.put_u8(NAN_REQ_ATTR_RANGING_AUTO_RESPONSE, msg->ranging_auto_response);
- CHECK_WIFI_STATUS_RETURN_FAIL(result, "enable:Failed to put ranging_auto_response");
+ CHECK_WIFI_STATUS_RETURN_FAIL(result, "publish:Failed to put ranging_auto_response");
result = putSdeaParams(&msg->sdea_params, &request);
if (result != 0)
@@ -1047,11 +1039,11 @@
if (result != 0)
return result;
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "publish:Failed to put transaction id");
+
request.attr_end(data);
- publishTid = id;
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- publishTid = 0;
ALOGE("failed to publish; result = %d", result);
} else {
ALOGD("NAN publish...success");
@@ -1075,11 +1067,11 @@
CHECK_CONFIG_PUT_16_RETURN_FAIL(1, msg->publish_id,
NAN_REQ_ATTR_PUBLISH_ID, request, result, "publishCancel:Failed to put msg->publish_id");
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "publishCancel:Failed to put transaction id");
+
request.attr_end(data);
- publishCancelTid = id;
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- publishCancelTid = 0;
ALOGE("failed to publishCancel; result = %d", result);
} else {
ALOGD("NAN publishCancel...success");
@@ -1177,7 +1169,7 @@
NAN_REQ_ATTR_PUBLISH_SDEA, request, result, "subscribe:Failed to put msg->sdea_service_specific_info");
result = request.put_u8(NAN_REQ_ATTR_RANGING_AUTO_RESPONSE, msg->ranging_auto_response);
- CHECK_WIFI_STATUS_RETURN_FAIL(result, "enable:Failed to put ranging_auto_response");
+ CHECK_WIFI_STATUS_RETURN_FAIL(result, "subscribe:Failed to put ranging_auto_response");
result = putSdeaParams(&msg->sdea_params, &request);
if (result != 0)
@@ -1192,11 +1184,11 @@
if (result != 0)
return result;
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "subscribe:Failed to put transaction id");
+
request.attr_end(data);
- subscribeTid = id;
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- subscribeTid = 0;
ALOGE("failed to subscribe; result = %d", result);
} else {
ALOGD("NAN subscribe...success");
@@ -1221,11 +1213,11 @@
CHECK_CONFIG_PUT_16_RETURN_FAIL(1, msg->subscribe_id,
NAN_REQ_ATTR_SUBSCRIBE_ID, request, result, "subscribeCancel:Failed to put msg->subscribe_id");
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "subscribeCancel:Failed to put transaction id");
+
request.attr_end(data);
- subscribeCancelTid = id;
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- subscribeCancelTid = 0;
ALOGE("failed to subscribeCancel; result = %d", result);
} else {
ALOGD("NAN subscribeCancel...success");
@@ -1276,11 +1268,11 @@
CHECK_CONFIG_PUT_RETURN_FAIL(msg->sdea_service_specific_info_len, msg->sdea_service_specific_info, msg->sdea_service_specific_info_len,
NAN_REQ_ATTR_PUBLISH_SDEA, request, result, "publish:Failed to put msg->sdea_service_specific_info");
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "followup:Failed to put transaction id");
+
request.attr_end(data);
- followupTid = id;
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- followupTid = 0;
ALOGE("failed to followup; result = %d", result);
} else {
ALOGD("NAN followup...success");
@@ -1296,10 +1288,15 @@
int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_NAN_CAPABILITIES);
CHECK_WIFI_STATUS_RETURN_FAIL(result, "getCapabilities:Failed to create WifiRequest");
- capabilitiesTid = id;
+ nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA);
+ if (!data) {
+ ALOGE("enable: request.attr_start fail");
+ return WIFI_ERROR_OUT_OF_MEMORY;
+ }
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "getCapabilities:Failed to put transaction id");
+ request.attr_end(data);
result = requestResponse(request);
if (result != WIFI_SUCCESS) {
- capabilitiesTid = 0;
ALOGE("failed to getCapabilities; result = %d", result);
} else {
ALOGD("NAN getCapabilities...success");
@@ -1361,51 +1358,10 @@
NanResponseMsg response;
memset(&response, 0, sizeof(response));
- if (processResponse(reply, &response) == NL_SKIP)
+ transaction_id id = processResponse(reply, &response);
+ if ( id < 0)
return NL_SKIP;
- transaction_id id = 0;
- switch ((int)response.response_type) {
- case NAN_RESPONSE_PUBLISH:
- id = publishTid;
- publishTid = 0;
- break;
- case NAN_RESPONSE_ENABLED:
- id = enableTid;
- enableTid = 0;
- break;
- case NAN_RESPONSE_DISABLED:
- id = disableTid;
- disableTid = 0;
- break;
- case NAN_RESPONSE_PUBLISH_CANCEL:
- id = publishCancelTid;
- publishCancelTid = 0;
- break;
- case NAN_RESPONSE_SUBSCRIBE_CANCEL:
- id = subscribeCancelTid;
- subscribeCancelTid = 0;
- break;
- case NAN_RESPONSE_CONFIG:
- id = configTid;
- configTid = 0;
- break;
- case NAN_GET_CAPABILITIES:
- id = capabilitiesTid;
- capabilitiesTid = 0;
- break;
- case NAN_RESPONSE_SUBSCRIBE:
- id = subscribeTid;
- subscribeTid = 0;
- break;
- case NAN_RESPONSE_TRANSMIT_FOLLOWUP:
- id = followupTid;
- /* followupTid is required on receiving followup_up transmit status.
- * Do not reset followupTid here*/
- break;
- default:
- id = datacmd.getResponseTransactionId(&response);
- }
ALOGD("NAN %s transId:%d status:%d, response:%d", __func__, id, response.status, response.response_type);
if (callbackEventHandler.NotifyResponse)
callbackEventHandler.NotifyResponse(id, &response);
diff --git a/wifi_nan_data_path.cpp b/wifi_nan_data_path.cpp
index fbc48df..315ac4a 100755
--- a/wifi_nan_data_path.cpp
+++ b/wifi_nan_data_path.cpp
@@ -41,7 +41,7 @@
return data;
}
-int NanDataCommand::dataInterfaceCreateDelete(char *ifaceName, int subcmd, WifiRequest &request) {
+int NanDataCommand::dataInterfaceCreateDelete(u16 id, char *ifaceName, int subcmd, WifiRequest &request) {
int result;
nlattr *data = newNlVendorMsg(subcmd, request);
if (!data)
@@ -51,11 +51,13 @@
CHECK_WIFI_STATUS_RETURN_FAIL(result, "Failed to put ifaceName_len");
result = request.put(NAN_REQ_ATTR_DATA_INTERFACE_NAME, ifaceName, strlen(ifaceName));
CHECK_WIFI_STATUS_RETURN_FAIL(result, "Failed to put ifaceName");
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "dataInterfacecreateDelete:Failed to put transaction id");
+
request.attr_end(data);
return WIFI_SUCCESS;
}
-int NanDataCommand::dataRequestInitiate(NanDataPathInitiatorRequest* msg, WifiRequest &request) {
+int NanDataCommand::dataRequestInitiate(u16 id, NanDataPathInitiatorRequest* msg, WifiRequest &request) {
int result;
nlattr *data = newNlVendorMsg(SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_REQUEST_INITIATOR, request);
if (!data)
@@ -89,11 +91,13 @@
CHECK_WIFI_STATUS_RETURN_FAIL(result, "Failed to put req_instance_id");
}
result = putSecurityInfo(msg->cipher_type, &msg->key_info, 0, NULL, &request);
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "dataRequestInitiate:Failed to put transaction id");
+
request.attr_end(data);
return result;
}
-int NanDataCommand::dataIndicationResponse(NanDataPathIndicationResponse* msg, WifiRequest &request) {
+int NanDataCommand::dataIndicationResponse(u16 id, NanDataPathIndicationResponse* msg, WifiRequest &request) {
int result;
nlattr *data = newNlVendorMsg(SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INDICATION_RESPONSE, request);
if (!data)
@@ -123,11 +127,13 @@
CHECK_WIFI_STATUS_RETURN_FAIL(result, "Failed to put req_instance_id");
}
result = putSecurityInfo(msg->cipher_type, &msg->key_info, 0, NULL, &request);
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "dataIndicationResponse:Failed to put transaction id");
+
request.attr_end(data);
return result;
}
-int NanDataCommand::dataEnd(NanDataPathEndRequest* msg, WifiRequest &request) {
+int NanDataCommand::dataEnd(u16 id, NanDataPathEndRequest* msg, WifiRequest &request) {
int result, i;
nlattr *data = newNlVendorMsg(SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_END, request);
if (!data)
@@ -136,6 +142,7 @@
result = request.put_u32(NAN_REQ_ATTR_NDP_INSTANCE_ID, msg->ndp_instance_id[i]);
CHECK_WIFI_STATUS_RETURN_FAIL(result, "Failed to put ndp_instance_id");
}
+ CHECK_CONFIG_PUT_16_RETURN_FAIL(1, id, NAN_REQ_ATTR_HAL_TRANSACTION_ID, request, result, "dataEnd:Failed to put transaction id");
request.attr_end(data);
return result;
@@ -299,26 +306,20 @@
m_ndp_count = 0;
m_data_iface_count = 0;
m_max_ndp_sessions = 0;
- memset(transaction_id, 0, sizeof(transaction_id));
}
int NanDataCommand::getDataPathNLMsg(u16 id, void *data, int subcmd, WifiRequest &request) {
switch (subcmd) {
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INTERFACE_CREATE:
- transaction_id[idx_iface_create] = id;
- return dataInterfaceCreateDelete((char *)data, subcmd, request);
+ return dataInterfaceCreateDelete(id, (char *)data, subcmd, request);
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INTERFACE_DELETE:
- transaction_id[idx_iface_delete] = id;
- return dataInterfaceCreateDelete((char *)data, subcmd, request);
+ return dataInterfaceCreateDelete(id, (char *)data, subcmd, request);
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_REQUEST_INITIATOR:
- transaction_id[idx_ndp_initiator] = id;
- return dataRequestInitiate((NanDataPathInitiatorRequest *)data, request);
+ return dataRequestInitiate(id, (NanDataPathInitiatorRequest *)data, request);
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_INDICATION_RESPONSE:
- transaction_id[idx_ndp_responder] = id;
- return dataIndicationResponse((NanDataPathIndicationResponse *)data, request);
+ return dataIndicationResponse(id, (NanDataPathIndicationResponse *)data, request);
case SLSI_NL80211_VENDOR_SUBCMD_NAN_DATA_END:
- transaction_id[idx_ndp_end] = id;
- return dataEnd((NanDataPathEndRequest *)data, request);
+ return dataEnd(id, (NanDataPathEndRequest *)data, request);
default:
ALOGE("unknown subcmd :0x%x", subcmd);
}
@@ -359,35 +360,6 @@
}
}
-int NanDataCommand::getResponseTransactionId(NanResponseMsg *res) {
- u16 id;
- switch(res->response_type) {
- case NAN_DP_INTERFACE_CREATE:
- id = transaction_id[idx_iface_create];
- transaction_id[idx_iface_create] = 0;
- break;
- case NAN_DP_INTERFACE_DELETE:
- id = transaction_id[idx_iface_delete];
- transaction_id[idx_iface_delete] = 0;
- break;
- case NAN_DP_INITIATOR_RESPONSE:
- id = transaction_id[idx_ndp_initiator];
- transaction_id[idx_ndp_initiator] = 0;
- break;
- case NAN_DP_RESPONDER_RESPONSE:
- id = transaction_id[idx_ndp_responder];
- transaction_id[idx_ndp_responder] = 0;
- break;
- case NAN_DP_END:
- id = transaction_id[idx_ndp_end];
- transaction_id[idx_ndp_end] = 0;
- break;
- default:
- id = 0;
- }
- return id;
-}
-
void NanDataCommand::setMaxNdpSessions(int max_ndp) {
m_max_ndp_sessions = max_ndp > SLSI_NAN_MAX_NDP ? SLSI_NAN_MAX_NDP : max_ndp;
}
@@ -440,4 +412,5 @@
return (const u8 *)"UNKNOWN CMD";
}
return (const u8 *)"UNKNOWN CMD";
-}
\ No newline at end of file
+}
+