When device done, the controller causes an interrupt. CPU reads any results from the controller's registers. No interrupts used. CPU puts request in controller's registers and then polls waiting for the request to finish. For example, disk read.
Controller gets the data and buffers it.
Look at Fig. Idea is to free CPU from being involved with transfer of data. It can put the data into the memory directly. Might have to buffer data anyway. Keep it simple by always buffering.