// SPDX-License-Identifier: MIT /* * Copyright 2021 Álvaro Fernández Rojas - https://github.com/Noltari/pico-uart-bridge * Copyright 2023 sysmocom - s.f.m.c. GmbH - adapted for Osmocom GTM900 board */ #include #include #include #include #include #include #include #include #include #define GPIO_nMODEM_LDO_ON 14 /* pico pin 19 */ #define GPIO_nPWON 6 /* pico pin 9 */ #define GPIO_UART1_TX 0 /* pico pin 1 */ #define GPIO_UART1_RX 1 /* pico pin 2 */ #define GPIO_UART1_CTS 2 /* pico pin 4 */ #define GPIO_UART1_RTS 3 /* pico pin 5 */ #define GPIO_UART2_TX 4 /* pico pin 6 */ #define GPIO_UART2_RX 5 /* pico pin 7 */ #define GPIO_LED 25 #if !defined(MIN) #define MIN(a, b) ((a > b) ? b : a) #endif /* MIN */ #define BUFFER_SIZE 2560 #define DEF_BIT_RATE 115200 #define DEF_STOP_BITS 1 #define DEF_PARITY 0 #define DEF_DATA_BITS 8 typedef struct { uart_inst_t *const inst; uint irq; void *irq_fn; uint8_t tx_pin; uint8_t rx_pin; int8_t cts_pin; int8_t rts_pin; } uart_id_t; typedef struct { cdc_line_coding_t usb_lc; cdc_line_coding_t uart_lc; mutex_t lc_mtx; uint8_t uart_buffer[BUFFER_SIZE]; uint32_t uart_pos; mutex_t uart_mtx; uint8_t usb_buffer[BUFFER_SIZE]; uint32_t usb_pos; mutex_t usb_mtx; } uart_data_t; void uart0_irq_fn(void); void uart1_irq_fn(void); const uart_id_t UART_ID[CFG_TUD_CDC] = { { .inst = uart0, .irq = UART0_IRQ, .irq_fn = &uart0_irq_fn, .tx_pin = GPIO_UART1_TX, .rx_pin = GPIO_UART1_RX, .cts_pin = GPIO_UART1_CTS, .rts_pin = GPIO_UART1_RTS, }, { .inst = uart1, .irq = UART1_IRQ, .irq_fn = &uart1_irq_fn, .tx_pin = GPIO_UART2_TX, .rx_pin = GPIO_UART2_RX, .cts_pin = -1, .rts_pin = -1, } }; uart_data_t UART_DATA[CFG_TUD_CDC]; static inline uint databits_usb2uart(uint8_t data_bits) { switch (data_bits) { case 5: return 5; case 6: return 6; case 7: return 7; default: return 8; } } static inline uart_parity_t parity_usb2uart(uint8_t usb_parity) { switch (usb_parity) { case 1: return UART_PARITY_ODD; case 2: return UART_PARITY_EVEN; default: return UART_PARITY_NONE; } } static inline uint stopbits_usb2uart(uint8_t stop_bits) { switch (stop_bits) { case 2: return 2; default: return 1; } } void update_uart_cfg(uint8_t itf) { const uart_id_t *ui = &UART_ID[itf]; uart_data_t *ud = &UART_DATA[itf]; mutex_enter_blocking(&ud->lc_mtx); if (ud->usb_lc.bit_rate != ud->uart_lc.bit_rate) { uart_set_baudrate(ui->inst, ud->usb_lc.bit_rate); ud->uart_lc.bit_rate = ud->usb_lc.bit_rate; } if ((ud->usb_lc.stop_bits != ud->uart_lc.stop_bits) || (ud->usb_lc.parity != ud->uart_lc.parity) || (ud->usb_lc.data_bits != ud->uart_lc.data_bits)) { uart_set_format(ui->inst, databits_usb2uart(ud->usb_lc.data_bits), stopbits_usb2uart(ud->usb_lc.stop_bits), parity_usb2uart(ud->usb_lc.parity)); ud->uart_lc.data_bits = ud->usb_lc.data_bits; ud->uart_lc.parity = ud->usb_lc.parity; ud->uart_lc.stop_bits = ud->usb_lc.stop_bits; } mutex_exit(&ud->lc_mtx); } void usb_read_bytes(uint8_t itf) { uart_data_t *ud = &UART_DATA[itf]; uint32_t len = tud_cdc_n_available(itf); if (len && mutex_try_enter(&ud->usb_mtx, NULL)) { len = MIN(len, BUFFER_SIZE - ud->usb_pos); if (len) { uint32_t count; count = tud_cdc_n_read(itf, &ud->usb_buffer[ud->usb_pos], len); ud->usb_pos += count; } mutex_exit(&ud->usb_mtx); } } void usb_write_bytes(uint8_t itf) { uart_data_t *ud = &UART_DATA[itf]; if (ud->uart_pos && mutex_try_enter(&ud->uart_mtx, NULL)) { uint32_t count; count = tud_cdc_n_write(itf, ud->uart_buffer, ud->uart_pos); if (count < ud->uart_pos) memmove(ud->uart_buffer, &ud->uart_buffer[count], ud->uart_pos - count); ud->uart_pos -= count; mutex_exit(&ud->uart_mtx); if (count) tud_cdc_n_write_flush(itf); } } void usb_cdc_process(uint8_t itf) { uart_data_t *ud = &UART_DATA[itf]; mutex_enter_blocking(&ud->lc_mtx); tud_cdc_n_get_line_coding(itf, &ud->usb_lc); mutex_exit(&ud->lc_mtx); usb_read_bytes(itf); usb_write_bytes(itf); } void core1_entry(void) { tusb_init(); while (1) { int itf; int con = 0; tud_task(); for (itf = 0; itf < CFG_TUD_CDC; itf++) { if (tud_cdc_n_connected(itf)) { con = 1; usb_cdc_process(itf); } } gpio_put(GPIO_LED, con); } } static inline void uart_read_bytes(uint8_t itf) { uart_data_t *ud = &UART_DATA[itf]; const uart_id_t *ui = &UART_ID[itf]; if (uart_is_readable(ui->inst)) { mutex_enter_blocking(&ud->uart_mtx); if (ud->uart_pos < BUFFER_SIZE) { ud->uart_buffer[ud->uart_pos] = uart_getc(ui->inst); ud->uart_pos++; } else { /* drop read byte to avoid endless interrupt loop */ uart_getc(ui->inst); } mutex_exit(&ud->uart_mtx); } } void uart0_irq_fn(void) { uart_read_bytes(0); } void uart1_irq_fn(void) { uart_read_bytes(1); } void uart_write_bytes(uint8_t itf) { uart_data_t *ud = &UART_DATA[itf]; if (ud->usb_pos && mutex_try_enter(&ud->usb_mtx, NULL)) { const uart_id_t *ui = &UART_ID[itf]; uint32_t count = 0; while (uart_is_writable(ui->inst) && count < ud->usb_pos) { uart_putc_raw(ui->inst, ud->usb_buffer[count]); count++; } if (count < ud->usb_pos) memmove(ud->usb_buffer, &ud->usb_buffer[count], ud->usb_pos - count); ud->usb_pos -= count; mutex_exit(&ud->usb_mtx); } } void init_uart_data(uint8_t itf) { const uart_id_t *ui = &UART_ID[itf]; uart_data_t *ud = &UART_DATA[itf]; /* Pinmux */ gpio_set_function(ui->tx_pin, GPIO_FUNC_UART); gpio_set_function(ui->rx_pin, GPIO_FUNC_UART); /* USB CDC LC */ ud->usb_lc.bit_rate = DEF_BIT_RATE; ud->usb_lc.data_bits = DEF_DATA_BITS; ud->usb_lc.parity = DEF_PARITY; ud->usb_lc.stop_bits = DEF_STOP_BITS; /* UART LC */ ud->uart_lc.bit_rate = DEF_BIT_RATE; ud->uart_lc.data_bits = DEF_DATA_BITS; ud->uart_lc.parity = DEF_PARITY; ud->uart_lc.stop_bits = DEF_STOP_BITS; /* Buffer */ ud->uart_pos = 0; ud->usb_pos = 0; /* Mutex */ mutex_init(&ud->lc_mtx); mutex_init(&ud->uart_mtx); mutex_init(&ud->usb_mtx); /* UART start */ uart_init(ui->inst, ud->usb_lc.bit_rate); if (ui->cts_pin != -1 && ui->rts_pin != -1) uart_set_hw_flow(ui->inst, true, true); else uart_set_hw_flow(ui->inst, false, false); uart_set_format(ui->inst, databits_usb2uart(ud->usb_lc.data_bits), stopbits_usb2uart(ud->usb_lc.stop_bits), parity_usb2uart(ud->usb_lc.parity)); uart_set_fifo_enabled(ui->inst, false); /* UART RX Interrupt */ irq_set_exclusive_handler(ui->irq, ui->irq_fn); irq_set_enabled(ui->irq, true); uart_set_irq_enables(ui->inst, true, false); } int main(void) { int itf; printf("Starting osmoGTM900 playground\n"); /* control lines as outputs */ gpio_init(GPIO_nMODEM_LDO_ON); gpio_pull_up(GPIO_nMODEM_LDO_ON); gpio_set_dir(GPIO_nMODEM_LDO_ON, true); gpio_put(GPIO_nMODEM_LDO_ON, 1); /* Make the GPIO Pin HIGH */ /* no pullup on nPWON (already one in the modem) */ gpio_init(GPIO_nPWON); gpio_set_dir(GPIO_nPWON, true); gpio_put(GPIO_nPWON, 1); gpio_init(GPIO_LED); gpio_set_dir(GPIO_LED, GPIO_OUT); set_sys_clock_khz(250000, false); usbd_serial_init(); /* Rx/Tx pins are handled inside UART */ for (itf = 0; itf < CFG_TUD_CDC; itf++) { const uart_id_t *ui = &UART_ID[itf]; if (ui->cts_pin != -1 && ui->rts_pin != -1) { gpio_set_function(ui->cts_pin, GPIO_FUNC_UART); gpio_set_function(ui->rts_pin, GPIO_FUNC_UART); } init_uart_data(itf); } multicore_launch_core1(core1_entry); printf("Entering main loop\n"); sleep_ms(500); /* Delay of 500ms to settle usb */ printf("Enabling modem power\n"); gpio_put(GPIO_nMODEM_LDO_ON, 0); /* LOW */ sleep_ms(500); /* delay to settle modem */ /* power on modem by simulating button press*/ printf("Resetting modem... "); gpio_put(GPIO_nPWON, 0); sleep_ms(200); gpio_put(GPIO_nPWON, 1); printf("done\n"); while (true) { for (itf = 0; itf < CFG_TUD_CDC; itf++) { update_uart_cfg(itf); uart_write_bytes(itf); } } }