// SPDX-License-Identifier: GPL-2.0-or-later // // schmode -- change the display mode. // // docs/plan/hybrid.md milestone H3. The interesting thing about this program // is how little it is: it opens a socket to `vgpud` and writes a line. No // syscall it makes has heard of a GPU, no kernel code runs that knows what a // scanout is, and if the driver on the other end gets the sequence wrong the // worst that happens is that one process dies and schinit starts another. // // That is the whole argument for moving the driver out, stated as a program // rather than as a paragraph. An in-kernel driver could do the same modeset, // and the same mistake would take the machine. // // schmode what the display is doing now // schmode modes what it will accept // schmode 1024 768 set it // schmode 1024x768 the same #include "core/types.h" #include "uapi/user.h" #include "net/socket.h" #define VGPUD_SOCKET "/run/vgpud" static void puts_(const char *s) { int n = 0; while (s[n]) n++; write(1, (void *)s, n); } static int dial(void) { struct sockaddr_un sa; int fd, i; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -1; for (i = 0; i < (int)sizeof(sa.sun_path); i++) sa.sun_path[i] = 0; sa.sun_family = AF_UNIX; for (i = 0; VGPUD_SOCKET[i] && i < (int)sizeof(sa.sun_path) - 1; i++) sa.sun_path[i] = VGPUD_SOCKET[i]; if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { close(fd); return -1; } return fd; } int main(int argc, char *argv[]) { char buf[256]; char line[128]; int fd, n = 0; fd = dial(); if (fd < 0) { // Naming the likely cause rather than the errno. On a machine with no // virtio-gpu, vgpud reports that and exits on purpose -- see // SCHINIT_EXIT_NORESTART -- so "not running" is the ordinary case and // not a fault. puts_("schmode: cannot reach vgpud on " VGPUD_SOCKET "\n"); puts_(" it is not running -- check: cat /proc/schinit\n"); return 1; } // Everything after the program name, rejoined. "1024 768" and "1024x768" // both arrive as one line either way, because the server parses digits and // skips whatever separates them. if (argc < 2) { line[n++] = 'i'; } else { if (argv[1][0] >= '0' && argv[1][0] <= '9') { // A bare size is a set, not a subcommand. line[n++] = 'm'; line[n++] = 'o'; line[n++] = 'd'; line[n++] = 'e'; line[n++] = ' '; } for (int i = 1; i < argc; i++) { if (i > 1 && n < (int)sizeof(line) - 1) line[n++] = ' '; for (int j = 0; argv[i][j] && n < (int)sizeof(line) - 1; j++) line[n++] = argv[i][j]; } } line[n++] = '\n'; if (write(fd, line, n) != n) { puts_("schmode: vgpud would not take that\n"); close(fd); return 1; } // One answer, then leave. The server writes its reply and waits for the // next line; it does not close, so reading to EOF would wait for ever. n = read(fd, buf, sizeof(buf)); if (n > 0) write(1, buf, n); close(fd); return 0; }