/* * Saitek X52 Pro MFD & LED driver - Client communication library * * Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org) * * SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0 */ #include "build-config.h" #include #include #include #include #include #include #include #include #include #include static int _setup_socket(struct sockaddr_un *remote, int len) { int sock; int saved_errno; /* Create a socket */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { /* Failure creating the socket, abort early */ return -1; } /* Connect to the socket */ if (connect(sock, (struct sockaddr *)remote, (socklen_t)len) == -1) { /* Failure connecting to the socket. Cleanup */ saved_errno = errno; /* close may modify errno, so we save it prior to the call */ close(sock); sock = -1; errno = saved_errno; } return sock; } int x52d_dial_command(const char *sock_path) { int len; struct sockaddr_un remote; len = x52d_setup_command_sock(sock_path, &remote); if (len < 0) { /* Error when setting up sockaddr */ return -1; } return _setup_socket(&remote, len); } int x52d_dial_notify(const char *sock_path) { int len; struct sockaddr_un remote; len = x52d_setup_notify_sock(sock_path, &remote); if (len < 0) { /* Error when setting up sockaddr */ return -1; } return _setup_socket(&remote, len); } const char *x52d_ipc_socket_path(const char *sock_path) { return x52d_ipc_sock_path(sock_path); } int x52d_dial_ipc(const char *sock_path) { const char *path = x52d_ipc_sock_path(sock_path); int fd; fd = lipc_socket_connect(path, LIPC_SOCKET_CLOEXEC); return fd; } int x52d_ipc_device_state_decode(const lipc_header *hdr, const void *payload, size_t payload_len, int *connected, uint16_t *vid, uint16_t *pid, const char **name_utf8, size_t *name_len) { if (!hdr) { return -1; } if (payload_len > 0 && !payload) { return -1; } if (hdr->tid != 0 || hdr->request != X52D_IPC_PUSH_DEVICE_STATE) { return -1; } if (hdr->index > 1u) { return -1; } if (hdr->length != (uint32_t)payload_len) { return -1; } if (connected) { *connected = (hdr->index == 1u) ? 1 : 0; } if (vid || pid) { x52d_ipc_device_state_unpack_usb(hdr->value, vid, pid); } if (name_utf8) { *name_utf8 = (payload_len > 0) ? (const char *)payload : NULL; } if (name_len) { *name_len = payload_len; } return 0; } lipc_status x52d_ipc_call(int fd, uint16_t request_id, uint16_t index, uint64_t value, const void *payload, size_t payload_len, lipc_header *reply_hdr, void *reply_payload, size_t reply_payload_cap, size_t *reply_len) { lipc_client *client; lipc_status st; client = lipc_client_create(0, NULL, NULL); if (!client) { return LIPC_INTERNAL_ERROR; } st = lipc_client_call(client, fd, request_id, index, value, payload, payload_len, reply_hdr, reply_payload, reply_payload_cap, reply_len); lipc_client_destroy(client); return st; } int x52d_format_command(int argc, const char **argv, char *buffer, size_t buflen) { int msglen; int i; if (argc == 0 || argv == NULL || buffer == NULL || buflen < X52D_BUFSZ) { errno = EINVAL; return -1; } memset(buffer, 0, buflen); msglen = 0; for (i = 0; i < argc; i++) { int arglen = strlen(argv[i]) + 1; if ((size_t)(msglen + arglen) >= buflen) { errno = E2BIG; return -1; } memcpy(&buffer[msglen], argv[i], arglen); msglen += arglen; } return msglen; } int x52d_send_command(int sock_fd, char *buffer, size_t bufin, size_t bufout) { int rc; for (;;) { /* * Unix sockets should have sufficient capacity to send the full * datagram in a single message. Assume that is the case. */ rc = send(sock_fd, buffer, bufin, 0); if (rc < 0) { // Error if (errno == EINTR) { // System call interrupted due to signal. Try again continue; } else { // Failed. Return early return -1; } } break; } /* Wait till we get a response */ for (;;) { rc = recv(sock_fd, buffer, bufout, 0); if (rc < 0) { // Error if (errno == EINTR) { // System call interrupted due to signal. Try again continue; } else { // Failed. Return early return -1; } } break; } return rc; } int x52d_recv_notification(int sock_fd, x52d_notify_callback_fn callback) { int rc; char buffer[X52D_BUFSZ]; int argc; char *argv[X52D_BUFSZ]; if (callback == NULL) { errno = EINVAL; return -1; } /* Wait till we get a response */ for (;;) { rc = recv(sock_fd, buffer, sizeof(buffer), 0); if (rc < 0) { // Error if (errno == EINTR) { // System call interrupted due to signal. Try again continue; } else { // Failed. Return early return -1; } } break; } /* Split into individual arguments */ x52d_split_args(&argc, argv, buffer, rc); return callback(argc, argv); }