Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
This commit is contained in:
commit
d0f7acf9eb
275 changed files with 51417 additions and 0 deletions
219
cmd/fbinfo/main.c
Normal file
219
cmd/fbinfo/main.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// fbinfo -- ask /dev/fb0 what it is, and optionally draw on it.
|
||||
//
|
||||
// This exists because the framebuffer is the one device whose correctness you
|
||||
// cannot check by reading kernel source: the numbers have to come back from
|
||||
// the hardware the bootloader actually programmed. Three places in the kernel
|
||||
// used to assume the framebuffer lived at 0xfd000000 and was 800x600x32; they
|
||||
// agreed with QEMU's stdvga by luck, and nothing would have told us when the
|
||||
// luck ran out. Now `fbinfo` would.
|
||||
//
|
||||
// fbinfo print the geometry
|
||||
// fbinfo fill draw a gradient over the whole screen through mmap
|
||||
// fbinfo bars draw colour bars through write(), no mmap involved
|
||||
//
|
||||
// The two drawing modes take different paths on purpose. If `fill` works and
|
||||
// `bars` does not, the mapping is right and the device's read/write is wrong;
|
||||
// if it is the other way round, the reverse. Having both makes a failure say
|
||||
// which half broke.
|
||||
|
||||
#include "types.h"
|
||||
#include "user.h"
|
||||
#include "fcntl.h"
|
||||
#include "fb.h"
|
||||
|
||||
#define PROT_READ 0x1
|
||||
#define PROT_WRITE 0x2
|
||||
#define MAP_SHARED 0x01
|
||||
|
||||
static void
|
||||
puts(const char *s)
|
||||
{
|
||||
int n = 0;
|
||||
while (s[n]) n++;
|
||||
write(1, (void*)s, n);
|
||||
}
|
||||
|
||||
// Unsigned decimal. No printf in lib/, and pulling one in for six numbers
|
||||
// would be a worse trade than fourteen lines.
|
||||
static void
|
||||
putu(unsigned long v)
|
||||
{
|
||||
char buf[24];
|
||||
int i = 0;
|
||||
|
||||
if (v == 0) {
|
||||
write(1, "0", 1);
|
||||
return;
|
||||
}
|
||||
while (v > 0 && i < (int)sizeof(buf)) {
|
||||
buf[i++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (i > 0) {
|
||||
i--;
|
||||
write(1, &buf[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
puthex(unsigned long v)
|
||||
{
|
||||
static const char d[] = "0123456789abcdef";
|
||||
char buf[16];
|
||||
int i = 0;
|
||||
|
||||
write(1, "0x", 2);
|
||||
if (v == 0) {
|
||||
write(1, "0", 1);
|
||||
return;
|
||||
}
|
||||
while (v > 0 && i < (int)sizeof(buf)) {
|
||||
buf[i++] = d[v & 0xf];
|
||||
v >>= 4;
|
||||
}
|
||||
while (i > 0) {
|
||||
i--;
|
||||
write(1, &buf[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Name, then spaces out to a fixed column. Padding inside each string
|
||||
// literal is what produced "bits_per_pixel32": the longest name happened to
|
||||
// be exactly as wide as the column, so it got no separator at all and the
|
||||
// value ran straight into it.
|
||||
static void
|
||||
label(const char *name)
|
||||
{
|
||||
int n = 0;
|
||||
while (name[n]) n++;
|
||||
write(1, (void*)name, n);
|
||||
while (n < 16) { write(1, " ", 1); n++; }
|
||||
}
|
||||
|
||||
static void
|
||||
field(const char *name, unsigned long v)
|
||||
{
|
||||
label(name);
|
||||
putu(v);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
chanline(const char *name, struct fb_bitfield *b)
|
||||
{
|
||||
label(name);
|
||||
puts("offset ");
|
||||
putu(b->offset);
|
||||
puts(", length ");
|
||||
putu(b->length);
|
||||
if (b->length == 0)
|
||||
puts(" (absent)");
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct fb_var_screeninfo var;
|
||||
struct fb_fix_screeninfo fix;
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/fb0", O_RDWR);
|
||||
if (fd < 0) {
|
||||
puts("fbinfo: cannot open /dev/fb0\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &var) < 0) {
|
||||
puts("fbinfo: FBIOGET_VSCREENINFO failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fix) < 0) {
|
||||
puts("fbinfo: FBIOGET_FSCREENINFO failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
label("id");
|
||||
puts(fix.id);
|
||||
puts("\n");
|
||||
field("xres", var.xres);
|
||||
field("yres", var.yres);
|
||||
field("bits_per_pixel", var.bits_per_pixel);
|
||||
label("smem_start");
|
||||
puthex(fix.smem_start);
|
||||
puts("\n");
|
||||
field("smem_len", fix.smem_len);
|
||||
field("line_length", fix.line_length);
|
||||
|
||||
// The stride is the number worth staring at. A card is free to pad
|
||||
// each row, and a program that computes row starts as
|
||||
// y * xres * bytes_per_pixel instead of y * line_length draws a
|
||||
// picture that shears progressively down the screen.
|
||||
if (fix.line_length != var.xres * (var.bits_per_pixel / 8)) {
|
||||
label("note");
|
||||
puts("stride is padded; rows are not xres*bpp/8 apart\n");
|
||||
}
|
||||
|
||||
chanline("red", &var.red);
|
||||
chanline("green", &var.green);
|
||||
chanline("blue", &var.blue);
|
||||
chanline("transp", &var.transp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argv[1][0] == 'f') { // fill
|
||||
unsigned char *fb = mmap(0, fix.smem_len,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (fb == 0 || (long)fb < 0) {
|
||||
puts("fbinfo: mmap failed\n");
|
||||
return 1;
|
||||
}
|
||||
unsigned bpp = var.bits_per_pixel / 8;
|
||||
for (unsigned y = 0; y < var.yres; y++) {
|
||||
unsigned char *row = fb + (unsigned long)y * fix.line_length;
|
||||
for (unsigned x = 0; x < var.xres; x++) {
|
||||
unsigned r = (x * 255) / (var.xres ? var.xres : 1);
|
||||
unsigned g = (y * 255) / (var.yres ? var.yres : 1);
|
||||
unsigned long px = ((unsigned long)r << var.red.offset) |
|
||||
((unsigned long)g << var.green.offset) |
|
||||
((unsigned long)0x80 << var.blue.offset);
|
||||
unsigned char *p = row + (unsigned long)x * bpp;
|
||||
for (unsigned b = 0; b < bpp; b++)
|
||||
p[b] = (unsigned char)(px >> (b * 8));
|
||||
}
|
||||
}
|
||||
puts("fbinfo: gradient drawn through mmap\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argv[1][0] == 'b') { // bars
|
||||
// One scanline at a time through write(). No mapping: this is the
|
||||
// path that used to be devsw's nullwrite, i.e. a silent no-op.
|
||||
static unsigned char row[8192];
|
||||
unsigned bpp = var.bits_per_pixel / 8;
|
||||
unsigned len = fix.line_length;
|
||||
if (len > sizeof(row))
|
||||
len = sizeof(row);
|
||||
|
||||
for (unsigned y = 0; y < var.yres; y++) {
|
||||
unsigned band = (y * 8) / (var.yres ? var.yres : 1);
|
||||
unsigned long px =
|
||||
((unsigned long)((band & 1) ? 255 : 0) << var.red.offset) |
|
||||
((unsigned long)((band & 2) ? 255 : 0) << var.green.offset) |
|
||||
((unsigned long)((band & 4) ? 255 : 0) << var.blue.offset);
|
||||
for (unsigned x = 0; x * bpp + bpp <= len; x++)
|
||||
for (unsigned b = 0; b < bpp; b++)
|
||||
row[x * bpp + b] = (unsigned char)(px >> (b * 8));
|
||||
if (write(fd, row, len) != (int)len) {
|
||||
puts("fbinfo: short write to /dev/fb0\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
puts("fbinfo: colour bars drawn through write()\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
puts("usage: fbinfo [fill|bars]\n");
|
||||
return 1;
|
||||
}
|
||||
Loading…
Reference in a new issue