#include <stdio.h>
#include <stdbool.h>
#include <string.h>
+#include <sys/stat.h>
#include <bluetooth-api.h>
#include "bluetooth.h"
#define BT_OTP_FEATURE_LENGTH 8
#define BT_ADDR_LENGTH 18
-#define BT_OTC_CONNECTION_MAX_TIMEOUT 30000 /* Timeout for OTC Connection in msec */
+#define BT_OTC_CONNECTION_MAX_TIMEOUT 10000 /* Timeout for OTC Connection in msec */
#define BT_L2CAP_BUFFER_LEN 672
#define BT_OTP_IS_OACP_SUPPORTED(feature) feature & 0xffffffff00000000
#define BT_OTP_IS_OLCP_SUPPORTED(feature) feature & 0x00000000ffffffff
#define BT_OTP_IS_OACP_READ_SUPPORTED(feature) feature & 0x0800000000000000
+#define BT_OTP_IS_OACP_CREATE_SUPPORTED(feature) feature & 0x8000000000000000
#define BT_OTP_IS_READ_PERMITTED(props) props & OBJECT_READ
+/* OTP Object Type Custom UUIDs */
+/* In SIG Assigned numbers not available */
+#define UNSUPPORTED_OBJECT_TYPE_UUID "7fb0"
+#define FIRMWARE_UUID "7fb1"
+#define ROUTE_GPX_UUID "7fb2"
+#define TRACK_GPX_UUID "7fb3"
+
typedef enum {
BT_OTP_NO_OPERATION = 0,
BT_OTP_OBJECT_DISCOVERY,
BT_OTP_OBJECT_READ,
BT_OTP_OBJECT_SELECT,
+ BT_OTP_OBJECT_CREATE,
} bt_otp_api_info_e;
typedef struct {
FILE *fp;
} bt_otp_client_read_op;
+typedef struct {
+ char *filename;
+ char *type_uuid;
+ time_t first_created;
+ uint64_t id;
+ uint32_t size;
+} bt_otp_client_create_op;
+
static bool is_otp_server_initialized = false;
+bt_otp_client_create_op *oacp_create_op = NULL;
bt_otp_client_read_op *oacp_read_op = NULL;
bool otc_connection_status = FALSE;
bt_otp_object_list_s *obj_list;
void __bt_otp_reset_api_info(bt_otp_client_s *otp_client_s);
static void _bt_otp_client_send_select_object_callback(int result,
bt_otp_client_s *otp_client_s);
+static void _bt_otp_send_create_object_callback(int result,
+ uint64_t id, bt_otp_client_s *otp_client_s);
int __bt_check_otp_server_init_status(void)
{
g_free(obj_list);
obj_list = NULL;
}
+
+ if (oacp_read_op) {
+ fclose(oacp_read_op->fp);
+ g_free(oacp_read_op->file_path);
+ g_free(oacp_read_op);
+ oacp_read_op = NULL;
+ }
+
+ if (oacp_create_op) {
+ g_free(oacp_create_op->filename);
+ g_free(oacp_create_op->type_uuid);
+ g_free(oacp_create_op);
+ oacp_create_op = NULL;
+ }
}
int bt_otp_client_set_connection_state_changed_cb(bt_otp_client_h otp_client,
BT_ERR("Failed to write control point : %s(0x%08x)", _bt_convert_error_to_string(error_code), error_code);
goto read_fail;
}
+ } else if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) {
+ metadata = g_malloc0(sizeof(object_metadata));
+ metadata->name = g_strdup(oacp_create_op->filename);
+ metadata->type = g_strdup(oacp_create_op->type_uuid);
+ metadata->curr_size = oacp_create_op->size;
+ metadata->alloc_size = oacp_create_op->size;
+ /* Dont hard code */
+ metadata->props = OBJECT_READ | OBJECT_WRITE;
+ metadata->first_created = oacp_create_op->first_created;
+ metadata->last_modified = oacp_create_op->first_created;
+ metadata->id = id;
+ otp_client_s->object_id = metadata->id;
+ otp_client_s->object_list = g_slist_append(otp_client_s->object_list, metadata);
}
}
read_fail:
__bt_otp_reset_api_info(otp_client_s);
}
}
+ if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) {
+ _bt_otp_send_create_object_callback(result,
+ otp_client_s->object_id, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+ }
}
}
if (result != BLUETOOTH_ERROR_NONE) {
BT_DBG("OTP Write Failed");
- if (otp_client_s->curr_op == BT_OTP_OBJECT_DISCOVERY) {
- _bt_otp_send_discovery_callback(result, otp_client_s);
- __bt_otp_reset_api_info(otp_client_s);
- } else if (otp_client_s->curr_op == BT_OTP_OBJECT_READ) {
- _bt_otp_client_notify_read_object_status(result,
- NULL, otp_client_s);
- __bt_otp_reset_api_info(otp_client_s);
- } else if (otp_client_s->curr_op == BT_OTP_OBJECT_SELECT) {
- _bt_otp_client_send_select_object_callback(result, otp_client_s);
- __bt_otp_reset_api_info(otp_client_s);
+ goto fail;
+ }
+
+ if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) {
+ if ((!g_strcmp0(otp_client_s->otp_name_obj_path, handle) ||
+ !g_strcmp0(otp_client_s->otp_first_created_obj_path, handle)) &&
+ oacp_create_op) {
+ struct tm fc_tm;
+ uint8_t value[8];
+ int error_code;
+ memset(value, 0, 8);
+ localtime_r(&(oacp_create_op->first_created), &fc_tm);
+
+ value[1] = ((fc_tm.tm_year+1900) >> 8) & 0xFF;
+ value[0] = (fc_tm.tm_year+1900) & 0xFF;
+ value[2] = (fc_tm.tm_mon+1) & 0xFF;
+ value[3] = fc_tm.tm_mday & 0xFF;
+ value[4] = fc_tm.tm_hour & 0xFF;
+ value[5] = fc_tm.tm_min & 0xFF;
+ value[6] = fc_tm.tm_sec & 0xFF;
+
+ if (!g_strcmp0(otp_client_s->otp_name_obj_path, handle)) {
+ /* Write First-Created */
+ error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_first_created_obj_path,
+ value, sizeof(value));
+ } else {
+ /* Write Last-Modified */
+ error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_last_modified_obj_path,
+ value, sizeof(value));
+ }
+
+ if (error_code != BT_ERROR_NONE) {
+ BT_ERR("Failed to write first_created / last_modified : %s(0x%08x)",
+ _bt_convert_error_to_string(error_code), error_code);
+ result = error_code;
+ goto fail;
+ }
+ } else if (!g_strcmp0(otp_client_s->otp_last_modified_obj_path, handle)) {
+ /* Read Object ID */
+ int error_code;
+ error_code = bluetooth_otp_read_characteristic_value(otp_client_s->otp_id_obj_path);
+ if (error_code != BLUETOOTH_ERROR_NONE) {
+ BT_INFO("Read object ID Failed %s(0x%08x)", _bt_convert_error_to_string(error_code), error_code);
+ result = error_code;
+ goto fail;
+ }
}
}
+ return;
+fail:
+ switch (otp_client_s->curr_op) {
+ case BT_OTP_OBJECT_DISCOVERY:
+ _bt_otp_send_discovery_callback(result, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+ break;
+ case BT_OTP_OBJECT_READ:
+ _bt_otp_client_notify_read_object_status(result,
+ NULL, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+ break;
+ case BT_OTP_OBJECT_SELECT:
+ _bt_otp_client_send_select_object_callback(result, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+ break;
+ case BT_OTP_OBJECT_CREATE:
+ _bt_otp_send_create_object_callback(result, 0, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+ break;
+ case BT_OTP_NO_OPERATION:
+ break;
+ }
}
void _bt_otp_client_notification_enabled(int result, char *handle)
switch (req_opcode) {
case OACP_CREATE:
+ BT_INFO("OACP_CREATE Indication received");
+ if ((otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) && oacp_create_op) {
+ int len = strlen(oacp_create_op->filename);
+ uint8_t *value = (uint8_t *)malloc(len * sizeof(uint8_t));
+ memcpy(value, oacp_create_op->filename, len);
+ error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_name_obj_path,
+ value, len);
+ if (error_code != BT_ERROR_NONE) {
+ BT_ERR("Failed to write object name : %s(0x%08x)",
+ _bt_convert_error_to_string(error_code), error_code);
+ result = error_code;
+ goto oacp_done;
+ }
+ }
+ break;
case OACP_DELETE:
case OACP_CALC_CHECKSUM:
case OACP_EXECUTE:
if (otp_client_s->curr_op == BT_OTP_OBJECT_READ) {
_bt_otp_client_notify_read_object_status(result, NULL, otp_client_s);
__bt_otp_reset_api_info(otp_client_s);
+ } else if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) {
+ _bt_otp_send_create_object_callback(result, 0, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
}
} else if (!g_strcmp0(otp_client_s->otp_olcp_control_point, info->handle)) {
uint8_t resp_code = info->data[0];
return error_code;
}
+
+static void _bt_otp_send_create_object_callback(int result,
+ uint64_t id, bt_otp_client_s *otp_client_s)
+{
+
+ ((bt_otp_client_object_create_cb)otp_client_s->callback)(
+ result, otp_client_s->remote_address,
+ id, otp_client_s->user_data);
+
+ if (oacp_create_op) {
+ g_free(oacp_create_op->filename);
+ g_free(oacp_create_op->type_uuid);
+ g_free(oacp_create_op);
+ oacp_create_op = NULL;
+ }
+}
+
+int bt_otp_client_create_object(bt_otp_client_h otp_client,
+ const char *file_path,
+ bt_otp_client_object_create_cb callback,
+ void *user_data)
+{
+ int error_code;
+ bt_otp_client_s *otp_client_s = (bt_otp_client_s *)otp_client;
+ struct stat st;
+ uint32_t size;
+ char *file_name, *last_token;
+ time_t curr_time;
+ char *type_uuid;
+
+ BT_CHECK_LE_SUPPORT();
+ BT_CHECK_INIT_STATUS();
+ BT_CHECK_INPUT_PARAMETER(otp_client_s);
+
+ if (_bt_otp_client_find(otp_client_s->remote_address) == NULL)
+ return BT_ERROR_NOT_INITIALIZED;
+
+ if (otp_client_s->connected == false) {
+ BT_ERR("Remote device [%s] is not conencted", otp_client_s->remote_address);
+ return BT_ERROR_OPERATION_FAILED;
+ }
+
+ if (otp_client_s->curr_op != BT_OTP_NO_OPERATION) {
+ BT_DBG("OTP Client is busy");
+ return BT_ERROR_OPERATION_FAILED;
+ }
+
+ if (~BT_OTP_IS_OACP_CREATE_SUPPORTED(otp_client_s->otp_feature)) {
+ BT_INFO("OACP Create not supported");
+ return BT_ERROR_OPERATION_FAILED;
+ }
+
+ otp_client_s->callback = callback;
+ otp_client_s->user_data = user_data;
+ otp_client_s->curr_op = BT_OTP_OBJECT_CREATE;
+
+ BT_DBG("OTP client create object [%s] in Remote device [%s]",
+ file_path, otp_client_s->remote_address);
+
+ /* Get file_name & size from file_path */
+ if (stat(file_path, &st) == -1) {
+ BT_INFO("stat failed: %s (%d)\n", strerror(errno), errno);
+ return BLUETOOTH_ERROR_INTERNAL;
+ }
+
+ size = (uint32_t)st.st_size;
+ last_token = strrchr(file_path, '/');
+ file_name = last_token + 1;
+
+ BT_INFO("Filepath [%s], Filename [%s], Size[%llu]\n",
+ file_path, file_name, size);
+
+ oacp_create_op = g_malloc0(sizeof(bt_otp_client_create_op));
+ oacp_create_op->size = size;
+ oacp_create_op->filename = g_strdup(file_name);
+
+ time(&curr_time);
+ oacp_create_op->first_created = curr_time;
+
+ /* There isn't clear description for Object Type in spec */
+ type_uuid = g_strdup(UNSUPPORTED_OBJECT_TYPE_UUID);
+ oacp_create_op->type_uuid = g_strdup(type_uuid);
+
+ /* UUIDs can be 128/64/16 bits */
+ uint8_t value[40] = {0x00};
+ value[0] = OACP_CREATE;
+ value[1] = size & 0xFF;
+ value[2] = (size >> 8) & 0xFF;
+ value[3] = (size >> 16) & 0xFF;
+ value[4] = (size >> 24) & 0xFF;
+
+ memcpy(value + 5, type_uuid, strlen(type_uuid));
+
+ error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_oacp_control_point,
+ value, 5 + strlen(type_uuid));
+ if (error_code != BT_ERROR_NONE) {
+ BT_ERR("Failed to write control point : %s(0x%08x)",
+ _bt_convert_error_to_string(error_code), error_code);
+ __bt_otp_reset_api_info(otp_client_s);
+ }
+ g_free(type_uuid);
+ return error_code;
+}
, BT_UNIT_TEST_FUNCTION_OTP_CLIENT_SELECT_OBJ},
{"bt_otp_client_read_object_contents"
, BT_UNIT_TEST_FUNCTION_OTP_CLIENT_READ_OBJ_CONTENTS},
+ {"bt_otp_client_create_object"
+ , BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ},
{NULL , 0x0000},
};
BT_ERR("Read object failed!");
}
+static void __bt_otp_client_object_create_cb(int result, const char *remote_address,
+ unsigned long long obj_id, void *user_data)
+{
+ if (result == BT_ERROR_NONE) {
+ TC_PRT("Object[%llu] created successfully in remote server!", obj_id);
+ } else
+ BT_ERR("Object creation failed!");
+}
+
static void __bt_initialize_all(void)
{
int ret;
break;
}
+ case BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ: {
+ if (param_index == 0) {
+ g_test_param.param_count = 1;
+ g_test_param.params = g_malloc0(sizeof(char *) *g_test_param.param_count);
+ }
+
+ if (param_index > 0) {
+ int len = strlen(param);
+ g_test_param.params[param_index - 1] = g_malloc0(len + 1);
+ /* Remove new line character */
+ param[len - 1] = '\0';
+ strncpy(g_test_param.params[param_index - 1], param, strlen(param));
+ }
+
+ if (param_index == g_test_param.param_count) {
+ need_to_set_params = false;
+#ifdef ARCH64
+ test_input_callback((void *)(uintptr_t)test_id);
+#else
+ test_input_callback((void *)test_id);
+#endif
+ param_index = 0;
+ return 0;
+ }
+
+ switch (param_index) {
+ case 0:
+ TC_PRT("Input Value in string");
+ break;
+ }
+
+ param_index++;
+
+ break;
+ }
default:
TC_PRT("There is no param to set\n");
need_to_set_params = false;
}
break;
}
+ case BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ: {
+ char *file_path = NULL;
+ if (g_test_param.param_count < 1) {
+ TC_PRT("Input parameters first");
+ break;
+ }
+
+ file_path = g_test_param.params[0];
+ TC_PRT("%s", file_path);
+
+ if (otp_client) {
+ ret = bt_otp_client_create_object(otp_client,
+ file_path, __bt_otp_client_object_create_cb, NULL);
+ if (ret < BT_ERROR_NONE)
+ TC_PRT("failed with [0x%04x]", ret);
+ else if (ret == BT_ERROR_NONE)
+ TC_PRT("Success");
+ }
+ __bt_free_test_param(&g_test_param);
+ break;
+ }
case BT_UNIT_TEST_FUNCTION_ACTIVATE_FLAG_TO_SET_PARAMETERS:
need_to_set_params = true;
TC_PRT("Select the function again");