Add ATA PIO IRQ handler, documentation

Still WIP, doesn't look like IRQ handler works yet.
diff --git a/doc/Makefile b/doc/Makefile
index 5afd552..6f05775 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -14,10 +14,15 @@
 
 .PHONY: help Makefile
 
+watch:
+	sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+dox: doxygen
+doxygen:
+	doxygen lisp.doxyfile
+	doxygen kernel.doxyfile
+
 # Catch-all target: route all unknown targets to Sphinx using the new
 # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
 %: Makefile
 	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-watch:
-	sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/doc/architecture.rst b/doc/architecture.rst
index a515f34..b946a54 100644
--- a/doc/architecture.rst
+++ b/doc/architecture.rst
@@ -45,11 +45,8 @@
 interface with a certain device (or class of devices). The relevant fields of
 ``struct pci_device_driver`` are shown here:
 
-.. code-block::
-
-    bool (* supports)(struct pci_device *dev);
-    void (* init)(struct pci_device dev, uchar bus, uchar slot, uchar func);
-    char *generic_name;
+.. doxygenstruct:: pci_device_driver
+    :project: Kernel
 
 A PCI device driver must pass an instance of this structure to
 ``pci_register_device_driver`` (in ``include/kernel/dri/pci/pci.h``. If
diff --git a/doc/kernel.doxyfile b/doc/kernel.doxyfile
index d75e0a8..5d5f1e1 100644
--- a/doc/kernel.doxyfile
+++ b/doc/kernel.doxyfile
@@ -467,7 +467,7 @@
 # normally produced when WARNINGS is set to YES.
 # The default value is: NO.
 
-EXTRACT_ALL            = NO
+EXTRACT_ALL            = YES
 
 # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
 # be included in the documentation.
@@ -862,7 +862,7 @@
 # be searched for input files as well.
 # The default value is: NO.
 
-RECURSIVE              = NO
+RECURSIVE              = YES
 
 # The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
diff --git a/doc/lisp.doxyfile b/doc/lisp.doxyfile
index fad1c79..8b843a2 100644
--- a/doc/lisp.doxyfile
+++ b/doc/lisp.doxyfile
@@ -467,7 +467,7 @@
 # normally produced when WARNINGS is set to YES.
 # The default value is: NO.
 
-EXTRACT_ALL            = NO
+EXTRACT_ALL            = YES
 
 # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
 # be included in the documentation.
@@ -862,7 +862,7 @@
 # be searched for input files as well.
 # The default value is: NO.
 
-RECURSIVE              = NO
+RECURSIVE              = YES
 
 # The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
diff --git a/include/kernel/dri/ata_pio/ata_pio.h b/include/kernel/dri/ata_pio/ata_pio.h
index c509f52..667ed3b 100644
--- a/include/kernel/dri/ata_pio/ata_pio.h
+++ b/include/kernel/dri/ata_pio/ata_pio.h
@@ -29,6 +29,7 @@
 	ATA_PORT_LBA_LOW,
 	ATA_PORT_LBA_MID,
 	ATA_PORT_LBA_HIGH,
+	ATA_PORT_STATUS = ATA_PORT_CMD,
 };
 
 // Commands
@@ -36,12 +37,15 @@
 {
 	ATA_CMD_READ = 0x20,
 	ATA_CMD_WRITE = 0x30,
+	ATA_CMD_IDENTIFY = 0xec,
 };
 
 void ata_pio_wait_bsy();
 void ata_pio_wait_drq();
 void ata_pio_read_sectors(void *buffer, uint lba, uchar num_sectors);
-void ata_pio_write_sectors(uint lba, uchar num_sectors, void *buffer);
+void ata_pio_write_sectors(uint lba, uchar num_sectors, ushort *buffer);
 uint ata_pio_get_error();
 
 void test_ata_pio();
+
+void init_ata_pio();
\ No newline at end of file
diff --git a/include/kernel/io.h b/include/kernel/io.h
index 718bb20..be82998 100644
--- a/include/kernel/io.h
+++ b/include/kernel/io.h
@@ -3,34 +3,95 @@
 #include "kint.h"
 #include "registers.h"
 
