Groundwork for usb-tablet, which is the fix for the guest cursor and the
host's never being in the same place: a PS/2 mouse reports how far it
moved, two pointers accumulating deltas independently drift apart, and no
amount of care in the compositor can make them agree. An absolute device
reports where it is, so they coincide by construction.
- the HID driver no longer matches the boot subclass only. usb-tablet
declares subclass 0 and was refused at the door; hid_probe decides
what an interface actually is, and still declines what it cannot make
sense of.
- a non-boot HID interface is taken to be an absolute pointer and is
not asked for boot protocol, which it does not have -- that request
failing is what stopped it binding.
- tablet_report decodes the fixed 6-byte layout (buttons, 16-bit X,
16-bit Y, wheel) and scales the logical range to pixels there,
because it is the only place that knows the range.
- input_mouse_abs reports EV_ABS/ABS_X/ABS_Y so a reader can tell an
absolute position from a delta, and mouse_set_abs places the pointer
rather than nudging it.
- Schcom treats EV_ABS as authoritative and EV_REL as a nudge.
The device binds and identifies correctly: "usb-hid: tablet (absolute) on
slot 1, endpoint 0x81 every 4 ms".
It is NOT finished, and QEMU_INPUT is empty by default because of it. No
reports have yet been observed arriving on the tablet's interrupt
endpoint, so making it the default pointer would trade a cursor that
drifts for one that does not move at all. Opt in with
make qemu QEMU_INPUT='-device qemu-xhci,id=xhci -device usb-tablet,bus=xhci.0'
Next step is to find out why nothing arrives: whether the first transfer
is queued but never completes, whether a completion needs re-arming for
this endpoint, or whether QEMU is not routing motion to the tablet at all
(`info mice`).
bootcheck 108 [ok], schcom-test passes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
484 lines
19 KiB
C
484 lines
19 KiB
C
#pragma once
|
|
|
|
struct buf;
|
|
struct context;
|
|
struct file;
|
|
struct inode;
|
|
struct pipe;
|
|
struct proc;
|
|
struct rtcdate;
|
|
struct spinlock;
|
|
struct sleeplock;
|
|
struct stat;
|
|
struct superblock;
|
|
struct trapframe;
|
|
struct dirent;
|
|
|
|
// entryother.S / syscall.S
|
|
void syscall_entry(void);
|
|
void ignore_sysret(void);
|
|
|
|
// bio.c
|
|
void binit(void);
|
|
struct buf* bread(uint, uint);
|
|
void brelse(struct buf*);
|
|
void bwrite(struct buf*);
|
|
|
|
// console.c
|
|
struct termios;
|
|
void consoleinit(void);
|
|
extern int tty_fg_pgrp;
|
|
// The terminal's line-discipline settings. TCGETS/TCSETS in sys_fs.c are the
|
|
// only callers; the console honours ICANON, ECHO, ECHOE, ECHONL, ISIG,
|
|
// ICRNL, INLCR, IGNCR, ISTRIP and VMIN.
|
|
void cons_get_termios(struct termios *);
|
|
void cons_set_termios(const struct termios *);
|
|
void cprintf(char*, ...);
|
|
void consoleintr(int(*)(void));
|
|
void panic(char*) __attribute__((noreturn));
|
|
|
|
// exec.c
|
|
int exec(char*, char**);
|
|
|
|
// file.c
|
|
struct file* filealloc(void);
|
|
int fdalloc(struct file*);
|
|
void fileclose(struct file*);
|
|
struct file* filedup(struct file*);
|
|
void fileinit(void);
|
|
int fileread(struct file*, char*, int n);
|
|
int file_poll(struct file*, int events);
|
|
int filestat(struct file*, struct stat*);
|
|
int filewrite(struct file*, char*, int n);
|
|
|
|
// fs.c
|
|
void fsinit(int);
|
|
int dirlink(struct inode*, char*, uint64);
|
|
struct inode* dirlookup(struct inode*, char*, uint*);
|
|
// Remove the entry `name` at the cookie dirlookup returned in *poff.
|
|
int dirunlink(struct inode*, char*, uint);
|
|
// 1 if the directory holds no real entries, 0 if it does, -1 on error.
|
|
int dirempty(struct inode*);
|
|
// Fill `out` with getdents64 records starting at *poff, which is an opaque
|
|
// position the filesystem advances. Returns bytes written, 0 at end of
|
|
// directory. The inode must be locked.
|
|
int getdents(struct inode*, char *out, int cap, uint64 *poff);
|
|
// Mount table: a substitution applied during path resolution. Nothing on disk
|
|
// records a mount, so unmounting is forgetting.
|
|
void mountinit(void);
|
|
int mount_add(uint dev, uint64 inum, uint fsdev, uint64 root);
|
|
int mount_del_by_root(uint fsdev, uint64 root);
|
|
struct inode* ialloc(uint, short);
|
|
struct inode* iget(uint, uint64);
|
|
struct inode* idup(struct inode*);
|
|
void iinit(void);
|
|
void ilock(struct inode*);
|
|
void iput(struct inode*);
|
|
void iunlock(struct inode*);
|
|
void iunlockput(struct inode*);
|
|
void iupdate(struct inode*);
|
|
int namecmp(const char*, const char*);
|
|
struct inode* namei(char*);
|
|
struct inode* nameiparent(char*, char*);
|
|
struct inode* nameifrom(struct inode*, char*);
|
|
struct inode* nameiparentfrom(struct inode*, char*, char*);
|
|
int readi(struct inode*, int, uint64, uint64, uint);
|
|
void stati(struct inode*, struct stat*);
|
|
int writei(struct inode*, int, uint64, uint64, uint);
|
|
void itrunc(struct inode*);
|
|
void itrunc_to(struct inode*, uint64);
|
|
unsigned int BKDRHash(char * str);
|
|
uint tree_expand(struct inode *ip, uint off);
|
|
void tree_search(struct inode* dp, char* name, uint* poff, struct dirent* de,uint add);
|
|
void build_dir_tree(struct inode* dp,uint64 pinum);
|
|
struct inode* divesymlink(struct inode *);
|
|
void readsb(int dev, struct superblock *sb);
|
|
|
|
// pci.c -- bus enumeration. Drivers that need to *find* their hardware use
|
|
// these; drivers for things that have lived at a fixed address since 1984
|
|
// (the PIC, the PIT, the legacy IDE ports) do not.
|
|
struct pci_dev;
|
|
struct pci_driver;
|
|
void pci_init(void);
|
|
uint32 pci_read32(uint8, uint8, uint8, uint8);
|
|
uint16 pci_read16(uint8, uint8, uint8, uint8);
|
|
uint8 pci_read8(uint8, uint8, uint8, uint8);
|
|
void pci_write32(uint8, uint8, uint8, uint8, uint32);
|
|
void pci_enable(struct pci_dev *, uint16 bits);
|
|
uint8 pci_find_cap(struct pci_dev *, uint8 id);
|
|
int pci_msi_enable(struct pci_dev *, int vector, int cpu);
|
|
int pci_count(void);
|
|
struct pci_dev *pci_get(int);
|
|
struct pci_dev *pci_find_class(uint8 class, uint8 subclass);
|
|
struct pci_dev *pci_find_id(uint16 vendor, uint16 device);
|
|
void pci_bind(const struct pci_driver *);
|
|
int pci_info(char *buf, int cap);
|
|
|
|
// drivers/registry.c -- the one list of every driver in the kernel.
|
|
void pci_bind_all(void);
|
|
|
|
// xhci.c — `echo u > /proc/sysrq`
|
|
void xhci_dump(void);
|
|
|
|
// ide.c
|
|
void ideinit(void);
|
|
void ideintr(void);
|
|
void iderw(struct buf*);
|
|
// For probes, not for reads: iderw() panics on an absent drive, so anything
|
|
// that is guessing whether a disk exists has to ask here first.
|
|
int ide_disk_present(int dev);
|
|
|
|
// fs/schfs/kmount.c -- SchFS.
|
|
//
|
|
// Objects are named by 64-bit object id, not by a path: resolution is the
|
|
// caller's business, so these compose with a VFS rather than assuming one.
|
|
// Every schfs_k* that changes anything is one transaction and is durable by the
|
|
// time it returns.
|
|
struct schfs_inode;
|
|
void schfs_kmount(uint dev);
|
|
int schfs_kmounted(void);
|
|
uint64 schfs_klookup(const char *path);
|
|
int schfs_kstat(uint64 ino, struct schfs_inode *out);
|
|
long schfs_kread(uint64 ino, uint64 off, void *buf, unsigned n);
|
|
int schfs_kreaddir(uint64 dir,
|
|
int (*fn)(uint64, unsigned short, const char *,
|
|
unsigned, void *),
|
|
void *arg);
|
|
int schfs_kinfo(char *buf, int cap);
|
|
|
|
int schfs_kcreate(uint64 dir, const char *name, unsigned namelen,
|
|
unsigned short type, uint mode,
|
|
unsigned short major, unsigned short minor, uint64 *out);
|
|
long schfs_kwrite(uint64 ino, uint64 off, const void *buf,
|
|
unsigned n);
|
|
int schfs_ktruncate(uint64 ino, uint64 size);
|
|
int schfs_kunlink(uint64 dir, const char *name, unsigned namelen);
|
|
int schfs_klink(uint64 dir, const char *name, unsigned namelen,
|
|
uint64 ino);
|
|
int schfs_krename(uint64 olddir, const char *oldname,
|
|
unsigned oldlen, uint64 newdir, const char *newname,
|
|
unsigned newlen);
|
|
int schfs_ksetattr(uint64 ino, const struct schfs_inode *in);
|
|
int schfs_ksync(void);
|
|
|
|
// Pieces the VFS layer (fs/schfs/vfs.c) needs, where a directory entry and the
|
|
// object it names are separate operations. The VFS keeps the link count.
|
|
uint64 schfs_klookup_at(uint64 dir, const char *name, unsigned len);
|
|
int schfs_kdirempty(uint64 dir);
|
|
int schfs_kalloc_object(unsigned short type, uint mode,
|
|
uint64 *out);
|
|
int schfs_kdiradd(uint64 dir, const char *name, unsigned len,
|
|
uint64 ino, unsigned short type);
|
|
int schfs_kdirdel(uint64 dir, const char *name, unsigned len);
|
|
int schfs_kfree(uint64 ino);
|
|
uint64 schfs_kroot(void);
|
|
|
|
// ramdisk.c
|
|
uint uptime(void);
|
|
|
|
// procfs.c
|
|
int procread(struct inode*, uint, char*, int);
|
|
int procwrite(struct inode*, uint, char*, int);
|
|
// Shared with the /proc filesystem (kernel/fs/procfs.c): one owns the
|
|
// namespace, the other the content.
|
|
int proc_gen_global(int id, char *buf, int cap);
|
|
int proc_write_sysrq(int user_src, uint64 src, uint n);
|
|
void procfs_mount(void);
|
|
|
|
// input.c -- raw evdev event devices
|
|
void inputinit(void);
|
|
int inputread(struct inode*, uint, char*, int);
|
|
int inputwrite(struct inode*, uint, char*, int);
|
|
int inputready(struct inode*);
|
|
void inputclose(struct inode*);
|
|
void inputopen(struct inode*);
|
|
int input_ioctl(struct inode*, int request, addr_t arg);
|
|
int input_grab(int dev, int on);
|
|
int input_is_grabbed(int dev);
|
|
void input_report(int dev, int type, int code, int value);
|
|
// An absolute pointing device (usb-tablet). Coordinates are already in screen
|
|
// pixels; see input_mouse_abs in kernel/dev/input.c for why this is separate
|
|
// from the relative path.
|
|
void input_mouse_abs(int x, int y, int buttons);
|
|
void mouse_set_abs(int x, int y, int buttons);
|
|
void input_sync(int dev);
|
|
int input_ready(int dev);
|
|
void input_ps2_scancode(int raw, int e0);
|
|
void input_key(int hid_usage, int down);
|
|
int input_hid_keycode(int hid_usage);
|
|
void input_mouse(int dx, int dy, int buttons);
|
|
|
|
void ramdiskinit(void);
|
|
void ramdiskrw(struct buf*);
|
|
int ramdisk_present(void);
|
|
|
|
// usb_storage.c
|
|
void usb_storage_rw(struct buf*);
|
|
int usb_storage_present(void);
|
|
void usb_storage_dump(void); // `echo s > /proc/sysrq`
|
|
|
|
// ioapic.c
|
|
void ioapicenable(int irq, int cpu);
|
|
void ioapicroute(int irq, int cpu, int level_low);
|
|
void ioapicmask(int irq);
|
|
extern uchar ioapicid;
|
|
void ioapicinit(void);
|
|
|
|
// dma.c — see include/dma.h for the full contract
|
|
void dma_init(void*, void*);
|
|
void *dma_alloc(uint64 size, uint64 align);
|
|
void dma_free(void*, uint64 size);
|
|
addr_t dma_phys(const void*);
|
|
uint64 dma_free_bytes(void);
|
|
uint64 dma_total_bytes(void);
|
|
|
|
// kalloc.c
|
|
char* kalloc(void);
|
|
uint64 kalloc_free_bytes(void);
|
|
void kfree(char*);
|
|
void kinit1(void*, void*);
|
|
void kinit2(void);
|
|
|
|
// kbd.c
|
|
void kbdintr(void);
|
|
|
|
// mouse.c
|
|
void mouseinit(void);
|
|
void mouseintr(void);
|
|
void mouse_inject(int dx, int dy, int buttons);
|
|
int get_mouse_x(void);
|
|
int get_mouse_y(void);
|
|
uint8 get_mouse_buttons(void);
|
|
|
|
// lapic.c
|
|
void cmostime(struct rtcdate *r);
|
|
|
|
// time.c -- wall-clock time, from the RTC read once at boot plus uptime.
|
|
void time_init(void);
|
|
void wallclock(uint64 *sec, uint64 *usec);
|
|
extern uint64 boot_epoch;
|
|
int cpunum(void);
|
|
extern volatile uint* lapic;
|
|
void lapiceoi(void);
|
|
void lapicinit(void);
|
|
void lapicstartap(uchar, uint);
|
|
void microdelay(int);
|
|
|
|
// log.c
|
|
void initlog(int dev);
|
|
void log_write(struct buf*);
|
|
void begin_op();
|
|
void end_op();
|
|
|
|
// mp.c
|
|
extern int ismp;
|
|
void mpinit(void);
|
|
|
|
// pipe.c
|
|
int pipealloc(struct file**, struct file**);
|
|
void pipeclose(struct pipe*, int);
|
|
int piperead(struct pipe*, char*, int);
|
|
int pipewrite(struct pipe*, char*, int);
|
|
|
|
// net/unix.c
|
|
struct socket;
|
|
void sockinit(void);
|
|
struct socket* socknew(int domain, int type);
|
|
void sockclose(struct socket*);
|
|
void sockdup(struct socket*);
|
|
int sockread(struct socket*, char*, int);
|
|
int sockwrite(struct socket*, char*, int);
|
|
int sockbind(struct socket*, const char *path);
|
|
int socklisten(struct socket*, int backlog);
|
|
struct socket* sockaccept(struct socket*);
|
|
int sockconnect(struct socket*, const char *path);
|
|
int sockreadable(struct socket*);
|
|
int sock_hup(struct socket*);
|
|
int sock_shutdown(struct socket*, int how);
|
|
int sockpair(struct socket **a, struct socket **b);
|
|
const char* sockname(struct socket*);
|
|
|
|
// proc.c
|
|
void exit(int status);
|
|
int fork(void);
|
|
int growproc(int64);
|
|
int kill(int);
|
|
int sigsend_pgrp(int pgid, int sig);
|
|
int proc_pgid(int pid);
|
|
int proc_setpgid(int pid, int pgid);
|
|
int proc_setsid(void);
|
|
int sigsend(int pid, int sig);
|
|
int proc_exists(int);
|
|
int proc_num(void);
|
|
struct procinfo;
|
|
int proc_snapshot(int idx, struct procinfo *out);
|
|
int proc_snapshot_pid(int pid, struct procinfo *out);
|
|
void pinit(void);
|
|
void procdump(void);
|
|
void scheduler(void) __attribute__((noreturn));
|
|
void sched(void);
|
|
void sleep(void*, struct spinlock*);
|
|
int wait(void);
|
|
int waitpid(int, int*, int);
|
|
void wakeup(void*);
|
|
void yield(void);
|
|
void sinit(void);
|
|
void kstack_check_canary(const char *where);
|
|
|
|
// swtch.S
|
|
void swtch(struct context**, struct context*);
|
|
|
|
// spinlock.c
|
|
void acquire(struct spinlock*);
|
|
void getcallerpcs(void*, addr_t*);
|
|
void getstackpcs(addr_t*, addr_t*);
|
|
int holding(struct spinlock*);
|
|
void initlock(struct spinlock*, char*);
|
|
void release(struct spinlock*);
|
|
void pushcli(void);
|
|
void popcli(void);
|
|
|
|
// sleeplock.c
|
|
void acquiresleep(struct sleeplock*);
|
|
void releasesleep(struct sleeplock*);
|
|
int holdingsleep(struct sleeplock*);
|
|
void initsleeplock(struct sleeplock*, char*);
|
|
|
|
// string.c
|
|
int memcmp(const void*, const void*, uint64);
|
|
void* memmove(void*, const void*, uint64);
|
|
void* memset(void*, int, uint64);
|
|
char* safestrcpy(char*, const char*, int);
|
|
int strlen(const char*);
|
|
int strnlen(const char*, int);
|
|
int strncmp(const char*, const char*, uint64);
|
|
char* strncpy(char*, const char*, int);
|
|
|
|
// syscall.c
|
|
void syscall(struct trapframe *);
|
|
void syscallinit(void);
|
|
void deliver_signals(struct trapframe *);
|
|
int argint(int, int*);
|
|
int argptr(int, char**, int);
|
|
int argstr(int, char**);
|
|
int argaddr(int, addr_t*);
|
|
int fetchaddr(addr_t, addr_t*);
|
|
int fetchstr(addr_t, char**);
|
|
int fetchint(addr_t, int*);
|
|
|
|
// trap.c
|
|
void idtinit(void);
|
|
extern uint ticks;
|
|
void tvinit(void);
|
|
extern struct spinlock tickslock;
|
|
|
|
// uart.c
|
|
void uartearlyinit(void);
|
|
void uartinit(void);
|
|
void uartintr(void);
|
|
void uartputc(int);
|
|
|
|
// vga.c
|
|
void vga_init(uint64);
|
|
void vga_draw_pixel(int, int, uint32);
|
|
void vga_draw_char(int, int, uchar, uint32);
|
|
void vga_puts(int, int, char*, uint32);
|
|
void vga_putc(int, uint32);
|
|
void vga_clear(uint32);
|
|
void vga_screen_reset(void);
|
|
void vga_set_bg(uint32);
|
|
// The mode actually in use. The resolution is a boot-time choice now, so no
|
|
// caller may hardcode it.
|
|
uint32 vga_get_width(void);
|
|
uint32 vga_get_height(void);
|
|
// Crash-screen primitives: the panic handler lays itself out rather than
|
|
// printing. See kernel/core/panic.c.
|
|
int vga_ready(void);
|
|
void vga_dims(int *w, int *h);
|
|
int vga_char_w(void);
|
|
int vga_char_h(void);
|
|
// Framebuffer geometry. vga.c reads the VBE mode info, so it is the only
|
|
// thing that knows where the framebuffer is; nobody else may assume.
|
|
uint64 vga_fb_phys(void);
|
|
uint64 vga_fb_size(void);
|
|
uint32 vga_fb_stride(void);
|
|
uint32 vga_fb_bpp(void);
|
|
volatile uchar *vga_fb_ptr(void);
|
|
|
|
// fb.c -- /dev/fb0, major 2.
|
|
void fbclose(struct inode*);
|
|
// Called from sys_mmap: a mapping of /dev/fb0 is a licence to draw with no
|
|
// further syscalls, so it is the last the kernel hears about it.
|
|
void fb_mapped(void);
|
|
int fbread(struct inode*, uint, char*, int);
|
|
int fbwrite(struct inode*, uint, char*, int);
|
|
int fb_ioctl(int request, addr_t arg);
|
|
// dev/disk.c -- /dev/hdN, raw seekable access to a whole disk. The minor is the
|
|
// IDE device number. Writes bypass every filesystem above them.
|
|
int diskread(struct inode*, uint, char*, int);
|
|
int diskwrite(struct inode*, uint, char*, int);
|
|
// drivers/net/e1000 -- raw Ethernet. No protocol stack above it yet, so these
|
|
// are the whole interface: queue a frame, take a frame, and the MAC.
|
|
int e1000_transmit(const void *frame, int len);
|
|
int e1000_receive(void *out, int cap);
|
|
const unsigned char *e1000_mac(void);
|
|
void e1000_selftest(void);
|
|
|
|
// registry.c
|
|
void platform_register_devices(void);
|
|
|
|
void e820_init(void);
|
|
int acpi_init(void);
|
|
void vga_fill_rect(int x, int y, int w, int h, uint32);
|
|
void vga_fill_circle(int cx, int cy, int r, uint32);
|
|
void vga_text_at(int, int, const char*, uint32);
|
|
void vga_draw_logo(int x, int y, uint32 color, uint32 bg);
|
|
void vga_logo_centered(int cy, uint32 color, uint32 bg);
|
|
// Cursor addressing and erasing, for the console's ANSI escape parser.
|
|
int vga_cols(void);
|
|
int vga_rows(void);
|
|
void vga_cursor_get(int *col, int *row);
|
|
void vga_cursor_set(int col, int row);
|
|
void vga_erase_display(int mode);
|
|
void vga_erase_line(int mode);
|
|
|
|
// console.c / panic.c
|
|
void cons_panic_mode(void);
|
|
extern int cons_serial_only;
|
|
void console_takeover(void);
|
|
|
|
// vm.c
|
|
void seginit(void);
|
|
void kvmalloc(void);
|
|
// Map device registers. Returns a kernel address for `size` bytes of physical
|
|
// MMIO at `pa`, or 0 if the window is exhausted. Use this rather than P2V for
|
|
// anything that came out of a BAR: firmware may place a 64-bit BAR far above
|
|
// the 4 GB the direct map covers.
|
|
void* ioremap(uint64 pa, uint64 size);
|
|
pml4e_t* setupkvm(void);
|
|
char* uva2ka(pml4e_t*, char*);
|
|
addr_t allocuvm(pml4e_t*, uint64, uint64);
|
|
addr_t deallocuvm(pml4e_t*, uint64, uint64);
|
|
void freevm(pml4e_t*);
|
|
void inituvm(pml4e_t*, char*, uint64);
|
|
int loaduvm(pml4e_t*, char*, struct inode*, uint64, uint64);
|
|
pml4e_t* copyuvm(pml4e_t*, uint64);
|
|
void switchuvm(struct proc*);
|
|
void switchkvm(void);
|
|
int pipe_poll(struct pipe*, int readable, int writable);
|
|
int console_input_ready(void);
|
|
int copyin(pml4e_t*, addr_t, void*, uint64);
|
|
int copyout(pml4e_t*, addr_t, void*, uint64);
|
|
void vm_unmap_range(pml4e_t*, uint64, uint64);
|
|
// Unmap without freeing: for pages this process does not own — device memory
|
|
// and shared segments. vm_unmap_range() would hand them to kfree.
|
|
void vm_unmap_norelease(pml4e_t*, uint64, uint64);
|
|
// Shared memory (sys_mem.c). shm_ref/shm_detach maintain the attachment
|
|
// count that decides when a segment's pages go back.
|
|
void shm_ref(int id);
|
|
void shm_detach(int id);
|
|
void proc_release_shared(struct proc *);
|
|
int copy_vma_range(pml4e_t *dstpgdir, pml4e_t *srcpgdir, uint64 va, uint64 length, int is_fb);
|
|
void clearpteu(pml4e_t *pgdir, char *uva);
|
|
|
|
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
|