#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sample_cvg.h" #include #define DEVICE_FILENAME "/dev/cvi_gadget" #define CVI_USB_S2D 0x81 #define CVI_USB_D2S 0x82 #define MMAP_SIZE (2<<20) #define BUF_SIZE (1<<16) #define OBJ_NUM (MMAP_SIZE/BUF_SIZE) #define CMD_SIZE 512 #define FILE_PATH_LEN 32 #define SIGCVG_CONN 44 #define SIGCVG_OUT 45 #define SIGCVG_IN 46 #define CVG_TEST_TYPE_PROTOCOL 0 #define CVG_TEST_TYPE_BLIND 1 #define CVG_MEM_TYPE_MMAP 0 #define CVG_MEM_TYPE_COPY 1 #define CVG_MEM_TYPE_ION 2 #define CVG_IO_TYPE_STREAM 0 #define CVG_IO_TYPE_BLK 1 #define CVG_ION_HEAP_ID 0 // carveout struct cvg_cmd { int dir; int file_len; char *file_path; }; struct cvg_object { FILE *fp; int io_fd; int ion_fd; int map_fd; uint8_t mem_type; uint8_t io_type; uint8_t test_type; uint8_t reserve; int queue_size; struct cvg_cmd cmd; }; struct cvg_stream_s { int index; int used; CVG_UURB_T uurb; int (*cb)(struct cvg_stream_s *); int remain; void *context; }; struct cvg_blind_header { int len; uint16_t chksum; uint16_t reserve; }; struct cvg_object cvg_obj; struct cvg_stream_s cvg_stream[OBJ_NUM]; static int connect; /* Table of CRC constants - implements x^16+x^12+x^5+1 */ static const uint16_t crc16_tab[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, }; static int start_cpu_access(int write) { int ret; int map_fd = cvg_obj.map_fd; struct dma_buf_sync sync; __u64 flags = (write?DMA_BUF_SYNC_WRITE:DMA_BUF_SYNC_READ) | DMA_BUF_SYNC_START; if (cvg_obj.mem_type != CVG_MEM_TYPE_ION) return 0; sync.flags = flags; ret = ioctl(map_fd, DMA_BUF_IOCTL_SYNC, &sync); if (ret < 0) { printf("start cpu access map_fd %d fail: %d: %s\n", map_fd, ret, strerror(errno)); return -errno; } return ret; } static int stop_cpu_access(int write) { int ret; int map_fd = cvg_obj.map_fd; struct dma_buf_sync sync; __u64 flags = (write?DMA_BUF_SYNC_WRITE:DMA_BUF_SYNC_READ) | DMA_BUF_SYNC_END; if (cvg_obj.mem_type != CVG_MEM_TYPE_ION) return 0; sync.flags = flags; ret = ioctl(map_fd, DMA_BUF_IOCTL_SYNC, &sync); if (ret < 0) { printf("stop cpu access map_fd %d fail: %d: %s\n", map_fd, ret, strerror(errno)); return -errno; } return ret; } static uint16_t crc16_ccitt(uint8_t *buf, int len) { uint16_t cksum = 0; for (int i = 0; i < len; i++) { cksum = crc16_tab[((cksum >> 8) ^ *buf++) & 0xff] ^ (cksum << 8); } return cksum; } static void fill_random_data(unsigned char *buf, int size) { int i; time_t t; srand((unsigned int)time(&t)); for (i = 0; i < size; i++) buf[i] = rand() & 0xFF; } /* 4 bytes data length, 2 bytes checksume.2 bytes padding. */ static int prepare_tx_data(unsigned char *buffer, int len) { struct cvg_blind_header *header; int n = sizeof(*header); if (len < n) return -1; header = (struct cvg_blind_header *)buffer; if (len == n) { header->len = 0; header->chksum = 0; return 0; } fill_random_data(&buffer[n], len-n); header->len = len - sizeof(*header); header->chksum = crc16_ccitt(&buffer[n], len-n); //for (n=0; nlen && !header->chksum) return 0; else return -1; } if (header->len != (len - n)) { printf("len mismatch [%d, %d]\n", header->len, len-n); return -1; } chksum = crc16_ccitt(&buffer[n], len-n); if (chksum != header->chksum) { printf("checksum mismatch [%d, %d]\n", header->chksum, chksum); return -1; } return 0; } static int cvg_stream_compl(int io_fd, int dir, int timeout) { struct pollfd event; CVG_UURB_T *uurb; struct cvg_stream_s *stream; int ret = 1, chunk; short poll_mask = (dir == CVG_UURB_DIR_IN) ? POLLIN : POLLOUT; event.fd = io_fd; event.events = poll_mask; event.events |= POLLERR; ret = poll(&event, sizeof(event)/sizeof(struct pollfd), timeout); if (!ret) { printf("timeout!\n"); ret = -1; goto _exit; } else if (ret < 0) { printf("err return %d\n", ret); goto _exit; } if (event.revents & POLLERR) { printf("err event happens\n"); ret = -1; goto _exit; } if (event.revents & poll_mask) { struct cvg_object *cvg; ret = ioctl(io_fd, (event.revents&POLLOUT)?TOP_CVG_IOCTL_REAPTXURB:TOP_CVG_IOCTL_REAPRXURB, (UINT_PTR_T)&uurb); syslog(LOG_DEBUG, "ret %d, status %d, actual %d\n", ret, uurb->status, uurb->actual_length); if (uurb->status) { printf("err:status: %d\n", uurb->status); ret = -1; goto _exit; } stream = (struct cvg_stream_s *)uurb->usercontext; cvg = (struct cvg_object *)stream->context; /* process data after stream in. */ if (dir == CVG_UURB_DIR_IN) { if (stream->cb) stream->cb(stream); } stream->used = 0; /* no more pending request.*/ syslog(LOG_DEBUG, "[%d] compl: remain %d, queue_size %d\n", stream->index, stream->remain, cvg->queue_size); if (!stream->remain) { ret = 0; goto _exit; } /* check if we need to submit another request. */ if (stream->remain > cvg->queue_size) { chunk = ((stream->remain - cvg->queue_size) > BUF_SIZE) ? BUF_SIZE : (stream->remain - cvg->queue_size); uurb->buffer_length = chunk; stream->remain = stream->remain - cvg->queue_size - chunk; stream->used = 1; /* prepare data before stream out. */ if (dir == CVG_UURB_DIR_OUT) { if (stream->cb) { if (stream->cb(stream) < 0) { ret = -1; goto _exit; } } } ret = ioctl(io_fd, TOP_CVG_IOCTL_SUBMITURB, uurb); if (ret < 0) { syslog(LOG_ERR, "io submit urb fail %d\n", ret); ret = -1; goto _exit; } syslog(LOG_DEBUG, "[%d] compl: submit request %d, ret = %d\n", stream->index, stream->remain, ret); } ret = 1; } _exit: return ret; } static int cvg_stream_io(int io_fd, uint64_t size, int timeout, int dir, int index, int depth, int (*cb)(struct cvg_stream_s *)) { int ret, chunk, remain = size, idx = index; if (io_fd < 0) { syslog(LOG_ERR, "wrong io fd!\n"); ret = -1; } /* how many stream objects we need? */ while (remain > 0) { CVG_UURB_T *uurb = &cvg_stream[idx++].uurb; struct cvg_stream_s *stream = (struct cvg_stream_s *)uurb->usercontext; chunk = remain > BUF_SIZE ? BUF_SIZE : remain; uurb->flags = dir; uurb->type = CVG_UURB_TYPE_BULK; uurb->buffer_length = chunk; stream->used = 1; stream->cb = cb; stream->remain = remain - chunk; /* prepare data before stream out. */ if (dir == CVG_UURB_DIR_OUT) { if (stream->cb) if (cb(stream) < 0) return -1; } ret = ioctl(io_fd, TOP_CVG_IOCTL_SUBMITURB, uurb); if (ret < 0) { syslog(LOG_ERR, "io submit urb fail %d\n", ret); return -1; } remain -= chunk; if (idx == (index+depth)) break; } while (cvg_stream_compl(io_fd, dir, timeout) > 0) ; return ret; } static int cvg_io(int io_fd, unsigned char *buf, uint64_t size, int timeout, int dir) { CVG_UURB_T uurb; CVG_UURB_T *result_uurb; struct pollfd event; short poll_mask = (dir == CVG_UURB_DIR_IN) ? POLLIN : POLLOUT; int ret; if (io_fd < 0) { syslog(LOG_ERR, "wrong io fd!\n"); ret = -1; } memset(&uurb, 0, sizeof(uurb)); uurb.buffer = (UINT_PTR_T)buf; uurb.flags = dir; uurb.type = CVG_UURB_TYPE_BULK; uurb.buffer_length = size; ret = ioctl(io_fd, TOP_CVG_IOCTL_SUBMITURB, &uurb); if (ret < 0) { printf("io submit urb fail %d\n", ret); return -1; } memset(&event, 0, sizeof(event)); event.fd = io_fd; event.events = poll_mask; event.events |= POLLERR; ret = poll(&event, sizeof(event)/sizeof(struct pollfd), timeout); if (!ret) { printf("timeout!\n"); ioctl(io_fd, TOP_CVG_IOCTL_DISCARDURB, &uurb); ret = -1; } else if (ret < 0) { printf("err return %d\n", ret); } else { if (event.revents & poll_mask) { ret = ioctl(io_fd, (event.revents&POLLOUT)?TOP_CVG_IOCTL_REAPTXURB:TOP_CVG_IOCTL_REAPRXURB, (UINT_PTR_T)&result_uurb); syslog(LOG_DEBUG, "ret %d, status %d, actual %d\n", ret, result_uurb->status, result_uurb->actual_length); syslog(LOG_DEBUG, "uurb: %p, result_uurb : %p\n", &uurb, result_uurb); if (result_uurb->status) { printf("err:status: %d\n", result_uurb->status); ret = -1; } else { return uurb.actual_length; } } if (event.revents & POLLERR) { syslog(LOG_ERR, "err event happens\n"); ret = -1; } } return ret; } static int rcv_cmd(struct cvg_object *cvg, unsigned char *buf) { ssize_t rd_sz = 0; cvg->cmd.file_path = (char *)malloc(FILE_PATH_LEN * sizeof(char)); memset(cvg->cmd.file_path, 0, FILE_PATH_LEN); start_cpu_access(1); memset(buf, 0, CMD_SIZE); stop_cpu_access(1); rd_sz = cvg_io(cvg->io_fd, buf, CMD_SIZE, -1, CVG_UURB_DIR_IN); if (rd_sz < 0) return -1; start_cpu_access(0); /* parse command */ syslog(LOG_DEBUG, "rd_sz: %zd\n", rd_sz); syslog(LOG_DEBUG, "\n============\n"); cvg->cmd.dir = buf[0]; cvg->cmd.file_len = (((uint64_t)(buf[1]) << 32) + ((uint64_t)(buf[2]) << 24) + ((uint64_t)(buf[3]) << 16) + ((uint64_t)(buf[4]) << 8) + ((uint64_t)(buf[5]))); memcpy(cvg->cmd.file_path, &buf[6], rd_sz - 6); stop_cpu_access(0); syslog(LOG_DEBUG, "req len:%d\n", cvg->cmd.file_len); syslog(LOG_DEBUG, "file path : %s\n", cvg->cmd.file_path); return 0; } static int load_send_data(struct cvg_object *cvg, unsigned char *buf) { size_t total = cvg->cmd.file_len; ssize_t sd_sz = 0, req_sz; cvg->fp = fopen(cvg->cmd.file_path, "rb"); if (cvg->fp == NULL) { syslog(LOG_ERR, "open fail\n"); return -1; } while (total > 0) { if (total > BUF_SIZE) req_sz = BUF_SIZE; else req_sz = total; start_cpu_access(1); sd_sz = fread(buf, 1, req_sz, cvg->fp); stop_cpu_access(1); syslog(LOG_DEBUG, "fread %zd\n", sd_sz); if (sd_sz != req_sz) { syslog(LOG_ERR, "f_read fail %d\n", errno); fclose(cvg->fp); return -1; } if (!sd_sz) { syslog(LOG_DEBUG, "f_read no more data\n"); break; } if (cvg_io(cvg->io_fd, buf, sd_sz, 5000, CVG_UURB_DIR_OUT) < 0) { syslog(LOG_ERR, "usb write fail!!\n"); fclose(cvg->fp); return -1; } total -= sd_sz; syslog(LOG_DEBUG, "rd_sz: %zu\n", sd_sz); syslog(LOG_DEBUG, "ttl_rd_size : %zu\n", total); } fclose(cvg->fp); return 0; } static int rcv_store_data(struct cvg_object *cvg, unsigned char *buf) { size_t total = cvg->cmd.file_len; ssize_t rd_sz = 0, req_sz = 0; cvg->fp = fopen(cvg->cmd.file_path, "wb"); if (cvg->fp == NULL) { syslog(LOG_ERR, "open fail\n"); return -1; } while (total > 0) { if (total > BUF_SIZE) req_sz = BUF_SIZE; else req_sz = total; rd_sz = cvg_io(cvg->io_fd, buf, req_sz, 5000, CVG_UURB_DIR_IN); if (rd_sz < 0) { syslog(LOG_ERR, "read fail!!\n"); return -1; } syslog(LOG_DEBUG, "rd_sz: %zu\n", rd_sz); total -= rd_sz; syslog(LOG_DEBUG, "ttl_rd_size : %zu\n", total); start_cpu_access(0); fwrite(buf, rd_sz, 1, cvg->fp); stop_cpu_access(0); } fclose(cvg->fp); return 0; } static int prepare_stream_out(struct cvg_stream_s *stream) { struct cvg_object *cvg = (struct cvg_object *)stream->context; FILE *fp = cvg->fp; CVG_UURB_T *uurb = &stream->uurb; int size; start_cpu_access(0); size = fread((void *)uurb->buffer, 1, uurb->buffer_length, fp); stop_cpu_access(0); if (size != uurb->buffer_length) { syslog(LOG_ERR, "f_read fail %d\n", errno); return -1; } syslog(LOG_DEBUG, "prepare stream [%d] %dB\n", stream->index, uurb->buffer_length); return 0; } static int proc_stream_in(struct cvg_stream_s *stream) { struct cvg_object *cvg = (struct cvg_object *)stream->context; FILE *fp = cvg->fp; CVG_UURB_T *uurb = &stream->uurb; start_cpu_access(1); fwrite((void *)uurb->buffer, 1, uurb->actual_length, fp); stop_cpu_access(1); syslog(LOG_DEBUG, "handle stream [%d] %dB\n", stream->index, uurb->actual_length); return 0; } static int stream_rcv_data(struct cvg_object *cvg) { int ret; cvg->fp = fopen(cvg->cmd.file_path, "wb"); if (cvg->fp == NULL) { syslog(LOG_ERR, "open fail\n"); return -1; } ret = cvg_stream_io(cvg->io_fd, cvg->cmd.file_len, -1, CVG_UURB_DIR_IN, 0, OBJ_NUM, proc_stream_in); fclose(cvg->fp); return ret; } static int blind_rcv(struct cvg_object *cvg, unsigned char *buf, int length) { int ret; cvg_stream_io(cvg->io_fd, length, -1, CVG_UURB_DIR_IN, 0, OBJ_NUM>>1, NULL); ret = parse_rx_data(buf, length); if (ret < 0) printf("parse crc fail at %d\n", length); return ret; } static int stream_send_data(struct cvg_object *cvg) { int ret; cvg->fp = fopen(cvg->cmd.file_path, "rb"); if (cvg->fp == NULL) { syslog(LOG_ERR, "open fail\n"); return -1; } ret = cvg_stream_io(cvg->io_fd, cvg->cmd.file_len, -1, CVG_UURB_DIR_OUT, 0, OBJ_NUM, prepare_stream_out); fclose(cvg->fp); return ret; } static int blind_snd(struct cvg_object *cvg, unsigned char *buf, int length) { int ret; /* limit the resource to half of the mmap. */ ret = prepare_tx_data(buf, length); if (ret >= 0) ret = cvg_stream_io(cvg->io_fd, length, -1, CVG_UURB_DIR_OUT, OBJ_NUM>>1, OBJ_NUM>>1, NULL); return ret; } static int register_connect_ioctl(int io_fd) { CVG_CONNECTSIGNAL_T cs; int ret; memset(&cs, 0, sizeof(cs)); cs.signr = SIGCVG_CONN; ret = ioctl(io_fd, TOP_CVG_IOCTL_CONNSIG, &cs); if (ret < 0) { printf("register connect signal fail %d", ret); return -1; } return 0; } static int proc_protocol_transfer(struct cvg_object *cvg, unsigned char *buffer) { int ret; /* receive commands. */ if (rcv_cmd(cvg, buffer) < 0) return -1; if (cvg->cmd.dir == CVI_USB_D2S) { /* load then send data. */ if (cvg->io_type == CVG_IO_TYPE_BLK) { ret = load_send_data(cvg, buffer); } else if (cvg->io_type == CVG_IO_TYPE_STREAM) { ret = stream_send_data(cvg); } else { printf("Unsupport io_type %d\n", cvg->io_type); ret = -1; } } else if (cvg->cmd.dir == CVI_USB_S2D) { /* recevie then store data. */ if (cvg->io_type == CVG_IO_TYPE_BLK) { ret = rcv_store_data(cvg, buffer); } else if (cvg->io_type == CVG_IO_TYPE_STREAM) { ret = stream_rcv_data(cvg); } else { printf("Unsupport io_type %d\n", cvg->io_type); ret = -1; } } else { ret = -1; } return ret; } static int proc_blind_tx(struct cvg_object *cvg, unsigned char *buffer) { int ret; int offset = 8; int start_len = sizeof(struct cvg_blind_header) + 4; int req_len = start_len; while (connect) { ret = blind_snd(cvg, buffer, req_len); if (ret < 0) exit(EXIT_FAILURE); req_len += offset; /* warp around if over the maximum buffer size. */ if (req_len > (MMAP_SIZE>>1)) req_len = start_len; } exit(EXIT_SUCCESS); } static int proc_blind_rx(struct cvg_object *cvg, unsigned char *buffer) { int ret; int offset = 8; int start_len = sizeof(struct cvg_blind_header) + 4; static int req_len = sizeof(struct cvg_blind_header) + 4; ret = blind_rcv(cvg, buffer, req_len); req_len += offset; /* warp around if over the maximum buffer size. */ if (req_len > (MMAP_SIZE>>1)) req_len = start_len; return ret; } static int proc_transfer(struct cvg_object *cvg, unsigned char *buffer) { int ret = -1; sigset_t mask; pid_t child; printf("proc_transfer start: io_type = %d\n", cvg->io_type); /* BLOCK SIGCVG_CONN signal. */ sigemptyset(&mask); sigaddset(&mask, SIGCVG_OUT); sigaddset(&mask, SIGCVG_IN); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { printf("Cannot block INOUT signal: %s.\n", strerror(errno)); exit(EXIT_FAILURE); } if (cvg->test_type == CVG_TEST_TYPE_BLIND) { child = fork(); if (child == -1) { printf("Cannot fork a tx proc: %s.\n", strerror(errno)); } else { if (!child) { /* TX uses the bottom-half memory*/ return proc_blind_tx(cvg, buffer+(MMAP_SIZE>>1)); } } } while (connect) { if (cvg->test_type == CVG_TEST_TYPE_PROTOCOL) { ret = proc_protocol_transfer(cvg, buffer); } else if (cvg->test_type == CVG_TEST_TYPE_BLIND) { /* RX uses the top-half memory*/ ret = proc_blind_rx(cvg, buffer); } else { ret = -1; } if (ret < 0) break; } printf("proc_transfer exit with %s\n", ret < 0?"fail":"success"); exit((ret < 0) ? EXIT_FAILURE : EXIT_SUCCESS); } static void query_ion_heap(int fd) { struct ion_heap_data heap_data[6]; struct ion_heap_query query; int ret; unsigned int i; memset(heap_data, 0, sizeof(heap_data)); query.cnt = 6; query.heaps = (size_t)heap_data; query.reserved0 = 0; query.reserved1 = 0; query.reserved2 = 0; ret = ioctl(fd, ION_IOC_HEAP_QUERY, &query); if (ret < 0) { printf("%s: failed\n", __func__); return; } for (i = 0; i < query.cnt; i++) { struct ion_heap_data *heap = &heap_data[i]; printf("heap name = %s, type = %d, id = %d\n", heap->name, heap->type, heap->heap_id); } } static unsigned char *ion_alloc(int fd, __u64 size, int dev_fd, __u32 heap_id, int *map_fd) { int ret; struct ion_allocation_data data = { .len = size, .heap_id_mask = 1u << heap_id, .flags = 0, .fd = 0, .unused = 0, .paddr = 0, }; unsigned char *ptr; CVG_ION_QUEUE_T q; ret = ioctl(fd, ION_IOC_ALLOC, &data); if (ret < 0) { printf("%s failed: %s\n", __func__, strerror(ret)); return NULL; } if (!data.fd) { printf("%s failed: cannot get dma_buf fd %s\n", __func__, strerror(ret)); return NULL; } *map_fd = data.fd; q.fd = data.fd; ptr = (unsigned char *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, data.fd, 0); if (ptr == MAP_FAILED) { printf("map_fd = %d mmap failed: %s\n", data.fd, strerror(errno)); return NULL; } q.vbase = (UINT_PTR_T)ptr; ret = ioctl(dev_fd, TOP_CVG_IOCTL_QUEUEION, &q); if (ret) { printf("%s failed, cannot queue ion buf, %s\n", __func__, strerror(ret)); return NULL; } return ptr; } static int wait_child_proc(pid_t child, int *status) { pid_t p = 0xFFFFFFFF; if (child > 0) { do { p = waitpid(child, status, 0); if (p == -1) { if (errno == EINTR) continue; printf("Parent: waitpid error %s.\n", strerror(errno)); return -1; } } while (p != child); if (WIFEXITED(*status) && (WEXITSTATUS(*status) == EXIT_FAILURE)) return -1; } return 0; } int test_io(int test_type, int io_type, int mem_type) { struct cvg_object *cvg = &cvg_obj; int i, ret, status; unsigned char *buffer; siginfo_t info; sigset_t mask; pid_t child = 0xFFFFFFFF; int ion_fd, map_fd = 0; cvg->io_fd = open(DEVICE_FILENAME, O_RDWR); syslog(LOG_DEBUG, "fd = %d\n", cvg->io_fd); if (cvg->io_fd <= 0) { printf("dev not ready\n"); return EXIT_FAILURE; } cvg->test_type = test_type; cvg->mem_type = mem_type; cvg->io_type = io_type; switch (mem_type) { case CVG_MEM_TYPE_COPY: /* allocate 4MB buffer. */ buffer = (unsigned char *)malloc(MMAP_SIZE); break; case CVG_MEM_TYPE_MMAP: /* get 4MB buffer from kernel. */ buffer = (unsigned char *)mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, cvg->io_fd, 0); break; case CVG_MEM_TYPE_ION: /* get 4MB buffer from ION. */ printf("open ion node\n"); ion_fd = open("/dev/ion", O_RDWR); if (ion_fd < 0) { printf("open /dev/ion failed!\n"); return -errno; } cvg->ion_fd = ion_fd; query_ion_heap(ion_fd); buffer = ion_alloc(ion_fd, MMAP_SIZE, cvg->io_fd, CVG_ION_HEAP_ID, &map_fd); cvg->map_fd = map_fd; printf("alloc dev mem 4 %p\n", buffer); break; default: printf("Unsupport memory type %d\n", mem_type); exit(EXIT_FAILURE); } if (!buffer) { syslog(LOG_ERR, "alloc dev mem failed errno %d\n", errno); return EXIT_FAILURE; } syslog(LOG_DEBUG, "alloc dev mem 4 %p\n", buffer); if (io_type == CVG_IO_TYPE_STREAM) { for (i = 0; i < OBJ_NUM; i++) { CVG_UURB_T *uurb = &cvg_stream[i].uurb; cvg_stream[i].index = i; cvg_stream[i].used = 0; cvg_stream[i].context = (void *)cvg; uurb->buffer = (UINT_PTR_T)(buffer+i*BUF_SIZE); uurb->buffer_length = BUF_SIZE; uurb->usercontext = (UINT_PTR_T)&cvg_stream[i]; } } /* Separate the resource for rx/tx blind test. */ if (cvg->test_type == CVG_TEST_TYPE_BLIND) cvg->queue_size = BUF_SIZE * ((OBJ_NUM>>1) - 1); else cvg->queue_size = BUF_SIZE * (OBJ_NUM - 1); /* BLOCK SIGCVG_CONN signal. */ sigemptyset(&mask); sigaddset(&mask, SIGCVG_CONN); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { printf("Cannot block SIGCVG_CONN: %s.\n", strerror(errno)); return EXIT_FAILURE; } /* Register the signr to cvg. The signal would be fired * immediately if the connect is true. So we need to block * the ISGCVG_CONN first. */ register_connect_ioctl(cvg->io_fd); _reconn: sigemptyset(&mask); sigaddset(&mask, SIGCVG_CONN); ret = sigwaitinfo(&mask, &info); if (ret == -1) { if (errno == EINTR) goto _reconn; printf("Parent process: sigwaitinfo() failed: %s.\n", strerror(errno)); goto _exit; } if (info.si_signo == SIGCVG_CONN) { connect = info.si_errno ? 0 : 1; printf("function %s\n", connect?"connect":"disconnect"); if (!connect) { /* wait transfer process terminated. */ if (wait_child_proc(child, &status) < 0) goto _exit; } else { /* create transfer child process. */ child = fork(); if (child == -1) { printf("Cannot fork a event handler: %s.\n", strerror(errno)); goto _exit; } else { if (!child) { return proc_transfer(cvg, buffer); } } } goto _reconn; } else { printf("other signal number %d, %d\n", ret, info.si_signo); } _exit: close(cvg->io_fd); return EXIT_SUCCESS; } void print_usage(void) { printf("cvg_test [testcase]:\n"); printf("[testcase]: 0 blocking read/write, mmap\n"); printf(" 1 blocking read/write, mem copy\n"); printf(" 2 streaming read/write, mmap\n"); printf(" 3 streaming read/write, mem copy\n"); printf(" 4 blind test\n"); printf(" 5 blocking read/write, ion\n"); printf(" 6 streaming read/write, ion\n"); } int main(int argc, char **argv) { int testcase; if (argc != 2) { print_usage(); return -1; } testcase = atoi(argv[1]); switch (testcase) { case 0: test_io(CVG_TEST_TYPE_PROTOCOL, CVG_IO_TYPE_BLK, CVG_MEM_TYPE_MMAP); break; case 1: test_io(CVG_TEST_TYPE_PROTOCOL, CVG_IO_TYPE_BLK, CVG_MEM_TYPE_COPY); break; case 2: test_io(CVG_TEST_TYPE_PROTOCOL, CVG_IO_TYPE_STREAM, CVG_MEM_TYPE_MMAP); break; case 3: test_io(CVG_TEST_TYPE_PROTOCOL, CVG_IO_TYPE_STREAM, CVG_MEM_TYPE_COPY); break; case 4: test_io(CVG_TEST_TYPE_BLIND, CVG_IO_TYPE_STREAM, CVG_MEM_TYPE_MMAP); break; case 5: test_io(CVG_TEST_TYPE_PROTOCOL, CVG_IO_TYPE_BLK, CVG_MEM_TYPE_ION); break; case 6: test_io(CVG_TEST_TYPE_PROTOCOL, CVG_IO_TYPE_STREAM, CVG_MEM_TYPE_ION); break; default: print_usage(); break; } return 0; }