+/**
+ * IO port for keyboard commands.
+ */
 #define KBD_CMD_PORT 0x64
+
+/**
+ * IO port for keyboard data
+ */
 #define KBD_DATA_PORT 0x60
 
-struct kbd_scan_code_info
-{
-	bool pressed;
-	bool escape;
-	char key;
-};
-
-extern struct kbd_scan_code_info scan_code_table[0xff];
-
+/**
+ * Write a byte to a port
+ */
 void outb(ushort port, uchar val);
+
+/**
+ * Write a double word to a port.
+ */
 void outl(ushort port, uint val);
+
+/**
+ * Read a byte from a port.
+ */
 uchar inb(ushort port);
+
+/**
+ * Read a word from a port.
+ */
 ushort inw(ushort port);
+
+/**
+ * Write a word to a port.
+ */
+void outw(ushort port, ushort val);
+
+/**
+ * Just waste some time.
+ */
+void nop();
+
+/**
+ * Read a double word from a port
+ */
 uint inl(ushort port);
 
-/* Random string.h stuff, TODO: move to own header */
+/**
+ * Set n bytes of s to c.
+ */
 void *memset(void *s, int c, size_t n);
+
+/**
+ * Copy n bytes from src to dest.
+ */
 void *memcpy(void *dest, const void *src, size_t n);
+
+/**
+ * Copy null terminated string src to dest.
+ */
 void strcpy(char *dest, char *src);
+
+/**
+ * Compare two strings. This might not work like the libc function, so be
+ * careful.
+ * @returns 0 if equal, non-0 otherwise.
+ */
 int strcmp(char *a, char *b);
+/**
+ * @returns the length of null-terminated string a.
+ */
 uint strlen(char *a);
 
 bool isdigit(char c);
+/**
+ * Ignores trailing non-digit characters.
+ * @returns 0 if string is not a valid decimal integer
+ */
 uint parse_int(char *string);
 
+/**
+ * Read a scan code from the keyboard
+ */
 uchar kbd_scan_code();
+
+/**
+ * Keyboard IRQ handler
+ */
 void kbd_handle_input(struct registers *registers);
+
+/**
+ * Set up keyboard driver.
+ */
 void init_kbd();
diff --git a/src/kernel/pic.h b/include/kernel/pic.h
similarity index 100%
rename from src/kernel/pic.h
rename to include/kernel/pic.h
diff --git a/include/kernel/registers.h b/include/kernel/registers.h
index 687bd0e..8abe32f 100644
--- a/include/kernel/registers.h
+++ b/include/kernel/registers.h
@@ -2,6 +2,9 @@
 
 #include "kint.h"
 
