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: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
803c1787ef
commit
a04d04231b
|
@ -327,6 +327,12 @@ nouveau-y += core/engine/vp/nv98.o
|
||||||
nouveau-y += core/engine/vp/nvc0.o
|
nouveau-y += core/engine/vp/nvc0.o
|
||||||
nouveau-y += core/engine/vp/nve0.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
|
# drm/core
|
||||||
nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
|
nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
|
||||||
nouveau-y += nouveau_vga.o nouveau_agp.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");
|
hprintk(handle, TRACE, "created\n");
|
||||||
|
|
||||||
*phandle = handle;
|
*phandle = handle;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef __NOUVEAU_CLASS_H__
|
#ifndef __NOUVEAU_CLASS_H__
|
||||||
#define __NOUVEAU_CLASS_H__
|
#define __NOUVEAU_CLASS_H__
|
||||||
|
|
||||||
|
#include <nvif/class.h>
|
||||||
|
|
||||||
/* Device class
|
/* Device class
|
||||||
*
|
*
|
||||||
* 0080: NV_DEVICE
|
* 0080: NV_DEVICE
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
#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
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
#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
|
|
@ -0,0 +1,20 @@
|
||||||
|
#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
|
|
@ -0,0 +1,353 @@
|
||||||
|
/*
|
||||||
|
* 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
|
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* 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(¬ify->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, ¬ify->flags)) {
|
||||||
|
int ret = nvif_notify_put_(notify);
|
||||||
|
if (test_bit(NVIF_NOTIFY_WORK, ¬ify->flags))
|
||||||
|
flush_work(¬ify->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(¬ify->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, ¬ify->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, ¬ify->flags)) {
|
||||||
|
atomic_inc(¬ify->putcnt);
|
||||||
|
memcpy((void *)notify->data, data, size);
|
||||||
|
schedule_work(¬ify->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(¬ify->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, ¬ify->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, ¬ify->object);
|
||||||
|
notify->flags = 0;
|
||||||
|
atomic_set(¬ify->putcnt, 1);
|
||||||
|
notify->dtor = dtor;
|
||||||
|
notify->func = func;
|
||||||
|
notify->data = NULL;
|
||||||
|
notify->size = reply;
|
||||||
|
if (work) {
|
||||||
|
INIT_WORK(¬ify->work, nvif_notify_work);
|
||||||
|
set_bit(NVIF_NOTIFY_WORK, ¬ify->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;
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
#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
|
|
@ -0,0 +1,302 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
#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
|
|
@ -0,0 +1 @@
|
||||||
|
../core/os.h
|
Loading…
Reference in New Issue