UDI Channels

From OSDev.wiki
Jump to navigation Jump to search

What is a UDI Channel?

The UDI Specification details entities called "Channels". Before we begin to explore in more detail what a channel is, you should look at UDI Operation Vectors.

Essentially then, a channel is a single flow of signaling between two drivers regions, a driver and a metalanguage, or a driver and the Management Agent, which is the OS pretty much. Calls into drivers are sent in via channels. A channel defines operation vectors, or entry points for each end of the channel.

For example, let us take the example of an imaginary device that accepts character data and displays it on some screen; better yet, let's call it a terminal device. And Let's say there's a UDI metalanguage which defines communications between an OS and a terminal device, and the API expects ASCII characters (which would be very naive, but we're still using it for the example anyway).

For the OS to call the device, the driver would have to export a set of entry points, much like any other driver. Therefore a set of operations vectors would be defined in the device's metalanguage. Let us do an initial mark-up.

typedef struct
{
	void (*udi_term_send_req)(char *stream, udi_ubit_32_t nchars);
	void (*udi_term_set_colour)(udi_ubit_8_t r, udi_ubit_8_t g, udi_ubit_8_t b);
	void (*udi_term_set_position)(udi_ubit_16_t x, udi_ubit_16_t x);
} udi_term_ops_t;

In your average driver interface, this would be sufficient. However, we must recognize that there are two signal flows running into the driver from the OS here: a control flow and an output flow. Let's begin by isolating this into channels, like the UDI model recommends:

// Ops vectors expected to be implemented by the Driver.
typedef struct
{
	void (*udi_term_set_colour)(udi_ubit_8_t r, udi_ubit_8_t g, udi_ubit_8_t b);
	void (*udi_term_set_position)(udi_ubit_16_t x, udi_ubit_16_t x);
} udi_term_ctl_ops_t;

typedef struct
{
	void (*udi_term_send_req)(char *stream, udi_ubit_32_t nchars);
} udi_term_send_ops_t;

Great. But this is not yet sufficient. What if the device ever needs to contact the OS, and not just the other way around? The *OS* now needs to provide some form of entry point into itself for the driver to call on so that information can be passed to the OS. Let's add the idea of our terminal device being able to take touchscreen-like co-ordinate data, just as an example. So one extra flow of signals is added. But wait!

This operation needs to be an entry into the OS, and not into the driver, not so? This means that either the OS provides a direct syscall for terminal devices to call in, or it provides a...library of sorts which it will place into every terminal driver's address space. This sounds a lot like a...UDI Metalanguage Library! So the UDI Metalanguage for terminal devices would specify the entry points into libuditerm.o which a driver will be able to call. This Metalanguage library is loaded into the address space of every driver which has a 'requires udi_term' in its udiprops. So now we have a set of entry points into the driver, and we're about to add entry points into the library so that the driver can call on the OS.

typedef struct
{
	void (*udi_term_coord_ind_t)(udi_ubit_16_t x, udi_ubit_16_t y, udi_ubit_16_t pressure);
} udi_term_touch_ops_t;

Nice. Now our metalanguage library, which our driver will call, has an entry point for telling our OS the co-ordinates that have been received on the device. However, this is yet another channel of signal flow within our driver, not so? Or more specifically, this signal flow goes into our OS from the driver by way of the Metalanguage library, which will call the embedding OS on the driver's behalf.

Let's re-do all of this yet again:

typedef struct
{
	// Notice the '_t's here now. In the final iteration these will be nothing more than typedefs, etc as seen in the spec.
	void (*udi_term_coord_ind_t)(udi_ubit_16_t x, udi_ubit_16_t y, udi_ubit_16_t pressure);
} udi_term_touch_ops_t;

typedef struct
{
	void (*udi_term_set_colour_t)(udi_ubit_8_t r, udi_ubit_8_t g, udi_ubit_8_t b);
	void (*udi_term_set_position_t)(udi_ubit_16_t x, udi_ubit_16_t x);
} udi_term_ctl_ops_t;

typedef struct
{
	void (*udi_term_send_req_t)(char *stream, udi_ubit_32_t nchars);
} udi_term_send_ops_t;

So there are three channels that we can visibly see of communication between the OS and the driver. Stop. Now we understand the relationship between Metalanguages and Operations Vectors a bit better: A metalanguage enumerates all of the data input and output flows between the driver and the environment and specifies an operations vector, or set of entry points *per signalling channel*. In our terminal driver model, there are, so far, three conceivable channels of signal flow between the driver and the environment. Two of them are flows where the environment calls into the driver, and the third is where the driver must call into the OS to indicate co-ordinate information from a user touching its screen.

But what of UDI's asynchronous model? Every operation in UDI has a corresponding reaction from the child. So when the OS calls into our terminal driver, the call ends there, and then when the driver has completed the request, it call call us and tell us so. So actually, communications along a channel aren't strictly one-way: the call-in direction is generally one way, but the call-back direction will obviously opposite.

So, image the OS calls the terminal driver and asks it to display a string. The OS would select the correct operation function pointer, and call that. Based on how complex the OS's choice of driver execution is, it may be as simple as a normal function call; If the driver is in a separate address space, this would require the call to be queued. It would make sense if the call is queued on the *channel to which it belongs*. This way queued calls are easily organized and channel operations can be cancelled more easily on channel teardown, etc. Now the OS makes sure that the terminal driver is awake. Pre-emption occurs on some random CPU, and the terminal driver's send-request-channel-handling-thread is chosen to run. The send-request channel processing thread checks its queue, decrements the semaphore and pulls an item. Assume the next item in the queue was our send request. The send-request thread enters whatever region handles writing to I/O for the driver, and sends the buffer of characters to the device.

Note that all this time, the calling thread has been put to sleep while its request was queued. Let's say this calling thread was...bash. Now the call has been completed from the OS-side. Now the driver must generate the call into the OS to confirm the transaction. Oh! Our API above does not have a corresponding response entry point into the Metalanguage library for the call-in! We'll deal with that shortly. For now, assume that the necessary vector has been implemented in the Metalanguage library, and this lib is in the driver's address space. Now the driver generates a call into the Metalanguage lib using the function pointer that it would have receieved when it was bound to its metalanguage via the UDI metalanguage bind sequence, which exports metalanguage entry points (that is, UDI library exported routines) to the driver, which will be explained in a separate article. So the driver calls the metalanguage library's call-back entry point, which would call into the OS to say that "The operation is confirmed to be completed". This call takes place, and the metalanguage lib calls into the OS with the control block used to queue the request, as defined by the specification.

Within the control block, the OS has its own opaque set of bytes which it attaches to every request: the Id of the process that made to call; This ID helps the OS know which process to wake when the confirmation call from the driver comes in. The OS reads the control block, and wakes up the calling thread, bash. Bash is now scheduled on the run queue, and when a CPU pulls it, it returns from the syscall, and life goes on for Bash.

Now let us finally, and once and for all define UDI-acceptable API from our highly inadequate example API above.

[ARTICLE INCOMPLETE]