+/**
+ * Represents the full execution state before an interrupt.
+ */
 struct registers
 {
 	uint ds;
diff --git a/include/kernel/task.h b/include/kernel/task.h
index 87b412b..b0d0f97 100644
--- a/include/kernel/task.h
+++ b/include/kernel/task.h
@@ -7,6 +7,9 @@
 
 extern bool tasks_initialized;
 
+/**
+ * A process. For now there is only one, the kernel.
+ */
 struct process
 {
 	bool exists;
@@ -20,12 +23,15 @@
 	// NOTE: must be PAGE ALIGNED
 	uint last_stack_pos;
 };
-
+/**
+ * The smallest schedulable unit, a thread of a process.
+ */
 struct task
 {
 	int id;
 	struct process *proc;
-	uint stack_top_p; // stack frame PHYSICAL address
+	/// Physical address of the top of the stack.
+	uint stack_top_p;
 	uint esp, ebp, eip;
 };
 
@@ -53,7 +59,23 @@
 #define TASK_FUNCTION(f) ((task_function_t)(f))
 
 void spawn_thread(task_function_t function, void *data);
+
+/**
+ * Halt the current thread.
+ */
 void kill_this_thread();
+
+/**
+ * Force a task switch. Only call in a safe environment (ISR).
+ */
 extern void switch_task();
+
+/**
+ * Switch to a specific task. Only call in a safe environment (ISR).
+ */
 void switch_to_task(struct task *task);
+
+/**
+ * Internal. Do not call.
+ */
 void _sys_init_tasks_h(struct registers *regs);
diff --git a/include/kernel/vfs.h b/include/kernel/vfs.h
index 1da0d6e..40a8e87 100644
--- a/include/kernel/vfs.h
+++ b/include/kernel/vfs.h
@@ -6,18 +6,27 @@
 
 struct fs_node
 {
-	char name[128]; /* file name */
-	uint inode;     /* identifier */
-	uint flags;     /* type of node */
-	uint mask;      /* permissions */
-	uint gid;       /* group id */
-	uint uid;       /* user id */
-	size_t size;    /* size in bytes */
-	uint dri_res;   /* reserved for driver */
+	/** file name */
+	char name[128];
+	/** identifier */
+	uint inode;
+	/** type of node */
+	uint flags;
+	/** permissions */
+	uint mask;
+	/** group id */
+	uint gid;
+	/** user id */
+	uint uid;
+	/** size in bytes */
+	size_t size;
+	/** reserved for driver */
+	uint dri_res;
 
 	struct fs_vtable *vtable;
 
-	struct fs_node *mount; /* used for mounts */
+	/** used for mounts */
+	struct fs_node *mount;
 };
 
 struct fs_dirent
@@ -26,13 +35,13 @@
 	uint inode;
 };
 
-typedef uint (* fs_read_t)(struct fs_node *node, size_t offset, size_t size, uchar *buffer);
-typedef uint (* fs_write_t)(struct fs_node *node, size_t offset, size_t size, uchar *buffer);
-typedef void (* fs_open_t)(struct fs_node *node);
-typedef void (* fs_close_t)(struct fs_node *node);
+typedef uint (*fs_read_t)(struct fs_node *node, size_t offset, size_t size, uchar *buffer);
+typedef uint (*fs_write_t)(struct fs_node *node, size_t offset, size_t size, uchar *buffer);
+typedef void (*fs_open_t)(struct fs_node *node);
+typedef void (*fs_close_t)(struct fs_node *node);
 
-typedef bool (* fs_readdir_t)(struct fs_node *node, uint index, struct fs_dirent *dirent);
-typedef struct fs_node *(* fs_finddir_t)(struct fs_node *node, char *name);
+typedef bool (*fs_readdir_t)(struct fs_node *node, uint index, struct fs_dirent *dirent);
+typedef struct fs_node *(*fs_finddir_t)(struct fs_node *node, char *name);
 
 struct fs_vtable
 {
@@ -52,8 +61,8 @@
 	FS_BLOCKDEVICE,
 	FS_PIPE,
 	FS_SYMLINK,
-	
-	FS_MOUNT = 8,   /* Can be or'd with others */
+
+	FS_MOUNT = 8, /* Can be or'd with others */
 };
 
 extern struct fs_node root, dev, initrd;
diff --git a/src/kernel/Jmk b/src/kernel/Jmk
index 8beb214..2e842a1 100644
--- a/src/kernel/Jmk
+++ b/src/kernel/Jmk
@@ -53,9 +53,9 @@
 			task_api.o \
 			faults.o \
 			lib(ext2) \
+			lib(ide) \
 			lib(ata_pio) \
-			lib(pci) \
-			lib(ide)
+			lib(pci)
 
 type(custom_link)
 
diff --git a/src/kernel/dri/ata_pio/ata_pio.c b/src/kernel/dri/ata_pio/ata_pio.c
index cf55b51..cd426ef 100644
--- a/src/kernel/dri/ata_pio/ata_pio.c
+++ b/src/kernel/dri/ata_pio/ata_pio.c
@@ -2,6 +2,7 @@
 
 #include <io.h>
 #include <log.h>
+#include <pic.h>
 
 /* TODO: Rewrite all of this to work with dri_ide in the case of multiple
  * devices */
