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_drm.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 (nvif_unpack(args
->v0
, 0, 0, true)) {
136 if (usif_notify_find(f
, args
->v0
.index
))
142 if (!(ntfy
= kmalloc(sizeof(*ntfy
), GFP_KERNEL
)))
144 atomic_set(&ntfy
->enabled
, 0);
146 if (nvif_unpack(req
->v0
, 0, 0, true)) {
147 ntfy
->reply
= sizeof(struct nvif_notify_rep_v0
) + req
->v0
.reply
;
148 ntfy
->route
= req
->v0
.route
;
149 ntfy
->token
= req
->v0
.token
;
150 req
->v0
.route
= NVDRM_NOTIFY_USIF
;
151 req
->v0
.token
= (unsigned long)(void *)ntfy
;
152 ret
= nvif_client_ioctl(client
, argv
, argc
);
153 req
->v0
.token
= ntfy
->token
;
154 req
->v0
.route
= ntfy
->route
;
155 ntfy
->handle
= args
->v0
.index
;
159 list_add(&ntfy
->head
, &cli
->notifys
);
166 usif_notify_del(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
168 struct nouveau_cli
*cli
= nouveau_cli(f
);
169 struct nvif_client
*client
= &cli
->base
;
171 struct nvif_ioctl_ntfy_del_v0 v0
;
173 struct usif_notify
*ntfy
;
176 if (nvif_unpack(args
->v0
, 0, 0, true)) {
177 if (!(ntfy
= usif_notify_find(f
, args
->v0
.index
)))
182 ret
= nvif_client_ioctl(client
, argv
, argc
);
184 usif_notify_dtor(ntfy
);
189 usif_notify_get(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
191 struct nouveau_cli
*cli
= nouveau_cli(f
);
192 struct nvif_client
*client
= &cli
->base
;
194 struct nvif_ioctl_ntfy_del_v0 v0
;
196 struct usif_notify
*ntfy
;
199 if (nvif_unpack(args
->v0
, 0, 0, true)) {
200 if (!(ntfy
= usif_notify_find(f
, args
->v0
.index
)))
205 if (atomic_xchg(&ntfy
->enabled
, 1))
208 ntfy
->p
= kmalloc(sizeof(*ntfy
->p
) + ntfy
->reply
, GFP_KERNEL
);
209 if (ret
= -ENOMEM
, !ntfy
->p
)
211 ntfy
->p
->base
.event
= &ntfy
->p
->e
.base
;
212 ntfy
->p
->base
.file_priv
= f
;
213 ntfy
->p
->base
.pid
= current
->pid
;
214 ntfy
->p
->base
.destroy
=(void(*)(struct drm_pending_event
*))kfree
;
215 ntfy
->p
->e
.base
.type
= DRM_NOUVEAU_EVENT_NVIF
;
216 ntfy
->p
->e
.base
.length
= sizeof(ntfy
->p
->e
.base
) + ntfy
->reply
;
218 ret
= nvif_client_ioctl(client
, argv
, argc
);
221 atomic_set(&ntfy
->enabled
, 0);
228 usif_notify_put(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
230 struct nouveau_cli
*cli
= nouveau_cli(f
);
231 struct nvif_client
*client
= &cli
->base
;
233 struct nvif_ioctl_ntfy_put_v0 v0
;
235 struct usif_notify
*ntfy
;
238 if (nvif_unpack(args
->v0
, 0, 0, true)) {
239 if (!(ntfy
= usif_notify_find(f
, args
->v0
.index
)))
244 ret
= nvif_client_ioctl(client
, argv
, argc
);
245 if (ret
== 0 && atomic_xchg(&ntfy
->enabled
, 0))
251 struct list_head head
;
252 struct list_head ntfy
;
258 usif_object_dtor(struct usif_object
*object
)
260 list_del(&object
->head
);
265 usif_object_new(struct drm_file
*f
, void *data
, u32 size
, void *argv
, u32 argc
)
267 struct nouveau_cli
*cli
= nouveau_cli(f
);
268 struct nvif_client
*client
= &cli
->base
;
270 struct nvif_ioctl_new_v0 v0
;
272 struct usif_object
*object
;
275 if (!(object
= kmalloc(sizeof(*object
), GFP_KERNEL
)))
277 list_add(&object
->head
, &cli
->objects
);
279 if (nvif_unpack(args
->v0
, 0, 0, true)) {
280 object
->route
= args
->v0
.route
;
281 object
->token
= args
->v0
.token
;
282 args
->v0
.route
= NVDRM_OBJECT_USIF
;
283 args
->v0
.token
= (unsigned long)(void *)object
;
284 ret
= nvif_client_ioctl(client
, argv
, argc
);
285 args
->v0
.token
= object
->token
;
286 args
->v0
.route
= object
->route
;
290 usif_object_dtor(object
);
295 usif_ioctl(struct drm_file
*filp
, void __user
*user
, u32 argc
)
297 struct nouveau_cli
*cli
= nouveau_cli(filp
);
298 struct nvif_client
*client
= &cli
->base
;
299 void *data
= kmalloc(argc
, GFP_KERNEL
);
302 struct nvif_ioctl_v0 v0
;
304 struct usif_object
*object
;
308 if (ret
= -ENOMEM
, !argv
)
310 if (ret
= -EFAULT
, copy_from_user(argv
, user
, size
))
313 if (nvif_unpack(argv
->v0
, 0, 0, true)) {
314 /* block access to objects not created via this interface */
315 owner
= argv
->v0
.owner
;
316 if (argv
->v0
.object
== 0ULL)
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
);