From bcb0fbdecfb59c1260b382eafe22faa42752f9c8 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 28 Feb 2024 12:26:21 +0100 Subject: [PATCH] Bluetooth: BAP: Shell: Add USB out support for the BAP shell Add USB out support for the BAP shell, so that decoded LC3 data can be sent to the host (e.g. a PC). Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/shell/CMakeLists.txt | 3 + subsys/bluetooth/audio/shell/audio.h | 31 ++ subsys/bluetooth/audio/shell/bap.c | 106 ++++--- subsys/bluetooth/audio/shell/bap_usb.c | 265 ++++++++++++++++++ .../nrf5340_audio_dk_nrf5340_cpuapp.conf | 6 + 5 files changed, 376 insertions(+), 35 deletions(-) create mode 100644 subsys/bluetooth/audio/shell/bap_usb.c diff --git a/subsys/bluetooth/audio/shell/CMakeLists.txt b/subsys/bluetooth/audio/shell/CMakeLists.txt index 9913d92994a721..cb86f9042a6723 100644 --- a/subsys/bluetooth/audio/shell/CMakeLists.txt +++ b/subsys/bluetooth/audio/shell/CMakeLists.txt @@ -81,6 +81,9 @@ zephyr_library_sources_ifdef( CONFIG_BT_BAP_STREAM bap.c ) +if (CONFIG_BT_AUDIO_RX AND CONFIG_LIBLC3 AND CONFIG_USB_DEVICE_AUDIO) + zephyr_library_sources(bap_usb.c) +endif() zephyr_library_sources_ifdef( CONFIG_BT_BAP_SCAN_DELEGATOR bap_scan_delegator.c diff --git a/subsys/bluetooth/audio/shell/audio.h b/subsys/bluetooth/audio/shell/audio.h index 639a94c7401d7a..4255a64a5d2379 100644 --- a/subsys/bluetooth/audio/shell/audio.h +++ b/subsys/bluetooth/audio/shell/audio.h @@ -22,6 +22,8 @@ #include "shell/bt.h" +#define MAX_CODEC_FRAMES_PER_SDU 5U + extern struct bt_csip_set_member_svc_inst *svc_inst; ssize_t audio_ad_data_add(struct bt_data *data, const size_t data_size, const bool discoverable, @@ -43,6 +45,13 @@ size_t pbp_ad_data_add(struct bt_data data[], size_t data_size); #if defined(CONFIG_LIBLC3) #include "lc3.h" + +#define USB_SAMPLE_RATE 48000U +#define LC3_MAX_SAMPLE_RATE 48000U +#define LC3_MAX_FRAME_DURATION_US 10000U +#define LC3_MAX_NUM_SAMPLES_MONO ((LC3_MAX_FRAME_DURATION_US * LC3_MAX_SAMPLE_RATE) / \ + USEC_PER_SEC) +#define LC3_MAX_NUM_SAMPLES_STEREO (LC3_MAX_NUM_SAMPLES_MONO * 2U) #endif /* CONFIG_LIBLC3 */ #define LOCATION BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT @@ -62,6 +71,12 @@ struct named_lc3_preset { const struct named_lc3_preset *bap_get_named_preset(bool is_unicast, enum bt_audio_dir dir, const char *preset_arg); +int bap_usb_init(void); +int bap_usb_add_frame(enum bt_audio_location lc3_chan_allocation, const int16_t *frame, + size_t frame_size); +void bap_usb_clear_frames(void); +void bap_usb_send_frames(void); + #if defined(CONFIG_BT_BAP_UNICAST) #define UNICAST_SERVER_STREAM_COUNT \ @@ -168,6 +183,22 @@ int cap_ac_unicast(const struct shell *sh, size_t argc, char **argv, #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */ #endif /* CONFIG_BT_BAP_UNICAST */ +static inline uint8_t get_chan_cnt(enum bt_audio_location chan_allocation) +{ + uint8_t cnt = 0U; + + if (chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO) { + return 1; + } + + while (chan_allocation != 0) { + cnt += chan_allocation & 1U; + chan_allocation >>= 1; + } + + return cnt; +} + static inline void print_qos(const struct shell *sh, const struct bt_audio_codec_qos *qos) { #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) || defined(CONFIG_BT_BAP_UNICAST) diff --git a/subsys/bluetooth/audio/shell/bap.c b/subsys/bluetooth/audio/shell/bap.c index ec20be77c45752..644399c4f9a550 100644 --- a/subsys/bluetooth/audio/shell/bap.c +++ b/subsys/bluetooth/audio/shell/bap.c @@ -32,10 +32,6 @@ #if defined(CONFIG_LIBLC3) -#define LC3_MAX_SAMPLE_RATE 48000 -#define LC3_MAX_FRAME_DURATION_US 10000 -#define LC3_MAX_NUM_SAMPLES ((LC3_MAX_FRAME_DURATION_US * LC3_MAX_SAMPLE_RATE) / USEC_PER_SEC) - static void clear_lc3_sine_data(struct bt_bap_stream *bap_stream); static void lc3_decoder_stream_clear(struct shell_stream *sh_stream); @@ -213,7 +209,7 @@ NET_BUF_POOL_FIXED_DEFINE(sine_tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT, SINE_TX_POOL #define AUDIO_VOLUME (INT16_MAX - 3000) /* codec does clipping above INT16_MAX - 3000 */ #define AUDIO_TONE_FREQUENCY_HZ 400 -static int16_t lc3_tx_buf[LC3_MAX_NUM_SAMPLES]; +static int16_t lc3_tx_buf[LC3_MAX_NUM_SAMPLES_MONO]; static lc3_encoder_t lc3_encoder; static lc3_encoder_mem_48k_t lc3_encoder_mem; static int lc3_encoder_freq_hz; @@ -602,7 +598,7 @@ static int lc3_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp static const struct bt_audio_codec_cap lc3_codec_cap = BT_AUDIO_CODEC_CAP_LC3( BT_AUDIO_CODEC_CAP_FREQ_ANY, BT_AUDIO_CODEC_CAP_DURATION_ANY, - BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1, 2), 30, 240, 2, CONTEXT); + BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1, 2), 30, 240, MAX_CODEC_FRAMES_PER_SDU, CONTEXT); static const struct bt_bap_unicast_server_cb unicast_server_cb = { .config = lc3_config, @@ -2330,10 +2326,10 @@ static struct bt_le_scan_cb bap_scan_cb = { #endif /* CONFIG_BT_BAP_BROADCAST_SINK */ #if defined(CONFIG_BT_AUDIO_RX) -static unsigned long recv_stats_interval = 100U; +static unsigned long recv_stats_interval = 1000U; #if defined(CONFIG_LIBLC3) -static int16_t lc3_rx_buf[LC3_MAX_NUM_SAMPLES]; +static int16_t lc3_rx_buf[LC3_MAX_NUM_SAMPLES_MONO]; static lc3_decoder_mem_48k_t lc3_decoder_mem; static K_SEM_DEFINE(lc3_decoder_sem, 0, 1); /* used as a signal */ static int lc3_decoder_frame_duration_us; @@ -2366,7 +2362,7 @@ static int init_lc3_decoder(const struct shell_stream *sh_stream) sh_stream->lc3_frame_duration_us, sh_stream->lc3_freq_hz); /* Create the encoder instance. This shall complete before stream_started() is called. */ lc3_decoder = lc3_setup_decoder(sh_stream->lc3_frame_duration_us, sh_stream->lc3_freq_hz, - 0, /* No resampling */ + IS_ENABLED(CONFIG_USB_DEVICE_AUDIO) ? USB_SAMPLE_RATE : 0, &lc3_decoder_mem); if (lc3_decoder == NULL) { shell_error(ctx_shell, "Failed to setup LC3 encoder - wrong parameters?\n"); @@ -2420,7 +2416,7 @@ static void lc3_decoder_stream_clear(struct shell_stream *sh_stream) } } -static void do_lc3_decode(struct shell_stream *sh_stream) +static int do_lc3_decode(struct shell_stream *sh_stream) { const uint16_t octets_per_frame = sh_stream->lc3_octets_per_frame; const uint8_t frame_blocks_per_sdu = sh_stream->lc3_frame_blocks_per_sdu; @@ -2432,7 +2428,7 @@ static void do_lc3_decode(struct shell_stream *sh_stream) if (sh_stream->in_buf == NULL) { k_mutex_unlock(&sh_stream->lc3_decoder_mutex); - return; + return 0; } /* Take ownership of buffer and unlock the stream */ @@ -2469,8 +2465,41 @@ static void do_lc3_decode(struct shell_stream *sh_stream) octets_per_frame * frame_cnt, buf->len); net_buf_unref(buf); + if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) { + bap_usb_clear_frames(); + } - return; + return 0; + } + + if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) { + const bool has_left = (sh_stream->lc3_chan_allocation & + BT_AUDIO_LOCATION_FRONT_LEFT) != 0; + const bool has_right = (sh_stream->lc3_chan_allocation & + BT_AUDIO_LOCATION_FRONT_RIGHT) != 0; + const bool is_mono = sh_stream->lc3_chan_allocation == + BT_AUDIO_LOCATION_MONO_AUDIO; + const bool is_left = j == 0 && has_left; + const bool is_right = has_right && (j == 0 || (j == 1 && has_left)); + enum bt_audio_location chan_alloc; + + if (is_left) { + chan_alloc = BT_AUDIO_LOCATION_FRONT_LEFT; + } else if (is_right) { + chan_alloc = BT_AUDIO_LOCATION_FRONT_RIGHT; + } else if (is_mono) { + chan_alloc = BT_AUDIO_LOCATION_MONO_AUDIO; + } else { + /* Not suitable for USB */ + continue; + } + + err = bap_usb_add_frame(chan_alloc, lc3_rx_buf, sizeof(lc3_rx_buf)); + if (err == -EINVAL) { + bap_usb_clear_frames(); + + return 0; + } } } } @@ -2478,11 +2507,15 @@ static void do_lc3_decode(struct shell_stream *sh_stream) sh_stream->decoded_cnt++; net_buf_unref(buf); + + return frame_cnt; } static void lc3_decoder_thread_func(void *arg1, void *arg2, void *arg3) { while (true) { + size_t frame_cnt = 0; + k_sem_take(&lc3_decoder_sem, K_FOREVER); /* Lock to avoid `lc3_decoder` becoming NULL in case of a stream stop */ @@ -2495,13 +2528,29 @@ static void lc3_decoder_thread_func(void *arg1, void *arg2, void *arg3) #if defined(CONFIG_BT_BAP_UNICAST) for (size_t i = 0U; i < ARRAY_SIZE(unicast_streams); i++) { - do_lc3_decode(&unicast_streams[i]); /* no-op if no data */ + frame_cnt += do_lc3_decode(&unicast_streams[i]); /* no-op if no data */ + } + + if (frame_cnt > 0U) { + if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) { + bap_usb_send_frames(); + } + + frame_cnt = 0U; } #endif /* CONFIG_BT_BAP_UNICAST */ #if defined(CONFIG_BT_BAP_BROADCAST_SINK) for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) { - do_lc3_decode(&broadcast_sink_streams[i]); + frame_cnt += do_lc3_decode(&broadcast_sink_streams[i]); + } + + if (frame_cnt > 0U) { + if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) { + bap_usb_send_frames(); + } + + frame_cnt = 0U; } #endif /* CONFIG_BT_BAP_BROADCAST_SINK */ @@ -2625,24 +2674,6 @@ static void stream_enabled_cb(struct bt_bap_stream *stream) } } -#if defined(CONFIG_LIBLC3) -static uint8_t get_chan_cnt(enum bt_audio_location chan_allocation) -{ - uint8_t cnt = 0U; - - if (chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO) { - return 1; - } - - while (chan_allocation != 0) { - cnt += chan_allocation & 1U; - chan_allocation >>= 1; - } - - return cnt; -} -#endif /* CONFIG_LIBLC3 */ - static void stream_started_cb(struct bt_bap_stream *bap_stream) { struct shell_stream *sh_stream = shell_stream_from_bap_stream(bap_stream); @@ -3421,14 +3452,19 @@ static int cmd_init(const struct shell *sh, size_t argc, char *argv[]) #if defined(CONFIG_LIBLC3) && defined(CONFIG_BT_AUDIO_RX) static K_KERNEL_STACK_DEFINE(lc3_decoder_thread_stack, 4096); - /* make it slightly lower priority than the RX thread */ - int lc3_decoder_thread_prio = K_PRIO_COOP(CONFIG_BT_RX_PRIO + 1); + int lc3_decoder_thread_prio = K_PRIO_PREEMPT(5); + static struct k_thread lc3_decoder_thread; k_thread_create(&lc3_decoder_thread, lc3_decoder_thread_stack, K_KERNEL_STACK_SIZEOF(lc3_decoder_thread_stack), lc3_decoder_thread_func, NULL, NULL, NULL, lc3_decoder_thread_prio, 0, K_NO_WAIT); - k_thread_name_set(&lc3_decoder_thread, "LC3 Decode"); + k_thread_name_set(&lc3_decoder_thread, "LC3 Decoder"); + + if (IS_ENABLED(CONFIG_USB_DEVICE_AUDIO)) { + err = bap_usb_init(); + __ASSERT(err == 0, "Failed to enable USB: %d", err); + } #endif /* CONFIG_LIBLC3 && CONFIG_BT_AUDIO_RX */ initialized = true; diff --git a/subsys/bluetooth/audio/shell/bap_usb.c b/subsys/bluetooth/audio/shell/bap_usb.c new file mode 100644 index 00000000000000..af9abd0a306796 --- /dev/null +++ b/subsys/bluetooth/audio/shell/bap_usb.c @@ -0,0 +1,265 @@ +/** + * @file + * @brief Bluetooth Basic Audio Profile shell USB extension + * + * This files handles all the USB related functionality to audio in/out for the BAP shell + * + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "shell/bt.h" +#include "audio.h" + +LOG_MODULE_REGISTER(bap_usb, CONFIG_BT_BAP_STREAM_LOG_LEVEL); + +#define USB_ENQUEUE_COUNT 10U +#define USB_FRAME_DURATION_US 1000U +#define USB_MONO_SAMPLE_SIZE \ + ((USB_FRAME_DURATION_US * USB_SAMPLE_RATE * sizeof(int16_t)) / USEC_PER_SEC) +#define USB_STEREO_SAMPLE_SIZE (USB_MONO_SAMPLE_SIZE * 2U) +#define USB_RING_BUF_SIZE (50U * LC3_MAX_NUM_SAMPLES_STEREO) /* up to 5 stereo frames */ + +static int16_t right_frames[MAX_CODEC_FRAMES_PER_SDU][LC3_MAX_NUM_SAMPLES_MONO]; +static int16_t left_frames[MAX_CODEC_FRAMES_PER_SDU][LC3_MAX_NUM_SAMPLES_MONO]; +static size_t right_frames_cnt; +static size_t left_frames_cnt; +static size_t mono_frames_cnt; + +RING_BUF_DECLARE(usb_out_ring_buf, USB_RING_BUF_SIZE); +NET_BUF_POOL_DEFINE(usb_tx_buf_pool, USB_ENQUEUE_COUNT, USB_STEREO_SAMPLE_SIZE, 0, net_buf_destroy); + +/* USB consumer callback, called every 1ms, consumes data from ring-buffer */ +static void usb_data_request_cb(const struct device *dev) +{ + uint8_t usb_audio_data[USB_STEREO_SAMPLE_SIZE] = {0}; + struct net_buf *pcm_buf; + uint32_t size; + int err; + + pcm_buf = net_buf_alloc(&usb_tx_buf_pool, K_NO_WAIT); + if (pcm_buf == NULL) { + LOG_WRN("Could not allocate pcm_buf"); + return; + } + + /* This may fail without causing issues since usb_audio_data is 0-initialized */ + size = ring_buf_get(&usb_out_ring_buf, usb_audio_data, sizeof(usb_audio_data)); + + net_buf_add_mem(pcm_buf, usb_audio_data, sizeof(usb_audio_data)); + + if (size != 0) { + static size_t cnt; + + if (++cnt % 1000 == 0) { /* TODO replace with recv_stats_interval */ + LOG_INF("[%zu]: Sending USB audio", cnt); + } + } + + err = usb_audio_send(dev, pcm_buf, sizeof(usb_audio_data)); + if (err != 0) { + LOG_ERR("Failed to send USB audio: %d", err); + net_buf_unref(pcm_buf); + } +} + +/* TODO: This currently is only used to do USB loopback, but will be replaced with USB in support + * Do NOT use this when also receiving audio over BLE as the ring_buf_put is not thread safe + */ +static void usb_data_received_cb(const struct device *dev, struct net_buf *buffer, size_t size) +{ + uint32_t size_put; + static int cnt; + + if (!buffer) { + return; + } + + if (!size) { + net_buf_unref(buffer); + return; + } + + size_put = ring_buf_put(&usb_out_ring_buf, buffer->data, size); + if (size_put != size) { + LOG_ERR("Failed to put %zu", size); + } + + if ((++cnt % 1000) == 0) { + LOG_INF("[%zu]: USB Data received", cnt); + } + + net_buf_unref(buffer); +} + +int bap_usb_add_frame(enum bt_audio_location chan_allocation, const int16_t *frame, + size_t frame_size) +{ + const bool is_left = (chan_allocation & BT_AUDIO_LOCATION_FRONT_LEFT) != 0; + const bool is_right = (chan_allocation & BT_AUDIO_LOCATION_FRONT_RIGHT) != 0; + const bool is_mono = chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO; + + if (frame_size > LC3_MAX_NUM_SAMPLES_MONO * sizeof(int16_t) || frame_size == 0U) { + LOG_DBG("Invalid frame of size %zu", frame_size); + + return -EINVAL; + } + + if (get_chan_cnt(chan_allocation) != 1) { + LOG_DBG("Invalid channel allocation %d", chan_allocation); + + return -EINVAL; + } + + if (((is_left || is_right) && mono_frames_cnt != 0) || + (is_mono && (left_frames_cnt != 0U || right_frames_cnt != 0U))) { + LOG_DBG("Cannot mix and match mono with left or right"); + + return -EINVAL; + } + + if (is_left) { + if (left_frames_cnt >= ARRAY_SIZE(left_frames)) { + LOG_WRN("Could not add more left frames"); + + return -ENOMEM; + } + + memcpy(left_frames[left_frames_cnt++], frame, frame_size); + } else if (is_right) { + if (right_frames_cnt >= ARRAY_SIZE(right_frames)) { + LOG_WRN("Could not add more right frames"); + + return -ENOMEM; + } + + memcpy(right_frames[right_frames_cnt++], frame, frame_size); + } else if (is_mono) { + /* Use left as mono*/ + if (mono_frames_cnt >= ARRAY_SIZE(left_frames)) { + LOG_WRN("Could not add more mono frames"); + + return -ENOMEM; + } + + memcpy(left_frames[mono_frames_cnt++], frame, frame_size); + } else { + /* Unsupported channel */ + LOG_DBG("Unsupported channel %d", chan_allocation); + return -EINVAL; + } + + return 0; +} + +void bap_usb_clear_frames(void) +{ + right_frames_cnt = 0U; + left_frames_cnt = 0U; + mono_frames_cnt = 0U; + memset(left_frames, 0, sizeof(left_frames)); + memset(right_frames, 0, sizeof(right_frames)); +} + +void bap_usb_send_frames(void) +{ + const bool is_left_only = right_frames_cnt == 0U && mono_frames_cnt == 0U; + const bool is_right_only = left_frames_cnt == 0U && mono_frames_cnt == 0U; + const bool is_mono_only = left_frames_cnt == 0U && right_frames_cnt == 0U; + + if (!is_left_only && !is_right_only && left_frames_cnt != right_frames_cnt) { + LOG_ERR("Mismatch between number of left (%zu) and right (%zu) frames, " + "discarding frames", + left_frames_cnt, right_frames_cnt); + + bap_usb_clear_frames(); + + return; + } + + /* Send frames to USB - If we only have a single channel we mix it to stereo */ + for (size_t i = 0U; i < MAX(mono_frames_cnt, MAX(left_frames_cnt, right_frames_cnt)); i++) { + const bool is_single_channel = is_left_only || is_right_only || is_mono_only; + static int16_t stereo_frame[LC3_MAX_NUM_SAMPLES_STEREO]; + const int16_t *right_frame = right_frames[i]; + const int16_t *left_frame = left_frames[i]; + const int16_t *mono_frame = left_frames[i]; /* use left as mono */ + uint32_t rb_size; + + /* Not enough space to store data */ + if (ring_buf_space_get(&usb_out_ring_buf) < sizeof(stereo_frame)) { + LOG_WRN("Could not send more than %zu frames to USB", i); + + break; + } + + /* Generate the stereo frame + * + * If we only have single channel then we mix that to stereo + */ + for (int j = 0; j < LC3_MAX_NUM_SAMPLES_MONO; j++) { + if (is_single_channel) { + int16_t sample = 0; + + /* Mix to stereo as LRLRLRLR */ + if (is_left_only) { + sample = left_frame[j]; + } else if (is_right_only) { + sample = right_frame[j]; + } else if (is_mono_only) { + sample = mono_frame[j]; + } + + stereo_frame[j * 2] = sample; + stereo_frame[j * 2 + 1] = sample; + } else { + stereo_frame[j * 2] = left_frame[j]; + stereo_frame[j * 2 + 1] = right_frame[j]; + } + } + + rb_size = ring_buf_put(&usb_out_ring_buf, (uint8_t *)stereo_frame, + sizeof(stereo_frame)); + if (rb_size != sizeof(stereo_frame)) { + LOG_WRN("Failed to put frame on USB ring buf"); + + break; + } + } + + bap_usb_clear_frames(); +} + +int bap_usb_init(void) +{ + const struct device *hs_dev = DEVICE_DT_GET(DT_NODELABEL(hs_0)); + static const struct usb_audio_ops usb_ops = { + .data_request_cb = usb_data_request_cb, + .data_received_cb = usb_data_received_cb, + }; + int err; + + if (!device_is_ready(hs_dev)) { + LOG_ERR("Cannot get USB Headset Device"); + return -EIO; + } + + usb_audio_register(hs_dev, &usb_ops); + err = usb_enable(NULL); + if (err != 0) { + LOG_ERR("Failed to enable USB"); + return err; + } + + return 0; +} diff --git a/tests/bluetooth/shell/boards/nrf5340_audio_dk_nrf5340_cpuapp.conf b/tests/bluetooth/shell/boards/nrf5340_audio_dk_nrf5340_cpuapp.conf index 8d48d5e1c20721..9a9c63c6358c61 100644 --- a/tests/bluetooth/shell/boards/nrf5340_audio_dk_nrf5340_cpuapp.conf +++ b/tests/bluetooth/shell/boards/nrf5340_audio_dk_nrf5340_cpuapp.conf @@ -1,6 +1,12 @@ # For LC3 the following configs are needed CONFIG_FPU=y CONFIG_LIBLC3=y +CONFIG_RING_BUFFER=y +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_NRFX_EVT_QUEUE_SIZE=64 +CONFIG_USB_REQUEST_BUFFER_SIZE=256 +CONFIG_USB_DEVICE_AUDIO=y +CONFIG_USB_DEVICE_PRODUCT="Zephyr Shell USB" # The LC3 codec uses a large amount of stack. This app runs the codec in the work-queue, hence # inctease stack size for that thread. CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096