Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
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/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;
|
|
}
|