2 * Copyright 2014 Red Hat Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
22 * Authors: Ben Skeggs <bskeggs@redhat.com>
25 #include "nouveau_drv.h"
26 #include "nouveau_usif.h"
27 #include "nouveau_abi16.h"
29 #include <nvif/notify.h>
30 #include <nvif/unpack.h>
31 #include <nvif/client.h>
32 #include <nvif/event.h>
33 #include <nvif/ioctl.h>
35 struct usif_notify_p
{
36 struct drm_pending_event base
;
38 struct drm_event base
;
44 struct list_head head
;
50 struct usif_notify_p
*p
;
53 static inline struct usif_notify
*
54 usif_notify_find(struct drm_file
*filp
, u32 handle
)
56 struct nouveau_cli
*cli
= nouveau_cli(filp
);
57 struct usif_notify
*ntfy
;
58 list_for_each_entry(ntfy
, &cli
->notifys
, head
) {
59 if (ntfy
->handle
== handle
)
66 usif_notify_dtor(struct usif_notify
*ntfy
)
68 list_del(&ntfy
->head
);
73 usif_notify(const void *header
, u32 length
, const void *data
, u32 size
)
75 struct usif_notify
*ntfy
= NULL
;
77 struct nvif_notify_rep_v0 v0
;
79 struct drm_device
*dev
;
80 struct drm_file
*filp
;
83 if (length
== sizeof(rep
->v0
) && rep
->v0
.version
== 0) {
84 if (WARN_ON(!(ntfy
= (void *)(unsigned long)rep
->v0
.token
)))
85 return NVIF_NOTIFY_DROP
;
86 BUG_ON(rep
->v0
.route
!= NVDRM_NOTIFY_USIF
);
89 return NVIF_NOTIFY_DROP
;
91 if (WARN_ON(!ntfy
->p
|| ntfy
->reply
!= (length
+ size
)))
92 return NVIF_NOTIFY_DROP
;
93 filp
= ntfy
->p
->base
.file_priv
;
94 dev
= filp
->minor
->dev
;
96 memcpy(&ntfy
->p
->e
.data
[0], header
, length
);
97 memcpy(&ntfy
->p
->e
.data
[length
], data
, size
);
98 switch (rep
->v0
.version
) {
100 struct nvif_notify_rep_v0
*rep
= (void *)ntfy
->p
->e
.data
;
101 rep
->route
= ntfy
->route
;
102 rep
->token
= ntfy
->token
;
110 spin_lock_irqsave(&dev
->event_lock
, flags
);
111 if (!WARN_ON(filp
->event_space
< ntfy
->p
->e
.base
.length
)) {
112 list_add_tail(&ntfy
->p
->base
.link
, &filp
->event_list
);
113 filp
->event_space
-= ntfy
->p
->e
.base
.length
;
115 wake_up_interruptible(&filp
->event_wait
);
116 spin_unlock_irqrestore(&dev
->event_lock
, flags
);
117 atomic_set(&ntfy
->enabled
, 0);
118 return NVIF_NOTIFY_DROP
;
122 usif_notify_new(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
124 struct nouveau_cli
*cli
= nouveau_cli(f
);
125 struct nvif_client
*client
= &cli
->base
;
127 struct nvif_ioctl_ntfy_new_v0 v0
;
130 struct nvif_notify_req_v0 v0
;
132 struct usif_notify
*ntfy
;
135 if (!(ret
= nvif_unpack(ret
, &data
, &size
, args
->v0
, 0, 0, true))) {
136 if (usif_notify_find(f
, args
->v0
.index
))
143 if (!(ntfy
= kmalloc(sizeof(*ntfy
), GFP_KERNEL
)))
145 atomic_set(&ntfy
->enabled
, 0);
147 if (!(ret
= nvif_unpack(ret
, &data
, &size
, req
->v0
, 0, 0, true))) {
148 ntfy
->reply
= sizeof(struct nvif_notify_rep_v0
) + req
->v0
.reply
;
149 ntfy
->route
= req
->v0
.route
;
150 ntfy
->token
= req
->v0
.token
;
151 req
->v0
.route
= NVDRM_NOTIFY_USIF
;
152 req
->v0
.token
= (unsigned long)(void *)ntfy
;
153 ret
= nvif_client_ioctl(client
, argv
, argc
);
154 req
->v0
.token
= ntfy
->token
;
155 req
->v0
.route
= ntfy
->route
;
156 ntfy
->handle
= args
->v0
.index
;
160 list_add(&ntfy
->head
, &cli
->notifys
);
167 usif_notify_del(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
169 struct nouveau_cli
*cli
= nouveau_cli(f
);
170 struct nvif_client
*client
= &cli
->base
;
172 struct nvif_ioctl_ntfy_del_v0 v0
;
174 struct usif_notify
*ntfy
;
177 if (!(ret
= nvif_unpack(ret
, &data
, &size
, args
->v0
, 0, 0, true))) {
178 if (!(ntfy
= usif_notify_find(f
, args
->v0
.index
)))
183 ret
= nvif_client_ioctl(client
, argv
, argc
);
185 usif_notify_dtor(ntfy
);
190 usif_notify_get(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
192 struct nouveau_cli
*cli
= nouveau_cli(f
);
193 struct nvif_client
*client
= &cli
->base
;
195 struct nvif_ioctl_ntfy_del_v0 v0
;
197 struct usif_notify
*ntfy
;
200 if (!(ret
= nvif_unpack(ret
, &data
, &size
, args
->v0
, 0, 0, true))) {
201 if (!(ntfy
= usif_notify_find(f
, args
->v0
.index
)))
206 if (atomic_xchg(&ntfy
->enabled
, 1))
209 ntfy
->p
= kmalloc(sizeof(*ntfy
->p
) + ntfy
->reply
, GFP_KERNEL
);
210 if (ret
= -ENOMEM
, !ntfy
->p
)
212 ntfy
->p
->base
.event
= &ntfy
->p
->e
.base
;
213 ntfy
->p
->base
.file_priv
= f
;
214 ntfy
->p
->e
.base
.type
= DRM_NOUVEAU_EVENT_NVIF
;
215 ntfy
->p
->e
.base
.length
= sizeof(ntfy
->p
->e
.base
) + ntfy
->reply
;
217 ret
= nvif_client_ioctl(client
, argv
, argc
);
220 atomic_set(&ntfy
->enabled
, 0);
227 usif_notify_put(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
229 struct nouveau_cli
*cli
= nouveau_cli(f
);
230 struct nvif_client
*client
= &cli
->base
;
232 struct nvif_ioctl_ntfy_put_v0 v0
;
234 struct usif_notify
*ntfy
;
237 if (!(ret
= nvif_unpack(ret
, &data
, &size
, args
->v0
, 0, 0, true))) {
238 if (!(ntfy
= usif_notify_find(f
, args
->v0
.index
)))
243 ret
= nvif_client_ioctl(client
, argv
, argc
);
244 if (ret
== 0 && atomic_xchg(&ntfy
->enabled
, 0))
250 struct list_head head
;
251 struct list_head ntfy
;
257 usif_object_dtor(struct usif_object
*object
)
259 list_del(&object
->head
);
264 usif_object_new(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
266 struct nouveau_cli
*cli
= nouveau_cli(f
);
267 struct nvif_client
*client
= &cli
->base
;
269 struct nvif_ioctl_new_v0 v0
;
271 struct usif_object
*object
;
274 if (!(object
= kmalloc(sizeof(*object
), GFP_KERNEL
)))
276 list_add(&object
->head
, &cli
->objects
);
278 if (!(ret
= nvif_unpack(ret
, &data
, &size
, args
->v0
, 0, 0, true))) {
279 object
->route
= args
->v0
.route
;
280 object
->token
= args
->v0
.token
;
281 args
->v0
.route
= NVDRM_OBJECT_USIF
;
282 args
->v0
.token
= (unsigned long)(void *)object
;
283 ret
= nvif_client_ioctl(client
, argv
, argc
);
284 args
->v0
.token
= object
->token
;
285 args
->v0
.route
= object
->route
;
289 usif_object_dtor(object
);
294 usif_ioctl(struct drm_file
*filp
, void __user
*user
, u32 argc
)
296 struct nouveau_cli
*cli
= nouveau_cli(filp
);
297 struct nvif_client
*client
= &cli
->base
;
298 void *data
= kmalloc(argc
, GFP_KERNEL
);
301 struct nvif_ioctl_v0 v0
;
303 struct usif_object
*object
;
307 if (ret
= -ENOMEM
, !argv
)
309 if (ret
= -EFAULT
, copy_from_user(argv
, user
, size
))
312 if (!(ret
= nvif_unpack(-ENOSYS
, &data
, &size
, argv
->v0
, 0, 0, true))) {
313 /* block access to objects not created via this interface */
314 owner
= argv
->v0
.owner
;
315 if (argv
->v0
.object
== 0ULL &&
316 argv
->v0
.type
!= NVIF_IOCTL_V0_DEL
)
317 argv
->v0
.owner
= NVDRM_OBJECT_ANY
; /* except client */
319 argv
->v0
.owner
= NVDRM_OBJECT_USIF
;
323 /* USIF slightly abuses some return-only ioctl members in order
324 * to provide interoperability with the older ABI16 objects
326 mutex_lock(&cli
->mutex
);
327 if (argv
->v0
.route
) {
328 if (ret
= -EINVAL
, argv
->v0
.route
== 0xff)
329 ret
= nouveau_abi16_usif(filp
, argv
, argc
);
331 mutex_unlock(&cli
->mutex
);
336 switch (argv
->v0
.type
) {
337 case NVIF_IOCTL_V0_NEW
:
338 ret
= usif_object_new(filp
, data
, size
, argv
, argc
);
340 case NVIF_IOCTL_V0_NTFY_NEW
:
341 ret
= usif_notify_new(filp
, data
, size
, argv
, argc
);
343 case NVIF_IOCTL_V0_NTFY_DEL
:
344 ret
= usif_notify_del(filp
, data
, size
, argv
, argc
);
346 case NVIF_IOCTL_V0_NTFY_GET
:
347 ret
= usif_notify_get(filp
, data
, size
, argv
, argc
);
349 case NVIF_IOCTL_V0_NTFY_PUT
:
350 ret
= usif_notify_put(filp
, data
, size
, argv
, argc
);
353 ret
= nvif_client_ioctl(client
, argv
, argc
);
356 if (argv
->v0
.route
== NVDRM_OBJECT_USIF
) {
357 object
= (void *)(unsigned long)argv
->v0
.token
;
358 argv
->v0
.route
= object
->route
;
359 argv
->v0
.token
= object
->token
;
360 if (ret
== 0 && argv
->v0
.type
== NVIF_IOCTL_V0_DEL
) {
361 list_del(&object
->head
);
365 argv
->v0
.route
= NVIF_IOCTL_V0_ROUTE_HIDDEN
;
368 argv
->v0
.owner
= owner
;
369 mutex_unlock(&cli
->mutex
);
371 if (copy_to_user(user
, argv
, argc
))
379 usif_client_fini(struct nouveau_cli
*cli
)
381 struct usif_object
*object
, *otemp
;
382 struct usif_notify
*notify
, *ntemp
;
384 list_for_each_entry_safe(notify
, ntemp
, &cli
->notifys
, head
) {
385 usif_notify_dtor(notify
);
388 list_for_each_entry_safe(object
, otemp
, &cli
->objects
, head
) {
389 usif_object_dtor(object
);
394 usif_client_init(struct nouveau_cli
*cli
)
396 INIT_LIST_HEAD(&cli
->objects
);
397 INIT_LIST_HEAD(&cli
->notifys
);