Commit a04d0423 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/nvif: import library functions for the ioctl/event interfaces

This is a wrapper around the interfaces defined in an earlier commit,
and is also used by various userspace (either by a libdrm backend, or
libpciaccess) tools/tests.

In the future this will be extended to handle channels, replacing some
long-unloved code we currently use, and allow fifo/display/mpeg (hi
Ilia ;)) engines to all be exposed in the same way.
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 803c1787
......@@ -327,6 +327,12 @@ nouveau-y += core/engine/vp/nv98.o
nouveau-y += core/engine/vp/nvc0.o
nouveau-y += core/engine/vp/nve0.o
# nvif
nouveau-y += nvif/object.o
nouveau-y += nvif/client.o
nouveau-y += nvif/device.o
nouveau-y += nvif/notify.o
# drm/core
nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
nouveau-y += nouveau_vga.o nouveau_agp.o
......
......@@ -146,9 +146,7 @@ nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,
}
hprintk(handle, TRACE, "created\n");
*phandle = handle;
return 0;
}
......
#ifndef __NOUVEAU_CLASS_H__
#define __NOUVEAU_CLASS_H__
#include <nvif/class.h>
/* Device class
*
* 0080: NV_DEVICE
......
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "client.h"
#include "driver.h"
#include "ioctl.h"
int
nvif_client_ioctl(struct nvif_client *client, void *data, u32 size)
{
return client->driver->ioctl(client->base.priv, client->super, data, size, NULL);
}
int
nvif_client_suspend(struct nvif_client *client)
{
return client->driver->suspend(client->base.priv);
}
int
nvif_client_resume(struct nvif_client *client)
{
return client->driver->resume(client->base.priv);
}
void
nvif_client_fini(struct nvif_client *client)
{
if (client->driver) {
client->driver->fini(client->base.priv);
client->driver = NULL;
client->base.parent = NULL;
nvif_object_fini(&client->base);
}
}
const struct nvif_driver *
nvif_drivers[] = {
#ifdef __KERNEL__
#if 0
&nvif_driver_nvkm,
#endif
#else
&nvif_driver_lib,
#endif
NULL
};
int
nvif_client_init(void (*dtor)(struct nvif_client *), const char *driver,
const char *name, u64 device, const char *cfg, const char *dbg,
struct nvif_client *client)
{
int ret, i;
ret = nvif_object_init(NULL, (void*)dtor, 0, 0, NULL, 0, &client->base);
if (ret)
return ret;
client->base.parent = &client->base;
client->base.handle = ~0;
client->object = &client->base;
client->super = true;
for (i = 0, ret = -EINVAL; (client->driver = nvif_drivers[i]); i++) {
if (!driver || !strcmp(client->driver->name, driver)) {
ret = client->driver->init(name, device, cfg, dbg,
&client->base.priv);
if (!ret || driver)
break;
}
}
if (ret)
nvif_client_fini(client);
return ret;
}
static void
nvif_client_del(struct nvif_client *client)
{
nvif_client_fini(client);
kfree(client);
}
int
nvif_client_new(const char *driver, const char *name, u64 device,
const char *cfg, const char *dbg,
struct nvif_client **pclient)
{
struct nvif_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
if (client) {
int ret = nvif_client_init(nvif_client_del, driver, name,
device, cfg, dbg, client);
if (ret) {
kfree(client);
client = NULL;
}
*pclient = client;
return ret;
}
return -ENOMEM;
}
void
nvif_client_ref(struct nvif_client *client, struct nvif_client **pclient)
{
nvif_object_ref(&client->base, (struct nvif_object **)pclient);
}
#ifndef __NVIF_CLIENT_H__
#define __NVIF_CLIENT_H__
#include "object.h"
struct nvif_client {
struct nvif_object base;
struct nvif_object *object; /*XXX: hack for nvif_object() */
const struct nvif_driver *driver;
bool super;
};
static inline struct nvif_client *
nvif_client(struct nvif_object *object)
{
while (object && object->parent != object)
object = object->parent;
return (void *)object;
}
int nvif_client_init(void (*dtor)(struct nvif_client *), const char *,
const char *, u64, const char *, const char *,
struct nvif_client *);
void nvif_client_fini(struct nvif_client *);
int nvif_client_new(const char *, const char *, u64, const char *,
const char *, struct nvif_client **);
void nvif_client_ref(struct nvif_client *, struct nvif_client **);
int nvif_client_ioctl(struct nvif_client *, void *, u32);
int nvif_client_suspend(struct nvif_client *);
int nvif_client_resume(struct nvif_client *);
/*XXX*/
#include <core/client.h>
#define nvkm_client(a) ({ \
struct nvif_client *_client = nvif_client(nvif_object(a)); \
nouveau_client(_client->base.priv); \
})
#endif
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "device.h"
void
nvif_device_fini(struct nvif_device *device)
{
nvif_object_fini(&device->base);
}
int
nvif_device_init(struct nvif_object *parent, void (*dtor)(struct nvif_device *),
u32 handle, u32 oclass, void *data, u32 size,
struct nvif_device *device)
{
int ret = nvif_object_init(parent, (void *)dtor, handle, oclass,
data, size, &device->base);
if (ret == 0) {
device->object = &device->base;
device->info.version = 0;
ret = nvif_object_mthd(&device->base, NV_DEVICE_V0_INFO,
&device->info, sizeof(device->info));
}
return ret;
}
static void
nvif_device_del(struct nvif_device *device)
{
nvif_device_fini(device);
kfree(device);
}
int
nvif_device_new(struct nvif_object *parent, u32 handle, u32 oclass,
void *data, u32 size, struct nvif_device **pdevice)
{
struct nvif_device *device = kzalloc(sizeof(*device), GFP_KERNEL);
if (device) {
int ret = nvif_device_init(parent, nvif_device_del, handle,
oclass, data, size, device);
if (ret) {
kfree(device);
device = NULL;
}
*pdevice = device;
return ret;
}
return -ENOMEM;
}
void
nvif_device_ref(struct nvif_device *device, struct nvif_device **pdevice)
{
nvif_object_ref(&device->base, (struct nvif_object **)pdevice);
}
#ifndef __NVIF_DEVICE_H__
#define __NVIF_DEVICE_H__
#include "object.h"
#include "class.h"
struct nvif_device {
struct nvif_object base;
struct nvif_object *object; /*XXX: hack for nvif_object() */
struct nv_device_info_v0 info;
};
static inline struct nvif_device *
nvif_device(struct nvif_object *object)
{
while (object && object->oclass != 0x0080 /*XXX: NV_DEVICE_CLASS*/ )
object = object->parent;
return (void *)object;
}
int nvif_device_init(struct nvif_object *, void (*dtor)(struct nvif_device *),
u32 handle, u32 oclass, void *, u32,
struct nvif_device *);
void nvif_device_fini(struct nvif_device *);
int nvif_device_new(struct nvif_object *, u32 handle, u32 oclass,
void *, u32, struct nvif_device **);
void nvif_device_ref(struct nvif_device *, struct nvif_device **);
/*XXX*/
#include <subdev/bios.h>
#include <subdev/fb.h>
#include <subdev/instmem.h>
#include <subdev/vm.h>
#include <subdev/bar.h>
#include <subdev/gpio.h>
#include <subdev/clock.h>
#include <subdev/i2c.h>
#include <subdev/timer.h>
#include <subdev/therm.h>
#define nvkm_device(a) nv_device(nvkm_object((a)))
#define nvkm_bios(a) nouveau_bios(nvkm_device(a))
#define nvkm_fb(a) nouveau_fb(nvkm_device(a))
#define nvkm_instmem(a) nouveau_instmem(nvkm_device(a))
#define nvkm_vmmgr(a) nouveau_vmmgr(nvkm_device(a))
#define nvkm_bar(a) nouveau_bar(nvkm_device(a))
#define nvkm_gpio(a) nouveau_gpio(nvkm_device(a))
#define nvkm_clock(a) nouveau_clock(nvkm_device(a))
#define nvkm_i2c(a) nouveau_i2c(nvkm_device(a))
#define nvkm_timer(a) nouveau_timer(nvkm_device(a))
#define nvkm_wait(a,b,c,d) nv_wait(nvkm_timer(a), (b), (c), (d))
#define nvkm_wait_cb(a,b,c) nv_wait_cb(nvkm_timer(a), (b), (c))
#define nvkm_therm(a) nouveau_therm(nvkm_device(a))
#include <engine/device.h>
#include <engine/fifo.h>
#include <engine/disp.h>
#include <engine/graph.h>
#include <engine/software.h>
#define nvkm_fifo(a) nouveau_fifo(nvkm_device(a))
#define nvkm_fifo_chan(a) ((struct nouveau_fifo_chan *)nvkm_object(a))
#define nvkm_disp(a) nouveau_disp(nvkm_device(a))
#define nvkm_gr(a) ((struct nouveau_graph *)nouveau_engine(nvkm_object(a), NVDEV_ENGINE_GR))
#endif
#ifndef __NVIF_DRIVER_H__
#define __NVIF_DRIVER_H__
struct nvif_driver {
const char *name;
int (*init)(const char *name, u64 device, const char *cfg,
const char *dbg, void **priv);
void (*fini)(void *priv);
int (*suspend)(void *priv);
int (*resume)(void *priv);
int (*ioctl)(void *priv, bool super, void *data, u32 size, void **hack);
void *(*map)(void *priv, u64 handle, u32 size);
void (*unmap)(void *priv, void *ptr, u32 size);
bool keep;
};
extern const struct nvif_driver nvif_driver_nvkm;
extern const struct nvif_driver nvif_driver_lib;
#endif
/*
* Copyright © 2010 Intel Corporation
* Copyright © 2010 Francisco Jerez <currojerez@riseup.net>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
/* Modified by Ben Skeggs <bskeggs@redhat.com> to match kernel list APIs */
#ifndef _XORG_LIST_H_
#define _XORG_LIST_H_
/**
* @file Classic doubly-link circular list implementation.
* For real usage examples of the linked list, see the file test/list.c
*
* Example:
* We need to keep a list of struct foo in the parent struct bar, i.e. what
* we want is something like this.
*
* struct bar {
* ...
* struct foo *list_of_foos; -----> struct foo {}, struct foo {}, struct foo{}
* ...
* }
*
* We need one list head in bar and a list element in all list_of_foos (both are of
* data type 'struct list_head').
*
* struct bar {
* ...
* struct list_head list_of_foos;
* ...
* }
*
* struct foo {
* ...
* struct list_head entry;
* ...
* }
*
* Now we initialize the list head:
*
* struct bar bar;
* ...
* INIT_LIST_HEAD(&bar.list_of_foos);
*
* Then we create the first element and add it to this list:
*
* struct foo *foo = malloc(...);
* ....
* list_add(&foo->entry, &bar.list_of_foos);
*
* Repeat the above for each element you want to add to the list. Deleting
* works with the element itself.
* list_del(&foo->entry);
* free(foo);
*
* Note: calling list_del(&bar.list_of_foos) will set bar.list_of_foos to an empty
* list again.
*
* Looping through the list requires a 'struct foo' as iterator and the
* name of the field the subnodes use.
*
* struct foo *iterator;
* list_for_each_entry(iterator, &bar.list_of_foos, entry) {
* if (iterator->something == ...)
* ...
* }
*
* Note: You must not call list_del() on the iterator if you continue the
* loop. You need to run the safe for-each loop instead:
*
* struct foo *iterator, *next;
* list_for_each_entry_safe(iterator, next, &bar.list_of_foos, entry) {
* if (...)
* list_del(&iterator->entry);
* }
*
*/
/**
* The linkage struct for list nodes. This struct must be part of your
* to-be-linked struct. struct list_head is required for both the head of the
* list and for each list node.
*
* Position and name of the struct list_head field is irrelevant.
* There are no requirements that elements of a list are of the same type.
* There are no requirements for a list head, any struct list_head can be a list
* head.
*/
struct list_head {
struct list_head *next, *prev;
};
/**
* Initialize the list as an empty list.
*
* Example:
* INIT_LIST_HEAD(&bar->list_of_foos);
*
* @param The list to initialized.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void
INIT_LIST_HEAD(struct list_head *list)
{
list->next = list->prev = list;
}
static inline void
__list_add(struct list_head *entry,
struct list_head *prev, struct list_head *next)
{
next->prev = entry;
entry->next = next;
entry->prev = prev;
prev->next = entry;
}
/**
* Insert a new element after the given list head. The new element does not
* need to be initialised as empty list.
* The list changes from:
* head → some element → ...
* to
* head → new element → older element → ...
*
* Example:
* struct foo *newfoo = malloc(...);
* list_add(&newfoo->entry, &bar->list_of_foos);
*
* @param entry The new element to prepend to the list.
* @param head The existing list.
*/
static inline void
list_add(struct list_head *entry, struct list_head *head)
{
__list_add(entry, head, head->next);
}
/**
* Append a new element to the end of the list given with this list head.
*
* The list changes from:
* head → some element → ... → lastelement
* to
* head → some element → ... → lastelement → new element
*
* Example:
* struct foo *newfoo = malloc(...);
* list_add_tail(&newfoo->entry, &bar->list_of_foos);
*
* @param entry The new element to prepend to the list.
* @param head The existing list.
*/
static inline void
list_add_tail(struct list_head *entry, struct list_head *head)
{
__list_add(entry, head->prev, head);
}
static inline void
__list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
/**
* Remove the element from the list it is in. Using this function will reset
* the pointers to/from this element so it is removed from the list. It does
* NOT free the element itself or manipulate it otherwise.
*
* Using list_del on a pure list head (like in the example at the top of
* this file) will NOT remove the first element from
* the list but rather reset the list as empty list.
*
* Example:
* list_del(&foo->entry);
*
* @param entry The element to remove.
*/
static inline void
list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void
list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
/**
* Check if the list is empty.
*
* Example:
* list_empty(&bar->list_of_foos);
*
* @return True if the list contains one or more elements or False otherwise.
*/
static inline bool
list_empty(struct list_head *head)
{
return head->next == head;
}
/**
* Returns a pointer to the container of this list element.
*
* Example:
* struct foo* f;
* f = container_of(&foo->entry, struct foo, entry);
* assert(f == foo);
*
* @param ptr Pointer to the struct list_head.
* @param type Data type of the list element.
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the data struct containing the list head.
*/
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (char *) &((type *)0)->member)
#endif
/**
* Alias of container_of
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* Retrieve the first list entry for the given list pointer.
*
* Example:
* struct foo *first;
* first = list_first_entry(&bar->list_of_foos, struct foo, list_of_foos);
*
* @param ptr The list head
* @param type Data type of the list element to retrieve
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the first list element.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* Retrieve the last list entry for the given listpointer.
*
* Example:
* struct foo *first;
* first = list_last_entry(&bar->list_of_foos, struct foo, list_of_foos);
*
* @param ptr The list head
* @param type Data type of the list element to retrieve
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the last list element.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
#define __container_of(ptr, sample, member) \
(void *)container_of((ptr), typeof(*(sample)), member)
/**
* Loop through the list given by head and set pos to struct in the list.
*
* Example:
* struct foo *iterator;
* list_for_each_entry(iterator, &bar->list_of_foos, entry) {
* [modify iterator]
* }
*
* This macro is not safe for node deletion. Use list_for_each_entry_safe
* instead.
*
* @param pos Iterator variable of the type of the list elements.
* @param head List head
* @param member Member name of the struct list_head in the list elements.
*
*/
#define list_for_each_entry(pos, head, member) \
for (pos = __container_of((head)->next, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
/**
* Loop through the list, keeping a backup pointer to the element. This
* macro allows for the deletion of a list element while looping through the
* list.
*
* See list_for_each_entry for more details.
*/
#define list_for_each_entry_safe(pos, tmp, head, member) \
for (pos = __container_of((head)->next, pos, member), \
tmp = __container_of(pos->member.next, pos, member); \
&pos->member != (head); \
pos = tmp, tmp = __container_of(pos->member.next, tmp, member))
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = __container_of((head)->prev, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.prev, pos, member))
#define list_for_each_entry_continue(pos, head, member) \
for (pos = __container_of(pos->member.next, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = __container_of(pos->member.prev, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.prev, pos, member))
#define list_for_each_entry_from(pos, head, member) \
for (; \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
#endif
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include <nvif/client.h>
#include <nvif/driver.h>
#include <nvif/notify.h>
#include <nvif/object.h>
#include <nvif/ioctl.h>
#include <nvif/event.h>
static inline int
nvif_notify_put_(struct nvif_notify *notify)
{
struct nvif_object *object = notify->object;
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_ntfy_put_v0 ntfy;
} args = {
.ioctl.type = NVIF_IOCTL_V0_NTFY_PUT,
.ntfy.index = notify->index,
};
if (atomic_inc_return(&notify->putcnt) != 1)
return 0;
return nvif_object_ioctl(object, &args, sizeof(args), NULL);
}
int
nvif_notify_put(struct nvif_notify *notify)
{
if (likely(notify->object) &&
test_and_clear_bit(NVIF_NOTIFY_USER, &notify->flags)) {
int ret = nvif_notify_put_(notify);
if (test_bit(NVIF_NOTIFY_WORK, &notify->flags))
flush_work(&notify->work);
return ret;
}
return 0;
}
static inline int
nvif_notify_get_(struct nvif_notify *notify)
{
struct nvif_object *object = notify->object;
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_ntfy_get_v0 ntfy;
} args = {
.ioctl.type = NVIF_IOCTL_V0_NTFY_GET,
.ntfy.index = notify->index,
};
if (atomic_dec_return(&notify->putcnt) != 0)
return 0;
return nvif_object_ioctl(object, &args, sizeof(args), NULL);
}
int
nvif_notify_get(struct nvif_notify *notify)
{
if (likely(notify->object) &&
!test_and_set_bit(NVIF_NOTIFY_USER, &notify->flags))
return nvif_notify_get_(notify);
return 0;
}
static void
nvif_notify_work(struct work_struct *work)
{
struct nvif_notify *notify = container_of(work, typeof(*notify), work);
if (notify->func(notify) == NVIF_NOTIFY_KEEP)
nvif_notify_get_(notify);
}
int
nvif_notify(const void *header, u32 length, const void *data, u32 size)
{
struct nvif_notify *notify = NULL;
const union {
struct nvif_notify_rep_v0 v0;
} *args = header;
int ret = NVIF_NOTIFY_DROP;
if (length == sizeof(args->v0) && args->v0.version == 0) {
if (WARN_ON(args->v0.route))
return NVIF_NOTIFY_DROP;
notify = (void *)(unsigned long)args->v0.token;
}
if (!WARN_ON(notify == NULL)) {
struct nvif_client *client = nvif_client(notify->object);
if (!WARN_ON(notify->size != size)) {
if (test_bit(NVIF_NOTIFY_WORK, &notify->flags)) {
atomic_inc(&notify->putcnt);
memcpy((void *)notify->data, data, size);
schedule_work(&notify->work);
return NVIF_NOTIFY_DROP;
}
notify->data = data;
ret = notify->func(notify);
notify->data = NULL;
if (ret != NVIF_NOTIFY_DROP && client->driver->keep) {
atomic_inc(&notify->putcnt);
nvif_notify_get_(notify);
}
}
}
return ret;
}
int
nvif_notify_fini(struct nvif_notify *notify)
{
struct nvif_object *object = notify->object;
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_ntfy_del_v0 ntfy;
} args = {
.ioctl.type = NVIF_IOCTL_V0_NTFY_DEL,
.ntfy.index = notify->index,
};
int ret = nvif_notify_put(notify);
if (ret >= 0 && object) {
ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
if (ret == 0) {
nvif_object_ref(NULL, &notify->object);
kfree((void *)notify->data);
}
}
return ret;
}
int
nvif_notify_init(struct nvif_object *object, void (*dtor)(struct nvif_notify *),
int (*func)(struct nvif_notify *), bool work, u8 event,
void *data, u32 size, u32 reply, struct nvif_notify *notify)
{
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_ntfy_new_v0 ntfy;
struct nvif_notify_req_v0 req;
} *args;
int ret = -ENOMEM;
notify->object = NULL;
nvif_object_ref(object, &notify->object);
notify->flags = 0;
atomic_set(&notify->putcnt, 1);
notify->dtor = dtor;
notify->func = func;
notify->data = NULL;
notify->size = reply;
if (work) {
INIT_WORK(&notify->work, nvif_notify_work);
set_bit(NVIF_NOTIFY_WORK, &notify->flags);
notify->data = kmalloc(notify->size, GFP_KERNEL);
if (!notify->data)
goto done;
}
if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
goto done;
args->ioctl.version = 0;
args->ioctl.type = NVIF_IOCTL_V0_NTFY_NEW;
args->ntfy.version = 0;
args->ntfy.event = event;
args->req.version = 0;
args->req.reply = notify->size;
args->req.route = 0;
args->req.token = (unsigned long)(void *)notify;
memcpy(args->req.data, data, size);
ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
notify->index = args->ntfy.index;
kfree(args);
done:
if (ret)
nvif_notify_fini(notify);
return ret;
}
static void
nvif_notify_del(struct nvif_notify *notify)
{
nvif_notify_fini(notify);
kfree(notify);
}
void
nvif_notify_ref(struct nvif_notify *notify, struct nvif_notify **pnotify)
{
BUG_ON(notify != NULL);
if (*pnotify)
(*pnotify)->dtor(*pnotify);
*pnotify = notify;
}
int
nvif_notify_new(struct nvif_object *object, int (*func)(struct nvif_notify *),
bool work, u8 type, void *data, u32 size, u32 reply,
struct nvif_notify **pnotify)
{
struct nvif_notify *notify = kzalloc(sizeof(*notify), GFP_KERNEL);
if (notify) {
int ret = nvif_notify_init(object, nvif_notify_del, func, work,
type, data, size, reply, notify);
if (ret)
kfree(notify);
*pnotify = notify;
return ret;
}
return -ENOMEM;
}
#ifndef __NVIF_NOTIFY_H__
#define __NVIF_NOTIFY_H__
struct nvif_notify {
struct nvif_object *object;
int index;
#define NVIF_NOTIFY_USER 0
#define NVIF_NOTIFY_WORK 1
unsigned long flags;
atomic_t putcnt;
void (*dtor)(struct nvif_notify *);
#define NVIF_NOTIFY_DROP 0
#define NVIF_NOTIFY_KEEP 1
int (*func)(struct nvif_notify *);
/* this is const for a *very* good reason - the data might be on the
* stack from an irq handler. if you're not nvif/notify.c then you
* should probably think twice before casting it away...
*/
const void *data;
u32 size;
struct work_struct work;
};
int nvif_notify_init(struct nvif_object *, void (*dtor)(struct nvif_notify *),
int (*func)(struct nvif_notify *), bool work, u8 type,
void *data, u32 size, u32 reply, struct nvif_notify *);
int nvif_notify_fini(struct nvif_notify *);
int nvif_notify_get(struct nvif_notify *);
int nvif_notify_put(struct nvif_notify *);
int nvif_notify(const void *, u32, const void *, u32);
int nvif_notify_new(struct nvif_object *, int (*func)(struct nvif_notify *),
bool work, u8 type, void *data, u32 size, u32 reply,
struct nvif_notify **);
void nvif_notify_ref(struct nvif_notify *, struct nvif_notify **);
#endif
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "object.h"
#include "client.h"
#include "driver.h"
#include "ioctl.h"
int
nvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack)
{
struct nvif_client *client = nvif_client(object);
union {
struct nvif_ioctl_v0 v0;
} *args = data;
if (size >= sizeof(*args) && args->v0.version == 0) {
args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY;
args->v0.path_nr = 0;
while (args->v0.path_nr < ARRAY_SIZE(args->v0.path)) {
args->v0.path[args->v0.path_nr++] = object->handle;
if (object->parent == object)
break;
object = object->parent;
}
} else
return -ENOSYS;
return client->driver->ioctl(client->base.priv, client->super, data, size, hack);
}
int
nvif_object_sclass(struct nvif_object *object, u32 *oclass, int count)
{
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_sclass_v0 sclass;
} *args;
u32 size = count * sizeof(args->sclass.oclass[0]);
int ret;
if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
return -ENOMEM;
args->ioctl.version = 0;
args->ioctl.type = NVIF_IOCTL_V0_SCLASS;
args->sclass.version = 0;
args->sclass.count = count;
memcpy(args->sclass.oclass, oclass, size);
ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
ret = ret ? ret : args->sclass.count;
memcpy(oclass, args->sclass.oclass, size);
kfree(args);
return ret;
}
u32
nvif_object_rd(struct nvif_object *object, int size, u64 addr)
{
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_rd_v0 rd;
} args = {
.ioctl.type = NVIF_IOCTL_V0_RD,
.rd.size = size,
.rd.addr = addr,
};
int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
if (ret) {
/*XXX: warn? */
return 0;
}
return args.rd.data;
}
void
nvif_object_wr(struct nvif_object *object, int size, u64 addr, u32 data)
{
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_wr_v0 wr;
} args = {
.ioctl.type = NVIF_IOCTL_V0_WR,
.wr.size = size,
.wr.addr = addr,
.wr.data = data,
};
int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
if (ret) {
/*XXX: warn? */
}
}
int
nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size)
{
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_mthd_v0 mthd;
} *args;
u8 stack[128];
int ret;
if (sizeof(*args) + size > sizeof(stack)) {
if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
return -ENOMEM;
} else {
args = (void *)stack;
}
args->ioctl.version = 0;
args->ioctl.type = NVIF_IOCTL_V0_MTHD;
args->mthd.version = 0;
args->mthd.method = mthd;
memcpy(args->mthd.data, data, size);
ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
memcpy(data, args->mthd.data, size);
if (args != (void *)stack)
kfree(args);
return ret;
}
void
nvif_object_unmap(struct nvif_object *object)
{
if (object->map.size) {
struct nvif_client *client = nvif_client(object);
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_unmap unmap;
} args = {
.ioctl.type = NVIF_IOCTL_V0_UNMAP,
};
if (object->map.ptr) {
client->driver->unmap(client, object->map.ptr,
object->map.size);
object->map.ptr = NULL;
}
nvif_object_ioctl(object, &args, sizeof(args), NULL);
object->map.size = 0;
}
}
int
nvif_object_map(struct nvif_object *object)
{
struct nvif_client *client = nvif_client(object);
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_map_v0 map;
} args = {
.ioctl.type = NVIF_IOCTL_V0_MAP,
};
int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
if (ret == 0) {
object->map.size = args.map.length;
object->map.ptr = client->driver->map(client, args.map.handle,
object->map.size);
if (ret = -ENOMEM, object->map.ptr)
return 0;
nvif_object_unmap(object);
}
return ret;
}
struct ctor {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_new_v0 new;
};
void
nvif_object_fini(struct nvif_object *object)
{
struct ctor *ctor = container_of(object->data, typeof(*ctor), new.data);
if (object->parent) {
struct {
struct nvif_ioctl_v0 ioctl;
struct nvif_ioctl_del del;
} args = {
.ioctl.type = NVIF_IOCTL_V0_DEL,
};
nvif_object_unmap(object);
nvif_object_ioctl(object, &args, sizeof(args), NULL);
if (object->data) {
object->size = 0;
object->data = NULL;
kfree(ctor);
}
nvif_object_ref(NULL, &object->parent);
}
}
int
nvif_object_init(struct nvif_object *parent, void (*dtor)(struct nvif_object *),
u32 handle, u32 oclass, void *data, u32 size,
struct nvif_object *object)
{
struct ctor *ctor;
int ret = 0;
object->parent = NULL;
object->object = object;
nvif_object_ref(parent, &object->parent);
kref_init(&object->refcount);
object->handle = handle;
object->oclass = oclass;
object->data = NULL;
object->size = 0;
object->dtor = dtor;
object->map.ptr = NULL;
object->map.size = 0;
if (object->parent) {
if (!(ctor = kmalloc(sizeof(*ctor) + size, GFP_KERNEL))) {
nvif_object_fini(object);
return -ENOMEM;
}
object->data = ctor->new.data;
object->size = size;
memcpy(object->data, data, size);
ctor->ioctl.version = 0;
ctor->ioctl.type = NVIF_IOCTL_V0_NEW;
ctor->new.version = 0;
ctor->new.route = NVIF_IOCTL_V0_ROUTE_NVIF;
ctor->new.token = (unsigned long)(void *)object;
ctor->new.handle = handle;
ctor->new.oclass = oclass;
ret = nvif_object_ioctl(parent, ctor, sizeof(*ctor) +
object->size, &object->priv);
}
if (ret)
nvif_object_fini(object);
return ret;
}
static void
nvif_object_del(struct nvif_object *object)
{
nvif_object_fini(object);
kfree(object);
}
int
nvif_object_new(struct nvif_object *parent, u32 handle, u32 oclass,
void *data, u32 size, struct nvif_object **pobject)
{
struct nvif_object *object = kzalloc(sizeof(*object), GFP_KERNEL);
if (object) {
int ret = nvif_object_init(parent, nvif_object_del, handle,
oclass, data, size, object);
if (ret)
kfree(object);
*pobject = object;
return ret;
}
return -ENOMEM;
}
static void
nvif_object_put(struct kref *kref)
{
struct nvif_object *object =
container_of(kref, typeof(*object), refcount);
object->dtor(object);
}
void
nvif_object_ref(struct nvif_object *object, struct nvif_object **pobject)
{
if (object)
kref_get(&object->refcount);
if (*pobject)
kref_put(&(*pobject)->refcount, nvif_object_put);
*pobject = object;
}
#ifndef __NVIF_OBJECT_H__
#define __NVIF_OBJECT_H__
#include <nvif/os.h>
struct nvif_object {
struct nvif_object *parent;
struct nvif_object *object; /*XXX: hack for nvif_object() */
struct kref refcount;
u32 handle;
u32 oclass;
void *data;
u32 size;
void *priv; /*XXX: hack */
void (*dtor)(struct nvif_object *);
struct {
void *ptr;
u32 size;
} map;
};
int nvif_object_init(struct nvif_object *, void (*dtor)(struct nvif_object *),
u32 handle, u32 oclass, void *, u32,
struct nvif_object *);
void nvif_object_fini(struct nvif_object *);
int nvif_object_new(struct nvif_object *, u32 handle, u32 oclass,
void *, u32, struct nvif_object **);
void nvif_object_ref(struct nvif_object *, struct nvif_object **);
int nvif_object_ioctl(struct nvif_object *, void *, u32, void **);
int nvif_object_sclass(struct nvif_object *, u32 *, int);
u32 nvif_object_rd(struct nvif_object *, int, u64);
void nvif_object_wr(struct nvif_object *, int, u64, u32);
int nvif_object_mthd(struct nvif_object *, u32, void *, u32);
int nvif_object_map(struct nvif_object *);
void nvif_object_unmap(struct nvif_object *);
#define nvif_object(a) (a)->object
#define ioread8_native ioread8
#define iowrite8_native iowrite8
#define nvif_rd(a,b,c) ({ \
struct nvif_object *_object = nvif_object(a); \
u32 _data; \
if (likely(_object->map.ptr)) \
_data = ioread##b##_native((u8 *)_object->map.ptr + (c)); \
else \
_data = nvif_object_rd(_object, (b) / 8, (c)); \
_data; \
})
#define nvif_wr(a,b,c,d) ({ \
struct nvif_object *_object = nvif_object(a); \
if (likely(_object->map.ptr)) \
iowrite##b##_native((d), (u8 *)_object->map.ptr + (c)); \
else \
nvif_object_wr(_object, (b) / 8, (c), (d)); \
})
#define nvif_rd08(a,b) ({ u8 _v = nvif_rd((a), 8, (b)); _v; })
#define nvif_rd16(a,b) ({ u16 _v = nvif_rd((a), 16, (b)); _v; })
#define nvif_rd32(a,b) ({ u32 _v = nvif_rd((a), 32, (b)); _v; })
#define nvif_wr08(a,b,c) nvif_wr((a), 8, (b), (u8)(c))
#define nvif_wr16(a,b,c) nvif_wr((a), 16, (b), (u16)(c))
#define nvif_wr32(a,b,c) nvif_wr((a), 32, (b), (u32)(c))
#define nvif_mask(a,b,c,d) ({ \
u32 _v = nvif_rd32(nvif_object(a), (b)); \
nvif_wr32(nvif_object(a), (b), (_v & ~(c)) | (d)); \
_v; \
})
#define nvif_mthd(a,b,c,d) nvif_object_mthd(nvif_object(a), (b), (c), (d))
/*XXX*/
#include <core/object.h>
#define nvkm_object(a) ((struct nouveau_object *)nvif_object(a)->priv)
#define nvif_exec(a,b,c,d) nv_exec(nvkm_object(a), (b), (c), (d))
#endif
../core/os.h
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment