mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-29 04:20:20 +00:00
gatt-service: implement MICS Client
This commit is contained in:
parent
01b628dca3
commit
35facaddd1
@ -88,6 +88,7 @@ GATT_CLIENT += \
|
||||
device_information_service_client.c \
|
||||
scan_parameters_service_client.c \
|
||||
hids_client.c \
|
||||
microphone_control_service_client.c \
|
||||
|
||||
PAN += \
|
||||
pan.c \
|
||||
|
516
src/ble/gatt-service/microphone_control_service_client.c
Normal file
516
src/ble/gatt-service/microphone_control_service_client.c
Normal file
@ -0,0 +1,516 @@
|
||||
/*
|
||||
* Copyright (C) 2021 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
#define BTSTACK_FILE__ "microphone_control_service_client.c"
|
||||
|
||||
#include "btstack_config.h"
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "ble/gatt-service/microphone_control_service_client.h"
|
||||
|
||||
#include "btstack_memory.h"
|
||||
#include "ble/core.h"
|
||||
#include "ble/gatt_client.h"
|
||||
#include "bluetooth_gatt.h"
|
||||
#include "btstack_debug.h"
|
||||
#include "btstack_event.h"
|
||||
#include "btstack_run_loop.h"
|
||||
#include "gap.h"
|
||||
|
||||
static microphone_control_service_client_t mic_client;
|
||||
static uint16_t mic_service_cid_counter = 0;
|
||||
|
||||
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
|
||||
static uint16_t microphone_control_service_get_next_cid(void){
|
||||
mic_service_cid_counter = btstack_next_cid_ignoring_zero(mic_service_cid_counter);
|
||||
return mic_service_cid_counter;
|
||||
}
|
||||
|
||||
|
||||
static void microphone_control_service_finalize_client(microphone_control_service_client_t * client){
|
||||
client->cid = 0;
|
||||
client->con_handle = HCI_CON_HANDLE_INVALID;
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_IDLE;
|
||||
client->need_polling = false;
|
||||
}
|
||||
|
||||
static microphone_control_service_client_t * microphone_control_service_get_client_for_con_handle(hci_con_handle_t con_handle){
|
||||
if (mic_client.con_handle == con_handle){
|
||||
return &mic_client;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static microphone_control_service_client_t * microphone_control_service_get_client_for_cid(uint16_t microphone_control_service_cid){
|
||||
if (mic_client.cid == microphone_control_service_cid){
|
||||
return &mic_client;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void microphone_control_service_emit_connection_established(microphone_control_service_client_t * client, uint8_t status){
|
||||
uint8_t event[6];
|
||||
int pos = 0;
|
||||
event[pos++] = HCI_EVENT_GATTSERVICE_META;
|
||||
event[pos++] = sizeof(event) - 2;
|
||||
event[pos++] = GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_CONNECTED;
|
||||
little_endian_store_16(event, pos, client->cid);
|
||||
pos += 2;
|
||||
event[pos++] = status;
|
||||
(*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
||||
}
|
||||
|
||||
static void microphone_control_service_emit_mute_state(microphone_control_service_client_t * client, uint16_t value_handle, uint8_t att_status, uint8_t mute_state){
|
||||
if (value_handle != client->mute_value_handle){
|
||||
return;
|
||||
}
|
||||
uint8_t event[7];
|
||||
int pos = 0;
|
||||
event[pos++] = HCI_EVENT_GATTSERVICE_META;
|
||||
event[pos++] = sizeof(event) - 2;
|
||||
event[pos++] = GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE;
|
||||
little_endian_store_16(event, pos, client->cid);
|
||||
pos += 2;
|
||||
event[pos++] = att_status;
|
||||
event[pos++] = mute_state;
|
||||
(*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
||||
}
|
||||
|
||||
static bool microphone_control_service_registered_notification(microphone_control_service_client_t * client){
|
||||
gatt_client_characteristic_t characteristic;
|
||||
// if there are services without notification, register pool timer,
|
||||
// othervise register for notifications
|
||||
characteristic.value_handle = client->mute_value_handle;
|
||||
characteristic.properties = client->properties;
|
||||
characteristic.end_handle = client->end_handle;
|
||||
|
||||
uint8_t status = gatt_client_write_client_characteristic_configuration(
|
||||
&handle_gatt_client_event,
|
||||
client->con_handle,
|
||||
&characteristic,
|
||||
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
|
||||
|
||||
// notification supported, register for value updates
|
||||
if (status == ERROR_CODE_SUCCESS){
|
||||
gatt_client_listen_for_characteristic_value_updates(
|
||||
&client->notification_listener,
|
||||
&handle_gatt_client_event,
|
||||
client->con_handle, &characteristic);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void microphone_control_service_run_for_client(microphone_control_service_client_t * client){
|
||||
uint8_t status;
|
||||
gatt_client_characteristic_t characteristic;
|
||||
|
||||
switch (client->state){
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED:
|
||||
if (client->need_polling){
|
||||
// clear bit of polled service
|
||||
client->need_polling = false;
|
||||
|
||||
// poll value of characteristic
|
||||
characteristic.value_handle = client->mute_value_handle;
|
||||
characteristic.properties = client->properties;
|
||||
characteristic.end_handle = client->end_handle;
|
||||
gatt_client_read_value_of_characteristic(&handle_gatt_client_event, client->con_handle, &characteristic);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_W2_WRITE_MUTE:
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
printf(" Write mute [0x%04X, %d]:\n",
|
||||
client->mute_value_handle, client->requested_mute);
|
||||
#endif
|
||||
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_W4_WRITE_MUTE_RESULT;
|
||||
|
||||
// see GATT_EVENT_QUERY_COMPLETE for end of write
|
||||
status = gatt_client_write_value_of_characteristic(
|
||||
&handle_gatt_client_event, client->con_handle,
|
||||
client->mute_value_handle,
|
||||
1, &client->requested_mute);
|
||||
UNUSED(status);
|
||||
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
|
||||
status = gatt_client_discover_primary_services_by_uuid16(&handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_MICROPHONE_CONTROL);
|
||||
// TODO handle status
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
|
||||
|
||||
gatt_client_discover_characteristics_for_handle_range_by_uuid16(
|
||||
&handle_gatt_client_event,
|
||||
client->con_handle,
|
||||
client->start_handle,
|
||||
client->end_handle,
|
||||
ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL);
|
||||
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS:
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT;
|
||||
// if there are services without notification, register pool timer,
|
||||
// othervise register for notifications
|
||||
characteristic.value_handle = client->mute_value_handle;
|
||||
characteristic.properties = client->properties;
|
||||
characteristic.end_handle = client->end_handle;
|
||||
|
||||
(void) gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
|
||||
printf("Read client characteristic value [handle 0x%04X]:\n", client->ccc_handle);
|
||||
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
|
||||
|
||||
// result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
|
||||
(void) gatt_client_read_characteristic_descriptor_using_descriptor_handle(
|
||||
&handle_gatt_client_event,
|
||||
client->con_handle,
|
||||
client->ccc_handle);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION:
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED;
|
||||
|
||||
status = microphone_control_service_registered_notification(client);
|
||||
if (status == ERROR_CODE_SUCCESS) return;
|
||||
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
if (client->ccc_handle != 0){
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED;
|
||||
microphone_control_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// @return true if client valid / run function should be called
|
||||
static bool microphone_control_service_client_handle_query_complete(microphone_control_service_client_t * client, uint8_t status){
|
||||
switch (client->state){
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
|
||||
if (status != ATT_ERROR_SUCCESS){
|
||||
microphone_control_service_emit_connection_established(client, status);
|
||||
microphone_control_service_finalize_client(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (client->num_instances == 0){
|
||||
microphone_control_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
||||
microphone_control_service_finalize_client(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
|
||||
if (status != ATT_ERROR_SUCCESS){
|
||||
microphone_control_service_emit_connection_established(client, status);
|
||||
microphone_control_service_finalize_client(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((client->num_instances == 0) || (client->mute_value_handle == 0)){
|
||||
microphone_control_service_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
|
||||
microphone_control_service_finalize_client(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
|
||||
#else
|
||||
// wait for notification registration
|
||||
// to send connection established event
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
|
||||
#endif
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED;
|
||||
microphone_control_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
||||
break;
|
||||
#endif
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
printf("read CCC 0x%02x, polling %d \n", client->ccc_handle, client->need_polling ? 1 : 0);
|
||||
if ( (client->ccc_handle != 0) && !client->need_polling ) {
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED;
|
||||
microphone_control_service_emit_connection_established(client, ERROR_CODE_SUCCESS);
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED:
|
||||
if (status != ATT_ERROR_SUCCESS){
|
||||
microphone_control_service_emit_mute_state(client, client->mute_value_handle, status, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case MICROPHONE_CONTROL_SERVICE_CLIENT_W4_WRITE_MUTE_RESULT:
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED;
|
||||
microphone_control_service_emit_mute_state(client, client->mute_value_handle, status, client->requested_mute);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(packet_type);
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
|
||||
microphone_control_service_client_t * client = NULL;
|
||||
gatt_client_service_t service;
|
||||
gatt_client_characteristic_t characteristic;
|
||||
bool call_run = true;
|
||||
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_SERVICE_QUERY_RESULT:
|
||||
client = microphone_control_service_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
|
||||
btstack_assert(client != NULL);
|
||||
|
||||
if (client->num_instances < 1){
|
||||
gatt_event_service_query_result_get_service(packet, &service);
|
||||
client->start_handle = service.start_group_handle;
|
||||
client->end_handle = service.end_group_handle;
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
printf("MIC Service: start handle 0x%04X, end handle 0x%04X\n", client->start_handle, client->end_handle);
|
||||
#endif
|
||||
client->num_instances++;
|
||||
} else {
|
||||
log_info("Found more then one MIC Service instance. ");
|
||||
}
|
||||
break;
|
||||
|
||||
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
|
||||
client = microphone_control_service_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
|
||||
btstack_assert(client != NULL);
|
||||
|
||||
gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
|
||||
btstack_assert(characteristic.uuid16 == ORG_BLUETOOTH_CHARACTERISTIC_MUTE);
|
||||
|
||||
client->mute_value_handle = characteristic.value_handle;
|
||||
client->properties = characteristic.properties;
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
printf("Mute Characteristic:\n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X\n",
|
||||
// hid_characteristic_name(characteristic.uuid16),
|
||||
characteristic.start_handle,
|
||||
characteristic.properties,
|
||||
characteristic.value_handle, characteristic.uuid16);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case GATT_EVENT_NOTIFICATION:
|
||||
if (gatt_event_notification_get_value_length(packet) != 1) break;
|
||||
|
||||
client = microphone_control_service_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
|
||||
btstack_assert(client != NULL);
|
||||
|
||||
microphone_control_service_emit_mute_state(client,
|
||||
gatt_event_notification_get_value_handle(packet),
|
||||
ATT_ERROR_SUCCESS,
|
||||
gatt_event_notification_get_value(packet)[0]);
|
||||
break;
|
||||
|
||||
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
|
||||
client = microphone_control_service_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
|
||||
btstack_assert(client != NULL);
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
if (client->state == MICROPHONE_CONTROL_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT){
|
||||
printf(" Received CCC value: ");
|
||||
printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet), gatt_event_characteristic_value_query_result_get_value_length(packet));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (gatt_event_characteristic_value_query_result_get_value_length(packet) != 1) break;
|
||||
|
||||
microphone_control_service_emit_mute_state(client,
|
||||
gatt_event_characteristic_value_query_result_get_value_handle(packet),
|
||||
ATT_ERROR_SUCCESS,
|
||||
gatt_event_characteristic_value_query_result_get_value(packet)[0]);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:{
|
||||
gatt_client_characteristic_descriptor_t characteristic_descriptor;
|
||||
|
||||
client = microphone_control_service_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
|
||||
btstack_assert(client != NULL);
|
||||
gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
|
||||
|
||||
if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
|
||||
client->ccc_handle = characteristic_descriptor.handle;
|
||||
|
||||
printf(" MICS Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
|
||||
characteristic_descriptor.handle,
|
||||
characteristic_descriptor.uuid16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
client = microphone_control_service_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
|
||||
btstack_assert(client != NULL);
|
||||
call_run = microphone_control_service_client_handle_query_complete(client, gatt_event_query_complete_get_att_status(packet));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (call_run && (client != NULL)){
|
||||
microphone_control_service_run_for_client(client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t microphone_control_service_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, uint16_t * mics_cid){
|
||||
btstack_assert(packet_handler != NULL);
|
||||
|
||||
microphone_control_service_client_t * client = microphone_control_service_get_client_for_con_handle(con_handle);
|
||||
if (client != NULL){
|
||||
return ERROR_CODE_COMMAND_DISALLOWED;
|
||||
}
|
||||
|
||||
uint16_t cid = microphone_control_service_get_next_cid();
|
||||
if (mics_cid != NULL) {
|
||||
*mics_cid = cid;
|
||||
}
|
||||
|
||||
client->cid = cid;
|
||||
client->con_handle = con_handle;
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_IDLE;
|
||||
client->client_handler = packet_handler;
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
|
||||
microphone_control_service_run_for_client(client);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t microphone_control_service_client_disconnect(uint16_t mics_cid){
|
||||
microphone_control_service_client_t * client = microphone_control_service_get_client_for_cid(mics_cid);
|
||||
if (client == NULL){
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
// finalize connections
|
||||
microphone_control_service_finalize_client(client);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t microphone_control_service_client_read_mute_state(uint16_t mics_cid){
|
||||
microphone_control_service_client_t * client = microphone_control_service_get_client_for_cid(mics_cid);
|
||||
if (client == NULL) {
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
if (client->state != MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED) {
|
||||
return GATT_CLIENT_IN_WRONG_STATE;
|
||||
}
|
||||
|
||||
client->need_polling = true;
|
||||
microphone_control_service_run_for_client(client);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t microphone_control_service_client_mute_write(uint16_t mics_cid, gatt_microphone_control_mute_t mute_status){
|
||||
microphone_control_service_client_t * client = microphone_control_service_get_client_for_cid(mics_cid);
|
||||
if (client == NULL) {
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
if (client->state != MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED) {
|
||||
return GATT_CLIENT_IN_WRONG_STATE;
|
||||
}
|
||||
client->requested_mute = (uint8_t)mute_status;
|
||||
client->state = MICROPHONE_CONTROL_SERVICE_CLIENT_W2_WRITE_MUTE;
|
||||
microphone_control_service_run_for_client(client);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t microphone_control_service_client_mute_turn_on(uint16_t mics_cid){
|
||||
return microphone_control_service_client_mute_write(mics_cid, GATT_MICROPHONE_CONTROL_MUTE_ON);
|
||||
}
|
||||
|
||||
uint8_t microphone_control_service_client_mute_turn_off(uint16_t mics_cid){
|
||||
return microphone_control_service_client_mute_write(mics_cid, GATT_MICROPHONE_CONTROL_MUTE_OFF);
|
||||
}
|
||||
|
||||
void microphone_control_service_client_init(void){}
|
||||
|
||||
void microphone_control_service_client_deinit(void){
|
||||
mic_service_cid_counter = 0;
|
||||
microphone_control_service_finalize_client(&mic_client);
|
||||
}
|
||||
|
194
src/ble/gatt-service/microphone_control_service_client.h
Normal file
194
src/ble/gatt-service/microphone_control_service_client.h
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2021 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @title Microphone Control Service Client
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MICROPHONE_CONTROL_SERVICE_CLIENT_H
|
||||
#define MICROPHONE_CONTROL_SERVICE_CLIENT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "btstack_defines.h"
|
||||
#include "bluetooth.h"
|
||||
#include "btstack_linked_list.h"
|
||||
#include "ble/gatt_client.h"
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @text The Microphone Control Service Client connects to the Microphone Control Services of a remote device
|
||||
* and it can query or set mute value if mute value on the remote side is enabled. The Mute updates are received via notifications.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_IDLE,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT,
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT,
|
||||
#endif
|
||||
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_STATE_CONNECTED,
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT,
|
||||
#endif
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_W2_WRITE_MUTE,
|
||||
MICROPHONE_CONTROL_SERVICE_CLIENT_W4_WRITE_MUTE_RESULT
|
||||
} microphone_service_client_state_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
// service
|
||||
uint16_t start_handle;
|
||||
uint16_t end_handle;
|
||||
|
||||
// characteristic
|
||||
uint16_t properties;
|
||||
uint16_t value_handle;
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
uint16_t ccc_handle;
|
||||
#endif
|
||||
|
||||
gatt_client_notification_t notification_listener;
|
||||
} microphone_service_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
btstack_linked_item_t item;
|
||||
|
||||
hci_con_handle_t con_handle;
|
||||
uint16_t cid;
|
||||
microphone_service_client_state_t state;
|
||||
btstack_packet_handler_t client_handler;
|
||||
|
||||
// microphone_service_t service;
|
||||
// service
|
||||
uint16_t start_handle;
|
||||
uint16_t end_handle;
|
||||
|
||||
// characteristic
|
||||
uint16_t properties;
|
||||
uint16_t mute_value_handle;
|
||||
|
||||
#ifdef ENABLE_TESTING_SUPPORT
|
||||
uint16_t ccc_handle;
|
||||
#endif
|
||||
|
||||
bool need_polling;
|
||||
uint16_t num_instances;
|
||||
uint8_t requested_mute;
|
||||
|
||||
gatt_client_notification_t notification_listener;
|
||||
} microphone_control_service_client_t;
|
||||
|
||||
/* API_START */
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize Microphone Control Service.
|
||||
*/
|
||||
void microphone_control_service_client_init(void);
|
||||
|
||||
/**
|
||||
* @brief Connect to Microphone Control Services of remote device. The client will automatically register for notifications.
|
||||
* The mute state is received via GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE event.
|
||||
* The mute state can be 0 - off, 1 - on, 2 - disabeled and it is valid if the ATTT status is equal to ATT_ERROR_SUCCESS,
|
||||
* see ATT errors (see bluetooth.h) for other values.
|
||||
*
|
||||
* Event GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_CONNECTED is emitted with status ERROR_CODE_SUCCESS on success, otherwise
|
||||
* GATT_CLIENT_IN_WRONG_STATE, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no microphone control service is found, or ATT errors (see bluetooth.h).
|
||||
*
|
||||
* @param con_handle
|
||||
* @param packet_handler
|
||||
* @param mics_cid
|
||||
* @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_COMMAND_DISALLOWED if there is already a client associated with con_handle, or BTSTACK_MEMORY_ALLOC_FAILED
|
||||
*/
|
||||
uint8_t microphone_control_service_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, uint16_t * mics_cid);
|
||||
|
||||
/**
|
||||
* @brief Read mute state. The mute state is received via GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE event.
|
||||
* @param mics_cid
|
||||
* @return status
|
||||
*/
|
||||
uint8_t microphone_control_service_client_read_mute_state(uint16_t mics_cid);
|
||||
|
||||
/**
|
||||
* @brief Turn on mute.
|
||||
* @param mics_cid
|
||||
* @return status
|
||||
*/
|
||||
uint8_t microphone_control_service_client_mute_turn_on(uint16_t mics_cid);
|
||||
|
||||
/**
|
||||
* @brief Turn off mute.
|
||||
* @param mics_cid
|
||||
* @return status
|
||||
*/
|
||||
uint8_t microphone_control_service_client_mute_turn_off(uint16_t mics_cid);
|
||||
|
||||
/**
|
||||
* @brief Disconnect.
|
||||
* @param mics_cid
|
||||
* @return status
|
||||
*/
|
||||
uint8_t microphone_control_service_client_disconnect(uint16_t mics_cid);
|
||||
|
||||
/**
|
||||
* @brief De-initialize Microphone Control Service.
|
||||
*/
|
||||
void microphone_control_service_client_deinit(void);
|
||||
|
||||
/* API_END */
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -93,6 +93,7 @@
|
||||
#include "ble/gatt-service/heart_rate_service_server.h"
|
||||
#include "ble/gatt-service/hids_client.h"
|
||||
#include "ble/gatt-service/hids_device.h"
|
||||
#include "ble/gatt-service/microphone_control_service_client.h"
|
||||
#include "ble/gatt-service/microphone_control_service_server.h"
|
||||
#include "ble/gatt-service/scan_parameters_service_client.h"
|
||||
#include "ble/gatt-service/scan_parameters_service_server.h"
|
||||
|
@ -3468,6 +3468,24 @@ typedef uint8_t sm_key_t[16];
|
||||
*/
|
||||
#define GATTSERVICE_SUBEVENT_SCAN_PARAMETERS_SERVICE_SCAN_INTERVAL_UPDATE 0x18
|
||||
|
||||
// LE Audio
|
||||
|
||||
/**
|
||||
* @format 121
|
||||
* @param subevent_code
|
||||
* @param hids_cid
|
||||
* @param status
|
||||
*/
|
||||
#define GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_CONNECTED 0x19
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
* @param subevent_code
|
||||
* @param mics_cid
|
||||
* @param status
|
||||
* @param mute_value
|
||||
*/
|
||||
#define GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE 0x1A
|
||||
|
||||
// MAP Meta Event Group
|
||||
|
||||
|
@ -10463,6 +10463,53 @@ static inline uint16_t gattservice_subevent_scan_parameters_service_scan_interva
|
||||
return little_endian_read_16(event, 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field hids_cid from event GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_CONNECTED
|
||||
* @param event packet
|
||||
* @return hids_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t gattservice_subevent_microphone_control_service_connected_get_hids_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field status from event GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_CONNECTED
|
||||
* @param event packet
|
||||
* @return status
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t gattservice_subevent_microphone_control_service_connected_get_status(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field mics_cid from event GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE
|
||||
* @param event packet
|
||||
* @return mics_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t gattservice_subevent_microphone_control_service_mute_get_mics_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field status from event GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE
|
||||
* @param event packet
|
||||
* @return status
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t gattservice_subevent_microphone_control_service_mute_get_status(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field mute_value from event GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE
|
||||
* @param event packet
|
||||
* @return mute_value
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t gattservice_subevent_microphone_control_service_mute_get_mute_value(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field map_cid from event MAP_SUBEVENT_CONNECTION_OPENED
|
||||
* @param event packet
|
||||
|
@ -82,6 +82,9 @@ AVRCP += \
|
||||
avrcp_browsing_target.c \
|
||||
avrcp_browsing_controller.c \
|
||||
|
||||
SRC_BLE_GATT_SERVICE_FILES += \
|
||||
microphone_control_service_server.c \
|
||||
|
||||
# include ${BTSTACK_ROOT}/example/Makefile.inc
|
||||
|
||||
HXCMOD_PLAYER = \
|
||||
@ -154,6 +157,7 @@ EXAMPLES += avdtp_source_test avdtp_sink_test avrcp_test gatt_profiles hrp_col_t
|
||||
EXAMPLES += hog_device_test hog_host_test hid_device_test hid_host_test
|
||||
EXAMPLES += csc_server_test csc_client_test cycling_power_server_test
|
||||
EXAMPLES += gap_peripheral_test gap_central_test
|
||||
EXAMPLES += microphone_control_service_client_test
|
||||
|
||||
all: ${EXAMPLES}
|
||||
|
||||
@ -262,6 +266,12 @@ gatt_profiles.h: gatt_profiles.gatt
|
||||
gatt_profiles: gatt_profiles.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} ${SRC_BLE_GATT_SERVICE_FILES_OBJ} gatt_client.c gatt_profiles.o
|
||||
${CC} $(filter-out gatt_profiles.h,$^) ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
microphone_control_service_client_test.h: microphone_control_service_client_test.gatt
|
||||
python3 ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@
|
||||
|
||||
microphone_control_service_client_test: microphone_control_service_client_test.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} microphone_control_service_client_test.c
|
||||
${CC} $(filter-out microphone_control_service_client_test.h,$^) ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
myclean:
|
||||
rm -rf *.pyc *.o $(AVDTP_TESTS) *.dSYM *_test *.wav *.sbc ${BTSTACK_ROOT}/port/libusb/*.o ${BTSTACK_ROOT}/src/*.o ${BTSTACK_ROOT}/src/classic/*.o ${BTSTACK_ROOT}/src/ble/*.o
|
||||
rm -rf hog_device_test.h
|
||||
@ -273,6 +283,7 @@ myclean:
|
||||
rm -rf hrp_server_test.h
|
||||
rm -f sco_input.msbc
|
||||
rm -f sco_output.msbc
|
||||
rm -f microphone_control_service_client_test.h
|
||||
|
||||
clean: myclean
|
||||
|
||||
|
285
test/pts/microphone_control_service_client_test.c
Normal file
285
test/pts/microphone_control_service_client_test.c
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (C) 2014 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
#define BTSTACK_FILE__ "microphone_control_service_client_test.c"
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "btstack.h"
|
||||
|
||||
#include "microphone_control_service_client_test.h"
|
||||
|
||||
typedef struct advertising_report {
|
||||
uint8_t type;
|
||||
uint8_t event_type;
|
||||
uint8_t address_type;
|
||||
bd_addr_t address;
|
||||
uint8_t rssi;
|
||||
uint8_t length;
|
||||
const uint8_t * data;
|
||||
} advertising_report_t;
|
||||
|
||||
static enum {
|
||||
APP_STATE_IDLE,
|
||||
APP_STATE_W4_SCAN_RESULT,
|
||||
APP_STATE_W4_CONNECT,
|
||||
APP_STATE_CONNECTED
|
||||
} app_state;
|
||||
|
||||
static int blacklist_index = 0;
|
||||
static bd_addr_t blacklist[20];
|
||||
static advertising_report_t report;
|
||||
|
||||
static hci_con_handle_t connection_handle;
|
||||
static uint16_t mips_cid;
|
||||
|
||||
static bd_addr_t cmdline_addr;
|
||||
static int cmdline_addr_found = 0;
|
||||
|
||||
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
|
||||
|
||||
static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
static void gatt_client_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
|
||||
static void microphone_control_service_client_setup(void){
|
||||
// Init L2CAP
|
||||
l2cap_init();
|
||||
|
||||
// Setup ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android phones
|
||||
att_server_init(profile_data, NULL, NULL);
|
||||
|
||||
// GATT Client setup
|
||||
gatt_client_init();
|
||||
|
||||
microphone_control_service_client_init();
|
||||
|
||||
sm_init();
|
||||
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
|
||||
|
||||
hci_event_callback_registration.callback = &hci_event_handler;
|
||||
hci_add_event_handler(&hci_event_callback_registration);
|
||||
}
|
||||
|
||||
static int blacklist_size(void){
|
||||
return sizeof(blacklist) / sizeof(bd_addr_t);
|
||||
}
|
||||
|
||||
static int blacklist_contains(bd_addr_t addr){
|
||||
int i;
|
||||
for (i=0; i<blacklist_size(); i++){
|
||||
if (bd_addr_cmp(addr, blacklist[i]) == 0) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_to_blacklist(bd_addr_t addr){
|
||||
printf("%s added to blacklist (no battery service found).\n", bd_addr_to_str(addr));
|
||||
bd_addr_copy(blacklist[blacklist_index], addr);
|
||||
blacklist_index = (blacklist_index + 1) % blacklist_size();
|
||||
}
|
||||
|
||||
static void dump_advertising_report(uint8_t *packet){
|
||||
bd_addr_t address;
|
||||
gap_event_advertising_report_get_address(packet, address);
|
||||
|
||||
printf(" * adv. event: evt-type %u, addr-type %u, addr %s, rssi %u, length adv %u, data: ",
|
||||
gap_event_advertising_report_get_advertising_event_type(packet),
|
||||
gap_event_advertising_report_get_address_type(packet),
|
||||
bd_addr_to_str(address),
|
||||
gap_event_advertising_report_get_rssi(packet),
|
||||
gap_event_advertising_report_get_data_length(packet));
|
||||
printf_hexdump(gap_event_advertising_report_get_data(packet), gap_event_advertising_report_get_data_length(packet));
|
||||
|
||||
}
|
||||
|
||||
static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
|
||||
bd_addr_t address;
|
||||
|
||||
if (packet_type != HCI_EVENT_PACKET){
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hci_event_packet_get_type(packet)) {
|
||||
|
||||
case BTSTACK_EVENT_STATE:
|
||||
// BTstack activated, get started
|
||||
if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break;
|
||||
if (cmdline_addr_found){
|
||||
printf("Connect to %s\n", bd_addr_to_str(cmdline_addr));
|
||||
app_state = APP_STATE_W4_CONNECT;
|
||||
gap_connect(cmdline_addr, 0);
|
||||
break;
|
||||
}
|
||||
printf("Start scanning!\n");
|
||||
app_state = APP_STATE_W4_SCAN_RESULT;
|
||||
gap_set_scan_parameters(0,0x0030, 0x0030);
|
||||
gap_start_scan();
|
||||
break;
|
||||
|
||||
case GAP_EVENT_ADVERTISING_REPORT:
|
||||
if (app_state != APP_STATE_W4_SCAN_RESULT) return;
|
||||
|
||||
gap_event_advertising_report_get_address(packet, address);
|
||||
if (blacklist_contains(address)) {
|
||||
break;
|
||||
}
|
||||
dump_advertising_report(packet);
|
||||
|
||||
// stop scanning, and connect to the device
|
||||
app_state = APP_STATE_W4_CONNECT;
|
||||
gap_stop_scan();
|
||||
printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(report.address));
|
||||
gap_connect(report.address,report.address_type);
|
||||
break;
|
||||
|
||||
case HCI_EVENT_LE_META:
|
||||
// Wait for connection complete
|
||||
if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
|
||||
|
||||
if (app_state != APP_STATE_W4_CONNECT) return;
|
||||
|
||||
// Get connection handle from event
|
||||
connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
|
||||
|
||||
(void) microphone_control_service_client_connect(connection_handle, gatt_client_event_handler, &mips_cid);
|
||||
|
||||
app_state = APP_STATE_CONNECTED;
|
||||
printf("Microphone Control service connected.\n");
|
||||
break;
|
||||
|
||||
case HCI_EVENT_DISCONNECTION_COMPLETE:
|
||||
connection_handle = HCI_CON_HANDLE_INVALID;
|
||||
// Disconnect battery service
|
||||
microphone_control_service_client_disconnect(mips_cid);
|
||||
|
||||
if (cmdline_addr_found){
|
||||
printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr));
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Disconnected %s\n", bd_addr_to_str(report.address));
|
||||
printf("Restart scan.\n");
|
||||
app_state = APP_STATE_W4_SCAN_RESULT;
|
||||
gap_start_scan();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gatt_client_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(packet_type);
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
|
||||
uint8_t status;
|
||||
uint8_t att_status;
|
||||
|
||||
if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META){
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hci_event_gattservice_meta_get_subevent_code(packet)){
|
||||
case GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_CONNECTED:
|
||||
status = gattservice_subevent_microphone_control_service_connected_get_status(packet);
|
||||
switch (status){
|
||||
case ERROR_CODE_SUCCESS:
|
||||
printf("Microphone Control service client connected\n");
|
||||
break;
|
||||
default:
|
||||
printf("Microphone Control service client connection failed, err 0x%02x.\n", status);
|
||||
add_to_blacklist(report.address);
|
||||
gap_disconnect(connection_handle);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case GATTSERVICE_SUBEVENT_MICROPHONE_CONTROL_SERVICE_MUTE:
|
||||
att_status = gattservice_subevent_microphone_control_service_mute_get_status(packet);
|
||||
if (att_status != ATT_ERROR_SUCCESS){
|
||||
printf("Mute status read failed, ATT Error 0x%02x\n", att_status);
|
||||
} else {
|
||||
printf("Mute status: %d\n", gattservice_subevent_microphone_control_service_mute_get_mute_value(packet));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int btstack_main(int argc, const char * argv[]);
|
||||
int btstack_main(int argc, const char * argv[]){
|
||||
|
||||
// parse address if command line arguments are provided
|
||||
int arg = 1;
|
||||
cmdline_addr_found = 0;
|
||||
|
||||
while (arg < argc) {
|
||||
if(!strcmp(argv[arg], "-a") || !strcmp(argv[arg], "--address")){
|
||||
arg++;
|
||||
cmdline_addr_found = sscanf_bd_addr(argv[arg], cmdline_addr);
|
||||
arg++;
|
||||
if (!cmdline_addr_found) exit(1);
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "\nUsage: %s [-a|--address aa:bb:cc:dd:ee:ff]\n", argv[0]);
|
||||
fprintf(stderr, "If no argument is provided, GATT browser will start scanning and connect to the first found device.\nTo connect to a specific device use argument [-a].\n\n");
|
||||
return 0;
|
||||
}
|
||||
(void)argv;
|
||||
|
||||
microphone_control_service_client_setup();
|
||||
|
||||
app_state = APP_STATE_IDLE;
|
||||
|
||||
// turn on!
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
4
test/pts/microphone_control_service_client_test.gatt
Normal file
4
test/pts/microphone_control_service_client_test.gatt
Normal file
@ -0,0 +1,4 @@
|
||||
PRIMARY_SERVICE, GAP_SERVICE
|
||||
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "MICS Client"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user