Blame view
drivers/media/media-device.c
22.3 KB
176fb0d10 [media] media: Me... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * Media device * * Copyright (C) 2010 Nokia Corporation * * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> * Sakari Ailus <sakari.ailus@iki.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. |
176fb0d10 [media] media: Me... |
17 |
*/ |
b2cd27448 [media] media-dev... |
18 19 |
/* We need to access legacy defines from linux/media.h */ #define __NEED_MEDIA_LEGACY_API |
b0a1f2a84 [media] media: im... |
20 21 |
#include <linux/compat.h> #include <linux/export.h> |
665faa971 [media] media: In... |
22 |
#include <linux/idr.h> |
176fb0d10 [media] media: Me... |
23 |
#include <linux/ioctl.h> |
140d88165 [media] media: Me... |
24 |
#include <linux/media.h> |
57208e5e2 [media] media: co... |
25 |
#include <linux/slab.h> |
497e80cdf [media] media-dev... |
26 |
#include <linux/types.h> |
41b44e35b [media] media-dev... |
27 28 |
#include <linux/pci.h> #include <linux/usb.h> |
176fb0d10 [media] media: Me... |
29 30 31 |
#include <media/media-device.h> #include <media/media-devnode.h> |
53e269c10 [media] media: En... |
32 |
#include <media/media-entity.h> |
176fb0d10 [media] media: Me... |
33 |
|
e576d60bb [media] media: de... |
34 |
#ifdef CONFIG_MEDIA_CONTROLLER |
140d88165 [media] media: Me... |
35 36 37 |
/* ----------------------------------------------------------------------------- * Userspace API */ |
0629e991a [media] media: Mo... |
38 39 40 41 |
static inline void __user *media_get_uptr(__u64 arg) { return (void __user *)(uintptr_t)arg; } |
140d88165 [media] media: Me... |
42 43 44 45 46 47 48 49 50 |
static int media_device_open(struct file *filp) { return 0; } static int media_device_close(struct file *filp) { return 0; } |
f3766ad7d media: media-devi... |
51 |
static long media_device_get_info(struct media_device *dev, void *arg) |
140d88165 [media] media: Me... |
52 |
{ |
f3766ad7d media: media-devi... |
53 |
struct media_device_info *info = arg; |
bcd5081b0 [media] media: Re... |
54 |
memset(info, 0, sizeof(*info)); |
140d88165 [media] media: Me... |
55 |
|
bb07bd6b6 [media] allow ove... |
56 |
if (dev->driver_name[0]) |
bcd5081b0 [media] media: Re... |
57 |
strlcpy(info->driver, dev->driver_name, sizeof(info->driver)); |
bb07bd6b6 [media] allow ove... |
58 |
else |
bcd5081b0 [media] media: Re... |
59 60 |
strlcpy(info->driver, dev->dev->driver->name, sizeof(info->driver)); |
bb07bd6b6 [media] allow ove... |
61 |
|
bcd5081b0 [media] media: Re... |
62 63 64 |
strlcpy(info->model, dev->model, sizeof(info->model)); strlcpy(info->serial, dev->serial, sizeof(info->serial)); strlcpy(info->bus_info, dev->bus_info, sizeof(info->bus_info)); |
140d88165 [media] media: Me... |
65 |
|
6c2c188f3 media: drop use o... |
66 67 |
info->media_version = LINUX_VERSION_CODE; info->driver_version = info->media_version; |
bcd5081b0 [media] media: Re... |
68 |
info->hw_revision = dev->hw_revision; |
140d88165 [media] media: Me... |
69 |
|
b17d39453 [media] media: fi... |
70 |
return 0; |
140d88165 [media] media: Me... |
71 |
} |
1651333b0 [media] media: En... |
72 73 74 75 76 77 |
static struct media_entity *find_entity(struct media_device *mdev, u32 id) { struct media_entity *entity; int next = id & MEDIA_ENT_ID_FLAG_NEXT; id &= ~MEDIA_ENT_ID_FLAG_NEXT; |
1651333b0 [media] media: En... |
78 |
media_device_for_each_entity(entity, mdev) { |
fa762394f [media] media: cr... |
79 80 |
if (((media_entity_id(entity) == id) && !next) || ((media_entity_id(entity) > id) && next)) { |
1651333b0 [media] media: En... |
81 82 83 |
return entity; } } |
1651333b0 [media] media: En... |
84 85 |
return NULL; } |
f3766ad7d media: media-devi... |
86 |
static long media_device_enum_entities(struct media_device *mdev, void *arg) |
1651333b0 [media] media: En... |
87 |
{ |
f3766ad7d media: media-devi... |
88 |
struct media_entity_desc *entd = arg; |
1651333b0 [media] media: En... |
89 |
struct media_entity *ent; |
1651333b0 [media] media: En... |
90 |
|
bcd5081b0 [media] media: Re... |
91 |
ent = find_entity(mdev, entd->id); |
1651333b0 [media] media: En... |
92 93 |
if (ent == NULL) return -EINVAL; |
bcd5081b0 [media] media: Re... |
94 95 96 |
memset(entd, 0, sizeof(*entd)); entd->id = media_entity_id(ent); |
de8eae360 [media] media: Us... |
97 |
if (ent->name) |
bcd5081b0 [media] media: Re... |
98 99 100 101 102 103 104 |
strlcpy(entd->name, ent->name, sizeof(entd->name)); entd->type = ent->function; entd->revision = 0; /* Unused */ entd->flags = ent->flags; entd->group_id = 0; /* Unused */ entd->pads = ent->num_pads; entd->links = ent->num_links - ent->num_backlinks; |
b2cd27448 [media] media-dev... |
105 106 107 108 109 110 111 112 113 114 115 116 117 |
/* * Workaround for a bug at media-ctl <= v1.10 that makes it to * do the wrong thing if the entity function doesn't belong to * either MEDIA_ENT_F_OLD_BASE or MEDIA_ENT_F_OLD_SUBDEV_BASE * Ranges. * * Non-subdevices are expected to be at the MEDIA_ENT_F_OLD_BASE, * or, otherwise, will be silently ignored by media-ctl when * printing the graphviz diagram. So, map them into the devnode * old range. */ if (ent->function < MEDIA_ENT_F_OLD_BASE || |
98d85f3cb [media] media: Pr... |
118 |
ent->function > MEDIA_ENT_F_TUNER) { |
b2cd27448 [media] media-dev... |
119 |
if (is_media_entity_v4l2_subdev(ent)) |
bcd5081b0 [media] media: Re... |
120 |
entd->type = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; |
b2cd27448 [media] media-dev... |
121 |
else if (ent->function != MEDIA_ENT_F_IO_V4L) |
bcd5081b0 [media] media: Re... |
122 |
entd->type = MEDIA_ENT_T_DEVNODE_UNKNOWN; |
b2cd27448 [media] media-dev... |
123 |
} |
bcd5081b0 [media] media: Re... |
124 |
memcpy(&entd->raw, &ent->info, sizeof(ent->info)); |
1651333b0 [media] media: En... |
125 126 127 128 129 130 |
return 0; } static void media_device_kpad_to_upad(const struct media_pad *kpad, struct media_pad_desc *upad) { |
fa762394f [media] media: cr... |
131 |
upad->entity = media_entity_id(kpad->entity); |
1651333b0 [media] media: En... |
132 133 134 |
upad->index = kpad->index; upad->flags = kpad->flags; } |
f3766ad7d media: media-devi... |
135 |
static long media_device_enum_links(struct media_device *mdev, void *arg) |
1651333b0 [media] media: En... |
136 |
{ |
f3766ad7d media: media-devi... |
137 |
struct media_links_enum *links = arg; |
1651333b0 [media] media: En... |
138 |
struct media_entity *entity; |
1651333b0 [media] media: En... |
139 |
|
b0a1f2a84 [media] media: im... |
140 |
entity = find_entity(mdev, links->entity); |
1651333b0 [media] media: En... |
141 142 |
if (entity == NULL) return -EINVAL; |
b0a1f2a84 [media] media: im... |
143 |
if (links->pads) { |
1651333b0 [media] media: En... |
144 145 146 147 |
unsigned int p; for (p = 0; p < entity->num_pads; p++) { struct media_pad_desc pad; |
c88e739b1 [media] media: in... |
148 149 |
memset(&pad, 0, sizeof(pad)); |
1651333b0 [media] media: En... |
150 |
media_device_kpad_to_upad(&entity->pads[p], &pad); |
b0a1f2a84 [media] media: im... |
151 |
if (copy_to_user(&links->pads[p], &pad, sizeof(pad))) |
1651333b0 [media] media: En... |
152 153 154 |
return -EFAULT; } } |
b0a1f2a84 [media] media: im... |
155 |
if (links->links) { |
b83e25083 [media] media-dev... |
156 157 |
struct media_link *link; struct media_link_desc __user *ulink_desc = links->links; |
1651333b0 [media] media: En... |
158 |
|
b83e25083 [media] media-dev... |
159 160 |
list_for_each_entry(link, &entity->links, list) { struct media_link_desc klink_desc; |
1651333b0 [media] media: En... |
161 162 |
/* Ignore backlinks. */ |
b83e25083 [media] media-dev... |
163 |
if (link->source->entity != entity) |
1651333b0 [media] media: En... |
164 |
continue; |
b83e25083 [media] media-dev... |
165 166 167 168 169 170 171 172 |
memset(&klink_desc, 0, sizeof(klink_desc)); media_device_kpad_to_upad(link->source, &klink_desc.source); media_device_kpad_to_upad(link->sink, &klink_desc.sink); klink_desc.flags = link->flags; if (copy_to_user(ulink_desc, &klink_desc, sizeof(*ulink_desc))) |
1651333b0 [media] media: En... |
173 |
return -EFAULT; |
b83e25083 [media] media-dev... |
174 |
ulink_desc++; |
1651333b0 [media] media: En... |
175 176 |
} } |
b0a1f2a84 [media] media: im... |
177 178 179 |
return 0; } |
f3766ad7d media: media-devi... |
180 |
static long media_device_setup_link(struct media_device *mdev, void *arg) |
97548ed4c [media] media: Li... |
181 |
{ |
f3766ad7d media: media-devi... |
182 |
struct media_link_desc *linkd = arg; |
97548ed4c [media] media: Li... |
183 |
struct media_link *link = NULL; |
97548ed4c [media] media: Li... |
184 185 |
struct media_entity *source; struct media_entity *sink; |
97548ed4c [media] media: Li... |
186 187 188 |
/* Find the source and sink entities and link. */ |
bcd5081b0 [media] media: Re... |
189 190 |
source = find_entity(mdev, linkd->source.entity); sink = find_entity(mdev, linkd->sink.entity); |
97548ed4c [media] media: Li... |
191 192 193 |
if (source == NULL || sink == NULL) return -EINVAL; |
bcd5081b0 [media] media: Re... |
194 195 |
if (linkd->source.index >= source->num_pads || linkd->sink.index >= sink->num_pads) |
97548ed4c [media] media: Li... |
196 |
return -EINVAL; |
bcd5081b0 [media] media: Re... |
197 198 |
link = media_entity_find_link(&source->pads[linkd->source.index], &sink->pads[linkd->sink.index]); |
97548ed4c [media] media: Li... |
199 200 201 202 |
if (link == NULL) return -EINVAL; /* Setup the link on both entities. */ |
bcd5081b0 [media] media: Re... |
203 |
return __media_entity_setup_link(link, linkd->flags); |
97548ed4c [media] media: Li... |
204 |
} |
f3766ad7d media: media-devi... |
205 |
static long media_device_get_topology(struct media_device *mdev, void *arg) |
8309f47c3 [media] media-dev... |
206 |
{ |
f3766ad7d media: media-devi... |
207 |
struct media_v2_topology *topo = arg; |
8309f47c3 [media] media-dev... |
208 209 210 211 |
struct media_entity *entity; struct media_interface *intf; struct media_pad *pad; struct media_link *link; |
d37410c27 [media] media: Pr... |
212 213 214 215 |
struct media_v2_entity kentity, __user *uentity; struct media_v2_interface kintf, __user *uintf; struct media_v2_pad kpad, __user *upad; struct media_v2_link klink, __user *ulink; |
9033b1a47 [media] media-dev... |
216 217 |
unsigned int i; int ret = 0; |
8309f47c3 [media] media-dev... |
218 219 220 221 222 |
topo->topology_version = mdev->topology_version; /* Get entities and number of entities */ i = 0; |
b3b7a9f13 [media] media-dev... |
223 |
uentity = media_get_uptr(topo->ptr_entities); |
8309f47c3 [media] media-dev... |
224 225 |
media_device_for_each_entity(entity, mdev) { i++; |
b3b7a9f13 [media] media-dev... |
226 |
if (ret || !uentity) |
8309f47c3 [media] media-dev... |
227 228 229 230 231 232 233 234 |
continue; if (i > topo->num_entities) { ret = -ENOSPC; continue; } /* Copy fields to userspace struct if not error */ |
b3b7a9f13 [media] media-dev... |
235 236 237 238 239 |
memset(&kentity, 0, sizeof(kentity)); kentity.id = entity->graph_obj.id; kentity.function = entity->function; strncpy(kentity.name, entity->name, sizeof(kentity.name)); |
8309f47c3 [media] media-dev... |
240 |
|
b3b7a9f13 [media] media-dev... |
241 |
if (copy_to_user(uentity, &kentity, sizeof(kentity))) |
8309f47c3 [media] media-dev... |
242 |
ret = -EFAULT; |
b3b7a9f13 [media] media-dev... |
243 |
uentity++; |
8309f47c3 [media] media-dev... |
244 245 246 247 248 |
} topo->num_entities = i; /* Get interfaces and number of interfaces */ i = 0; |
b3b7a9f13 [media] media-dev... |
249 |
uintf = media_get_uptr(topo->ptr_interfaces); |
8309f47c3 [media] media-dev... |
250 251 |
media_device_for_each_intf(intf, mdev) { i++; |
b3b7a9f13 [media] media-dev... |
252 |
if (ret || !uintf) |
8309f47c3 [media] media-dev... |
253 254 255 256 257 258 |
continue; if (i > topo->num_interfaces) { ret = -ENOSPC; continue; } |
b3b7a9f13 [media] media-dev... |
259 |
memset(&kintf, 0, sizeof(kintf)); |
8309f47c3 [media] media-dev... |
260 261 |
/* Copy intf fields to userspace struct */ |
b3b7a9f13 [media] media-dev... |
262 263 264 |
kintf.id = intf->graph_obj.id; kintf.intf_type = intf->type; kintf.flags = intf->flags; |
8309f47c3 [media] media-dev... |
265 266 267 268 269 |
if (media_type(&intf->graph_obj) == MEDIA_GRAPH_INTF_DEVNODE) { struct media_intf_devnode *devnode; devnode = intf_to_devnode(intf); |
b3b7a9f13 [media] media-dev... |
270 271 |
kintf.devnode.major = devnode->major; kintf.devnode.minor = devnode->minor; |
8309f47c3 [media] media-dev... |
272 |
} |
b3b7a9f13 [media] media-dev... |
273 |
if (copy_to_user(uintf, &kintf, sizeof(kintf))) |
8309f47c3 [media] media-dev... |
274 |
ret = -EFAULT; |
b3b7a9f13 [media] media-dev... |
275 |
uintf++; |
8309f47c3 [media] media-dev... |
276 277 278 279 280 |
} topo->num_interfaces = i; /* Get pads and number of pads */ i = 0; |
b3b7a9f13 [media] media-dev... |
281 |
upad = media_get_uptr(topo->ptr_pads); |
8309f47c3 [media] media-dev... |
282 283 |
media_device_for_each_pad(pad, mdev) { i++; |
b3b7a9f13 [media] media-dev... |
284 |
if (ret || !upad) |
8309f47c3 [media] media-dev... |
285 286 287 288 289 290 |
continue; if (i > topo->num_pads) { ret = -ENOSPC; continue; } |
b3b7a9f13 [media] media-dev... |
291 |
memset(&kpad, 0, sizeof(kpad)); |
8309f47c3 [media] media-dev... |
292 293 |
/* Copy pad fields to userspace struct */ |
b3b7a9f13 [media] media-dev... |
294 295 296 |
kpad.id = pad->graph_obj.id; kpad.entity_id = pad->entity->graph_obj.id; kpad.flags = pad->flags; |
8309f47c3 [media] media-dev... |
297 |
|
b3b7a9f13 [media] media-dev... |
298 |
if (copy_to_user(upad, &kpad, sizeof(kpad))) |
8309f47c3 [media] media-dev... |
299 |
ret = -EFAULT; |
b3b7a9f13 [media] media-dev... |
300 |
upad++; |
8309f47c3 [media] media-dev... |
301 302 303 304 305 |
} topo->num_pads = i; /* Get links and number of links */ i = 0; |
b3b7a9f13 [media] media-dev... |
306 |
ulink = media_get_uptr(topo->ptr_links); |
8309f47c3 [media] media-dev... |
307 |
media_device_for_each_link(link, mdev) { |
39d1ebc60 [media] media-dev... |
308 309 |
if (link->is_backlink) continue; |
8309f47c3 [media] media-dev... |
310 |
i++; |
b3b7a9f13 [media] media-dev... |
311 |
if (ret || !ulink) |
8309f47c3 [media] media-dev... |
312 313 314 315 316 317 |
continue; if (i > topo->num_links) { ret = -ENOSPC; continue; } |
b3b7a9f13 [media] media-dev... |
318 |
memset(&klink, 0, sizeof(klink)); |
8309f47c3 [media] media-dev... |
319 320 |
/* Copy link fields to userspace struct */ |
b3b7a9f13 [media] media-dev... |
321 322 323 324 |
klink.id = link->graph_obj.id; klink.source_id = link->gobj0->id; klink.sink_id = link->gobj1->id; klink.flags = link->flags; |
8309f47c3 [media] media-dev... |
325 |
|
b3b7a9f13 [media] media-dev... |
326 |
if (copy_to_user(ulink, &klink, sizeof(klink))) |
8309f47c3 [media] media-dev... |
327 |
ret = -EFAULT; |
b3b7a9f13 [media] media-dev... |
328 |
ulink++; |
8309f47c3 [media] media-dev... |
329 330 331 332 333 |
} topo->num_links = i; return ret; } |
bcd5081b0 [media] media: Re... |
334 |
static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd) |
8309f47c3 [media] media-dev... |
335 |
{ |
bcd5081b0 [media] media: Re... |
336 337 |
/* All media IOCTLs are _IOWR() */ if (copy_from_user(karg, uarg, _IOC_SIZE(cmd))) |
a5c82e562 [media] media-dev... |
338 |
return -EFAULT; |
8309f47c3 [media] media-dev... |
339 |
|
bcd5081b0 [media] media: Re... |
340 341 |
return 0; } |
8309f47c3 [media] media-dev... |
342 |
|
bcd5081b0 [media] media: Re... |
343 344 345 346 |
static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd) { /* All media IOCTLs are _IOWR() */ if (copy_to_user(uarg, karg, _IOC_SIZE(cmd))) |
a5c82e562 [media] media-dev... |
347 |
return -EFAULT; |
8309f47c3 [media] media-dev... |
348 |
|
a5c82e562 [media] media-dev... |
349 |
return 0; |
8309f47c3 [media] media-dev... |
350 |
} |
7d4b64028 [media] media: Ad... |
351 352 353 354 355 356 |
/* Do acquire the graph mutex */ #define MEDIA_IOC_FL_GRAPH_MUTEX BIT(0) #define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user) \ [_IOC_NR(MEDIA_IOC_##__cmd)] = { \ .cmd = MEDIA_IOC_##__cmd, \ |
bcd5081b0 [media] media: Re... |
357 |
.fn = (long (*)(struct media_device *, void *))func, \ |
7d4b64028 [media] media: Ad... |
358 359 360 |
.flags = fl, \ .arg_from_user = from_user, \ .arg_to_user = to_user, \ |
6975264c4 [media] media: Un... |
361 |
} |
cf439d5f4 [media] media: De... |
362 |
|
7d4b64028 [media] media: Ad... |
363 364 |
#define MEDIA_IOC(__cmd, func, fl) \ MEDIA_IOC_ARG(__cmd, func, fl, copy_arg_from_user, copy_arg_to_user) |
bcd5081b0 [media] media: Re... |
365 |
|
cf439d5f4 [media] media: De... |
366 367 368 |
/* the table is indexed by _IOC_NR(cmd) */ struct media_ioctl_info { unsigned int cmd; |
7d4b64028 [media] media: Ad... |
369 |
unsigned short flags; |
bcd5081b0 [media] media: Re... |
370 371 372 |
long (*fn)(struct media_device *dev, void *arg); long (*arg_from_user)(void *karg, void __user *uarg, unsigned int cmd); long (*arg_to_user)(void __user *uarg, void *karg, unsigned int cmd); |
cf439d5f4 [media] media: De... |
373 374 375 |
}; static const struct media_ioctl_info ioctl_info[] = { |
7d4b64028 [media] media: Ad... |
376 377 378 379 380 |
MEDIA_IOC(DEVICE_INFO, media_device_get_info, MEDIA_IOC_FL_GRAPH_MUTEX), MEDIA_IOC(ENUM_ENTITIES, media_device_enum_entities, MEDIA_IOC_FL_GRAPH_MUTEX), MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX), MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX), MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX), |
cf439d5f4 [media] media: De... |
381 |
}; |
140d88165 [media] media: Me... |
382 |
static long media_device_ioctl(struct file *filp, unsigned int cmd, |
bcd5081b0 [media] media: Re... |
383 |
unsigned long __arg) |
140d88165 [media] media: Me... |
384 385 |
{ struct media_devnode *devnode = media_devnode_data(filp); |
a087ce704 [media] media-dev... |
386 |
struct media_device *dev = devnode->media_dev; |
6975264c4 [media] media: Un... |
387 |
const struct media_ioctl_info *info; |
bcd5081b0 [media] media: Re... |
388 389 |
void __user *arg = (void __user *)__arg; char __karg[256], *karg = __karg; |
140d88165 [media] media: Me... |
390 |
long ret; |
cf439d5f4 [media] media: De... |
391 392 393 |
if (_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_info) || ioctl_info[_IOC_NR(cmd)].cmd != cmd) return -ENOIOCTLCMD; |
6975264c4 [media] media: Un... |
394 |
info = &ioctl_info[_IOC_NR(cmd)]; |
bcd5081b0 [media] media: Re... |
395 396 397 398 399 400 401 402 403 404 405 |
if (_IOC_SIZE(info->cmd) > sizeof(__karg)) { karg = kmalloc(_IOC_SIZE(info->cmd), GFP_KERNEL); if (!karg) return -ENOMEM; } if (info->arg_from_user) { ret = info->arg_from_user(karg, arg, cmd); if (ret) goto out_free; } |
7d4b64028 [media] media: Ad... |
406 407 |
if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) mutex_lock(&dev->graph_mutex); |
bcd5081b0 [media] media: Re... |
408 |
ret = info->fn(dev, karg); |
7d4b64028 [media] media: Ad... |
409 410 411 |
if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) mutex_unlock(&dev->graph_mutex); |
140d88165 [media] media: Me... |
412 |
|
bcd5081b0 [media] media: Re... |
413 414 415 416 417 418 |
if (!ret && info->arg_to_user) ret = info->arg_to_user(arg, karg, cmd); out_free: if (karg != __karg) kfree(karg); |
140d88165 [media] media: Me... |
419 420 |
return ret; } |
b0a1f2a84 [media] media: im... |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
#ifdef CONFIG_COMPAT struct media_links_enum32 { __u32 entity; compat_uptr_t pads; /* struct media_pad_desc * */ compat_uptr_t links; /* struct media_link_desc * */ __u32 reserved[4]; }; static long media_device_enum_links32(struct media_device *mdev, struct media_links_enum32 __user *ulinks) { struct media_links_enum links; compat_uptr_t pads_ptr, links_ptr; memset(&links, 0, sizeof(links)); if (get_user(links.entity, &ulinks->entity) || get_user(pads_ptr, &ulinks->pads) || get_user(links_ptr, &ulinks->links)) return -EFAULT; links.pads = compat_ptr(pads_ptr); links.links = compat_ptr(links_ptr); |
bcd5081b0 [media] media: Re... |
445 |
return media_device_enum_links(mdev, &links); |
b0a1f2a84 [media] media: im... |
446 447 448 449 450 451 452 453 |
} #define MEDIA_IOC_ENUM_LINKS32 _IOWR('|', 0x02, struct media_links_enum32) static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); |
a087ce704 [media] media-dev... |
454 |
struct media_device *dev = devnode->media_dev; |
b0a1f2a84 [media] media: im... |
455 456 457 |
long ret; switch (cmd) { |
b0a1f2a84 [media] media: im... |
458 459 460 461 462 463 464 465 |
case MEDIA_IOC_ENUM_LINKS32: mutex_lock(&dev->graph_mutex); ret = media_device_enum_links32(dev, (struct media_links_enum32 __user *)arg); mutex_unlock(&dev->graph_mutex); break; default: |
f73696275 [media] media-dev... |
466 |
return media_device_ioctl(filp, cmd, arg); |
b0a1f2a84 [media] media: im... |
467 468 469 470 471 |
} return ret; } #endif /* CONFIG_COMPAT */ |
176fb0d10 [media] media: Me... |
472 473 |
static const struct media_file_operations media_device_fops = { .owner = THIS_MODULE, |
140d88165 [media] media: Me... |
474 475 |
.open = media_device_open, .ioctl = media_device_ioctl, |
b0a1f2a84 [media] media: im... |
476 477 478 |
#ifdef CONFIG_COMPAT .compat_ioctl = media_device_compat_ioctl, #endif /* CONFIG_COMPAT */ |
140d88165 [media] media: Me... |
479 |
.release = media_device_close, |
176fb0d10 [media] media: Me... |
480 481 482 483 484 485 486 487 488 |
}; /* ----------------------------------------------------------------------------- * sysfs */ static ssize_t show_model(struct device *cd, struct device_attribute *attr, char *buf) { |
a087ce704 [media] media-dev... |
489 490 |
struct media_devnode *devnode = to_media_devnode(cd); struct media_device *mdev = devnode->media_dev; |
176fb0d10 [media] media: Me... |
491 492 493 494 495 496 497 498 499 500 |
return sprintf(buf, "%.*s ", (int)sizeof(mdev->model), mdev->model); } static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); /* ----------------------------------------------------------------------------- * Registration/unregistration */ |
552636be7 media: devnode: R... |
501 |
static void media_device_release(struct media_devnode *devnode) |
176fb0d10 [media] media: Me... |
502 |
{ |
552636be7 media: devnode: R... |
503 504 |
dev_dbg(devnode->parent, "Media device released "); |
176fb0d10 [media] media: Me... |
505 506 507 |
} /** |
b2ed8af91 [media] media-dev... |
508 509 510 511 512 513 514 |
* media_device_register_entity - Register an entity with a media device * @mdev: The media device * @entity: The entity */ int __must_check media_device_register_entity(struct media_device *mdev, struct media_entity *entity) { |
afcbdb558 [media] media: Me... |
515 |
struct media_entity_notify *notify, *next; |
b2ed8af91 [media] media-dev... |
516 |
unsigned int i; |
ba0dd729b [media] media-ent... |
517 |
int ret; |
b2ed8af91 [media] media-dev... |
518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN || entity->function == MEDIA_ENT_F_UNKNOWN) dev_warn(mdev->dev, "Entity type for entity %s was not initialized! ", entity->name); /* Warn if we apparently re-register an entity */ WARN_ON(entity->graph_obj.mdev != NULL); entity->graph_obj.mdev = mdev; INIT_LIST_HEAD(&entity->links); entity->num_links = 0; entity->num_backlinks = 0; |
ba0dd729b [media] media-ent... |
532 533 |
if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL)) return -ENOMEM; |
e2c91d4d7 [media] media-dev... |
534 |
mutex_lock(&mdev->graph_mutex); |
665faa971 [media] media: In... |
535 |
|
ba0dd729b [media] media-ent... |
536 537 538 |
ret = ida_get_new_above(&mdev->entity_internal_idx, 1, &entity->internal_idx); if (ret < 0) { |
e2c91d4d7 [media] media-dev... |
539 |
mutex_unlock(&mdev->graph_mutex); |
ba0dd729b [media] media-ent... |
540 |
return ret; |
665faa971 [media] media: In... |
541 542 543 544 |
} mdev->entity_internal_idx_max = max(mdev->entity_internal_idx_max, entity->internal_idx); |
b2ed8af91 [media] media-dev... |
545 546 547 548 549 550 551 |
/* Initialize media_gobj embedded at the entity */ media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj); /* Initialize objects at the pads */ for (i = 0; i < entity->num_pads; i++) media_gobj_create(mdev, MEDIA_GRAPH_PAD, &entity->pads[i].graph_obj); |
afcbdb558 [media] media: Me... |
552 |
/* invoke entity_notify callbacks */ |
10e81bcdd media: Remove use... |
553 554 |
list_for_each_entry_safe(notify, next, &mdev->entity_notify, list) notify->notify(entity, notify->notify_data); |
afcbdb558 [media] media: Me... |
555 |
|
0c426c472 [media] media: Al... |
556 557 |
if (mdev->entity_internal_idx_max >= mdev->pm_count_walk.ent_enum.idx_max) { |
20b852273 [media] media: Re... |
558 |
struct media_graph new = { .top = 0 }; |
0c426c472 [media] media: Al... |
559 560 561 562 563 564 |
/* * Initialise the new graph walk before cleaning up * the old one in order not to spoil the graph walk * object of the media device if graph walk init fails. */ |
20b852273 [media] media: Re... |
565 |
ret = media_graph_walk_init(&new, mdev); |
0c426c472 [media] media: Al... |
566 567 568 569 |
if (ret) { mutex_unlock(&mdev->graph_mutex); return ret; } |
20b852273 [media] media: Re... |
570 |
media_graph_walk_cleanup(&mdev->pm_count_walk); |
0c426c472 [media] media: Al... |
571 572 573 |
mdev->pm_count_walk = new; } mutex_unlock(&mdev->graph_mutex); |
b2ed8af91 [media] media-dev... |
574 575 576 |
return 0; } EXPORT_SYMBOL_GPL(media_device_register_entity); |
821ed3662 [media] media-dev... |
577 |
static void __media_device_unregister_entity(struct media_entity *entity) |
b2ed8af91 [media] media-dev... |
578 579 580 581 582 |
{ struct media_device *mdev = entity->graph_obj.mdev; struct media_link *link, *tmp; struct media_interface *intf; unsigned int i; |
665faa971 [media] media: In... |
583 |
ida_simple_remove(&mdev->entity_internal_idx, entity->internal_idx); |
b2ed8af91 [media] media-dev... |
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
/* Remove all interface links pointing to this entity */ list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) { list_for_each_entry_safe(link, tmp, &intf->links, list) { if (link->entity == entity) __media_remove_intf_link(link); } } /* Remove all data links that belong to this entity */ __media_entity_remove_links(entity); /* Remove all pads that belong to this entity */ for (i = 0; i < entity->num_pads; i++) media_gobj_destroy(&entity->pads[i].graph_obj); /* Remove the entity */ media_gobj_destroy(&entity->graph_obj); |
afcbdb558 [media] media: Me... |
601 |
/* invoke entity_notify callbacks to handle entity removal?? */ |
b2ed8af91 [media] media-dev... |
602 603 |
entity->graph_obj.mdev = NULL; } |
821ed3662 [media] media-dev... |
604 605 606 607 608 609 610 |
void media_device_unregister_entity(struct media_entity *entity) { struct media_device *mdev = entity->graph_obj.mdev; if (mdev == NULL) return; |
e2c91d4d7 [media] media-dev... |
611 |
mutex_lock(&mdev->graph_mutex); |
821ed3662 [media] media-dev... |
612 |
__media_device_unregister_entity(entity); |
e2c91d4d7 [media] media-dev... |
613 |
mutex_unlock(&mdev->graph_mutex); |
821ed3662 [media] media-dev... |
614 |
} |
b2ed8af91 [media] media-dev... |
615 616 617 |
EXPORT_SYMBOL_GPL(media_device_unregister_entity); /** |
9832e155f [media] media-dev... |
618 |
* media_device_init() - initialize a media device |
176fb0d10 [media] media: Me... |
619 620 621 622 623 624 625 626 |
* @mdev: The media device * * The caller is responsible for initializing the media device before * registration. The following fields must be set: * * - dev must point to the parent device * - model must be filled with the device model name */ |
9832e155f [media] media-dev... |
627 |
void media_device_init(struct media_device *mdev) |
176fb0d10 [media] media: Me... |
628 |
{ |
53e269c10 [media] media: En... |
629 |
INIT_LIST_HEAD(&mdev->entities); |
57cf79b79 [media] media: ad... |
630 |
INIT_LIST_HEAD(&mdev->interfaces); |
9155d859b [media] media-dev... |
631 632 |
INIT_LIST_HEAD(&mdev->pads); INIT_LIST_HEAD(&mdev->links); |
afcbdb558 [media] media: Me... |
633 |
INIT_LIST_HEAD(&mdev->entity_notify); |
503c3d829 [media] media: En... |
634 |
mutex_init(&mdev->graph_mutex); |
665faa971 [media] media: In... |
635 |
ida_init(&mdev->entity_internal_idx); |
53e269c10 [media] media: En... |
636 |
|
9832e155f [media] media-dev... |
637 638 639 640 |
dev_dbg(mdev->dev, "Media device initialized "); } EXPORT_SYMBOL_GPL(media_device_init); |
9832e155f [media] media-dev... |
641 642 |
void media_device_cleanup(struct media_device *mdev) { |
665faa971 [media] media: In... |
643 644 |
ida_destroy(&mdev->entity_internal_idx); mdev->entity_internal_idx_max = 0; |
20b852273 [media] media: Re... |
645 |
media_graph_walk_cleanup(&mdev->pm_count_walk); |
9832e155f [media] media-dev... |
646 647 648 |
mutex_destroy(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_device_cleanup); |
9832e155f [media] media-dev... |
649 650 651 |
int __must_check __media_device_register(struct media_device *mdev, struct module *owner) { |
a087ce704 [media] media-dev... |
652 |
struct media_devnode *devnode; |
9832e155f [media] media-dev... |
653 |
int ret; |
a087ce704 [media] media-dev... |
654 655 656 |
devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); if (!devnode) return -ENOMEM; |
176fb0d10 [media] media: Me... |
657 |
/* Register the device node. */ |
a087ce704 [media] media-dev... |
658 659 660 661 |
mdev->devnode = devnode; devnode->fops = &media_device_fops; devnode->parent = mdev->dev; devnode->release = media_device_release; |
8a9507966 [media] media-dev... |
662 663 664 |
/* Set version 0 to indicate user-space that the graph is static */ mdev->topology_version = 0; |
a087ce704 [media] media-dev... |
665 666 |
ret = media_devnode_register(mdev, devnode, owner); if (ret < 0) { |
5b28dde51 [media] media: fi... |
667 |
/* devnode free is handled in media_devnode_*() */ |
a087ce704 [media] media-dev... |
668 |
mdev->devnode = NULL; |
176fb0d10 [media] media: Me... |
669 |
return ret; |
a087ce704 [media] media-dev... |
670 |
} |
176fb0d10 [media] media: Me... |
671 |
|
a087ce704 [media] media-dev... |
672 |
ret = device_create_file(&devnode->dev, &dev_attr_model); |
176fb0d10 [media] media: Me... |
673 |
if (ret < 0) { |
5b28dde51 [media] media: fi... |
674 |
/* devnode free is handled in media_devnode_*() */ |
a087ce704 [media] media-dev... |
675 |
mdev->devnode = NULL; |
6f0dd24a0 [media] media: fi... |
676 |
media_devnode_unregister_prepare(devnode); |
a087ce704 [media] media-dev... |
677 |
media_devnode_unregister(devnode); |
176fb0d10 [media] media: Me... |
678 679 |
return ret; } |
8f5a3188f [media] media: ad... |
680 681 |
dev_dbg(mdev->dev, "Media device registered "); |
176fb0d10 [media] media: Me... |
682 683 |
return 0; } |
85de721c4 [media] media: Us... |
684 |
EXPORT_SYMBOL_GPL(__media_device_register); |
176fb0d10 [media] media: Me... |
685 |
|
afcbdb558 [media] media: Me... |
686 687 688 |
int __must_check media_device_register_entity_notify(struct media_device *mdev, struct media_entity_notify *nptr) { |
e2c91d4d7 [media] media-dev... |
689 |
mutex_lock(&mdev->graph_mutex); |
afcbdb558 [media] media: Me... |
690 |
list_add_tail(&nptr->list, &mdev->entity_notify); |
e2c91d4d7 [media] media-dev... |
691 |
mutex_unlock(&mdev->graph_mutex); |
afcbdb558 [media] media: Me... |
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 |
return 0; } EXPORT_SYMBOL_GPL(media_device_register_entity_notify); /* * Note: Should be called with mdev->lock held. */ static void __media_device_unregister_entity_notify(struct media_device *mdev, struct media_entity_notify *nptr) { list_del(&nptr->list); } void media_device_unregister_entity_notify(struct media_device *mdev, struct media_entity_notify *nptr) { |
e2c91d4d7 [media] media-dev... |
708 |
mutex_lock(&mdev->graph_mutex); |
afcbdb558 [media] media: Me... |
709 |
__media_device_unregister_entity_notify(mdev, nptr); |
e2c91d4d7 [media] media-dev... |
710 |
mutex_unlock(&mdev->graph_mutex); |
afcbdb558 [media] media: Me... |
711 712 |
} EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify); |
176fb0d10 [media] media: Me... |
713 714 |
void media_device_unregister(struct media_device *mdev) { |
53e269c10 [media] media: En... |
715 716 |
struct media_entity *entity; struct media_entity *next; |
d47109fa4 [media] media-dev... |
717 |
struct media_interface *intf, *tmp_intf; |
afcbdb558 [media] media: Me... |
718 |
struct media_entity_notify *notify, *nextp; |
d47109fa4 [media] media-dev... |
719 |
|
821ed3662 [media] media-dev... |
720 721 |
if (mdev == NULL) return; |
e2c91d4d7 [media] media-dev... |
722 |
mutex_lock(&mdev->graph_mutex); |
821ed3662 [media] media-dev... |
723 |
|
223d19c56 [media] media-dev... |
724 |
/* Check if mdev was ever registered at all */ |
a087ce704 [media] media-dev... |
725 |
if (!media_devnode_is_registered(mdev->devnode)) { |
e2c91d4d7 [media] media-dev... |
726 |
mutex_unlock(&mdev->graph_mutex); |
223d19c56 [media] media-dev... |
727 |
return; |
821ed3662 [media] media-dev... |
728 |
} |
223d19c56 [media] media-dev... |
729 |
|
6f0dd24a0 [media] media: fi... |
730 731 |
/* Clear the devnode register bit to avoid races with media dev open */ media_devnode_unregister_prepare(mdev->devnode); |
f50d51661 [media] dvbdev: r... |
732 733 |
/* Remove all entities from the media device */ list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) |
821ed3662 [media] media-dev... |
734 |
__media_device_unregister_entity(entity); |
f50d51661 [media] dvbdev: r... |
735 |
|
afcbdb558 [media] media: Me... |
736 737 738 |
/* Remove all entity_notify callbacks from the media device */ list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list) __media_device_unregister_entity_notify(mdev, notify); |
d47109fa4 [media] media-dev... |
739 |
/* Remove all interfaces from the media device */ |
d47109fa4 [media] media-dev... |
740 741 |
list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces, graph_obj.list) { |
e7cd17a29 [media] drivers/m... |
742 743 744 745 746 |
/* * Unlink the interface, but don't free it here; the * module which created it is responsible for freeing * it */ |
d47109fa4 [media] media-dev... |
747 |
__media_remove_intf_links(intf); |
c350ef830 [media] media_ent... |
748 |
media_gobj_destroy(&intf->graph_obj); |
d47109fa4 [media] media-dev... |
749 |
} |
821ed3662 [media] media-dev... |
750 |
|
e2c91d4d7 [media] media-dev... |
751 |
mutex_unlock(&mdev->graph_mutex); |
53e269c10 [media] media: En... |
752 |
|
a087ce704 [media] media-dev... |
753 754 |
dev_dbg(mdev->dev, "Media device unregistered "); |
6f0dd24a0 [media] media: fi... |
755 756 757 758 |
device_remove_file(&mdev->devnode->dev, &dev_attr_model); media_devnode_unregister(mdev->devnode); /* devnode free is handled in media_devnode_*() */ mdev->devnode = NULL; |
176fb0d10 [media] media: Me... |
759 760 |
} EXPORT_SYMBOL_GPL(media_device_unregister); |
53e269c10 [media] media: En... |
761 |
|
b34ecd5aa [media] media-dev... |
762 |
#if IS_ENABLED(CONFIG_PCI) |
6cf5dad17 [media] media_dev... |
763 764 765 |
void media_device_pci_init(struct media_device *mdev, struct pci_dev *pci_dev, const char *name) |
41b44e35b [media] media-dev... |
766 |
{ |
41b44e35b [media] media-dev... |
767 768 769 770 771 772 773 774 775 776 777 |
mdev->dev = &pci_dev->dev; if (name) strlcpy(mdev->model, name, sizeof(mdev->model)); else strlcpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model)); sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev)); mdev->hw_revision = (pci_dev->subsystem_vendor << 16) | pci_dev->subsystem_device; |
41b44e35b [media] media-dev... |
778 |
media_device_init(mdev); |
41b44e35b [media] media-dev... |
779 780 |
} EXPORT_SYMBOL_GPL(media_device_pci_init); |
b34ecd5aa [media] media-dev... |
781 |
#endif |
41b44e35b [media] media-dev... |
782 |
|
b34ecd5aa [media] media-dev... |
783 |
#if IS_ENABLED(CONFIG_USB) |
6cf5dad17 [media] media_dev... |
784 785 786 787 |
void __media_device_usb_init(struct media_device *mdev, struct usb_device *udev, const char *board_name, const char *driver_name) |
41b44e35b [media] media-dev... |
788 |
{ |
41b44e35b [media] media-dev... |
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 |
mdev->dev = &udev->dev; if (driver_name) strlcpy(mdev->driver_name, driver_name, sizeof(mdev->driver_name)); if (board_name) strlcpy(mdev->model, board_name, sizeof(mdev->model)); else if (udev->product) strlcpy(mdev->model, udev->product, sizeof(mdev->model)); else strlcpy(mdev->model, "unknown model", sizeof(mdev->model)); if (udev->serial) strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial)); usb_make_path(udev, mdev->bus_info, sizeof(mdev->bus_info)); mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); |
41b44e35b [media] media-dev... |
805 806 |
media_device_init(mdev); |
41b44e35b [media] media-dev... |
807 808 |
} EXPORT_SYMBOL_GPL(__media_device_usb_init); |
b34ecd5aa [media] media-dev... |
809 |
#endif |
41b44e35b [media] media-dev... |
810 |
|
e576d60bb [media] media: de... |
811 |
#endif /* CONFIG_MEDIA_CONTROLLER */ |