@@ -51,7 +52,7 @@
 	ata_pio_wait_bsy();
 }
 
-void ata_pio_write_sectors(uint lba, uchar num_sectors, void *buffer)
+void ata_pio_write_sectors(uint lba, uchar num_sectors, ushort *buffer)
 {
 	ata_pio_wait_bsy();
 
@@ -60,8 +61,10 @@
 
 	ata_pio_wait_bsy();
 
-	asm volatile("rep outsw" :: "c"(num_sectors * 256), "d"(ATA_PORT_DATA),
-				 "S"(buffer));
+	for (int i = 0; i < (num_sectors * 256); i++)
+	{
+		outw(ATA_PORT_DATA, buffer[i]);
+	}
 }
 
 static void print_buffer()
@@ -89,3 +92,53 @@
 	// ata_pio_read_sectors(test_buffer, 0, 1);
 	// print_buffer();
 }
+
+void ata_pio_handle_irq(struct registers *regs)
+{
+	// TODO: check that this IRQ came from the hard drive
+	
+	// TODO: use a lock and inform the scheduler that the thread waiting for
+	// this can stop sleeping
+	
+	// Acknowledge the IRQ
+	uchar status = inw(ATA_PORT_STATUS);
+
+	kprintf(DEBUG "ATA PIO IRQ received %d (0x%x)\n", status, status);
+}
+
+void init_ata_pio()
+{
+	add_interrupt_handler(46, ata_pio_handle_irq);
+
+/*
+	// TODO: Consider adding this back.
+
+	// 0xA0 for master, 0xB0 for slave
+	outb(ATA_PORT_DRIVE_SEL, 0xA0);
+
+	outb(ATA_PORT_LBA_LOW, 0);
+	outb(ATA_PORT_LBA_MID, 0);
+	outb(ATA_PORT_LBA_HIGH, 0);
+
+	outb(ATA_PORT_CMD, ATA_CMD_IDENTIFY);
+
+	if (inb(ATA_PORT_STATUS))
+	{
+		kprintf(OKAY "ATA drive exists\n");
+
+		// Wait until either DRQ or ERR is set
+		uchar status;
+		while ((status = inb(ATA_PORT_STATUS)) & (ATA_DRQ | ATA_ERR))
+		{
+			if (status & ATA_ERR)
+			{
+
+			}
+		}
+	}
+	else
+	{
+		kprintf(ERROR "ATA drive does not exist\n");
+	}
+*/
+}
diff --git a/src/kernel/dri/fs/ext2/ext2.c b/src/kernel/dri/fs/ext2/ext2.c
index d0dd3bd..3d7b1bc 100644
--- a/src/kernel/dri/fs/ext2/ext2.c
+++ b/src/kernel/dri/fs/ext2/ext2.c
@@ -1,5 +1,5 @@
-#include <dri/fs/ext2/ext2.h>
 #include <dri/ata_pio/ata_pio.h>
+#include <dri/fs/ext2/ext2.h>
 #include <kint.h>
 #include <log.h>
 
@@ -12,11 +12,56 @@
 	return *sb;
 }
 
+uint ext2_num_block_groups(struct ext2_superblock *sb)
+{
+	// This is a mildly janky way of rounding up
+	uint a = (sb->total_blocks - 1) / (sb->blocks_per_block_group + 1);
+	uint b = (sb->total_inodes - 1) / (sb->inodes_per_block_group + 1);
+
+	if (a == b)
+	{
+		return a;
+	}
+	else
+	{
+		kprintf(ERROR "EXT2 cannot find number of block groups, %d and %d "
+					  "should equal.\n",
+				a, b);
+		kpanic("Corrupted filesystem");
+	}
+}
+
+uint ext2_block_size(struct ext2_superblock *sb)
+{
+	return 1024 << sb->block_size_shift;
+}
+
+void ext2_load_block_group_descriptor_table(struct ext2_superblock *sb,
+											uint num_block_groups)
+{
+	/**
+	 * The BGDT (not to be confused with the GDT) is located the block after the
+	 * superblock. On any block size EXCEPT 1024 (the minimum, remember that the
+	 * block size is specified by X where 1024 << X is the real size) this is
+	 * the second block (0-indexed, so 1). On 1024 this is the third block.
+	 */
+	uint bgdt_block = 1;
+	uint block_size = ext2_block_size(sb);
+
+	if (block_size == 1024)
+		bgdt_block = 2;
+
+	kprintf(DEBUG "BGDT block = %d block size = %d\n", bgdt_block, block_size);
+}
+
 void ext2_mount(struct fs_node *where)
 {
 	struct ext2_superblock sb = ext2_read_superblock();
 
-	kprintf(INFO "EXT2 magic = 0x%x\n", sb.signature);
+	kprintf(DEBUG "EXT2 magic = 0x%x\n", sb.signature);
+
+	uint num_block_groups = ext2_num_block_groups(&sb);
+	ext2_load_block_group_descriptor_table(&sb, num_block_groups);
 }
 
 bool ext2_valid_filesystem()
