Tool/software: Linux
Hi,
I have a beagle boneblack devkit, which equipped with am3359. It running with uboot and kernel compiled from the newest sdk 'ti-processor-sdk-linux-am335x-evm-05.02.00.10'. So the kernel version number is '4.14.79'.
I connect UART4 of the devkit to my host PC (via USB2RS232 converter). By sending continuous bytes from PC to UART4 of devkit, I found that the RX DMA of uart4 does never work, that means every byte that it received is by polling(serial8250_rx_chars ) not dma.
The following are part of my dts configuration:
uart4_pins: pinmux_uart4_pins {
pinctrl-single,pins = <
0x70 (PIN_INPUT_PULLUP | MUX_MODE6) /* gpmc_wait0.uart4_rxd */
0x74 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpmc_wpn.uart4_txd */
>;
};
&uart4 {
pinctrl-names = "default";
pinctrl-0 = <&uart4_pins>;
status = "okay";
};
uart4: serial@481a8000 {
compatible = "ti,omap3-uart";
ti,hwmods = "uart5";
clock-frequency = <48000000>;
reg = <0x481a8000 0x2000>;
reg-shift = <2>;
interrupts = <45>;
status = "disabled";
dmas = <&edma 34>, <&edma 35>;
dma-names = "tx", "rx";
};
By searching the whole dts files(dts and dtsi), I confirmed the channel 34 and 35 are never refereneced by other functionalities.
I add some printk in 8250_omap.c, and have some discoveries:
Everytime when the rx char is comming, the following stacks are called sequentially:
omap_8250_handle_rx_dma >>> handle_rx_dma >>> omap_8250_rx_dma_flush >>> serial8250_rx_chars >>> omap_8250_rx_dma
It looks like, a dma slave is prepared and always waiting for the incoming bytes, but when the interrupt comes, the dma slave is flushed, none inserted into tty buffer when flush occors.
Then, all chars left in FIFO are read out by 'serial8250_rx_chars'.
Again, a new slave is prepared for the next dma, it still get flushed at the next interrupt, also none bytes are transferred by that dma.
Again and again, in the total test, none bytes from PC to devkit are transferred by DMA, they are all read out one by one in 'serial8250_rx_chars '.
The attached 'send.c' and 'recv.c' are my test program. The 'send.c' are running on PC (Ubuntu), while 'recv.c' are running on devkit(am335x).
Any help will be appreciated.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <pthread.h> #include <string.h> #include <sys/mman.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include "list.h" /* #define UUB_SIZE 0x8000 */ /* #define UUB_SIZE 0x400 */ /* #define UUB_COUNT 0x8000 */ /* #define UUB_COUNT 0x800 */ #define UUB_SIZE 512 #define UUB_COUNT 1024 #define BAUD B115200 #define UART "/dev/ttyS5" static unsigned int m_def_baud = BAUD; static char *m_def_uart = UART; struct uart_user_buf_st { unsigned char buf[UUB_SIZE]; unsigned int len; struct list_head node; }; static LIST_HEAD(uub_rcv); static LIST_HEAD(uub_rec); static pthread_mutex_t lock_rcv; static pthread_mutex_t lock_rec; static pthread_cond_t cond_rcv=PTHREAD_COND_INITIALIZER; static pthread_cond_t cond_rec=PTHREAD_COND_INITIALIZER; static pthread_t pth_rcv; static pthread_t pth_rec; #define ERR_RET(num) do { \ err = num; \ goto error; \ } while(0) static unsigned int rcvNum = 0; static unsigned int recNum = 0; static unsigned int recRunning = 1; static int uart_init(void) { int fd=-1, err=-1; struct termios ios; fd = open(m_def_uart, O_RDWR | O_NONBLOCK); if (fd < 0) return -1; if (tcgetattr(fd, &ios) < 0) ERR_RET(-1); cfsetispeed(&ios, m_def_baud); cfsetospeed(&ios, m_def_baud); ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); ios.c_oflag &= ~OPOST; ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); ios.c_cflag &= ~(CSIZE | PARENB); ios.c_cflag |= CS8; if (tcsetattr(fd, TCSANOW, &ios) < 0) ERR_RET(-1); return fd; error: if (fd > 0) close(fd); return err; } static void uart_close(int fd) { if (fd > 0) close(fd); } static int file_init(void) { int fd = -1; fd = open("/data/1.bin", O_CREAT | O_RDWR | O_TRUNC); if (fd < 0) return -1; return fd; } static void file_close(int fd) { if (fd > 0) close(fd); } static int puub_free(struct list_head *lst_rcv, struct list_head *lst_rec); static int puub_init(struct list_head *lst_rcv, struct list_head *lst_rec) { int i, err=-1; struct uart_user_buf_st *puub; /* They should be empty before initialize */ if (!list_empty(lst_rcv) || !list_empty(lst_rec)) return -1; for (i = 0; i < UUB_COUNT; i++) { puub = calloc(1, sizeof(struct uart_user_buf_st)); if (!puub) { fprintf(stderr, "Out of memory(-1)\n"); ERR_RET(-1); } list_add_tail(&puub->node, lst_rcv); } return 0; error: puub_free(lst_rcv, lst_rec); return err; } static int puub_free(struct list_head *lst_rcv, struct list_head *lst_rec) { struct uart_user_buf_st *puub, *n; if (lst_rcv) { list_for_each_entry_safe(puub, n, lst_rcv, node) free(puub); } if (lst_rec) { list_for_each_entry_safe(puub, n, lst_rec, node) free(puub); } return 0; } static struct uart_user_buf_st *puub_pop_head(struct list_head *lst) { struct uart_user_buf_st *puub; if (list_empty(lst)) return NULL; /* remove head */ puub = list_entry(lst->next, struct uart_user_buf_st, node); list_del(&puub->node); return puub; } static void puub_push_tail(struct list_head *lst, struct uart_user_buf_st *puub) { /* add to tail */ list_add_tail(&puub->node, lst); } static int read_assurance(int fd, unsigned char *buf, int size, int timeout, int *perr) { fd_set rfds, efds; struct timeval to; int ret, count; int err=0, actual_size=0; to.tv_sec = timeout; to.tv_usec = 0; while (size > 0) { FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(fd, &rfds); FD_SET(fd, &efds); ret = select(fd+1, &rfds, NULL, &efds, &to); if (ret > 0) { if (FD_ISSET(fd, &rfds)) { count = read(fd, buf+actual_size, size); if (count < 0) { fprintf(stderr, "UART Read return error\n"); ERR_RET(-1); } actual_size += count; size -= count; } else if (FD_ISSET(fd, &efds)) { fprintf(stderr, "UART select indicate error\n"); ERR_RET(-1); } } else if (ret == 0) { ERR_RET(0); } else { fprintf(stderr, "UART select return error\n"); ERR_RET(-1); } } error: if (perr) *perr = err; return actual_size; } /* static int read_assurance_continuous(int fd, unsigned char *buf, int size, int timeout, int *perr) */ /* { */ /* int count; */ /* int err=0, actual_size=0; */ /* while (size > 0) { */ /* count = read(fd, buf+actual_size, size); */ /* if (count < 0) { */ /* fprintf(stderr, "UART Read return error\n"); */ /* ERR_RET(-1); */ /* } */ /* actual_size += count; */ /* size -= count; */ /* } */ /* error: */ /* if (perr) */ /* *perr = err; */ /* return actual_size; */ /* } */ /* #define MAPSUPPORT */ #ifdef MAPSUPPORT static unsigned char *mbase = NULL; unsigned int serialin(unsigned char reg) { unsigned int val; val = *(volatile unsigned int *)((unsigned int)mbase + (reg<<2)); return val; } static int read_from_map(int fd, unsigned char *buf, int size, int timeout, int *perr) { int count; int err=0, actual_size=0; while (size > 0) { if (serialin(5) & 0x1) { *(buf+actual_size) = serialin(0); actual_size++; size--; } } return actual_size; } #endif void *rcv_proc(void *arg) { int fd = *(int *)arg, ret; struct uart_user_buf_st *uub = NULL; int err; #ifdef MAPSUPPORT do { int mapfd = open("/dev/mem", O_RDWR | O_SYNC); if (mapfd == -1) { fprintf(stderr, "Open Map Error"); pthread_exit(NULL); } mbase = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, mapfd, 0x481aa000); if (!mbase) { fprintf(stderr, "Map Error"); close(mapfd); pthread_exit(NULL); } /* while (1) { */ /* if (serialin(5) & 0x1) */ /* fprintf(stderr, "read return %x\n", serialin(0)); */ /* } */ } while(0); #endif while (1) { while (uub == NULL) { // 1. get buffer pthread_mutex_lock(&lock_rcv); if (list_empty(&uub_rcv)) pthread_cond_wait(&cond_rcv, &lock_rcv); uub = puub_pop_head(&uub_rcv); pthread_mutex_unlock(&lock_rcv); } // 2. read data ret = read_assurance(fd, uub->buf, sizeof(uub->buf), 10, &err); /* ret = read_assurance_continuous(fd, uub->buf, sizeof(uub->buf), 10, &err); */ /* #ifdef MAPSUPPORT */ /* ret = read_from_map(fd, uub->buf, sizeof(uub->buf), 10, &err); */ /* #endif */ if (ret > 0) { if (ret != sizeof(uub->buf)) fprintf(stderr, "unexpect bytes: %d\n", ret); uub->len = ret; rcvNum += ret; // 3. delivery it to rec thread pthread_mutex_lock(&lock_rec); puub_push_tail(&uub_rec, uub); pthread_cond_signal(&cond_rec); pthread_mutex_unlock(&lock_rec); uub = NULL; } if (ret == 0 // timeout || err != 0) // Error occured break; } recRunning = 0; pthread_cond_signal(&cond_rec); pthread_exit(NULL); } void *rec_proc(void *arg) { /* int fd = *(int *)arg; */ int ret; struct uart_user_buf_st *uub = NULL; while (recRunning) { // 1. get buffer while (uub == NULL && recRunning) { pthread_mutex_lock(&lock_rec); if (list_empty(&uub_rec)) pthread_cond_wait(&cond_rec, &lock_rec); uub = puub_pop_head(&uub_rec); pthread_mutex_unlock(&lock_rec); } if (!recRunning) break; // 2. write file /* ret = write(fd, uub->buf, uub->len); */ /* if (ret < 0) */ /* break; */ ret = uub->len; recNum += ret; // 3. return buffer pthread_mutex_lock(&lock_rcv); puub_push_tail(&uub_rcv, uub); pthread_cond_signal(&cond_rcv); pthread_mutex_unlock(&lock_rcv); uub = NULL; } pthread_exit(NULL); } int main(int argc, char **argv) { int uartfd=-1; int filefd=-1; int rpfd=-1; int err=0; /* rpfd = open("/dev/rp0", O_RDONLY); */ /* if (rpfd < 0) */ /* return -1; */ if (argc > 1) m_def_uart = argv[1]; if (argc > 2) { if (!strcmp(argv[2], "115200")) m_def_baud = B115200; else if (!strcmp(argv[2], "3686400")) m_def_baud = B75; } printf("Recv via '%s', baud '%d'...\n", m_def_uart, m_def_baud); uartfd = uart_init(); if (uartfd < 0) { fprintf(stderr, "Initialize UART Error\n"); /* close(rpfd); */ return -1; } if (puub_init(&uub_rcv, &uub_rec) < 0) { ERR_RET(-1); } filefd = file_init(); if (filefd < 0) { fprintf(stderr, "Initialize File Error\n"); ERR_RET(-1); } pthread_mutex_init(&lock_rcv, NULL); pthread_mutex_init(&lock_rec, NULL); pthread_create(&pth_rcv, NULL, rcv_proc, (void *)&uartfd); /* pthread_create(&pth_rcv, NULL, rcv_proc, (void *)&rpfd); */ pthread_create(&pth_rec, NULL, rec_proc, (void *)&filefd); /// ... pthread_join(pth_rcv, NULL); pthread_join(pth_rec, NULL); fprintf(stderr, "TOTAL: recvd %d, reced: %d\n", rcvNum, recNum); error: uart_close(rpfd); uart_close(uartfd); file_close(filefd); puub_free(&uub_rcv, &uub_rec); return err; }
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <pthread.h> #include <errno.h> #include <string.h> #include <time.h> #include <signal.h> #include <sys/mman.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #define BUF_SIZE 400 #define BUF_COUNT 4096 #define BAUD B115200 #define UART "/dev/ttyS5" static unsigned int m_def_baud = BAUD; static char *m_def_uart = UART; #define ERR_RET(num) do { \ err = num; \ goto error; \ } while(0) static int running = 1; static int uart_init(void) { int fd=-1, err=-1; struct termios ios; fd = open(m_def_uart, O_RDWR); if (fd < 0) return -1; if (tcgetattr(fd, &ios) < 0) ERR_RET(-1); ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); ios.c_oflag &= ~OPOST; ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); ios.c_cflag &= ~(CSIZE | PARENB); ios.c_cflag |= CS8; cfsetispeed(&ios, m_def_baud); cfsetospeed(&ios, m_def_baud); if (tcsetattr(fd, TCSANOW, &ios) < 0) ERR_RET(-1); return fd; error: if (fd > 0) close(fd); return err; } static void uart_close(int fd) { if (fd > 0) close(fd); } void sig_exit(int sig) { running = 0; } int main(int argc, char **argv) { unsigned char buf[BUF_SIZE]; int uartfd=-1; int i, ret, total=0; time_t tb, te; unsigned int total_len = BUF_COUNT*BUF_SIZE; /* parse arguments */ /* ./send uart_dev baud size_in_bytes*/ if (argc > 1) m_def_uart = argv[1]; if (argc > 2) { if (!strcmp(argv[2], "115200")) m_def_baud = B115200; else if (!strcmp(argv[2], "3686400")) m_def_baud = B75; } if (argc > 3) { total_len = atoi(argv[3]); if (total_len&(BUF_SIZE-1)) total_len = (total_len&(~(BUF_SIZE-1))) + BUF_SIZE; } printf("Send via '%s', baud '%d', size: '%d(%xh)'...\n", \ m_def_uart, m_def_baud, total_len, total_len); uartfd = uart_init(); if (uartfd < 0) { fprintf(stderr, "Initialize UART Error\n"); return -1; } for (i = 0; i < sizeof(buf); i++) buf[i] = i&0xff; tb = time(NULL); for (i = 0; i < total_len/BUF_SIZE; i++) { if (!running) break; ret = write(uartfd, buf, sizeof(buf)); if (ret < 0) { fprintf(stderr, "Err >> Write error: %s\n", strerror(errno)); break; } else { if (ret != sizeof(buf)) fprintf(stderr, "Warn>> Writed %d bytes, it should be %u\n", ret, sizeof(buf)); total += ret; /* fprintf(stderr, "Write>> %d\n", total); */ } usleep(5000); } te = time(NULL); uart_close(uartfd); fprintf(stderr, "TOTAL Send %d(%xH) Bytes, %dKBps\n", total, total, \ (te-tb > 0) ? total/1024/(int)(te-tb) : 0); return 0; }
Best Regards,
XJ