Operating Systems Project 3


The goals of this project are:

  1. to learn to modify an interrupt handler
  2. to practice writing to a hardware device
  3. to practice using timers
  4. to see how easy it is to hang a system when running with interrupts suspended

Like all projects in this course, this is an individual or group project, at your choice. If you work in a group, be sure that whoever submits the project cc's all the team members and also lists all the team members' names in the email.

The project is due Monday, April 12th 2004, any time. Please send in your project on time -- late submissions will not be accepted, and I prefer to have partially-working (or even non-working) projects rather than no project at all.

Instructions for submitting the project are at the end of this description. You will only get full credit for this project if you successfully follow all the steps, but you will get partial credit if you do only some of the steps.

This project, like all projects to follow, builds on project 1 and requires the 2.6.1 kernel (you may use a different kernel, but it is up to you to figure out if the differences between your kernel and 2.6.1 might affect your results).

Project Description

This project requires you to modify the keyboard driver in drivers/char/keyboard.c, to do three main things:

  1. light up the NumLock and ScrollLock LEDs on your keyboard
  2. convert all upper-case characters to lower case
  3. flash the NumLock LED once a second

As an additional exercise, you will be required to lock up your computer by running an infinite loop in the keyboard interrupt handler. You should of course keep an unmodified backup kernel handy for regular use, including modifying and compiling the source.

Note that in order to implement this project, you will probably need to do some searching through the kernel code. This searching is part of the project, though some suggestions are given below.

Also note that in the author's distribution all the source files are write protected. If the same is true for you, to avoid frustration you probably want to start by giving yourself write permission to drivers/char/keyboard.c with

chmod +w drivers/char/keyboard.c

Lighting the Keyboard LEDs

Since you only need to light up these LEDs once, you may as well do this in kbd_init in drivers/char/keyboard.c. To set the LEDs, all you have to do is appropriately set kbd->ledflagstate = 0x.., where the value 0x.. is the numerical value you need to use to turn on the scroll lock and num lock LEDs. You can figure out this value through experimentation and careful study of the bit descriptions in include/linux/kbd_kern.h, should help you identify the value.

Note this does not directly turn on the LEDs. Instead, the LEDs are turned on when kbd_bh executes. The execution of kbd_bh is a result of calling tasklet_schedule(&keyboard_tasklet);. In turn, kbd_bh calls input_event, which is defined in drivers/input/input.c. As you can see starting at line 151 of input.c, the LED is changed by calling change_bit with as argument a pointer to the device, dev->led. It is not necessary to do such direct hardware manipulation in this project, but you should be familiar with how it is done.

For this part of the project, simply turn in your code to turn on the two LEDs. A single line should be sufficient. Also, state that you have read the relevant parts of input.c.

Note: if you turn on the caps lock LED, you will only be able to enter uppercase letters. This may require you to reboot if your login name or password contain lowercase letters.

Turning all input characters lowercase

If you are successful in this phase of the project (changing all input characters to lowercase), you will only be able to input lowercase characters from the keyboard. Therefore, if your password has any uppercase characters (which, in general, is a good idea, except for this project) you will not be able to log in from the keyboard (you may still be able to log in from the network if your kernel development system has a network connection). This is not necessarily a problem, as you can always reboot your system and select your backup kernel, but if you want to be able to log in and experiment with your modified kernel, be sure your login password is all lowercase for the duration of this projcet.

A good place to begin this part of the project is to to print and/or understand the output of "man ascii". After you understand the ASCII encoding, you must study the keyboard driver input processing.

Much of the keyboard driver input processing is done by the function kbd_keycode, which eventually computes a keysym value, then gives it to the k_handler function pointer. A good place to convert uppercase characters to lower case is after keysym is computed and before k_handler is called.

For this part of the project, only ASCII characters will be converted. The value for keysym for such characters contains the ASCII value in the low-order 8 bits, and the string 0xfb in the upper 8 bits (for a total of 16 significant bits).

To detect if an 8-bit value (e.g., the variable c) is in the range of 'A' to 'Z' and convert it to lowercase, you can use the time-honored C code

if ((c >= 'A') && (c <= 'Z')) {
  c += ('a' - 'A');
}

For this part of the project, simply turn in your code to convert lowercase input keysims to uppercase input keysims. A few lines of code should be sufficient.

Note that there are a large number of creative ways to do this part of the project other than as described above. You are welcome to do those, but if you do, please provide me with sufficient detail so that I can figure out what you are doing.

Also note that it is equally easy to turn all input characters uppercase, but doing so most likely would make it very hard to use your shell, since most unix commands have lowercase names.

Finally note that the above code snippet, however time-honored, only works for ASCII, and does not work for many international languages (some of which do not even have a notion of upper and lower case). Please use it with care.

Hanging the system

Now modify your code so when the letter 'q' is entered on the keyboard, your interrupt handler goes into an infinite loop, hanging the system. To get out of this infinite loop, you can reset or shut down the system.

For this part of the project, turn in your code to hang the system and also report whether Ctrl-Alt-Delete can be used to restart your system from the hung state.

If you are interested, you may also try and see what the effects are of dereferencing a null pointer or dividing by zero (this is not a required part of the project, so please do not turn it in). Here is sample code for both:

/* dereferencing a null pointer */
char * p = NULL;
if (*p == 'a')
printk (KERN_ERR "incredible!\n");

/* dividing by zero */
int n = 10;
int m = 0;
printk ("the result of dividing n by m is %d\n", n / m);

Blinking the NumLock LED

You need to disable the above part of your project before continuing with this last part -- otherwise, your system will hang whenever you happen to type the letter 'q'.

For this part, you need to create a new timer that will complement the NumLock LED twice a second (so the LED will blink once a second). The timer should be started by the kbd_init function, and should restart itself each time it runs.

You may want to check Love Chapter 9, starting on page 155, for information about timers.

To achieve a 0.5s delay, I suggest you use the value HZ / 2.

You also have to create a timer function whose main tasks are

  1. toggle the LED
  2. schedule the timer to run again in another half second.

To toggle the LED, you can either access the hardware directly in the manner of input.c, or you can call kbd_bh, either indirectly by scheduling the keyboard tasklet, or directly from your code. Any of these solutions is fine for this project, and I am also open to other solutions that do not violate the spirit of this project (I personally know of at least one way to do this from user space, which would definitely violate the spirit of this project).

For this part of the project, turn in your code to declare the timer, to initialize the timer, and your code for the timer function. A few lines of code (probably no more than 30) should be sufficient.

General suggestions

When I'm not sure what I am doing, I often find it useful to write a very small piece of code that uses printk to give me more information (printk in the kernel, otherwise printf). I can then watch the flow of control, as long as I've been careful with my printk's and not added too many (during one test for this project, I was printing a string to the screen every 5 seconds. That made it annoying to edit files, but not annoying enough that I was tempted to reboot into my backup kernel). Once I think I understand what the unmodified code is doing, I am usually comfortable going in and changing it. I suggest you only try this technique if and when you are stuck or confused.

Turning in the Project

To turn in the project, email the instructor the specified source code (and a statement that you have read the relevant part of input.c, and also the answer to whether Ctrl-Alt-Delete will reboot your hung system).

Please use plain text without attachments.

Further Reading

This project is about writing device driver code, but this project is relatively simple. A recent article in the Linux Journal gives much more detailed information for a different kind of driver for a USB device. It is highly recommended reading.