262 lines
7.5 KiB
C
262 lines
7.5 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "ql_audio.h"
|
|
#include "ql_fs.h"
|
|
#include "ql_gpio.h"
|
|
#include "ql_power.h"
|
|
#include "ql_rtos.h"
|
|
|
|
#include "factory_audio.h"
|
|
#include "factory_test.h"
|
|
|
|
#define ES8311_ADDR 0x18
|
|
#define ES8311_DEVICE_ID_REG 0xFD
|
|
#define ES8311_DEVICE_ID 0x83
|
|
#define ES8311_VOLUME_100 0x8E
|
|
#define FT_PCM_CHUNK_BYTES 640
|
|
#define FT_MP3_TIMEOUT_TICKS (15000 / 5)
|
|
|
|
extern void sw_i2c_init(void);
|
|
extern int8 sw_i2c_start(void);
|
|
extern void sw_i2c_stop(void);
|
|
extern int8 sw_i2c_write_byte(uint8_t data);
|
|
extern uint8_t sw_i2c_read_byte(uint8_t ack);
|
|
|
|
static const uint16_t g_es8311_init_regs[] = {
|
|
0x0045, 0x3001, 0x1002, 0x0002, 0x1003, 0x2416, 0x2004, 0x0005,
|
|
0x2006, 0x0007, 0xff08, 0x0f09, 0x0f0a, 0x000b, 0x000c, 0x1310,
|
|
0x7f11, 0x3f01, 0x8000, 0x010d, 0x1014, 0x2812, 0x1013, 0x020e,
|
|
0x440f, 0x0015, 0x0a1b, 0x6a1c, 0x0837, 0x0044, 0xa717, 0x8e32,
|
|
0x0018
|
|
};
|
|
|
|
static ql_sem_t g_mp3_sem;
|
|
static volatile int g_mp3_result;
|
|
static Mp3PlaybackHandle g_mp3_handle;
|
|
|
|
static int es8311_write(uint8_t reg, uint8_t value)
|
|
{
|
|
if (sw_i2c_start() != 0) return -1;
|
|
if (sw_i2c_write_byte((uint8_t)(ES8311_ADDR << 1)) != 0 ||
|
|
sw_i2c_write_byte(reg) != 0 || sw_i2c_write_byte(value) != 0) {
|
|
sw_i2c_stop();
|
|
return -1;
|
|
}
|
|
sw_i2c_stop();
|
|
return 0;
|
|
}
|
|
|
|
static int es8311_read(uint8_t reg, uint8_t *value)
|
|
{
|
|
if (!value || sw_i2c_start() != 0) return -1;
|
|
if (sw_i2c_write_byte((uint8_t)(ES8311_ADDR << 1)) != 0 ||
|
|
sw_i2c_write_byte(reg) != 0 || sw_i2c_start() != 0 ||
|
|
sw_i2c_write_byte((uint8_t)((ES8311_ADDR << 1) | 1)) != 0) {
|
|
sw_i2c_stop();
|
|
return -1;
|
|
}
|
|
*value = sw_i2c_read_byte(0);
|
|
sw_i2c_stop();
|
|
return 0;
|
|
}
|
|
|
|
static void factory_pa_control(unsigned int on)
|
|
{
|
|
ql_gpio_init(FT_PA_PIN, PIN_DIRECTION_OUT, PIN_PULL_DISABLE,
|
|
on ? PIN_LEVEL_HIGH : PIN_LEVEL_LOW);
|
|
}
|
|
|
|
static void factory_mp3_callback(Mp3PlayEventType event, int value)
|
|
{
|
|
if (event != MP3_PLAYBACK_EVENT_STATUS) return;
|
|
if (value == MP3_PLAYBACK_STATUS_STARTED) {
|
|
g_mp3_result = 1;
|
|
} else if (value == MP3_PLAYBACK_STATUS_ENDED) {
|
|
g_mp3_result = 2;
|
|
if (g_mp3_sem) ql_rtos_semaphore_release(g_mp3_sem);
|
|
} else if (value == MP3_PLAYBACK_STATUS_TIMEOUT) {
|
|
g_mp3_result = -1;
|
|
if (g_mp3_sem) ql_rtos_semaphore_release(g_mp3_sem);
|
|
}
|
|
}
|
|
|
|
static void factory_track_callback(audio_track_handle handle,
|
|
acm_audio_track_event_t event)
|
|
{
|
|
(void)handle;
|
|
(void)event;
|
|
}
|
|
|
|
static void factory_record_callback(audio_record_handle handle,
|
|
acm_audio_record_event_t event)
|
|
{
|
|
(void)handle;
|
|
(void)event;
|
|
}
|
|
|
|
int factory_audio_init(void)
|
|
{
|
|
uint8_t id = 0;
|
|
unsigned int i;
|
|
u32 start = ft_now_ms();
|
|
ql_codecpcm_config pcm_config[] = {
|
|
PCM_FS_256, PCM_CODEC_SAMPLE_16000, 0
|
|
};
|
|
|
|
while (!ql_get_audio_state()) {
|
|
if (ft_now_ms() - start > 10000) return -1;
|
|
ql_rtos_task_sleep_ms(50);
|
|
}
|
|
|
|
sw_i2c_init();
|
|
if (es8311_read(ES8311_DEVICE_ID_REG, &id) != 0 || id != ES8311_DEVICE_ID) {
|
|
return -1;
|
|
}
|
|
|
|
ql_bind_speakerpa_cb(factory_pa_control);
|
|
ql_codec_choose(AUD_EXTERNAL_CODEC, pcm_config);
|
|
for (i = 0; i < sizeof(g_es8311_init_regs) / sizeof(g_es8311_init_regs[0]); ++i) {
|
|
if (es8311_write((uint8_t)(g_es8311_init_regs[i] & 0xff),
|
|
(uint8_t)(g_es8311_init_regs[i] >> 8)) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ql_set_audio_path_speaker();
|
|
ql_set_volume(AUDIOHAL_SPK_VOL_11);
|
|
es8311_write(0x32, ES8311_VOLUME_100);
|
|
factory_pa_control(0);
|
|
|
|
if (!g_mp3_sem && ql_rtos_semaphore_create(&g_mp3_sem, 0) != 0) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int factory_audio_play_prompt(const char *path)
|
|
{
|
|
Mp3PlayConfigInfo config;
|
|
int ret;
|
|
|
|
if (!path || ql_access(path, 0) != 0) return -2;
|
|
|
|
while (ql_rtos_semaphore_wait(g_mp3_sem, QL_NO_WAIT) == 0) { }
|
|
memset(&config, 0, sizeof(config));
|
|
config.listener = factory_mp3_callback;
|
|
config.sync_mode = 0;
|
|
g_mp3_result = 0;
|
|
|
|
es8311_write(0x32, ES8311_VOLUME_100);
|
|
ql_set_volume(AUDIOHAL_SPK_VOL_11);
|
|
factory_pa_control(1);
|
|
ret = ql_play_mp3((char *)path, &g_mp3_handle, &config);
|
|
if (ret != 0) {
|
|
factory_pa_control(0);
|
|
return -1;
|
|
}
|
|
|
|
ret = ql_rtos_semaphore_wait(g_mp3_sem, FT_MP3_TIMEOUT_TICKS);
|
|
if (ret != 0 || g_mp3_result != 2) {
|
|
ql_stop_mp3_play(g_mp3_handle, 0);
|
|
factory_pa_control(0);
|
|
return -1;
|
|
}
|
|
factory_pa_control(0);
|
|
return 0;
|
|
}
|
|
|
|
static int wait_pwrkey_level(unsigned char expected, u32 timeout_ms)
|
|
{
|
|
u32 start = ft_now_ms();
|
|
while (ql_get_pwrkey_status() != expected) {
|
|
if (ft_now_ms() - start >= timeout_ms) return -1;
|
|
ql_rtos_task_sleep_ms(10);
|
|
}
|
|
ql_rtos_task_sleep_ms(40);
|
|
return ql_get_pwrkey_status() == expected ? 0 : -1;
|
|
}
|
|
|
|
int factory_audio_wait_and_loopback(u32 wait_timeout_ms, u32 *recorded_ms)
|
|
{
|
|
unsigned char *buffer = NULL;
|
|
unsigned int total = 0;
|
|
unsigned int request;
|
|
unsigned int offset;
|
|
audio_record_handle record_handle = 0;
|
|
audio_track_handle track_handle = 0;
|
|
int ret = -1;
|
|
|
|
if (recorded_ms) *recorded_ms = 0;
|
|
if (wait_pwrkey_level(0, 10000) != 0 ||
|
|
wait_pwrkey_level(1, wait_timeout_ms) != 0) {
|
|
return -4;
|
|
}
|
|
|
|
buffer = (unsigned char *)malloc(FT_AUDIO_BUFFER_BYTES);
|
|
if (!buffer) return -1;
|
|
|
|
record_handle = ql_audio_record_open(FT_AUDIO_RATE_HZ, FT_AUDIO_CHANNELS,
|
|
0, factory_record_callback);
|
|
if (!record_handle) goto cleanup;
|
|
|
|
while (ql_get_pwrkey_status() && total < FT_AUDIO_BUFFER_BYTES) {
|
|
request = FT_PCM_CHUNK_BYTES;
|
|
if (request > FT_AUDIO_BUFFER_BYTES - total) {
|
|
request = FT_AUDIO_BUFFER_BYTES - total;
|
|
}
|
|
if (ql_audio_record_read(record_handle, (char *)(buffer + total), &request) == 0 &&
|
|
request > 0) {
|
|
total += request;
|
|
}
|
|
ql_rtos_task_sleep_ms(10);
|
|
}
|
|
ql_audio_record_close(record_handle, 0);
|
|
record_handle = 0;
|
|
|
|
if (total == FT_AUDIO_BUFFER_BYTES && ql_get_pwrkey_status()) {
|
|
if (wait_pwrkey_level(0, 30000) != 0) goto cleanup;
|
|
} else if (wait_pwrkey_level(0, 3000) != 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (total < FT_PCM_CHUNK_BYTES) {
|
|
ret = -2;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (recorded_ms) {
|
|
*recorded_ms = total * 1000U / (FT_AUDIO_RATE_HZ * 2U * FT_AUDIO_CHANNELS);
|
|
}
|
|
|
|
es8311_write(0x32, ES8311_VOLUME_100);
|
|
ql_set_volume(AUDIOHAL_SPK_VOL_11);
|
|
factory_pa_control(1);
|
|
track_handle = ql_audio_track_open(FT_AUDIO_RATE_HZ, FT_AUDIO_CHANNELS,
|
|
0, factory_track_callback);
|
|
if (!track_handle) {
|
|
ret = -3;
|
|
goto cleanup;
|
|
}
|
|
|
|
for (offset = 0; offset < total; offset += FT_PCM_CHUNK_BYTES) {
|
|
unsigned int size = total - offset;
|
|
if (size > FT_PCM_CHUNK_BYTES) size = FT_PCM_CHUNK_BYTES;
|
|
if (ql_audio_track_write(track_handle, (char *)(buffer + offset), size) != 0) {
|
|
ret = -3;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
ql_rtos_task_sleep_ms(total * 1000U /
|
|
(FT_AUDIO_RATE_HZ * 2U * FT_AUDIO_CHANNELS) + 300U);
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (record_handle) ql_audio_record_close(record_handle, 1);
|
|
if (track_handle) ql_audio_track_close(track_handle, ret == 0 ? 0 : 1);
|
|
factory_pa_control(0);
|
|
if (buffer) free(buffer);
|
|
return ret;
|
|
}
|