Each layer hides the layer below and offers a cleaner service above (top to bottom):
Requests flow down; interrupts/completions flow up. The lowest software layer (interrupt handler) is minimal and fast; the driver holds device knowledge; the device-independent layer gives the logical, device-neutral interface.
DMA: with programmed/interrupt I/O the CPU moves one byte/word at a time and is interrupted per transfer. DMA offloads this: the driver programs the DMA controller (memory address, device address, count, direction) and is free to do other work; the controller moves a whole block between the device buffer and memory using cycle stealing, raising only one interrupt per block. Benefits: higher throughput, far lower CPU overhead, overlap of I/O and computation. Trade-off: bus/memory contention and cache coherence.
Uniform interface: devices differ (block vs char, speed, commands, errors). Without a uniform interface the OS would be unmaintainable. A common interface (open/read/write/close/ioctl; block/char abstractions) gives abstraction, portability, code reuse and separation of concerns; ioctl is the escape hatch.
Key point: DMA removes the CPU from data movement; the uniform interface removes the device's identity from higher-level software.