Instrument Neutral Distributed Interface INDI  2.0.2
v4l2_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2005 by Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4  Based on V4L 2 Example
5  http://v4l2spec.bytesex.org/spec-single/v4l2.html#CAPTURE-EXAMPLE
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public
9  License as published by the Free Software Foundation; either
10  version 2.1 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21 */
22 
23 #include "v4l2_base.h"
24 
25 #include "ccvt.h"
26 #include "eventloop.h"
27 #include "indidevapi.h"
28 #include "indilogger.h"
29 #include "lilxml.h"
30 // PWC framerate support
31 #include "pwc-ioctl.h"
32 
33 #include <iostream>
34 
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <assert.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <stdio.h>
42 #include <cerrno>
43 #include <sys/mman.h>
44 #include <cstring>
45 #include <ctime>
46 #include <cmath>
47 #include <sys/time.h>
48 
49 #ifdef __linux__
50 #include <asm/types.h> /* for videodev2.h */
51 /* Kernel headers version */
52 #include <linux/version.h>
53 #elif __FreeBSD__
54 #define LINUX_VERSION_CODE 1
55 #define KERNEL_VERSION(...) 1
56 #endif
57 
58 #define ERRMSGSIZ 1024
59 
60 #define CLEAR(x) memset(&(x), 0, sizeof(x))
61 
62 #define XIOCTL(fd, ioctl, arg) xioctl(fd, ioctl, arg, #ioctl)
63 
64 #define DBG_STR_PIX "%c%c%c%c"
65 #define DBG_PIX(pf) ((pf) >> 0) & 0xFF, ((pf) >> 8) & 0xFF, ((pf) >> 16) & 0xFF, ((pf) >> 24) & 0xFF
66 
67 /* TODO: Before 3.17, the only way to determine a format is compressed is to
68  * consolidate a matrix with v4l2_pix_format::pixelformat and v4l2_fourcc
69  * values. After 3.17, field 'flags' in v4l2_pix_format is assumed to be
70  * properly filled. For now we rely on 'flags', but we could just check the
71  * most used pixel formats in a CCD whitelist (YUVx, RGBxxx...).
72  */
73 
74 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
75 #define DBG_STR_FLAGS "...%c .%c%c%c %c%c.%c .%c%c%c %c%c%c%c"
76 #define DBG_FLAGS(b) \
77  /* 0x00010000 */ ((b).flags & V4L2_BUF_FLAG_TSTAMP_SRC_SOE) ? 'S' : 'E', \
78  /* 0x00004000 */ ((b).flags & V4L2_BUF_FLAG_TIMESTAMP_COPY) ? 'c' : '.', \
79  /* 0x00002000 */ ((b).flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) ? 'm' : '.', \
80  /* 0x00001000 */ ((b).flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN) ? 'C' : '.', \
81  /* 0x00000800 */ ((b).flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE) ? 'I' : '.', \
82  /* 0x00000400 */ ((b).flags & V4L2_BUF_FLAG_PREPARED) ? 'p' : '.', \
83  /* 0x00000100 */ ((b).flags & V4L2_BUF_FLAG_TIMECODE) ? 'T' : '.', \
84  /* 0x00000040 */ ((b).flags & V4L2_BUF_FLAG_ERROR) ? 'E' : '.', \
85  /* 0x00000020 */ ((b).flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '.', \
86  /* 0x00000010 */ ((b).flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : '.', \
87  /* 0x00000008 */ ((b).flags & V4L2_BUF_FLAG_KEYFRAME) ? 'K' : '.', \
88  /* 0x00000004 */ ((b).flags & V4L2_BUF_FLAG_DONE) ? 'd' : '.', \
89  /* 0x00000002 */ ((b).flags & V4L2_BUF_FLAG_QUEUED) ? 'q' : '.', \
90  /* 0x00000001 */ ((b).flags & V4L2_BUF_FLAG_MAPPED) ? 'm' : '.'
91 #else
92 #define DBG_STR_FLAGS "%s"
93 #define DBG_FLAGS(b) ""
94 #endif
95 
96 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
97 #define DBG_STR_FMT "%ux%u " DBG_STR_PIX " %scompressed (%ssupported)"
98 #define DBG_FMT(f) \
99  (f).fmt.pix.width, (f).fmt.pix.height, DBG_PIX((f).fmt.pix.pixelformat), \
100  ((f).fmt.pix.flags & V4L2_FMT_FLAG_COMPRESSED) ? "" : "un", \
101  (decoder->issupportedformat((f).fmt.pix.pixelformat) ? "" : "un")
102 #else
103 #define DBG_STR_FMT "%ux%u " DBG_STR_PIX " (%ssupported)"
104 #define DBG_FMT(f) \
105  (f).fmt.pix.width, (f).fmt.pix.height, DBG_PIX((f).fmt.pix.pixelformat), \
106  (decoder->issupportedformat((f).fmt.pix.pixelformat) ? "" : "un")
107 #endif
108 
109 #define DBG_STR_BUF "#%d " DBG_STR_FLAGS " % 7d bytes %4.4s seq %d:%d stamp %ld.%06ld"
110 #define DBG_BUF(b) \
111  (b).index, DBG_FLAGS(b), (b).bytesused, \
112  ((b).memory == V4L2_MEMORY_MMAP) ? \
113  "mmap" : \
114  ((b).memory == V4L2_MEMORY_USERPTR) ? "uptr" : \
115  ((b).memory == 4 /* kernel 3.8.0: V4L2_MEMORY_DMABUF */) ? \
116  "dma" : \
117  ((b).memory == V4L2_MEMORY_OVERLAY) ? "over" : "", \
118  (b).sequence, (b).field, (b).timestamp.tv_sec, (b).timestamp.tv_usec
119 
120 using namespace std;
121 
122 /*char *entityXML(char *src) {
123  char *s = src;
124  while (*s) {
125  if ((*s == '&') || (*s == '<') || (*s == '>') || (*s == '\'') || (*s == '"'))
126  *s = '_';
127  s += 1;
128  }
129  return src;
130 }*/
131 
132 namespace INDI
133 {
134 
135 V4L2_Base::V4L2_Base()
136 {
137  frameRate.numerator = 1;
138  frameRate.denominator = 25;
139 
140  selectCallBackID = -1;
141  //dropFrameCount = 1;
142  //dropFrame = 0;
143 
144  xmax = xmin = 160;
145  ymax = ymin = 120;
146 
147  io = IO_METHOD_MMAP;
148  fd = -1;
149  buffers = nullptr;
150  n_buffers = 0;
151 
152  callback = nullptr;
153 
154  cancrop = true;
155  cansetrate = true;
156  streamedonce = false;
157 
158  v4l2_decode = new V4L2_Decode();
159  decoder = v4l2_decode->getDefaultDecoder();
160  decoder->init();
161  dodecode = true;
162 
163  bpp = 8;
164  has_ext_pix_format = false;
165  const std::vector<unsigned int> &vsuppformats = decoder->getsupportedformats();
167  "Using default decoder '%s'\n Supported V4L2 formats are:", decoder->getName());
168  for (std::vector<unsigned int>::const_iterator it = vsuppformats.begin(); it != vsuppformats.end(); ++it)
169  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%c%c%c%c ", (*it >> 0), (*it >> 8), (*it >> 16),
170  (*it >> 24));
171  //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,INDI::Logger::DBG_SESSION,"Default decoder: %s", decoder->getName());
172 
173  getframerate = nullptr;
174  setframerate = nullptr;
175 
176  reallocate_buffers = false;
177  path = nullptr;
178  uptr = nullptr;
179 
180  lxstate = LX_ACTIVE;
181  streamactive = false;
182  cropset = false;
183 }
184 
185 V4L2_Base::~V4L2_Base()
186 {
187  delete v4l2_decode;
188 }
189 
201 bool V4L2_Base::is_compressed() const
202 {
203  /* See note at top of this file */
204 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
205  switch (fmt.fmt.pix.pixelformat)
206  {
207  case V4L2_PIX_FMT_JPEG:
208  case V4L2_PIX_FMT_MJPEG:
209  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: format %c%c%c%c patched to be considered compressed",
210  __FUNCTION__, fmt.fmt.pix.pixelformat, fmt.fmt.pix.pixelformat >> 8,
211  fmt.fmt.pix.pixelformat >> 16, fmt.fmt.pix.pixelformat >> 24);
212  return true;
213 
214  default:
215  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: format %c%c%c%c has compressed flag %d",
216  __FUNCTION__, fmt.fmt.pix.pixelformat, fmt.fmt.pix.pixelformat >> 8,
217  fmt.fmt.pix.pixelformat >> 16, fmt.fmt.pix.pixelformat >> 24,
218  fmt.fmt.pix.flags & V4L2_FMT_FLAG_COMPRESSED);
219  return fmt.fmt.pix.flags & V4L2_FMT_FLAG_COMPRESSED;
220  }
221 #else
222  switch (fmt.fmt.pix.pixelformat)
223  {
224  case V4L2_PIX_FMT_GREY:
225  /* case V4L2_PIX_FMT... add other uncompressed and supported formats here */
226  return false;
227 
228  default:
229  return true;
230  }
231 #endif
232 }
233 
245 int V4L2_Base::xioctl(int fd, int request, void * arg, char const * const request_str)
246 {
247  int r = -1;
248 
249  do
250  {
251  r = ioctl(fd, request, arg);
252  }
253  while (-1 == r && EINTR == errno);
254 
255  if (-1 == r)
256  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: ioctl 0x%08X/%s received errno %d (%s)", __FUNCTION__,
257  request, request_str, errno, strerror(errno));
258 
259  return r;
260 }
261 
262 /* @internal Setting a V4L2 format through ioctl VIDIOC_S_FMT
263  *
264  * If the format type is non-zero, this function executes ioctl
265  * VIDIOC_S_FMT on the argument data format, and updates the instance
266  * data format on success. If an error arises, the instance data
267  * format is left unmodified.
268  *
269  * If the format type is zero, this function executes ioctl
270  * VIDIOC_G_FMT on a temporary data format, and updates the instance
271  * data format on success. If an error arises, the instance data
272  * format is left unmodified.
273  *
274  * @warning If the format type is non-zero and the device streamed
275  * at least once before the call, the device is closed and reopened
276  * before updating the format.
277  *
278  * @warning If successful, this function updates the instance data
279  * format 'fmt'.
280  *
281  * @note The frame decoder format is updated with the resulting format,
282  * and the instance depth field 'bpp' is updated with the resulting
283  * frame decoder depth.
284  *
285  * @param new_fmt is the v4l2_format to set, eventually with type set
286  * to zero to refresh the instance format with the current device format.
287  * @return 0 if ioctl is successful, or -1 with error message updated.
288  */
289 int V4L2_Base::ioctl_set_format(struct v4l2_format new_fmt, char * errmsg)
290 {
291  /* Reopen device if it streamed at least once and we want to update the format*/
292  if (streamedonce && new_fmt.type)
293  {
294  close_device();
295 
296  if (open_device(path, errmsg))
297  {
298  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed reopening device %s (%s)", __FUNCTION__, path,
299  errmsg);
300  return -1;
301  }
302  }
303 
304  /* Trying format with VIDIOC_TRY_FMT has no interesting advantage here */
305  if (false)
306  {
307  if (-1 == XIOCTL(fd, VIDIOC_TRY_FMT, &new_fmt))
308  {
309  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed VIDIOC_TRY_FMT with " DBG_STR_FMT,
310  __FUNCTION__, DBG_FMT(new_fmt));
311  return errno_exit("VIDIOC_TRY_FMT", errmsg);
312  }
313  }
314 
315  if (new_fmt.type)
316  {
317  /* Set format */
318  if (-1 == XIOCTL(fd, VIDIOC_S_FMT, &new_fmt))
319  {
320  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed VIDIOC_S_FMT with " DBG_STR_FMT, __FUNCTION__,
321  DBG_FMT(new_fmt));
322  return errno_exit("VIDIOC_S_FMT", errmsg);
323  }
324  }
325  else
326  {
327  /* Retrieve format */
328  new_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
329  if (-1 == XIOCTL(fd, VIDIOC_G_FMT, &new_fmt))
330  {
331  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed VIDIOC_G_FMT", __FUNCTION__);
332  return errno_exit("VIDIOC_G_FMT", errmsg);
333  }
334  }
335 
336  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: current format " DBG_STR_FMT, __FUNCTION__,
337  DBG_FMT(new_fmt));
338 
339  /* Update internals */
340  decoder->setformat(new_fmt, has_ext_pix_format);
341  this->bpp = decoder->getBpp();
342 
343  /* Assign the format as current */
344  fmt = new_fmt;
345 
346  return 0;
347 }
348 
349 int V4L2_Base::errno_exit(const char * s, char * errmsg)
350 {
351  fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
352  snprintf(errmsg, ERRMSGSIZ, "%s error %d, %s\n", s, errno, strerror(errno));
353 
354  if (streamactive)
355  stop_capturing(errmsg);
356 
357  return -1;
358 }
359 
360 void V4L2_Base::doDecode(bool d)
361 {
362  dodecode = d;
363 }
364 
365 int V4L2_Base::connectCam(const char * devpath, char * errmsg, int pixelFormat, int width, int height)
366 {
367  INDI_UNUSED(pixelFormat);
368  INDI_UNUSED(width);
369  INDI_UNUSED(height);
370  selectCallBackID = -1;
371  cancrop = true;
372  cansetrate = true;
373  streamedonce = false;
374  frameRate.numerator = 1;
375  frameRate.denominator = 25;
376 
377  if (open_device(devpath, errmsg) < 0)
378  return -1;
379 
380  path = devpath;
381 
382  if (check_device(errmsg) < 0)
383  return -1;
384 
385  //cerr << "V4L2 Check: All successful, returning\n";
386  return fd;
387 }
388 
389 void V4L2_Base::disconnectCam(bool stopcapture)
390 {
391  if (selectCallBackID != -1)
392  rmCallback(selectCallBackID);
393 
394  if (stopcapture)
395  {
396  char errmsg[ERRMSGSIZ] = {0};
397  stop_capturing(errmsg);
398  }
399 
400  //uninit_device (errmsg);
401 
402  close_device();
403 
404  //fprintf(stderr, "Disconnect cam\n");
405 }
406 
407 bool V4L2_Base::isLXmodCapable()
408 {
409  if (!(strcmp((const char *)cap.driver, "pwc")))
410  return true;
411  else
412  return false;
413 }
414 
415 /* @internal Calculate epoch time shift
416  *
417  * The clock CLOCK_MONOTONIC starts counting from an undefined origin (boot time
418  * for instance). This function computes the offset between the current time returned
419  * by gettimeofday and the monotonic time returned by clock_gettime in milliseconds.
420  * This value can then be used to determine the time and date of frames from their
421  * timestamp as returned by the kernel.
422  *
423  * Code provided at:
424  * http://stackoverflow.com/questions/10266451/where-does-v4l2-buffer-timestamp-value-starts-counting
425  *
426  * @return the milliseconds offset to apply to the timestamp returned by gettimeofday for
427  * it to have the same reference as clock_gettime.
428  */
429 //static long getEpochTimeShift()
430 //{
431 // struct timeval epochtime = { 0, 0 };
432 // struct timespec vsTime = { 0, 0 };
433 //
434 // gettimeofday(&epochtime, nullptr);
435 // clock_gettime(CLOCK_MONOTONIC, &vsTime);
436 //
437 // long const uptime_ms = vsTime.tv_sec * 1000 + (long)round(vsTime.tv_nsec / 1000000.0);
438 // long const epoch_ms = epochtime.tv_sec * 1000 + (long)round(epochtime.tv_usec / 1000.0);
439 //
440 // long const epoch_shift = epoch_ms - uptime_ms;
441 // //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"%s: epoch shift is %ld",__FUNCTION__,epoch_shift);
442 //
443 // return epoch_shift;
444 //}
445 
446 /* @brief Reading a frame from the V4L2 driver.
447  *
448  * This function will attempt to read a frame with the adequate
449  * method for the device, and forward the frame read to the configured
450  * decoder and/or recorder.
451  *
452  * With the MMAP method, the first available buffer is dequeued to read
453  * the embedded frame. If the frame is marked erroneous by the driver, or
454  * the frame is known to be uncompressed but its length doesn't match the
455  * expected size, the buffer is re-enqueued immediately.
456  *
457  * Although only the MMAP method is actually supported, two other methods
458  * are also implemented:
459  * - With the READ method, the frame is read directly from the device
460  * descriptor, using the first buffer characteristics are address and
461  * length. But no processing is done actually.
462  * - With the USERPTR method, the first available buffer is dequeued, then
463  * verified against the buffer list. No processing is done actually, and
464  * the buffer is immediately requeued.
465  *
466  * @param errmsg is the error messsage updated in case of error.
467  * @return 0 if frame read is processed, or -1 with error message updated.
468  */
469 int V4L2_Base::read_frame(char * errmsg)
470 {
471  unsigned int i;
472  //cerr << "in read Frame" << endl;
473 
474  switch (io)
475  {
476  case IO_METHOD_READ:
477  cerr << "in read Frame method read" << endl;
478  if (-1 == read(fd, buffers[0].start, buffers[0].length))
479  {
480  switch (errno)
481  {
482  case EAGAIN:
483  return 0;
484  case EIO:
485  /* Could ignore EIO, see spec. */
486  /* fall through */
487  default:
488  return errno_exit("read", errmsg);
489  }
490  }
491  //process_image (buffers[0].start);
492  break;
493 
494  case IO_METHOD_MMAP:
495  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: using MMAP to recover frame buffer", __FUNCTION__);
496  CLEAR(buf);
497 
498  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
499  buf.memory = V4L2_MEMORY_MMAP;
500 
501  /* For debugging purposes */
502  if (false)
503  {
504  for (i = 0; i < n_buffers; ++i)
505  {
506  buf.index = i;
507  if (-1 == XIOCTL(fd, VIDIOC_QUERYBUF, &buf))
508  switch (errno)
509  {
510  case EINVAL:
512  "%s: invalid buffer query, doing as if buffer was in output queue",
513  __FUNCTION__);
514  break;
515 
516  default:
517  return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QUERYBUF", errmsg);
518  }
519 
520  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: " DBG_STR_BUF, __FUNCTION__, DBG_BUF(buf));
521  }
522  }
523 
524  if (-1 == XIOCTL(fd, VIDIOC_DQBUF, &buf))
525  switch (errno)
526  {
527  case EAGAIN:
529  "%s: no buffer found with DQBUF ioctl (EAGAIN) - frame not ready or not requested",
530  __FUNCTION__);
531  return 0;
532 
533  case EIO:
534  /* Could ignore EIO, see spec. */
535  /* Fall through */
537  "%s: transitory internal error with DQBUF ioctl (EIO)", __FUNCTION__);
538  return 0;
539 
540  case EINVAL:
541  case EPIPE:
542  default:
543  return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_DQBUF", errmsg);
544  }
545 
546  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: buffer #%d dequeued from fd:%d\n", __FUNCTION__,
547  buf.index, fd);
548 
549  if (buf.flags & V4L2_BUF_FLAG_ERROR)
550  {
552  "%s: recoverable error with DQBUF ioctl (BUF_FLAG_ERROR) - frame should be dropped",
553  __FUNCTION__);
554  if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
555  return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);
556  buf.bytesused = 0;
557  return 0;
558  }
559 
560  if (!is_compressed() && buf.bytesused != fmt.fmt.pix.sizeimage)
561  {
563  "%s: frame is %d-byte long, expected %d - frame should be dropped", __FUNCTION__,
564  buf.bytesused, fmt.fmt.pix.sizeimage);
565 
566  if (false)
567  {
568  unsigned char const * b = (unsigned char const *)buffers[buf.index].start;
569  unsigned char const * end = b + buf.bytesused;
570 
571  do
573  "%s: [%p] %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
574  __FUNCTION__, b, b[0 * 4 + 0], b[0 * 4 + 1], b[0 * 4 + 2], b[0 * 4 + 3],
575  b[1 * 4 + 0], b[1 * 4 + 1], b[1 * 4 + 2], b[1 * 4 + 3], b[2 * 4 + 0], b[2 * 4 + 1],
576  b[2 * 4 + 2], b[2 * 4 + 3], b[3 * 4 + 0], b[3 * 4 + 1], b[3 * 4 + 2],
577  b[3 * 4 + 3]);
578  while ((b += 16) < end);
579  }
580 
581  if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
582  return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);
583  buf.bytesused = 0;
584  return 0;
585  }
586 
587 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
588  /* TODO: the timestamp can be checked against the expected exposure to validate the frame - doesn't work, yet */
589  switch (buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK)
590  {
591  case V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN:
592  /* FIXME: try monotonic clock when timestamp clock type is unknown */
593  case V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC:
594  {
595  struct timespec uptime = { 0, 0 };
596  clock_gettime(CLOCK_MONOTONIC, &uptime);
597 
598  struct timeval epochtime = { 0, 0 };
599  /*gettimeofday(&epochtime, nullptr); uncomment this to get the timestamp from epoch start */
600 
601  float const secs =
602  (epochtime.tv_sec - uptime.tv_sec + buf.timestamp.tv_sec) +
603  (epochtime.tv_usec - uptime.tv_nsec / 1000.0f + buf.timestamp.tv_usec) / 1000000.0f;
604 
605  if (V4L2_BUF_FLAG_TSTAMP_SRC_SOE == (buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK))
606  {
608  "%s: frame exposure started %.03f seconds ago", __FUNCTION__, -secs);
609  }
610  else if (V4L2_BUF_FLAG_TSTAMP_SRC_EOF == (buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK))
611  {
613  "%s: frame finished capturing %.03f seconds ago", __FUNCTION__, -secs);
614  }
615  else
616  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: unsupported timestamp in frame",
617  __FUNCTION__);
618 
619  break;
620  }
621 
622  case V4L2_BUF_FLAG_TIMESTAMP_COPY:
623  default:
624  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: no usable timestamp found in frame",
625  __FUNCTION__);
626  }
627 #endif
628 
629  /* TODO: there is probably a better error handling than asserting the buffer index */
630  assert(buf.index < n_buffers);
631 
632  if (dodecode)
633  {
634  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: [%p] decoding %d-byte buffer %p cropset %c",
635  __FUNCTION__, decoder, buf.bytesused, buffers[buf.index].start, cropset ? 'Y' : 'N');
636  decoder->decode((unsigned char *)(buffers[buf.index].start), &buf, m_Native);
637  }
638 
639  /*
640  if (dorecord)
641  {
642  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: [%p] recording %d-byte buffer %p", __FUNCTION__,
643  recorder, buf.bytesused, buffers[buf.index].start);
644  recorder->writeFrame((unsigned char *)(buffers[buf.index].start));
645  }
646  */
647 
648  //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"lxstate is %d, dropFrame %c\n", lxstate, (dropFrame?'Y':'N'));
649 
650  /* Requeue buffer */
651  if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
652  return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);
653 
654  if (lxstate == LX_ACTIVE)
655  {
656  /* Call provided callback function if any */
657  //if (callback && !dorecord)
658  if (callback)
659  (*callback)(uptr);
660  }
661 
662  if (lxstate == LX_TRIGGERED)
663  lxstate = LX_ACTIVE;
664 
665  break;
666 
667  case IO_METHOD_USERPTR:
668  cerr << "in read Frame method userptr" << endl;
669  CLEAR(buf);
670 
671  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
672  buf.memory = V4L2_MEMORY_USERPTR;
673 
674  if (-1 == XIOCTL(fd, VIDIOC_DQBUF, &buf))
675  {
676  switch (errno)
677  {
678  case EAGAIN:
679  return 0;
680  case EIO:
681  /* Could ignore EIO, see spec. */
682  /* fall through */
683  default:
684  errno_exit("VIDIOC_DQBUF", errmsg);
685  }
686  }
687 
688  for (i = 0; i < n_buffers; ++i)
689  if (buf.m.userptr == (unsigned long)buffers[i].start && buf.length == buffers[i].length)
690  break;
691 
692  assert(i < n_buffers);
693 
694  //process_image ((void *) buf.m.userptr);
695 
696  if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
697  errno_exit("ReadFrame IO_METHOD_USERPTR: VIDIOC_QBUF", errmsg);
698 
699  break;
700  }
701 
702  return 0;
703 }
704 
705 int V4L2_Base::stop_capturing(char * errmsg)
706 {
707  enum v4l2_buf_type type;
708 
709  switch (io)
710  {
711  case IO_METHOD_READ:
712  /* Nothing to do. */
713  break;
714 
715  case IO_METHOD_MMAP:
716  /* Kernel 3.11 problem with streamoff: vb2_is_busy(queue) remains true so we can not change anything without diconnecting */
717  /* It seems that device should be closed/reopened to change any capture format settings. From V4L2 API Spec. (Data Negotiation) */
718  /* Switching the logical stream or returning into "panel mode" is possible by closing and reopening the device. */
719  /* Drivers may support a switch using VIDIOC_S_FMT. */
720  case IO_METHOD_USERPTR:
721  // N.B. I used this as a hack to solve a problem with capturing a frame
722  // long time ago. I recently tried taking this hack off, and it worked fine!
723 
724  type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
725  if (selectCallBackID != -1)
726  {
727  IERmCallback(selectCallBackID);
728  selectCallBackID = -1;
729  }
730  streamactive = false;
731  if (-1 == XIOCTL(fd, VIDIOC_STREAMOFF, &type))
732  return errno_exit("VIDIOC_STREAMOFF", errmsg);
733  break;
734  }
735  //uninit_device(errmsg);
736 
737  return 0;
738 }
739 
740 int V4L2_Base::start_capturing(char * errmsg)
741 {
742  unsigned int i;
743  enum v4l2_buf_type type;
744 
745  if (!streamedonce)
746  init_device(errmsg);
747 
748  switch (io)
749  {
750  case IO_METHOD_READ:
751  /* Nothing to do. */
752  break;
753 
754  case IO_METHOD_MMAP:
755  for (i = 0; i < n_buffers; ++i)
756  {
757  struct v4l2_buffer buf;
758 
759  CLEAR(buf);
760 
761  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
762  buf.memory = V4L2_MEMORY_MMAP;
763  buf.index = i;
764  //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"v4l2_start_capturing: enqueuing buffer %d for fd=%d\n", buf.index, fd);
765  /*if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
766  return errno_exit ("StartCapturing IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);*/
767  XIOCTL(fd, VIDIOC_QBUF, &buf);
768  }
769 
770  type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
771  if (-1 == XIOCTL(fd, VIDIOC_STREAMON, &type))
772  return errno_exit("VIDIOC_STREAMON", errmsg);
773 
774  selectCallBackID = IEAddCallback(fd, newFrame, this);
775  streamactive = true;
776 
777  break;
778 
779  case IO_METHOD_USERPTR:
780  for (i = 0; i < n_buffers; ++i)
781  {
782  struct v4l2_buffer buf;
783 
784  CLEAR(buf);
785 
786  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
787  buf.memory = V4L2_MEMORY_USERPTR;
788  buf.m.userptr = (unsigned long)buffers[i].start;
789  buf.length = buffers[i].length;
790 
791  if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
792  return errno_exit("StartCapturing IO_METHOD_USERPTR: VIDIOC_QBUF", errmsg);
793  }
794 
795  type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
796 
797  if (-1 == XIOCTL(fd, VIDIOC_STREAMON, &type))
798  return errno_exit("VIDIOC_STREAMON", errmsg);
799 
800  break;
801  }
802  //if (dropFrameEnabled)
803  //dropFrame = dropFrameCount;
804  streamedonce = true;
805  return 0;
806 }
807 
808 void V4L2_Base::newFrame(int /*fd*/, void * p)
809 {
810  char errmsg[ERRMSGSIZ];
811 
812  ((V4L2_Base *)(p))->read_frame(errmsg);
813 }
814 
815 int V4L2_Base::uninit_device(char * errmsg)
816 {
817  switch (io)
818  {
819  case IO_METHOD_READ:
820  free(buffers[0].start);
821  break;
822 
823  case IO_METHOD_MMAP:
824  for (unsigned int i = 0; i < n_buffers; ++i)
825  if (-1 == munmap(buffers[i].start, buffers[i].length))
826  return errno_exit("munmap", errmsg);
827  break;
828 
829  case IO_METHOD_USERPTR:
830  for (unsigned int i = 0; i < n_buffers; ++i)
831  free(buffers[i].start);
832  break;
833  }
834 
835  free(buffers);
836 
837  return 0;
838 }
839 
840 void V4L2_Base::init_read(unsigned int buffer_size)
841 {
842  buffers = (buffer *)calloc(1, sizeof(*buffers));
843 
844  if (!buffers)
845  {
846  fprintf(stderr, "Out of memory\n");
847  exit(EXIT_FAILURE);
848  }
849 
850  buffers[0].length = buffer_size;
851  buffers[0].start = malloc(buffer_size);
852 
853  if (!buffers[0].start)
854  {
855  fprintf(stderr, "Out of memory\n");
856  exit(EXIT_FAILURE);
857  }
858 }
859 
860 int V4L2_Base::init_mmap(char * errmsg)
861 {
862  struct v4l2_requestbuffers req;
863 
864  CLEAR(req);
865 
866  req.count = 4;
867  //req.count = 1;
868  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
869  req.memory = V4L2_MEMORY_MMAP;
870 
871  if (-1 == XIOCTL(fd, VIDIOC_REQBUFS, &req))
872  {
873  if (EINVAL == errno)
874  {
875  fprintf(stderr, "%.*s does not support memory mapping\n", (int)sizeof(dev_name), dev_name);
876  snprintf(errmsg, ERRMSGSIZ, "%.*s does not support memory mapping\n", (int)sizeof(dev_name), dev_name);
877  return -1;
878  }
879  else
880  {
881  return errno_exit("VIDIOC_REQBUFS", errmsg);
882  }
883  }
884 
885  if (req.count < 2)
886  {
887  fprintf(stderr, "Insufficient buffer memory on %.*s\n", (int)sizeof(dev_name), dev_name);
888  snprintf(errmsg, ERRMSGSIZ, "Insufficient buffer memory on %.*s\n", (int)sizeof(dev_name), dev_name);
889  return -1;
890  }
891 
892  buffers = (buffer *)calloc(req.count, sizeof(*buffers));
893 
894  if (!buffers)
895  {
896  fprintf(stderr, "buffers. Out of memory\n");
897  strncpy(errmsg, "buffers. Out of memory\n", ERRMSGSIZ);
898  return -1;
899  }
900 
901  for (n_buffers = 0; n_buffers < req.count; n_buffers++)
902  {
903  struct v4l2_buffer buf;
904 
905  CLEAR(buf);
906 
907  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
908  buf.memory = V4L2_MEMORY_MMAP;
909  buf.index = n_buffers;
910 
911  if (-1 == XIOCTL(fd, VIDIOC_QUERYBUF, &buf))
912  return errno_exit("VIDIOC_QUERYBUF", errmsg);
913 
914  buffers[n_buffers].length = buf.length;
915  buffers[n_buffers].start = mmap(nullptr /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */,
916  MAP_SHARED /* recommended */, fd, buf.m.offset);
917 
918  if (MAP_FAILED == buffers[n_buffers].start)
919  return errno_exit("mmap", errmsg);
920  }
921 
922  return 0;
923 }
924 
925 void V4L2_Base::init_userp(unsigned int buffer_size)
926 {
927  struct v4l2_requestbuffers req;
928  char errmsg[ERRMSGSIZ];
929 
930  CLEAR(req);
931 
932  req.count = 4;
933  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
934  req.memory = V4L2_MEMORY_USERPTR;
935 
936  if (-1 == XIOCTL(fd, VIDIOC_REQBUFS, &req))
937  {
938  if (EINVAL == errno)
939  {
940  fprintf(stderr, "%.*s does not support user pointer i/o\n", (int)sizeof(dev_name), dev_name);
941  exit(EXIT_FAILURE);
942  }
943  else
944  {
945  errno_exit("VIDIOC_REQBUFS", errmsg);
946  }
947  }
948 
949  buffers = (buffer *)calloc(4, sizeof(*buffers));
950 
951  if (!buffers)
952  {
953  fprintf(stderr, "Out of memory\n");
954  exit(EXIT_FAILURE);
955  }
956 
957  for (n_buffers = 0; n_buffers < 4; ++n_buffers)
958  {
959  buffers[n_buffers].length = buffer_size;
960  buffers[n_buffers].start = malloc(buffer_size);
961 
962  if (!buffers[n_buffers].start)
963  {
964  fprintf(stderr, "Out of memory\n");
965  exit(EXIT_FAILURE);
966  }
967  }
968 }
969 
970 int V4L2_Base::check_device(char * errmsg)
971 {
972  struct v4l2_input input_avail;
973 
974  if (-1 == XIOCTL(fd, VIDIOC_QUERYCAP, &cap))
975  {
976  if (EINVAL == errno)
977  {
978  fprintf(stderr, "%.*s is no V4L2 device\n", (int)sizeof(dev_name), dev_name);
979  snprintf(errmsg, ERRMSGSIZ, "%.*s is no V4L2 device\n", (int)sizeof(dev_name), dev_name);
980  return -1;
981  }
982  else
983  {
984  return errno_exit("VIDIOC_QUERYCAP", errmsg);
985  }
986  }
987 
988  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Driver %.*s (version %u.%u.%u)", (int)sizeof(cap.driver),
989  cap.driver, (cap.version >> 16) & 0xFF, (cap.version >> 8) & 0xFF, (cap.version & 0xFF));
990  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " card: %.*s", (int)sizeof(cap.card), cap.card);
991  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " bus: %.*s", (int)sizeof(cap.bus_info), cap.bus_info);
992 
993  setframerate = &V4L2_Base::stdsetframerate;
994  getframerate = &V4L2_Base::stdgetframerate;
995 
996  // if (!(strncmp((const char *)cap.driver, "pwc", sizeof(cap.driver))))
997  // {
998  //unsigned int qual = 3;
999  //pwc driver does not allow to get current fps with VIDIOCPWC
1000  //frameRate.numerator=1; // using default module load fps
1001  //frameRate.denominator=10;
1002  //if (ioctl(fd, VIDIOCPWCSLED, &qual)) {
1003  // DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"ioctl: can't set pwc video quality to High (uncompressed).\n");
1004  //}
1005  //else
1006  // DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG," Setting pwc video quality to High (uncompressed)\n");
1007  //setframerate=&V4L2_Base::pwcsetframerate;
1008  // }
1009 
1010  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Driver capabilities:");
1011  if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
1012  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_CAPTURE");
1013  if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)
1014  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_OUTPUT");
1015  if (cap.capabilities & V4L2_CAP_VIDEO_OVERLAY)
1016  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_OVERLAY");
1017  if (cap.capabilities & V4L2_CAP_VBI_CAPTURE)
1018  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VBI_CAPTURE");
1019  if (cap.capabilities & V4L2_CAP_VBI_OUTPUT)
1020  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VBI_OUTPUT");
1021  if (cap.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE)
1022  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_SLICED_VBI_CAPTURE");
1023  if (cap.capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
1024  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_SLICED_VBI_OUTPUT");
1025  if (cap.capabilities & V4L2_CAP_RDS_CAPTURE)
1026  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_RDS_CAPTURE");
1027  if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)
1028  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_OUTPUT_OVERLAY");
1029  if (cap.capabilities & V4L2_CAP_TUNER)
1030  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_TUNER");
1031  if (cap.capabilities & V4L2_CAP_AUDIO)
1032  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_AUDIO");
1033  if (cap.capabilities & V4L2_CAP_RADIO)
1034  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_RADIO");
1035  if (cap.capabilities & V4L2_CAP_READWRITE)
1036  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_READWRITE");
1037  if (cap.capabilities & V4L2_CAP_ASYNCIO)
1038  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_ASYNCIO");
1039  if (cap.capabilities & V4L2_CAP_STREAMING)
1040  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_STREAMING");
1041  /*if (cap.capabilities & V4L2_CAP_EXT_PIX_FORMAT) {
1042  has_ext_pix_format=true;
1043  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG," V4L2_CAP_EXT_PIX_FORMAT\n");
1044  }*/
1045  if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
1046  {
1047  fprintf(stderr, "%.*s is no video capture device\n", (int)sizeof(dev_name), dev_name);
1048  snprintf(errmsg, ERRMSGSIZ, "%.*s is no video capture device", (int)sizeof(dev_name), dev_name);
1049  return -1;
1050  }
1051 
1052  switch (io)
1053  {
1054  case IO_METHOD_READ:
1055  if (!(cap.capabilities & V4L2_CAP_READWRITE))
1056  {
1057  fprintf(stderr, "%.*s does not support read i/o", (int)sizeof(dev_name), dev_name);
1058  snprintf(errmsg, ERRMSGSIZ, "%.*s does not support read i/o", (int)sizeof(dev_name), dev_name);
1059  return -1;
1060  }
1061  break;
1062 
1063  case IO_METHOD_MMAP:
1064  case IO_METHOD_USERPTR:
1065  if (!(cap.capabilities & V4L2_CAP_STREAMING))
1066  {
1067  fprintf(stderr, "%.*s does not support streaming i/o", (int)sizeof(dev_name), dev_name);
1068  snprintf(errmsg, ERRMSGSIZ, "%.*s does not support streaming i/o", (int)sizeof(dev_name), dev_name);
1069  return -1;
1070  }
1071 
1072  break;
1073  }
1074 
1075  /* Select video input, video standard and tune here. */
1076 
1077  if (-1 == ioctl(fd, VIDIOC_G_INPUT, &input.index))
1078  {
1079  perror("VIDIOC_G_INPUT");
1080  return -1;
1081  }
1082 
1083  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Enumerating available Inputs:");
1084  for (input_avail.index = 0; ioctl(fd, VIDIOC_ENUMINPUT, &input_avail) != -1; input_avail.index++)
1085  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%2d. %.*s (type %s)%s", input_avail.index,
1086  (int)sizeof(input_avail.name), input_avail.name,
1087  (input_avail.type == V4L2_INPUT_TYPE_TUNER ? "Tuner/RF Demodulator" : "Composite/S-Video"),
1088  input.index == input_avail.index ? " current" : "");
1089  if (errno != EINVAL)
1090  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "(problem enumerating inputs)");
1091  enumeratedInputs = input_avail.index;
1092 
1093  /* Cropping */
1094  cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1095  cancrop = true;
1096  if (-1 == XIOCTL(fd, VIDIOC_CROPCAP, &cropcap))
1097  {
1098  perror("VIDIOC_CROPCAP");
1099  crop.c.top = -1;
1100  cancrop = false;
1101  /* Errors ignored. */
1102  }
1103  if (cancrop)
1104  {
1106  " Crop capabilities: bounds = (top=%d, left=%d, width=%d, height=%d)", cropcap.bounds.top,
1107  cropcap.bounds.left, cropcap.bounds.width, cropcap.bounds.height);
1109  " Crop capabilities: defrect = (top=%d, left=%d, width=%d, height=%d)", cropcap.defrect.top,
1110  cropcap.defrect.left, cropcap.defrect.width, cropcap.defrect.height);
1111  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " Crop capabilities: pixelaspect = %d / %d",
1112  cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator);
1113  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Explicitly resetting crop area to default...");
1114  crop.c.top = cropcap.defrect.top;
1115  crop.c.left = cropcap.defrect.left;
1116  crop.c.width = cropcap.defrect.width;
1117  crop.c.height = cropcap.defrect.height;
1118  if (-1 == XIOCTL(fd, VIDIOC_S_CROP, &crop))
1119  {
1120  perror("VIDIOC_S_CROP");
1121  cancrop = false;
1122  /* Errors ignored. */
1123  }
1124  if (-1 == XIOCTL(fd, VIDIOC_G_CROP, &crop))
1125  {
1126  perror("VIDIOC_G_CROP");
1127  crop.c.top = -1;
1128  cancrop = false;
1129  /* Errors ignored. */
1130  }
1131  }
1132  decoder->usesoftcrop(!cancrop);
1133 
1134  // Enumerating capture format
1135  {
1136  struct v4l2_fmtdesc fmt_avail;
1137  fmt_avail.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1138  //LOG_INFO("Available Capture Image formats:");
1139  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Enumerating available Capture Image formats:");
1140  for (fmt_avail.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmt_avail) != -1; fmt_avail.index++)
1141  {
1142  //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,INDI::Logger::DBG_SESSION,"\t%d. %s (%c%c%c%c) %s\n", fmt_avail.index, fmt_avail.description, (fmt_avail.pixelformat)&0xFF, (fmt_avail.pixelformat >> 8)&0xFF,
1143  // (fmt_avail.pixelformat >> 16)&0xFF, (fmt_avail.pixelformat >> 24)&0xFF, (decoder->issupportedformat(fmt_avail.pixelformat)?"supported":"UNSUPPORTED"));
1144  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%2d. Format %.*s (%c%c%c%c) is %s", fmt_avail.index,
1145  (int)sizeof(fmt_avail.description), fmt_avail.description, (fmt_avail.pixelformat) & 0xFF,
1146  (fmt_avail.pixelformat >> 8) & 0xFF, (fmt_avail.pixelformat >> 16) & 0xFF,
1147  (fmt_avail.pixelformat >> 24) & 0xFF,
1148  (decoder->issupportedformat(fmt_avail.pixelformat) ? "supported" : "UNSUPPORTED"));
1149  {
1150  // Enumerating frame sizes available for this pixel format
1151  struct v4l2_frmsizeenum frm_sizeenum;
1152  frm_sizeenum.pixel_format = fmt_avail.pixelformat;
1154  " Enumerating available Frame sizes/rates for this format:");
1155  for (frm_sizeenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMESIZES, &frm_sizeenum) != -1;
1156  frm_sizeenum.index++)
1157  {
1158  switch (frm_sizeenum.type)
1159  {
1160  case V4L2_FRMSIZE_TYPE_DISCRETE:
1162  " %2d. (Discrete) width %d x height %d\n", frm_sizeenum.index,
1163  frm_sizeenum.discrete.width, frm_sizeenum.discrete.height);
1164  break;
1165  case V4L2_FRMSIZE_TYPE_STEPWISE:
1167  " (Stepwise) min. width %d, max. width %d step width %d",
1168  frm_sizeenum.stepwise.min_width, frm_sizeenum.stepwise.max_width,
1169  frm_sizeenum.stepwise.step_width);
1171  " (Stepwise) min. height %d, max. height %d step height %d ",
1172  frm_sizeenum.stepwise.min_height, frm_sizeenum.stepwise.max_height,
1173  frm_sizeenum.stepwise.step_height);
1174  break;
1175  case V4L2_FRMSIZE_TYPE_CONTINUOUS:
1177  " (Continuous--step=1) min. width %d, max. width %d",
1178  frm_sizeenum.stepwise.min_width, frm_sizeenum.stepwise.max_width);
1180  " (Continuous--step=1) min. height %d, max. height %d ",
1181  frm_sizeenum.stepwise.min_height, frm_sizeenum.stepwise.max_height);
1182  break;
1183  default:
1184  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " Unknown Frame size type: %d\n",
1185  frm_sizeenum.type);
1186  break;
1187  }
1188  {
1189  // Enumerating frame intervals available for this frame size and this pixel format
1190  struct v4l2_frmivalenum frmi_valenum;
1191  frmi_valenum.pixel_format = fmt_avail.pixelformat;
1192  if (frm_sizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE)
1193  {
1194  frmi_valenum.width = frm_sizeenum.discrete.width;
1195  frmi_valenum.height = frm_sizeenum.discrete.height;
1196  }
1197  else
1198  {
1199  frmi_valenum.width = frm_sizeenum.stepwise.max_width;
1200  frmi_valenum.height = frm_sizeenum.stepwise.max_height;
1201  }
1202  frmi_valenum.type = 0;
1203  frmi_valenum.stepwise.min.numerator = 0;
1204  frmi_valenum.stepwise.min.denominator = 0;
1205  frmi_valenum.stepwise.max.numerator = 0;
1206  frmi_valenum.stepwise.max.denominator = 0;
1207  frmi_valenum.stepwise.step.numerator = 0;
1208  frmi_valenum.stepwise.step.denominator = 0;
1209  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " Frame intervals:");
1210  for (frmi_valenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmi_valenum) != -1;
1211  frmi_valenum.index++)
1212  {
1213  switch (frmi_valenum.type)
1214  {
1215  case V4L2_FRMIVAL_TYPE_DISCRETE:
1216  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " %d/%d s",
1217  frmi_valenum.discrete.numerator, frmi_valenum.discrete.denominator);
1218  break;
1219  case V4L2_FRMIVAL_TYPE_STEPWISE:
1220  DEBUGFDEVICE(
1221  deviceName, INDI::Logger::DBG_DEBUG,
1222  " (Stepwise) min. %d/%ds, max. %d / %d s, step %d / %d s",
1223  frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1224  frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator,
1225  frmi_valenum.stepwise.step.numerator, frmi_valenum.stepwise.step.denominator);
1226  break;
1227  case V4L2_FRMIVAL_TYPE_CONTINUOUS:
1228  DEBUGFDEVICE(
1229  deviceName, INDI::Logger::DBG_DEBUG,
1230  " (Continuous) min. %d / %d s, max. %d / %d s",
1231  frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1232  frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator);
1233  break;
1234  default:
1236  " Unknown Frame rate type: %d", frmi_valenum.type);
1237  break;
1238  }
1239  }
1240  if (frmi_valenum.index == 0)
1241  {
1242  perror("VIDIOC_ENUM_FRAMEINTERVALS");
1243  switch (frmi_valenum.type)
1244  {
1245  case V4L2_FRMIVAL_TYPE_DISCRETE:
1246  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " %d/%d s",
1247  frmi_valenum.discrete.numerator, frmi_valenum.discrete.denominator);
1248  break;
1249  case V4L2_FRMIVAL_TYPE_STEPWISE:
1250  DEBUGFDEVICE(
1251  deviceName, INDI::Logger::DBG_DEBUG,
1252  " (Stepwise) min. %d/%ds, max. %d / %d s, step %d / %d s",
1253  frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1254  frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator,
1255  frmi_valenum.stepwise.step.numerator, frmi_valenum.stepwise.step.denominator);
1256  break;
1257  case V4L2_FRMIVAL_TYPE_CONTINUOUS:
1258  DEBUGFDEVICE(
1259  deviceName, INDI::Logger::DBG_DEBUG,
1260  " (Continuous) min. %d / %d s, max. %d / %d s",
1261  frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1262  frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator);
1263  break;
1264  default:
1266  " Unknown Frame rate type: %d", frmi_valenum.type);
1267  break;
1268  }
1269  }
1270  //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"error %d, %s\n", errno, strerror (errno));
1271  }
1272  }
1273  }
1274  }
1275  if (errno != EINVAL)
1276  DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Problem enumerating capture formats.");
1277  enumeratedCaptureFormats = fmt_avail.index;
1278  }
1279 
1280  // CLEAR (fmt);
1281 
1282  // fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1283 
1284  // if (-1 == XIOCTL(fd, VIDIOC_G_FMT, &fmt))
1285  // return errno_exit ("VIDIOC_G_FMT", errmsg);
1286 
1287  // fmt.fmt.pix.width = (width == -1) ? fmt.fmt.pix.width : width;
1288  // fmt.fmt.pix.height = (height == -1) ? fmt.fmt.pix.height : height;
1289  // fmt.fmt.pix.pixelformat = (pixelFormat == -1) ? fmt.fmt.pix.pixelformat : pixelFormat;
1290  // //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
1291 
1292  // if (-1 == XIOCTL(fd, VIDIOC_S_FMT, &fmt))
1293  // return errno_exit ("VIDIOC_S_FMT", errmsg);
1294 
1295  // /* Note VIDIOC_S_FMT may change width and height. */
1296 
1297  // /* Buggy driver paranoia. */
1298  // min = fmt.fmt.pix.width * 2;
1299  // if (fmt.fmt.pix.bytesperline < min)
1300  // fmt.fmt.pix.bytesperline = min;
1301  // min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
1302  // if (fmt.fmt.pix.sizeimage < min)
1303  // fmt.fmt.pix.sizeimage = min;
1304 
1305  /* Refresh the instance format with the current device format */
1306  CLEAR(fmt);
1307  return ioctl_set_format(fmt, errmsg);
1308 }
1309 
1310 int V4L2_Base::init_device(char * errmsg)
1311 {
1312  //findMinMax();
1313 
1314  // decode allocBuffers();
1315 
1316  lxstate = LX_ACTIVE;
1317 
1318  switch (io)
1319  {
1320  case IO_METHOD_READ:
1321  init_read(fmt.fmt.pix.sizeimage);
1322  break;
1323 
1324  case IO_METHOD_MMAP:
1325  return init_mmap(errmsg);
1326  break;
1327 
1328  case IO_METHOD_USERPTR:
1329  init_userp(fmt.fmt.pix.sizeimage);
1330  break;
1331  }
1332  return 0;
1333 }
1334 
1335 void V4L2_Base::close_device()
1336 {
1337  char errmsg[ERRMSGSIZ];
1338  uninit_device(errmsg);
1339 
1340  if (-1 == close(fd))
1341  errno_exit("close", errmsg);
1342 
1343  fd = -1;
1344 }
1345 
1346 int V4L2_Base::open_device(const char * devpath, char * errmsg)
1347 {
1348  struct stat st;
1349 
1350  strncpy(dev_name, devpath, 64);
1351 
1352  if (-1 == stat(dev_name, &st))
1353  {
1354  fprintf(stderr, "Cannot identify %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno, strerror(errno));
1355  snprintf(errmsg, ERRMSGSIZ, "Cannot identify %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno,
1356  strerror(errno));
1357  return -1;
1358  }
1359 
1360  if (!S_ISCHR(st.st_mode))
1361  {
1362  fprintf(stderr, "%.*s is no device\n", (int)sizeof(dev_name), dev_name);
1363  snprintf(errmsg, ERRMSGSIZ, "%.*s is no device\n", (int)sizeof(dev_name), dev_name);
1364  return -1;
1365  }
1366 
1367  fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
1368 
1369  if (-1 == fd)
1370  {
1371  fprintf(stderr, "Cannot open %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno, strerror(errno));
1372  snprintf(errmsg, ERRMSGSIZ, "Cannot open %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno,
1373  strerror(errno));
1374  return -1;
1375  }
1376 
1377  streamedonce = false;
1378  snprintf(errmsg, ERRMSGSIZ, "%s\n", strerror(0));
1379  return 0;
1380 }
1381 
1382 /* @brief Storing inputs in a switch vector property, marking current as selected.
1383  *
1384  * @param inputssp is the property to store to (nothing is stored if null).
1385  */
1386 void V4L2_Base::getinputs(ISwitchVectorProperty * inputssp)
1387 {
1388  if (!inputssp)
1389  return;
1390 
1391  struct v4l2_input input_avail;
1392 
1393  /* Allocate inputs from previously enumerated count */
1394  size_t const inputsLen = enumeratedInputs * sizeof(ISwitch);
1395  ISwitch * inputs = (ISwitch *)malloc(inputsLen);
1396  if (!inputs)
1397  exit(EXIT_FAILURE);
1398  memset(inputs, 0, inputsLen);
1399 
1400  /* Ask device about each input */
1401  for (input_avail.index = 0; (int)input_avail.index < enumeratedInputs; input_avail.index++)
1402  {
1403  /* Enumeration ends with EINVAL */
1404  if (XIOCTL(fd, VIDIOC_ENUMINPUT, &input_avail))
1405  break;
1406 
1407  /* Store input description */
1408  strncpy(inputs[input_avail.index].name, (const char *)input_avail.name, MAXINDINAME);
1409  strncpy(inputs[input_avail.index].label, (const char *)input_avail.name, MAXINDILABEL);
1410  }
1411 
1412  /* Free inputs before replacing */
1413  if (inputssp->sp)
1414  free(inputssp->sp);
1415 
1416  /* Store inputs */
1417  inputssp->sp = inputs;
1418  inputssp->nsp = input_avail.index;
1419 
1420  IUResetSwitch(inputssp);
1421 
1422  /* And mark current */
1423  inputs[input.index].s = ISS_ON;
1424  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current video input is %d. %.*s", input.index,
1425  (int)sizeof(inputs[input.index].name), inputs[input.index].name);
1426 }
1427 
1428 int V4L2_Base::setinput(unsigned int inputindex, char * errmsg)
1429 {
1430  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Setting Video input to %d", inputindex);
1431  if (streamedonce)
1432  {
1433  close_device();
1434 
1435  if (open_device(path, errmsg))
1436  {
1437  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed reopening device %s (%s)", __FUNCTION__, path,
1438  errmsg);
1439  return -1;
1440  }
1441  }
1442  if (-1 == XIOCTL(fd, VIDIOC_S_INPUT, &inputindex))
1443  {
1444  return errno_exit("VIDIOC_S_INPUT", errmsg);
1445  }
1446  if (-1 == XIOCTL(fd, VIDIOC_G_INPUT, &input.index))
1447  {
1448  return errno_exit("VIDIOC_G_INPUT", errmsg);
1449  }
1450  //decode reallocate_buffers=true;
1451  return 0;
1452 }
1453 
1454 /* @brief Storing capture formats in a switch vector property, marking current as selected.
1455  *
1456  * @param captureformatssp is the property to store to (nothing is stored if null).
1457  */
1458 void V4L2_Base::getcaptureformats(ISwitchVectorProperty * captureformatssp)
1459 {
1460  if (!captureformatssp)
1461  return;
1462 
1463  struct v4l2_fmtdesc fmt_avail;
1464 
1465  /* Allocate capture formats from preliminary enumerated count */
1466  size_t const formatsLen = enumeratedCaptureFormats * sizeof(ISwitch);
1467  ISwitch * formats = (ISwitch *)malloc(formatsLen);
1468  if (!formats)
1469  exit(EXIT_FAILURE);
1470  memset(formats, 0, formatsLen);
1471 
1472  /* Ask device about each format */
1473  fmt_avail.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1474  for (fmt_avail.index = 0; (int)fmt_avail.index < enumeratedCaptureFormats; fmt_avail.index++)
1475  {
1476  /* Enumeration ends with EINVAL */
1477  if (XIOCTL(fd, VIDIOC_ENUM_FMT, &fmt_avail))
1478  break;
1479 
1480  /* Store format description */
1481  strncpy(formats[fmt_avail.index].name, (const char *)fmt_avail.description, MAXINDINAME);
1482  strncpy(formats[fmt_avail.index].label, (const char *)fmt_avail.description, MAXINDILABEL);
1483 
1484  /* And store pixel format for reference */
1485  /* FIXME: store pixel format as void pointer to avoid that malloc */
1486  formats[fmt_avail.index].aux = (int *)malloc(sizeof(int));
1487  if (!formats[fmt_avail.index].aux)
1488  exit(EXIT_FAILURE);
1489  *(int *)formats[fmt_avail.index].aux = fmt_avail.pixelformat;
1490  }
1491 
1492  /* Free formats before replacing */
1493  if (captureformatssp->sp)
1494  free(captureformatssp->sp);
1495 
1496  /* Store formats */
1497  captureformatssp->sp = formats;
1498  captureformatssp->nsp = fmt_avail.index;
1499  IUResetSwitch(captureformatssp);
1500 
1501  /* And mark current */
1502  for (unsigned int i = 0; i < fmt_avail.index; i++)
1503  {
1504  if ((int)fmt.fmt.pix.pixelformat == *(int *)formats[i].aux)
1505  {
1506  formats[i].s = ISS_ON;
1507  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current capture format is %d. %c%c%c%c.", i,
1508  (fmt.fmt.pix.pixelformat) & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
1509  (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
1510  }
1511  else
1512  formats[i].s = ISS_OFF;
1513  }
1514 }
1515 
1516 /* @brief Setting the pixel format of the capture.
1517  *
1518  * @param captureformat is the identifier of the pixel format to set.
1519  * @param errmsg is the error message to return in case of failure.
1520  * @return 0 if successful, else -1 with error message updated.
1521  */
1522 int V4L2_Base::setcaptureformat(unsigned int captureformat, char * errmsg)
1523 {
1524  struct v4l2_format new_fmt;
1525  CLEAR(new_fmt);
1526 
1527  new_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1528  new_fmt.fmt.pix.pixelformat = captureformat;
1529 
1530  return ioctl_set_format(new_fmt, errmsg);
1531 }
1532 
1533 void V4L2_Base::getcapturesizes(ISwitchVectorProperty * capturesizessp, INumberVectorProperty * capturesizenp)
1534 {
1535  struct v4l2_frmsizeenum frm_sizeenum;
1536  ISwitch * sizes = nullptr;
1537  INumber * sizevalue = nullptr;
1538  bool sizefound = false;
1539 
1540  // FIXME JM 2019-01-20: This leads to always a crash
1541  // with error: corrupted size vs. prev_size
1542  // Must CHECK and FIX
1543  if (capturesizessp->sp)
1544  free(capturesizessp->sp);
1545  if (capturesizenp->np)
1546  free(capturesizenp->np);
1547 
1548  frm_sizeenum.pixel_format = fmt.fmt.pix.pixelformat;
1549  //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"\t Available Frame sizes/rates for this format:\n");
1550  for (frm_sizeenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMESIZES, &frm_sizeenum) != -1; frm_sizeenum.index++)
1551  {
1552  switch (frm_sizeenum.type)
1553  {
1554  case V4L2_FRMSIZE_TYPE_DISCRETE:
1555  sizes = (sizes == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
1556  (ISwitch *)realloc(sizes, (frm_sizeenum.index + 1) * sizeof(ISwitch));
1557 
1558  snprintf(sizes[frm_sizeenum.index].name, MAXINDINAME, "%dx%d", frm_sizeenum.discrete.width,
1559  frm_sizeenum.discrete.height);
1560  snprintf(sizes[frm_sizeenum.index].label, MAXINDINAME, "%dx%d", frm_sizeenum.discrete.width,
1561  frm_sizeenum.discrete.height);
1562  sizes[frm_sizeenum.index].s = ISS_OFF;
1563  if (!sizefound)
1564  {
1565  if ((fmt.fmt.pix.width == frm_sizeenum.discrete.width) &&
1566  (fmt.fmt.pix.height == frm_sizeenum.discrete.height))
1567  {
1568  sizes[frm_sizeenum.index].s = ISS_ON;
1569  sizefound = true;
1570  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current capture size is (%d.) %dx%d",
1571  frm_sizeenum.index, frm_sizeenum.discrete.width, frm_sizeenum.discrete.height);
1572  }
1573  }
1574  break;
1575  case V4L2_FRMSIZE_TYPE_STEPWISE:
1576  case V4L2_FRMSIZE_TYPE_CONTINUOUS:
1577  sizevalue = (INumber *)malloc(2 * sizeof(INumber));
1578  IUFillNumber(sizevalue, "Width", "Width", "%.0f", frm_sizeenum.stepwise.min_width,
1579  frm_sizeenum.stepwise.max_width, frm_sizeenum.stepwise.step_width, fmt.fmt.pix.width);
1580  IUFillNumber(sizevalue + 1, "Height", "Height", "%.0f", frm_sizeenum.stepwise.min_height,
1581  frm_sizeenum.stepwise.max_height, frm_sizeenum.stepwise.step_height, fmt.fmt.pix.height);
1582  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current capture size is %dx%d", fmt.fmt.pix.width,
1583  fmt.fmt.pix.height);
1584  break;
1585  default:
1586  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Unknown Frame size type: %d", frm_sizeenum.type);
1587  break;
1588  }
1589  }
1590 
1591  if (sizes != nullptr)
1592  {
1593  capturesizessp->sp = sizes;
1594  capturesizessp->nsp = frm_sizeenum.index;
1595  capturesizenp->np = nullptr;
1596  }
1597  else
1598  {
1599  capturesizenp->np = sizevalue;
1600  capturesizenp->nnp = 2;
1601  capturesizessp->sp = nullptr;
1602  }
1603 }
1604 
1605 /* @brief Updating the capture dimensions.
1606  *
1607  * @param w is the updated width of the capture.
1608  * @param h is the update height of the capture.
1609  * @param errmsg is the returned error message in case of failure.
1610  * @return 0 if successful, else -1 with error message updated.
1611  */
1612 int V4L2_Base::setcapturesize(unsigned int w, unsigned int h, char * errmsg)
1613 {
1614  struct v4l2_format new_fmt = fmt;
1615 
1616  new_fmt.fmt.pix.width = w;
1617  new_fmt.fmt.pix.height = h;
1618 
1619  return ioctl_set_format(new_fmt, errmsg);
1620 }
1621 
1622 void V4L2_Base::getframerates(ISwitchVectorProperty * frameratessp, INumberVectorProperty * frameratenp)
1623 {
1624  struct v4l2_frmivalenum frmi_valenum;
1625  ISwitch * rates = nullptr;
1626  INumber * ratevalue = nullptr;
1627  struct v4l2_fract frate;
1628 
1629  if (frameratessp->sp)
1630  free(frameratessp->sp);
1631  if (frameratenp->np)
1632  free(frameratenp->np);
1633  frate = (this->*getframerate)();
1634 
1635  bzero(&frmi_valenum, sizeof(frmi_valenum));
1636  frmi_valenum.pixel_format = fmt.fmt.pix.pixelformat;
1637  frmi_valenum.width = fmt.fmt.pix.width;
1638  frmi_valenum.height = fmt.fmt.pix.height;
1639 
1640  for (frmi_valenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmi_valenum) != -1; frmi_valenum.index++)
1641  {
1642  switch (frmi_valenum.type)
1643  {
1644  case V4L2_FRMIVAL_TYPE_DISCRETE:
1645  rates = (rates == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
1646  (ISwitch *)realloc(rates, (frmi_valenum.index + 1) * sizeof(ISwitch));
1647  snprintf(rates[frmi_valenum.index].name, MAXINDINAME, "%d/%d", frmi_valenum.discrete.numerator,
1648  frmi_valenum.discrete.denominator);
1649  snprintf(rates[frmi_valenum.index].label, MAXINDINAME, "%d/%d", frmi_valenum.discrete.numerator,
1650  frmi_valenum.discrete.denominator);
1651  if ((frate.numerator == frmi_valenum.discrete.numerator) &&
1652  (frate.denominator == frmi_valenum.discrete.denominator))
1653  {
1654  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current frame interval is %d/%d",
1655  frmi_valenum.discrete.numerator, frmi_valenum.discrete.denominator);
1656  rates[frmi_valenum.index].s = ISS_ON;
1657  }
1658  else
1659  rates[frmi_valenum.index].s = ISS_OFF;
1660  break;
1661  case V4L2_FRMIVAL_TYPE_STEPWISE:
1662  case V4L2_FRMIVAL_TYPE_CONTINUOUS:
1663  ratevalue = (INumber *)malloc(sizeof(INumber));
1664  IUFillNumber(ratevalue, "V4L2_FRAME_INTERVAL", "Frame Interval", "%.0f",
1665  frmi_valenum.stepwise.min.numerator / (double)frmi_valenum.stepwise.min.denominator,
1666  frmi_valenum.stepwise.max.numerator / (double)frmi_valenum.stepwise.max.denominator,
1667  frmi_valenum.stepwise.step.numerator / (double)frmi_valenum.stepwise.step.denominator,
1668  frate.numerator / (double)frate.denominator);
1669  break;
1670  default:
1671  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Unknown Frame rate type: %d", frmi_valenum.type);
1672  break;
1673  }
1674  }
1675  frameratessp->sp = nullptr;
1676  frameratessp->nsp = 0;
1677  frameratenp->np = nullptr;
1678  frameratenp->nnp = 0;
1679  if (frmi_valenum.index != 0)
1680  {
1681  if (rates != nullptr)
1682  {
1683  frameratessp->sp = rates;
1684  frameratessp->nsp = frmi_valenum.index;
1685  }
1686  else
1687  {
1688  frameratenp->np = ratevalue;
1689  frameratenp->nnp = 1;
1690  }
1691  }
1692 }
1693 
1694 int V4L2_Base::setcroprect(int x, int y, int w, int h, char * errmsg)
1695 {
1696  // Do we set a full-frame crop?
1697  if (x == 0 && y == 0 && w == (int)fmt.fmt.pix.width && h == (int)fmt.fmt.pix.height)
1698  {
1699  cropset = false;
1700  decoder->resetcrop();
1701  return 0;
1702  }
1703 
1704  int const pix_width = static_cast <int> (fmt.fmt.pix.width);
1705  int const pix_height = static_cast <int> (fmt.fmt.pix.height);
1706 
1707  // Clamp against frame dimensions
1708  crop.c.left = 0 <= x ? x < pix_width ? x : pix_width - 1 : 0;
1709  crop.c.top = 0 <= y ? y < pix_height ? y : pix_height - 1 : 0;
1710  crop.c.width = 0 <= w ? w < pix_width ? w : pix_width : 0;
1711  crop.c.height = 0 <= h ? h < pix_height ? h : pix_height : 0;
1712 
1713  // Sanitize arguments against frame dimensions
1714  if (x + w < 0 || y + h < 0 || pix_width <= x || pix_height <= y)
1715  {
1716  strncpy(errmsg, "requested crop rectangle is outside of frame", ERRMSGSIZ);
1717  return -1;
1718  }
1719 
1720  // Clip arguments against left and top of frame
1721  if (x < 0)
1722  {
1723  w = x + w;
1724  x = 0;
1725  }
1726  if (y < 0)
1727  {
1728  h = y + h;
1729  y = 0;
1730  }
1731 
1732  // Clip arguments against right and bottom of frame
1733  if (0 + pix_width < x + w)
1734  {
1735  w = pix_width - x;
1736  }
1737  if (0 + pix_height < y + h)
1738  {
1739  h = pix_height - y;
1740  }
1741 
1742  // We have our required crop
1743  struct v4l2_crop software_crop;
1744  software_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1745  software_crop.c.left = x;
1746  software_crop.c.top = y;
1747  software_crop.c.width = w;
1748  software_crop.c.height = h;
1749 
1750  // If the hardware is able to crop, request the kernel driver
1751  if (cancrop)
1752  {
1753  // We need to adjust our hardware crop to driver-level V4L2 capabilities
1754  struct v4l2_crop hardware_crop = software_crop;
1755 
1756  // Make sure vertical coordinate is on an even line
1757  if (y % 2)
1758  {
1759  hardware_crop.c.top--;
1760  hardware_crop.c.height++;
1761  }
1762 
1763  // Make sure we crop an even number of horizontal lines
1764  if (h % 2)
1765  {
1766  hardware_crop.c.height++;
1767  }
1768 
1769  // Ask the hardware to crop - don't fail, fallback to software cropping if not possible
1770  if (-1 == XIOCTL(fd, VIDIOC_S_CROP, &hardware_crop))
1771  {
1773  "Failed V4L2 hardware crop request 0x%08X (%dx%d at (%d, %d)), falling back to software crop",
1774  (unsigned int)VIDIOC_S_CROP, hardware_crop.c.width, hardware_crop.c.height,
1775  hardware_crop.c.left, hardware_crop.c.top);
1776  //return errno_exit("VIDIOC_S_CROP", errmsg);
1777  }
1778  else if (-1 == XIOCTL(fd, VIDIOC_G_CROP, &hardware_crop))
1779  {
1780  //return errno_exit("VIDIOC_G_CROP", errmsg);
1781  }
1782  else
1783  {
1784  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_SESSION, "V4L2 hardware crop request 0x%08X accepted as %dx%d at (%d, %d)",
1785  (unsigned int)VIDIOC_S_CROP, hardware_crop.c.width, hardware_crop.c.height,
1786  hardware_crop.c.left, hardware_crop.c.top);
1787  }
1788  }
1789 
1790  // In any case, request the decoder to crop
1791  bool const softcrop = decoder->setcrop(software_crop);
1792 
1793  if (!softcrop && !cancrop)
1794  {
1795  cropset = false;
1796  strncpy(errmsg, "No hardware and software cropping for this format", ERRMSGSIZ);
1797  return -1;
1798  }
1799 
1800  // Crop request is set, store state
1801  cropset = true;
1802  crop = software_crop;
1803 
1804  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "V4L2 base setcroprect %dx%d at (%d, %d)", crop.c.width,
1805  crop.c.height, crop.c.left, crop.c.top);
1806  return 0;
1807 }
1808 
1809 int V4L2_Base::getWidth()
1810 {
1811  if (cropset)
1812  return crop.c.width;
1813  else
1814  return fmt.fmt.pix.width;
1815 }
1816 
1817 int V4L2_Base::getHeight()
1818 {
1819  if (cropset)
1820  return crop.c.height;
1821  else
1822  return fmt.fmt.pix.height;
1823 }
1824 
1825 int V4L2_Base::getBpp()
1826 {
1827  return bpp;
1828 }
1829 
1830 int V4L2_Base::getFormat()
1831 {
1832  return fmt.fmt.pix.pixelformat;
1833 }
1834 
1835 struct v4l2_rect V4L2_Base::getcroprect()
1836 {
1837  return crop.c;
1838 }
1839 
1840 int V4L2_Base::stdsetframerate(struct v4l2_fract frate, char * errmsg)
1841 {
1842  struct v4l2_streamparm sparm;
1843  //if (!cansetrate) {sprintf(errmsg, "Can not set rate"); return -1;}
1844  bzero(&sparm, sizeof(struct v4l2_streamparm));
1845  sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1846  sparm.parm.capture.timeperframe = frate;
1847  if (-1 == XIOCTL(fd, VIDIOC_S_PARM, &sparm))
1848  {
1849  //cansetrate=false;
1850  return errno_exit("VIDIOC_S_PARM", errmsg);
1851  }
1852  return 0;
1853 }
1854 
1855 /* @brief Setting the framerate for Philips-based PWC devices.
1856  *
1857  * @param frate is the v4l2_fract structure defining framerate.
1858  * @param errmsg is the returned error message in case of error.
1859  * @return 0 if successful, else -1 with error message updated.
1860  */
1861 int V4L2_Base::pwcsetframerate(struct v4l2_fract frate, char * errmsg)
1862 {
1863  int const fps = frate.denominator / frate.numerator;
1864 
1865  struct v4l2_format new_fmt = fmt;
1866  new_fmt.fmt.pix.priv |= (fps << PWC_FPS_SHIFT);
1867 
1868  if (-1 == ioctl_set_format(new_fmt, errmsg))
1869  return errno_exit("pwcsetframerate", errmsg);
1870 
1871  frameRate = frate;
1872 
1873  return 0;
1874 }
1875 
1876 struct v4l2_fract V4L2_Base::stdgetframerate()
1877 {
1878  struct v4l2_streamparm sparm;
1879  //if (!cansetrate) return frameRate;
1880  bzero(&sparm, sizeof(struct v4l2_streamparm));
1881  sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1882  if (-1 == XIOCTL(fd, VIDIOC_G_PARM, &sparm))
1883  {
1884  perror("VIDIOC_G_PARM");
1885  }
1886  else
1887  {
1888  frameRate = sparm.parm.capture.timeperframe;
1889  }
1890  //if (!(sparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) cansetrate=false;
1891 
1892  return frameRate;
1893 }
1894 
1896 {
1897  return ((char *)cap.card);
1898 }
1899 
1900 void V4L2_Base::getMaxMinSize(int &x_max, int &y_max, int &x_min, int &y_min)
1901 {
1902  x_max = xmax;
1903  y_max = ymax;
1904  x_min = xmin;
1905  y_min = ymin;
1906 }
1907 
1908 /* @brief Setting the dimensions of the capture frame.
1909  *
1910  * @param x is the width of the capture frame.
1911  * @param y is the height of the capture frame.
1912  * @return 0 if successful, else -1.
1913  */
1914 int V4L2_Base::setSize(int x, int y)
1915 {
1916  char errmsg[ERRMSGSIZ];
1917  struct v4l2_format new_fmt = fmt;
1918 
1919  new_fmt.fmt.pix.width = x;
1920  new_fmt.fmt.pix.height = y;
1921 
1922  if (-1 == ioctl_set_format(new_fmt, errmsg))
1923  return -1;
1924 
1925  /* PWC bug? It seems that setting the "wrong" width and height will mess something in the driver.
1926  Only 160x120, 320x280, and 640x480 are accepted. If I try to set it for example to 300x200, it wii
1927  get set to 320x280, which is fine, but then the video information is messed up for some reason. */
1928  // XIOCTL(fd, VIDIOC_S_FMT, &fmt);
1929 
1930  //allocBuffers();
1931 
1932  return 0;
1933 }
1934 
1935 void V4L2_Base::setColorProcessing(bool quantization, bool colorconvert, bool linearization)
1936 {
1937  INDI_UNUSED(colorconvert);
1938  decoder->setQuantization(quantization);
1939  decoder->setLinearization(linearization);
1940  bpp = decoder->getBpp();
1941 }
1942 
1943 unsigned char * V4L2_Base::getY()
1944 {
1945  return decoder->getY();
1946 }
1947 
1948 unsigned char * V4L2_Base::getU()
1949 {
1950  return decoder->getU();
1951 }
1952 
1953 unsigned char * V4L2_Base::getV()
1954 {
1955  return decoder->getV();
1956 }
1957 
1958 unsigned char * V4L2_Base::getMJPEGBuffer(int &size)
1959 {
1960  return decoder->getMJPEGBuffer(size);
1961 }
1962 
1963 unsigned char * V4L2_Base::getRGBBuffer()
1964 {
1965  return decoder->getRGBBuffer();
1966 }
1967 
1968 float * V4L2_Base::getLinearY()
1969 {
1970  return decoder->getLinearY();
1971 }
1972 
1973 void V4L2_Base::registerCallback(WPF * fp, void * ud)
1974 {
1975  callback = fp;
1976  uptr = ud;
1977 }
1978 
1979 void V4L2_Base::findMinMax()
1980 {
1981  char errmsg[ERRMSGSIZ];
1982  struct v4l2_format tryfmt;
1983  CLEAR(tryfmt);
1984 
1985  xmin = xmax = fmt.fmt.pix.width;
1986  ymin = ymax = fmt.fmt.pix.height;
1987 
1988  tryfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1989  tryfmt.fmt.pix.width = 10;
1990  tryfmt.fmt.pix.height = 10;
1991  tryfmt.fmt.pix.pixelformat = fmt.fmt.pix.pixelformat;
1992  tryfmt.fmt.pix.field = fmt.fmt.pix.field;
1993 
1994  if (-1 == XIOCTL(fd, VIDIOC_TRY_FMT, &tryfmt))
1995  {
1996  errno_exit("VIDIOC_TRY_FMT 1", errmsg);
1997  return;
1998  }
1999 
2000  xmin = tryfmt.fmt.pix.width;
2001  ymin = tryfmt.fmt.pix.height;
2002 
2003  tryfmt.fmt.pix.width = 1600;
2004  tryfmt.fmt.pix.height = 1200;
2005 
2006  if (-1 == XIOCTL(fd, VIDIOC_TRY_FMT, &tryfmt))
2007  {
2008  errno_exit("VIDIOC_TRY_FMT 2", errmsg);
2009  return;
2010  }
2011 
2012  xmax = tryfmt.fmt.pix.width;
2013  ymax = tryfmt.fmt.pix.height;
2014 
2015  cerr << "Min X: " << xmin << " - Max X: " << xmax << " - Min Y: " << ymin << " - Max Y: " << ymax << endl;
2016 }
2017 
2018 void V4L2_Base::enumerate_ctrl()
2019 {
2020  char errmsg[ERRMSGSIZ];
2021  CLEAR(queryctrl);
2022 
2023  for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++)
2024  {
2025  if (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2026  {
2027  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2028  {
2029  cerr << "DISABLED--Control " << queryctrl.name << endl;
2030  continue;
2031  }
2032  cerr << "Control " << queryctrl.name << endl;
2033 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2034  if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2035 #else
2036  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2037 #endif
2038  enumerate_menu();
2039  if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2040  cerr << " boolean" << endl;
2041  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2042  cerr << " integer" << endl;
2043  //if (queryctrl.type == V4L2_CTRL_TYPE_BITMASK) //not in < 3.1
2044  // cerr << " bitmask" <<endl;
2045  if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2046  cerr << " button" << endl;
2047  }
2048  else
2049  {
2050  if (errno == EINVAL)
2051  continue;
2052 
2053  errno_exit("VIDIOC_QUERYCTRL", errmsg);
2054  return;
2055  }
2056  }
2057 
2058  for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++)
2059  {
2060  if (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2061  {
2062  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2063  {
2064  cerr << "DISABLED--Private Control " << queryctrl.name << endl;
2065  continue;
2066  }
2067 
2068  cerr << "Private Control " << queryctrl.name << endl;
2069 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2070  if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2071 #else
2072  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2073 #endif
2074  enumerate_menu();
2075  if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2076  cerr << " boolean" << endl;
2077  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2078  cerr << " integer" << endl;
2079  //if (queryctrl.type == V4L2_CTRL_TYPE_BITMASK)
2080  // cerr << " bitmask" <<endl;
2081  if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2082  cerr << " button" << endl;
2083  }
2084  else
2085  {
2086  if (errno == EINVAL)
2087  break;
2088 
2089  errno_exit("VIDIOC_QUERYCTRL", errmsg);
2090  return;
2091  }
2092  }
2093 }
2094 
2095 void V4L2_Base::enumerate_menu()
2096 {
2097 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2098  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2099  cerr << " Menu items:" << endl;
2100  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2101  cerr << " Integer Menu items:" << endl;
2102 #else
2103  cerr << " Menu items:" << endl;
2104 #endif
2105 
2106  CLEAR(querymenu);
2107  querymenu.id = queryctrl.id;
2108 
2109  for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2110  {
2111  if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2112  {
2113  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2114  cerr << " " << querymenu.name << endl;
2115 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2116  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2117  {
2118  char menuname[19];
2119  menuname[18] = '\0';
2120  snprintf(menuname, 19, "0x%016llX", querymenu.value);
2121  cerr << " " << menuname << endl;
2122  }
2123 #endif
2124  }
2125  //else
2126  //{
2127  // errno_exit("VIDIOC_QUERYMENU", errmsg);
2128  // return;
2129  //}
2130  }
2131 }
2132 
2133 int V4L2_Base::query_ctrl(unsigned int ctrl_id, double &ctrl_min, double &ctrl_max, double &ctrl_step,
2134  double &ctrl_value, char * errmsg)
2135 {
2136  struct v4l2_control control;
2137 
2138  CLEAR(queryctrl);
2139 
2140  queryctrl.id = ctrl_id;
2141 
2142  if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2143  {
2144  if (errno != EINVAL)
2145  return errno_exit("VIDIOC_QUERYCTRL", errmsg);
2146 
2147  else
2148  {
2149  cerr << "#" << ctrl_id << " is not supported" << endl;
2150  snprintf(errmsg, ERRMSGSIZ, "# %d is not supported", ctrl_id);
2151  return -1;
2152  }
2153  }
2154  else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2155  {
2156  cerr << "#" << ctrl_id << " is disabled" << endl;
2157  snprintf(errmsg, ERRMSGSIZ, "# %d is disabled", ctrl_id);
2158  return -1;
2159  }
2160 
2161  ctrl_min = queryctrl.minimum;
2162  ctrl_max = queryctrl.maximum;
2163  ctrl_step = queryctrl.step;
2164  ctrl_value = queryctrl.default_value;
2165 
2166  /* Get current value */
2167  CLEAR(control);
2168  control.id = ctrl_id;
2169 
2170  if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2171  ctrl_value = control.value;
2172 
2173  cerr << queryctrl.name << " -- min: " << ctrl_min << " max: " << ctrl_max << " step: " << ctrl_step
2174  << " value: " << ctrl_value << endl;
2175 
2176  return 0;
2177 }
2178 
2179 void V4L2_Base::queryControls(INumberVectorProperty * nvp, unsigned int * nnumber, ISwitchVectorProperty ** options,
2180  unsigned int * noptions, const char * dev, const char * group)
2181 {
2182  struct v4l2_control control;
2183 
2184  INumber * numbers = nullptr;
2185  unsigned int * num_ctrls = nullptr;
2186  int nnum = 0;
2187  ISwitchVectorProperty * opt = nullptr;
2188  unsigned int nopt = 0;
2189  char optname[] = "OPT000";
2190  char swonname[] = "SET_OPT000";
2191  char swoffname[] = "UNSET_OPT000";
2192  char menuname[] = "MENU000";
2193  char menuoptname[] = "MENU000_OPT000";
2194  *noptions = 0;
2195  *nnumber = 0;
2196 
2197  CLEAR(queryctrl);
2198 
2199  for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++)
2200  {
2201  if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2202  {
2203  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2204  {
2205  cerr << queryctrl.name << " is disabled." << endl;
2206  continue;
2207  }
2208 
2209  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2210  {
2211  numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2212  (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2213 
2214  num_ctrls = (num_ctrls == nullptr) ?
2215  (unsigned int *)malloc(sizeof(unsigned int)) :
2216  (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2217 
2218  strncpy(numbers[nnum].name, (const char *)entityXML((char *)queryctrl.name), MAXINDINAME);
2219  strncpy(numbers[nnum].label, (const char *)entityXML((char *)queryctrl.name), MAXINDILABEL);
2220  strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2221  numbers[nnum].min = queryctrl.minimum;
2222  numbers[nnum].max = queryctrl.maximum;
2223  numbers[nnum].step = queryctrl.step;
2224  numbers[nnum].value = queryctrl.default_value;
2225 
2226  /* Get current value if possible */
2227  CLEAR(control);
2228  control.id = queryctrl.id;
2229  if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2230  numbers[nnum].value = control.value;
2231 
2232  /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2233  num_ctrls[nnum] = queryctrl.id;
2234 
2235  cerr << "Adding " << queryctrl.name << " -- min: " << queryctrl.minimum << " max: " << queryctrl.maximum
2236  << " step: " << queryctrl.step << " value: " << numbers[nnum].value << endl;
2237 
2238  nnum++;
2239  }
2240  if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2241  {
2242  ISwitch * sw = (ISwitch *)malloc(2 * sizeof(ISwitch));
2243  snprintf(optname + 3, 4, "%03d", nopt);
2244  snprintf(swonname + 7, 4, "%03d", nopt);
2245  snprintf(swoffname + 9, 4, "%03d", nopt);
2246 
2247  opt = (opt == nullptr) ?
2248  (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2249  (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2250 
2251  CLEAR(control);
2252  control.id = queryctrl.id;
2253  XIOCTL(fd, VIDIOC_G_CTRL, &control);
2254 
2255  IUFillSwitch(sw, swonname, "Off", (control.value ? ISS_OFF : ISS_ON));
2256  IUFillSwitch(sw + 1, swoffname, "On", (control.value ? ISS_ON : ISS_OFF));
2257  queryctrl.name[31] = '\0';
2258  IUFillSwitchVector(&opt[nopt], sw, 2, dev, optname, (const char *)entityXML((char *)queryctrl.name),
2259  group, IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2260  opt[nopt].aux = malloc(sizeof(unsigned int));
2261  *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2262 
2263  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding switch %.*s (%s)\n",
2264  (int)sizeof(queryctrl.name), queryctrl.name, (control.value ? "On" : "Off"));
2265  nopt += 1;
2266  }
2267 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2268  if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2269 #else
2270  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2271 #endif
2272  {
2273  ISwitch * sw = nullptr;
2274  unsigned int nmenuopt = 0;
2275  char sname[32];
2276  snprintf(menuname + 4, 4, "%03d", nopt);
2277  snprintf(menuoptname + 4, 4, "%03d", nopt);
2278  menuoptname[7] = '_';
2279 
2280  opt = (opt == nullptr) ?
2281  (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2282  (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2283 
2284  CLEAR(control);
2285  control.id = queryctrl.id;
2286  XIOCTL(fd, VIDIOC_G_CTRL, &control);
2287 
2288  CLEAR(querymenu);
2289  querymenu.id = queryctrl.id;
2290 
2291  for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2292  {
2293  if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2294  {
2295  sw = (sw == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
2296  (ISwitch *)realloc(sw, (nmenuopt + 1) * sizeof(ISwitch));
2297  snprintf(menuoptname + 11, 4, "%03d", nmenuopt);
2298 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2299  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2300  {
2301  snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2302  sname[31] = '\0';
2303  }
2304  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2305  {
2306  snprintf(sname, 19, "0x%016llX", querymenu.value);
2307  sname[31] = '\0';
2308  }
2309 #else
2310  snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2311  sname[31] = '\0';
2312 #endif
2313  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu item %.*s %.*s item %d",
2314  (int)sizeof(sname), sname, (int)sizeof(menuoptname), menuoptname, nmenuopt);
2315  //IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)sname, (control.value==nmenuopt?ISS_ON:ISS_OFF));
2316  IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)entityXML((char *)sname),
2317  (control.value == (int)nmenuopt ? ISS_ON : ISS_OFF));
2318  nmenuopt += 1;
2319  }
2320  else
2321  {
2322  //errno_exit("VIDIOC_QUERYMENU", errmsg);
2323  //exit(3);
2324  }
2325  }
2326 
2327  queryctrl.name[31] = '\0';
2328  IUFillSwitchVector(&opt[nopt], sw, nmenuopt, dev, menuname,
2329  (const char *)entityXML((char *)queryctrl.name), group, IP_RW, ISR_1OFMANY, 0.0,
2330  IPS_IDLE);
2331  opt[nopt].aux = malloc(sizeof(unsigned int));
2332  *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2333 
2334  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu %.*s (item %d set)",
2335  (int)sizeof(queryctrl.name), queryctrl.name, control.value);
2336  nopt += 1;
2337  }
2338  }
2339  else
2340  {
2341  if (errno != EINVAL)
2342  {
2343  if (numbers)
2344  free(numbers);
2345  if (opt)
2346  free(opt);
2347  perror("VIDIOC_QUERYCTRL");
2348  return;
2349  }
2350  }
2351  }
2352 
2353  for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++)
2354  {
2355  if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2356  {
2357  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2358  {
2359  cerr << queryctrl.name << " is disabled." << endl;
2360  continue;
2361  }
2362 
2363  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2364  {
2365  numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2366  (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2367 
2368  num_ctrls = (num_ctrls == nullptr) ?
2369  (unsigned int *)malloc(sizeof(unsigned int)) :
2370  (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2371 
2372  strncpy(numbers[nnum].name, (const char *)entityXML((char *)queryctrl.name), MAXINDINAME);
2373  strncpy(numbers[nnum].label, (const char *)entityXML((char *)queryctrl.name), MAXINDILABEL);
2374  strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2375  numbers[nnum].min = queryctrl.minimum;
2376  numbers[nnum].max = queryctrl.maximum;
2377  numbers[nnum].step = queryctrl.step;
2378  numbers[nnum].value = queryctrl.default_value;
2379 
2380  /* Get current value if possible */
2381  CLEAR(control);
2382  control.id = queryctrl.id;
2383  if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2384  numbers[nnum].value = control.value;
2385 
2386  /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2387  num_ctrls[nnum] = queryctrl.id;
2388  cerr << "Adding ext. " << queryctrl.name << " -- min: " << queryctrl.minimum
2389  << " max: " << queryctrl.maximum << " step: " << queryctrl.step
2390  << " value: " << numbers[nnum].value << endl;
2391 
2392  nnum++;
2393  }
2394  if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2395  {
2396  ISwitch * sw = (ISwitch *)malloc(2 * sizeof(ISwitch));
2397  snprintf(optname + 3, 4, "%03d", nopt);
2398  snprintf(swonname + 7, 4, "%03d", nopt);
2399  snprintf(swoffname + 9, 4, "%03d", nopt);
2400 
2401  opt = (opt == nullptr) ?
2402  (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2403  (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2404 
2405  CLEAR(control);
2406  control.id = queryctrl.id;
2407  XIOCTL(fd, VIDIOC_G_CTRL, &control);
2408 
2409  IUFillSwitch(sw, swonname, "On", (control.value ? ISS_ON : ISS_OFF));
2410  IUFillSwitch(sw + 1, swoffname, "Off", (control.value ? ISS_OFF : ISS_ON));
2411  queryctrl.name[31] = '\0';
2412  IUFillSwitchVector(&opt[nopt], sw, 2, dev, optname, (const char *)entityXML((char *)queryctrl.name),
2413  group, IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2414 
2415  opt[nopt].aux = malloc(sizeof(unsigned int));
2416  *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2417 
2418  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding ext. switch %.*s (%s)\n",
2419  (int)sizeof(queryctrl.name), queryctrl.name, (control.value ? "On" : "Off"));
2420  nopt += 1;
2421  }
2422 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2423  if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2424 #else
2425  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2426 #endif
2427  {
2428  ISwitch * sw = nullptr;
2429  unsigned int nmenuopt = 0;
2430  char sname[32];
2431  snprintf(menuname + 4, 4, "%03d", nopt);
2432  snprintf(menuoptname + 4, 4, "%03d", nopt);
2433  menuoptname[7] = '_';
2434 
2435  opt = (opt == nullptr) ?
2436  (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2437  (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2438 
2439  CLEAR(control);
2440  control.id = queryctrl.id;
2441  XIOCTL(fd, VIDIOC_G_CTRL, &control);
2442 
2443  CLEAR(querymenu);
2444  querymenu.id = queryctrl.id;
2445 
2446  for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2447  {
2448  if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2449  {
2450  sw = (sw == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
2451  (ISwitch *)realloc(sw, (nmenuopt + 1) * sizeof(ISwitch));
2452  snprintf(menuoptname + 11, 4, "%03d", nmenuopt);
2453 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2454  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2455  {
2456  snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2457  sname[31] = '\0';
2458  }
2459  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2460  {
2461  snprintf(sname, 19, "0x%016llX", querymenu.value);
2462  sname[31] = '\0';
2463  }
2464 #else
2465  snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2466  sname[31] = '\0';
2467 #endif
2468  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu item %.*s %.*s item %d",
2469  (int)sizeof(sname), sname, (int)sizeof(menuoptname), menuoptname, nmenuopt);
2470  //IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)sname, (control.value==nmenuopt?ISS_ON:ISS_OFF));
2471  IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)entityXML((char *)querymenu.name),
2472  (control.value == (int)nmenuopt ? ISS_ON : ISS_OFF));
2473  nmenuopt += 1;
2474  }
2475  else
2476  {
2477  //errno_exit("VIDIOC_QUERYMENU", errmsg);
2478  //exit(3);
2479  }
2480  }
2481 
2482  queryctrl.name[31] = '\0';
2483  IUFillSwitchVector(&opt[nopt], sw, nmenuopt, dev, menuname,
2484  (const char *)entityXML((char *)queryctrl.name), group, IP_RW, ISR_1OFMANY, 0.0,
2485  IPS_IDLE);
2486 
2487  opt[nopt].aux = malloc(sizeof(unsigned int));
2488  *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2489 
2490  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding ext. menu %.*s (item %d set)",
2491  (int)sizeof(queryctrl.name), queryctrl.name, control.value);
2492  nopt += 1;
2493  }
2494  }
2495  else
2496  break;
2497  }
2498 
2499  /* Store numbers in aux0 */
2500  for (int i = 0; i < nnum; i++)
2501  numbers[i].aux0 = &num_ctrls[i];
2502 
2503  nvp->np = numbers;
2504  nvp->nnp = nnum;
2505  *nnumber = nnum;
2506 
2507  *options = opt;
2508  *noptions = nopt;
2509 }
2510 
2511 int V4L2_Base::queryINTControls(INumberVectorProperty * nvp)
2512 {
2513  struct v4l2_control control;
2514  char errmsg[ERRMSGSIZ];
2515  CLEAR(queryctrl);
2516  INumber * numbers = nullptr;
2517  unsigned int * num_ctrls = nullptr;
2518  int nnum = 0;
2519 
2520  for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++)
2521  {
2522  if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2523  {
2524  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2525  {
2526  cerr << queryctrl.name << " is disabled." << endl;
2527  continue;
2528  }
2529 
2530  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2531  {
2532  numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2533  (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2534 
2535  num_ctrls = (num_ctrls == nullptr) ?
2536  (unsigned int *)malloc(sizeof(unsigned int)) :
2537  (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2538 
2539  strncpy(numbers[nnum].name, ((char *)queryctrl.name), MAXINDINAME);
2540  strncpy(numbers[nnum].label, ((char *)queryctrl.name), MAXINDILABEL);
2541  strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2542  numbers[nnum].min = queryctrl.minimum;
2543  numbers[nnum].max = queryctrl.maximum;
2544  numbers[nnum].step = queryctrl.step;
2545  numbers[nnum].value = queryctrl.default_value;
2546 
2547  /* Get current value if possible */
2548  CLEAR(control);
2549  control.id = queryctrl.id;
2550  if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2551  numbers[nnum].value = control.value;
2552 
2553  /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2554  num_ctrls[nnum] = queryctrl.id;
2555 
2556  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%.*s -- min: %d max: %d step: %d value: %d",
2557  (int)sizeof(queryctrl.name), queryctrl.name, queryctrl.minimum, queryctrl.maximum,
2558  queryctrl.step, numbers[nnum].value);
2559 
2560  nnum++;
2561  }
2562  }
2563  else if (errno != EINVAL)
2564  {
2565  if (numbers)
2566  free(numbers);
2567  return errno_exit("VIDIOC_QUERYCTRL", errmsg);
2568  }
2569  }
2570 
2571  for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++)
2572  {
2573  if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2574  {
2575  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2576  {
2577  cerr << queryctrl.name << " is disabled." << endl;
2578  continue;
2579  }
2580 
2581  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2582  {
2583  numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2584  (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2585 
2586  num_ctrls = (num_ctrls == nullptr) ?
2587  (unsigned int *)malloc(sizeof(unsigned int)) :
2588  (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2589 
2590  strncpy(numbers[nnum].name, ((char *)queryctrl.name), MAXINDINAME);
2591  strncpy(numbers[nnum].label, ((char *)queryctrl.name), MAXINDILABEL);
2592  strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2593  numbers[nnum].min = queryctrl.minimum;
2594  numbers[nnum].max = queryctrl.maximum;
2595  numbers[nnum].step = queryctrl.step;
2596  numbers[nnum].value = queryctrl.default_value;
2597 
2598  /* Get current value if possible */
2599  CLEAR(control);
2600  control.id = queryctrl.id;
2601  if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2602  numbers[nnum].value = control.value;
2603 
2604  /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2605  num_ctrls[nnum] = queryctrl.id;
2606 
2607  nnum++;
2608  }
2609  }
2610  else
2611  break;
2612  }
2613 
2614  /* Store numbers in aux0 */
2615  for (int i = 0; i < nnum; i++)
2616  numbers[i].aux0 = &num_ctrls[i];
2617 
2618  nvp->np = numbers;
2619  nvp->nnp = nnum;
2620 
2621  return nnum;
2622 }
2623 
2624 int V4L2_Base::getControl(unsigned int ctrl_id, double * value, char * errmsg)
2625 {
2626  struct v4l2_control control;
2627 
2628  CLEAR(control);
2629  control.id = ctrl_id;
2630 
2631  if (-1 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2632  return errno_exit("VIDIOC_G_CTRL", errmsg);
2633  *value = (double)control.value;
2634  return 0;
2635 }
2636 
2637 int V4L2_Base::setINTControl(unsigned int ctrl_id, double new_value, char * errmsg)
2638 {
2639  struct v4l2_control control;
2640 
2641  CLEAR(queryctrl);
2642 
2643  queryctrl.id = ctrl_id;
2644  if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2645  return 0;
2646  if ((queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) || (queryctrl.flags & V4L2_CTRL_FLAG_GRABBED) ||
2647  (queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE) || (queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE))
2648  {
2649  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_WARNING, "Setting INT control %.*s will fail, currently %s%s%s%s",
2650  (int)sizeof(queryctrl.name), queryctrl.name,
2651  queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY ? "read only " : "",
2652  queryctrl.flags & V4L2_CTRL_FLAG_GRABBED ? "grabbed " : "",
2653  queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE ? "inactive " : "",
2654  queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE ? "volatile" : "");
2655  return 0;
2656  }
2657 
2658  CLEAR(control);
2659 
2660  //cerr << "The id is " << ctrl_id << " new value is " << new_value << endl;
2661 
2662  control.id = ctrl_id;
2663  control.value = (int)new_value;
2664  if (-1 == XIOCTL(fd, VIDIOC_S_CTRL, &control))
2665  {
2666  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_ERROR, "Setting INT control %.*s failed (%s)",
2667  (int)sizeof(queryctrl.name), queryctrl.name, errmsg);
2668  return errno_exit("VIDIOC_S_CTRL", errmsg);
2669  }
2670  return 0;
2671 }
2672 
2673 int V4L2_Base::setOPTControl(unsigned int ctrl_id, unsigned int new_value, char * errmsg)
2674 {
2675  struct v4l2_control control;
2676 
2677  CLEAR(queryctrl);
2678 
2679  queryctrl.id = ctrl_id;
2680  if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2681  return 0;
2682  if ((queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) || (queryctrl.flags & V4L2_CTRL_FLAG_GRABBED) ||
2683  (queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE) || (queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE))
2684  {
2685  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Setting OPT control %.*s will fail, currently %s%s%s%s",
2686  (int)sizeof(queryctrl.name), queryctrl.name,
2687  queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY ? "read only " : "",
2688  queryctrl.flags & V4L2_CTRL_FLAG_GRABBED ? "grabbed " : "",
2689  queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE ? "inactive " : "",
2690  queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE ? "volatile" : "");
2691  return 0;
2692  }
2693 
2694  CLEAR(control);
2695 
2696  //cerr << "The id is " << ctrl_id << " new value is " << new_value << endl;
2697 
2698  control.id = ctrl_id;
2699  control.value = new_value;
2700  if (-1 == XIOCTL(fd, VIDIOC_S_CTRL, &control))
2701  {
2702  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_ERROR, "Setting INT control %.*s failed (%s)",
2703  (int)sizeof(queryctrl.name), queryctrl.name, errmsg);
2704  return errno_exit("VIDIOC_S_CTRL", errmsg);
2705  }
2706  return 0;
2707 }
2708 
2709 bool V4L2_Base::enumerate_ext_ctrl()
2710 {
2711  //struct v4l2_queryctrl queryctrl;
2712 
2713  CLEAR(queryctrl);
2714 
2715  queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2716  if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2717  return false;
2718 
2719  queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2720  while (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2721  {
2722  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2723  {
2724  cerr << "DISABLED--Control " << queryctrl.name << endl;
2725  queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2726  continue;
2727  }
2728 
2729  if (queryctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS)
2730  {
2731  cerr << "Control Class " << queryctrl.name << endl;
2732  queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2733  continue;
2734  }
2735 
2736  cerr << "Control " << queryctrl.name << endl;
2737 
2738 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2739  if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2740 #else
2741  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2742 #endif
2743  enumerate_menu();
2744  if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2745  cerr << " boolean" << endl;
2746  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2747  cerr << " integer" << endl;
2748  //if (queryctrl.type == V4L2_CTRL_TYPE_BITMASK) //not in < 3.1
2749  // cerr << " bitmask" <<endl;
2750  if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2751  cerr << " button" << endl;
2752  queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2753  }
2754  return true;
2755 }
2756 
2757 // TODO free opt/menu aux placeholders
2758 
2759 bool V4L2_Base::queryExtControls(INumberVectorProperty * nvp, unsigned int * nnumber, ISwitchVectorProperty ** options,
2760  unsigned int * noptions, const char * dev, const char * group)
2761 {
2762  struct v4l2_control control;
2763 
2764  INumber * numbers = nullptr;
2765  unsigned int * num_ctrls = nullptr;
2766  int nnum = 0;
2767  ISwitchVectorProperty * opt = nullptr;
2768  unsigned int nopt = 0;
2769  char optname[] = "OPT000";
2770  char swonname[] = "SET_OPT000";
2771  char swoffname[] = "UNSET_OPT000";
2772  char menuname[] = "MENU000";
2773  char menuoptname[] = "MENU000_OPT000";
2774  *noptions = 0;
2775  *nnumber = 0;
2776 
2777  CLEAR(queryctrl);
2778 
2779  queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2780  if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2781  return false;
2782 
2783  CLEAR(queryctrl);
2784  queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2785  while (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2786  {
2787  if (queryctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS)
2788  {
2789  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Control Class %.*s", (int)sizeof(queryctrl.name),
2790  queryctrl.name);
2791  queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2792  continue;
2793  }
2794 
2795  if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2796  {
2797  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%.*s is disabled.", (int)sizeof(queryctrl.name),
2798  queryctrl.name);
2799  queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2800  continue;
2801  }
2802 
2803  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2804  {
2805  numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2806  (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2807 
2808  num_ctrls = (num_ctrls == nullptr) ? (unsigned int *)malloc(sizeof(unsigned int)) :
2809  (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2810 
2811  strncpy(numbers[nnum].name, (const char *)entityXML((char *)queryctrl.name), MAXINDINAME);
2812  strncpy(numbers[nnum].label, (const char *)entityXML((char *)queryctrl.name), MAXINDILABEL);
2813  strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2814  numbers[nnum].min = queryctrl.minimum;
2815  numbers[nnum].max = queryctrl.maximum;
2816  numbers[nnum].step = queryctrl.step;
2817  numbers[nnum].value = queryctrl.default_value;
2818 
2819  /* Get current value if possible */
2820  CLEAR(control);
2821  control.id = queryctrl.id;
2822  if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2823  numbers[nnum].value = control.value;
2824 
2825  /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2826  num_ctrls[nnum] = queryctrl.id;
2827 
2828  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding %.*s -- min: %d max: %d step: %d value: %d",
2829  (int)sizeof(queryctrl.name), queryctrl.name, queryctrl.minimum, queryctrl.maximum,
2830  queryctrl.step, numbers[nnum].value);
2831 
2832  nnum++;
2833  }
2834  if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2835  {
2836  ISwitch * sw = (ISwitch *)malloc(2 * sizeof(ISwitch));
2837  snprintf(optname + 3, 4, "%03d", nopt);
2838  snprintf(swonname + 7, 4, "%03d", nopt);
2839  snprintf(swoffname + 9, 4, "%03d", nopt);
2840 
2841  opt = (opt == nullptr) ? (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2842  (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2843 
2844  CLEAR(control);
2845  control.id = queryctrl.id;
2846  XIOCTL(fd, VIDIOC_G_CTRL, &control);
2847 
2848  IUFillSwitch(sw, swonname, "Off", (control.value ? ISS_OFF : ISS_ON));
2849  sw->aux = nullptr;
2850  IUFillSwitch(sw + 1, swoffname, "On", (control.value ? ISS_ON : ISS_OFF));
2851  (sw + 1)->aux = nullptr;
2852  queryctrl.name[31] = '\0';
2853  IUFillSwitchVector(&opt[nopt], sw, 2, dev, optname, (const char *)entityXML((char *)queryctrl.name), group,
2854  IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2855  opt[nopt].aux = malloc(sizeof(unsigned int));
2856  *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2857 
2858  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding switch %.*s (%s)", (int)sizeof(queryctrl.name),
2859  queryctrl.name, (control.value ? "On" : "Off"));
2860  nopt += 1;
2861  }
2862  if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2863  {
2864  ISwitch * sw = (ISwitch *)malloc(sizeof(ISwitch));
2865  snprintf(optname + 3, 4, "%03d", nopt);
2866  snprintf(swonname + 7, 4, "%03d", nopt);
2867  //snprintf(swoffname+9, 4, "%03d", nopt);
2868 
2869  opt = (opt == nullptr) ? (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2870  (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2871 
2872  queryctrl.name[31] = '\0';
2873  IUFillSwitch(sw, swonname, (const char *)entityXML((char *)queryctrl.name), ISS_OFF);
2874  sw->aux = nullptr;
2875  IUFillSwitchVector(&opt[nopt], sw, 1, dev, optname, (const char *)entityXML((char *)queryctrl.name), group,
2876  IP_RW, ISR_NOFMANY, 0.0, IPS_IDLE);
2877  opt[nopt].aux = malloc(sizeof(unsigned int));
2878  *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2879  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding Button %.*s", (int)sizeof(queryctrl.name),
2880  queryctrl.name);
2881  nopt += 1;
2882  }
2883 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2884  if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2885 #else
2886  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2887 #endif
2888  {
2889  ISwitch * sw = nullptr;
2890  unsigned int nmenuopt = 0;
2891  char sname[32];
2892  snprintf(menuname + 4, 4, "%03d", nopt);
2893  snprintf(menuoptname + 4, 4, "%03d", nopt);
2894  menuoptname[7] = '_';
2895 
2896  opt = (opt == nullptr) ? (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2897  (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2898 
2899  CLEAR(control);
2900  control.id = queryctrl.id;
2901  XIOCTL(fd, VIDIOC_G_CTRL, &control);
2902 
2903  CLEAR(querymenu);
2904  querymenu.id = queryctrl.id;
2905 
2906  for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2907  {
2908  if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2909  {
2910  sw = (sw == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
2911  (ISwitch *)realloc(sw, (nmenuopt + 1) * sizeof(ISwitch));
2912  snprintf(menuoptname + 11, 4, "%03d", nmenuopt);
2913 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2914  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2915  {
2916  snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2917  sname[31] = '\0';
2918  }
2919  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2920  {
2921  snprintf(sname, 19, "0x%016llX", querymenu.value);
2922  sname[31] = '\0';
2923  }
2924 #else
2925  snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2926  sname[31] = '\0';
2927 #endif
2928  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu item %.*s %.*s item %d index %d",
2929  (int)sizeof(sname), sname, (int)sizeof(menuoptname), menuoptname, nmenuopt,
2930  querymenu.index);
2931  //IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)sname, (control.value==nmenuopt?ISS_ON:ISS_OFF));
2932  IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)entityXML((char *)querymenu.name),
2933  (control.value == (int)nmenuopt ? ISS_ON : ISS_OFF));
2934  sw[nmenuopt].aux = malloc(sizeof(unsigned int));
2935  *(unsigned int *)(sw[nmenuopt].aux) = (querymenu.index);
2936  nmenuopt += 1;
2937  }
2938  else
2939  {
2940  //errno_exit("VIDIOC_QUERYMENU", errmsg);
2941  //exit(3);
2942  }
2943  }
2944 
2945  queryctrl.name[31] = '\0';
2946  IUFillSwitchVector(&opt[nopt], sw, nmenuopt, dev, menuname, (const char *)entityXML((char *)queryctrl.name),
2947  group, IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2948  opt[nopt].aux = malloc(sizeof(unsigned int));
2949  *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2950 
2951  DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu %.*s (item %d set)",
2952  (int)sizeof(queryctrl.name), queryctrl.name, control.value);
2953  nopt += 1;
2954  }
2955 
2956  //if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2957  // {
2958  // DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"Control type not implemented\n");
2959  //}
2960 
2961  queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2962  }
2963 
2964  /* Store numbers in aux0 */
2965  for (int i = 0; i < nnum; i++)
2966  numbers[i].aux0 = &num_ctrls[i];
2967 
2968  nvp->np = numbers;
2969  nvp->nnp = nnum;
2970  *nnumber = nnum;
2971 
2972  *options = opt;
2973  *noptions = nopt;
2974 
2975  return true;
2976 }
2977 
2978 void V4L2_Base::setDeviceName(const char * name)
2979 {
2980  strncpy(deviceName, name, MAXINDIDEVICE);
2981 }
2982 
2983 
2991 std::map<std::string, std::string> V4L2_Base::enumerate()
2992 {
2993  std::map<std::string, std::string> devices;
2994 
2995  auto searchPath = [&](std::string prefix)
2996  {
2997  struct dirent **namelist;
2998  std::vector<std::string> detectedDevices;
2999  int devCount = 0;
3000  devCount = scandir(prefix.c_str(), &namelist, video_dev_file_select, alphasort);
3001  if (devCount > 0)
3002  {
3003  while (devCount--)
3004  {
3005  if (detectedDevices.size() < 10)
3006  {
3007  std::string s(namelist[devCount]->d_name);
3008  s.erase(s.find_last_not_of(" \n\r\t") + 1);
3009  detectedDevices.push_back(prefix + s);
3010  }
3011  free(namelist[devCount]);
3012  }
3013  free(namelist);
3014  }
3015 
3016  return detectedDevices;
3017  };
3018 
3019  const std::vector<std::string> videoDevices = searchPath("/dev/");
3020 
3021  for (const auto &oneDevice : videoDevices)
3022  {
3023  int fd = open(oneDevice.c_str(), O_RDWR | O_NONBLOCK, 0);
3024  if (fd >= 0)
3025  {
3026  struct v4l2_capability cap;
3027  if (ioctl(fd, VIDIOC_QUERYCAP, &cap) >= 0)
3028  {
3029  devices[std::string(reinterpret_cast<const char *>(cap.card))] = oneDevice;
3030  }
3031  close(fd);
3032  }
3033  }
3034 
3035  return devices;
3036 }
3037 
3041 int V4L2_Base::video_dev_file_select(const dirent * entry)
3042 {
3043  static const char *filter_names[] = { "video", nullptr };
3044  const char **filter;
3045 
3046  for (filter = filter_names; *filter; ++filter)
3047  {
3048  if (strstr(entry->d_name, *filter) != nullptr)
3049  {
3050  return (true);
3051  }
3052  }
3053  return (false);
3054 }
3055 }
void IERmCallback(int callbackid)
Remove a callback function.
Definition: eventloop.c:577
void rmCallback(int cid)
Definition: eventloop.c:189
int IEAddCallback(int readfiledes, IE_CBF *fp, void *p)
Register a new callback, fp, to be called with userpointer as argument when readfiledes is ready.
Definition: eventloop.c:572
Public interface to INDI's eventloop mechanism.
void() WPF(void *)
Signature of a work procedure function.
Definition: eventloop.h:43
int errno
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
#define MAXINDIDEVICE
Definition: indiapi.h:193
#define MAXINDIFORMAT
Definition: indiapi.h:195
@ IP_RW
Definition: indiapi.h:186
@ IPS_IDLE
Definition: indiapi.h:161
#define MAXINDILABEL
Definition: indiapi.h:192
struct _ISwitchVectorProperty ISwitchVectorProperty
struct _INumber INumber
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_NOFMANY
Definition: indiapi.h:175
#define MAXINDINAME
Definition: indiapi.h:191
struct _ISwitch ISwitch
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
Interface to the reference INDI C API device implementation on the Device Driver side.
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
#define DEBUGDEVICE(device, priority, msg)
Definition: indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
int fd
Definition: intelliscope.c:43
char * entityXML(char *s)
return a string with all xml-sensitive characters within the passed string replaced with their entity...
Definition: lilxml.cpp:975
A little DOM-style library to handle parsing and processing an XML file.
Namespace to encapsulate INDI client, drivers, and mediator classes.
const char * getDeviceName()
Definition: json.h:4973
#define PWC_FPS_SHIFT
Definition: pwc-ioctl.h:80
__le16 type
Definition: pwc-ioctl.h:0
One number descriptor.
One switch descriptor.
Number vector property descriptor.
Definition: indiapi.h:319
Switch vector property descriptor.
Definition: indiapi.h:367
#define DBG_BUF(b)
Definition: v4l2_base.cpp:110
#define DBG_STR_BUF
Definition: v4l2_base.cpp:109
#define DBG_FMT(f)
Definition: v4l2_base.cpp:98
#define CLEAR(x)
Definition: v4l2_base.cpp:60
#define XIOCTL(fd, ioctl, arg)
Definition: v4l2_base.cpp:62
#define ERRMSGSIZ
Definition: v4l2_base.cpp:58
#define DBG_STR_FMT
Definition: v4l2_base.cpp:97
@ LX_ACTIVE
Definition: v4l2_base.h:50
@ LX_TRIGGERED
Definition: v4l2_base.h:51