uvc-v4l2.cpp
Go to the documentation of this file.
00001 // License: Apache 2.0. See LICENSE file in root directory.
00002 // Copyright(c) 2015 Intel Corporation. All Rights Reserved.
00003 
00004 #ifdef RS_USE_V4L2_BACKEND
00005 
00006 #include "uvc.h"
00007 
00008 #include <cassert>
00009 #include <cstdlib>
00010 #include <cstdio>
00011 #include <sstream>
00012 #include <cstring>
00013 
00014 #include <algorithm>
00015 #include <functional>
00016 #include <string>
00017 #include <sstream>
00018 #include <fstream>
00019 #include <regex>
00020 #include <thread>
00021 #include <utility> // for pair
00022 #include <chrono>
00023 #include <thread>
00024 
00025 #include <dirent.h>
00026 #include <fcntl.h>
00027 #include <unistd.h>
00028 #include <limits.h>
00029 #include <errno.h>
00030 #include <sys/stat.h>
00031 #include <sys/mman.h>
00032 #include <sys/ioctl.h>
00033 #include <sys/sysmacros.h>
00034 #include <linux/usb/video.h>
00035 #include <linux/uvcvideo.h>
00036 #include <linux/videodev2.h>
00037 
00038 #pragma GCC diagnostic ignored "-Wpedantic"
00039 #include <libusb.h>
00040 #pragma GCC diagnostic pop
00041 
00042 #pragma GCC diagnostic ignored "-Woverflow"
00043 
00044 namespace rsimpl
00045 {
00046     namespace uvc
00047     {
00048         static void throw_error(const char * s)
00049         {
00050             std::ostringstream ss;
00051             ss << s << " error " << errno << ", " << strerror(errno);
00052             throw std::runtime_error(ss.str());
00053         }
00054 
00055         static void warn_error(const char * s)
00056         {
00057             LOG_ERROR(s << " error " << errno << ", " << strerror(errno));
00058         }
00059 
00060         static int xioctl(int fh, int request, void *arg)
00061         {
00062             int r;
00063             do {
00064                 r = ioctl(fh, request, arg);
00065             } while (r < 0 && errno == EINTR);
00066             return r;
00067         }
00068 
00069         struct buffer { void * start; size_t length; };
00070 
00071         struct context
00072         {
00073             libusb_context * usb_context;
00074 
00075             context()
00076             {
00077                 int status = libusb_init(&usb_context);
00078                 if(status < 0) throw std::runtime_error(to_string() << "libusb_init(...) returned " << libusb_error_name(status));
00079             }
00080             ~context()
00081             {
00082                 libusb_exit(usb_context);
00083             }
00084         };
00085 
00086         struct subdevice
00087         {
00088             std::string dev_name;   // Device name (typically of the form /dev/video*)
00089             int busnum, devnum, parent_devnum;     // USB device bus number and device number (needed for F200/SR300 direct USB controls)
00090             int vid, pid, mi;       // Vendor ID, product ID, and multiple interface index
00091             int fd;                 // File descriptor for this device
00092             std::vector<buffer> buffers;
00093 
00094             int width, height, format, fps;
00095             video_channel_callback callback = nullptr;
00096             data_channel_callback  channel_data_callback = nullptr;    // handle non-uvc data produced by device
00097             bool is_capturing;
00098             bool is_metastream;
00099 
00100             subdevice(const std::string & name) : dev_name("/dev/" + name), vid(), pid(), fd(), width(), height(), format(), callback(nullptr), channel_data_callback(nullptr), is_capturing(), is_metastream()
00101             {
00102                 struct stat st;
00103                 if(stat(dev_name.c_str(), &st) < 0)
00104                 {
00105                     std::ostringstream ss; ss << "Cannot identify '" << dev_name << "': " << errno << ", " << strerror(errno);
00106                     throw std::runtime_error(ss.str());
00107                 }
00108                 if(!S_ISCHR(st.st_mode)) throw std::runtime_error(dev_name + " is no device");
00109 
00110                 // Search directory and up to three parent directories to find busnum/devnum
00111                 std::ostringstream ss; ss << "/sys/dev/char/" << major(st.st_rdev) << ":" << minor(st.st_rdev) << "/device/";
00112                 auto path = ss.str();
00113                 bool good = false;
00114                 for(int i=0; i<=3; ++i)
00115                 {
00116                     if(std::ifstream(path + "busnum") >> busnum)
00117                     {
00118                         if(std::ifstream(path + "devnum") >> devnum)
00119                         {
00120                             if(std::ifstream(path + "../devnum") >> parent_devnum)
00121                             {
00122                                 good = true;
00123                                 break;
00124                             }
00125                         }
00126                     }
00127                     path += "../";
00128                 }
00129                 if(!good) throw std::runtime_error("Failed to read busnum/devnum");
00130 
00131                 std::string modalias;
00132                 if(!(std::ifstream("/sys/class/video4linux/" + name + "/device/modalias") >> modalias))
00133                     throw std::runtime_error("Failed to read modalias");
00134                 if(modalias.size() < 14 || modalias.substr(0,5) != "usb:v" || modalias[9] != 'p')
00135                     throw std::runtime_error("Not a usb format modalias");
00136                 if(!(std::istringstream(modalias.substr(5,4)) >> std::hex >> vid))
00137                     throw std::runtime_error("Failed to read vendor ID");
00138                 if(!(std::istringstream(modalias.substr(10,4)) >> std::hex >> pid))
00139                     throw std::runtime_error("Failed to read product ID");
00140                 if(!(std::ifstream("/sys/class/video4linux/" + name + "/device/bInterfaceNumber") >> std::hex >> mi))
00141                     throw std::runtime_error("Failed to read interface number");
00142 
00143                 fd = open(dev_name.c_str(), O_RDWR | O_NONBLOCK, 0);
00144                 if(fd < 0)
00145                 {
00146                     std::ostringstream ss; ss << "Cannot open '" << dev_name << "': " << errno << ", " << strerror(errno);
00147                     throw std::runtime_error(ss.str());
00148                 }
00149 
00150                 v4l2_capability cap = {};
00151                 if(xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
00152                 {
00153                     if(errno == EINVAL) throw std::runtime_error(dev_name + " is no V4L2 device");
00154                     else throw_error("VIDIOC_QUERYCAP");
00155                 }
00156                 if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) throw std::runtime_error(dev_name + " is no video capture device");
00157                 if(!(cap.capabilities & V4L2_CAP_STREAMING)) throw std::runtime_error(dev_name + " does not support streaming I/O");
00158                 if((cap.device_caps & V4L2_CAP_META_CAPTURE)){
00159                     is_metastream=true;
00160                 }
00161 
00162                 // Select video input, video standard and tune here.
00163                 v4l2_cropcap cropcap = {};
00164                 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00165                 if(xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0)
00166                 {
00167                     v4l2_crop crop = {};
00168                     crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00169                     crop.c = cropcap.defrect; // reset to default
00170                     if(xioctl(fd, VIDIOC_S_CROP, &crop) < 0)
00171                     {
00172                         switch (errno)
00173                         {
00174                         case EINVAL: break; // Cropping not supported
00175                         default: break; // Errors ignored
00176                         }
00177                     }
00178                 } else {} // Errors ignored
00179             }
00180 
00181             ~subdevice()
00182             {
00183                 stop_capture();
00184                 if(close(fd) < 0) warn_error("close");
00185             }
00186 
00187             int get_vid() const { return vid; }
00188             int get_pid() const { return pid; }
00189             int get_mi() const { return mi; }
00190 
00191             void get_control(const extension_unit & xu, uint8_t control, void * data, size_t size)
00192             {
00193             uvc_xu_control_query q = {static_cast<uint8_t>(xu.unit), control, UVC_GET_CUR, static_cast<uint16_t>(size), reinterpret_cast<uint8_t *>(data)};
00194                 if(xioctl(fd, UVCIOC_CTRL_QUERY, &q) < 0) throw_error("UVCIOC_CTRL_QUERY:UVC_GET_CUR");
00195             }
00196 
00197             void set_control(const extension_unit & xu, uint8_t control, void * data, size_t size)
00198             {
00199             uvc_xu_control_query q = {static_cast<uint8_t>(xu.unit), control, UVC_SET_CUR, static_cast<uint16_t>(size), reinterpret_cast<uint8_t *>(data)};
00200                 if(xioctl(fd, UVCIOC_CTRL_QUERY, &q) < 0) throw_error("UVCIOC_CTRL_QUERY:UVC_SET_CUR");
00201             }
00202 
00203             void set_format(int width, int height, int fourcc, int fps, video_channel_callback callback)
00204             {
00205                 this->width = width;
00206                 this->height = height;
00207                 this->format = fourcc;
00208                 this->fps = fps;
00209                 this->callback = callback;
00210             }
00211 
00212             void set_data_channel_cfg(data_channel_callback callback)
00213             {                
00214                 this->channel_data_callback = callback;
00215             }
00216 
00217             void start_capture()
00218             {
00219                 if(!is_capturing)
00220                 {
00221                     v4l2_format fmt = {};
00222                     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00223                     fmt.fmt.pix.width       = width;
00224                     fmt.fmt.pix.height      = height;
00225                     fmt.fmt.pix.pixelformat = format;
00226                     fmt.fmt.pix.field       = V4L2_FIELD_NONE;
00227                     if(xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) throw_error("VIDIOC_S_FMT");
00228 
00229                     v4l2_streamparm parm = {};
00230                     parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00231                     if(xioctl(fd, VIDIOC_G_PARM, &parm) < 0) throw_error("VIDIOC_G_PARM");
00232                     parm.parm.capture.timeperframe.numerator = 1;
00233                     parm.parm.capture.timeperframe.denominator = fps;
00234                     if(xioctl(fd, VIDIOC_S_PARM, &parm) < 0) throw_error("VIDIOC_S_PARM");
00235 
00236                     // Init memory mapped IO
00237                     v4l2_requestbuffers req = {};
00238                     req.count = 4;
00239                     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00240                     req.memory = V4L2_MEMORY_MMAP;
00241                     if(xioctl(fd, VIDIOC_REQBUFS, &req) < 0)
00242                     {
00243                         if(errno == EINVAL) throw std::runtime_error(dev_name + " does not support memory mapping");
00244                         else throw_error("VIDIOC_REQBUFS");
00245                     }
00246                     if(req.count < 2)
00247                     {
00248                         throw std::runtime_error("Insufficient buffer memory on " + dev_name);
00249                     }
00250 
00251                     buffers.resize(req.count);
00252                     for(size_t i = 0; i < buffers.size(); ++i)
00253                     {
00254                         v4l2_buffer buf = {};
00255                         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00256                         buf.memory = V4L2_MEMORY_MMAP;
00257                         buf.index = i;
00258                         if(xioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) throw_error("VIDIOC_QUERYBUF");
00259 
00260                         buffers[i].length = buf.length;
00261                         buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
00262                         if(buffers[i].start == MAP_FAILED) throw_error("mmap");
00263                     }
00264 
00265                     // Start capturing
00266                     for(size_t i = 0; i < buffers.size(); ++i)
00267                     {
00268                         v4l2_buffer buf = {};
00269                         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00270                         buf.memory = V4L2_MEMORY_MMAP;
00271                         buf.index = i;
00272                         if(xioctl(fd, VIDIOC_QBUF, &buf) < 0) throw_error("VIDIOC_QBUF");
00273                     }
00274 
00275                     v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00276                     for(int i=0; i<10; ++i)
00277                     {
00278                         if(xioctl(fd, VIDIOC_STREAMON, &type) < 0)
00279                         {
00280                             std::this_thread::sleep_for(std::chrono::milliseconds(100));
00281                         }
00282                     }
00283                     if(xioctl(fd, VIDIOC_STREAMON, &type) < 0) throw_error("VIDIOC_STREAMON");
00284 
00285                     is_capturing = true;
00286                 }
00287             }
00288 
00289             void stop_capture()
00290             {
00291                 if(is_capturing)
00292                 {
00293                     // Stop streamining
00294                     v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00295                     if(xioctl(fd, VIDIOC_STREAMOFF, &type) < 0) warn_error("VIDIOC_STREAMOFF");
00296 
00297                     for(size_t i = 0; i < buffers.size(); i++)
00298                     {
00299                         if(munmap(buffers[i].start, buffers[i].length) < 0) warn_error("munmap");
00300                     }
00301 
00302                     // Close memory mapped IO
00303                     struct v4l2_requestbuffers req = {};
00304                     req.count = 0;
00305                     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00306                     req.memory = V4L2_MEMORY_MMAP;
00307                     if(xioctl(fd, VIDIOC_REQBUFS, &req) < 0)
00308                     {
00309                         if(errno == EINVAL) LOG_ERROR(dev_name + " does not support memory mapping");
00310                         else warn_error("VIDIOC_REQBUFS");
00311                     }
00312 
00313                     callback = nullptr;
00314                     is_capturing = false;
00315                 }
00316             }
00317 
00318             static void poll(const std::vector<subdevice *> & subdevices)
00319             {
00320                 int max_fd = 0;
00321                 fd_set fds;
00322                 FD_ZERO(&fds);
00323                 for(auto * sub : subdevices)
00324                 {
00325                     FD_SET(sub->fd, &fds);
00326                     max_fd = std::max(max_fd, sub->fd);
00327                 }
00328 
00329                 struct timeval tv = {0,10000};
00330                 if(select(max_fd + 1, &fds, NULL, NULL, &tv) < 0)
00331                 {
00332                     if (errno == EINTR) return;
00333                     throw_error("select");
00334                 }
00335 
00336                 for(auto * sub : subdevices)
00337                 {
00338                     if(FD_ISSET(sub->fd, &fds))
00339                     {
00340                         v4l2_buffer buf = {};
00341                         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00342                         buf.memory = V4L2_MEMORY_MMAP;
00343                         if(xioctl(sub->fd, VIDIOC_DQBUF, &buf) < 0)
00344                         {
00345                             if(errno == EAGAIN) return;
00346                             throw_error("VIDIOC_DQBUF");
00347                         }
00348 
00349                         sub->callback(sub->buffers[buf.index].start,
00350                                 [sub, buf]() mutable {
00351                                     if(xioctl(sub->fd, VIDIOC_QBUF, &buf) < 0) throw_error("VIDIOC_QBUF");
00352                                 });
00353                     }
00354                 }
00355             }            
00356 
00357             static void poll_interrupts(libusb_device_handle *handle, const std::vector<subdevice *> & subdevices, uint16_t timeout)
00358             {
00359                 static const unsigned short interrupt_buf_size = 0x400;
00360                 uint8_t buffer[interrupt_buf_size];                       /* 64 byte transfer buffer  - dedicated channel*/
00361                 int num_bytes             = 0;                           /* Actual bytes transferred. */
00362 
00363                 // TODO - replace hard-coded values : 0x82 and 1000
00364                 int res = libusb_interrupt_transfer(handle, 0x84, buffer, interrupt_buf_size, &num_bytes, timeout);
00365                 if (0 == res)
00366                 {
00367                     // Propagate the data to device layer
00368                     for(auto & sub : subdevices)
00369                         if (sub->channel_data_callback)
00370                             sub->channel_data_callback(buffer, num_bytes);
00371                 }
00372                 else
00373                 {
00374                     switch (res)
00375                     {
00376                     case LIBUSB_ERROR_TIMEOUT :
00377                         LOG_WARNING("interrupt e.p. timeout");
00378                         break;
00379                     default:
00380                         throw std::runtime_error(to_string() << "USB Interrupt end-point error " << libusb_strerror((libusb_error)res));
00381                         break;
00382                     }
00383                 }
00384             }
00385         };
00386 
00387         struct device
00388         {
00389             const std::shared_ptr<context> parent;
00390             std::vector<std::unique_ptr<subdevice>> subdevices;
00391             std::thread thread;
00392             std::thread data_channel_thread;
00393             volatile bool stop;
00394             volatile bool data_stop;
00395 
00396             libusb_device * usb_device;
00397             libusb_device_handle * usb_handle;
00398             std::vector<int> claimed_interfaces;
00399 
00400             device(std::shared_ptr<context> parent) : parent(parent), stop(), data_stop(), usb_device(), usb_handle() {}
00401             ~device()
00402             {
00403                 stop_streaming();
00404 
00405                 for(auto interface_number : claimed_interfaces)
00406                 {
00407                     int status = libusb_release_interface(usb_handle, interface_number);
00408                     if(status < 0) LOG_ERROR("libusb_release_interface(...) returned " << libusb_error_name(status));
00409                 }
00410 
00411                 if(usb_handle) libusb_close(usb_handle);
00412                 if(usb_device) libusb_unref_device(usb_device);
00413             }
00414 
00415             bool has_mi(int mi) const
00416             {
00417                 for(auto & sub : subdevices)
00418                 {
00419                     if(sub->get_mi() == mi) return true;
00420                 }
00421                 return false;
00422             }
00423 
00424             void start_streaming()
00425             {
00426                 std::vector<subdevice *> subs;
00427 
00428                 for(auto & sub : subdevices)
00429                 {
00430                     if(sub->callback)
00431                     {
00432                         sub->start_capture();
00433                         subs.push_back(sub.get());
00434                     }                
00435                 }
00436 
00437                 thread = std::thread([this, subs]()
00438                 {
00439                     while(!stop) subdevice::poll(subs);
00440                 });
00441             }
00442 
00443             void stop_streaming()
00444             {
00445                 if(thread.joinable())
00446                 {
00447                     stop = true;
00448                     thread.join();
00449                     stop = false;
00450 
00451                     for(auto & sub : subdevices) sub->stop_capture();
00452                 }                
00453             }
00454 
00455             void start_data_acquisition()
00456             {
00457                 std::vector<subdevice *> data_channel_subs;
00458                 for (auto & sub : subdevices)
00459                 {                   
00460                     if (sub->channel_data_callback)
00461                     {                        
00462                         data_channel_subs.push_back(sub.get());
00463                     }
00464                 }
00465                 
00466                 // Motion events polling pipe
00467                 if (claimed_interfaces.size())
00468                 {
00469                     data_channel_thread = std::thread([this, data_channel_subs]()
00470                     {
00471                         // Polling 100ms timeout
00472                         while (!data_stop)
00473                         {
00474                             subdevice::poll_interrupts(this->usb_handle, data_channel_subs, 100);
00475                         }
00476                     });
00477                 }
00478             }
00479 
00480             void stop_data_acquisition()
00481             {
00482                 if (data_channel_thread.joinable())
00483                 {
00484                     data_stop = true;
00485                     data_channel_thread.join();
00486                     data_stop = false;
00487                 }
00488             }
00489         };
00490 
00492         // device //
00494 
00495         int get_vendor_id(const device & device) { return device.subdevices[0]->get_vid(); }
00496         int get_product_id(const device & device) { return device.subdevices[0]->get_pid(); }
00497 
00498         std::string get_usb_port_id(const device & device)
00499         {
00500             std::string usb_bus = std::to_string(libusb_get_bus_number(device.usb_device));
00501 
00502             // As per the USB 3.0 specs, the current maximum limit for the depth is 7.
00503             const int max_usb_depth = 8;
00504             uint8_t usb_ports[max_usb_depth];
00505             std::stringstream port_path;
00506             int port_count = libusb_get_port_numbers(device.usb_device, usb_ports, max_usb_depth);
00507 
00508             for (size_t i = 0; i < port_count; ++i)
00509             {
00510                 port_path << "-" << std::to_string(usb_ports[i]);
00511             }
00512 
00513             return usb_bus + port_path.str();
00514         }
00515 
00516         void get_control(const device & device, const extension_unit & xu, uint8_t ctrl, void * data, int len)
00517         {
00518             device.subdevices[xu.subdevice]->get_control(xu, ctrl, data, len);
00519         }
00520         void set_control(device & device, const extension_unit & xu, uint8_t ctrl, void * data, int len)
00521         {
00522             device.subdevices[xu.subdevice]->set_control(xu, ctrl, data, len);
00523         }
00524 
00525         void claim_interface(device & device, const guid & /*interface_guid*/, int interface_number)
00526         {
00527             if(!device.usb_handle)
00528             {
00529                 int status = libusb_open(device.usb_device, &device.usb_handle);
00530                 if(status < 0) throw std::runtime_error(to_string() << "libusb_open(...) returned " << libusb_error_name(status));
00531             }
00532 
00533             int status = libusb_claim_interface(device.usb_handle, interface_number);
00534             if(status < 0) throw std::runtime_error(to_string() << "libusb_claim_interface(...) returned " << libusb_error_name(status));
00535             device.claimed_interfaces.push_back(interface_number);
00536         }
00537         void claim_aux_interface(device & device, const guid & interface_guid, int interface_number)
00538         {
00539             claim_interface(device, interface_guid, interface_number);
00540         }
00541 
00542         void bulk_transfer(device & device, unsigned char endpoint, void * data, int length, int *actual_length, unsigned int timeout)
00543         {
00544             if(!device.usb_handle) throw std::logic_error("called uvc::bulk_transfer before uvc::claim_interface");
00545             int status = libusb_bulk_transfer(device.usb_handle, endpoint, (unsigned char *)data, length, actual_length, timeout);
00546             if(status < 0) throw std::runtime_error(to_string() << "libusb_bulk_transfer(...) returned " << libusb_error_name(status));
00547         }
00548 
00549         void interrupt_transfer(device & device, unsigned char endpoint, void * data, int length, int *actual_length, unsigned int timeout)
00550         {
00551             if(!device.usb_handle) throw std::logic_error("called uvc::interrupt_transfer before uvc::claim_interface");
00552             int status = libusb_interrupt_transfer(device.usb_handle, endpoint, (unsigned char *)data, length, actual_length, timeout);
00553             if(status < 0) throw std::runtime_error(to_string() << "libusb_interrupt_transfer(...) returned " << libusb_error_name(status));
00554         }
00555 
00556         void set_subdevice_mode(device & device, int subdevice_index, int width, int height, uint32_t fourcc, int fps, video_channel_callback callback)
00557         {
00558             device.subdevices[subdevice_index]->set_format(width, height, (const big_endian<int> &)fourcc, fps, callback);
00559         }
00560 
00561         void set_subdevice_data_channel_handler(device & device, int subdevice_index, data_channel_callback callback)
00562         {
00563             device.subdevices[subdevice_index]->set_data_channel_cfg(callback);
00564         }
00565 
00566         void start_streaming(device & device, int /*num_transfer_bufs*/)
00567         {
00568             device.start_streaming();
00569         }
00570 
00571         void stop_streaming(device & device)
00572         {
00573             device.stop_streaming();
00574         }       
00575 
00576         void start_data_acquisition(device & device)
00577         {
00578             device.start_data_acquisition();
00579         }
00580 
00581         void stop_data_acquisition(device & device)
00582         {
00583             device.stop_data_acquisition();
00584         }
00585 
00586         static uint32_t get_cid(rs_option option)
00587         {
00588             switch(option)
00589             {
00590             case RS_OPTION_COLOR_BACKLIGHT_COMPENSATION: return V4L2_CID_BACKLIGHT_COMPENSATION;
00591             case RS_OPTION_COLOR_BRIGHTNESS: return V4L2_CID_BRIGHTNESS;
00592             case RS_OPTION_COLOR_CONTRAST: return V4L2_CID_CONTRAST;
00593             case RS_OPTION_COLOR_EXPOSURE: return V4L2_CID_EXPOSURE_ABSOLUTE; // Is this actually valid? I'm getting a lot of VIDIOC error 22s...
00594             case RS_OPTION_COLOR_GAIN: return V4L2_CID_GAIN;
00595             case RS_OPTION_COLOR_GAMMA: return V4L2_CID_GAMMA;
00596             case RS_OPTION_COLOR_HUE: return V4L2_CID_HUE;
00597             case RS_OPTION_COLOR_SATURATION: return V4L2_CID_SATURATION;
00598             case RS_OPTION_COLOR_SHARPNESS: return V4L2_CID_SHARPNESS;
00599             case RS_OPTION_COLOR_WHITE_BALANCE: return V4L2_CID_WHITE_BALANCE_TEMPERATURE;
00600             case RS_OPTION_COLOR_ENABLE_AUTO_EXPOSURE: return V4L2_CID_EXPOSURE_AUTO; // Automatic gain/exposure control
00601             case RS_OPTION_COLOR_ENABLE_AUTO_WHITE_BALANCE: return V4L2_CID_AUTO_WHITE_BALANCE;
00602             case RS_OPTION_FISHEYE_GAIN: return V4L2_CID_GAIN;
00603             default: throw std::runtime_error(to_string() << "no v4l2 cid for option " << option);
00604             }
00605         }
00606 
00607         void set_pu_control(device & device, int subdevice, rs_option option, int value)
00608         {
00609             struct v4l2_control control = {get_cid(option), value};
00610             if (RS_OPTION_COLOR_ENABLE_AUTO_EXPOSURE==option) { control.value = value ? V4L2_EXPOSURE_APERTURE_PRIORITY : V4L2_EXPOSURE_MANUAL; }
00611             if (xioctl(device.subdevices[subdevice]->fd, VIDIOC_S_CTRL, &control) < 0) throw_error("VIDIOC_S_CTRL");
00612         }
00613 
00614         int get_pu_control(const device & device, int subdevice, rs_option option)
00615         {
00616             struct v4l2_control control = {get_cid(option), 0};
00617             if (xioctl(device.subdevices[subdevice]->fd, VIDIOC_G_CTRL, &control) < 0) throw_error("VIDIOC_G_CTRL");
00618             if (RS_OPTION_COLOR_ENABLE_AUTO_EXPOSURE==option)  { control.value = (V4L2_EXPOSURE_MANUAL==control.value) ? 0 : 1; }
00619             return control.value;
00620         }
00621 
00622         void get_pu_control_range(const device & device, int subdevice, rs_option option, int * min, int * max, int * step, int * def)
00623         {
00624             // Auto controls range is trimed to {0,1} range
00625             if(option >= RS_OPTION_COLOR_ENABLE_AUTO_EXPOSURE && option <= RS_OPTION_COLOR_ENABLE_AUTO_WHITE_BALANCE)
00626             {
00627                 if(min)  *min  = 0;
00628                 if(max)  *max  = 1;
00629                 if(step) *step = 1;
00630                 if(def)  *def  = 1;
00631                 return;
00632             }
00633 
00634             struct v4l2_queryctrl query = {};
00635             query.id = get_cid(option);
00636             if (xioctl(device.subdevices[subdevice]->fd, VIDIOC_QUERYCTRL, &query) < 0)
00637             {
00638                 // Some controls (exposure, auto exposure, auto hue) do not seem to work on V4L2
00639                 // Instead of throwing an error, return an empty range. This will cause this control to be omitted on our UI sample.
00640                 // TODO: Figure out what can be done about these options and make this work
00641                 query.minimum = query.maximum = 0;
00642             }
00643             if(min)  *min  = query.minimum;
00644             if(max)  *max  = query.maximum;
00645             if(step) *step = query.step;
00646             if(def)  *def  = query.default_value;
00647         }
00648 
00649         void get_extension_control_range(const device & device, const extension_unit & xu, char control, int * min, int * max, int * step, int * def)
00650         {
00651             __u16 size = 0;
00652             __u8 value = 0; /* all of the real sense extended controls are one byte,
00653                             checking return value for UVC_GET_LEN and allocating
00654                             appropriately might be better */
00655             __u8 * data = (__u8 *)&value;
00656             struct uvc_xu_control_query xquery;
00657             memset(&xquery, 0, sizeof(xquery));
00658             xquery.query = UVC_GET_LEN;
00659             xquery.size = 2; /* size seems to always be 2 for the LEN query, but
00660                              doesn't seem to be documented. Use result for size
00661                              in all future queries of the same control number */
00662             xquery.selector = control;
00663             xquery.unit = xu.unit;
00664             xquery.data = (__u8 *)&size;
00665 
00666             if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
00667                 throw std::runtime_error(to_string() << " ioctl failed on UVC_GET_LEN");
00668             }
00669 
00670             xquery.query = UVC_GET_MIN;
00671             xquery.size = size;
00672             xquery.selector = control;
00673             xquery.unit = xu.unit;
00674             xquery.data = data;
00675             if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
00676                 throw std::runtime_error(to_string() << " ioctl failed on UVC_GET_MIN");
00677             }
00678             *min = value;
00679 
00680             xquery.query = UVC_GET_MAX;
00681             xquery.size = size;
00682             xquery.selector = control;
00683             xquery.unit = xu.unit;
00684             xquery.data = data;
00685             if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
00686                 throw std::runtime_error(to_string() << " ioctl failed on UVC_GET_MAX");
00687             }
00688             *max = value;
00689 
00690             xquery.query = UVC_GET_DEF;
00691             xquery.size = size;
00692             xquery.selector = control;
00693             xquery.unit = xu.unit;
00694             xquery.data = data;
00695             if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
00696                 throw std::runtime_error(to_string() << " ioctl failed on UVC_GET_DEF");
00697             }
00698             *def = value;
00699 
00700             xquery.query = UVC_GET_RES;
00701             xquery.size = size;
00702             xquery.selector = control;
00703             xquery.unit = xu.unit;
00704             xquery.data = data;
00705             if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
00706                 throw std::runtime_error(to_string() << " ioctl failed on UVC_GET_CUR");
00707             }
00708             *step = value;
00709 
00710         }
00712         // context //
00714 
00715         std::shared_ptr<context> create_context()
00716         {
00717             return std::make_shared<context>();
00718         }
00719 
00720         bool is_device_connected(device & device, int vid, int pid)
00721         {
00722             for (auto& sub : device.subdevices)
00723             {
00724                 if (sub->vid == vid && sub->pid == pid)
00725                     return true;
00726             }
00727 
00728             return false;
00729         }
00730 
00731         std::vector<std::shared_ptr<device>> query_devices(std::shared_ptr<context> context)
00732         {
00733             // Enumerate all subdevices present on the system
00734             std::vector<std::unique_ptr<subdevice>> subdevices;
00735             DIR * dir = opendir("/sys/class/video4linux");
00736             if(!dir) throw std::runtime_error("Cannot access /sys/class/video4linux");
00737             while (dirent * entry = readdir(dir))
00738             {
00739                 std::string name = entry->d_name;
00740                 if(name == "." || name == "..") continue;
00741 
00742                 // Resolve a pathname to ignore virtual video devices
00743                 std::string path = "/sys/class/video4linux/" + name;
00744                 char buff[PATH_MAX];
00745                 ssize_t len = ::readlink(path.c_str(), buff, sizeof(buff)-1);
00746                 if (len != -1) 
00747                 {
00748                     buff[len] = '\0';
00749                     std::string real_path = std::string(buff);
00750                     if (real_path.find("virtual") != std::string::npos)
00751                         continue;
00752                 }
00753 
00754                 try
00755                 {
00756                     std::unique_ptr<subdevice> sub(new subdevice(name));
00757                     subdevices.push_back(move(sub));
00758                 }
00759                 catch(const std::exception & e)
00760                 {
00761                     LOG_INFO("Not a USB video device: " << e.what());
00762                 }
00763             }
00764             closedir(dir);
00765 
00766             // Note: Subdevices of a given device may not be contiguous. We can test our grouping/sorting logic by calling random_shuffle.
00767             // std::random_shuffle(begin(subdevices), end(subdevices));
00768 
00769             // Group subdevices by busnum/devnum
00770             std::vector<std::shared_ptr<device>> devices;
00771             for(auto & sub : subdevices)
00772             {
00773                 bool is_new_device = true;
00774                 for(auto & dev : devices)
00775                 {
00776                     if(sub->busnum == dev->subdevices[0]->busnum && sub->devnum == dev->subdevices[0]->devnum)
00777                     {
00778                         if (sub->is_metastream)  // avoid inserting metadata streamer
00779                             continue;
00780                         dev->subdevices.push_back(move(sub));
00781                         is_new_device = false;
00782                         break;
00783                     }
00784                 }
00785                 if(is_new_device)
00786                 {
00787                     if (sub->vid == VID_INTEL_CAMERA && sub->pid == ZR300_FISHEYE_PID)  // avoid inserting fisheye camera as a device
00788                         continue;
00789                     if (sub->is_metastream)  // avoid inserting metadata streamer
00790                         continue;
00791                     devices.push_back(std::make_shared<device>(context));
00792                     devices.back()->subdevices.push_back(move(sub));
00793                 }
00794             }
00795 
00796             // Sort subdevices within each device by multiple-interface index
00797             for(auto & dev : devices)
00798             {
00799                 std::sort(begin(dev->subdevices), end(dev->subdevices), [](const std::unique_ptr<subdevice> & a, const std::unique_ptr<subdevice> & b)
00800                 {
00801                     return a->mi < b->mi;
00802                 });
00803             }
00804 
00805 
00806             // Insert fisheye camera as subDevice of ZR300
00807             for(auto & sub : subdevices)
00808             {
00809                 if (!sub)
00810                     continue;
00811 
00812                 for(auto & dev : devices)
00813                 {
00814                     if (sub->is_metastream)  // avoid inserting metadata streamer
00815                         continue;
00816                     if (dev->subdevices[0]->vid == VID_INTEL_CAMERA && dev->subdevices[0]->pid == ZR300_CX3_PID && 
00817                         sub->vid == VID_INTEL_CAMERA && sub->pid == ZR300_FISHEYE_PID && dev->subdevices[0]->parent_devnum == sub->parent_devnum)
00818                     {
00819                         dev->subdevices.push_back(move(sub));
00820                         break;
00821                     }
00822                 }
00823             }
00824 
00825 
00826             // Obtain libusb_device_handle for each device
00827             libusb_device ** list;
00828             int status = libusb_get_device_list(context->usb_context, &list);
00829             if(status < 0) throw std::runtime_error(to_string() << "libusb_get_device_list(...) returned " << libusb_error_name(status));
00830             for(int i=0; list[i]; ++i)
00831             {
00832                 libusb_device * usb_device = list[i];
00833                 int busnum = libusb_get_bus_number(usb_device);
00834                 int devnum = libusb_get_device_address(usb_device);
00835 
00836                 // Look for a video device whose busnum/devnum matches this USB device
00837                 for(auto & dev : devices)
00838                 {
00839                     if (dev->subdevices.size() >=4)      // Make sure that four subdevices present
00840                     {
00841                         auto parent_device = libusb_get_parent(usb_device);
00842                         if (parent_device)
00843                         {
00844                             int parent_devnum = libusb_get_device_address(libusb_get_parent(usb_device));
00845 
00846                             // First, handle the special case of FishEye
00847                             bool bFishEyeDevice = ((busnum == dev->subdevices[3]->busnum) && (parent_devnum == dev->subdevices[3]->parent_devnum));
00848                             if(bFishEyeDevice && !dev->usb_device)
00849                             {
00850                                 dev->usb_device = usb_device;
00851                                 libusb_ref_device(usb_device);
00852                                 break;
00853                             }
00854                         }
00855                     }
00856 
00857                     if(busnum == dev->subdevices[0]->busnum && devnum == dev->subdevices[0]->devnum)
00858                     {
00859                         if (!dev->usb_device) // do not override previous configuration
00860                         {
00861                             dev->usb_device = usb_device;
00862                             libusb_ref_device(usb_device);
00863                             break;
00864                         }
00865                     }                    
00866                 }
00867             }
00868             libusb_free_device_list(list, 1);
00869 
00870             return devices;
00871         }
00872     }
00873 }
00874 
00875 #endif


librealsense
Author(s): Sergey Dorodnicov , Mark Horn , Reagan Lopez
autogenerated on Tue Jun 25 2019 19:54:39