blob: 7ad8a9831eb41fd84785385ce7a2737830cc5a5d [file] [log] [blame]
/*
* cros-yavta -- ChromiumOS Yet Another V4L2 Test Application
*
* Copyright (C) 2005-2015 cros-yavta authors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "videodev2.h"
#include "remote.h"
int writelen(int fd, int size, const void *buf)
{
while (size > 0) {
int ret = write(fd, buf, size);
if (ret == 0) {
fprintf(stderr, "socket disconnected\n");
return -1;
} else if (ret < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
perror("write");
return ret;
}
buf += ret;
size -= ret;
}
return 0;
}
int readlen(int fd, int size, void *buf)
{
while (size > 0) {
int ret = read(fd, buf, size);
if (ret == 0) {
fprintf(stderr, "socket disconnected\n");
return -1;
} else if (ret < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
perror("read");
return ret;
}
buf += ret;
size -= ret;
}
return 0;
}
/* ------------------------------------------------------------------
* Padding
* The padding schemes are different across different platforms. 64 bits
* platforms may have more paddings or larger size for some types, ex. time_t
* and pointer. In remote yavta, both server and client call
* serialize_ioctl_payload() to remove paddings of ioctl payload before
* transmission, and call deserialize_ioctl_payload() to insert proper paddings
* after receiving data.
*/
#define IGNORE_SIZE(R) ((R) & ~IOCSIZE_MASK)
// Describe where the padding and the size are.
// The offset is relative to data before padding.
struct padding {
int offset;
int size;
};
// Padding type
// Note, the padding of x86-32 and arm-32 are different.
enum padding_type {
PADDING_TYPE_UNKNOWN = -1,
PADDING_TYPE_X86_64 = 0,
PADDING_TYPE_ARM_32,
PADDING_TYPE_X86_32,
PADDING_TYPE_NUM
};
#define PADDING_RECORD_NUM 6
struct padding_scheme {
// Payload size of ioctl excluding padding
int payload_size;
// Payload size of ioctl including padding
int payload_size_padded;
// Padding records. {0,0} means the end of records.
struct padding padding[PADDING_TYPE_NUM][PADDING_RECORD_NUM];
};
struct padding_scheme padding_v4l2_buffer = {
// x86-64, 88 bytes
// x86-32, arm-32, 68 bytes
68,
sizeof(struct v4l2_buffer),
{
[PADDING_TYPE_X86_64] = {
{20, 4}, // before timestamp
{24, 4}, // timestamp is 64bit
{28, 4}, // timestamp is 64bit
{56, 4}, // sizeof(m.planes) and sizeof(m.userptr) are 4 bytes more
{68, 4}, // padding for total size
{0, 0},
},
},
};
struct padding_scheme padding_v4l2_ext_controls = {
// x86-64, 32 bytes
// x86-32, arm-32, 24 bytes
24,
sizeof(struct v4l2_ext_controls),
{
[PADDING_TYPE_X86_64] = {
{20, 4}, // before controls
{24, 4}, // sizeof(controls) is 8 bytes more
{0, 0},
},
},
};
struct padding_scheme padding_v4l2_format = {
// x86-64, 208 bytes
// x86-32, arm-32, 204 bytes
204,
sizeof(struct v4l2_format),
{
[PADDING_TYPE_X86_64] = {
{4, 4}, // before fmt
{0, 0},
},
},
};
struct padding_scheme padding_v4l2_input = {
// x86-64, arm-32, 80 bytes
// x86-32, 76 bytes
76,
sizeof(struct v4l2_input),
{
[PADDING_TYPE_X86_64] = {
{76, 4}, // padding for total size
{0, 0},
},
[PADDING_TYPE_ARM_32] = {
{76, 4}, // padding for total size
{0, 0},
},
},
};
/* Mapping from ioctl request number to padding scheme */
struct padding_scheme *padding_schemes[1 << _IOC_NRBITS] = {
[_IOC_NR(VIDIOC_QUERYBUF)] = &padding_v4l2_buffer,
[_IOC_NR(VIDIOC_QBUF)] = &padding_v4l2_buffer,
[_IOC_NR(VIDIOC_DQBUF)] = &padding_v4l2_buffer,
[_IOC_NR(VIDIOC_PREPARE_BUF)] = &padding_v4l2_buffer,
[_IOC_NR(VIDIOC_G_EXT_CTRLS)] = &padding_v4l2_ext_controls,
[_IOC_NR(VIDIOC_S_EXT_CTRLS)] = &padding_v4l2_ext_controls,
[_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = &padding_v4l2_ext_controls,
[_IOC_NR(VIDIOC_G_FMT)] = &padding_v4l2_format,
[_IOC_NR(VIDIOC_S_FMT)] = &padding_v4l2_format,
[_IOC_NR(VIDIOC_TRY_FMT)] = &padding_v4l2_format,
[_IOC_NR(VIDIOC_ENUMINPUT)] = &padding_v4l2_input,
// NOTE, following requests have padding issues, too. Since we don't
// use them, just ignore.
// VIDIOC_G_FBUF, VIDIOC_S_FBUF (v4l2_framebuffer)
// VIDIOC_DQEVENT (v4l2_event)
// VIDIOC_CREATE_BUFS (v4l2_create_buffers)
// VIDIOC_ENUMSTD (v4l2_standard)
};
static enum padding_type determine_padding_type()
{
if (_IOC_SIZE(VIDIOC_QUERYBUF) == 88)
return PADDING_TYPE_X86_64;
if (_IOC_SIZE(VIDIOC_QUERYBUF) == 68) {
if (_IOC_SIZE(VIDIOC_ENUMINPUT) == 76)
return PADDING_TYPE_X86_32;
if (_IOC_SIZE(VIDIOC_ENUMINPUT) == 80)
return PADDING_TYPE_ARM_32;
}
return PADDING_TYPE_UNKNOWN;
}
static const struct padding *query_padding(const struct padding_scheme *scheme)
{
enum padding_type type = determine_padding_type();
assert(type != PADDING_TYPE_UNKNOWN);
if (scheme == NULL)
return NULL;
return scheme->padding[type];
}
int serialize_ioctl_payload(int request, const char *inbuf, char *outbuf)
{
const char *src = inbuf;
char *dst = outbuf;
const struct padding_scheme *scheme = padding_schemes[_IOC_NR(request)];
const struct padding *padding = query_padding(scheme);
int size;
if (padding) {
int s;
while (padding->offset) {
s = padding->offset - (dst - outbuf);
assert(s >= 0);
memcpy(dst, src, s);
src += s;
dst += s;
src += padding->size;
padding++;
}
s = scheme->payload_size - (dst - outbuf);
assert(s >= 0);
memcpy(dst, src, s);
size = scheme->payload_size;
} else {
size = _IOC_SIZE(request);
memcpy(outbuf, inbuf, size);
}
assert(size <= _IOC_SIZE(request));
return size;
}
int deserialize_ioctl_payload(int request, const char *inbuf, char *outbuf,
int len, int *req)
{
const char *src = inbuf;
char *dst = outbuf;
const struct padding_scheme *scheme = padding_schemes[_IOC_NR(request)];
const struct padding *padding = query_padding(scheme);
if (scheme && scheme->payload_size != len) {
fprintf(stderr,
"packed payload size (%d) is not expected (%d)\n", len,
scheme->payload_size);
return -1;
}
int size;
if (padding) {
int s;
while (padding->offset) {
s = padding->offset - (src - inbuf);
assert(s >= 0);
memcpy(dst, src, s);
src += s;
dst += s;
dst += padding->size;
padding++;
}
s = scheme->payload_size - (src - inbuf);
assert(s >= 0);
memcpy(dst, src, s);
size = scheme->payload_size_padded;
} else {
size = _IOC_SIZE(request);
memcpy(outbuf, inbuf, size);
}
if (req)
*req = IGNORE_SIZE(request) | size << IOCSIZE_SHIFT;
return 0;
}