@@ -24,4 +69,4 @@
 	struct ext2_superblock sb = ext2_read_superblock();
 
 	return sb.signature == EXT2_SIGNATURE;
-}
\ No newline at end of file
+}
diff --git a/src/kernel/dri/ide/ide.c b/src/kernel/dri/ide/ide.c
index 0025970..de4144c 100644
--- a/src/kernel/dri/ide/ide.c
+++ b/src/kernel/dri/ide/ide.c
@@ -1,59 +1,65 @@
-#include <dri/ide/ide.h>
-#include <task.h>
 #include <alloc.h>
+#include <dri/ata_pio/ata_pio.h>
+#include <dri/ide/ide.h>
 #include <log.h>
+#include <task.h>
 
 struct ide_thread_data
 {
-    struct pci_device dev;
-    uchar bus, slot, func;
+	struct pci_device dev;
+	uchar bus, slot, func;
 };
 
 bool ide_supports(struct pci_device *dev)
 {
-    return dev->class == 1 && dev->subclass == 1;
+	return dev->class == 1 && dev->subclass == 1;
 }
 
 void ide_print_device(struct ide_device *dev)
 {
-    kprintf(INFO "<ide-device dma=%b>", dev->supports_dma);
+	kprintf(INFO "<ide-device dma=%b>\n", dev->supports_dma);
 }
 
 void ide_thread(struct ide_thread_data *data)
 {
-    kprintf(DEBUG "IDE driver thread starting: device=0x%x\n", data->dev.device_id);
+	struct ide_device dev;
 
-    struct ide_device dev;
+	uchar p = data->dev.prog_if;
+	dev.channel_mode[0] = p & 1;
+	dev.channel_mode_modifiable[0] = p & (1 << 1);
+	dev.channel_mode[1] = p & (1 << 2);
+	dev.channel_mode_modifiable[1] = p & (1 << 3);
+	dev.supports_dma = p & (1 << 7);
 
-    uchar p = data->dev.prog_if;
-    dev.channel_mode[0] = p & 1;
-    dev.channel_mode_modifiable[0] = p & (1 << 1);
-    dev.channel_mode[1] = p & (1 << 2);
-    dev.channel_mode_modifiable[1] = p & (1 << 3);
-    dev.supports_dma = p & (1 << 7);
+	ide_print_device(&dev);
 
-    ide_print_device(&dev);
+	// TODO: pass ATA PIO driver information about the IDE device (i.e. what
+	// ports to use)
+	init_ata_pio();
+
+	kprintf(OKAY "Set up ATA PIO\n");
+
+	free(data);
 }
 
 void ide_init(struct pci_device dev, uchar bus, uchar slot, uchar func)
 {
-    struct ide_thread_data *data = malloc(sizeof(struct ide_thread_data));
-    data->dev = dev;
-    data->bus = bus;
-    data->slot = slot;
-    data->func = func;
+	struct ide_thread_data *data = malloc(sizeof(struct ide_thread_data));
+	data->dev = dev;
+	data->bus = bus;
+	data->slot = slot;
+	data->func = func;
 
-    spawn_thread(TASK_FUNCTION(ide_thread), data);
+	ide_thread(data);
 }
 
 void ide_register()
 {
-    struct pci_device_driver dri =
-    {
-        .supports = ide_supports,
-        .init = ide_init,
-        .generic_name = "IDE Controller",
-    };
+	struct pci_device_driver dri = {
+		.supports = ide_supports,
+		.init = ide_init,
+		.generic_name = "IDE Controller",
+	};
 
-    pci_register_device_driver(dri);
+	pci_register_device_driver(dri);
 }
