chardevs2: the kernel whispers

Learning objective

Deepen and enrich your undestanding of character devices with a more concrete example

Overview

  1. Review the kdlpdev example from last week

    1. open(2), close(2), and read(2) implementations
  2. Introduce write(2), ioctl(2), and llseek(2)

  3. Introduce the jiffies counter and HZ constant

  4. Discuss the device class system and struct device

  5. Overview of MIDI and our simplification

  6. Work through the design and implementation of kkey

  7. Listen to the sound of the kernel

Review

kdlpdev.c

  1. Remember what we needed to make cat work?

  2. We have to manually create the device file with mknod(7)

  3. Annoying! Today we introduce an alternative

  4. Guesses on what this might be?

New syscalls

write(2): reverse the polarity of read(2)

read(2) and write(2) considerations

  1. Make sure special cases of count value handled correctly

  2. Properly advance the file position value

  3. Take care to return sane error values if appropriate

New syscalls

ioctl(2): general purpose interface with a character device

  1. Commonly used for device-specifc operations

  2. Originally used to control the terminal on earlier unix systems

  3. tty := teletype terminal

  4. historically controlled terminals

unlocked_ioctl?

Kernel had a single lock long ago

  1. IOCTL calls took the lock

  2. Long since irrelevant

IOCTL cmd/arg details

Kernel prefered way to define IOCTL commands with several macros

  1. Can specify type of transfered data for validation

  2. kernel/ioctl.h

IOCTL cmd example (AI generated)

  1. example ioctl cmd #defines from claude:
#include <linux/ioctl.h>
#define MYDEVICE_IOC_MAGIC  'k'
#define MYDEVICE_IOCRESET    _IO(MYDEVICE_IOC_MAGIC, 0)
#define MYDEVICE_IOCSQUALITY _IOW(MYDEVICE_IOC_MAGIC, 1, int)
#define MYDEVICE_IOCGQUALITY _IOR(MYDEVICE_IOC_MAGIC, 2, int)

New syscalls

lseek(2): change the file position pointer

Three common modes:

  1. Offset from current value

  2. Negative offset from end

  3. Set value directly

All fairly boilerplate

Compatibility quirk

Any guesses why we find llseek in file_operations instead of lseek?

  • Hint: llseek is short for "long long seek"

  • 32-bit and 64-bit compatibility

Synchronizing with the CPU

  1. The jiffies counts CPU timer interupts since boot

  2. Allows calculating elapsed time between in-kernel events

  3. jiffies defined

  4. get_jiffies_64()

Synchronizing with the CPU

  1. HZ kernel config value contains timer frequency

  2. Units are in interupts per second

  3. HZ

HZ and jiffies

  1. jiffies unit is timer interupts (since boot)

  2. HZ unit is interupts/seconds

  3. Therefore: seconds since boot = jiffies / HZ

Grouping devices

Linux provides a device class reprsentation

  1. Each device class groups devices of similar type

Device class implementation

  1. struct class

  2. class_create()

  3. class_destroy()

Device instance

The kernel provides a specific device instance representation

  1. Can create these by hand

  2. Can instantiate using a device class

  3. Represents device instance directly

Device instance vs device class

  1. Device instance identified by major/minor pair

  2. Device class identified by name string

  3. Both provide a sysfs interface

Devices vs character devices

A character device can provide an interface for a device instance

  1. The device instance could alternatively provide a block device interface

Device instance implementation

  1. struct device

  2. device_create()

    1. From class
  3. device_register()

    1. By hand
  4. device_destroy()

demo

The least abstract demo so far this semester

First step

What kind of audio format would be suitable?

  • Regardless of anything said just now in class, we chose MIDI

MIDI format overview

Encodes musical information in events

  1. rather than a stream of audio

MIDI format overview

Series of event messages sent to MIDI device

  1. Events processed as they're received

MIDI messages

  1. Event messages relating to music, e.g. press, release

  2. Meta messages e.g. time signature, tempo, track names, lyrics

MIDI time between events

  1. deltas placed in between messages

  2. deltas are variable-width, with 7 bits of information

  3. Leading bit 1 until final byte to indicate end

MIDI binary format outline

  1. One global MIDI header

  2. Some some number of track headers

  3. A series of meta or event messages

    1. delimited by variable length delta values

Further MIDI reading

  1. http://midi.teragonaudio.com

  2. https://faydoc.tripod.com/formats/mid.htm

Music input scheme

write system calls to one of the /dev/kkeyXXX files

Input value is either:

  1. 0 - key release

  2. 0 - key press

Music output scheme

Use read system call on any /dev/kkeyXXX

  1. Output data is valid MIDI file

  2. Listen to the music

A couple more kkey details

  1. Simplify format with single track and channel

  2. An IOCTL cmd emptires the internal buffer

  3. But how do we calculate the delta values?

An analogy

If the timer interupt is the "heartbeat" of the kernel, then kkey reads it's pulse

  1. This is not rigorous

Optimization

We cache the MIDI file when it's generated

  1. As long as we read again before writing, no need to re-generate

Concerns, anyone?

Spot any flaws?

  1. Think about this as we peruse the implementation

Complete implementation

We will now tour kkey.c

  1. All course demos and more are now published to this repo

  2. Any suggestions or fixes? Contributions welcome!

demo

makefile usage:

make
make reset
make play
make clean

demo

middle_c.sh

Note table at bottom of https://faydoc.tripod.com/formats/mid.htm

demo

c_major.sh

demo

A classic musical "Hello world" tune

twinkle_twinkle.c

demo

A modified C418 song used in Minecraft soundtrack IIRC

microvenus.c

Glaring issue number 1

Any guesses?

  • No locking! (intentional)

Other non-deterministic behavior

Any quesses?

  • Each usage of kkey produces slightly different output

Summary

The six entries of struct file_operations implemented by kkey are the complete set required for the remaining assignments in this course

Summary

Using struct device and struct class, one can more easily group together and manage similar devices

Summary

Using jiffies and HZ, we can calculate the passage of real wall clock time relative to system boot

Summary

The interfaces provides to kernel modules allow for very flexible behavior and creative features

  1. This module implements an interface similar to the final project

End

Coming up soon: concurrency and a race-free kkey