#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_OACP_WRITE_SUPPORTED(feature) feature & 0x0400000000000000
#define BT_OTP_IS_READ_PERMITTED(props) props & OBJECT_READ
BT_OTP_OBJECT_READ,
BT_OTP_OBJECT_SELECT,
BT_OTP_OBJECT_CREATE,
+ BT_OTP_OBJECT_WRITE,
} bt_otp_api_info_e;
typedef struct {
char *filename;
char *type_uuid;
time_t first_created;
- uint64_t id;
uint32_t size;
} bt_otp_client_create_op;
+typedef struct {
+ char *file_path;
+ time_t last_modified;
+ uint32_t size;
+ uint32_t offset;
+ uint32_t length;
+ uint8_t mode;
+ FILE *fp;
+} bt_otp_client_write_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;
+bt_otp_client_write_op *oacp_write_op = NULL;
bool otc_connection_status = FALSE;
bt_otp_object_list_s *obj_list;
uint64_t select_obj_id = 0;
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,
+static void _bt_otp_client_send_create_object_callback(int result,
uint64_t id, bt_otp_client_s *otp_client_s);
+static void _bt_otp_client_send_write_object_callback(int result,
+ unsigned int length, bt_otp_client_s *otp_client_s);
int __bt_check_otp_server_init_status(void)
{
BT_DBG("OTP Object Properties handle [%s]", otp_client_s->otp_props_obj_path);
} else {
- BT_DBG("Other OTP Characteristic handle [%s]", g_strdup(characteristic.handle));
- BT_DBG("UUID [%s]", g_strdup(characteristic.uuid));
+ BT_DBG("Other OTP Characteristic handle [%s]", characteristic.handle);
+ BT_DBG("UUID [%s]", characteristic.uuid);
} /* Control Point characteristic */
} /* Characteristic property get successful */
bluetooth_gatt_free_char_property(&characteristic);
/* Update all serv & charc */
error_code = __bt_update_otp_server_data(&addr_hex, otp_client_s);
if (error_code != BLUETOOTH_ERROR_NONE) {
+ __bt_otp_client_reset_server_data(otp_client_s);
g_free(otp_client_s);
return BT_ERROR_OPERATION_FAILED;
}
otp_client_s->remote_address = g_strdup(remote_address);
if (otp_client_s->remote_address == NULL) {
- free(otp_client_s);
- error_code = BT_ERROR_OUT_OF_MEMORY;
- BT_ERR("%s(0x%08x)", _bt_convert_error_to_string(error_code), error_code);
- return error_code;
+ BT_ERR("%s(0x%08x)", _bt_convert_error_to_string(error_code),
+ error_code);
+ __bt_otp_client_reset_server_data(otp_client_s);
+ g_free(otp_client_s);
+ return BT_ERROR_OUT_OF_MEMORY;
}
otp_client_s->otp_olcp_control_point = NULL;
}
obj_list = g_malloc0(sizeof(bt_otp_object_list_s));
obj_list->num_objects = g_slist_length(otp_client_s->object_list);
- obj_list->data = (otp_object_metadata_s **)g_malloc0(sizeof(otp_object_metadata_s)*obj_list->num_objects);
+ obj_list->data = (otp_object_metadata_s **)g_malloc0(sizeof(otp_object_metadata_s *)*obj_list->num_objects);
k = 0;
for (l = otp_client_s->object_list; l; l = g_slist_next(l)) {
metadata = l->data;
otp_object_metadata_s *m_data = g_malloc0(sizeof(otp_object_metadata_s));
m_data->id = metadata->id;
m_data->name = g_strdup(metadata->name);
+ obj_list->data[k] = (otp_object_metadata_s *)g_malloc0(sizeof(otp_object_metadata_s));
obj_list->data[k] = m_data;
k++;
}
BT_DBG("OTP client Read Value Response [%d] [%s] [%s] [%d]", result,
char_path, value, len);
- if (result != BLUETOOTH_ERROR_NONE) {
- BT_INFO("Read failed for [%s]", char_path);
- goto read_fail;
- }
otp_client_s = _bt_otp_client_find(remote_address);
if (otp_client_s == NULL)
return;
+ if (result != BLUETOOTH_ERROR_NONE) {
+ BT_INFO("Read failed for [%s]", char_path);
+ goto read_fail;
+ }
+
if (!g_strcmp0(otp_client_s->otp_feature_obj_path, char_path)) {
uint64_t feature = (uint64_t)(value[0] & 0xFF) << 56 |
(uint64_t)(value[1] & 0xFF) << 48 |
}
}
if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) {
- _bt_otp_send_create_object_callback(result,
+ _bt_otp_client_send_create_object_callback(result,
otp_client_s->object_id, otp_client_s);
__bt_otp_reset_api_info(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_client_send_create_object_callback(result, 0, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+ break;
+ case BT_OTP_OBJECT_WRITE:
+ _bt_otp_client_send_write_object_callback(result, 0, otp_client_s);
__bt_otp_reset_api_info(otp_client_s);
break;
case BT_OTP_NO_OPERATION:
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));
+ int len;
+ uint8_t value[BT_FILE_PATH_MAX_LEN];
+
+ len = strlen(oacp_create_op->filename) + 1;
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) {
case OACP_EXECUTE:
break;
case OACP_READ:
- BT_INFO("OACP_READ Indication Response received");
+ case OACP_WRITE:
+ BT_INFO("OACP Read/Write Indication received");
bluetooth_device_address_t addr_hex = { {0,} };
_bt_convert_address_to_hex(&addr_hex, otp_client_s->remote_address);
if (otc_connection_status) {
goto oacp_done;
}
break;
- case OACP_WRITE:
case OACP_ABORT:
break;
default:
_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_client_send_create_object_callback(result, 0, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+ } else if (otp_client_s->curr_op == BT_OTP_OBJECT_WRITE) {
+ _bt_otp_client_send_write_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)) {
if (otp_client_s->object_id == id) {
BT_INFO("Object already selected");
+ _bt_otp_client_send_select_object_callback(BLUETOOTH_ERROR_NONE, otp_client_s);
return BLUETOOTH_ERROR_NONE;
}
if (otp_client_s->curr_op != BT_OTP_NO_OPERATION) {
object_info = _bt_otp_client_find_object(otp_client_s->object_list, obj_id);
if (!object_info) {
BT_INFO("Object Not Found [%llu]", obj_id);
- return TRUE;
+ goto fail;
}
snprintf(file_path, sizeof(file_path), "%s%s",
fp = fopen(file_path, "w");
if (!fp) {
BT_DBG("fopen() failed : %s", strerror(errno));
- return TRUE;
+ goto fail;
}
oacp_read_op = g_malloc0(sizeof(bt_otp_client_read_op));
timeout_id = g_timeout_add(BT_OTC_CONNECTION_MAX_TIMEOUT,
(GSourceFunc)__bt_otc_connection_timeout_cb, (gpointer)otc_info);
}
-
+fail:
g_free(buffer);
return TRUE;
}
file_path, otp_client_s->user_data);
}
+static void _bt_otp_client_write_on_fd(bluetooth_otc_info_t *otc_info)
+{
+ char buf[BT_L2CAP_BUFFER_LEN];
+ int written, read, len;
+ FILE *fp = NULL;
+ int length;
+ unsigned int byteswritten = 0;
+ int fd = otc_info->fd;
+ char *address = otc_info->address;
+ int result = BLUETOOTH_ERROR_NONE;
+ bt_otp_client_s *otp_client_s = NULL;
+ bluetooth_device_address_t addr_hex = { {0,} };
+ struct tm fc_tm;
+ uint8_t value[7];
+
+ if (!oacp_write_op) {
+ result = BLUETOOTH_ERROR_INTERNAL;
+ goto fail;
+ }
+
+ BT_DBG("file_path = [%s]", oacp_write_op->file_path);
+
+ fp = fopen(oacp_write_op->file_path, "r");
+ if (!fp) {
+ BT_DBG("fopen() failed : %s", strerror(errno));
+ result = BLUETOOTH_ERROR_INTERNAL;
+ goto fail;
+ }
+
+ if (oacp_write_op->offset > 0)
+ fseek(fp, oacp_write_op->offset, SEEK_SET);
+
+ BT_DBG("length [%d]", oacp_write_op->length);
+ length = oacp_write_op->length;
+
+ while (length > 0) {
+ if (length < BT_L2CAP_BUFFER_LEN)
+ len = length;
+ else
+ len = BT_L2CAP_BUFFER_LEN;
+
+ read = fread(buf, 1, len, fp);
+ written = write(fd, buf, len);
+
+ if (written < 0) {
+ result = BLUETOOTH_ERROR_INTERNAL;
+ goto fail;
+ }
+
+ length -= written;
+ byteswritten += written;
+
+ BT_DBG("read [%d], written [%d], rem_len [%d]",
+ read, written, length);
+ }
+
+fail:
+ if (fp)
+ fclose(fp);
+
+ /* Disconnect and notify application */
+ _bt_convert_address_to_hex(&addr_hex, address);
+ bluetooth_otp_disconnect_otc(&addr_hex);
+
+ otp_client_s = _bt_otp_client_find(address);
+
+ if (result == BLUETOOTH_ERROR_NONE) {
+ /* Write Last-modified */
+ memset(value, 0, 7);
+ localtime_r(&(oacp_write_op->last_modified), &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;
+ result = bluetooth_otp_write_characteristics_value(otp_client_s->otp_last_modified_obj_path,
+ value, 7);
+ }
+
+ _bt_otp_client_send_write_object_callback(result,
+ byteswritten, otp_client_s);
+ __bt_otp_reset_api_info(otp_client_s);
+}
+
void _bt_otc_connection_state_changed(int result, bluetooth_otc_info_t *otc_info)
{
GIOChannel *data_io;
- char *file_path = NULL;
+ char file_path[BT_FILE_PATH_MAX_LEN];
+ bt_otp_client_s *otp_client_s = NULL;
+
+ otp_client_s = _bt_otp_client_find(otc_info->address);
BT_DBG("OTC Channel status [%d], fd [%d]", otc_info->connected, otc_info->fd);
if (oacp_read_op) {
if (!otc_info->connected)
- file_path = g_strdup(oacp_read_op->file_path);
+ memcpy(file_path, oacp_read_op->file_path,
+ strlen(oacp_read_op->file_path) + 1);
/*
* Remove request in both connected/disconnected case.
* So that new request can proceed.
g_io_channel_set_encoding(data_io, NULL, NULL);
g_io_channel_set_flags(data_io, G_IO_FLAG_NONBLOCK, NULL);
- id = g_io_add_watch(data_io,
+ if (otp_client_s->curr_op == BT_OTP_OBJECT_WRITE) {
+ _bt_otp_client_write_on_fd(otc_info);
+ } else if (otp_client_s->curr_op == BT_OTP_OBJECT_READ) {
+ id = g_io_add_watch(data_io,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
__client_data_received_cb, (gpointer)otc_info);
- timeout_id = g_timeout_add(BT_OTC_CONNECTION_MAX_TIMEOUT,
- (GSourceFunc)__bt_otc_connection_timeout_cb, (gpointer)otc_info);
+ timeout_id = g_timeout_add(BT_OTC_CONNECTION_MAX_TIMEOUT,
+ (GSourceFunc)__bt_otc_connection_timeout_cb, (gpointer)otc_info);
+ }
} else {
BT_DBG("OTC Disconnected");
otc_connection_status = FALSE;
g_source_remove(timeout_id);
timeout_id = 0;
}
- bt_otp_client_s *otp_client_s = NULL;
- otp_client_s = _bt_otp_client_find(otc_info->address);
if (otp_client_s && (otp_client_s->curr_op == BT_OTP_OBJECT_READ)) {
_bt_otp_client_notify_read_object_status(BLUETOOTH_ERROR_NONE,
return error_code;
}
-static void _bt_otp_send_create_object_callback(int result,
+static void _bt_otp_client_send_create_object_callback(int result,
uint64_t id, bt_otp_client_s *otp_client_s)
{
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);
return BLUETOOTH_ERROR_INTERNAL;
}
+ otp_client_s->callback = callback;
+ otp_client_s->user_data = user_data;
+ otp_client_s->curr_op = BT_OTP_OBJECT_CREATE;
+
size = (uint32_t)st.st_size;
last_token = strrchr(file_path, '/');
file_name = last_token + 1;
g_free(type_uuid);
return error_code;
}
+
+static void _bt_otp_client_send_write_object_callback(int result,
+ unsigned int length, bt_otp_client_s *otp_client_s)
+{
+ ((bt_otp_client_object_write_cb)otp_client_s->callback)(
+ result, otp_client_s->remote_address,
+ length, otp_client_s->user_data);
+
+ if (oacp_write_op) {
+ g_free(oacp_write_op->file_path);
+ g_free(oacp_write_op);
+ oacp_write_op = NULL;
+ }
+}
+
+int bt_otp_client_write_object(bt_otp_client_h otp_client,
+ const char *file_path,
+ int offset, int length, int mode,
+ bt_otp_client_object_write_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;
+ time_t curr_time;
+
+ BT_CHECK_LE_SUPPORT();
+ BT_CHECK_INIT_STATUS();
+ BT_CHECK_INPUT_PARAMETER(otp_client_s);
+ BT_CHECK_INPUT_PARAMETER(file_path);
+
+ 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_WRITE_SUPPORTED(otp_client_s->otp_feature)) {
+ BT_INFO("OACP Write not supported");
+ return BT_ERROR_OPERATION_FAILED;
+ }
+
+ BT_DBG("OTP client wirte 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;
+
+ BT_INFO("Filepath [%s], Size[%llu]",
+ file_path, size);
+ BT_INFO("offset[%llu], length[%llu], mode[%u]",
+ offset, length, mode);
+
+ oacp_write_op = g_malloc0(sizeof(bt_otp_client_write_op));
+ oacp_write_op->size = size;
+ oacp_write_op->file_path = g_strdup(file_path);
+ oacp_write_op->offset = offset;
+ oacp_write_op->length = length;
+ oacp_write_op->mode = mode;
+
+ time(&curr_time);
+ oacp_write_op->last_modified = curr_time;
+
+ if (offset < 0 && (offset > size || (length - offset) > size)) {
+ /* Invalid param */
+ return BLUETOOTH_ERROR_INTERNAL;
+ }
+
+ otp_client_s->callback = callback;
+ otp_client_s->user_data = user_data;
+ otp_client_s->curr_op = BT_OTP_OBJECT_WRITE;
+
+ uint8_t value[10] = {0x00};
+ value[0] = OACP_WRITE;
+ value[1] = offset & 0xFF;
+ value[2] = (offset >> 8) & 0xFF;
+ value[3] = (offset >> 16) & 0xFF;
+ value[4] = (offset >> 24) & 0xFF;
+ value[5] = length & 0xFF;
+ value[6] = (length >> 8) & 0xFF;
+ value[7] = (length >> 16) & 0xFF;
+ value[8] = (length >> 24) & 0xFF;
+ value[9] = mode & 0xFF;
+
+ error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_oacp_control_point,
+ value, 10);
+ 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);
+ }
+
+ return error_code;
+}
\ No newline at end of file
, BT_UNIT_TEST_FUNCTION_OTP_CLIENT_READ_OBJ_CONTENTS},
{"bt_otp_client_create_object"
, BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ},
+ {"bt_otp_client_write_object"
+ , BT_UNIT_TEST_FUNCTION_OTP_CLIENT_WRITE_OBJ},
{NULL , 0x0000},
};
BT_ERR("Object creation failed!");
}
+static void __bt_otp_client_object_write_cb(int result, const char *remote_address,
+ unsigned int length, void *user_data)
+{
+ if (result == BT_ERROR_NONE)
+ TC_PRT("Object write success!, [%u] bytes written", length);
+ else
+ BT_ERR("Object write failed!, [%u] bytes written", length);
+}
+
static void __bt_initialize_all(void)
{
int ret;
break;
}
+ case BT_UNIT_TEST_FUNCTION_OTP_CLIENT_WRITE_OBJ: {
+ if (param_index == 0) {
+ g_test_param.param_count = 4;
+ 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 file path");
+ break;
+ case 1:
+ TC_PRT("Input offset");
+ break;
+ case 2:
+ TC_PRT("Input Length");
+ break;
+ case 3:
+ TC_PRT("Input mode");
+ break;
+ }
+
+ param_index++;
+
+ break;
+ }
default:
TC_PRT("There is no param to set\n");
need_to_set_params = false;
break;
}
- id = atoi(g_test_param.params[0]);
+ id = (unsigned long long)atoi(g_test_param.params[0]);
TC_PRT("Object ID[%llu]", id);
ret = bt_otp_client_select_object(otp_client, id,
__bt_otp_client_object_select_cb, NULL);
__bt_free_test_param(&g_test_param);
break;
}
+ case BT_UNIT_TEST_FUNCTION_OTP_CLIENT_WRITE_OBJ: {
+ char *file_path = NULL;
+ int offset, length, mode;
+
+ if (g_test_param.param_count < 4) {
+ TC_PRT("Input parameters first");
+ break;
+ }
+
+ file_path = g_test_param.params[0];
+ TC_PRT("%s", file_path);
+
+ offset = strtoul(g_test_param.params[1], NULL, 10);
+ length = strtoul(g_test_param.params[2], NULL, 10);
+ mode = strtoul(g_test_param.params[3], NULL, 10);
+
+ if (otp_client) {
+ ret = bt_otp_client_write_object(otp_client,
+ file_path, offset, length, mode,
+ __bt_otp_client_object_write_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");