6. Functions' Specification

As stated above, one of the goals of this lab is to develop a generic module that will be used to create a library, which you will be able to use in the course's project. In this lab, we specify the API, but we want you to play a more active role in future labs: rather than being just an implementer of an API, we want you to design an API.

We'll use this lab to show you by example how you can develop a fairly general API, which can be used to operate on the timer/counter, and that you can use to implement the testing functions that we will use to test your code. In the next labs, we'll just specify the testing functions, as we have done above, and it will be up to you to design the API for operating on the I/O devices.

6.1 timer_test_config()

The purpose of this function is to test your code that displays the status/configuration of a timer. This test function is a simple wrapper that calls directly the function that displays in a human friendly way the status/configuration of a timer. Although such a function is fairly simple, we will implement it in two steps, and therefore, in addition to the function that shows the configuration of a timer, we specify a function that reads the configuration of a timer:

int timer_get_conf(unsigned char timer, unsigned char *st) int timer_display_conf(unsigned char conf)

Both functions should be implemented also in timer.c. (You may wish to use the file timer.c, which includes stubs for these functions.) The following paragraphs describe these functions. You can also read their doxygen documentation.

The timer_get_conf() function should read the configuration of a timer as described in Subsection 3.3. It needs not parse the value read from the status register. This function could be designed as a C API to the Read-Back command, allowing to read the configuration of not only a timer but also of several timers and also the counting values of those timers. However, we have decided not to do it because that functionality is seldom needed.

The timer_display_conf() function should parse the value passed in its argument, which is assumed to be the configuration of a timer as returned by the read-back command, an display it in a human friendly way.

6.2 timer_test_time_base()

The purpose of this function is to test your code to configure timer 0 to generate a time base with a given frequency.

As mentioned above, Minix 3 uses Timer 0 as a time reference to maintain the time of the day and for implementing SW timers (for measuring the duration of time intervals). Thus, when it starts up, it configures Timer 0 to generate interrupts at a fixed rate, by default 60 Hz. Then, on every Timer 0 interrupt, Minix 3 increments a counter variable, which it uses to measure the time -- it expects that counter to be incremented by 60 every second.

timer_test_time_base() should configure Timer 0 to interrupt with a frequency equal to its argument. Thus, if, for example, it is invoked with an argument of 30 (meaning 30 Hz), Timer 0 counter will be incremented by 30 every second rather than by 60. As a result Minix's time of day will rapidly become late, as you'll be able to check by giving the command date

$ date

in Minix and by comparing its output with that generated by running the same command in Linux, i.e. the host OS.

You could easily implement this function by loading the appropriate value into Timer 0's counter. However, this would hardly be reusable (except by cut and pasting). It would not allow us to control, for example, other timers. What we want is that you implement a more general function that is able to change the frequency of any timer:

int timer_set_frequency(unsigned char timer, unsigned long freq)

Function timer_test_time_base() needs only to call timer_set_frequency() with the appropriate arguments. Function timer_set_frequency() should also be implemented in timer.c. (You may wish to use the file timer.c, which includes stubs for this function.) For more information about it, please read its doxygen documentation.

IMP: Make sure that you do not change the 4 least significant bits (i.e. the counting mode and BCD vs. binary couting) of the control world, because that may change the timer's operating mode. (This requires you to read the input timer configuration before you change it.)

6.3 timer_test_int()

The purpose of this function is to test your code that handles interrupts generated by Timer 0. With that in mind, it should print a message on every second, using the function printf(), for a time interval whose duration is specified in its argument. These messages will appear on the console and will be appended to the file /usr/log/messages. Thus, if you are on a terminal different from the console, you can still see these messages by giving the command:

$ tail -f /usr/log/messages

Again, you could implement the entire functionality required-- subscribing an interrupt, handling the interrupts and unsubscribing an interrupt -- inside timer_test_int(), by invoking the right Minix 3 kernel calls. Again, that would not be reusable. More interesting is to design functions that you can use later in the project:

void timer_int_handler() int timer_subscribe_int() int timer_unsubscribe_int()

which should be implemented in a file named timer.c. (You may wish to use the file timer.c, which includes stubs for these functions.) The following paragraphs describe these functions. You can also read their doxygen documentation.

As stated above the main use for the periodic interrupt generated by Timer 0 is as a time reference that can be used to measure time. This requires incrementing a counter on every interrupt. Thus all timer_int_handler() needs to do is to increment a global counter variable.

The implementation of timer_subscribe_int() is straightforward. You must call the sys_irqsetpolicy() and the sys_irqenable() calls, described in the paragraph on interrupt subscription in Subsection 5.3 . The policy you should specify in sys_irqsetpolicy() is IRQ_REENABLE, so that the generic interrupt handler will acknowledge the interrupt, i.e. ouput the EOI command to the PIC, thus enabling further interrupts on the corresponding IRQ line. The implementation of timer_unsusbscribe_int() is even simpler.

Although these functions can be reused to (un)subscribe Timer 0 interrupts, they cannot be used to (un)subscribe interrupts for other I/O devices. Although, we would like to be able to do that, it is a bit too early to devise a general API. It requires more experience with the use of interrupts on Minix 3, as well as the use of some C features that you do not know yet.

This is even "more true", for the "interrupt loop", i.e. the loop for receiving an interrupt notification. Thus, for the time being, I suggest that the interrupt loop be included in timer_test_int().