Well, honestly, I'm still not sure how I want my driver model to work. Here's what I've been considering:
I've been considering a system not unlike older architectures where access to drivers is memory mapped. For example, if I want to write to disk (which we'll whimsically call device 3), then I'll place in address 3 a position independent pointer to the data I want to copy and a size reference. Instead of the usual trap system, the memory access causes a fault. The buffer pointed to is copied into kernel space and the hardware access continues as planned without requiring further interaction from the program. If I want to know the return value, I'll read from address 3. If the driver hasn't finished, it'll block.
I really like the idea of writing things to memory as opposed to a trap system. It seems like an easier way to handle hardware access.
I suspect a more flexible way to write this would be to have a page which is broken up into structures of three integers and a pointer. The first integer encodes the desired device. The second integer encodes the desired function of the device (read, write, &c). The third integer is a count and the pointer points to a buffer within the process memory space. This also allows the device to return more information.
There is a downside to this approach: Typically the reason NULL and pointers near 0 cause segmentation faults is because the zero page is an inaccessible page by design. In deference to this strategy which nicely traps NULL pointers, I think a different location would be needed. Perhaps an address provided by the operating system? I haven't quite decided yet.