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