1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
//! System call interface for userspace applications. //! //! Drivers implement these interfaces to expose operations to applications. //! //! # System-call Overview //! //! Tock supports four system calls. The `yield` system call is handled entirely //! by the scheduler, while three others are passed along to drivers: //! //! * `subscribe` lets an application pass a callback to the driver to be //! called later, when an event has occurred or data of interest is available. //! //! * `command` tells the driver to do something immediately. //! //! * `allow` provides the driver access to an application buffer. //! //! ## Mapping system-calls to drivers //! //! Each of these three system calls takes at least two parameters. The first is //! a _driver major number_ and tells the scheduler which driver to forward the //! system call to. The second parameters is a _driver minor number_ and is used //! by the driver to differentiate system calls with different driver-specific //! meanings (e.g. `subscribe` to "data ready" vs `subscribe` to "send //! complete"). The mapping between _driver major numbers_ and drivers is //! determined by a particular platform, while the _driver minor number_ is //! driver-specific. //! //! One convention in Tock is that _driver minor number_ 0 for the `command` //! syscall can always be used to determine if the driver is supported by //! the running kernel by checking the return code. If the return value is //! greater than or equal to zero then the driver is present. Typically this is //! implemented by a null command that only returns 0, but in some cases the //! command can also return more information, like the number of supported //! devices (useful for things like the number of LEDs). //! //! # The `yield` System-call //! //! While drivers do not handle the `yield` system call, it is important to //! understand its function and how it interacts with `subscribe`. use returncode::ReturnCode; /// `Driver`s implement the three driver-specific system calls: `subscribe`, /// `command` and `allow`. /// /// See [the module level documentation](index.html) for an overview of how /// system calls are assigned to drivers. pub trait Driver { /// `subscribe` lets an application pass a callback to the driver to be /// called later. This returns `ENOSUPPORT` if not used. /// /// Calls to subscribe should do minimal synchronous work. Instead, they /// should defer most work and returns results to the application via the /// callback. For example, a subscribe call might setup a DMA transfer to /// read from a sensor, and asynchronously respond to the application by /// passing the result to the application via the callback. /// /// Drivers should allow each application to register a single callback for /// each minor number subscription. Thus, a second call to subscribe from /// the same application would replace a previous callback. /// /// This pushes most per-application virtualization to the application /// itself. For example, a timer driver exposes only one timer to each /// application, and the application is responsible for virtualizing that /// timer if it needs to. /// /// The driver should signal success or failure through the sign of the /// return value from `subscribe`. A negative return value signifies an /// error, while positive a return values signifies success. In addition, /// the magnitude of the return value of can signify extra information such /// as error type. #[allow(unused_variables)] fn subscribe(&self, minor_num: usize, callback: ::Callback) -> ReturnCode { ReturnCode::ENOSUPPORT } /// `command` instructs a driver to perform some action synchronously. This /// returns `ENOSUPPORT` if not used. /// /// The return value should reflect the result of an action. For example, /// enabling/disabling a peripheral should return a success or error code. /// Reading the current system time should return the time as an integer. /// /// Commands should not execute long running tasks synchronously. However, /// commands might "kick-off" asynchronous tasks in coordination with a /// `subscribe` call. /// /// All drivers must support the command with `minor_num` 0, and return 0 /// or greater if the driver is supported. This command should not have any /// side effects. This convention ensures that applications can query the /// kernel for supported drivers on a given platform. #[allow(unused_variables)] fn command(&self, minor_num: usize, r2: usize, r3: usize, caller_id: ::AppId) -> ReturnCode { ReturnCode::ENOSUPPORT } /// `allow` lets an application give the driver access to a buffer in the /// application's memory. This returns `ENOSUPPORT` if not used. /// /// The buffer is __shared__ between the application and driver, meaning the /// driver should not rely on the contents of the buffer to remain /// unchanged. #[allow(unused_variables)] fn allow(&self, app: ::AppId, minor_num: usize, slice: ::AppSlice<::Shared, u8>) -> ReturnCode { ReturnCode::ENOSUPPORT } }