diff --git a/configure.ac b/configure.ac index cd37db5..3b0f1f0 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,9 @@ AC_CONFIG_AUX_DIR([bin]) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_SYSTEM +dnl Required LIBDRM versions (same as Mesa's DRI drivers) +LIBDRM_NOUVEAU_REQUIRED=2.4.66 + dnl Add an --enable-debug option AX_CHECK_ENABLE_DEBUG(no, DEBUG) @@ -23,11 +26,18 @@ AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_LIBTOOL AC_CHECK_PROGS([DOXYGEN], [doxygen]) +AC_CHECK_PROGS([PERL], [perl]) + +if test -z "$DOXYGEN"; then + AC_MSG_WARN([Doxygen not found - documentation will not be built]) +fi +AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) if test "x$ac_cv_prog_cc_c99" = xno; then AC_MSG_ERROR([Building liballocator requires a C99-enabled compiler]) fi +dnl Available built-in checks AX_SEARCH_LIBS_OPT([floor], [m], [MATH_LIBS], [], [ AC_MSG_ERROR([unable to find the floor() function]) ]) @@ -40,11 +50,7 @@ AC_CHECK_FUNC([strdup], [], [ AC_MSG_ERROR([The function strdup() is required and was not found.]) ]) -if test -z "$DOXYGEN"; then - AC_MSG_WARN([Doxygen not found - documentation will not be built]) -fi -AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) - +dnl Required libraries checks PKG_CHECK_MODULES(LIBDRM, [libdrm], [have_libdrm="yes"], [have_libdrm="no"]) if test "x$have_libdrm" != xyes; then AC_MSG_WARN([libdrm is a hard build-time dependency for some allocator @@ -52,12 +58,56 @@ if test "x$have_libdrm" != xyes; then fi AM_CONDITIONAL([HAVE_LIBDRM], [test "x$have_libdrm" = xyes]) +dnl What drivers to build +AC_ARG_WITH([drivers], + [AS_HELP_STRING([--with-drivers@<:@=DIRS...@:>@], + [comma delimited allocator drivers list, e.g. + "nouveau" @<:@default=auto@:>@])], + [with_drivers="$withval"], + [with_drivers=auto]) + +if test "x$with_drivers" = xauto; then + if test "x$have_libdrm" = xyes; then + with_drivers="nouveau" + else + with_drivers="" + fi +fi + +if test -n "$with_drivers"; then + drivers=`IFS=', '; echo $with_drivers` + for driver in $drivers; do + case "x$driver" in + xnouveau) + PKG_CHECK_MODULES(LIBDRM_NOUVEAU, [libdrm >= $LIBDRM_NOUVEAU_REQUIRED libdrm_nouveau >= $LIBDRM_NOUVEAU_REQUIRED], + [have_libdrm_nouveau="yes"], [have_libdrm_nouveau="no"]) + if test "x$have_libdrm_nouveau" != xyes; then + AC_MSG_ERROR([$driver requires libdrm and libdrm_nouveau >= $LIBDRM_NOUVEAU_REQUIRED]) + fi + ;; + *) + AC_MSG_ERROR([Allocator driver '$driver' does not exist]) + ;; + esac + done + + dnl Perl is needed to generate drivers json files + if test -z "$PERL"; then + AC_MSG_ERROR([Perl is required to generate drivers json files]) + fi +fi +AM_CONDITIONAL([ENABLE_NOUVEAU], [test "x$have_libdrm_nouveau" = xyes]) + + AC_CONFIG_FILES([Makefile src/Makefile + src/drivers/Makefile include/Makefile tests/Makefile liballocator.pc]) AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([Doxyfile])]) +AM_COND_IF([ENABLE_NOUVEAU], + [AC_CONFIG_FILES([src/drivers/nouveau/Makefile])]) AC_OUTPUT diff --git a/include/allocator/allocator.h b/include/allocator/allocator.h index 4cdc083..213285d 100644 --- a/include/allocator/allocator.h +++ b/include/allocator/allocator.h @@ -125,36 +125,6 @@ extern int device_export_allocation(device_t *dev, void **metadata, int *fd); -/*! - * Free an array of capability sets created by the allocator library - */ -extern void free_capability_sets(uint32_t num_capability_sets, - capability_set_t *capability_sets); - -/*! - * Serialize a capability set to a stream of raw bytes. - * - * The caller is responsible for freeing the memory pointed to by - * : - * - * free(data); - */ -extern int serialize_capability_set(const capability_set_t *capability_set, - size_t *data_size, - void **data); - -/*! - * Allocate a capability set and populate it from a raw stream of bytes. - * - * The caller is responsible for freeing the memory pointed to by - * : - * - * free_capability_sets(1, *capability_set); - */ -extern int deserialize_capability_set(size_t data_size, - const void *data, - capability_set_t **capability_set); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/include/allocator/helpers.h b/include/allocator/helpers.h new file mode 100644 index 0000000..0015ab1 --- /dev/null +++ b/include/allocator/helpers.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017 NVIDIA CORPORATION. All rights reserved. + * + * 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 + * 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. + */ + +#ifndef __ALLOCATOR_HELPERS_H__ +#define __ALLOCATOR_HELPERS_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file Inline helper functions to deal with capability sets. + */ + +/*! + * Free an array of capability sets created by the allocator library + */ +static inline void +free_capability_sets(uint32_t num_capability_sets, + capability_set_t *capability_sets) +{ + uint32_t i, j; + + if (capability_sets) { + for (i = 0; i < num_capability_sets; i++) { + if (capability_sets[i].capabilities) { + for (j = 0; j < capability_sets[i].num_capabilities; j++) { + free((void *)capability_sets[i].capabilities[j]); + } + + free((void *)capability_sets[i].capabilities); + } + + free((void *)capability_sets[i].constraints); + } + + free(capability_sets); + } +} + +/*! + * Returns an exact copy of the given capability set + */ +static inline capability_set_t * +dup_capability_set(const capability_set_t *set) +{ + constraint_t *constraints; + capability_header_t **capabilities; + size_t constraints_size; + size_t cap_size; + uint32_t i; + + capability_set_t *dup_set = (capability_set_t *)calloc(1, sizeof(*set)); + if (!dup_set) { + return NULL; + } + + constraints_size = set->num_constraints * sizeof(*set->constraints); + dup_set->constraints = constraints = + (constraint_t *)malloc(constraints_size); + if (!dup_set->constraints) { + free_capability_sets(1, dup_set); + return NULL; + } + + dup_set->num_constraints = set->num_constraints; + memcpy(constraints, set->constraints, constraints_size); + + capabilities = (capability_header_t **) + calloc(set->num_capabilities, sizeof(*dup_set->capabilities)); + dup_set->capabilities = (const capability_header_t *const *)capabilities; + if (!dup_set->capabilities) { + free_capability_sets(1, dup_set); + return NULL; + } + + dup_set->num_capabilities = set->num_capabilities; + for (i = 0; i < set->num_capabilities; i++) { + cap_size = sizeof(*capabilities[i]) + + sizeof(uint32_t) * set->capabilities[i]->common.length_in_words; + + capabilities[i] = (capability_header_t *)malloc(cap_size); + if (!capabilities[i]) { + free_capability_sets(1, dup_set); + return NULL; + } + + memcpy(capabilities[i], set->capabilities[i], cap_size); + } + + return dup_set; +} + +/*! + * Serialize a capability set to a stream of raw bytes. + * + * The caller is responsible for freeing the memory pointed to by + * : + * + * free(data); + */ +static inline int +serialize_capability_set(const capability_set_t *set, + size_t *data_size, + void **data) +{ + size_t size = 0; + unsigned char *d = NULL; + uint32_t i; + + size += sizeof(set->num_constraints); + size += sizeof(set->num_capabilities); + + /* constraints are fixed-size objects */ + size += set->num_constraints * sizeof(set->constraints[0]); + + for (i = 0; i < set->num_capabilities; i++) { + /* length_in_words does not include the size of the capability header */ + size += sizeof(*set->capabilities[i]); + + /* Add in the size of the post-header capability content. */ + size += set->capabilities[i]->common.length_in_words * sizeof(uint32_t); + } + + d = malloc(size); + + if (!d) { + return -1; + } + + *data = d; + *data_size = size; + +#define SERIALIZE(src, len) \ + assert(((d + (len)) - (unsigned char *)*data) <= size); \ + memcpy(d, (src), (len)); \ + d += (len) + + SERIALIZE(&set->num_constraints, sizeof(set->num_constraints)); + SERIALIZE(&set->num_capabilities, sizeof(set->num_capabilities)); + for (i = 0; i < set->num_constraints; i++) { + SERIALIZE(&set->constraints[i], sizeof(set->constraints[i])); + } + + for (i = 0; i < set->num_capabilities; i++) { + size_t cap_size = sizeof(*set->capabilities[i]) + + set->capabilities[i]->common.length_in_words * sizeof(uint32_t); + + SERIALIZE(set->capabilities[i], cap_size); + } + +#undef SERIALIZE + + return 0; +} + +/*! + * Allocate a capability set and populate it from a raw stream of bytes. + * + * The caller is responsible for freeing the memory pointed to by + * : + * + * free_capability_sets(1, *capability_set); + */ +static inline int +deserialize_capability_set(size_t data_size, + const void *data, + capability_set_t **capability_set) +{ + const unsigned char *d = data; + constraint_t *constraints = NULL; + capability_header_t **capabilities = NULL; + uint32_t i; + + capability_set_t *set = calloc(1, sizeof(capability_set_t)); + + if (!set) { + goto fail; + } + +#define PEEK_DESERIALIZE(dst, size) \ + if (((d + size) - (const unsigned char *)data) > data_size) goto fail; \ + memcpy((dst), d, (size)) + +#define DESERIALIZE(dst, size) \ + PEEK_DESERIALIZE((dst), (size)); \ + d += (size) + + DESERIALIZE(&set->num_constraints, sizeof(set->num_constraints)); + DESERIALIZE(&set->num_capabilities, sizeof(set->num_capabilities)); + + set->constraints = constraints = + calloc(set->num_constraints, sizeof(*set->constraints)); + capabilities = calloc(set->num_capabilities, sizeof(*capabilities)); + set->capabilities = (const capability_header_t *const *)capabilities; + + if (!constraints || !capabilities) { + goto fail; + } + + for (i = 0; i < set->num_constraints; i++) { + DESERIALIZE(&constraints[i], sizeof(set->constraints[i])); + } + + for (i = 0; i < set->num_capabilities; i++) { + capability_header_t header; + + PEEK_DESERIALIZE(&header, sizeof(header)); + + capabilities[i] = calloc(1, sizeof(header) + + header.common.length_in_words * + sizeof(uint32_t)); + + if (!capabilities[i]) { + goto fail; + } + + DESERIALIZE(capabilities[i], sizeof(*capabilities[i]) + + header.common.length_in_words * sizeof(uint32_t)); + } + +#undef DESERIALIZE +#undef PEEK_DESERIALIZE + + set->constraints = constraints; + set->capabilities = (const capability_header_t *const *)capabilities; + *capability_set = set; + + return 0; + +fail: + free_capability_sets(1, set); + + return -1; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __ALLOCATOR_HELPERS_H__ */ diff --git a/include/allocator/utils.h b/include/allocator/utils.h new file mode 100644 index 0000000..ff6952d --- /dev/null +++ b/include/allocator/utils.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017 NVIDIA CORPORATION. All rights reserved. + * + * 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 + * 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. + */ + +#ifndef __ALLOCATOR_UTILS_H__ +#define __ALLOCATOR_UTILS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file Inline miscellaneous utility macros and functions. + */ + +#define UTIL_MAX2(X, Y) ((X) > (Y) ? (X) : (Y)) + +static inline const usage_t * +util_find_use(uint32_t num_uses, + const usage_t *uses, + device_t *dev, + uint16_t name) +{ + uint32_t i; + + for (i = 0; i < num_uses; i++) { + if (uses[i].usage->name == name && + (dev == DEVICE_NONE || uses[i].dev == dev)) { + return &uses[i]; + } + } + + return NULL; +} + +static inline const constraint_t * +util_find_constraint(const capability_set_t *set, uint16_t name) +{ + uint32_t i; + + for (i = 0; i < set->num_constraints; i++) { + if (set->constraints[i].name == name) { + return &set->constraints[i]; + } + } + + return NULL; +} + +static inline const capability_header_t * +util_find_cap(const capability_set_t *set, uint16_t name) +{ + uint32_t i; + + for (i = 0; i < set->num_capabilities; i++) { + if (set->capabilities[i]->common.name == name) { + return set->capabilities[i]; + } + } + + return NULL; +} + +static inline uint64_t +util_align(uint64_t value, uint64_t alignment) +{ + value += (alignment - 1); + value &= ~(alignment - 1); + return value; +} + +static inline uint64_t +util_next_power_of_two(uint64_t x) +{ + uint64_t val = x; + + if (x <= 1) { + return 1; + } + + /* Already power of two? */ + if ((x & (x - 1)) == 0) { + return x; + } + + val--; + val = (val >> 1) | val; + val = (val >> 2) | val; + val = (val >> 4) | val; + val = (val >> 8) | val; + val = (val >> 16) | val; + val++; + + return val; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __ALLOCATOR_UTILS_H__ */ diff --git a/scripts/json-generator.pl b/scripts/json-generator.pl new file mode 100644 index 0000000..97d11b6 --- /dev/null +++ b/scripts/json-generator.pl @@ -0,0 +1,50 @@ +#!/bin/perl + +use warnings "all"; +use strict; +use Getopt::Long; + +my %options = (); + +# +# Print usage information for this script. +# +sub usage +{ + my ($error) = @_; + my $FILE = $error ? *STDERR : *STDOUT; + + print $FILE "\n"; + print $FILE "json-generator.pl: Emit contents of a .json file that is\n"; + print $FILE " consumed by the generic allocator driver\n"; + print $FILE " discovery mechanism.\n"; + print $FILE "\n"; + print $FILE " perl json-generator.pl [-driver PATH | -help]\n"; + print $FILE "\n"; + print $FILE "PATH can be a relative path, including just a filename, or\n"; + print $FILE "an absolute path.\n"; + print $FILE "\n"; + + exit($error); +} + +GetOptions(\%options, "help", "driver=s") or usage(1); + +if ($options{help}) +{ + usage(0); +} + +if (!defined $options{driver}) { + print STDERR "Missing required parameter: -driver\n"; + usage(1); +} + +print <<"EOF"; +{ + "file_format_version" : "1.0.0", + "allocator_driver": { + "library_path": "$options{driver}" + } +} +EOF diff --git a/src/Makefile.am b/src/Makefile.am index a4d798b..0d4bd75 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +SUBDIRS = drivers + lib_LTLIBRARIES = liballocator.la liballocator_la_CFLAGS = -I$(top_srcdir)/include diff --git a/src/allocator.c b/src/allocator.c index be1d632..0836934 100644 --- a/src/allocator.c +++ b/src/allocator.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "driver_manager.h" #include "constraint_funcs.h" @@ -468,153 +469,6 @@ void device_destroy_allocation(device_t *dev, allocation_t *allocation) dev->destroy_allocation(dev, allocation); } -void free_capability_sets(uint32_t num_capability_sets, - capability_set_t *capability_sets) -{ - uint32_t i, j; - - if (capability_sets) { - for (i = 0; i < num_capability_sets; i++) { - if (capability_sets[i].capabilities) { - for (j = 0; j < capability_sets[i].num_capabilities; j++) { - free((void *)capability_sets[i].capabilities[j]); - } - - free((void *)capability_sets[i].capabilities); - } - - free((void *)capability_sets[i].constraints); - } - - free(capability_sets); - } -} - -int serialize_capability_set(const capability_set_t *set, - size_t *data_size, - void **data) -{ - size_t size = 0; - unsigned char *d = NULL; - uint32_t i; - - size += sizeof(set->num_constraints); - size += sizeof(set->num_capabilities); - - /* constraints are fixed-size objects */ - size += set->num_constraints * sizeof(set->constraints[0]); - - for (i = 0; i < set->num_capabilities; i++) { - /* length_in_words does not include the size of the capability header */ - size += sizeof(*set->capabilities[i]); - - /* Add in the size of the post-header capability content. */ - size += set->capabilities[i]->common.length_in_words * sizeof(uint32_t); - } - - d = malloc(size); - - if (!d) { - return -1; - } - - *data = d; - *data_size = size; - -#define SERIALIZE(src, len) \ - assert(((d + (len)) - (unsigned char *)*data) <= size); \ - memcpy(d, (src), (len)); \ - d += (len) - - SERIALIZE(&set->num_constraints, sizeof(set->num_constraints)); - SERIALIZE(&set->num_capabilities, sizeof(set->num_capabilities)); - for (i = 0; i < set->num_constraints; i++) { - SERIALIZE(&set->constraints[i], sizeof(set->constraints[i])); - } - - for (i = 0; i < set->num_capabilities; i++) { - size_t cap_size = sizeof(*set->capabilities[i]) + - set->capabilities[i]->common.length_in_words * sizeof(uint32_t); - - SERIALIZE(set->capabilities[i], cap_size); - } - -#undef SERIALIZE - - return 0; -} - -int deserialize_capability_set(size_t data_size, - const void *data, - capability_set_t **capability_set) -{ - const unsigned char *d = data; - constraint_t *constraints = NULL; - capability_header_t **capabilities = NULL; - uint32_t i; - - capability_set_t *set = calloc(1, sizeof(capability_set_t)); - - if (!set) { - goto fail; - } - -#define PEEK_DESERIALIZE(dst, size) \ - if (((d + size) - (const unsigned char *)data) > data_size) goto fail; \ - memcpy((dst), d, (size)) - -#define DESERIALIZE(dst, size) \ - PEEK_DESERIALIZE((dst), (size)); \ - d += (size) - - DESERIALIZE(&set->num_constraints, sizeof(set->num_constraints)); - DESERIALIZE(&set->num_capabilities, sizeof(set->num_capabilities)); - - set->constraints = constraints = - calloc(set->num_constraints, sizeof(*set->constraints)); - capabilities = calloc(set->num_capabilities, sizeof(*capabilities)); - set->capabilities = (const capability_header_t *const *)capabilities; - - if (!constraints || !capabilities) { - goto fail; - } - - for (i = 0; i < set->num_constraints; i++) { - DESERIALIZE(&constraints[i], sizeof(set->constraints[i])); - } - - for (i = 0; i < set->num_capabilities; i++) { - capability_header_t header; - - PEEK_DESERIALIZE(&header, sizeof(header)); - - capabilities[i] = calloc(1, sizeof(header) + - header.common.length_in_words * - sizeof(uint32_t)); - - if (!capabilities[i]) { - goto fail; - } - - DESERIALIZE(capabilities[i], sizeof(*capabilities[i]) + - header.common.length_in_words * sizeof(uint32_t)); - } - -#undef DESERIALIZE -#undef PEEK_DESERIALIZE - - set->constraints = constraints; - set->capabilities = (const capability_header_t *const *)capabilities; - *capability_set = set; - - return 0; - -fail: - free_capability_sets(1, set); - - return -1; -} - int device_export_allocation(device_t *dev, const allocation_t *allocation, uint64_t *allocation_size, diff --git a/src/driver_manager.c b/src/driver_manager.c index 7fc77f6..125193b 100644 --- a/src/driver_manager.c +++ b/src/driver_manager.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -319,7 +321,8 @@ static int add_drivers_in_dir(const char *dir_name) size_t dir_name_len; if (count < 0 ) { - ret = -1; + /* Skip non-existent directories */ + ret = (errno == ENOENT) ? 0 : -1; goto done; } else if (count == 0) { ret = 0; @@ -377,20 +380,65 @@ static int add_drivers_in_dir(const char *dir_name) /*! * Enumerate, load, and initialize all available driver libraries on the system * - * TODO expand this function to scan home directories and other default config - * file locations and locations specified by environment variables. Be sure - * to check for suid when checking non-secure locations. - * * \return 0 on success, -1 on failure. Not success does not indicate any * drivers were found. */ + +#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) + +static const char *DEFAULT_SYS_CONF_DIRS[] = { + "/usr/share/liballocator", + "/usr/local/share/liballocator" +}; + +static const char *DEFAULT_USR_CONF_DIRS[] = { + ".liballocator" +}; + static int init_drivers(void) { if (!drivers_initialized) { + int i; + drivers_initialized = 1; - /* TODO Don't use a single hard-coded path. */ - return add_drivers_in_dir("/etc/allocator"); + /* Load drivers under default system configuration directories */ + for (i = 0; i < ARRAY_LEN(DEFAULT_SYS_CONF_DIRS); i++) { + if (add_drivers_in_dir(DEFAULT_SYS_CONF_DIRS[i]) < 0) { + return -1; + } + } + + /* If not running as setuid, also try user configuration directories */ + if (getuid() == geteuid()) { + const char *home = getenv("HOME"); + const char *usr_extra = getenv("__LIBALLOCATOR_EXTRA_CONF_DIRS"); + + /* Default user configuration directories */ + for (i = 0; i < ARRAY_LEN(DEFAULT_USR_CONF_DIRS); i++) { + char tmp[PATH_MAX]; + strcpy(tmp, home); + strcat(tmp, "/"); + strcat(tmp, DEFAULT_USR_CONF_DIRS[i]); + if (add_drivers_in_dir(tmp) < 0) { + return -1; + } + } + + /* User-defined extra directories */ + if (usr_extra) { + char *tmp = strdup(usr_extra); + char *dir = strtok(tmp, ":"); + while (dir) { + if (add_drivers_in_dir(dir) < 0) { + free(tmp); + return -1; + } + dir = strtok(NULL, ":"); + } + free(tmp); + } + } } return 0; diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am new file mode 100644 index 0000000..fb86226 --- /dev/null +++ b/src/drivers/Makefile.am @@ -0,0 +1,25 @@ +# Copyright (c) 2017 NVIDIA CORPORATION. All rights reserved. +# +# 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 +# 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. + +SUBDIRS = + +if ENABLE_NOUVEAU +SUBDIRS += nouveau +endif diff --git a/src/drivers/nouveau/Makefile.am b/src/drivers/nouveau/Makefile.am new file mode 100644 index 0000000..940258d --- /dev/null +++ b/src/drivers/nouveau/Makefile.am @@ -0,0 +1,23 @@ +lib_LTLIBRARIES = libnouveau-allocator.la + +libnouveau_allocator_la_CFLAGS = -I$(top_srcdir)/include $(LIBDRM_NOUVEAU_CFLAGS) +libnouveau_allocator_la_LDFLAGS = $(LIBDRM_NOUVEAU_LIBS) + +libnouveau_allocator_la_LDFLAGS += -avoid-version -module -shared +libnouveau_allocator_la_LDFLAGS += -export-symbols nouveau_allocator_driver.syms + +libnouveau_allocator_la_SOURCES = nouveau_allocator_driver.c + +NOUVEAU_ALLOCATOR_CONF_FILE=50_nouveau_allocator.json + +dist_pkgdata_DATA = $(NOUVEAU_ALLOCATOR_CONF_FILE) + +# Generate Nouveau's driver Json +JSON_GENERATOR=$(top_srcdir)/scripts/json-generator.pl + +$(NOUVEAU_ALLOCATOR_CONF_FILE): $(JSON_GENERATOR) + @echo -e " JSON $@" + @$(PERL) $(JSON_GENERATOR) -driver=libnouveau-allocator.so > $(builddir)/$@ + +clean-local: + rm -f $(NOUVEAU_ALLOCATOR_CONF_FILE) diff --git a/src/drivers/nouveau/nouveau_allocator_driver.c b/src/drivers/nouveau/nouveau_allocator_driver.c new file mode 100644 index 0000000..b947c6e --- /dev/null +++ b/src/drivers/nouveau/nouveau_allocator_driver.c @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2017 NVIDIA CORPORATION. All rights reserved. + * + * 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 + * 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. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Nouveau allocator driver and device definitions */ +typedef struct { + driver_t *pbase; +} nouveau_driver_t; + +typedef struct { + device_t base; + + struct nouveau_drm *drm; + struct nouveau_device *dev; + + struct { + uint64_t address_alignment; + uint32_t pitch_alignment; + uint32_t max_pitch; + uint32_t max_dimensions; + } properties; +} nouveau_device_t; + +typedef struct { + allocation_t base; + + struct nouveau_bo *bo; + int fd; +} nouveau_allocation_t; + +/* Nouveau capability definitions */ +#define NOUVEAU_CAP_WORDS(c) ((sizeof(c) - sizeof(capability_header_t) + 3)/4) + +typedef struct { + capability_header_t header; +} nouveau_cap_vidmem_t; + +#define NOUVEAU_CAP_VIDMEM_NAME 0xF000 +#define NOUVEAU_CAP_VIDMEM_WORDS NOUVEAU_CAP_WORDS(nouveau_cap_vidmem_t) + +typedef struct { + capability_header_t header; +} nouveau_cap_contig_t; + +#define NOUVEAU_CAP_CONTIG_NAME 0xF001 +#define NOUVEAU_CAP_CONTIG_WORDS NOUVEAU_CAP_WORDS(nouveau_cap_contig_t) + + +static int +nouveau_is_fd_supported(driver_t *driver, int fd) +{ + /* XXX: This only checks whether the given fd is a DRM device. Note that + * this doesn't necessarily means it's a nouveau device */ + switch (drmGetNodeTypeFromFd(fd)) { + case DRM_NODE_PRIMARY: + case DRM_NODE_CONTROL: + case DRM_NODE_RENDER: + return 1; + default: + return 0; + } +} + +static int nouveau_check_uses(const nouveau_device_t *dev, + uint32_t num_uses, + const usage_t *uses) +{ + uint32_t i; + + for (i = 0; i < num_uses; i++) { + if ((uses[i].dev == &dev->base) || (uses[i].dev == DEVICE_NONE)) { + return 1; + } + } + + return 0; +} + +static int +nouveau_device_get_capabilities(device_t *dev, + const assertion_t *assertion, + uint32_t num_uses, + const usage_t *uses, + uint32_t *num_sets, + capability_set_t **capability_sets) +{ + const nouveau_device_t *nouveau_device = dev->device_private; + uint32_t n_sets = 0; + capability_set_t *cap_sets; + + constraint_t *tmp_constr; + capability_header_t **tmp_caps; + + capability_pitch_linear_t *cap_pitch; + nouveau_cap_vidmem_t *cap_vidmem; + nouveau_cap_contig_t *cap_contig; + int is_display; + + uint32_t n_pitch_constr = 3; /* pitch+address alignment, max pitch */ + uint32_t n_pitch_caps = 2; /* pitch, vidmem */ + + if (!nouveau_check_uses(nouveau_device, num_uses, uses)) { + /* The app didn't specify any use for this device */ + *num_sets = 0; + *capability_sets = NULL; + return 0; + } + + is_display = !!util_find_use(num_uses, uses, dev, USAGE_BASE_DISPLAY); + if (is_display) { + n_pitch_caps += 1; /* contig */ + } + + n_sets = 1; /* Pitch-linear */ + + cap_sets = calloc(n_sets, sizeof(*cap_sets)); + if (!cap_sets) { + return -1; + } + + /* Fill in pitch-linear constraints and capabilities */ + + tmp_constr = calloc(n_pitch_constr, sizeof(*tmp_constr)); + tmp_caps = calloc(n_pitch_caps, sizeof(*tmp_caps)); + cap_pitch = calloc(1, sizeof(capability_pitch_linear_t)); + cap_vidmem = calloc(1, sizeof(nouveau_cap_vidmem_t)); + if (is_display) { + cap_contig = calloc(1, sizeof(nouveau_cap_contig_t)); + } + + if (!tmp_constr || + !tmp_caps || + !cap_pitch || + !cap_vidmem || + (is_display && !cap_contig)) { + free(tmp_constr); + free(tmp_caps); + free(cap_pitch); + free(cap_vidmem); + free(cap_contig); + free(cap_sets); + return -1; + } + + tmp_constr[0].name = CONSTRAINT_ADDRESS_ALIGNMENT; + tmp_constr[0].u.address_alignment.value = + nouveau_device->properties.address_alignment; + + tmp_constr[1].name = CONSTRAINT_PITCH_ALIGNMENT; + tmp_constr[1].u.pitch_alignment.value = + nouveau_device->properties.pitch_alignment; + + tmp_constr[2].name = CONSTRAINT_MAX_PITCH; + tmp_constr[2].u.max_pitch.value = + nouveau_device->properties.max_pitch; + + cap_pitch->header.common.vendor = VENDOR_BASE; + cap_pitch->header.common.name = CAP_BASE_PITCH_LINEAR; + cap_pitch->header.common.length_in_words = 0; + cap_pitch->header.required = 1; + tmp_caps[0] = &cap_pitch->header; + + cap_vidmem->header.common.vendor = VENDOR_NVIDIA; + cap_vidmem->header.common.name = NOUVEAU_CAP_VIDMEM_NAME; + cap_vidmem->header.common.length_in_words = NOUVEAU_CAP_VIDMEM_WORDS; + cap_vidmem->header.required = 0; + tmp_caps[1] = &cap_vidmem->header; + + if (cap_contig) { + cap_contig->header.common.vendor = VENDOR_NVIDIA; + cap_contig->header.common.name = NOUVEAU_CAP_CONTIG_NAME; + cap_contig->header.common.length_in_words = NOUVEAU_CAP_CONTIG_WORDS; + cap_contig->header.required = 1; + tmp_caps[2] = &cap_contig->header; + } + + cap_sets[0].constraints = tmp_constr; + cap_sets[0].num_constraints = n_pitch_constr; + cap_sets[0].capabilities = (const capability_header_t *const *)tmp_caps; + cap_sets[0].num_capabilities = n_pitch_caps; + + *capability_sets = cap_sets; + *num_sets = n_sets; + + return 0; +} + +static int +nouveau_device_get_assertion_hints(device_t *dev, + uint32_t num_uses, + const usage_t *uses, + uint32_t *num_hints, + assertion_hint_t **hints) +{ + nouveau_device_t *nouveau_device = (nouveau_device_t *)dev->device_private; + uint32_t n_hints; + assertion_hint_t *hint_array; + uint32_t i; + + if (!nouveau_check_uses(nouveau_device, num_uses, uses)) { + /* The app didn't specify any use for this device */ + *num_hints = 0; + *hints = NULL; + return 0; + } + + /* + * TODO: 1. Fetch internal pixel formats for this device + * 2. For each use + * For each pixel format + * If use and pixel format are not compatible, discard pixel + * format + * If different uses have different requirements (e.g. different + * size limits for scanout/render surfaces) + * Find the minimum set that satisfies all uses + * 3. If no pixel formats survived OR no compatible uses + * Return 0 hints + * 4. Return N hints + */ + n_hints = 1; + + hint_array = (assertion_hint_t *)calloc(n_hints, sizeof(*hint_array)); + if (!hint_array) { + return -1; + } + + for (i = 0; i < n_hints; i++) { + assertion_hint_t _HINT_INIT_ = { + /* XXX max_width/max_height should vary based on usage */ + .max_width = nouveau_device->properties.max_dimensions, + .max_height = nouveau_device->properties.max_dimensions, + + /* XXX No format enumeration yet. Assume RGBA8888 everywhere */ + .num_formats = 0, + .formats = NULL + }; + + /* Need this because of assertion_hint_t constness */ + memcpy(&hint_array[i], &_HINT_INIT_, sizeof(_HINT_INIT_)); + } + + *num_hints = n_hints; + *hints = hint_array; + + return 0; +} + +static void +nouveau_device_destroy_allocation(device_t *dev, + allocation_t *allocation) +{ + nouveau_allocation_t *nouveau_allocation = + (nouveau_allocation_t *)allocation->allocation_private; + + if (nouveau_allocation) { + if (nouveau_allocation->fd >= 0) { + close(nouveau_allocation->fd); + } + + nouveau_bo_ref(NULL, &nouveau_allocation->bo); + + free_capability_sets(1, (capability_set_t *)allocation->capability_set); + + free(nouveau_allocation); + } +} + +static int nouveau_device_create_allocation(device_t *dev, + const assertion_t *assertion, + const capability_set_t *capability_set, + allocation_t **allocation) +{ + nouveau_driver_t *nouveau_driver = + (nouveau_driver_t *)dev->driver->driver_private; + nouveau_device_t *nouveau_device = + (nouveau_device_t *)dev->device_private; + nouveau_allocation_t *nouveau_allocation = NULL; + + int is_vidmem = !!util_find_cap(capability_set, NOUVEAU_CAP_VIDMEM_NAME); + int is_contig = !!util_find_cap(capability_set, NOUVEAU_CAP_CONTIG_NAME); + const constraint_t *address_alignment = + util_find_constraint(capability_set, CONSTRAINT_ADDRESS_ALIGNMENT); + const constraint_t *pitch_alignment = + util_find_constraint(capability_set, CONSTRAINT_PITCH_ALIGNMENT); + + uint32_t bpp = 32; /* XXX Should be based on assertion->format */ + uint32_t pitch = 0; + uint32_t height = 0; + uint32_t flags = 0; + union nouveau_bo_config bo_config = { 0 }; + + /* Allocate the allocation object */ + nouveau_allocation = + (nouveau_allocation_t *)calloc(1, sizeof(nouveau_allocation_t)); + if (!nouveau_allocation) { + goto fail; + } + + nouveau_allocation->base.allocation_private = nouveau_allocation; + nouveau_allocation->fd = -1; + + /* Save the capability set for future use */ + nouveau_allocation->base.capability_set = dup_capability_set(capability_set); + if (!nouveau_allocation->base.capability_set) { + goto fail; + } + + /* Set the approriate buffer object flags */ + flags |= (is_vidmem ? NOUVEAU_BO_VRAM : 0); + flags |= (is_contig ? NOUVEAU_BO_CONTIG : 0); + flags |= NOUVEAU_BO_NOSNOOP; + + /* Allocation size */ + pitch = bpp * assertion->width / 8; + pitch = util_align(pitch, (pitch_alignment ? + pitch_alignment->u.pitch_alignment.value : 1)); + + /* Account for very generous prefetch (allocate size as if tiled). */ + height = UTIL_MAX2(assertion->height, 8); + height = util_next_power_of_two(height); + + nouveau_allocation->base.size = pitch * height; + + /* Memory type and tile mode according to format. + * + * Memory type values below come from the nouveau Gallium driver + * implementation in Mesa. + */ + /* TODO: Handle all formats and tile modes */ + switch (nouveau_device->dev->chipset & ~0xf) { + /* Fermi */ + case 0xc0: + case 0xd0: + /* Kepler */ + case 0xe0: + case 0xf0: + case 0x100: + /* Maxwell */ + case 0x110: + case 0x120: + /* Pascal */ + case 0x130: + switch (bpp) { + case 32: + bo_config.nvc0.memtype = 0xfe; /* uncompressed */ + break; + default: + bo_config.nvc0.memtype = 0; + break; + } + bo_config.nvc0.tile_mode = 0; /* No tiling */ + break; + default: + switch (bpp) { + case 32: + bo_config.nv50.memtype = 0x70; /* uncompressed */ + break; + default: + bo_config.nvc0.memtype = 0; + break; + } + bo_config.nv50.tile_mode = 0; /* No tiling */ + break; + } + + if (nouveau_bo_new(nouveau_device->dev, + flags, + (address_alignment ? + address_alignment->u.address_alignment.value : 1), + nouveau_allocation->base.size, + &bo_config, + &nouveau_allocation->bo)) + { + goto fail; + } + + if (nouveau_bo_set_prime(nouveau_allocation->bo, &nouveau_allocation->fd)) { + goto fail; + } + + *allocation = &nouveau_allocation->base; + + return 0; + +fail: + if (nouveau_allocation) { + dev->destroy_allocation(dev, &nouveau_allocation->base); + } + return -1; +} + +static int nouveau_device_get_allocation_fd(device_t *dev, + const allocation_t *allocation, + int *fd) +{ + const nouveau_allocation_t *nouveau_allocation = + (const nouveau_allocation_t *)allocation->allocation_private; + + *fd = dup(nouveau_allocation->fd); + + return (*fd < 0) ? -1 : 0; +} + +static void nouveau_device_destroy(device_t *device) +{ + nouveau_device_t *nouveau_device = device->device_private; + + if (nouveau_device) { + nouveau_device_del(&nouveau_device->dev); + nouveau_drm_del(&nouveau_device->drm); + free(device); + } +} + +static device_t *nouveau_device_create_from_fd(driver_t *driver, int fd) +{ + nouveau_device_t *nouveau_device; + int ret; + + if (!driver->is_fd_supported(driver, fd)) { + return NULL; + } + + nouveau_device = (nouveau_device_t *)calloc(1, sizeof(nouveau_device_t)); + if (!nouveau_device) { + return NULL; + } + + ret = nouveau_drm_new(fd, &nouveau_device->drm); + if (ret) { + goto fail; + } + + ret = nouveau_device_new(&nouveau_device->drm->client, NV_DEVICE, + &(struct nv_device_v0) { + .device = ~0LL, + }, sizeof(struct nv_device_v0), + &nouveau_device->dev); + if (ret) { + goto fail; + } + + nouveau_device->base.device_private = nouveau_device; + nouveau_device->base.driver = driver; + nouveau_device->base.destroy = &nouveau_device_destroy; + nouveau_device->base.get_capabilities = &nouveau_device_get_capabilities; + nouveau_device->base.get_assertion_hints = &nouveau_device_get_assertion_hints; + nouveau_device->base.create_allocation = &nouveau_device_create_allocation; + nouveau_device->base.destroy_allocation = &nouveau_device_destroy_allocation; + nouveau_device->base.get_allocation_fd = &nouveau_device_get_allocation_fd; + + switch (nouveau_device->dev->chipset & ~0xf) { + /* Fermi */ + case 0xc0: + case 0xd0: + /* Kepler */ + case 0xe0: + case 0xf0: + case 0x100: + /* Maxwell */ + case 0x110: + case 0x120: + /* Pascal */ + case 0x130: + nouveau_device->properties.address_alignment = 4096; + nouveau_device->properties.pitch_alignment = 128; + nouveau_device->properties.max_pitch = INT_MAX; + nouveau_device->properties.max_dimensions = 16384; + break; + default: + nouveau_device->properties.address_alignment = 4096; + nouveau_device->properties.pitch_alignment = 64; + nouveau_device->properties.max_pitch = INT_MAX; + nouveau_device->properties.max_dimensions = 16384; + break; + } + + return &nouveau_device->base; + +fail: + nouveau_device_del(&nouveau_device->dev); + nouveau_drm_del(&nouveau_device->drm); + free(nouveau_device); + return NULL; +} + +static void nouveau_driver_destroy(driver_t *driver) +{ + nouveau_driver_t *nouveau_driver = + (nouveau_driver_t *)driver->driver_private; + + if (nouveau_driver) { + free(nouveau_driver); + driver->driver_private = NULL; + } +} + +int allocator_driver_init(driver_t *driver) +{ + nouveau_driver_t *nouveau_driver; + + // Workaround for compiler warning that no unsigned value is < 0 + const unsigned int our_header_version = DRIVER_INTERFACE_VERSION; + + if (driver->driver_interface_version < our_header_version) { + return -1; + } + + nouveau_driver = (nouveau_driver_t *)calloc(1, sizeof(nouveau_driver_t)); + if (!nouveau_driver) { + return -1; + } + + nouveau_driver->pbase = driver; + + driver->driver_private = (void *)nouveau_driver; + + driver->driver_interface_version = DRIVER_INTERFACE_VERSION; + + driver->destroy = &nouveau_driver_destroy; + driver->is_fd_supported = &nouveau_is_fd_supported; + driver->device_create_from_fd = &nouveau_device_create_from_fd; + + return 0; +} diff --git a/src/drivers/nouveau/nouveau_allocator_driver.syms b/src/drivers/nouveau/nouveau_allocator_driver.syms new file mode 100644 index 0000000..ea80386 --- /dev/null +++ b/src/drivers/nouveau/nouveau_allocator_driver.syms @@ -0,0 +1 @@ +allocator_driver_init diff --git a/tests/capability_set_ops.c b/tests/capability_set_ops.c index a0c0990..c0816f1 100644 --- a/tests/capability_set_ops.c +++ b/tests/capability_set_ops.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "test_utils.h" @@ -62,12 +63,27 @@ int main(int argc, char *argv[]) USAGE_LENGTH_IN_WORDS(usage_texture_t) /* length_in_word */ } }; + static usage_display_t display_usage = { + { /* header */ + VENDOR_BASE, /* usage vendor */ + USAGE_BASE_DISPLAY, /* usage name */ + USAGE_LENGTH_IN_WORDS(usage_display_t) /* length_in_word */ + }, + + USAGE_BASE_DISPLAY_ROTATION_0 + }; - static usage_t uses = { - NULL, /* dev, overidden below */ - &texture_usage.header /* usage */ + static usage_t uses[] = { + { + NULL, /* dev, overidden below */ + &texture_usage.header /* usage */ + }, + { + NULL, /* dev, overidden below */ + &display_usage.header /* usage */ + } }; - static const uint32_t num_uses = 1; + static const uint32_t num_uses = sizeof(uses) / sizeof(usage_t); uint32_t num_assertion_hints = 0; assertion_hint_t *assertion_hints; @@ -80,7 +96,7 @@ int main(int argc, char *argv[]) int opt; size_t num_devices = 0; - int i; + int i, j; char **dev_file_names = NULL; char **temp; int *dev_fds; @@ -147,10 +163,12 @@ int main(int argc, char *argv[]) FAIL("Couldn't create allocator device from device FD\n"); } - uses.dev = devs[i]; + for (j = 0; j < num_uses; j++) { + uses[j].dev = devs[i]; + } /* Query assertion hints and use maximum surface size reported */ - if (device_get_assertion_hints(devs[i], num_uses, &uses, + if (device_get_assertion_hints(devs[i], num_uses, uses, &num_assertion_hints, &assertion_hints) || (num_assertion_hints == 0)) { @@ -164,7 +182,7 @@ int main(int argc, char *argv[]) free_assertion_hints(num_assertion_hints, assertion_hints); /* Query capabilities for a common usage case from the device */ - if (device_get_capabilities(devs[i], &assertion, num_uses, &uses, + if (device_get_capabilities(devs[i], &assertion, num_uses, uses, &num_capability_sets[i], &capability_sets[i])) { FAIL("Couldn't get capabilities for given usage from device %i\n", diff --git a/tests/drm_import_allocation.c b/tests/drm_import_allocation.c index 833d51b..7d76320 100644 --- a/tests/drm_import_allocation.c +++ b/tests/drm_import_allocation.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -39,8 +40,8 @@ static void usage(void) { - printf("\nUsage: drm_import_allocation [-f|--file] " - "[-d|--drm-file] \n"); + printf("\nUsage: drm_import_allocation [-d|--drm-file] " + "[[-f|--file] ]\n"); } int main(int argc, char *argv[]) @@ -119,14 +120,23 @@ int main(int argc, char *argv[]) } } - if (!dev_file_name || !drm_file_name) { + if (!drm_file_name) { usage(); exit(1); } - dev_fd = open(dev_file_name, O_RDWR); - if (dev_fd < 0) { - FAIL("Couldn't open allocator device file %s\n", dev_file_name); + drm_fd = open(drm_file_name, O_RDWR); + if (drm_fd < 0) { + FAIL("Couldn't open DRM device file %s\n", dev_file_name); + } + + if (!dev_file_name || !strcmp(drm_file_name, dev_file_name)) { + dev_fd = drm_fd; + } else { + dev_fd = open(dev_file_name, O_RDWR); + if (dev_fd < 0) { + FAIL("Couldn't open allocator device file %s\n", dev_file_name); + } } dev = device_create(dev_fd); @@ -134,11 +144,6 @@ int main(int argc, char *argv[]) FAIL("Couldn't create allocator device from device FD\n"); } - drm_fd = open(drm_file_name, O_RDWR); - if (drm_fd < 0) { - FAIL("Couldn't open DRM device file %s\n", dev_file_name); - } - /* Query capabilities for a common usage case from the device */ if (device_get_capabilities(dev, &assertion, num_uses, &uses, &num_capability_sets, @@ -181,7 +186,7 @@ int main(int argc, char *argv[]) } pitch_alignment_cons = - find_constraint(&capability_sets[i], CONSTRAINT_PITCH_ALIGNMENT); + util_find_constraint(&capability_sets[i], CONSTRAINT_PITCH_ALIGNMENT); if (pitch_alignment_cons) { pitch_alignment = pitch_alignment_cons->u.pitch_alignment.value; } @@ -215,7 +220,9 @@ int main(int argc, char *argv[]) done: device_destroy(dev); - close(dev_fd); + if (dev_fd != drm_fd) { + close(dev_fd); + } printf("Success\n"); diff --git a/tests/test_utils.c b/tests/test_utils.c index 27cd2f7..9d494a3 100644 --- a/tests/test_utils.c +++ b/tests/test_utils.c @@ -71,19 +71,6 @@ int compare_capability_sets(capability_set_t *set0, capability_set_t *set1) return 0; } -const constraint_t *find_constraint(const capability_set_t *set, uint32_t name) -{ - int i; - - for (i = 0; i < set->num_constraints; i++) { - if (set->constraints[i].name == name) { - return &set->constraints[i]; - } - } - - return NULL; -} - void print_constraint(const constraint_t *constraint) { int i;