README: rewritten around what runs (Linux software, Xorg, apk, Linux's drivers), the honest gaps, measured numbers. plan/: 1.0.md is the finish line, where each clause of the acceptance test stands, and the register (one failing clause: a root that survives a reboot); built plans (foundations, hybrid, alpine, xorg) and the decided-against one (wayland) are gone, their content lives in architecture/; roadmap.md is only what is still open. guides: building/layout/testing/adding-a-driver (Linux code may be used). history: four entries (ls -lR, TLB shootdowns, MDIO 0x02460246, netlink MSG_TRUNC). Website: cards, stack, download page, docs index. Code comments that pointed at deleted plan docs point at the architecture docs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
109 lines
3.2 KiB
C
109 lines
3.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
//
|
|
// schmode -- change the display mode.
|
|
//
|
|
// docs/architecture/devices.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;
|
|
}
|