diff --git a/src/kernel/io.c b/src/kernel/io.c
index c53ef17..0ba13ae 100644
--- a/src/kernel/io.c
+++ b/src/kernel/io.c
@@ -26,22 +26,32 @@
 ushort inw(ushort port)
 {
 	ushort ret;
-	asm volatile("inw %1, %0" : "=a"(ret) : "dN"(port));
+	asm("inw %1, %0" : "=a"(ret) : "dN"(port));
 	return ret;
 }
 
 void outl(ushort port, uint val)
 {
-	asm volatile("outl %1, %0" : : "dN"(port), "a"(val));
+	asm("outl %1, %0" : : "dN"(port), "a"(val));
 }
 
 uint inl(ushort port)
 {
 	uint ret;
-	asm volatile("inl %1, %0" : "=a"(ret) : "dN"(port));
+	asm("inl %1, %0" : "=a"(ret) : "dN"(port));
 	return ret;
 }
 
+void outw(ushort port, ushort val)
+{
+	asm("outw %1, %0" :: "dN"(port), "a"(val));
+}
+
+void __attribute__((noinline)) nop()
+{
+	asm("nop");
+}
+
 void *memset(void *s, int c, size_t n)
 {
 	for (size_t i = 0; i < n; i++)
diff --git a/src/kernel/kbd.h b/src/kernel/kbd.h
index 98d6fd7..805d4ab 100644
--- a/src/kernel/kbd.h
+++ b/src/kernel/kbd.h
@@ -2,15 +2,24 @@
 
 #include "kint.h"
 
+/**
+ * A scan code -> keyboard char mapping
+ */
 struct kbd_scan_codes
 {
 	bool ascii;
 	int symbol;
+	/**
+	 * Uppercase version of symbol
+	 */
 	int up_symbol;
 	uchar prefix;
 	bool brk;
 };
 
+/**
+ * Non-char keyboard keys, assigned values above 7-bit ascii range (>128)
+ */
 enum kbd_keys
 {
 	FIRST_KBD_KEY = 129,
diff --git a/src/kernel/main.c b/src/kernel/main.c
index 04dc9a7..098d37b 100644
--- a/src/kernel/main.c
+++ b/src/kernel/main.c
@@ -84,7 +84,7 @@
 
 	pci_load();
 
-	kprintf(OKAY "Loaded PCI devices\n");
+	kprintf(OKAY "Loaded PCI device drivers\n");
 
 #ifdef TEST_THREADS
 	spawn_thread(other_thread, NULL);
@@ -101,13 +101,13 @@
 	test_ata_pio();
 #endif
 
-#ifdef TEST_EXT2
 	if (ext2_valid_filesystem())
 	{
-		kprintf(INFO "Mounting EXT2 to /\n");
-		ext2_mount(&root);
+		kprintf(OKAY "EXT2 filesystem is valid, again = %b\n", ext2_valid_filesystem());
+
+		//kprintf(INFO "Mounting EXT2 to /\n");
+		//ext2_mount(&root);
 	}
-#endif
 
 	while (true)
 		asm("hlt");
diff --git a/src/kernel/pic.c b/src/kernel/pic.c
index 5d33213..e4ed88c 100644
--- a/src/kernel/pic.c
+++ b/src/kernel/pic.c
@@ -51,6 +51,7 @@
 
 void pic_remap()
 {
+	// I don't remember what this does.
 	outb(0x20, 0x11);
 	outb(0xA0, 0x11);
 	outb(0x21, 0x20);