• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "v4l2_uvc.h"
17 #include "securec.h"
18 #include "v4l2_control.h"
19 #include "v4l2_fileformat.h"
20 #include "v4l2_dev.h"
21 
22 namespace OHOS::Camera {
HosV4L2UVC()23 HosV4L2UVC::HosV4L2UVC() {}
~HosV4L2UVC()24 HosV4L2UVC::~HosV4L2UVC() {}
25 
V4L2UvcSearchCapability(const std::string devName,const std::string v4l2Device,bool inOut)26 void HosV4L2UVC::V4L2UvcSearchCapability(const std::string devName, const std::string v4l2Device, bool inOut)
27 {
28     if (devName.length() == 0 || v4l2Device.length() == 0) {
29         CAMERA_LOGE("UVC:V4L2UvcSearchCapability devName or v4l2Device is null");
30     }
31 
32     std::vector<DeviceControl>().swap(control_);
33     std::vector<DeviceFormat>().swap(format_);
34 
35     if (inOut) {
36         char *name = nullptr;
37         char absPath[PATH_MAX] = {0};
38 
39         name = realpath(v4l2Device.c_str(), absPath);
40         if (name == nullptr) {
41             CAMERA_LOGE("UVC:V4L2UvcMatchDev realpath error\n");
42             return;
43         }
44         int fd = open(name, O_RDWR | O_NONBLOCK, 0);
45         free(name);
46         if (fd < 0) {
47             CAMERA_LOGE("UVC:V4L2UvcSearchCapability open %s name %s error\n", v4l2Device.c_str(), devName.c_str());
48         } else {
49             std::shared_ptr<HosFileFormat> fileFormat = nullptr;
50             fileFormat = std::make_shared<HosFileFormat>();
51             if (fileFormat == nullptr) {
52                 CAMERA_LOGE("UVC:V4L2UvcMatchDev fileFormat make_shared is NULL\n");
53             } else {
54                 fileFormat->V4L2GetFmtDescs(fd, format_);
55             }
56 
57             std::shared_ptr<HosV4L2Control> control = nullptr;
58             control = std::make_shared<HosV4L2Control>();
59             if (control == nullptr) {
60                 CAMERA_LOGE("UVC:V4L2UvcMatchDev control make_shared is NULL\n");
61             } else {
62                 control->V4L2GetControls(fd, control_);
63             }
64             close(fd);
65         }
66     }
67 }
68 
V4L2UvcMatchDev(const std::string name,const std::string v4l2Device,bool inOut)69 void HosV4L2UVC::V4L2UvcMatchDev(const std::string name, const std::string v4l2Device, bool inOut)
70 {
71     std::pair<std::map<std::string, std::string>::iterator, bool> iter;
72     constexpr uint32_t nameSize = 16;
73     int i = 0;
74     char devName[nameSize] = {0};
75 
76     CAMERA_LOGD("UVC:V4L2UvcMatchDev name %{public}s v4l2Device %{public}s inOut = %{public}d\n",
77         name.c_str(), v4l2Device.c_str(), inOut);
78     if ((sprintf_s(devName, sizeof(devName), "%s", name.c_str())) < 0) {
79         CAMERA_LOGE("%s: sprintf devName failed", __func__);
80         return;
81     }
82     if (inOut) {
83         {
84             std::lock_guard<std::mutex> l(HosV4L2Dev::deviceFdLock_);
85             iter = HosV4L2Dev::deviceMatch.insert(std::make_pair(std::string(devName), v4l2Device));
86         }
87         if (!iter.second) {
88             for (i = 1; i < MAXUVCNODE; i++) {
89                 if ((sprintf_s(devName, sizeof(devName), "%s%d", devName, i)) < 0) {
90                     CAMERA_LOGE("%{public}s: sprintf devName failed", __func__);
91                     return;
92                 }
93                 {
94                     std::lock_guard<std::mutex> l(HosV4L2Dev::deviceFdLock_);
95                     iter = HosV4L2Dev::deviceMatch.insert(std::make_pair(std::string(devName), v4l2Device));
96                 }
97                 if (iter.second) {
98                     CAMERA_LOGD("UVC:V4L2UvcMatchDev::deviceMatch.insert: %{public}s devName %{public}s i %{public}d\n",
99                         v4l2Device.c_str(), devName, i);
100                     break;
101                 }
102             }
103         }
104     } else {
105         CAMERA_LOGD("UVC: HosV4L2Dev::deviceMatch.erase: %{public}s devName %{public}s\n",
106             v4l2Device.c_str(), devName);
107         std::lock_guard<std::mutex> l(HosV4L2Dev::deviceFdLock_);
108         HosV4L2Dev::deviceMatch.erase(std::string(devName));
109     }
110 
111     V4L2UvcSearchCapability(std::string(devName), v4l2Device, inOut);
112 
113     uvcCallbackFun_(std::string(devName), control_, format_, inOut);
114 }
115 
V4L2UvcGetCap(const std::string v4l2Device,struct v4l2_capability & cap)116 RetCode HosV4L2UVC::V4L2UvcGetCap(const std::string v4l2Device, struct v4l2_capability& cap)
117 {
118     int fd, rc;
119     char *devName = nullptr;
120     char absPath[PATH_MAX] = {0};
121 
122     devName = realpath(v4l2Device.c_str(), absPath);
123     if (devName == nullptr) {
124         CAMERA_LOGE("UVC:V4L2UvcGetCap realpath error v4l2Device == %{public}s\n", v4l2Device.c_str());
125         return RC_ERROR;
126     }
127 
128     fd = open(devName, O_RDWR | O_NONBLOCK, 0);
129     free(devName);
130     if (fd < 0) {
131         CAMERA_LOGE("UVC:ERROR opening V4L2 interface for %{public}s\n", v4l2Device.c_str());
132         return RC_ERROR;
133     }
134 
135     rc = ioctl(fd, VIDIOC_QUERYCAP, &cap);
136     if (rc < 0) {
137         CAMERA_LOGE("UVC:%{public}s V4L2EnmeDevices VIDIOC_QUERYCAP erro\n", v4l2Device.c_str());
138         close(fd);
139         return RC_ERROR;
140     }
141     close(fd);
142 
143     if (!((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && (cap.capabilities & V4L2_CAP_STREAMING))) {
144         return RC_ERROR;
145     }
146 
147     return RC_OK;
148 }
149 
V4L2UVCGetCapability(int fd,const std::string devName,std::string & cameraId)150 RetCode HosV4L2UVC::V4L2UVCGetCapability(int fd, const std::string devName, std::string& cameraId)
151 {
152     struct v4l2_capability capability = {};
153     constexpr uint32_t removeCount = 5;
154 
155     int rc = ioctl(fd, VIDIOC_QUERYCAP, &capability);
156     if (rc < 0) {
157         return RC_ERROR;
158     }
159 
160     if (!((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) && (capability.capabilities & V4L2_CAP_STREAMING))) {
161         return RC_ERROR;
162     }
163 
164     if (cameraId != std::string((char*)capability.driver)) {
165         return RC_ERROR;
166     }
167     V4L2UvcMatchDev(std::string((char*)capability.driver), devName, true);
168 
169     CAMERA_LOGD("UVC:v4l2 driver name = %{public}s\n", capability.driver);
170     CAMERA_LOGD("UVC:v4l2 capabilities = 0x{public}%x\n", capability.capabilities);
171     CAMERA_LOGD("UVC:v4l2 card: %{public}s\n", capability.card);
172     CAMERA_LOGD("UVC:v4l2 bus info: %{public}s\n", capability.bus_info);
173 
174     return RC_OK;
175 }
176 
V4L2UvcEnmeDevices()177 void HosV4L2UVC::V4L2UvcEnmeDevices()
178 {
179     struct stat sta = {};
180     std::string name = DEVICENAMEX;
181     char devName[16] = {0};
182     std::string cameraId = "uvcvideo";
183     int rc = 0;
184     int fd = 0;
185 
186     for (int j = 0; j < MAXVIDEODEVICE; ++j) {
187         if ((sprintf_s(devName, sizeof(devName), "%s%d", name.c_str(), j)) < 0) {
188             CAMERA_LOGE("%{public}s: sprintf devName failed", __func__);
189             return;
190         }
191 
192         if (stat(devName, &sta) != 0) {
193             continue;
194         }
195 
196         if (!S_ISCHR(sta.st_mode)) {
197             continue;
198         }
199 
200         fd = open(devName, O_RDWR | O_NONBLOCK, 0);
201         if (fd == -1) {
202             continue;
203         }
204 
205         rc = V4L2UVCGetCapability(fd, devName, cameraId);
206         if (rc == RC_ERROR) {
207             close(fd);
208             continue;
209         }
210 
211         close(fd);
212         break;
213     }
214 }
215 
V4L2GetUsbValue(const char * key,const char * str,int len)216 const char* HosV4L2UVC::V4L2GetUsbValue(const char* key, const char* str, int len)
217 {
218     if (key == nullptr || str == nullptr || len <= 0 || strlen(key) > len) {
219         return nullptr;
220     }
221 
222     const char* pos = strstr(str, key);
223     if (pos == nullptr) {
224         return nullptr;
225     }
226 
227     if (pos + strlen(key) - str > len) {
228         return nullptr;
229     }
230     return pos + strlen(key);
231 }
232 
V4L2GetUsbString(std::string & action,std::string & subsystem,std::string & devnode,char * buf,unsigned int len)233 void HosV4L2UVC::V4L2GetUsbString(std::string& action, std::string& subsystem,
234     std::string& devnode, char* buf, unsigned int len)
235 {
236     int lineLen;
237     int pos = 0;
238     const char* retVal;
239 
240     CAMERA_LOGD("UVC:V4L2GetUsbString enter\n");
241 
242     lineLen = strlen(buf);
243     while (pos + lineLen < len && lineLen) {
244         if (action == "") {
245             retVal = V4L2GetUsbValue("ACTION=", buf + pos, lineLen);
246             if (retVal == nullptr) {
247                 action = "";
248             } else {
249                 action = std::string(retVal);
250                 CAMERA_LOGD("UVC:V4L2GetUsbString action %{public}s\n", action.c_str());
251             }
252         }
253 
254         if (subsystem == "") {
255             retVal = V4L2GetUsbValue("SUBSYSTEM=", buf + pos, lineLen);
256             if (retVal == nullptr) {
257                 subsystem = "";
258             } else {
259                 subsystem = std::string(retVal);
260                 CAMERA_LOGD("UVC:V4L2GetUsbString subsystem %{public}s\n", subsystem.c_str());
261             }
262         }
263 
264         if (devnode == "") {
265             retVal = V4L2GetUsbValue("DEVNAME=", buf + pos, lineLen);
266             if (retVal == nullptr) {
267                 devnode = "";
268             } else {
269                 devnode = std::string(retVal);
270                 CAMERA_LOGD("UVC:V4L2GetUsbString devnode %{public}s\n", devnode.c_str());
271             }
272         }
273 
274         pos += lineLen + 1;
275         lineLen = strlen(buf + pos);
276     }
277 
278     CAMERA_LOGD("UVC:V4L2GetUsbString exit\n");
279 }
280 
loopUvcDevice()281 void HosV4L2UVC::loopUvcDevice()
282 {
283     fd_set fds;
284     int rc;
285     constexpr uint32_t delayTime = 200000;
286     CAMERA_LOGD("UVC:loopUVCDevice fd = %{public}d getuid() = %{public}d\n", uDevFd_, getuid());
287     V4L2UvcEnmeDevices();
288     FD_ZERO(&fds);
289     FD_SET(uDevFd_, &fds);
290     FD_SET(eventFd_, &fds);
291     while (uvcDetectEnable_) {
292         rc = select(((uDevFd_ > eventFd_) ? uDevFd_ : eventFd_) + 1, &fds, &fds, NULL, NULL);
293         if (rc > 0 && FD_ISSET(uDevFd_, &fds)) {
294             usleep(delayTime);
295             constexpr uint32_t buffSize = 4096;
296             char buf[buffSize] = {};
297             unsigned int len = recv(uDevFd_, buf, sizeof(buf), 0);
298             if (len > 0 && (strstr(buf, "video4linux") != nullptr)) {
299                 std::string action = "", subsystem = "", devnode = "";
300                 V4L2GetUsbString(action, subsystem, devnode, buf, len);
301                 if (subsystem == "video4linux") {
302                     CAMERA_LOGD("UVC:ACTION = %{public}s, SUBSYSTEM = %{public}s, DEVNAME = %{public}s\n", \
303                                 action.c_str(), subsystem.c_str(), devnode.c_str());
304                     if (action == "remove") {
305                         for (auto &itr : HosV4L2Dev::deviceMatch) {
306                             std::string devName = {};
307                             devName = "/dev/" + devnode;
308                             if (itr.second == devName) {
309                                 CAMERA_LOGD("UVC:loop HosV4L2Dev::deviceMatch %{public}s\n", action.c_str());
310                                 V4L2UvcMatchDev(itr.first, devName, false);
311                                 break;
312                             }
313                         }
314                     } else {
315                         struct v4l2_capability cap = {};
316                         std::string devName = {};
317                         devName = "/dev/" + devnode;
318                         rc = V4L2UvcGetCap(devName, cap);
319                         if (rc == RC_ERROR) {
320                             CAMERA_LOGE("UVC:lop V4L2UvcGetCap err rc %d devnode = %{public}s\n", rc, devnode.c_str());
321                             continue;
322                         }
323                         CAMERA_LOGD("UVC:loop HosV4L2Dev::deviceMatch %{public}s\n", action.c_str());
324                         V4L2UvcMatchDev(std::string((char*)cap.driver), devName, true);
325                     }
326                 }
327             }
328         } else
329             CAMERA_LOGD("UVC:No Device from udev_monitor_receive_device() or exit uvcDetectEnable_ = %{public}d\n", \
330                 uvcDetectEnable_);
331     }
332 }
333 
V4L2UvcDetectUnInit()334 void HosV4L2UVC::V4L2UvcDetectUnInit()
335 {
336     int rc;
337     constexpr uint32_t delayTime = 300000;
338 
339     uvcDetectEnable_ = 0;
340 
341     CAMERA_LOGD("UVC:loop V4L2UvcDetectUnInit\n");
342 
343     uint64_t one = 1;
344     rc = write(eventFd_, &one, sizeof(one));
345     if (rc < 0) {
346         usleep(delayTime);
347         rc = write(eventFd_, &one, sizeof(one));
348     }
349 
350     uvcDetectThread_->join();
351     close(uDevFd_);
352     close(eventFd_);
353 
354     delete uvcDetectThread_;
355     uvcDetectThread_ = nullptr;
356 }
357 
V4L2UvcDetectInit(UvcCallback cb)358 RetCode HosV4L2UVC::V4L2UvcDetectInit(UvcCallback cb)
359 {
360     int rc;
361     struct sockaddr_nl nls;
362 
363     CAMERA_LOGD("UVC:V4L2Detect enter\n");
364 
365     if (cb == nullptr || uvcDetectEnable_) {
366         CAMERA_LOGE("UVC:V4L2Detect is on or UvcCallback is NULL\n");
367         return RC_ERROR;
368     }
369     // set callback
370     uvcCallbackFun_ = cb;
371 
372     uDevFd_ = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
373     if (uDevFd_ < 0) {
374         CAMERA_LOGE("UVC:V4L2Detect socket() error\n");
375         return RC_ERROR;
376     }
377 
378     memset_s(&nls, sizeof(nls), 0, sizeof(nls));
379     nls.nl_family = AF_NETLINK;
380     nls.nl_pid = getpid();
381     nls.nl_groups = 1;
382     rc = bind(uDevFd_, (struct sockaddr *)&nls, sizeof(nls));
383     if (rc < 0) {
384         CAMERA_LOGE("UVC:V4L2Detect bind() error\n");
385         goto error;
386     }
387 
388     eventFd_ = eventfd(0, 0);
389     if (eventFd_ < 0) {
390         CAMERA_LOGE("UVC:V4L2Detect eventfd error\n");
391         goto error;
392     }
393 
394     uvcDetectEnable_ = 1;
395     uvcDetectThread_ = new (std::nothrow) std::thread(&HosV4L2UVC::loopUvcDevice, this);
396     if (uvcDetectThread_ == nullptr) {
397         uvcDetectEnable_ = 0;
398         CAMERA_LOGE("UVC:V4L2Detect creat loopUVCDevice thread error\n");
399         goto error1;
400     }
401 
402     return RC_OK;
403 
404 error1:
405     close (eventFd_);
406     uvcCallbackFun_ = nullptr;
407 error:
408     close (uDevFd_);
409 
410     return RC_ERROR;
411 }
412 } // namespace OHOS::Camera
413