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