Files
Linux_Drivers/middleware/v2/sample/cvg/sample_cvg.c
sam.xiang 89f501af2a [middleware] add cvitek's multimedia framework
Change-Id: Iffc3cf32b99b95ba3ba534081a97881a2e004a14
2023-03-10 20:36:18 +08:00

1017 lines
25 KiB
C

#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <poll.h>
#include <sys/wait.h>
#include "sample_cvg.h"
#include <sys/ioctl.h>
#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; n<len; n++)
// printf("%d ", buffer[n]);
//printf("\n");
return 0;
}
static int parse_rx_data(unsigned char *buffer, int len)
{
struct cvg_blind_header *header;
int n = sizeof(*header);
uint16_t chksum;
if (len < n)
return -1;
header = (struct cvg_blind_header *)buffer;
if (len == n) {
if (!header->len && !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;
}