Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,183 @@
/*
* 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 "board/board.h"
#include "drivers/mic.h"
#include "drivers/accessory.h"
#include "kernel/events.h"
#include "kernel/pbl_malloc.h"
#if RECOVERY_FW
#include "services/prf/accessory/accessory_manager.h"
#else
#include "services/normal/accessory/accessory_manager.h"
#endif
#include "system/logging.h"
#include "system/passert.h"
#include "util/circular_buffer.h"
#include "util/legacy_checksum.h"
#include "system/profiler.h"
static int s_timeout = 0;
static bool s_is_8_bit;
static bool s_is_8khz;
static TimerID s_start_timer;
static int16_t *s_test_buffer;
static void prv_put_byte(uint8_t datum) {
accessory_send_data(&datum, 1);
}
static const uint8_t HDLC_START = 0x7E;
static const uint8_t HDLC_ESCAPE = 0x7D;
static const uint8_t HDLC_ESCAPE_MASK = 0x20;
static void prv_put_hdlc_frame_delimiter(void) {
prv_put_byte(HDLC_START);
}
static void prv_put_byte_hdlc(uint8_t datum) {
if ((datum == HDLC_ESCAPE) || (datum == HDLC_START)) {
prv_put_byte(HDLC_ESCAPE);
prv_put_byte(datum ^ HDLC_ESCAPE_MASK);
} else {
prv_put_byte(datum);
}
}
static void prv_prompt_output_cb(int16_t *samples, size_t sample_count, void *context) {
const int OUTPUT_SAMPLE_RATE = (s_is_8khz) ? 8000 : 16000;
int to_process = MIN((int)sample_count, s_timeout);
s_timeout -= to_process;
if (to_process > 0) {
// Groups of samples are encapsulated in HDLC-like framing in order to verify the integrity of
// samples
prv_put_hdlc_frame_delimiter();
int num_bytes = (to_process * (s_is_8_bit ? 1 : 2)) / (MIC_SAMPLE_RATE/OUTPUT_SAMPLE_RATE);
// Store frame in temporary buffer in order to calculate CRC after subsample and/or bit-width
// conversion
uint8_t buf[num_bytes];
int buf_idx = 0;
uint8_t *d = (uint8_t *)samples;
// Subsample by skipping every second sample if output rate is set to 8kHz
for (int i = 0; i < to_process * 2; i += 2 * (MIC_SAMPLE_RATE/OUTPUT_SAMPLE_RATE)) {
if (s_is_8_bit) {
// Convert 16-bit PCM representation to 8-bit PCM representation
uint8_t b = d[i + 1] ^ 0x80;
buf[buf_idx++] = b;
prv_put_byte_hdlc(b);
} else {
buf[buf_idx++] = d[i];
prv_put_byte_hdlc(d[i]);
buf[buf_idx++] = d[i + 1];
prv_put_byte_hdlc(d[i + 1]);
}
}
uint32_t crc = legacy_defective_checksum_memory(buf, num_bytes);
prv_put_byte_hdlc((uint8_t) crc);
prv_put_byte_hdlc((uint8_t) (crc >> 8));
prv_put_byte_hdlc((uint8_t) (crc >> 16));
prv_put_byte_hdlc((uint8_t) (crc >> 24));
prv_put_hdlc_frame_delimiter();
}
if (s_timeout == 0) {
mic_stop(MIC);
PROFILER_STOP;
PROFILER_PRINT_STATS;
kernel_free(s_test_buffer);
accessory_enable_input();
#if RECOVERY_FW
const AccessoryInputState input_state = AccessoryInputStateMfg;
#else
const AccessoryInputState input_state = AccessoryInputStateIdle;
#endif
PBL_ASSERTN(accessory_manager_set_state(input_state));
}
}
static void prv_mic_start(void *data) {
const size_t BUFFER_SIZE = 24;
new_timer_delete(s_start_timer);
s_test_buffer = kernel_malloc(BUFFER_SIZE * sizeof(int16_t));
if (!s_test_buffer) {
PBL_LOG(LOG_LEVEL_ERROR, "Failed to malloc buffer for 'mic start' command");
return;
}
uint32_t width = s_is_8_bit ? 8 : 16;
uint32_t rate = s_is_8khz ? 8 : 16;
PBL_LOG(LOG_LEVEL_ALWAYS, "Starting mic recording: %"PRIu32"-bit @ %"PRIu32"kHz for %"PRIu32
" samples", width, rate, (s_timeout / (MIC_SAMPLE_RATE / (rate * 1000))));
PROFILER_INIT;
PROFILER_START;
if (!mic_start(MIC, &prv_prompt_output_cb, NULL, s_test_buffer, BUFFER_SIZE)) {
kernel_free(s_test_buffer);
}
}
void command_mic_start(char *timeout_str, char *sample_size_str, char *sample_rate_str,
char *volume_str) {
static const int MAX_TIMEOUT = 60;
if (!accessory_manager_set_state(AccessoryInputStateMic)) {
PBL_LOG(LOG_LEVEL_ERROR, "The accessory is already in use!");
return;
}
s_timeout = strtol(timeout_str, NULL, 10);
if (s_timeout <= 0) {
s_timeout = 1;
} else if (s_timeout > MAX_TIMEOUT) {
s_timeout = MAX_TIMEOUT;
}
int volume = strtol(volume_str, NULL, 10);
mic_set_volume(MIC, MIN(volume, 1024));
s_is_8_bit = strtol(sample_size_str, NULL, 10) == 8; // assume 16 bit if not 8 bit
s_is_8khz = strtol(sample_rate_str, NULL, 10) == 8000; // assume 16kHz if not set to 8000
// Convert timeout in seconds to sample count so that the exact amount of samples are sent
s_timeout *= MIC_SAMPLE_RATE;
// Boost the accessory connector baud rate if necessary
accessory_disable_input();
if ((!s_is_8_bit) && (!s_is_8khz)) {
accessory_set_baudrate(AccessoryBaud460800);
} else if ((!s_is_8_bit) || (!s_is_8khz)) {
accessory_set_baudrate(AccessoryBaud230400);
}
// Start timer after a short delay to allow receiving end to switch baud rate
s_start_timer = new_timer_create();
new_timer_start(s_start_timer, 500, &prv_mic_start, NULL, 0);
}
void command_mic_read() {
command_mic_start("3", "16", "16000", "100");
}