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