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