
Explore how a device driver integrates code parts—user space access and hardware communication—treats devices as files and uses character devices for byte-wise data with system calls like open and read.
Learn to differentiate character and block device files and identify real-world examples like serial ports, console terminals, hard disks, and ramdisks through device file types.
Execute three steps to create a character driver: allocate a number dynamically for the structure, initialize the driver with its file operations structures, and register the character device.
Learn how the name-based connection between a user application and the device file contrasts with major and minor numbers, where the major identifies the driver and the minor distinguishes devices.
Major numbers are statically assigned and documented by government sources, showing used device numbers; the lecture contrasts static and dynamic assignment and explains how dynamic mode allocates the last number.
Understand the data type dev_t for device numbers in character device drivers, and how to combine major and minor numbers to create a device.
Explore a simple example using the major, minor, and mkdev macros to create and dissect a device number, print the major and minor values, and verify them by extraction.
Explore the /proc/devices file to see the devices currently present on the system, including their major numbers and names. The content is shown in two sections.
Examine static and dynamic allocation of major and minor device numbers in character device drivers, and why dynamic allocation helps avoid conflicts.
Explore the static allocation of device numbers for character device drivers, illustrating default values and how explicit IDs are assigned and verified within the system.
Explore test cases on a previous example within character device drivers. Examine arguments discussed in the caption to understand how the topic unfolds.
Explore what happens when two character device drivers register the same major number, and why dynamic allocation prevents conflicts unlike static allocation.
The lecture explores a reverse case of registering the same name in character device drivers, showing successful registration when major numbers differ, using usb and uspi as examples.
Explain how one major number supports multiple minor numbers via the magic number, with 126 majors and minor numbers starting at zero or ten, illustrating registration and overflow.
Explain how the kernel enforces an artificial limit on major numbers via the Major_Max macro. Show that the maximum major number is 511 and that allocations like 095 are blocked.
Examine dynamic allocation of device numbers to replace static locations, address allocation parameters such as minimum numbers and device counts, and minimize conflicts.
Explore an example of dynamic allocation of a device number in character device drivers. Consider practical notes on minimum values and common pitfalls during allocation.
Explore how alloc_chrdev_region is called in a loop to allocate dynamic major numbers, revealing two segments, end-of-segment values (234 and 384), and why the region fills and requires a reboot.
Explore how to manually create device nodes in character device drivers by assigning major and minor numbers and linking them to a device name for registration and runtime use.
This lecture explains the older mechanism of device node creation and introduces the new approach using class_create, detailing creating and destroying a class and related steps.
Explore automatic device node creation using device_create in character device drivers, covering major and minor numbers, device class creation, and clean removal of devices.
This example demonstrates automatic device node creation, using a device number and a name, and discusses allocation and creation steps.
Learn how to implement a Linux character device using struct file_operations with open, close, read, and write handlers, including owner, permissions, and translating user-space parameters to driver parameters.
Bind the file_operations structure to a character device by initializing it and attaching it to the module, illustrating how the kernel registers device operations.
Compare cdev_init and cdev_alloc in character device drivers, showing how memory allocation and dynamic setup affect device activation and protection against module unload.
Explore creating a Linux kernel module character device with cdev_alloc, registering with a major number, and handling open, read, and release.
Learn how a Linux kernel module registers file operations with cdev_init, manages memory and permissions, and handles permission denied errors when accessing a character device.
Learn about pseudo devices in Linux character drivers, focusing on /dev/null, /dev/zero, and /dev/random, device files without physical hardware that discard data or generate streams.
Explore how to implement a character device driver in Linux by examining mem.c, including file operations, major and minor numbers, device class, and the register process.
Explore how open and release calls behave in fork scenarios for character device drivers, highlighting that open and release may occur only once and how close is involved.
Explore how many times open and release are invoked when multiple processes access character device drivers.
Explore how the struct file represents an open file and how the open call and driver implementation preserve state and manage file operations.
Examine an example of the struct file in a character device driver, illustrating user-space interactions and the handling of operations and pointers within the driver context.
Explore the inode structure and its file metadata. Understand size, blocks, block size, timestamps, and device numbers, and how stat reveals them and how major and minor numbers identify devices.
Study an example of struct inode in character device drivers, focusing on permissions, the user of the device globally, and major and minor numbers for device identification.
Showcases how struct inode and struct file interact during file open and creation, and highlights behavior when a file exists, is created anew, or is deleted.
Learn how printing a user space buffer inside the write function works in character device drivers and the implications for user space interaction.
Accessing a user space buffer from kernel space is discussed, highlighting security issues and the risks of copying from user space in character device drivers.
Learn how copy_from_user transfers a block of data from userspace to the device driver, handling bytes and access rules.
Examine what happens when a NULL pointer is passed to the write system call in character device drivers, and how the kernel handles bytes not copied from user space.
Explore copy_to_user in character device drivers, illustrating how data is copied to user space and how it contrasts with copy operations.
Explore how put_user in character device drivers handles 64-bit values, stores data, and manages timing with sleeps to illustrate concurrent device interactions.
Explore get_user in character device drivers, showing how to retrieve device values and manage a global variable versus a local property as the latest values change.
Pass a structure between user space and kernel space by copying data to and from the kernel, using open, read, and write operations on the device.
This lecture examines passing user heap variables to a kernel driver. It shows copying from user space into a kernel buffer and writing to a device file descriptor.
Examine why passing a structure with one member on the stack and the other in the heap fails in character device driver operations, and analyze practical implications.
Learn how kernel utilities print dev_t using print_dev_t and format_dev_t, extract major and minor numbers with macros, write to a buffer, and return the number of characters written.
Explore whether strlen can be safely used in kernel space for character device drivers, including length calculation, memory buffers, user space interactions, and common pitfalls.
Learn how strnlen_user checks the length of a null-terminated string from userspace by passing a userspace pointer and a maximum length, returning the size including the terminating null.
Learn to pass a structure with a stack and a heap member by copying from user twice and using pointers to access both parts.
Reveal a flaw in a character driver when reading data byte by byte. Improper buffer handling and copying to user space can repeat the first character rather than advancing.
Learn how to add offset support to read and write in character device drivers, updating the current position after each transfer, handling buffer limits, and copying data safely.
Explain updated logic for character device driver read/write when requested bytes exceed or fall short of available bytes, using buffer, offset, and available space calculations to avoid illegal seeks.
Explore implementing lseek support in deep character device drivers by managing offsets, current positions, and maximum sizes to adjust the file position safely.
Explore creating multiple character device nodes by allocating several device numbers, initializing each device in a loop, and ensuring each uses a separate buffer to prevent data sharing.
Explain how the container_of macro derives a structure's base address from a pointer to one of its members by taking three arguments—the member pointer, the type, and the member name.
Learn how to add support for private data in a character device driver by initializing device messages, linking devices, and accessing the base address, service name, and file pointers.
User space writes to a device file trigger the kernel's write via the vfs, invoking the device's file operations with the buffer, position, and fd.
Explore input and output control for character device drivers, focusing on device-specific custom commands through system calls, including format, reset, and shutdown operations and per-device parameter handling.
Learn how ioctl BLKGETSIZE retrieves a block device size by returning the number of 512-byte blocks, so you can compute disk size.
Learn an ioctl example with BLKGETSIZE64 to obtain a block device size in bytes and sectors, and see how user space can issue device specific custom commands.
Investigate the unlocked_ioctl ioctl method in character device drivers, showing how optional arguments and commands pass from user space, and how values are received, processed, and aliased.
Explore adding three ioctls in a character device driver by using a switch to select behavior based on the command, arguments, and buffer length.
Learn how symbolic names map to ioctl commands for character device drivers, including defining macros, sharing definitions between kernel modules and userspace, and documenting the command numbers for clarity.
learn how to define ioctl commands using unique magic numbers and four-part macros with type, number, size, and direction to prevent clashes and ensure safe device control.
Explore how ioctl macros define command numbers and magic numbers for character device drivers, with examples of read and write operations and command value semantics.
Learn how macros decode information from ioctl commands, extracting the magic number, sequence number, and size from combined values to interpret device driver operations.
Leverage ioctl macros to update character device driver code, using a magic number and defined command codes to read from the device and pass data to a clear operation.
Show how a character device driver should respond to unsupported commands from user space, including choosing an appropriate return value when the command is not supported.
Explore how the access_ok macro validates user-space pointers in a character device driver, including when to verify addresses without data transfer and version-specific argument usage.
Explore how to update character device driver code with the access_ok macro to validate userspace arguments, perform access checks, and safely manage addresses in kernel space.
Examine fail scenarios of access_ok in character device drivers, highlighting how incorrect access and address handling can lead to accidental exposure when passing or making addresses accessible.
Explore how ioctl command sizes affect data transfer in character device drivers, showing why mismatched argument sizes can yield incorrect data and how to ensure you pass the exact size.
Explore how compat_ioctl is used in character device drivers to handle 64 bit architecture, manage library dependencies, and read device data.
On a 64-bit machine, the unsigned long size changes, complicating ioctl handling for 32-bit processes. Map or redefine command numbers to match the actual command size.
Explore sending a signal from a module to a process, using the signal function with two arguments to target a specific task, with user-space context and practical examples.
Increment a usage counter in the device driver's open entry point each time an application opens the device. Access to data follows open, reflecting how often the driver is used.
Use open and release to ensure only one process can open the device at a time, avoiding concurrency issues.
Restrict device access to a single user at a time by designating an owner on first open and enforcing authority.
Explore how capabilities assign only the necessary privileges to processes, instead of full privileges, and how extended attributes store these capabilities to safeguard privileged operations.
Explore how capabilities like cap_dac_override govern file-system access, with a practical example of granting or denying access based on user permissions, groups, and capability checks.
Presents an example of using the cap_sys_module capability to load modules within character device drivers. Shows how this capability enables observing system interactions and managing module execution.
Examine cap_sys_admin in character device drivers. A process must hold the appropriate capability to perform privileged operations, with permissions gating signals and actions in Android user contexts.
Explain how the open system call uses open flags to control file access from user space, detailing permissions, flags, and a simple echo example.
When the process terminates, the release function runs automatically, even if you don't call close, to release resources.
Learn to work with character device drivers by allocating major and minor numbers, registering and unregistering devices, using misc devices, and implementing init and exit sequences.
Explore a basic example of a misc driver, showing a single function, its dynamic creation, and how delays and messages illustrate simple driver behavior.
All the files used in the course
Updated on Oct 10: Added Downloads section
What will you learn from this course?
Introduction to character drivers
What is device number and device file
Allocating device number - statically and dynamically
Creating device file - Manually(mknod) and automatically (udev)
Registering character device and its file operation with Kernel
Copying data from user space to kernel space and vice versa
Understanding the various structures - struct file and struct inode
Implementation of open, release, read, write, llseek, ioctl file operations
How to create multiple device nodes and add support for private data
Flow of write system call from kernel system call entry point to driver write file operations
Handling all the error cases in ioctl implementation
Sending a signal to user space from kernel space
Various access control mechanisms and capabilities
Misc Driver
API's covered in this course:
MAJOR
MINOR
MKDEV
register_chrdev_region
alloc_chrdev_region
class_create
device_create
class_destroy
device_destroy
unregister_chrdev_region
imajor
iminor
copy_from_user
copy_to_user
put_user
get_user
print_dev_t
format_dev_t
strnlen_user
container_of
access_ok
Commands used in this course:
mknod
udevadm monitor