mirror of
https://github.com/google/pebble.git
synced 2025-05-12 12:23:16 -04:00
256 lines
7.8 KiB
C
256 lines
7.8 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <bluetooth/bt_driver_comm.h>
|
|
|
|
#include "comm/bt_lock.h"
|
|
|
|
#include "console/pulse_protocol_impl.h"
|
|
#include "console/pulse2_transport_impl.h"
|
|
|
|
#include "kernel/event_loop.h"
|
|
#include "kernel/events.h"
|
|
#include "kernel/pbl_malloc.h"
|
|
|
|
#include "services/common/comm_session/session_transport.h"
|
|
|
|
#include "system/passert.h"
|
|
#include "system/logging.h"
|
|
|
|
#include "util/attributes.h"
|
|
#include "util/math.h"
|
|
|
|
#include <string.h>
|
|
|
|
#define PULSE_PP_OPCODE_DATA (1)
|
|
#define PULSE_PP_OPCODE_OPEN (2)
|
|
#define PULSE_PP_OPCODE_CLOSE (3)
|
|
|
|
#define PULSE_PP_OPCODE_UNKNOWN (255)
|
|
|
|
typedef struct PACKED PulsePPPacket {
|
|
uint8_t opcode;
|
|
uint8_t data[0];
|
|
} PulsePPPacket;
|
|
|
|
typedef struct PACKED PulsePPCallbackPacket {
|
|
size_t packet_length;
|
|
PulsePPPacket packet;
|
|
} PulsePPCallbackPacket;
|
|
|
|
typedef struct {
|
|
CommSession *session;
|
|
} PULSETransport;
|
|
|
|
//! The CommSession that the PULSE transport is managing.
|
|
//! Currently there's only one for the System session.
|
|
static PULSETransport s_transport;
|
|
|
|
static void prv_send_next(Transport *transport) {
|
|
CommSession *session = s_transport.session;
|
|
PBL_ASSERTN(session);
|
|
|
|
size_t bytes_remaining = comm_session_send_queue_get_length(session);
|
|
size_t mss = pulse_reliable_max_send_size() - sizeof(PulsePPPacket);
|
|
|
|
while (bytes_remaining) {
|
|
bt_unlock();
|
|
PulsePPPacket *resp = (PulsePPPacket*) pulse_reliable_send_begin(PULSE2_PEBBLE_PROTOCOL);
|
|
bt_lock();
|
|
|
|
if (resp) {
|
|
resp->opcode = PULSE_PP_OPCODE_DATA;
|
|
|
|
const size_t bytes_to_copy = MIN(bytes_remaining, mss);
|
|
comm_session_send_queue_copy(session, 0 /* start_offset */,
|
|
bytes_to_copy, &resp->data[0]);
|
|
pulse_reliable_send(resp, bytes_to_copy + sizeof(PulsePPPacket));
|
|
comm_session_send_queue_consume(session, bytes_to_copy);
|
|
|
|
bytes_remaining -= bytes_to_copy;
|
|
} else {
|
|
// Reliable transport went down while waiting to send.
|
|
// The CommSession has already been closed so simply return without doing
|
|
// anything further.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void prv_reset(Transport *transport) {
|
|
PBL_LOG(LOG_LEVEL_INFO, "Unimplemented");
|
|
}
|
|
|
|
static void prv_granted_kernel_main_cb(void *ctx) {
|
|
ResponsivenessGrantedHandler granted_handler = ctx;
|
|
granted_handler();
|
|
}
|
|
|
|
static void prv_set_connection_responsiveness(
|
|
Transport *transport, BtConsumer consumer, ResponseTimeState state, uint16_t max_period_secs,
|
|
ResponsivenessGrantedHandler granted_handler) {
|
|
if (granted_handler) {
|
|
launcher_task_add_callback(prv_granted_kernel_main_cb, granted_handler);
|
|
}
|
|
}
|
|
|
|
static CommSessionTransportType prv_get_type(struct Transport *transport) {
|
|
return CommSessionTransportType_PULSE;
|
|
}
|
|
|
|
static void prv_send_job(void *data) {
|
|
CommSession *session = (CommSession *)data;
|
|
bt_driver_run_send_next_job(session, true);
|
|
}
|
|
|
|
static bool prv_schedule_send_next_job(CommSession *session) {
|
|
launcher_task_add_callback(prv_send_job, session);
|
|
return true;
|
|
}
|
|
|
|
static bool prv_is_current_task_schedule_task(struct Transport *transport) {
|
|
return launcher_task_is_current_task();
|
|
}
|
|
|
|
//! Defined in session.c
|
|
extern void comm_session_set_capabilities(
|
|
CommSession *session, CommSessionCapability capability_flags);
|
|
|
|
bool pulse_transport_is_connected(void) {
|
|
return (s_transport.session != NULL);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
void pulse_transport_set_connected(bool is_connected) {
|
|
if (pulse_transport_is_connected() == is_connected) {
|
|
return;
|
|
}
|
|
|
|
static const TransportImplementation s_pulse_transport_implementation = {
|
|
.send_next = prv_send_next,
|
|
.reset = prv_reset,
|
|
.set_connection_responsiveness = prv_set_connection_responsiveness,
|
|
.get_type = prv_get_type,
|
|
.schedule = prv_schedule_send_next_job,
|
|
.is_current_task_schedule_task = prv_is_current_task_schedule_task,
|
|
};
|
|
|
|
bool send_event = true;
|
|
|
|
if (is_connected) {
|
|
s_transport.session = comm_session_open((Transport *) &s_transport,
|
|
&s_pulse_transport_implementation,
|
|
TransportDestinationHybrid);
|
|
if (!s_transport.session) {
|
|
PBL_LOG(LOG_LEVEL_ERROR, "CommSession couldn't be opened");
|
|
send_event = false;
|
|
}
|
|
|
|
// Give it the appropriate capabilities
|
|
const CommSessionCapability capabilities = CommSessionRunState |
|
|
CommSessionInfiniteLogDumping |
|
|
CommSessionVoiceApiSupport |
|
|
CommSessionAppMessage8kSupport |
|
|
CommSessionWeatherAppSupport |
|
|
CommSessionExtendedNotificationService;
|
|
comm_session_set_capabilities(s_transport.session, capabilities);
|
|
} else {
|
|
comm_session_close(s_transport.session, CommSessionCloseReason_UnderlyingDisconnection);
|
|
s_transport.session = NULL;
|
|
}
|
|
|
|
if (send_event) {
|
|
PebbleEvent e = {
|
|
.type = PEBBLE_BT_CONNECTION_EVENT,
|
|
.bluetooth = {
|
|
.connection = {
|
|
.state = (s_transport.session) ? PebbleBluetoothConnectionEventStateConnected
|
|
: PebbleBluetoothConnectionEventStateDisconnected
|
|
}
|
|
}
|
|
};
|
|
event_put(&e);
|
|
}
|
|
}
|
|
|
|
static void prv_pulse_pp_transport_set_connected(bool connected) {
|
|
bt_lock();
|
|
pulse_transport_set_connected(connected);
|
|
bt_unlock();
|
|
}
|
|
|
|
static void prv_pulse_pp_handle_data(void *data, size_t length) {
|
|
bt_lock();
|
|
|
|
if (!s_transport.session) {
|
|
PBL_LOG(LOG_LEVEL_ERROR, "Received PULSE serial data, but session not connected!");
|
|
goto unlock;
|
|
}
|
|
comm_session_receive_router_write(s_transport.session, data, length);
|
|
|
|
unlock:
|
|
bt_unlock();
|
|
}
|
|
|
|
static void prv_pulse_pp_send_cb(void *data) {
|
|
PulsePPCallbackPacket *cb_data = data;
|
|
uint8_t *resp = pulse_reliable_send_begin(PULSE2_PEBBLE_PROTOCOL);
|
|
memcpy(resp, &cb_data->packet, cb_data->packet_length);
|
|
pulse_reliable_send(resp, cb_data->packet_length);
|
|
kernel_free(data);
|
|
}
|
|
|
|
static void prv_pulse_pp_send(uint8_t opcode, uint8_t *data, size_t data_length) {
|
|
size_t packet_length = sizeof(PulsePPCallbackPacket) + data_length;
|
|
PulsePPCallbackPacket *cb_data = kernel_malloc_check(packet_length);
|
|
cb_data->packet_length = sizeof(PulsePPPacket) + data_length;
|
|
cb_data->packet.opcode = opcode;
|
|
|
|
if (data) {
|
|
memcpy(&cb_data->packet.data[0], data, data_length);
|
|
}
|
|
|
|
launcher_task_add_callback(prv_pulse_pp_send_cb, cb_data);
|
|
}
|
|
|
|
void pulse_pp_transport_open_handler(void) {
|
|
return;
|
|
}
|
|
|
|
void pulse_pp_transport_closed_handler(void) {
|
|
prv_pulse_pp_transport_set_connected(false);
|
|
}
|
|
|
|
void pulse_pp_transport_handle_received_data(void *data, size_t length) {
|
|
PulsePPPacket *packet = data;
|
|
|
|
switch (packet->opcode) {
|
|
case PULSE_PP_OPCODE_DATA:
|
|
prv_pulse_pp_handle_data(&packet->data[0], length - sizeof(PulsePPPacket));
|
|
break;
|
|
case PULSE_PP_OPCODE_OPEN:
|
|
prv_pulse_pp_send(PULSE_PP_OPCODE_OPEN, NULL, 0);
|
|
prv_pulse_pp_transport_set_connected(true);
|
|
break;
|
|
case PULSE_PP_OPCODE_CLOSE:
|
|
prv_pulse_pp_transport_set_connected(false);
|
|
prv_pulse_pp_send(PULSE_PP_OPCODE_CLOSE, NULL, 0);
|
|
break;
|
|
default:
|
|
prv_pulse_pp_send(PULSE_PP_OPCODE_UNKNOWN, &packet->opcode, sizeof(packet->opcode));
|
|
break;
|
|
}
|
|
}
|