#include #include #include #include #include #include SCENARIO("Devices allow access to their resources", "[kapi][devices]") { GIVEN("A device without any resources") { auto device = kstd::make_shared("test_device"); THEN("its resources vector is empty") { REQUIRE(device->resources().empty()); } WHEN("trying to get the first MMIO resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("trying to get the first MMIO resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::mmio, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("trying to get the first I/O port resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("trying to get the first I/O port resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::port, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("trying to get the first IRQ resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("trying to get the first IRQ resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::irq, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("trying to get the first DMA resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("trying to get the first DMA resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::dma, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } WHEN("adding an MMIO resource") { auto range = kapi::devices::mmio_range{{}, kstd::units::bytes{100}}; device->set_resources({kapi::devices::resource{range}}); AND_WHEN("trying to get the first MMIO resource via the type-safe accessor") { auto result = device->request_resource(); THEN("the result is a success") { REQUIRE(result); } THEN("the result is the expected MMIO range") { REQUIRE(result == range); } } AND_WHEN("trying to get the first MMIO resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::mmio); THEN("the result is a success") { REQUIRE(result); } THEN("the result is the expected MMIO range") { REQUIRE(result == kapi::devices::resource{range}); REQUIRE(result == range); } } AND_WHEN("trying to get the first I/O port resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first I/O port resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::port, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first IRQ resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first IRQ resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::irq, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first DMA resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first DMA resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::dma, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } } WHEN("adding an I/O port resource") { auto port = kapi::devices::io_port{0x40, 1}; device->set_resources({kapi::devices::resource{port}}); AND_WHEN("trying to get the first MMIO resource via the type-safe accessor") { auto result = device->request_resource(); THEN("the result is en error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first MMIO resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::mmio); THEN("the result is en error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first I/O port resource via the type-safe accessor") { auto result = device->request_resource(); THEN("the result is a success") { REQUIRE(result); } THEN("the result is the expected MMIO range") { REQUIRE(result == port); } } AND_WHEN("trying to get the first I/O port resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::port); THEN("the result is a success") { REQUIRE(result); } THEN("the result is the expected MMIO range") { REQUIRE(result == kapi::devices::resource{port}); REQUIRE(result == port); } } AND_WHEN("trying to get the first IRQ resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first IRQ resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::irq, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first DMA resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first DMA resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::dma, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } } WHEN("adding an interrupt line resource") { auto line = kapi::devices::interrupt_line{128}; device->set_resources({kapi::devices::resource{line}}); AND_WHEN("trying to get the first MMIO resource via the type-safe accessor") { auto result = device->request_resource(); THEN("the result is en error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first MMIO resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::mmio); THEN("the result is en error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first I/O port resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first I/O port resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::port, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first IRQ resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is a success") { REQUIRE(result); } THEN("the result is the expected interrupt line") { REQUIRE(result == line); } } AND_WHEN("trying to get the first IRQ resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::irq, 0); THEN("the result is a success") { REQUIRE(result); } THEN("the result is the expected interrupt line") { REQUIRE(result == kapi::devices::resource{line}); REQUIRE(result == line); } } AND_WHEN("trying to get the first DMA resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first DMA resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::dma, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } } WHEN("adding a DMA channel resource") { auto channel = kapi::devices::dma_channel{1024}; device->set_resources({kapi::devices::resource{channel}}); AND_WHEN("trying to get the first MMIO resource via the type-safe accessor") { auto result = device->request_resource(); THEN("the result is en error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first MMIO resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::mmio); THEN("the result is en error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first I/O port resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first I/O port resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::port, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first IRQ resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first IRQ resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::irq, 0); THEN("the result is an error") { REQUIRE_FALSE(result); } THEN("the error is 'invalid argument'") { REQUIRE(result.error() == kstd::errc::invalid_argument); } } AND_WHEN("trying to get the first DMA resource via the type-safe accessor") { auto result = device->request_resource(0); THEN("the result is a success") { REQUIRE(result); } THEN("the result it the expected DMA channel") { REQUIRE(result == channel); } } AND_WHEN("trying to get the first DMA resource via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::dma, 0); THEN("the result is a success") { REQUIRE(result); } THEN("the result it the expected DMA channel") { REQUIRE(result == kapi::devices::resource{channel}); REQUIRE(result == channel); } } } } GIVEN("A device with one memory-mapped I/O and two I/O port resources") { auto device = kstd::make_shared("test_device"); auto range = kapi::devices::mmio_range{{}, kstd::units::bytes{128}}; auto port_a = kapi::devices::io_port{0x40, 4}; auto port_b = kapi::devices::io_port{0x50, 1}; device->set_resources( {kapi::devices::resource{range}, kapi::devices::resource{port_a}, kapi::devices::resource{port_b}}); THEN("all resources are returned in order") { auto all = device->resources(); REQUIRE(all.size() == 3); REQUIRE(all[0] == range); REQUIRE(all[1] == port_a); REQUIRE(all[2] == port_b); } THEN("requesting the MMIO resource finds it via the type-safe accessor") { auto result = device->request_resource(); REQUIRE(result == range); } THEN("requesting the MMIO resource finds it via the unsafe accessor") { auto result = device->request_resource(kapi::devices::resource_type::mmio); REQUIRE(result == range); } THEN("requesting the I/O port resources finds them at the correct index via the type-safe accessor") { auto result_a = device->request_resource(0); auto result_b = device->request_resource(1); REQUIRE(result_a == port_a); REQUIRE(result_b == port_b); } THEN("requesting the I/O port resources finds them at the correct index via the unsafe accessor") { auto result_a = device->request_resource(kapi::devices::resource_type::port, 0); auto result_b = device->request_resource(kapi::devices::resource_type::port, 1); REQUIRE(result_a == port_a); REQUIRE(result_b == port_b); } THEN("requesting an out of range index of an assigned type fails") { auto result = device->request_resource(1); REQUIRE_FALSE(result); } WHEN("the resources are reassigned") { auto replacement = kapi::devices::dma_channel{1}; device->set_resources({kapi::devices::resource{replacement}}); THEN("only the resources are present") { auto all = device->resources(); REQUIRE(all.size() == 1); REQUIRE(all[0] == replacement); REQUIRE_FALSE(device->request_resource(0)); REQUIRE_FALSE(device->request_resource(0)); REQUIRE_FALSE(device->request_resource(1)); } } } }