// experimental client API uint16_t att_uuid_for_handle(uint16_t handle){ att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok) return 0; if (it.flags & ATT_PROPERTY_UUID128) return 0; return READ_BT_16(it.uuid, 0); }
// // MARK: ATT_WRITE_REQUEST 0x12 static uint16_t handle_write_request(att_connection_t * att_connection, uint8_t * request_buffer, uint16_t request_len, uint8_t * response_buffer, uint16_t response_buffer_size){ uint8_t request_type = ATT_WRITE_REQUEST; uint16_t handle = READ_BT_16(request_buffer, 1); att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok) { return setup_error_invalid_handle(response_buffer, request_type, handle); } if (!att_write_callback) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } if ((it.flags & ATT_PROPERTY_WRITE) == 0) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } // check security requirements uint8_t error_code = att_validate_security(att_connection, &it); if (error_code) { return setup_error(response_buffer, request_type, handle, error_code); } error_code = (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3); if (error_code) { return setup_error(response_buffer, request_type, handle, error_code); } response_buffer[0] = ATT_WRITE_RESPONSE; return 1; }
// // MARK: ATT_READ_MULTIPLE_REQUEST 0x0e // static uint16_t handle_read_multiple_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t num_handles, uint16_t * handles){ printf("ATT_READ_MULTIPLE_REQUEST: num handles %u\n", num_handles); uint16_t offset = 1; int i; for (i=0;i<num_handles;i++){ uint16_t handle = handles[i]; if (handle == 0){ return setup_error_invalid_handle(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle); } att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok){ return setup_error_invalid_handle(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle); } att_update_value_len(&it); // limit data if (offset + it.value_len > response_buffer_size) { it.value_len = response_buffer_size - 1; } // store uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); offset += bytes_copied; } response_buffer[0] = ATT_READ_MULTIPLE_RESPONSE; return offset; }
// // MARK: ATT_READ_BLOB_REQUEST 0x0c // static uint16_t handle_read_blob_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle, uint16_t value_offset){ printf("ATT_READ_BLOB_REQUEST: handle %04x, offset %u\n", handle, value_offset); att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok){ return setup_error_atribute_not_found(response_buffer, ATT_READ_BLOB_REQUEST, handle); } att_update_value_len(&it); if (value_offset >= it.value_len){ return setup_error_invalid_offset(response_buffer, ATT_READ_BLOB_REQUEST, handle); } // limit data uint16_t offset = 1; if (offset + it.value_len - value_offset > response_buffer_size) { it.value_len = response_buffer_size - 1 + value_offset; } // store uint16_t bytes_copied = att_copy_value(&it, value_offset, response_buffer + offset, it.value_len - value_offset); offset += bytes_copied; response_buffer[0] = ATT_READ_BLOB_RESPONSE; return offset; }
// // MARK: ATT_READ_BY_TYPE_REQUEST // static uint16_t handle_read_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle){ printf("ATT_READ_REQUEST: handle %04x\n", handle); att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok){ return setup_error_atribute_not_found(response_buffer, ATT_READ_REQUEST, handle); } att_update_value_len(&it); uint16_t offset = 1; // limit data if (offset + it.value_len > response_buffer_size) { it.value_len = response_buffer_size - 1; } // store uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); offset += bytes_copied; response_buffer[0] = ATT_READ_RESPONSE; return offset; }
// // MARK: ATT_READ_MULTIPLE_REQUEST 0x0e // static uint16_t handle_read_multiple_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t num_handles, uint8_t * handles){ log_info("ATT_READ_MULTIPLE_REQUEST: num handles %u", num_handles); uint8_t request_type = ATT_READ_MULTIPLE_REQUEST; // TODO: figure out which error to respond with // if (num_handles < 2){ // return setup_error(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle, ???); // } uint16_t offset = 1; int i; uint8_t error_code = 0; uint16_t handle = 0; for (i=0;i<num_handles;i++){ handle = READ_BT_16(handles, i << 1); if (handle == 0){ return setup_error_invalid_handle(response_buffer, request_type, handle); } att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok){ return setup_error_invalid_handle(response_buffer, request_type, handle); } // check if handle can be read if ((it.flags & ATT_PROPERTY_READ) == 0) { error_code = ATT_ERROR_READ_NOT_PERMITTED; break; } // check security requirements error_code = att_validate_security(att_connection, &it); if (error_code) break; att_update_value_len(&it, att_connection->con_handle); // limit data if (offset + it.value_len > response_buffer_size) { it.value_len = response_buffer_size - 1; } // store uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len, att_connection->con_handle); offset += bytes_copied; } if (error_code){ return setup_error(response_buffer, request_type, handle, error_code); } response_buffer[0] = ATT_READ_MULTIPLE_RESPONSE; return offset; }
// MARK: ATT_WRITE_COMMAND 0x52 static void handle_write_command(uint8_t * request_buffer, uint16_t request_len, uint8_t * response_buffer, uint16_t response_buffer_size){ if (!att_write_callback) return; uint16_t handle = READ_BT_16(request_buffer, 1); att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok) return; if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return; (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3, NULL); }
// MARK: ATT_WRITE_COMMAND 0x52 // Core 4.0, vol 3, part F, 3.4.5.3 // "No Error Response or Write Response shall be sent in response to this command" static void handle_write_command(att_connection_t * att_connection, uint8_t * request_buffer, uint16_t request_len, uint8_t * response_buffer, uint16_t response_buffer_size){ if (!att_write_callback) return; uint16_t handle = READ_BT_16(request_buffer, 1); att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok) return; if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return; if ((it.flags & ATT_PROPERTY_WRITE_WITHOUT_RESPONSE) == 0) return; if (att_validate_security(att_connection, &it)) return; (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3); }
// // MARK: ATT_PREPARE_WRITE_REQUEST 0x16 static uint16_t handle_prepare_write_request(att_connection_t * att_connection, uint8_t * request_buffer, uint16_t request_len, uint8_t * response_buffer, uint16_t response_buffer_size){ uint8_t request_type = ATT_PREPARE_WRITE_REQUEST; uint16_t handle = READ_BT_16(request_buffer, 1); uint16_t offset = READ_BT_16(request_buffer, 3); if (!att_write_callback) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok) { return setup_error_invalid_handle(response_buffer, request_type, handle); } if ((it.flags & ATT_PROPERTY_WRITE) == 0) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) { return setup_error_write_not_permitted(response_buffer, request_type, handle); } // check security requirements uint8_t error_code = att_validate_security(att_connection, &it); if (error_code) { return setup_error(response_buffer, request_type, handle, error_code); } error_code = (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_ACTIVE, offset, request_buffer + 5, request_len - 5); switch (error_code){ case 0: break; case ATT_ERROR_INVALID_OFFSET: case ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH: // postpone to execute write request att_prepare_write_update_errors(error_code, handle); break; default: return setup_error(response_buffer, request_type, handle, error_code); } // response: echo request memcpy(response_buffer, request_buffer, request_len); response_buffer[0] = ATT_PREPARE_WRITE_RESPONSE; return request_len; }
// // MARK: ATT_WRITE_REQUEST 0x12 static uint16_t handle_write_request(uint8_t * request_buffer, uint16_t request_len, uint8_t * response_buffer, uint16_t response_buffer_size){ uint16_t handle = READ_BT_16(request_buffer, 1); if (!att_write_callback) { // TODO: Use "Write Not Permitted" return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); } att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok) { return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); } if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) { // TODO: Use "Write Not Permitted" return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); } (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3, NULL); response_buffer[0] = ATT_WRITE_RESPONSE; return 1; }
// // MARK: ATT_READ_BLOB_REQUEST 0x0c // static uint16_t handle_read_blob_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle, uint16_t value_offset){ log_info("ATT_READ_BLOB_REQUEST: handle %04x, offset %u", handle, value_offset); uint8_t request_type = ATT_READ_BLOB_REQUEST; att_iterator_t it; int ok = att_find_handle(&it, handle); if (!ok){ return setup_error_invalid_handle(response_buffer, request_type, handle); } // check if handle can be read if ((it.flags & ATT_PROPERTY_READ) == 0) { return setup_error_read_not_permitted(response_buffer, request_type, handle); } // check security requirements uint8_t error_code = att_validate_security(att_connection, &it); if (error_code) { return setup_error(response_buffer, request_type, handle, error_code); } att_update_value_len(&it, att_connection->con_handle); if (value_offset > it.value_len){ return setup_error_invalid_offset(response_buffer, request_type, handle); } // limit data uint16_t offset = 1; if (offset + it.value_len - value_offset > response_buffer_size) { it.value_len = response_buffer_size - 1 + value_offset; } // store uint16_t bytes_copied = att_copy_value(&it, value_offset, response_buffer + offset, it.value_len - value_offset, att_connection->con_handle); offset += bytes_copied; response_buffer[0] = ATT_READ_BLOB_RESPONSE; return offset; }