blob: 59321f8f60eeb3170a5519a0db6a0d7557bc9d58 [file] [log] [blame]
Keiichi Watanabeaea439c2019-03-22 22:56:51 +09001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4#include <ctype.h>
5#include <libgen.h>
6#include <linux/media.h>
7#include <linux/videodev2.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/param.h>
13#include <unistd.h>
14
15#include "label_detect.h"
16
17/* Checks if the given device is a USB camera that is not vivid. */
18static bool is_real_usb_camera(int fd) {
19 struct v4l2_capability cap;
20
21 if (do_ioctl(fd, VIDIOC_QUERYCAP, &cap))
22 return false;
23
24 /* we assume all the UVC devices on Chrome OS are USB cameras. */
25 return strcmp((const char*)cap.driver, "uvcvideo") == 0;
26}
27
28/* Checks if the given device is a vivid emulating a USB camera. */
29bool is_vivid_camera(int fd) {
30 struct v4l2_capability cap;
31
32 if (do_ioctl(fd, VIDIOC_QUERYCAP, &cap))
33 return false;
34
35 if (strcmp((const char*)cap.driver, "vivid"))
36 return false;
37
38 /* Check if vivid is emulating a video capture device. */
39 bool check_caps = false;
40 check_caps |= (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
41 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT);
42
43 check_caps |= (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) &&
44 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE);
45
46 return check_caps;
47}
48
49/* Fills vendor_id with idVendor for a given device */
50static bool get_vendor_id(const char* dev_path, char vendor_id[5]) {
51 /* Copy dev_path because basename() may modify its contents. */
52 char* copied_dev_path = strdup(dev_path);
53 if (copied_dev_path == NULL)
54 return false;
55
56 char* dev_name = basename(copied_dev_path);
57 char vid_path[MAXPATHLEN];
58 snprintf(vid_path, MAXPATHLEN, "/sys/class/video4linux/%s/device/../idVendor",
59 dev_name);
60
61 FILE* fp = fopen(vid_path, "r");
62 if (fp == NULL) {
63 TRACE("failed to open %s\n", vid_path);
64 free(copied_dev_path);
65 return false;
66 }
67
68 bool ret = true;
69 if (fgets(vendor_id, 5, fp) == NULL) {
70 TRACE("failed to read %s\n", vid_path);
71 ret = false;
72 }
73
74 free(copied_dev_path);
75 fclose(fp);
76 return ret;
77}
78
79/* Checks if the device is a builtin USB camera. */
80static bool is_builtin_usb_camera(const char* dev_path, int fd) {
81 if (!is_real_usb_camera(fd))
82 return false;
83
84 /*
Ren-Pei Zenge17d8502021-01-06 11:22:21 +080085 * Check if the camera is not an external one. The vendor IDs of external
86 * cameras used in the lab need to be listed here.
Keiichi Watanabeaea439c2019-03-22 22:56:51 +090087 *
Keiichi Watanabeaea439c2019-03-22 22:56:51 +090088 * If there are many kinds of external cameras, we might want to have a list
89 * of vid:pid of builtin cameras instead.
90 */
Ren-Pei Zenge17d8502021-01-06 11:22:21 +080091 const char* kExternalCameraVendorIds[] = {
92 "046d", // Logitech
93 "2bd9", // Huddly GO
94 };
Keiichi Watanabeaea439c2019-03-22 22:56:51 +090095 char vendor_id[5];
96 if (!get_vendor_id(dev_path, vendor_id)) {
97 TRACE("failed to get vendor ID\n");
98 return false;
99 }
Ren-Pei Zenge17d8502021-01-06 11:22:21 +0800100 for (size_t i = 0; i < sizeof(kExternalCameraVendorIds) /
101 sizeof(kExternalCameraVendorIds[0]);
102 ++i) {
103 if (strcmp(vendor_id, kExternalCameraVendorIds[i]) == 0)
104 return false;
105 }
106 return true;
Keiichi Watanabeaea439c2019-03-22 22:56:51 +0900107}
108
109/* Checks if the device is a builtin MIPI camera. */
110static bool is_builtin_mipi_camera(int fd) {
Ren-Pei Zeng52c13712020-11-06 14:37:01 +0800111 struct media_device_info info;
Keiichi Watanabeaea439c2019-03-22 22:56:51 +0900112 struct media_entity_desc desc;
113
Ren-Pei Zeng52c13712020-11-06 14:37:01 +0800114 memset(&info, 0, sizeof(info));
115 if (do_ioctl(fd, MEDIA_IOC_DEVICE_INFO, &info) != 0) {
116 TRACE("failed to get media device info\n");
117 return false;
118 }
119 if (strcmp(info.driver, "uvcvideo") == 0)
120 return false;
121
122 memset(&desc, 0, sizeof(desc));
Keiichi Watanabeaea439c2019-03-22 22:56:51 +0900123 for (desc.id = MEDIA_ENT_ID_FLAG_NEXT;
124 !do_ioctl(fd, MEDIA_IOC_ENUM_ENTITIES, &desc);
125 desc.id |= MEDIA_ENT_ID_FLAG_NEXT) {
126 if (desc.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR)
127 return true;
128 }
129
130 return false;
131}
132
133/*
134 * Exported functions
135 */
136
137static const char kVideoDeviceName[] = "/dev/video*";
138
139/* Determines "builtin_usb_camera" label. */
140bool detect_builtin_usb_camera(void) {
141 return is_any_device_with_path(kVideoDeviceName, is_builtin_usb_camera);
142}
143
144/* Determines "builtin_mipi_camera" label. */
145bool detect_builtin_mipi_camera(void) {
146 static const char kMediaDeviceName[] = "/dev/media*";
147
148 return is_any_device(kMediaDeviceName, is_builtin_mipi_camera);
149}
150
151/* Determines "builtin_vivid_camera" label. */
152bool detect_vivid_camera(void) {
153 return is_any_device(kVideoDeviceName, is_vivid_camera);
154}
155
156/* Determines "builtin_camera" label. */
157bool detect_builtin_camera(void) {
158 return detect_builtin_usb_camera() || detect_builtin_mipi_camera();
159}
160
161/* Determines "builtin_or_vivid_camera" label. */
162bool detect_builtin_or_vivid_camera(void) {
163 return detect_builtin_camera() || detect_vivid_camera();
164}