pebble/src/fw/console/pulse_pp.c
2025-01-27 11:38:16 -08:00

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;
}
}