/* * 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 #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 #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; } }