diff options
Diffstat (limited to 'kms/server/kms_server.c')
-rw-r--r-- | kms/server/kms_server.c | 248 |
1 files changed, 159 insertions, 89 deletions
diff --git a/kms/server/kms_server.c b/kms/server/kms_server.c index fbd101e..c585f24 100644 --- a/kms/server/kms_server.c +++ b/kms/server/kms_server.c @@ -1,3 +1,7 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include "../kms_shared.h" #include <stdio.h> @@ -6,6 +10,7 @@ #include <stdlib.h> #include <unistd.h> +#include <limits.h> #include <fcntl.h> #include <sys/socket.h> #include <sys/un.h> @@ -14,17 +19,18 @@ #include <xf86drm.h> #include <xf86drmMode.h> #include <drm_mode.h> +#include <drm_fourcc.h> #define MAX_CONNECTORS 32 typedef struct { int drmfd; - drmModePlaneResPtr planes; } gsr_drm; typedef struct { uint32_t connector_id; uint64_t crtc_id; + uint64_t hdr_metadata_blob_id; } connector_crtc_pair; typedef struct { @@ -36,6 +42,14 @@ static int max_int(int a, int b) { return a > b ? a : b; } +static int count_num_fds(const gsr_kms_response *response) { + int num_fds = 0; + for(int i = 0; i < response->num_items; ++i) { + num_fds += response->items[i].num_dma_bufs; + } + return num_fds; +} + static int send_msg_to_client(int client_fd, gsr_kms_response *response) { struct iovec iov; iov.iov_base = response; @@ -45,21 +59,25 @@ static int send_msg_to_client(int client_fd, gsr_kms_response *response) { response_message.msg_iov = &iov; response_message.msg_iovlen = 1; - char cmsgbuf[CMSG_SPACE(sizeof(int) * max_int(1, response->num_fds))]; + const int num_fds = count_num_fds(response); + char cmsgbuf[CMSG_SPACE(sizeof(int) * max_int(1, num_fds))]; memset(cmsgbuf, 0, sizeof(cmsgbuf)); - if(response->num_fds > 0) { + if(num_fds > 0) { response_message.msg_control = cmsgbuf; response_message.msg_controllen = sizeof(cmsgbuf); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&response_message); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int) * response->num_fds); + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds); int *fds = (int*)CMSG_DATA(cmsg); - for(int i = 0; i < response->num_fds; ++i) { - fds[i] = response->fds[i].fd; + int fd_index = 0; + for(int i = 0; i < response->num_items; ++i) { + for(int j = 0; j < response->items[i].num_dma_bufs; ++j) { + fds[fd_index++] = response->items[i].dma_buf[j].fd; + } } response_message.msg_controllen = cmsg->cmsg_len; @@ -118,18 +136,18 @@ static bool connector_get_property_by_name(int drmfd, drmModeConnectorPtr props, } typedef enum { - PLANE_PROPERTY_X = 1 << 0, - PLANE_PROPERTY_Y = 1 << 1, - PLANE_PROPERTY_SRC_X = 1 << 2, - PLANE_PROPERTY_SRC_Y = 1 << 3, - PLANE_PROPERTY_SRC_W = 1 << 4, - PLANE_PROPERTY_SRC_H = 1 << 5, - PLANE_PROPERTY_IS_CURSOR = 1 << 6, + PLANE_PROPERTY_X = 1 << 0, + PLANE_PROPERTY_Y = 1 << 1, + PLANE_PROPERTY_SRC_X = 1 << 2, + PLANE_PROPERTY_SRC_Y = 1 << 3, + PLANE_PROPERTY_SRC_W = 1 << 4, + PLANE_PROPERTY_SRC_H = 1 << 5, + PLANE_PROPERTY_IS_CURSOR = 1 << 6, + PLANE_PROPERTY_IS_PRIMARY = 1 << 7, } plane_property_mask; /* Returns plane_property_mask */ -static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, bool *is_cursor, int *x, int *y, int *src_x, int *src_y, int *src_w, int *src_h) { - *is_cursor = false; +static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, int *x, int *y, int *src_x, int *src_y, int *src_w, int *src_h) { *x = 0; *y = 0; *src_x = 0; @@ -141,8 +159,9 @@ static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, bool *is_curs drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drmfd, plane_id, DRM_MODE_OBJECT_PLANE); if(!props) - return false; + return property_mask; + // TODO: Dont do this every frame for(uint32_t i = 0; i < props->count_props; ++i) { drmModePropertyPtr prop = drmModeGetProperty(drmfd, props->props[i]); if(!prop) @@ -171,8 +190,10 @@ static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, bool *is_curs } else if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) { const uint64_t current_enum_value = props->prop_values[i]; for(int j = 0; j < prop->count_enums; ++j) { - if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Cursor") == 0) { - *is_cursor = true; + if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Primary") == 0) { + property_mask |= PLANE_PROPERTY_IS_PRIMARY; + break; + } else if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Cursor") == 0) { property_mask |= PLANE_PROPERTY_IS_CURSOR; break; } @@ -186,13 +207,13 @@ static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, bool *is_curs return property_mask; } -/* Returns 0 if not found */ -static uint32_t get_connector_by_crtc_id(const connector_to_crtc_map *c2crtc_map, uint32_t crtc_id) { +/* Returns NULL if not found */ +static const connector_crtc_pair* get_connector_pair_by_crtc_id(const connector_to_crtc_map *c2crtc_map, uint32_t crtc_id) { for(int i = 0; i < c2crtc_map->num_maps; ++i) { if(c2crtc_map->maps[i].crtc_id == crtc_id) - return c2crtc_map->maps[i].connector_id; + return &c2crtc_map->maps[i]; } - return 0; + return NULL; } static void map_crtc_to_connector_ids(gsr_drm *drm, connector_to_crtc_map *c2crtc_map) { @@ -209,8 +230,12 @@ static void map_crtc_to_connector_ids(gsr_drm *drm, connector_to_crtc_map *c2crt uint64_t crtc_id = 0; connector_get_property_by_name(drm->drmfd, connector, "CRTC_ID", &crtc_id); + uint64_t hdr_output_metadata_blob_id = 0; + connector_get_property_by_name(drm->drmfd, connector, "HDR_OUTPUT_METADATA", &hdr_output_metadata_blob_id); + c2crtc_map->maps[c2crtc_map->num_maps].connector_id = connector->connector_id; c2crtc_map->maps[c2crtc_map->num_maps].crtc_id = crtc_id; + c2crtc_map->maps[c2crtc_map->num_maps].hdr_metadata_blob_id = hdr_output_metadata_blob_id; ++c2crtc_map->num_maps; drmModeFreeConnector(connector); @@ -238,21 +263,56 @@ static void drm_mode_cleanup_handles(int drmfd, drmModeFB2Ptr drmfb) { } } -static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response, connector_to_crtc_map *c2crtc_map) { +static bool get_hdr_metadata(int drm_fd, uint64_t hdr_metadata_blob_id, struct hdr_output_metadata *hdr_metadata) { + drmModePropertyBlobPtr hdr_metadata_blob = drmModeGetPropertyBlob(drm_fd, hdr_metadata_blob_id); + if(!hdr_metadata_blob) + return false; + + if(hdr_metadata_blob->length >= sizeof(struct hdr_output_metadata)) + *hdr_metadata = *(struct hdr_output_metadata*)hdr_metadata_blob->data; + + drmModeFreePropertyBlob(hdr_metadata_blob); + return true; +} + +/* Returns the number of drm handles that we managed to get */ +static int drm_prime_handles_to_fds(gsr_drm *drm, drmModeFB2Ptr drmfb, int *fb_fds) { + for(int i = 0; i < GSR_KMS_MAX_DMA_BUFS; ++i) { + if(!drmfb->handles[i]) + return i; + + const int ret = drmPrimeHandleToFD(drm->drmfd, drmfb->handles[i], O_RDONLY, &fb_fds[i]); + if(ret != 0 || fb_fds[i] == -1) + return i; + } + return GSR_KMS_MAX_DMA_BUFS; +} + +static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) { int result = -1; response->result = KMS_RESULT_OK; response->err_msg[0] = '\0'; - response->num_fds = 0; + response->num_items = 0; + + connector_to_crtc_map c2crtc_map; + c2crtc_map.num_maps = 0; + map_crtc_to_connector_ids(drm, &c2crtc_map); + + drmModePlaneResPtr planes = drmModeGetPlaneResources(drm->drmfd); + if(!planes) { + fprintf(stderr, "kms server error: failed to get plane resources, error: %s\n", strerror(errno)); + goto done; + } - for(uint32_t i = 0; i < drm->planes->count_planes && response->num_fds < GSR_KMS_MAX_PLANES; ++i) { + for(uint32_t i = 0; i < planes->count_planes && response->num_items < GSR_KMS_MAX_ITEMS; ++i) { drmModePlanePtr plane = NULL; drmModeFB2Ptr drmfb = NULL; - plane = drmModeGetPlane(drm->drmfd, drm->planes->planes[i]); + plane = drmModeGetPlane(drm->drmfd, planes->planes[i]); if(!plane) { response->result = KMS_RESULT_FAILED_TO_GET_PLANE; - snprintf(response->err_msg, sizeof(response->err_msg), "failed to get drm plane with id %u, error: %s\n", drm->planes->planes[i], strerror(errno)); + snprintf(response->err_msg, sizeof(response->err_msg), "failed to get drm plane with id %u, error: %s\n", planes->planes[i], strerror(errno)); fprintf(stderr, "kms server error: %s\n", response->err_msg); goto next; } @@ -279,41 +339,54 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response, connector_to_crt // TODO: Check if dimensions have changed by comparing width and height to previous time this was called. // TODO: Support other plane formats than rgb (with multiple planes, such as direct YUV420 on wayland). - int fb_fd = -1; - const int ret = drmPrimeHandleToFD(drm->drmfd, drmfb->handles[0], O_RDONLY, &fb_fd); - if(ret != 0 || fb_fd == -1) { + int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0; + plane_property_mask property_mask = plane_get_properties(drm->drmfd, plane->plane_id, &x, &y, &src_x, &src_y, &src_w, &src_h); + if(!(property_mask & PLANE_PROPERTY_IS_PRIMARY) && !(property_mask & PLANE_PROPERTY_IS_CURSOR)) + continue; + + int fb_fds[GSR_KMS_MAX_DMA_BUFS]; + const int num_fb_fds = drm_prime_handles_to_fds(drm, drmfb, fb_fds); + if(num_fb_fds == 0) { response->result = KMS_RESULT_FAILED_TO_GET_PLANE; snprintf(response->err_msg, sizeof(response->err_msg), "failed to get fd from drm handle, error: %s", strerror(errno)); fprintf(stderr, "kms server error: %s\n", response->err_msg); goto cleanup_handles; } - bool is_cursor = false; - int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0; - plane_get_properties(drm->drmfd, plane->plane_id, &is_cursor, &x, &y, &src_x, &src_y, &src_w, &src_h); - - response->fds[response->num_fds].fd = fb_fd; - response->fds[response->num_fds].width = drmfb->width; - response->fds[response->num_fds].height = drmfb->height; - response->fds[response->num_fds].pitch = drmfb->pitches[0]; - response->fds[response->num_fds].offset = drmfb->offsets[0]; - response->fds[response->num_fds].pixel_format = drmfb->pixel_format; - response->fds[response->num_fds].modifier = drmfb->modifier; - response->fds[response->num_fds].connector_id = get_connector_by_crtc_id(c2crtc_map, plane->crtc_id); - response->fds[response->num_fds].is_cursor = is_cursor; - response->fds[response->num_fds].is_combined_plane = false; - if(is_cursor) { - response->fds[response->num_fds].x = x; - response->fds[response->num_fds].y = y; - response->fds[response->num_fds].src_w = 0; - response->fds[response->num_fds].src_h = 0; + const int item_index = response->num_items; + + const connector_crtc_pair *crtc_pair = get_connector_pair_by_crtc_id(&c2crtc_map, plane->crtc_id); + if(crtc_pair && crtc_pair->hdr_metadata_blob_id) { + response->items[item_index].has_hdr_metadata = get_hdr_metadata(drm->drmfd, crtc_pair->hdr_metadata_blob_id, &response->items[item_index].hdr_metadata); + } else { + response->items[item_index].has_hdr_metadata = false; + } + + for(int j = 0; j < num_fb_fds; ++j) { + response->items[item_index].dma_buf[j].fd = fb_fds[j]; + response->items[item_index].dma_buf[j].pitch = drmfb->pitches[j]; + response->items[item_index].dma_buf[j].offset = drmfb->offsets[j]; + } + response->items[item_index].num_dma_bufs = num_fb_fds; + + response->items[item_index].width = drmfb->width; + response->items[item_index].height = drmfb->height; + response->items[item_index].pixel_format = drmfb->pixel_format; + response->items[item_index].modifier = drmfb->flags & DRM_MODE_FB_MODIFIERS ? drmfb->modifier : DRM_FORMAT_MOD_INVALID; + response->items[item_index].connector_id = crtc_pair ? crtc_pair->connector_id : 0; + response->items[item_index].is_cursor = property_mask & PLANE_PROPERTY_IS_CURSOR; + if(property_mask & PLANE_PROPERTY_IS_CURSOR) { + response->items[item_index].x = x; + response->items[item_index].y = y; + response->items[item_index].src_w = 0; + response->items[item_index].src_h = 0; } else { - response->fds[response->num_fds].x = src_x; - response->fds[response->num_fds].y = src_y; - response->fds[response->num_fds].src_w = src_w; - response->fds[response->num_fds].src_h = src_h; + response->items[item_index].x = src_x; + response->items[item_index].y = src_y; + response->items[item_index].src_w = src_w; + response->items[item_index].src_h = src_h; } - ++response->num_fds; + ++response->num_items; cleanup_handles: drm_mode_cleanup_handles(drm->drmfd, drmfb); @@ -325,16 +398,28 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response, connector_to_crt drmModeFreePlane(plane); } - if(response->num_fds > 0) + done: + + if(planes) + drmModeFreePlaneResources(planes); + + if(response->num_items > 0) response->result = KMS_RESULT_OK; if(response->result == KMS_RESULT_OK) { result = 0; } else { - for(int i = 0; i < response->num_fds; ++i) { - close(response->fds[i].fd); + for(int i = 0; i < response->num_items; ++i) { + for(int j = 0; j < response->items[i].num_dma_bufs; ++j) { + gsr_kms_response_dma_buf *dma_buf = &response->items[i].dma_buf[j]; + if(dma_buf->fd > 0) { + close(dma_buf->fd); + dma_buf->fd = -1; + } + } + response->items[i].num_dma_bufs = 0; } - response->num_fds = 0; + response->num_items = 0; } return result; @@ -348,21 +433,11 @@ static double clock_get_monotonic_seconds(void) { return (double)ts.tv_sec + (double)ts.tv_nsec * 0.000000001; } -static void strncpy_safe(char *dst, const char *src, int len) { - int src_len = strlen(src); - int min_len = src_len; - if(len - 1 < min_len) - min_len = len - 1; - memcpy(dst, src, min_len); - dst[min_len] = '\0'; -} - int main(int argc, char **argv) { int res = 0; int socket_fd = 0; gsr_drm drm; drm.drmfd = 0; - drm.planes = NULL; if(argc != 3) { fprintf(stderr, "usage: gsr-kms-server <domain_socket_path> <card_path>\n"); @@ -395,17 +470,6 @@ int main(int argc, char **argv) { fprintf(stderr, "kms server warning: drmSetClientCap DRM_CLIENT_CAP_ATOMIC failed, error: %s. The wrong monitor may be captured as a result\n", strerror(errno)); } - drm.planes = drmModeGetPlaneResources(drm.drmfd); - if(!drm.planes) { - fprintf(stderr, "kms server error: failed to get plane resources, error: %s\n", strerror(errno)); - res = 2; - goto done; - } - - connector_to_crtc_map c2crtc_map; - c2crtc_map.num_maps = 0; - map_crtc_to_connector_ids(&drm, &c2crtc_map); - fprintf(stderr, "kms server info: connecting to the client\n"); bool connected = false; const double connect_timeout_sec = 5.0; @@ -413,7 +477,7 @@ int main(int argc, char **argv) { while(clock_get_monotonic_seconds() - start_time < connect_timeout_sec) { struct sockaddr_un remote_addr = {0}; remote_addr.sun_family = AF_UNIX; - strncpy_safe(remote_addr.sun_path, domain_socket_path, sizeof(remote_addr.sun_path)); + snprintf(remote_addr.sun_path, sizeof(remote_addr.sun_path), "%s", domain_socket_path); // TODO: Check if parent disconnected if(connect(socket_fd, (struct sockaddr*)&remote_addr, sizeof(remote_addr.sun_family) + strlen(remote_addr.sun_path)) == -1) { if(errno == ECONNREFUSED || errno == ENOENT) { @@ -463,7 +527,7 @@ int main(int argc, char **argv) { } if(request.version != GSR_KMS_PROTOCOL_VERSION) { - fprintf(stderr, "kms server error: expected gpu screen recorder protocol version to be %u, but it's %u\n", GSR_KMS_PROTOCOL_VERSION, request.version); + fprintf(stderr, "kms server error: expected gpu screen recorder protocol version to be %u, but it's %u. please reinstall gpu screen recorder\n", GSR_KMS_PROTOCOL_VERSION, request.version); /* if(request.new_connection_fd > 0) close(request.new_connection_fd); @@ -475,7 +539,7 @@ int main(int argc, char **argv) { case KMS_REQUEST_TYPE_REPLACE_CONNECTION: { gsr_kms_response response; response.version = GSR_KMS_PROTOCOL_VERSION; - response.num_fds = 0; + response.num_items = 0; if(request.new_connection_fd > 0) { if(socket_fd > 0) @@ -498,9 +562,9 @@ int main(int argc, char **argv) { case KMS_REQUEST_TYPE_GET_KMS: { gsr_kms_response response; response.version = GSR_KMS_PROTOCOL_VERSION; - response.num_fds = 0; + response.num_items = 0; - if(kms_get_fb(&drm, &response, &c2crtc_map) == 0) { + if(kms_get_fb(&drm, &response) == 0) { if(send_msg_to_client(socket_fd, &response) == -1) fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_GET_KMS request\n"); } else { @@ -508,9 +572,17 @@ int main(int argc, char **argv) { fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_GET_KMS request\n"); } - for(int i = 0; i < response.num_fds; ++i) { - close(response.fds[i].fd); + for(int i = 0; i < response.num_items; ++i) { + for(int j = 0; j < response.items[i].num_dma_bufs; ++j) { + gsr_kms_response_dma_buf *dma_buf = &response.items[i].dma_buf[j]; + if(dma_buf->fd > 0) { + close(dma_buf->fd); + dma_buf->fd = -1; + } + } + response.items[i].num_dma_bufs = 0; } + response.num_items = 0; break; } @@ -518,7 +590,7 @@ int main(int argc, char **argv) { gsr_kms_response response; response.version = GSR_KMS_PROTOCOL_VERSION; response.result = KMS_RESULT_INVALID_REQUEST; - response.num_fds = 0; + response.num_items = 0; snprintf(response.err_msg, sizeof(response.err_msg), "invalid request type %d, expected %d (%s)", request.type, KMS_REQUEST_TYPE_GET_KMS, "KMS_REQUEST_TYPE_GET_KMS"); fprintf(stderr, "kms server error: %s\n", response.err_msg); @@ -531,8 +603,6 @@ int main(int argc, char **argv) { } done: - if(drm.planes) - drmModeFreePlaneResources(drm.planes); if(drm.drmfd > 0) close(drm.drmfd); if(socket_fd > 0) |