commit d5fe55d2e0bd0dc61f4de4ca8a008bdfabdcb163 Author: Kamil Trzcinski Date: Mon Apr 4 13:51:29 2022 +0200 WIP diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..261bebb --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +camera_stream: *.c *.h + gcc -o camera_stream *.c \ No newline at end of file diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..0174013 --- /dev/null +++ b/buffer.c @@ -0,0 +1,72 @@ +#include "buffer.h" +#include "buffer_list.h" +#include "device.h" + +buffer_t *buffer_open(const char *name, buffer_list_t *buf_list, int index) { + buffer_t *buf = calloc(1, sizeof(buffer_t)); + device_t *dev = buf_list->device; + + buf->name = strdup(name); + buf->index = index; + buf->buf_list = buf_list; + buf->dma_fd = -1; + + if (buf_list->do_mmap) { + buf->v4l2_buffer.type = buf_list->type; + buf->v4l2_buffer.memory = V4L2_MEMORY_MMAP; + buf->v4l2_buffer.index = index; + + if (buf_list->do_mplanes) { + buf->v4l2_buffer.length = 1; + buf->v4l2_buffer.m.planes = &buf->v4l2_plane; + } + + E_XIOCTL(buf_list, dev->fd, VIDIOC_QUERYBUF, &buf->v4l2_buffer, "Cannot query buffer %d", index); + + if (buf_list->do_mplanes) { + buf->offset = buf->v4l2_plane.m.mem_offset; + buf->length = buf->v4l2_plane.length; + } else { + buf->offset = buf->v4l2_buffer.m.offset; + buf->length = buf->v4l2_buffer.length; + } + + buf->start = mmap(NULL, buf->length, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, buf->offset); + if (buf->start == MAP_FAILED) { + goto error; + } + } + + if (buf_list->do_dma) { + struct v4l2_exportbuffer v4l2_exp = {0}; + v4l2_exp.type = buf_list->type; + v4l2_exp.index = index; + v4l2_exp.plane = 0; + E_XIOCTL(buf_list, dev->fd, VIDIOC_EXPBUF, &v4l2_exp, "Can't export queue buffer=%u to DMA", index); + buf->dma_fd = v4l2_exp.fd; + } + + buffer_capture_enqueue(buf); + + return buf; + +error: + buffer_close(buf); + return NULL; +} + +void buffer_close(buffer_t *buf) +{ + if (buf == NULL) { + return; + } + + if (buf->start && buf->start != MAP_FAILED) { + munmap(buf->start, buf->length); + } + if (buf->dma_fd >= 0) { + close(buf->dma_fd); + } + free(buf->name); + free(buf); +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..649d1e0 --- /dev/null +++ b/buffer.h @@ -0,0 +1,21 @@ +#pragma once + +#include "v4l2.h" + +typedef struct buffer_s { + char *name; + struct buffer_list_s *buf_list; + int index; + void *start; + size_t offset; + size_t length; + struct v4l2_buffer v4l2_buffer; + struct v4l2_plane v4l2_plane; + int dma_fd; + bool enqueued; +} buffer_t; + +buffer_t *buffer_open(const char *name, struct buffer_list_s *buf_list, int buffer); +void buffer_close(buffer_t *buf); +bool buffer_output_dequeue(buffer_t *buf); +bool buffer_capture_enqueue(buffer_t *buf); diff --git a/buffer_list.c b/buffer_list.c new file mode 100644 index 0000000..546c20d --- /dev/null +++ b/buffer_list.c @@ -0,0 +1,158 @@ +#include "buffer.h" +#include "buffer_list.h" +#include "device.h" + +buffer_list_t *buffer_list_open(const char *name, struct device_s *dev, unsigned type) +{ + buffer_list_t *buf_list = calloc(1, sizeof(buffer_list_t)); + + buf_list->device = dev; + buf_list->name = strdup(name); + buf_list->type = type; + + switch(type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + buf_list->do_mplanes = true; + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + buf_list->do_dma = true; + buf_list->do_mmap = true; + buf_list->do_capture = true; + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + buf_list->do_dma = true; + buf_list->do_mmap = true; + buf_list->do_mplanes = true; + buf_list->do_capture = true; + break; + + default: + E_LOG_PERROR(buf_list, "Unknown type=%d", type); + goto error; + } + + return buf_list; + +error: + buffer_list_close(buf_list); + return NULL; +} + +void buffer_list_close(buffer_list_t *buf_list) +{ + if (!buf_list) { + return; + } + + if (buf_list->bufs) { + for (int i = 0; i < buf_list->nbufs; i++) { + buffer_close(buf_list->bufs[i]); + } + free(buf_list->bufs); + buf_list->bufs = NULL; + buf_list->nbufs = 0; + } + + free(buf_list->name); + free(buf_list); +} + +int buffer_list_set_format(buffer_list_t *buf_list, unsigned width, unsigned height, unsigned format) +{ + struct v4l2_format *fmt = &buf_list->v4l2_format; + + fmt->type = buf_list->type; + + if (buf_list->do_mplanes) { + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + fmt->fmt.pix.width = width; + fmt->fmt.pix.height = height; + fmt->fmt.pix.pixelformat = format; + fmt->fmt.pix.field = V4L2_FIELD_ANY; + fmt->fmt.pix.bytesperline = fourcc_to_stride(width, format); + } else { + fmt->fmt.pix_mp.colorspace = V4L2_COLORSPACE_JPEG; + fmt->fmt.pix_mp.width = width; + fmt->fmt.pix_mp.height = height; + fmt->fmt.pix_mp.pixelformat = format; + fmt->fmt.pix_mp.field = V4L2_FIELD_ANY; + fmt->fmt.pix_mp.num_planes = 1; + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = fourcc_to_stride(width, format); + } + + E_LOG_DEBUG(buf_list, "Configuring format ..."); + E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_S_FMT, fmt, "Can't set format"); + + if (fmt->fmt.pix.width != width || fmt->fmt.pix.height != height) { + E_LOG_ERROR(buf_list, "Requested resolution=%ux%u is unavailable. Got %ux%u.", + width, height, fmt->fmt.pix.width, fmt->fmt.pix.height); + } + + if (fmt->fmt.pix.pixelformat != format) { + E_LOG_ERROR(buf_list, "Could not obtain the requested format=%s; driver gave us %s", + fourcc_to_string(format).buf, + fourcc_to_string(fmt->fmt.pix.pixelformat).buf); + } + + E_LOG_INFO(buf_list, "Using: %ux%u/%s", + fmt->fmt.pix.width, fmt->fmt.pix.height, fourcc_to_string(fmt->fmt.pix.pixelformat).buf); + + return 0; + +error: + return -1; +} + +int buffer_list_request(buffer_list_t *buf_list, int nbufs) +{ + struct v4l2_requestbuffers v4l2_req = {0}; + v4l2_req.count = nbufs; + v4l2_req.type = buf_list->type; + v4l2_req.memory = buf_list->do_mmap ? V4L2_MEMORY_MMAP : V4L2_MEMORY_DMABUF; + + E_LOG_DEBUG(buf_list, "Requesting %u buffers", v4l2_req.count); + + E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_REQBUFS, &v4l2_req, "Can't request buffers"); + if (v4l2_req.count < 1) { + E_LOG_ERROR(buf_list, "Insufficient buffer memory: %u", v4l2_req.count); + } + + E_LOG_DEBUG(buf_list, "Got %u buffers", v4l2_req.count); + + buf_list->bufs = calloc(v4l2_req.count, sizeof(buffer_t*)); + buf_list->nbufs = v4l2_req.count; + + for (unsigned i = 0; i < buf_list->nbufs; i++) { + char name[64]; + sprintf(name, "%s:buf%d", buf_list->name, i); + buffer_t *buf = buffer_open(name, buf_list, i); + if (!buf) { + E_LOG_ERROR(buf_list, "Cannot open buffer: %u", i); + goto error; + } + buf_list->bufs[i] = buf; + } + + E_LOG_DEBUG(buf_list, "Opened %u buffers", buf_list->nbufs); + return 0; + +error: + return -1; +} + +int buffer_list_stream(buffer_list_t *buf_list, bool do_on) +{ + enum v4l2_buf_type type = buf_list->type; + + E_XIOCTL(buf_list, buf_list->device->fd, do_on ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type, "Cannot set streaming state"); + + return 0; + +error: + return -1; +} diff --git a/buffer_list.h b/buffer_list.h new file mode 100644 index 0000000..1ee2933 --- /dev/null +++ b/buffer_list.h @@ -0,0 +1,30 @@ +#pragma once + +#include "v4l2.h" + +typedef struct buffer_list_s { + char *name; + struct device_s *device; + buffer_t **bufs; + int nbufs; + int type; + + struct v4l2_format v4l2_format; + bool do_mplanes; + bool do_mmap; + bool do_dma; + bool do_capture; +} buffer_list_t; + +buffer_list_t *buffer_list_open(const char *name, struct device_s *dev, unsigned type); +void buffer_list_close(buffer_list_t *buf_list); + +int buffer_list_set_format(buffer_list_t *buffer_list, unsigned width, unsigned height, unsigned format); +int buffer_list_request(buffer_list_t *buf_list, int nbufs); + +bool buffer_list_wait_pool(buffer_list_t *buf_list, int timeout); + +buffer_t *buffer_list_output_enqueue(buffer_list_t *buf_list, buffer_t *dma_buf); +buffer_t *buffer_list_capture_dequeue(buffer_list_t *buf_list); + +int buffer_list_stream(buffer_list_t *buf_list, bool do_on); diff --git a/buffer_queue.c b/buffer_queue.c new file mode 100644 index 0000000..94664d8 --- /dev/null +++ b/buffer_queue.c @@ -0,0 +1,120 @@ +#include "buffer.h" +#include "buffer_list.h" +#include "device.h" + +bool buffer_output_dequeue(buffer_t *buf) +{ + // if (buf->enqueued || buf->buf_list->do_capture) { + // return false; + // } + + return false; +} + +bool buffer_capture_enqueue(buffer_t *buf) +{ + if (buf->enqueued || !buf->buf_list->do_mmap || !buf->buf_list->do_capture) { + return false; + } + + E_LOG_DEBUG(buf, "Queuing buffer..."); + E_XIOCTL(buf, buf->buf_list->device->fd, VIDIOC_QBUF, &buf->v4l2_buffer, "Can't queue buffer."); + + buf->enqueued = true; + +error: + return true; +} + +buffer_t *buffer_list_output_enqueue(buffer_list_t *buf_list, buffer_t *dma_buf) +{ + // if (dma_buf->enqueued || dma_buf->dma_fd < 0) { + // return NULL; + // } + + // struct v4l2_buffer v4l2_buf = {0}; + // struct v4l2_plane v4l2_plane = {0}; + + // v4l2_buf.type = buf_list->type; + // v4l2_buf.memory = V4L2_MEMORY_MMAP; + + // if (buf_list->do_mplanes) { + // v4l2_buf.length = 1; + // v4l2_buf.m.planes = &v4l2_plane; + // } + + // E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_DQBUF, &v4l2_buf, "Can't grab capture buffer"); + // E_LOG_DEBUG(buf_list, "Grabbed INPUT buffer=%u", v4l2_buf.index); + + return NULL; +} + +buffer_t *buffer_list_capture_dequeue(buffer_list_t *buf_list) +{ + if (!buf_list->do_mmap || !buf_list->do_capture) { + return NULL; + } + + struct v4l2_buffer v4l2_buf = {0}; + struct v4l2_plane v4l2_plane = {0}; + + v4l2_buf.type = buf_list->type; + v4l2_buf.memory = V4L2_MEMORY_MMAP; + + if (buf_list->do_mplanes) { + v4l2_buf.length = 1; + v4l2_buf.m.planes = &v4l2_plane; + } + + E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_DQBUF, &v4l2_buf, "Can't grab capture buffer"); + E_LOG_DEBUG(buf_list, "Grabbed INPUT buffer=%u, bytes=%d", + v4l2_buf.index, v4l2_buf.length); + + buffer_t *buf = buf_list->bufs[v4l2_buf.index]; + buf->v4l2_plane = v4l2_plane; + buf->v4l2_buffer = v4l2_buf; + if (buf_list->do_mplanes) { + buf->v4l2_buffer.m.planes = &buf->v4l2_plane; + } + + buf->enqueued = false; + + return buf; + +error: + return NULL; +} + +bool buffer_list_wait_pool(buffer_list_t *buf_list, int timeout) { + struct pollfd fds = {buf_list->device->fd, POLLIN, 0}; + + if (poll(&fds, 1, timeout) < 0 && errno != EINTR) { + E_LOG_ERROR(buf_list, "Can't poll encoder"); + } + + E_LOG_DEBUG(buf_list, "Polling encoder %d, %d...", errno, fds.revents); + if (fds.revents & POLLIN) { + return true; + } + if (fds.revents & POLLPRI) { + E_LOG_DEBUG(buf_list, "fd POLLPRI"); + } + if (fds.revents & POLLOUT) { + E_LOG_DEBUG(buf_list, "fd POLLOUT"); + } + if (fds.revents & POLLERR) { + E_LOG_DEBUG(buf_list, "fd POLLERR"); + device_consume_event(buf_list->device); + } + if (fds.revents & POLLHUP) { + E_LOG_DEBUG(buf_list, "fd POLLHUP"); + } + if (fds.revents & POLLNVAL) { + E_LOG_DEBUG(buf_list, "fd POLLNVAL"); + } + + return false; + +error: + return false; +} \ No newline at end of file diff --git a/camera_stream b/camera_stream new file mode 100755 index 0000000..1dd7eff Binary files /dev/null and b/camera_stream differ diff --git a/device.c b/device.c new file mode 100644 index 0000000..b4fc1a4 --- /dev/null +++ b/device.c @@ -0,0 +1,141 @@ +#include "device.h" +#include "buffer.h" +#include "buffer_list.h" + +device_t *device_open(const char *name, const char *path) { + device_t *dev = calloc(1, sizeof(device_t)); + dev->name = strdup(name); + dev->path = strdup(path); + dev->fd = open(path, O_RDWR|O_NONBLOCK); + if(dev->fd < 0) { + E_LOG_ERROR(dev, "Can't open device"); + } + + E_LOG_DEBUG(dev, "Querying device capabilities ..."); + E_XIOCTL(dev, dev->fd, VIDIOC_QUERYCAP, &dev->v4l2_cap, "Can't query device capabilities");\ + + if (!(dev->v4l2_cap.capabilities & V4L2_CAP_STREAMING)) { + E_LOG_ERROR(dev, "Device doesn't support streaming IO"); + } + + E_LOG_INFO(dev, "Device path=%s fd=%d opened", dev->path, dev->fd); + return dev; + +error: + device_close(dev); + return NULL; +} + +void device_close(device_t *dev) { + if(dev == NULL) { + return; + } + + if (dev->capture_list) { + buffer_list_close(dev->capture_list); + dev->capture_list = NULL; + } + + if (dev->output_list) { + buffer_list_close(dev->output_list); + dev->output_list = NULL; + } + + if(dev->fd >= 0) { + close(dev->fd); + } + + free(dev); +} + +int device_open_buffer_list(device_t *dev, bool do_capture, unsigned width, unsigned height, unsigned format, int nbufs) +{ + unsigned type; + char name[64]; + struct buffer_list_s **buf_list = NULL; + + if (do_capture) { + buf_list = &dev->capture_list; + + if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + sprintf(name, "%s:capture", dev->name); + } else if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) { + type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + sprintf(name, "%s:capture:mplane", dev->name); + } else { + E_LOG_ERROR(dev, "Video capture is not supported by device"); + } + } else { + buf_list = &dev->capture_list; + + if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) { + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + sprintf(name, "%s:output", dev->name); + } else if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) { + type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + sprintf(name, "%s:output:mplane", dev->name); + } else { + E_LOG_ERROR(dev, "Video output is not supported by device"); + } + } + + *buf_list = buffer_list_open(name, dev, type); + if (!*buf_list) { + goto error; + } + + if (buffer_list_set_format(*buf_list, width, height, format) < 0) { + goto error; + } + + if (buffer_list_request(*buf_list, nbufs) < 0) { + goto error; + } + + return 0; + +error: + buffer_list_close(*buf_list); + *buf_list = NULL; + return -1; +} + +int device_stream(device_t *dev, bool do_on) +{ + if (dev->capture_list) { + if (buffer_list_stream(dev->capture_list, do_on) < 0) { + return -1; + } + } + + if (dev->output_list) { + if (buffer_list_stream(dev->output_list, do_on) < 0) { + return -1; + } + } + + return 0; +} + +int device_consume_event(device_t *dev) +{ + struct v4l2_event event; + + E_LOG_DEBUG(dev, "Consuming V4L2 event ..."); + E_XIOCTL(dev, dev->fd, VIDIOC_DQEVENT, &event, "Got some V4L2 device event, but where is it?"); + + switch (event.type) { + case V4L2_EVENT_SOURCE_CHANGE: + E_LOG_INFO(dev, "Got V4L2_EVENT_SOURCE_CHANGE: source changed"); + return -1; + case V4L2_EVENT_EOS: + E_LOG_INFO(dev, "Got V4L2_EVENT_EOS: end of stream (ignored)"); + return 0; + } + + return 0; + +error: + return -1; +} diff --git a/device.h b/device.h new file mode 100644 index 0000000..4eecb4e --- /dev/null +++ b/device.h @@ -0,0 +1,21 @@ +#pragma once + +#include "v4l2.h" + +typedef struct device_s { + char *name; + char *path; + int fd; + struct v4l2_capability v4l2_cap; + + struct buffer_list_s *capture_list; + struct buffer_list_s *output_list; +} device_t; + +device_t *device_open(const char *name, const char *path); +void device_close(device_t *device); + +int device_open_buffer_list(device_t *dev, bool do_capture, unsigned width, unsigned height, unsigned format, int nbufs); +int device_consume_event(device_t *device); + +int device_stream(device_t *dev, bool do_on); diff --git a/main.c b/main.c new file mode 100644 index 0000000..b5700a1 --- /dev/null +++ b/main.c @@ -0,0 +1,82 @@ +#include "buffer.h" +#include "buffer_list.h" +#include "device.h" +#include "v4l2.h" + +int camera_width = 1920; +int camera_height = 1080; +int camera_format = V4L2_PIX_FMT_SRGGB10P; +int camera_nbufs = 4; + +device_t *camera = NULL; +device_t *isp_srgb = NULL; +device_t *isp_yuuv = NULL; +device_t *isp_yuuv_low = NULL; + +int open_camera(const char *path) +{ + camera = device_open("CAMERA", path); + if (!camera) { + return -1; + } + + if (device_open_buffer_list(camera, true, camera_width, camera_height, camera_format, camera_nbufs) < 0) { + return -1; + } + + return 0; +} + +int open_isp(const char *srgb_path, const char *yuuv_path, const char *yuuv_low_path) +{ + isp_srgb = device_open("ISP-SRGB", srgb_path); + isp_yuuv = device_open("ISP-YUUV", yuuv_path); + isp_yuuv_low = device_open("ISP-YUUV-LOW", yuuv_low_path); + + if (!isp_srgb || !isp_yuuv || !isp_yuuv_low) { + return -1; + } + + if (device_open_buffer_list(isp_srgb, false, camera_width, camera_height, camera_format, camera_nbufs) < 0 || + device_open_buffer_list(isp_yuuv, true, camera_width, camera_height, V4L2_PIX_FMT_YUYV, camera_nbufs) < 0 || + device_open_buffer_list(isp_yuuv, true, camera_width / 2, camera_height / 2, V4L2_PIX_FMT_YUYV, camera_nbufs) < 0) { + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + if (open_camera("/dev/video0") < 0) { + goto error; + } + + // if (open_isp("/dev/video13", "/dev/video14", "/dev/video15") < 0) { + // goto error; + // } + +// return; + + if (device_stream(camera, true) < 0) { + goto error; + } + //return; + + while(true) { + if (buffer_list_wait_pool(camera->capture_list, 1000000)) { + buffer_t *buf; + if (buf = buffer_list_capture_dequeue(camera->capture_list)) { + E_LOG_INFO(camera, "Got camera buffer: %p", buf); + buffer_capture_enqueue(buf); + } + } + } + +error: + device_close(isp_yuuv_low); + device_close(isp_yuuv); + device_close(isp_srgb); + device_close(camera); + return 0; +} diff --git a/v4l2.c b/v4l2.c new file mode 100644 index 0000000..7f5cd9c --- /dev/null +++ b/v4l2.c @@ -0,0 +1,69 @@ +#include "v4l2.h" + +int xioctl(int fd, int request, void *arg) +{ + int retries = XIOCTL_RETRIES; + int retval = -1; + + do { + retval = ioctl(fd, request, arg); + } while ( + retval + && retries-- + && ( + errno == EINTR + || errno == EAGAIN + || errno == ETIMEDOUT + ) + ); + + // cppcheck-suppress knownConditionTrueFalse + if (retval && retries <= 0) { + E_LOG_PERROR(NULL, "ioctl(%d) retried %u times; giving up", request, XIOCTL_RETRIES); + } + return retval; +} + +fourcc_string fourcc_to_string(unsigned format) +{ + fourcc_string fourcc; + char *ptr = fourcc.buf; + *ptr++ = format & 0x7F; + *ptr++ = (format >> 8) & 0x7F; + *ptr++ = (format >> 16) & 0x7F; + *ptr++ = (format >> 24) & 0x7F; + if (format & ((unsigned)1 << 31)) { + *ptr++ = '-'; + *ptr++ = 'B'; + *ptr++ = 'E'; + *ptr++ = '\0'; + } else { + *ptr++ = '\0'; + } + *ptr++ = 0; + return fourcc; +} + +static size_t align_size(size_t size, size_t to) +{ + return ((size + (to - 1)) & ~(to - 1)); +} + +unsigned fourcc_to_stride(unsigned width, unsigned format) +{ + switch (format) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_RGB565: + return align_size(width * 2, 32); + + case V4L2_PIX_FMT_RGB24: + return align_size(width * 3, 32); + + case V4L2_PIX_FMT_SRGGB10P: + return align_size(width * 5 / 4, 32); + + default: + E_LOG_PERROR(NULL, "Unknown format: %s", fourcc_to_string(format)); + } +} diff --git a/v4l2.h b/v4l2.h new file mode 100644 index 0000000..6f864bd --- /dev/null +++ b/v4l2.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifndef CFG_XIOCTL_RETRIES +# define CFG_XIOCTL_RETRIES 4 +#endif +#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES)) + +// assumes that name is first item +#define dev_name(dev) (dev ? *(const char**)dev : "?") +#define E_LOG_ERROR(dev, _msg, ...) do { fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__); goto error; } while(0) +#define E_LOG_PERROR(dev, _msg, ...) do { fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__); exit(-1); } while(0) +#define E_LOG_INFO(dev, _msg, ...) fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__) +#define E_LOG_VERBOSE(dev, _msg, ...) fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__) +#define E_LOG_DEBUG(dev, _msg, ...) fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__) + +typedef struct { + char buf[10]; +} fourcc_string; + +fourcc_string fourcc_to_string(unsigned format); +unsigned fourcc_to_stride(unsigned width, unsigned format); +int xioctl(int fd, int request, void *arg); + +#define E_XIOCTL(dev, _fd, _request, _value, _msg, ...) do { \ + int ret; \ + if ((ret = xioctl(_fd, _request, _value)) < 0) { \ + E_LOG_ERROR(dev, "xioctl(ret=%d): " _msg, ret, ##__VA_ARGS__); \ + } \ + } while(0)