diff options
-rw-r--r-- | README.md | 44 | ||||
-rw-r--r-- | common/log.h | 5 | ||||
-rw-r--r-- | drm-lease-manager/drm-lease.h | 20 | ||||
-rw-r--r-- | drm-lease-manager/lease-config.c | 166 | ||||
-rw-r--r-- | drm-lease-manager/lease-config.h | 19 | ||||
-rw-r--r-- | drm-lease-manager/lease-manager.c | 338 | ||||
-rw-r--r-- | drm-lease-manager/lease-manager.h | 3 | ||||
-rw-r--r-- | drm-lease-manager/main.c | 20 | ||||
-rw-r--r-- | drm-lease-manager/meson.build | 5 | ||||
-rw-r--r-- | drm-lease-manager/test/lease-config-test.c | 140 | ||||
-rw-r--r-- | drm-lease-manager/test/lease-manager-test.c | 379 | ||||
-rw-r--r-- | drm-lease-manager/test/meson.build | 18 | ||||
-rw-r--r-- | drm-lease-manager/test/test-drm-device.c | 47 | ||||
-rw-r--r-- | drm-lease-manager/test/test-drm-device.h | 14 | ||||
-rw-r--r-- | meson.build | 1 |
15 files changed, 1050 insertions, 169 deletions
@@ -24,18 +24,33 @@ The basic build procedure is as follows: `<build_dir>` can be any directory name, but `build` is commonly used. -## Running +## Configuration -Once installed, running the following command will start the DRM Lease Manager daemon +The drm-lease-manager configuration file allows the user to specify the mapping +of DRM connectors to DRM leases. The location of the configuration file can +be specified with the `--config` command line option. - drm-lease-manager [<path DRM device>] +The configuration file consists of a list of lease definitions, containing a name +of the lease and a list of the included connector names. -If no DRM device is specified, `/dev/dri/card0` will be used. -More detailed options can be displayed by specifying the `-h` flag. +Each list entry is of the following form: -### Lease naming +```toml +[[lease]] +name="My lease" +connectors=["connector 1", "connector 2"] +``` +* Note: quotes around all string values are mandatory. -One DRM lease will be created for each connector on the DRM device (up to the number of available CRTCs). +This will create a lease named `My lease` and add the two connectors `connector 1` and +`connector 2` to the lease. +If there is no connector with either of the names exists on the system, that name +will be omitted from the lease. + +### Default configuration + +If no configuration file is specified one DRM lease will be created for each connector +on the DRM device (up to the number of available CRTCs). The names of the DRM leases will have the following pattern: @@ -44,21 +59,30 @@ The names of the DRM leases will have the following pattern: So, for example, a DRM lease for the first LVDS device on the device `/dev/dri/card0` would be named `card0-LVDS-1`. +## Running + +Once installed, running the following command will start the DRM Lease Manager daemon + + drm-lease-manager [<path DRM device>] + +If no DRM device is specified, the first available device capabale of modesetting will +be used. More detailed options can be displayed by specifying the `-h` flag. + ### Dynamic lease transfer When `drm-lease-manager` is started with the `-t` option, the -ownership of a leases resourses can be transfered from +ownership of a leases resources can be transferred from one client to another. This allows the ownership of the leased resources to be transferred without the display being closed and the screen blanking. -`drm-lease-manager` handles the timing of the tranfser and manages the +`drm-lease-manager` handles the timing of the transfer and manages the references to the DRM device, so that the last framebuffer of the old client stays on screen until the new client presents its first frame. The transition can be completed without direct communication between the old and new client applications, however, the client that the lease will be -transitioned *from* must be able to handle unexpected lease revokation. +transitioned *from* must be able to handle unexpected lease revocation. Once the lease is revoked, all DRM API calls referring to the DRM resources managed by the lease will fail with -ENOENT. The client should be able to gracefully handle this condition by, for example, diff --git a/common/log.h b/common/log.h index cd0c0e3..ed3b2db 100644 --- a/common/log.h +++ b/common/log.h @@ -19,8 +19,9 @@ #include <stdbool.h> #include <stdio.h> -#define DEBUG_LOG(FMT, ...) \ - dlm_log_print(true, stdout, "DEBUG: %s: " FMT, __func__, ##__VA_ARGS__) +#define DEBUG_LOG(FMT, ...) \ + dlm_log_print(true, stdout, "DEBUG: %s: line %d: " FMT, __func__, \ + __LINE__, ##__VA_ARGS__) #define INFO_LOG(FMT, ...) \ dlm_log_print(false, stdout, "INFO: " FMT, ##__VA_ARGS__) #define WARN_LOG(FMT, ...) \ diff --git a/drm-lease-manager/drm-lease.h b/drm-lease-manager/drm-lease.h index a855054..5670fa8 100644 --- a/drm-lease-manager/drm-lease.h +++ b/drm-lease-manager/drm-lease.h @@ -15,9 +15,29 @@ #ifndef DRM_LEASE_H #define DRM_LEASE_H +#include <stdbool.h> +#include <stdint.h> struct lease_handle { char *name; void *user_data; }; + +struct connector_config { + char *name; + bool optional; + int nplanes; + uint32_t *planes; +}; + +struct lease_config { + char *lease_name; + + int ncids; + uint32_t *connector_ids; + + int nconnectors; + struct connector_config *connectors; +}; + #endif diff --git a/drm-lease-manager/lease-config.c b/drm-lease-manager/lease-config.c new file mode 100644 index 0000000..aaba6b6 --- /dev/null +++ b/drm-lease-manager/lease-config.c @@ -0,0 +1,166 @@ +/* Copyright 2022 IGEL Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lease-config.h" +#include "log.h" +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <toml.h> + +#define CONFIG_ERROR(x, ...) ERROR_LOG("%s: " x, filename, ##__VA_ARGS__) + +static bool populate_connector_planes(struct connector_config *config, + toml_array_t *planes) +{ + config->nplanes = toml_array_nelem(planes); + config->planes = calloc(config->nplanes, sizeof(uint32_t)); + if (!config->planes) { + DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno)); + return false; + } + + for (int j = 0; j < config->nplanes; j++) { + toml_datum_t plane = toml_int_at(planes, j); + if (!plane.ok) { + return false; + } + config->planes[j] = plane.u.i; + } + return true; +} + +static bool populate_connector_config(struct lease_config *config, + toml_table_t *global_table, + toml_array_t *conns) +{ + int nconnectors = toml_array_nelem(conns); + config->connectors = calloc(nconnectors, sizeof(*config->connectors)); + if (!config->connectors) { + DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno)); + return false; + } + + config->nconnectors = nconnectors; + + for (int i = 0; i < config->nconnectors; i++) { + struct connector_config *conn_config = &config->connectors[i]; + toml_datum_t conn = toml_string_at(conns, i); + if (!conn.ok) { + ERROR_LOG("Invalid connector in lease %s: idx:%d\n", + config->lease_name, i); + return false; + } + conn_config->name = conn.u.s; + + toml_table_t *conn_config_data = + toml_table_in(global_table, conn.u.s); + if (!conn_config_data) + continue; + + toml_datum_t optional = + toml_bool_in(conn_config_data, "optional"); + if (optional.ok) + config->connectors[i].optional = optional.u.b; + + toml_array_t *planes = + toml_array_in(conn_config_data, "planes"); + if (planes && !populate_connector_planes(conn_config, planes)) { + ERROR_LOG("Invalid plane id for connector: %s\n", + conn_config->name); + return false; + } + } + return true; +} + +int parse_config(char *filename, struct lease_config **parsed_config) +{ + struct lease_config *config = NULL; + int nconfigs, i, ret = 0; + char parse_error[160]; + + FILE *fp = fopen(filename, "r"); + if (!fp) + return 0; + + toml_table_t *t_config = + toml_parse_file(fp, parse_error, sizeof parse_error); + if (!t_config) { + CONFIG_ERROR("configuration file parse error: %s\n", + parse_error); + fclose(fp); + return 0; + } + + toml_array_t *leases = toml_array_in(t_config, "lease"); + if (!leases) { + CONFIG_ERROR( + "Invalid config - cannot find any 'lease' configs"); + goto err; + } + nconfigs = toml_array_nelem(leases); + config = calloc(nconfigs, sizeof *config); + + if (!config) { + DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno)); + goto err; + } + + for (i = 0; i < toml_array_nelem(leases); i++) { + toml_table_t *lease = toml_table_at(leases, i); + + toml_datum_t name = toml_string_in(lease, "name"); + if (!name.ok) { + CONFIG_ERROR("Invalid lease name in entry #%d\n", i); + goto err_free_config; + } + + config[i].lease_name = name.u.s; + + toml_array_t *conns = toml_array_in(lease, "connectors"); + if (conns && + !populate_connector_config(&config[i], t_config, conns)) { + CONFIG_ERROR("Error configuring lease: %s\n", + config[i].lease_name); + goto err_free_config; + } + } + + *parsed_config = config; + ret = nconfigs; +err: + toml_free(t_config); + fclose(fp); + return ret; +err_free_config: + release_config(i, config); + goto err; +} + +void release_config(int num_leases, struct lease_config *config) +{ + for (int i = 0; i < num_leases; i++) { + struct lease_config *c = &config[i]; + free(c->lease_name); + for (int j = 0; j < c->nconnectors; j++) { + free(c->connectors[j].name); + free(c->connectors[j].planes); + } + free(c->connectors); + } + free(config); +} diff --git a/drm-lease-manager/lease-config.h b/drm-lease-manager/lease-config.h new file mode 100644 index 0000000..0760a99 --- /dev/null +++ b/drm-lease-manager/lease-config.h @@ -0,0 +1,19 @@ +/* Copyright 2022 IGEL Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "drm-lease.h" + +int parse_config(char *filename, struct lease_config **parsed_config); +void release_config(int num_leasess, struct lease_config *config); diff --git a/drm-lease-manager/lease-manager.c b/drm-lease-manager/lease-manager.c index 5eeef16..885ca29 100644 --- a/drm-lease-manager/lease-manager.c +++ b/drm-lease-manager/lease-manager.c @@ -34,9 +34,9 @@ #include <xf86drm.h> #include <xf86drmMode.h> -/* Number of resources, excluding planes, to be included in each DRM lease. - * Each lease needs at least a CRTC and conector. */ -#define DRM_LEASE_MIN_RES (2) +/* Number of resources, to be included in a DRM lease for each connector. + * Each connector needs both a CRTC and conector object:. */ +#define DRM_OBJECTS_PER_CONNECTOR (2) #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0])) @@ -64,6 +64,9 @@ struct lm { drmModePlaneResPtr drm_plane_resource; uint32_t available_crtcs; + char **connector_names; + int nconnectors; + struct lease **leases; int nleases; }; @@ -90,7 +93,7 @@ static const char *const connector_type_names[] = { [DRM_MODE_CONNECTOR_WRITEBACK] = "Writeback", }; -static char *drm_create_lease_name(struct lm *lm, drmModeConnectorPtr connector) +static char *drm_create_connector_name(drmModeConnectorPtr connector) { uint32_t type = connector->connector_type; uint32_t id = connector->connector_type_id; @@ -104,8 +107,18 @@ static char *drm_create_lease_name(struct lm *lm, drmModeConnectorPtr connector) id = connector->connector_id; char *name; - if (asprintf(&name, "card%d-%s-%d", minor(lm->dev_id), - connector_type_names[type], id) < 0) + if (asprintf(&name, "%s-%d", connector_type_names[type], id) < 0) + return NULL; + + return name; +} + +static char *drm_create_default_lease_name(struct lm *lm, int cindex) +{ + char *connector_name = lm->connector_names[cindex]; + + char *name; + if (asprintf(&name, "card%d-%s", minor(lm->dev_id), connector_name) < 0) return NULL; return name; @@ -189,17 +202,62 @@ static void drm_find_available_crtcs(struct lm *lm) } } -static bool lease_add_planes(struct lm *lm, struct lease *lease, int crtc_index) +static bool drm_find_connector(struct lm *lm, char *name, uint32_t *id) { - for (uint32_t i = 0; i < lm->drm_plane_resource->count_planes; i++) { - uint32_t plane_id = lm->drm_plane_resource->planes[i]; + int connectors = lm->drm_resource->count_connectors; + + for (int i = 0; i < connectors; i++) { + if (strcmp(lm->connector_names[i], name)) + continue; + if (id) + *id = lm->drm_resource->connectors[i]; + return true; + } + return false; +} + +static void config_get_planes(struct lm *lm, + const struct connector_config *config, + int *nplanes, uint32_t **planes) +{ + if (config && config->planes) { + *nplanes = config->nplanes; + *planes = config->planes; + } else { + *nplanes = (int)lm->drm_plane_resource->count_planes; + *planes = lm->drm_plane_resource->planes; + } +} + +static bool lease_add_planes(struct lm *lm, struct lease *lease, + uint32_t crtc_index, + const struct connector_config *con_config) +{ + int nplanes; + uint32_t *planes; + uint32_t crtc_mask = (1 << crtc_index); + + /* Only allow shared planes when plane list is explicitly set */ + bool allow_shared = con_config && con_config->planes; + + config_get_planes(lm, con_config, &nplanes, &planes); + + for (int i = 0; i < nplanes; i++) { + uint32_t plane_id = planes[i]; drmModePlanePtr plane = drmModeGetPlane(lm->drm_fd, plane_id); - assert(plane); + if (!plane) { + ERROR_LOG( + "Unknown plane id %d configured in lease: %s\n", + plane_id, lease->base.name); + return false; + } - // Exclude planes that can be used with multiple CRTCs for now - if (plane->possible_crtcs == (1u << crtc_index)) { - lease->object_ids[lease->nobject_ids++] = plane_id; + if (plane->possible_crtcs & crtc_mask) { + bool shared_plane = plane->possible_crtcs != crtc_mask; + if (allow_shared || !shared_plane) + lease->object_ids[lease->nobject_ids++] = + plane_id; } drmModeFreePlane(plane); } @@ -294,42 +352,97 @@ static void lease_free(struct lease *lease) free(lease); } -static struct lease *lease_create(struct lm *lm, drmModeConnectorPtr connector) +static struct lease *lease_create(struct lm *lm, + const struct lease_config *config) { - struct lease *lease = calloc(1, sizeof(struct lease)); + struct lease *lease; + + if (!config->lease_name) { + ERROR_LOG("Mising lease name\n"); + return NULL; + } + + lease = calloc(1, sizeof(struct lease)); if (!lease) { DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno)); return NULL; } - lease->base.name = drm_create_lease_name(lm, connector); + lease->base.name = strdup(config->lease_name); if (!lease->base.name) { DEBUG_LOG("Can't create lease name: %s\n", strerror(errno)); goto err; } - int nobjects = lm->drm_plane_resource->count_planes + DRM_LEASE_MIN_RES; + int nconnectors = + config->nconnectors > 0 ? config->nconnectors : config->ncids; + int nobjects = lm->drm_plane_resource->count_planes + + nconnectors * DRM_OBJECTS_PER_CONNECTOR; + lease->object_ids = calloc(nobjects, sizeof(uint32_t)); if (!lease->object_ids) { DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno)); goto err; } - int crtc_index = drm_get_crtc_index(lm, connector); - if (crtc_index < 0) { - DEBUG_LOG("No crtc found for connector: %s\n", - lease->base.name); - goto err; - } + for (int i = 0; i < nconnectors; i++) { + uint32_t cid; + struct connector_config *con_config = NULL; - if (!lease_add_planes(lm, lease, crtc_index)) - goto err; + if (config->nconnectors > 0) + con_config = &config->connectors[i]; + + if (con_config) { + char *connector_name = con_config->name; + bool optional = con_config->optional; - uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index]; - lease->crtc_id = crtc_id; - lease->object_ids[lease->nobject_ids++] = crtc_id; - lease->object_ids[lease->nobject_ids++] = connector->connector_id; + bool found = + drm_find_connector(lm, connector_name, &cid); + bool missing_mandatory = !found && !optional; + bool missing_optional = !found && optional; + + if (missing_mandatory) { + ERROR_LOG("Lease: %s, " + "mandatory connector %s not found\n", + config->lease_name, connector_name); + goto err; + } else if (missing_optional) { + WARN_LOG("Lease: %s, " + "unknown DRM connector: %s\n", + config->lease_name, connector_name); + continue; + } + } else { + cid = config->connector_ids[i]; + } + + drmModeConnectorPtr connector = + drmModeGetConnector(lm->drm_fd, cid); + + if (connector == NULL) { + ERROR_LOG("Can't find connector id: %d\n", cid); + goto err; + } + + int crtc_index = drm_get_crtc_index(lm, connector); + + drmModeFreeConnector(connector); + + if (crtc_index < 0) { + DEBUG_LOG("No crtc found for connector: %d, lease %s\n", + cid, lease->base.name); + goto err; + } + + if (!lease_add_planes(lm, lease, crtc_index, con_config)) + goto err; + + uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index]; + lease->crtc_id = crtc_id; + lease->object_ids[lease->nobject_ids++] = crtc_id; + lease->object_ids[lease->nobject_ids++] = cid; + } lease->is_granted = false; lease->lease_fd = -1; @@ -340,7 +453,58 @@ err: return NULL; } -struct lm *lm_create(const char *device) +static void destroy_default_lease_configs(int num_configs, + struct lease_config *configs) +{ + for (int i = 0; i < num_configs; i++) { + free(configs[i].connector_ids); + free(configs[i].lease_name); + } + + free(configs); +} + +static int create_default_lease_configs(struct lm *lm, + struct lease_config **configs) +{ + struct lease_config *def_configs; + int num_configs = lm->drm_resource->count_connectors; + + if (num_configs < 0) + return -1; + + def_configs = calloc(num_configs, sizeof(*def_configs)); + if (!def_configs) { + DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno)); + return -1; + } + + for (int i = 0; i < num_configs; i++) { + uint32_t cid = lm->drm_resource->connectors[i]; + + def_configs[i].connector_ids = malloc(sizeof(uint32_t)); + if (!def_configs[i].connector_ids) { + DEBUG_LOG("Memory allocation failed: %s\n", + strerror(errno)); + goto err; + } + + def_configs[i].lease_name = + drm_create_default_lease_name(lm, i); + + def_configs[i].connector_ids[0] = cid; + def_configs[i].ncids = 1; + } + + *configs = def_configs; + return num_configs; + +err: + destroy_default_lease_configs(num_configs, def_configs); + return -1; +} + +static struct lm *drm_device_get_resources(const char *device) { struct lm *lm = calloc(1, sizeof(struct lm)); if (!lm) { @@ -354,6 +518,13 @@ struct lm *lm_create(const char *device) goto err; } + /* Enable universal planes so that ALL planes, even primary and cursor + * planes can be assigned from lease configurations. */ + if (drmSetClientCap(lm->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { + DEBUG_LOG("drmSetClientCap failed\n"); + goto err; + } + lm->drm_resource = drmModeGetResources(lm->drm_fd); if (!lm->drm_resource) { ERROR_LOG("Invalid DRM device(%s)\n", device); @@ -376,27 +547,65 @@ struct lm *lm_create(const char *device) lm->dev_id = st.st_rdev; - int num_leases = lm->drm_resource->count_connectors; + lm->nconnectors = lm->drm_resource->count_connectors; + lm->connector_names = calloc(lm->nconnectors, sizeof(char *)); + + for (int i = 0; i < lm->nconnectors; i++) { + drmModeConnectorPtr connector; + uint32_t cid = lm->drm_resource->connectors[i]; + + connector = drmModeGetConnector(lm->drm_fd, cid); + lm->connector_names[i] = drm_create_connector_name(connector); + drmModeFreeConnector(connector); + + if (!lm->connector_names[i]) { + DEBUG_LOG("Can't create name for connector %d: %s\n", + cid, strerror(errno)); + goto err; + } + } + return lm; +err: + lm_destroy(lm); + return NULL; +} + +static struct lm *drm_find_drm_device(const char *device) +{ + drmDevicePtr devices[64]; + int ndevs; + struct lm *lm = NULL; + + if (device) + return drm_device_get_resources(device); + + ndevs = drmGetDevices2(0, devices, 64); + + for (int i = 0; i < ndevs; i++) { + lm = drm_device_get_resources( + devices[i]->nodes[DRM_NODE_PRIMARY]); + if (lm) + break; + } + + drmFreeDevices(devices, ndevs); + + return lm; +} +static int lm_create_leases(struct lm *lm, int num_leases, + const struct lease_config *configs) +{ lm->leases = calloc(num_leases, sizeof(struct lease *)); if (!lm->leases) { DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno)); - goto err; + return -1; } drm_find_available_crtcs(lm); for (int i = 0; i < num_leases; i++) { - uint32_t connector_id = lm->drm_resource->connectors[i]; - drmModeConnectorPtr connector = - drmModeGetConnector(lm->drm_fd, connector_id); - - if (!connector) - continue; - - struct lease *lease = lease_create(lm, connector); - drmModeFreeConnector(connector); - + struct lease *lease = lease_create(lm, &configs[i]); if (!lease) continue; @@ -404,13 +613,45 @@ struct lm *lm_create(const char *device) lm->nleases++; } if (lm->nleases == 0) - goto err; + return -1; + + return 0; +} + +struct lm *lm_create_with_config(const char *device, int num_leases, + struct lease_config *configs) +{ + struct lease_config *default_configs = NULL; + struct lm *lm = drm_find_drm_device(device); + + if (!lm) { + ERROR_LOG("No available DRM device found\n"); + return NULL; + } + + if (configs == NULL || num_leases == 0) { + num_leases = create_default_lease_configs(lm, &default_configs); + if (num_leases < 0) { + lm_destroy(lm); + ERROR_LOG("DRM connector enumeration failed\n"); + return NULL; + } + configs = default_configs; + } + + if (lm_create_leases(lm, num_leases, configs) < 0) { + lm_destroy(lm); + lm = NULL; + } + if (default_configs) + destroy_default_lease_configs(num_leases, default_configs); return lm; +} -err: - lm_destroy(lm); - return NULL; +struct lm *lm_create(const char *device) +{ + return lm_create_with_config(device, 0, NULL); } void lm_destroy(struct lm *lm) @@ -425,6 +666,11 @@ void lm_destroy(struct lm *lm) } free(lm->leases); + + for (int i = 0; i < lm->nconnectors; i++) + free(lm->connector_names[i]); + free(lm->connector_names); + drmModeFreeResources(lm->drm_resource); drmModeFreePlaneResources(lm->drm_plane_resource); close(lm->drm_fd); diff --git a/drm-lease-manager/lease-manager.h b/drm-lease-manager/lease-manager.h index 55bf81e..d13ca43 100644 --- a/drm-lease-manager/lease-manager.h +++ b/drm-lease-manager/lease-manager.h @@ -20,6 +20,9 @@ struct lm; struct lm *lm_create(const char *path); +struct lm *lm_create_with_config(const char *path, int leases, + struct lease_config *configs); + void lm_destroy(struct lm *lm); int lm_get_lease_handles(struct lm *lm, struct lease_handle ***lease_handles); diff --git a/drm-lease-manager/main.c b/drm-lease-manager/main.c index 2927253..b4ad379 100644 --- a/drm-lease-manager/main.c +++ b/drm-lease-manager/main.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "lease-config.h" #include "lease-manager.h" #include "lease-server.h" #include "log.h" @@ -27,24 +28,28 @@ static void usage(const char *progname) printf("Usage: %s [OPTIONS] [<DRM device>]\n\n" "Options:\n" "-h, --help \tPrint this help\n" + "-c, --config \t path to configuration file (default " + "/etc/drm-lease-manager.toml)\n" "-v, --verbose \tEnable verbose debug messages\n" "-t, --lease-transfer \tAllow lease transfter to new clients\n" "-k, --keep-on-crash \tDon't close lease on client crash\n", progname); } -const char *opts = "vtkh"; +const char *opts = "vtkhc:"; const struct option options[] = { {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, {"lease-transfer", no_argument, NULL, 't'}, {"keep-on-crash", no_argument, NULL, 'k'}, + {"config", required_argument, NULL, 'c'}, {NULL, 0, NULL, 0}, }; int main(int argc, char **argv) { - char *device = "/dev/dri/card0"; + char *device = NULL; + char *config_file = "/etc/drm-lease-manager.toml"; bool debug_log = false; bool can_transfer_leases = false; @@ -63,6 +68,9 @@ int main(int argc, char **argv) case 'k': keep_on_crash = true; break; + case 'c': + config_file = optarg; + break; case 'h': ret = EXIT_SUCCESS; /* fall through */ @@ -77,7 +85,12 @@ int main(int argc, char **argv) dlm_log_enable_debug(debug_log); - struct lm *lm = lm_create(device); + struct lease_config *lease_configs = NULL; + int num_configs = parse_config(config_file, &lease_configs); + + struct lm *lm = + lm_create_with_config(device, num_configs, lease_configs); + if (!lm) { ERROR_LOG("DRM Lease initialization failed\n"); return EXIT_FAILURE; @@ -145,5 +158,6 @@ int main(int argc, char **argv) done: ls_destroy(ls); lm_destroy(lm); + release_config(num_configs, lease_configs); return EXIT_FAILURE; } diff --git a/drm-lease-manager/meson.build b/drm-lease-manager/meson.build index 7157f01..4732283 100644 --- a/drm-lease-manager/meson.build +++ b/drm-lease-manager/meson.build @@ -1,9 +1,10 @@ lease_manager_files = files('lease-manager.c') lease_server_files = files('lease-server.c') +lease_config_files = files('lease-config.c') main = executable('drm-lease-manager', - [ 'main.c', lease_manager_files, lease_server_files ], - dependencies: [ drm_dep, dlmcommon_dep, thread_dep ], + [ 'main.c', lease_manager_files, lease_server_files, lease_config_files ], + dependencies: [ drm_dep, dlmcommon_dep, thread_dep, toml_dep ], install: true, ) diff --git a/drm-lease-manager/test/lease-config-test.c b/drm-lease-manager/test/lease-config-test.c new file mode 100644 index 0000000..0dc171f --- /dev/null +++ b/drm-lease-manager/test/lease-config-test.c @@ -0,0 +1,140 @@ +/* Copyright 2022 IGEL Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lease-config.h" +#include <check.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define CONFIG_FILE_TEMPLATE "/tmp/dlmconfig_tmpXXXXXX" +int config_fd; +char config_file[] = CONFIG_FILE_TEMPLATE; + +static void test_setup(void) +{ + strcpy(config_file, CONFIG_FILE_TEMPLATE); + config_fd = mkstemp(config_file); + ck_assert_int_ge(config_fd, 0); +} + +static void test_shutdown(void) +{ + close(config_fd); + unlink(config_file); +} + +/* parse config file test */ +/* Test details: Parse a config file + * Expected results: a config with the expected results + */ + +START_TEST(parse_leases) +{ + ck_assert_ptr_ne(config_file, NULL); + + char test_data[] = "[[lease]]\n" + "name = \"lease 1\"\n" + "connectors = [\"1\", \"b\",\"gamma\" ]\n" + "[[lease]]\n" + "name = \"lease 2\"\n" + "connectors = [\"connector 3\"]\n"; + + write(config_fd, test_data, sizeof(test_data)); + + struct lease_config *config = NULL; + int nconfigs = parse_config(config_file, &config); + + ck_assert_int_eq(nconfigs, 2); + ck_assert_ptr_ne(config, NULL); + + ck_assert_str_eq(config[0].lease_name, "lease 1"); + ck_assert_int_eq(config[0].nconnectors, 3); + ck_assert_str_eq(config[0].connectors[0].name, "1"); + ck_assert_str_eq(config[0].connectors[1].name, "b"); + ck_assert_str_eq(config[0].connectors[2].name, "gamma"); + + ck_assert_str_eq(config[1].lease_name, "lease 2"); + ck_assert_int_eq(config[1].nconnectors, 1); + ck_assert_str_eq(config[1].connectors[0].name, "connector 3"); + + release_config(nconfigs, config); +} +END_TEST + +START_TEST(connector_config) +{ + ck_assert_ptr_ne(config_file, NULL); + + char test_data[] = "[[lease]]\n" + "name = \"lease 1\"\n" + "connectors = [\"1\", \"b\",\"gamma\" ]\n" + "[b]\n" + "optional = true\n" + "planes = [1, 4, 3]\n"; + + write(config_fd, test_data, sizeof(test_data)); + + struct lease_config *config = NULL; + int nconfigs = parse_config(config_file, &config); + + ck_assert_int_eq(nconfigs, 1); + ck_assert_ptr_ne(config, NULL); + + ck_assert_str_eq(config[0].lease_name, "lease 1"); + ck_assert_int_eq(config[0].nconnectors, 3); + ck_assert_str_eq(config[0].connectors[0].name, "1"); + ck_assert_str_eq(config[0].connectors[1].name, "b"); + ck_assert_str_eq(config[0].connectors[2].name, "gamma"); + + ck_assert(!config[0].connectors[0].optional); + ck_assert(config[0].connectors[1].optional); + ck_assert(!config[0].connectors[2].optional); + + ck_assert_int_eq(config[0].connectors[1].nplanes, 3); + ck_assert_int_eq(config[0].connectors[1].planes[0], 1); + ck_assert_int_eq(config[0].connectors[1].planes[1], 4); + ck_assert_int_eq(config[0].connectors[1].planes[2], 3); + + release_config(nconfigs, config); +} +END_TEST +static void add_parse_tests(Suite *s) +{ + TCase *tc = tcase_create("Config file parsing tests"); + + tcase_add_checked_fixture(tc, test_setup, test_shutdown); + + tcase_add_test(tc, parse_leases); + tcase_add_test(tc, connector_config); + suite_add_tcase(s, tc); +} + +int main(void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + s = suite_create("DLM lease manager tests"); + + add_parse_tests(s); + + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/drm-lease-manager/test/lease-manager-test.c b/drm-lease-manager/test/lease-manager-test.c index c741cb3..fa87815 100644 --- a/drm-lease-manager/test/lease-manager-test.c +++ b/drm-lease-manager/test/lease-manager-test.c @@ -25,6 +25,8 @@ #include "test-drm-device.h" #include "test-helpers.h" +#define INVALID_OBJECT_ID (0) + /* CHECK_LEASE_OBJECTS * * Checks the list of objects associated with a given lease_index. @@ -33,7 +35,7 @@ #define CHECK_LEASE_OBJECTS(lease, ...) \ do { \ - lm_lease_grant(lm, lease); \ + lm_lease_grant(g_lm, lease); \ uint32_t objs[] = {__VA_ARGS__}; \ int nobjs = ARRAY_LEN(objs); \ ck_assert_int_eq(drmModeCreateLease_fake.arg2_val, nobjs); \ @@ -59,8 +61,10 @@ FAKE_VOID_FUNC(drmModeFreeEncoder, drmModeEncoderPtr); FAKE_VALUE_FUNC(int, drmModeCreateLease, int, const uint32_t *, int, int, uint32_t *); FAKE_VALUE_FUNC(int, drmModeRevokeLease, int, uint32_t); +FAKE_VALUE_FUNC(int, drmSetClientCap, int, uint64_t, uint64_t); /************** Test fixutre functions *************************/ +struct lm *g_lm = NULL; static void test_setup(void) { @@ -86,11 +90,36 @@ static void test_setup(void) drmModeGetConnector_fake.custom_fake = get_connector; drmModeGetEncoder_fake.custom_fake = get_encoder; drmModeCreateLease_fake.custom_fake = create_lease; + + drmSetClientCap_fake.return_val = 0; + + ck_assert_msg(g_lm == NULL, + "Lease manager context not clear at start of test"); } static void test_shutdown(void) { reset_drm_test_device(); + lm_destroy(g_lm); + g_lm = NULL; +} + +static struct lease_handle **create_leases(int num_leases, + struct lease_config *configs) +{ + if (configs) + g_lm = + lm_create_with_config(TEST_DRM_DEVICE, num_leases, configs); + else + g_lm = lm_create(TEST_DRM_DEVICE); + + ck_assert_ptr_ne(g_lm, NULL); + + struct lease_handle **handles; + ck_assert_int_eq(num_leases, lm_get_lease_handles(g_lm, &handles)); + ck_assert_ptr_ne(handles, NULL); + + return handles; } /************** Resource enumeration tests *************/ @@ -114,32 +143,12 @@ START_TEST(all_outputs_connected) { int out_cnt = 2, plane_cnt = 0; - ck_assert_int_eq( - setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true); - - drmModeConnector connectors[] = { - CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1), - CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1), - }; - - drmModeEncoder encoders[] = { - ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x3), - ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2), - }; - - setup_test_device_layout(connectors, encoders, NULL); - - struct lm *lm = lm_create(TEST_DRM_DEVICE); - ck_assert_ptr_ne(lm, NULL); + setup_layout_simple_test_device(out_cnt, plane_cnt); - struct lease_handle **handles; - ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles)); - ck_assert_ptr_ne(handles, NULL); + struct lease_handle **handles = create_leases(out_cnt, NULL); CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0)); CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1)); - - lm_destroy(lm); } END_TEST @@ -170,17 +179,13 @@ START_TEST(no_outputs_connected) setup_test_device_layout(connectors, encoders, NULL); - struct lm *lm = lm_create(TEST_DRM_DEVICE); - ck_assert_ptr_ne(lm, NULL); + g_lm = lm_create(TEST_DRM_DEVICE); + ck_assert_ptr_ne(g_lm, NULL); - struct lease_handle **handles; - ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles)); - ck_assert_ptr_ne(handles, NULL); + struct lease_handle **handles = create_leases(out_cnt, NULL); CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(1), CONNECTOR_ID(0)); CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(0), CONNECTOR_ID(1)); - - lm_destroy(lm); } END_TEST @@ -210,17 +215,10 @@ START_TEST(some_outputs_connected) setup_test_device_layout(connectors, encoders, NULL); - struct lm *lm = lm_create(TEST_DRM_DEVICE); - ck_assert_ptr_ne(lm, NULL); - - struct lease_handle **handles; - ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles)); - ck_assert_ptr_ne(handles, NULL); + struct lease_handle **handles = create_leases(out_cnt, NULL); CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0)); CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1)); - - lm_destroy(lm); } END_TEST @@ -251,16 +249,10 @@ START_TEST(fewer_crtcs_than_connectors) setup_test_device_layout(connectors, encoders, NULL); - struct lm *lm = lm_create(TEST_DRM_DEVICE); - ck_assert_ptr_ne(lm, NULL); - - struct lease_handle **handles; - ck_assert_int_eq(lm_get_lease_handles(lm, &handles), crtc_cnt); - ck_assert_ptr_ne(handles, NULL); + struct lease_handle **handles = create_leases(crtc_cnt, NULL); CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0)); CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(2)); - lm_destroy(lm); } END_TEST @@ -275,39 +267,14 @@ START_TEST(separate_overlay_planes_by_crtc) int out_cnt = 2, plane_cnt = 3; - ck_assert_int_eq( - setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true); - - drmModeConnector connectors[] = { - CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1), - CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1), - }; - - drmModeEncoder encoders[] = { - ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1), - ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2), - }; - - drmModePlane planes[] = { - PLANE(PLANE_ID(0), 0x2), - PLANE(PLANE_ID(1), 0x1), - PLANE(PLANE_ID(2), 0x2), - }; - - setup_test_device_layout(connectors, encoders, planes); + setup_layout_simple_test_device(out_cnt, plane_cnt); - struct lm *lm = lm_create(TEST_DRM_DEVICE); - ck_assert_ptr_ne(lm, NULL); + struct lease_handle **handles = create_leases(out_cnt, NULL); - struct lease_handle **handles; - ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles)); - ck_assert_ptr_ne(handles, NULL); - - CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(1), CRTC_ID(0), + CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(0), PLANE_ID(2), CRTC_ID(0), CONNECTOR_ID(0)); - CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(0), PLANE_ID(2), CRTC_ID(1), + CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(1), CRTC_ID(1), CONNECTOR_ID(1)); - lm_destroy(lm); } END_TEST @@ -344,18 +311,12 @@ START_TEST(reject_planes_shared_between_multiple_crtcs) setup_test_device_layout(connectors, encoders, planes); - struct lm *lm = lm_create(TEST_DRM_DEVICE); - ck_assert_ptr_ne(lm, NULL); - - struct lease_handle **handles; - ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles)); - ck_assert_ptr_ne(handles, NULL); + struct lease_handle **handles = create_leases(out_cnt, NULL); CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(1), CRTC_ID(0), CONNECTOR_ID(0)); CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(0), CRTC_ID(1), CONNECTOR_ID(1)); - lm_destroy(lm); } END_TEST @@ -382,39 +343,63 @@ static void add_connector_enum_tests(Suite *s) */ START_TEST(create_and_revoke_lease) { - int lease_cnt = 2; + int lease_cnt = 2, plane_cnt = 0; + + setup_layout_simple_test_device(lease_cnt, plane_cnt); + + struct lease_handle **handles = create_leases(lease_cnt, NULL); + + for (int i = 0; i < lease_cnt; i++) { + ck_assert_int_ge(lm_lease_grant(g_lm, handles[i]), 0); + lm_lease_revoke(g_lm, handles[i]); + } + + ck_assert_int_eq(drmModeRevokeLease_fake.call_count, lease_cnt); + + for (int i = 0; i < lease_cnt; i++) { + ck_assert_int_eq(drmModeRevokeLease_fake.arg1_history[i], + LESSEE_ID(i)); + } +} +END_TEST + +/* Test lease names */ +/* Test details: Create some leases and verify that they have the correct names + * Expected results: lease names should match the expected values + */ +START_TEST(verify_lease_names) +{ + int lease_cnt = 3; bool res = setup_drm_test_device(lease_cnt, lease_cnt, lease_cnt, 0); ck_assert_int_eq(res, true); drmModeConnector connectors[] = { - CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1), - CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1), + CONNECTOR_FULL(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1, + DRM_MODE_CONNECTOR_HDMIA, 1), + CONNECTOR_FULL(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1, + DRM_MODE_CONNECTOR_LVDS, 3), + CONNECTOR_FULL(CONNECTOR_ID(2), ENCODER_ID(2), &ENCODER_ID(2), 1, + DRM_MODE_CONNECTOR_eDP, 6), }; drmModeEncoder encoders[] = { - ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1), - ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2), + ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x7), + ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x7), + ENCODER(ENCODER_ID(2), CRTC_ID(2), 0x7), }; setup_test_device_layout(connectors, encoders, NULL); - struct lm *lm = lm_create(TEST_DRM_DEVICE); - ck_assert_ptr_ne(lm, NULL); - - struct lease_handle **handles; - ck_assert_int_eq(lease_cnt, lm_get_lease_handles(lm, &handles)); - ck_assert_ptr_ne(handles, NULL); - - for (int i = 0; i < lease_cnt; i++) { - ck_assert_int_ge(lm_lease_grant(lm, handles[i]), 0); - lm_lease_revoke(lm, handles[i]); - } + const char *expected_names[] = { + "card3-HDMI-A-1", + "card3-LVDS-3", + "card3-eDP-6", + }; - ck_assert_int_eq(drmModeRevokeLease_fake.call_count, lease_cnt); + struct lease_handle **handles = create_leases(lease_cnt, NULL); for (int i = 0; i < lease_cnt; i++) { - ck_assert_int_eq(drmModeRevokeLease_fake.arg1_history[i], - LESSEE_ID(i)); + ck_assert_str_eq(handles[i]->name, expected_names[i]); } } END_TEST @@ -426,6 +411,199 @@ static void add_lease_management_tests(Suite *s) tcase_add_checked_fixture(tc, test_setup, test_shutdown); tcase_add_test(tc, create_and_revoke_lease); + tcase_add_test(tc, verify_lease_names); + suite_add_tcase(s, tc); +} + +/***************** Lease Configuration Tests *************/ + +/* multiple_connector_lease */ +/* Test details: Create a lease with multipe connectors + * Expected results: a lease is created with the CRTC and connector ID for both + * connectors. + */ +START_TEST(multiple_connector_lease) +{ + int out_cnt = 2, plane_cnt = 0, lease_cnt = 1; + + setup_layout_simple_test_device(out_cnt, plane_cnt); + + struct lease_config lconfig = { + .lease_name = "Lease Config Test 1", + .ncids = 2, + .connector_ids = (uint32_t[]){CONNECTOR_ID(0), CONNECTOR_ID(1)}, + }; + + struct lease_handle **handles = create_leases(lease_cnt, &lconfig); + + CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0), CRTC_ID(1), + CONNECTOR_ID(1)); +} +END_TEST + +/* single_failed_lease */ +/* Test details: Create 2 lease configs. One with valid data, one without. + * Expected results: A handle is created for the single valid lease. + */ +START_TEST(single_failed_lease) +{ + int out_cnt = 3, plane_cnt = 0, success_lease_cnt = 1; + + setup_layout_simple_test_device(out_cnt, plane_cnt); + + struct lease_config lconfigs[2] = { + [0] = + { + .lease_name = "Lease Config Test 1", + .ncids = 1, + .connector_ids = (uint32_t[]){INVALID_OBJECT_ID}, + }, + [1] = + { + .lease_name = "Lease Config Test 2", + .ncids = 2, + .connector_ids = + (uint32_t[]){CONNECTOR_ID(0), CONNECTOR_ID(1)}, + }, + }; + + /* Expect fewer leases than configurations supplied, so explicitly + * create and check leases. */ + g_lm = lm_create_with_config(TEST_DRM_DEVICE, ARRAY_LEN(lconfigs), + lconfigs); + ck_assert_ptr_ne(g_lm, NULL); + + struct lease_handle **handles; + ck_assert_int_eq(success_lease_cnt, + lm_get_lease_handles(g_lm, &handles)); + ck_assert_ptr_ne(handles, NULL); + + CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0), CRTC_ID(1), + CONNECTOR_ID(1)); +} +END_TEST + +/* named_connector_config */ +/* Test details: Test specifying connectors by name in config + * Expected results: A handle is created for each named connector + */ + +START_TEST(named_connector_config) +{ + int out_cnt = 2, plane_cnt = 0, lease_cnt = 1; + + ck_assert_int_eq( + setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true); + + drmModeConnector connectors[] = { + CONNECTOR_FULL(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1, + DRM_MODE_CONNECTOR_HDMIA, 1), + CONNECTOR_FULL(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1, + DRM_MODE_CONNECTOR_VGA, 3), + }; + + drmModeEncoder encoders[] = { + ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1), + ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2), + }; + + setup_test_device_layout(connectors, encoders, NULL); + + struct lease_config lconfig = { + .lease_name = "Lease Config Test 1", + .nconnectors = 2, + .connectors = + (struct connector_config[]){ + {.name = "HDMI-A-1"}, + {.name = "VGA-3"}, + }, + }; + + struct lease_handle **handles = create_leases(lease_cnt, &lconfig); + + ck_assert_str_eq(handles[0]->name, lconfig.lease_name); + CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0), CRTC_ID(1), + CONNECTOR_ID(1)); +} +END_TEST + +/* config plane sharing */ +/* Test details: Add overlay planes to leases. Some planes are shared between + * multiple CRTCs. These planes are explicitly assigned to a + * connector. Expected results: The leases contain all of the unique planes for + * each CRTC. Shared planes are also included as defined by the lease + * configuration. + */ +START_TEST(config_plane_sharing) +{ + + int out_cnt = 2, plane_cnt = 3, lease_cnt = 2; + + ck_assert_int_eq( + setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true); + + drmModeConnector connectors[] = { + CONNECTOR_FULL(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1, + DRM_MODE_CONNECTOR_HDMIA, 1), + CONNECTOR_FULL(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1, + DRM_MODE_CONNECTOR_VGA, 3), + }; + + drmModeEncoder encoders[] = { + ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1), + ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2), + }; + + drmModePlane planes[] = { + PLANE(PLANE_ID(0), 0x2), + PLANE(PLANE_ID(1), 0x1), + PLANE(PLANE_ID(2), 0x3), + }; + + setup_test_device_layout(connectors, encoders, planes); + + struct lease_config lconfig[] = { + [0] = + { + .lease_name = "Lease Config Test 1", + .nconnectors = 1, + .connectors = + (struct connector_config[]){ + {.name = "HDMI-A-1", + .nplanes = 2, + .planes = (uint32_t[]){PLANE_ID(1), PLANE_ID(2)}}, + }, + }, + [1] = + { + .lease_name = "Lease Config Test 2", + .nconnectors = 1, + .connectors = + (struct connector_config[]){ + {.name = "VGA-3"}, + }, + }, + }; + + struct lease_handle **handles = create_leases(lease_cnt, lconfig); + + CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(1), PLANE_ID(2), CRTC_ID(0), + CONNECTOR_ID(0)); + CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(0), CRTC_ID(1), + CONNECTOR_ID(1)); +} +END_TEST + +static void add_lease_config_tests(Suite *s) +{ + TCase *tc = tcase_create("Lease configuration"); + + tcase_add_checked_fixture(tc, test_setup, test_shutdown); + + tcase_add_test(tc, multiple_connector_lease); + tcase_add_test(tc, single_failed_lease); + tcase_add_test(tc, named_connector_config); + tcase_add_test(tc, config_plane_sharing); suite_add_tcase(s, tc); } @@ -439,6 +617,7 @@ int main(void) add_connector_enum_tests(s); add_lease_management_tests(s); + add_lease_config_tests(s); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); diff --git a/drm-lease-manager/test/meson.build b/drm-lease-manager/test/meson.build index 7d42bec..8d5c06f 100644 --- a/drm-lease-manager/test/meson.build +++ b/drm-lease-manager/test/meson.build @@ -2,6 +2,10 @@ check_dep = dependency('check') ls_inc = include_directories('..') +test_c_args = [ + '-Wno-missing-field-initializers', #not all tests explicitly initialize all lease config fields +] + ls_objects = main.extract_objects(lease_server_files) ls_test_sources = [ 'lease-server-test.c', @@ -12,6 +16,7 @@ ls_test = executable('lease-server-test', sources: ls_test_sources, objects: ls_objects, dependencies: [check_dep, fff_dep, dlmcommon_dep, thread_dep], + c_args: test_c_args, include_directories: ls_inc) lm_objects = main.extract_objects(lease_manager_files) @@ -24,7 +29,20 @@ lm_test = executable('lease-manager-test', sources: lm_test_sources, objects: lm_objects, dependencies: [check_dep, fff_dep, dlmcommon_dep, drm_dep], + c_args: test_c_args, + include_directories: ls_inc) + +lc_objects = main.extract_objects(lease_config_files) +lc_test_sources = [ + 'lease-config-test.c' +] + +lc_test = executable('lease-config-test', + sources: lc_test_sources, + objects: lc_objects, + dependencies: [check_dep, dlmcommon_dep, toml_dep], include_directories: ls_inc) test('DRM Lease manager - socket server test', ls_test, is_parallel: false) test('DRM Lease manager - DRM interface test', lm_test) +test('DRM Lease manager - config parse test', lc_test) diff --git a/drm-lease-manager/test/test-drm-device.c b/drm-lease-manager/test/test-drm-device.c index 844599a..1d1e096 100644 --- a/drm-lease-manager/test/test-drm-device.c +++ b/drm-lease-manager/test/test-drm-device.c @@ -17,6 +17,7 @@ #include <stdbool.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <xf86drmMode.h> #include "test-drm-device.h" @@ -75,6 +76,13 @@ void reset_drm_test_device(void) free(test_device.resources.encoders); free(test_device.plane_resources.planes); free(test_device.leases.lessee_ids); + + if (test_device.layout.free_on_reset) { + free(test_device.layout.connectors); + free(test_device.layout.encoders); + free(test_device.layout.planes); + } + memset(&test_device, 0, sizeof(test_device)); } @@ -86,6 +94,42 @@ void setup_test_device_layout(drmModeConnector *connectors, test_device.layout.planes = planes; } +void setup_layout_simple_test_device(int conn_cnt, int plane_cnt) +{ + drmModeConnector *connectors; + drmModeEncoder *encoders; + drmModePlane *planes = NULL; + + ck_assert_int_ge(conn_cnt, 1); + + setup_drm_test_device(conn_cnt, conn_cnt, conn_cnt, plane_cnt); + + ck_assert_ptr_ne( + connectors = calloc(sizeof(drmModeConnector), conn_cnt), NULL); + ck_assert_ptr_ne(encoders = calloc(sizeof(drmModeEncoder), conn_cnt), + NULL); + + if (plane_cnt > 0) + ck_assert_ptr_ne( + planes = calloc(sizeof(drmModePlane), plane_cnt), NULL); + + int crtc_mask = (1 << conn_cnt) - 1; + for (int i = 0; i < conn_cnt; i++) { + connectors[i] = (drmModeConnector)CONNECTOR( + CONNECTOR_ID(i), ENCODER_ID(i), &ENCODER_ID(i), 1); + encoders[i] = (drmModeEncoder)ENCODER(ENCODER_ID(i), CRTC_ID(i), + crtc_mask); + } + + for (int i = 0; i < plane_cnt; i++) { + planes[i] = + (drmModePlane)PLANE(PLANE_ID(i), 1 << (i % conn_cnt)); + } + + setup_test_device_layout(connectors, encoders, planes); + test_device.layout.free_on_reset = true; +} + #define GET_DRM_RESOURCE_FN(Res, res, RES, container) \ drmMode##Res##Ptr get_##res(int fd, uint32_t id) \ { \ @@ -105,7 +149,6 @@ GET_DRM_RESOURCE_FN(Plane, plane, PLANE, plane_resources) int create_lease(int fd, const uint32_t *objects, int num_objects, int flags, uint32_t *lessee_id) { - UNUSED(fd); UNUSED(objects); UNUSED(num_objects); UNUSED(flags); @@ -118,5 +161,5 @@ int create_lease(int fd, const uint32_t *objects, int num_objects, int flags, test_device.leases.count++; - return 0; + return dup(fd); } diff --git a/drm-lease-manager/test/test-drm-device.h b/drm-lease-manager/test/test-drm-device.h index 1d5b683..897e0f7 100644 --- a/drm-lease-manager/test/test-drm-device.h +++ b/drm-lease-manager/test/test-drm-device.h @@ -30,6 +30,7 @@ struct drm_device { drmModeConnector *connectors; drmModeEncoder *encoders; drmModePlane *planes; + bool free_on_reset; } layout; struct { @@ -44,6 +45,7 @@ extern struct drm_device test_device; bool setup_drm_test_device(int crtcs, int connectors, int encoders, int planes); void setup_test_device_layout(drmModeConnector *connectors, drmModeEncoder *encoders, drmModePlane *planes); +void setup_layout_simple_test_device(int connectors, int planes); void reset_drm_test_device(void); drmModeConnectorPtr get_connector(int fd, uint32_t id); @@ -61,10 +63,14 @@ int create_lease(int fd, const uint32_t *objects, int num_objects, int flags, #define PLANE_ID(x) (test_device.plane_resources.planes[x]) #define LESSEE_ID(x) (test_device.leases.lessee_ids[x]) -#define CONNECTOR(cid, eid, encs, enc_cnt) \ - { \ - .connector_id = cid, .encoder_id = eid, \ - .count_encoders = enc_cnt, .encoders = encs, \ +#define CONNECTOR(cid, eid, encs, enc_cnt) \ + CONNECTOR_FULL(cid, eid, encs, enc_cnt, DRM_MODE_CONNECTOR_Unknown, cid) + +#define CONNECTOR_FULL(cid, eid, encs, enc_cnt, type, type_id) \ + { \ + .connector_id = cid, .encoder_id = eid, \ + .count_encoders = enc_cnt, .encoders = encs, \ + .connector_type = type, .connector_type_id = type_id, \ } #define ENCODER(eid, crtc, crtc_mask) \ diff --git a/meson.build b/meson.build index 7f8adf5..c2c88a6 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,7 @@ configuration_inc = include_directories('.') drm_dep = dependency('libdrm', version: '>= 2.4.89') thread_dep = dependency('threads') +toml_dep = dependency('libtoml') enable_tests = get_option('enable-tests') |