blob: b3b93a6d610f917eff83fa6fc7a4286ee2c84ee1 [file] [log] [blame]
Austin Schuhc97d48d2022-12-26 14:09:13 -08001#ifndef FRC971_VISION_MEDIA_DEVICE_H_
2#define FRC971_VISION_MEDIA_DEVICE_H_
3
4#include <linux/media.h>
5#include <linux/v4l2-subdev.h>
6#include <linux/videodev2.h>
7
8#include <cstddef>
9#include <cstdint>
10#include <optional>
11#include <string>
12#include <string_view>
13#include <vector>
14
15#include "aos/scoped/scoped_fd.h"
16#include "glog/logging.h"
17
18namespace frc971 {
19namespace vision {
20
21class MediaDevice;
22class Pad;
23class Entity;
24
25// See
26// https://www.kernel.org/doc/html/v4.9/media/uapi/mediactl/media-controller.html
27// for more info.
28//
29// This set of intertwined classes represents a Media device as exposed by the
30// media controller API from V4L2.
31//
32// A MediaDevice is a peripheral.
33//
34// That piece of hardware has Entities which are functions that that peripheral
35// exposes. This can be things like various steps in the ISP, or
36// encoder/decoder pipelines.
37//
38// Those entities each have Pads. Pads are inputs or outputs from each piece of
39// hardware.
40//
41// Pads are connected by Links. Links can only be enabled/disabled, not
42// connected at will (from what I can tell).
43//
44// The underlying API has enough info in it to make all this possible to deduce
45// without classes, but given how much we want to manipulate things, it is
46// easier to create a set of classes to index things better and add helper
47// functions.
48
49// A link between pads.
50class Link {
51 public:
52 // Returns the source pad where the data comes from.
53 Pad *source() { return source_; }
54 const Pad *source() const { return source_; }
55 // Returns the sink pad where the data goes to.
56 Pad *sink() { return sink_; }
57 const Pad *sink() const { return sink_; }
58
59 // Returns true if this pad is enabled.
60 bool enabled() const { return !!(flags_ & MEDIA_LNK_FL_ENABLED); }
61 // Returns true if this pad can be enabled/disabled.
62 bool immutable() const { return !!(flags_ & MEDIA_LNK_FL_IMMUTABLE); }
63 // Returns true if this pad is dynamic.
64 bool dynamic() const { return !!(flags_ & MEDIA_LNK_FL_DYNAMIC); }
65
66 // Returns the raw flags.
67 uint32_t flags() const { return flags_; }
68
69 private:
70 friend class MediaDevice;
71 uint32_t flags_;
72 uint32_t id_;
73
74 // Pointers to this pad. MediaDevice is responsible for updating the pointers
75 // and maintaining their lifetime.
76 Pad *source_ = nullptr;
77 Pad *sink_ = nullptr;
78};
79
80// A pad of an entity connected by links.
81class Pad {
82 public:
83 // Returns the unique ID that v4l2 assigns to this pad.
84 uint32_t id() const { return id_; }
85 // The pad index that some APIs need to talk about this pad. This starts from
86 // 0 and counts up.
87 uint16_t index() const { return index_; }
88
89 // The entity that this pad is on.
90 const Entity *entity() const { return entity_; }
91
92 // Returns true if this pad is a source.
93 bool source() const { return !!(flags_ & MEDIA_PAD_FL_SOURCE); }
94 // Returns true if this pad is a sink.
95 bool sink() const { return !!(flags_ & MEDIA_PAD_FL_SINK); }
96
97 // Returns the number of links.
98 size_t links_size() const { return links_.size(); }
99 // Returns a specific link.
100 Link *links(size_t index) { return links_[index]; }
101 const Link *links(size_t index) const { return links_[index]; }
102
103 // Prints out a representation of this pad for debugging.
104 void Log() const;
105
106 // Sets the format of this pad.
107 // TODO(austin): Should this set the paired sink pad automatically? Formats
108 // need to match...
109 void SetSubdevFormat(uint32_t width, uint32_t height, uint32_t code);
110 void SetSubdevCrop(uint32_t width, uint32_t height);
111
112 private:
113 friend class MediaDevice;
114
115 uint16_t index_;
116
117 uint32_t id_;
118 uint32_t flags_;
119 // List of links. MediaDevice is responsible for updating them and their
120 // lifetime.
121 std::vector<Link *> links_;
122
123 Entity *entity_ = nullptr;
124};
125
126// A hardware entity.
127class Entity {
128 public:
129 // Returns the ID that v4l2 assignes to this entity.
130 uint32_t id() const { return entity_.id; }
131 // Returns the name of this entity.
132 std::string_view name() const { return entity_.name; }
133
134 // Returns the function that this entity serves.
135 uint32_t function() const { return entity_.function; }
136 // Returns the type of interface.
137 uint32_t interface_type() const { return interface_.intf_type; }
138 // Returns the major and minor device numbers. Most likely, you just want the
139 // device instead.
140 uint32_t major() const { return interface_.devnode.major; }
141 uint32_t minor() const { return interface_.devnode.minor; }
142
143 // Returns the device needed to access this entity.
144 const std::string &device() const {
145 CHECK(has_interface_);
146 return device_;
147 }
148
149 // Returns all the pads, in order.
150 const std::vector<Pad *> pads() const { return pads_; }
151 Pad *pads(size_t index) { return pads_[index]; }
152
153 // Sets the format of this device.
154 void SetFormat(uint32_t width, uint32_t height, uint32_t code);
155
156 // Logs this device in a human readable format.
157 void Log() const;
158
159 private:
160 friend class MediaDevice;
161
162 // Updates device_.
163 void UpdateDevice();
164
165 struct media_v2_entity entity_;
166 // If true, interface_ has been set.
167 bool has_interface_ = false;
168 struct media_v2_interface interface_ = {0, 0, 0, {}, {}};
169
170 std::string device_;
171
172 std::vector<Pad *> pads_;
173};
174
175// Class representing a media device.
176class MediaDevice {
177 public:
178 static std::optional<MediaDevice> Initialize(int index);
179
180 // Not copyable but movable.
181 MediaDevice(const MediaDevice &) = delete;
182 MediaDevice &operator=(const MediaDevice &) = delete;
183 MediaDevice(MediaDevice &&) = default;
184 MediaDevice &operator=(MediaDevice &&) = default;
185
186 // Logs this media device in a human readable format.
187 void Log() const;
188
189 // Finds the entity with the provided name.
190 Entity *FindEntity(std::string_view entity_name);
191
192 // Finds the link connecting a source and destination entity through the
193 // provided pads. LOG(FATAL)'s if it can't find one.
194 Link *FindLink(std::string_view source, int source_pad, std::string_view sink,
195 int sink_pad);
196
197 // Disables all the mutable links in this device.
198 void Reset();
199
200 // TODO(austin): get the fd down deep enough into the link that this works.
201 // Disables the link.
202 void Reset(Link *link);
203 // Enables the link.
204 void Enable(Link *link);
205
206 // Returns the name of the driver implementing the media API as a
207 // NUL-terminated ASCII string. The driver version is stored in the
208 // driver_version field.
209 //
210 // Driver specific applications can use this information to verify the driver
211 // identity. It is also useful to work around known bugs, or to identify
212 // drivers in error reports.
213 std::string_view driver() const { return device_info_.driver; }
214
215 // Returns the device model name as a NUL-terminated UTF-8 string. The device
216 // version is stored in the device_version field and is not be appended to the
217 // model name.
218 std::string_view model() const { return device_info_.model; }
219
220 // Returns the serial number as a NUL-terminated ASCII string.
221 std::string_view serial() const { return device_info_.serial; }
222
223 // Returns the location of the device in the system as a NUL-terminated ASCII
224 // string. This includes the bus type name (PCI, USB, ...) and a bus-specific
225 // identifier.
226 std::string_view bus_info() const { return device_info_.bus_info; }
227
228 // Returns the list of entities in this device.
229 std::vector<Entity> *entities() { return &entities_; }
230
231 // Returns all the links between pads in this device.
232 std::vector<Link> *links() { return &links_; }
233
234 private:
235 MediaDevice(int fd) : fd_(fd) { Update(); }
236
237 void Update();
238
239 friend class Link;
240 friend class Entity;
241 friend class Pad;
242
243 aos::ScopedFD fd_;
244
245 std::vector<Pad> pads_;
246 std::vector<Entity> entities_;
247 std::vector<Link> links_;
248
249 struct media_device_info device_info_;
250};
251
252// Returns the MediaDevice with the requested bus_info() if it can be found, or
253// nullopt otherwise.
254std::optional<MediaDevice> FindMediaDevice(std::string_view device);
255
256} // namespace vision
257} // namespace frc971
258
259#endif // FRC971_VISION_MEDIA_DEVICE_H_