diff --git a/Linux/Device_Driver/1 LDD Introduction/README.md b/Linux/Device_Driver/1 LDD Introduction/README.md new file mode 100644 index 0000000..1744cf4 --- /dev/null +++ b/Linux/Device_Driver/1 LDD Introduction/README.md @@ -0,0 +1,59 @@ +# Linux Device Driver – Part 1: Introduction +- This repository contains notes and example code for learning the basics of Linux device drivers, focusing on how drivers integrate with the Linux kernel and user space. + +## What is a Device Driver? +- A device driver is a piece of software that lets the operating system communicate with hardware devices. +- In Linux, many drivers are built into the kernel or loaded as kernel modules, allowing hardware to be + controlled via standard interfaces. + +## Types of Linux Device Drivers + +Common categories of Linux device drivers include: + +- **Character drivers** – Accessed as streams of bytes (for example `/dev/ttyS0` for serial ports) +- **Block drivers** – Handle data in blocks and are used for storage devices +- **Network drivers** – Interface network hardware with the kernel’s networking stack + +This introduction usually starts with character drivers because they are simpler conceptually. + +## Kernel Space vs User Space + +Linux clearly separates: + +- **User space** – Where normal applications run, with restricted access +- **Kernel space** – Where the kernel and drivers run, with full access to hardware and system resources + +A device driver typically runs in kernel space and exposes an interface (often via `/dev` files or sysfs) that user-space programs can use. + +## Loadable Kernel Modules (LKM) + +Instead of compiling every driver into the kernel, Linux supports **loadable kernel modules**: + +- Drivers can be compiled as modules +- Modules can be inserted (`insmod`/`modprobe`) and removed (`rmmod`) at runtime +- `dmesg` and `/var/log` can be used to inspect kernel messages from drivers + +This introduction often walks through how to build a simple module, load it, and see messages using `dmesg`. + +## Basic Steps to Write a Simple Driver Module + +Typical steps you will follow in this series: + +1. Write a minimal C file that defines `init` and `exit` functions for the module +2. Use `module_init()` and `module_exit()` macros to register these functions +3. Add a `Makefile` that uses the kernel build system to compile the module +4. Build the module against your current kernel headers +5. Load the module and verify its messages in the kernel log +6. Unload the module cleanly + +## Prerequisites + +Before following along: + +- Basic knowledge of C programming +- A Linux environment (for example, Ubuntu) with kernel headers installed +- Ability to use the terminal and run basic compilation commands + +REFER: +- https://github.com/darshankharbikar/raspberry-pi-4b +- https://embetronicx.com/tutorials/linux/device-drivers/setup-ubuntu-and-raspberry-pi-linux-device-driver-tutorial/ diff --git a/Linux/Device_Driver/Atomic_variable/README.md b/Linux/Device_Driver/Atomic_variable/README.md new file mode 100644 index 0000000..c9d9135 --- /dev/null +++ b/Linux/Device_Driver/Atomic_variable/README.md @@ -0,0 +1,933 @@ +# Linux Device Driver - Atomic Variables and Atomic Operations in Linux Kernel + +## Overview + +Atomic operations provide a way to safely manipulate shared integer variables without using mutexes or spinlocks. + +The operation is performed as a single indivisible action, preventing race conditions when multiple CPUs, threads, or interrupt handlers access the same variable simultaneously. ([EmbeTronicX][1]) + +--- + +# Why Atomic Variables? + +Consider a shared variable: + +```c +int counter = 0; +``` + +Two threads execute: + +```c +counter++; +``` + +Internally this is not a single operation: + +```text +1. Read counter +2. Increment value +3. Write back counter +``` + +Possible execution: + +```text +Thread1 reads 0 +Thread2 reads 0 + +Thread1 writes 1 +Thread2 writes 1 +``` + +Expected: + +```text +counter = 2 +``` + +Actual: + +```text +counter = 1 +``` + +This is a race condition. ([EmbeTronicX][1]) + +--- + +# Traditional Solution + +Using a mutex: + +```c +mutex_lock(&lock); + +counter++; + +mutex_unlock(&lock); +``` + +or spinlock: + +```c +spin_lock(&lock); + +counter++; + +spin_unlock(&lock); +``` + +Works correctly but introduces synchronization overhead. ([EmbeTronicX][1]) + +--- + +# Atomic Solution + +Use an atomic variable: + +```c +atomic_t counter = ATOMIC_INIT(0); + +atomic_inc(&counter); +``` + +Internally: + +```text +Read + Modify + Write + | + +--> One Atomic Operation +``` + +No additional lock required. ([EmbeTronicX][1]) + +--- + +# What Does "Atomic" Mean? + +Atomic means: + +```text +Indivisible +``` + +An operation either: + +```text +Completes Entirely + OR +Does Not Happen +``` + +No other CPU can observe a partially completed atomic operation. ([Reddit][2]) + +--- + +# When Should Atomic Variables Be Used? + +Good candidates: + +```text +Reference Counters +Packet Counters +Statistics +Flags +Device Usage Count +Simple Shared Integers +``` + +Example: + +```c +atomic_t packet_count; +``` + +--- + +# When NOT To Use Atomic Variables + +Bad candidates: + +```text +Complex Data Structures +Linked Lists +Multiple Variables +Large Critical Sections +``` + +Use: + +```text +Mutex +Spinlock +RW Spinlock +RCU +``` + +instead. + +--- + +# Atomic Variable Types + +Linux provides two primary atomic integer types. + +--- + +## atomic_t + +Stores a 32-bit integer. + +```c +atomic_t counter; +``` + +Kernel definition conceptually: + +```c +typedef struct { + int counter; +} atomic_t; +``` + +([EmbeTronicX][1]) + +--- + +## atomic64_t + +Stores a 64-bit integer. + +```c +atomic64_t counter64; +``` + +Conceptually: + +```c +typedef struct { + long counter; +} atomic64_t; +``` + +Available on supported architectures. ([EmbeTronicX][1]) + +--- + +# Header Files + +```c +#include +``` + +Older kernels may use: + +```c +#include +``` + +([EmbeTronicX][1]) + +--- + +# Creating Atomic Variables + +## Method 1 + +Declaration only: + +```c +atomic_t etx_counter; +``` + +Must initialize later. + +--- + +## Method 2 + +Initialize during declaration: + +```c +atomic_t etx_counter = + ATOMIC_INIT(0); +``` + +Creates: + +```text +Atomic Integer +Initial Value = 0 +``` + +([EmbeTronicX][1]) + +--- + +# Reading Atomic Variables + +## atomic_read() + +Reads the atomic variable. + +```c +int value; + +value = atomic_read( + &etx_counter); +``` + +Example: + +```c +pr_info( + "Value=%d\n", + atomic_read( + &etx_counter)); +``` + +Returns current value. ([EmbeTronicX][1]) + +--- + +# Writing Atomic Variables + +## atomic_set() + +Assign value atomically. + +```c +atomic_set( + &etx_counter, + 10); +``` + +Result: + +```text +etx_counter = 10 +``` + +([EmbeTronicX][1]) + +--- + +# Atomic Arithmetic Operations + +--- + +## atomic_inc() + +Increment by one. + +```c +atomic_inc( + &etx_counter); +``` + +Equivalent: + +```c +counter++; +``` + +but atomic. ([EmbeTronicX][1]) + +--- + +## atomic_dec() + +Decrement by one. + +```c +atomic_dec( + &etx_counter); +``` + +Equivalent: + +```c +counter--; +``` + +but atomic. ([EmbeTronicX][1]) + +--- + +## atomic_add() + +Add value. + +```c +atomic_add( + 5, + &etx_counter); +``` + +Result: + +```text +counter += 5 +``` + +([EmbeTronicX][1]) + +--- + +## atomic_sub() + +Subtract value. + +```c +atomic_sub( + 5, + &etx_counter); +``` + +Result: + +```text +counter -= 5 +``` + +([EmbeTronicX][1]) + +--- + +# Test Operations + +Useful when checking conditions immediately after modification. + +--- + +## atomic_inc_and_test() + +```c +if (atomic_inc_and_test( + &counter)) +{ +} +``` + +Returns: + +```text +true -> Result became 0 +false -> Otherwise +``` + +([EmbeTronicX][1]) + +--- + +## atomic_dec_and_test() + +```c +if (atomic_dec_and_test( + &counter)) +{ +} +``` + +Returns: + +```text +true -> Counter reached 0 +``` + +Commonly used in reference counting. ([EmbeTronicX][1]) + +--- + +## atomic_sub_and_test() + +```c +if (atomic_sub_and_test( + 5, + &counter)) +{ +} +``` + +Returns: + +```text +true -> Result equals 0 +``` + +([EmbeTronicX][1]) + +--- + +# Return Value Operations + +--- + +## atomic_add_return() + +```c +int value; + +value = + atomic_add_return( + 5, + &counter); +``` + +Returns: + +```text +New Value After Addition +``` + +Example: + +```text +counter = 10 + +atomic_add_return(5) + +returns 15 +``` + +([EmbeTronicX][1]) + +--- + +## atomic_inc_return() + +```c +value = + atomic_inc_return( + &counter); +``` + +Returns incremented value. + +--- + +## atomic_dec_return() + +```c +value = + atomic_dec_return( + &counter); +``` + +Returns decremented value. + +--- + +# Conditional Atomic Operations + +## atomic_add_unless() + +```c +atomic_add_unless( + &counter, + 1, + 100); +``` + +Meaning: + +```text +Add 1 +Unless Value == 100 +``` + +Useful for reference counting. ([EmbeTronicX][1]) + +--- + +# Atomic Bit Operations + +Linux also supports atomic operations on individual bits. + +Example: + +```c +unsigned long flags; +``` + +--- + +## set_bit() + +```c +set_bit( + 0, + &flags); +``` + +Set bit 0. + +--- + +## clear_bit() + +```c +clear_bit( + 0, + &flags); +``` + +Clear bit 0. + +--- + +## test_bit() + +```c +if(test_bit( + 0, + &flags)) +{ +} +``` + +Check bit. + +--- + +## test_and_set_bit() + +```c +old = +test_and_set_bit( + 0, + &flags); +``` + +Returns previous value and sets bit atomically. ([EmbeTronicX][1]) + +--- + +# Example Driver + +## Atomic Variable + +```c +atomic_t etx_counter = + ATOMIC_INIT(0); +``` + +--- + +## Thread 1 + +```c +while(!kthread_should_stop()) +{ + atomic_inc( + &etx_counter); + + pr_info( + "Thread1=%d\n", + atomic_read( + &etx_counter)); + + msleep(1000); +} +``` + +--- + +## Thread 2 + +```c +while(!kthread_should_stop()) +{ + atomic_inc( + &etx_counter); + + pr_info( + "Thread2=%d\n", + atomic_read( + &etx_counter)); + + msleep(1000); +} +``` + +Both threads update the same variable safely without locks. ([EmbeTronicX][1]) + +--- + +# Execution Flow + +```text +Thread1 + | + +--> atomic_inc() + +Thread2 + | + +--> atomic_inc() + +CPU Guarantees +Atomic Update +``` + +Result: + +```text +0 +1 +2 +3 +4 +5 +... +``` + +No lost updates. + +--- + +# Atomic Variable vs Mutex + +| Feature | Atomic Variable | Mutex | +| ---------------- | --------------- | -------- | +| Single Integer | Excellent | Overkill | +| Sleep | No | Yes | +| Ownership | No | Yes | +| Critical Section | No | Yes | +| Performance | Very High | Lower | +| ISR Safe | Yes | No | + +([EmbeTronicX][1]) + +--- + +# Atomic Variable vs Spinlock + +| Feature | Atomic | Spinlock | +| ------------------ | --------- | --------- | +| Single Variable | Excellent | Overkill | +| Multiple Variables | Poor | Excellent | +| Locking Required | No | Yes | +| Complexity | Low | Medium | +| Performance | Higher | Lower | + +([EmbeTronicX][1]) + +--- + +# Common Driver Use Cases + +## Packet Counter + +```c +atomic_t rx_packets; +``` + +```c +atomic_inc( + &rx_packets); +``` + +--- + +## Open Count + +```c +atomic_t open_count; +``` + +```c +atomic_inc( + &open_count); + +atomic_dec( + &open_count); +``` + +--- + +## Reference Counter + +```c +atomic_t refcount; +``` + +```c +if (atomic_dec_and_test( + &refcount)) +{ + free_resource(); +} +``` + +--- + +## Device State Flags + +```c +set_bit(0, &flags); +clear_bit(0, &flags); +``` + +--- + +# Common Mistakes + +## Using Atomic Variable for Complex Logic + +Wrong: + +```c +if(counter == 0) +{ + counter++; +} +``` + +Race possible. + +Use: + +```c +atomic operations +or +locks +``` + +--- + +## Protecting Multiple Variables + +Wrong: + +```c +atomic_t a; +atomic_t b; +``` + +Need consistency between both. + +Use: + +```c +spinlock +mutex +``` + +instead. + +--- + +## Sleeping Assumption + +Atomic operations: + +```text +Do NOT Sleep +Do NOT Block +``` + +They only guarantee atomic access to the variable. + +--- + +# Important APIs Summary + +## Declaration + +```c +atomic_t +atomic64_t +ATOMIC_INIT() +``` + +--- + +## Read / Write + +```c +atomic_read() + +atomic_set() +``` + +--- + +## Arithmetic + +```c +atomic_inc() + +atomic_dec() + +atomic_add() + +atomic_sub() +``` + +--- + +## Test Operations + +```c +atomic_inc_and_test() + +atomic_dec_and_test() + +atomic_sub_and_test() +``` + +--- + +## Return Operations + +```c +atomic_add_return() + +atomic_inc_return() + +atomic_dec_return() +``` + +--- + +## Conditional + +```c +atomic_add_unless() +``` + +--- + +## Bit Operations + +```c +set_bit() + +clear_bit() + +test_bit() + +test_and_set_bit() +``` + +--- + +# Key Takeaways + +* Atomic operations provide lock-free synchronization for simple integer and bit variables. +* `atomic_t` is used for 32-bit atomic integers; `atomic64_t` for 64-bit values. +* Atomic operations prevent race conditions without mutexes or spinlocks. +* They are ideal for counters, flags, statistics, and reference counts. +* Atomic variables are not a replacement for mutexes or spinlocks when protecting complex data structures. +* Use atomic operations when the shared resource is a single integer or bit field and performance is important. ([EmbeTronicX][1]) + +[1]: https://embetronicx.com/tutorials/linux/device-drivers/atomic-variables-atomic-operation/?utm_source=chatgpt.com "Atomic variable in Linux - Linux Device Driver Tutorial Part 30" +[2]: https://www.reddit.com/r/linux/comments/1dxdfoy?utm_source=chatgpt.com "Linux 6.11 To Introduce Block Atomic Writes - Including NVMe & SCSI Support" + + +## Conclusion +This is just a basic linux device driver which explains about the atomic variables in linux device driver. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/atomic-variables-atomic-operation/ diff --git a/Linux/Device_Driver/Atomic_variable/ReadMe.md b/Linux/Device_Driver/Atomic_variable/ReadMe.md deleted file mode 100644 index c14a13f..0000000 --- a/Linux/Device_Driver/Atomic_variable/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains about the atomic variables in linux device driver. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/atomic-variables-atomic-operation/ \ No newline at end of file diff --git a/Linux/Device_Driver/Completion/ReadMe.md b/Linux/Device_Driver/Completion/ReadMe.md index 0775bcb..8dfd83d 100644 --- a/Linux/Device_Driver/Completion/ReadMe.md +++ b/Linux/Device_Driver/Completion/ReadMe.md @@ -1,4 +1,829 @@ +# Linux Device Driver - Completion in Linux Kernel + +## Overview + +A **Completion** is a synchronization mechanism used when one thread must wait until another thread finishes a specific task. + +Think of it as: + +```text +Thread A + | + +--> Wait Until Event Happens + | + v + Thread B + | + +--> Finish Work + | + +--> Notify Thread A +``` + +Completion provides a simple, race-free mechanism for one execution context to wait for an event and another execution context to signal that the event has occurred. It is built on top of Linux waitqueues. ([EmbeTronicX][1]) + +--- + +# Why Do We Need Completion? + +Without completion: + +```text +Thread A + | + +--> while(flag == 0) + ; +``` + +Problems: + +* CPU waste (busy waiting) +* Race conditions +* Poor readability +* Difficult synchronization + +With completion: + +```text +Thread A + | + +--> Sleep + +Thread B + | + +--> Work Finished + | + +--> Wake Thread A +``` + +Completion provides efficient sleep/wakeup behavior using the scheduler. ([Kernel][2]) + +--- + +# Real World Example + +Imagine ordering food: + +```text +Customer + | + +--> Wait + +Chef + | + +--> Cook Food + | + +--> Ring Bell + +Customer + | + +--> Resume +``` + +Customer = Waiting Thread + +Chef = Worker Thread + +Bell = Completion Event + +--- + +# Completion vs Wait Queue + +| Feature | Wait Queue | Completion | +| ---------------------------- | ---------- | ---------- | +| General Purpose | Yes | No | +| Event Synchronization | Possible | Excellent | +| Complexity | Higher | Lower | +| Readability | Moderate | High | +| Race-Free Event Notification | Manual | Built-In | + +Completion is essentially a specialized waitqueue designed for event synchronization. ([EmbeTronicX][1]) + +--- + +# Completion Architecture + +```text +Thread A + | + +--> wait_for_completion() + | + +--> Sleep + +Thread B + | + +--> complete() + | + +--> Wake Thread A +``` + +--- + +# Internal Structure + +Include: + +```c +#include +``` + +Kernel structure: + +```c +struct completion +{ + unsigned int done; + wait_queue_head_t wait; +}; +``` + +Fields: + +| Field | Description | +| ----- | --------------- | +| done | Completion flag | +| wait | Wait queue | + +The `done` field indicates whether the event has been completed. ([EmbeTronicX][1]) + +--- + +# Completion Lifecycle + +```text +Create Completion + | + v +Initialize + | + v +wait_for_completion() + | + v +Sleep + | + v +complete() + | + v +Wake Up +``` + +--- + +# Static Initialization + +## DECLARE_COMPLETION() + +```c +DECLARE_COMPLETION( + data_read_done); +``` + +Creates and initializes completion. + +Example: + +```c +static DECLARE_COMPLETION( + data_ready); +``` + +No need for `init_completion()`. ([EmbeTronicX][1]) + +--- + +# Dynamic Initialization + +Declare: + +```c +struct completion + data_ready; +``` + +Initialize: + +```c +init_completion( + &data_ready); +``` + +This: + +```text +Initializes Wait Queue +Sets done = 0 +``` + +Meaning: + +```text +Not Completed Yet +``` + +([EmbeTronicX][1]) + +--- + +# Reinitialize Completion + +## reinit_completion() + +```c +reinit_completion( + &data_ready); +``` + +Used when: + +```text +Reuse Existing Completion +``` + +Resets: + +```text +done = 0 +``` + +without reinitializing the waitqueue. ([EmbeTronicX][1]) + +--- + +# Waiting For Completion + +The waiting thread sleeps until another thread signals completion. + +--- + +## wait_for_completion() + +```c +wait_for_completion( + &data_ready); +``` + +Behavior: + +```text +Not Completed + | + +--> Sleep + +Completed + | + +--> Continue +``` + +Characteristics: + +* Sleeps indefinitely +* Not interruptible +* No timeout + +([EmbeTronicX][1]) + +--- + +## wait_for_completion_timeout() + +```c +wait_for_completion_timeout( + &data_ready, + timeout); +``` + +Example: + +```c +wait_for_completion_timeout( + &data_ready, + msecs_to_jiffies(5000)); +``` + +Wait: + +```text +Maximum 5 Seconds +``` + +Return: + +| Value | Meaning | +| ----- | --------- | +| 0 | Timed Out | +| >0 | Completed | + +([EmbeTronicX][1]) + +--- + +## wait_for_completion_interruptible() + +```c +wait_for_completion_interruptible( + &data_ready); +``` + +Can be interrupted by signals. + +Returns: + +| Value | Meaning | +| ------------ | ----------- | +| 0 | Completed | +| -ERESTARTSYS | Interrupted | + +([EmbeTronicX][1]) + +--- + +## wait_for_completion_interruptible_timeout() + +```c +wait_for_completion_interruptible_timeout( + &data_ready, + timeout); +``` + +Features: + +* Interruptible +* Timeout supported + +([EmbeTronicX][1]) + +--- + +## wait_for_completion_killable() + +```c +wait_for_completion_killable( + &data_ready); +``` + +Interruptible only by fatal signals. + +([EmbeTronicX][1]) + +--- + +## wait_for_completion_killable_timeout() + +```c +wait_for_completion_killable_timeout( + &data_ready, + timeout); +``` + +Supports: + +* Kill signal +* Timeout + +([EmbeTronicX][1]) + +--- + +# Non-Blocking Wait + +## try_wait_for_completion() + +```c +if(try_wait_for_completion( + &data_ready)) +{ + /* completed */ +} +``` + +Behavior: + +```text +Completed + | + +--> Return TRUE + +Not Completed + | + +--> Return FALSE +``` + +No sleeping. + +Safe in IRQ context. ([EmbeTronicX][1]) + +--- + +# Signaling Completion + +The worker thread notifies waiting threads. + +--- + +## complete() + +Wake one waiting task. + +```c +complete( + &data_ready); +``` + +Behavior: + +```text +Thread Sleeping + | + +--> Wake One +``` + +FIFO wake-up order is maintained. ([Kernel][2]) + +--- + +## complete_all() + +Wake all waiting tasks. + +```c +complete_all( + &data_ready); +``` + +Behavior: + +```text +Thread1 Sleeping +Thread2 Sleeping +Thread3 Sleeping + + | + +--> Wake All +``` + +Useful when multiple threads wait for the same event. ([EmbeTronicX][1]) + +--- + +# Checking Completion Status + +## completion_done() + +```c +completion_done( + &data_ready); +``` + +Returns: + +| Value | Meaning | +| ----- | ------------- | +| 0 | Not Completed | +| 1 | Completed | + +Safe in interrupt context. ([EmbeTronicX][1]) + +--- + +# Simple Example + +## Waiting Thread + +```c +static int wait_thread_fn( + void *data) +{ + pr_info( + "Waiting...\n"); + + wait_for_completion( + &data_ready); + + pr_info( + "Completed\n"); + + return 0; +} +``` + +--- + +## Worker Thread + +```c +static int worker_thread_fn( + void *data) +{ + msleep(5000); + + complete( + &data_ready); + + return 0; +} +``` + +--- + +# Execution Flow + +```text +Thread A + | + +--> wait_for_completion() + | + +--> Sleep + +Thread B + | + +--> Do Work + | + +--> complete() + +Thread A + | + +--> Wake Up +``` + +--- + +# Typical Driver Example + +## Firmware Loading + +```text +Driver + | + +--> Request Firmware + | + +--> Wait + +Firmware Thread + | + +--> Load Firmware + | + +--> complete() +``` + +--- + +## DMA Transfer + +```text +Start DMA + | + +--> Wait + +DMA ISR + | + +--> complete() +``` + +--- + +## Device Initialization + +```text +Main Driver + | + +--> Wait + +Worker Thread + | + +--> Hardware Setup + | + +--> complete() +``` + +--- + +# Completion vs Semaphore + +| Feature | Completion | Semaphore | +| ------------------ | ---------- | --------- | +| Event Notification | Yes | Limited | +| Resource Counting | No | Yes | +| One-Time Event | Excellent | Poor | +| Simplicity | High | Moderate | + +--- + +# Completion vs Mutex + +| Feature | Completion | Mutex | +| --------------------- | ---------- | ----- | +| Synchronization Event | Yes | No | +| Resource Protection | No | Yes | +| Lock Ownership | No | Yes | +| Sleep/Wakeup | Yes | Yes | + +--- + +# Completion vs Wait Queue + +| Feature | Completion | Wait Queue | +| ----------------------- | ---------- | ---------- | +| Specialized Event Sync | Yes | No | +| Simplicity | High | Lower | +| Explicit Wake Condition | No | Yes | +| Readability | Excellent | Moderate | + +--- + +# Common Driver Use Cases + +## DMA Completion + +```text +Start DMA + | + +--> Wait + +DMA Interrupt + | + +--> complete() +``` + +--- + +## Firmware Loading + +```text +Load Firmware + | + +--> Wait + +Firmware Ready + | + +--> complete() +``` + +--- + +## Hardware Initialization + +```text +Init Driver + | + +--> Wait + +Hardware Ready + | + +--> complete() +``` + +--- + +## Thread Synchronization + +```text +Thread A Waits + +Thread B Finishes + +Thread B Calls complete() +``` + +--- + +# Common Mistakes + +## Forgetting Initialization + +Wrong: + +```c +struct completion comp; + +wait_for_completion( + &comp); +``` + +Correct: + +```c +init_completion( + &comp); +``` + +--- + +## Double init_completion() + +Wrong: + +```c +init_completion(&comp); + +init_completion(&comp); +``` + +Use: + +```c +reinit_completion( + &comp); +``` + +instead. ([Linux Kernel Documentation][3]) + +--- + +## Waiting in Atomic Context + +Wrong: + +```c +spin_lock(&lock); + +wait_for_completion( + &comp); +``` + +Reason: + +```text +wait_for_completion() +Can Sleep +``` + +Only use in process context. ([Linux Kernel Documentation][3]) + +--- + +# Important APIs Summary + +## Initialization + +```c +DECLARE_COMPLETION() + +init_completion() + +reinit_completion() +``` + +--- + +## Waiting + +```c +wait_for_completion() + +wait_for_completion_timeout() + +wait_for_completion_interruptible() + +wait_for_completion_interruptible_timeout() + +wait_for_completion_killable() + +wait_for_completion_killable_timeout() + +try_wait_for_completion() +``` + +--- + +## Wake Up + +```c +complete() + +complete_all() +``` + +--- + +## Status + +```c +completion_done() +``` + +--- + +# Key Takeaways + +* Completion is a synchronization mechanism for **event notification**. +* It is built on top of Linux waitqueues. +* One thread waits using `wait_for_completion()`. +* Another thread signals using `complete()`. +* `complete()` wakes one waiter; `complete_all()` wakes all waiters. +* Completion eliminates busy waiting and race-prone polling loops. +* It is ideal for DMA completion, firmware loading, hardware initialization, and thread synchronization. +* Waiting APIs can sleep and must be used only in process context. +* Completion code is generally simpler and clearer than using semaphores or raw waitqueues for event synchronization. ([EmbeTronicX][1]) + +[1]: https://embetronicx.com/tutorials/linux/device-drivers/completion-in-linux/?utm_source=chatgpt.com "Completion in Linux - Linux Device Driver Tutorial Part 28" +[2]: https://www.kernel.org/doc/html/latest/scheduler/completion.html?utm_source=chatgpt.com "Completions - “wait for completion” barrier APIs — The Linux Kernel documentation" +[3]: https://docs.kernel.org/scheduler/completion.html?utm_source=chatgpt.com "Completions - “wait for completion” barrier APIs — The Linux Kernel documentation" + + +## Conclusion This is just a basic linux device driver. This will explain completion in the linux device driver. Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/completion-in-linux/ \ No newline at end of file +https://embetronicx.com/tutorials/linux/device-drivers/completion-in-linux/ diff --git a/Linux/Device_Driver/Device_File_Creation/ReadMe.md b/Linux/Device_Driver/Device_File_Creation/ReadMe.md index ba490dc..fc5956e 100644 --- a/Linux/Device_Driver/Device_File_Creation/ReadMe.md +++ b/Linux/Device_Driver/Device_File_Creation/ReadMe.md @@ -1,3 +1,164 @@ +# Device File Creation for Character Drivers + +This repository demonstrates how to create device files (device nodes) under `/dev` for a Linux character device driver. It covers both manual creation and automatic creation using kernel APIs and udev. + +## Goal + +The main goals of this example are: + +- To understand why device files are needed for character drivers +- To manually create a device file (for example with `mknod`) +- To automatically create a device file using kernel classes and device APIs +- To see how `udev` rules can control device node names and permissions + +## Why Device Files? + +In Linux: + +- User-space programs interact with devices via device files like `/dev/mydev` +- The kernel uses these files to route I/O to the correct driver +- The device file encodes the major and minor number of the device + +Without a device file, a character driver is registered but not easily usable from user space. + +## Manual Device File Creation + +A traditional approach is: + +1. Register the character device with a known major number +2. Use `mknod` to create the device file manually: + +```bash +sudo mknod /dev/mydev c 0 +``` + +where: + +- `c` indicates a character device +- `` is the major number assigned to your driver +- `0` is the minor number + +This method is simple but not ideal for modern systems because: + +- The device file won’t be recreated automatically on reboot +- It doesn’t integrate well with dynamic device management + +## Automatic Device File Creation + +Modern kernels use `udev` to manage device nodes automatically. Common approaches in the driver: + +- Create a device class with `class_create()` (or the newer `device_class` API) +- Create a device with `device_create()` (or `dev_device_create()` variants) +- The kernel then asks `udev` to create a device node under `/dev` automatically + +This approach: + +- Recreates device nodes automatically on boot/reload +- Supports permissions and naming via udev rules +- Is the recommended method for modern drivers + +Key APIs often used: + +- `class_create()` / `device_create()` (older style) +- `device_class` and related helpers (newer style) +- Cleanup with `class_destroy()` / `device_destroy()` + +Your exact code may vary depending on the kernel version and tutorial style. + +## Files in this Repository + +- `dev_file_creation.c` – Driver source that creates a character device and device file +- `Makefile` – Builds the kernel module using the kernel build system +- `README.md` – This documentation +- (Optional) `udev.rules` – Example udev rule file for custom naming/permissions + +Rename files to match your actual source. + +## Building the Module + +Ensure kernel headers and tools are installed. + +To build: + +```bash +make +``` + +This should produce a `.ko` file (for example `dev_file_creation.ko`). + +To clean: + +```bash +make clean +``` + +## Loading the Module + +Insert the module: + +```bash +sudo insmod dev_file_creation.ko +dmesg | tail +``` + +Check: + +- The driver prints the major number and confirms device creation +- A device node appears under `/dev` (e.g., `/dev/mydev`) +- The device is listed in `/proc/devices` and/or under `/sys/class/` + +## Unloading the Module + +To remove the module: + +```bash +sudo rmmod dev_file_creation +dmesg | tail +``` + +Ensure the driver: + +- Removes the device node (`device_destroy()` or equivalent) +- Destroys the device class (`class_destroy()` or equivalent) +- Unregisters the character device region + +## Testing from User Space + +Once the device node exists: + +```bash +echo "hello" | sudo tee /dev/your_device_name +sudo cat /dev/your_device_name +``` + +This exercises `write` and `read` callbacks if they are implemented. Replace `/dev/your_device_name` with the actual device name. + +## Customizing Device Name and Permissions with udev + +You can: + +- Create a udev rule file (for example `/etc/udev/rules.d/99-mydev.rules`) +- Control the device node name and permissions (e.g. `MODE="0666"`) + +Example rule: + +```text +KERNEL=="mydev", NAME="mydev", MODE="0666" +``` + +Then reload udev rules: + +```bash +sudo udevadm control --reload-rules +``` + +## Prerequisites + +- Linux system with kernel headers and build tools +- Basic understanding of character devices and kernel modules +- Familiarity with `insmod`, `rmmod`, `dmesg`, `mknod`, and `udev` + +## Conclusion This is just a basic linux device driver. This will explain about the device file and how to create that in the linux device driver. Please update your Beaglebone board's kernel directory in the Makefile. diff --git a/Linux/Device_Driver/EXPORT_SYMBOL/README.md b/Linux/Device_Driver/EXPORT_SYMBOL/README.md new file mode 100644 index 0000000..c7f7960 --- /dev/null +++ b/Linux/Device_Driver/EXPORT_SYMBOL/README.md @@ -0,0 +1,776 @@ +# Linux Device Driver - EXPORT_SYMBOL in Linux Kernel + +## Overview + +`EXPORT_SYMBOL()` is a Linux kernel macro used to make a function or variable available to other loadable kernel modules. Without exporting a symbol, it remains private to the module where it is defined. ([EmbeTronicX][1]) + +Think of it as: + +```text +Driver A + | + +--> Function + +--> Variable + | + +--> EXPORT_SYMBOL() + | + v +Kernel Symbol Table + | + v +Driver B Can Access It +``` + +--- + +# What is a Symbol? + +In Linux kernel terminology, a symbol is: + +* Function name +* Global variable +* Data structure instance + +Examples: + +```c +int device_count; + +void device_init(void); +``` + +Both are symbols. + +A symbol represents a named location in memory containing either: + +```text +Data -> Variables +Code -> Functions +``` + +([EmbeTronicX][1]) + +--- + +# Why EXPORT_SYMBOL? + +Suppose we have two drivers: + +```text +Driver1 + | + +--> Common Function + +--> Shared Variable + +Driver2 + | + +--> Wants To Use Them +``` + +Without exporting: + +```text +Driver2 + | + +--> Unknown Symbol Error +``` + +With exporting: + +```text +Driver1 + | + +--> EXPORT_SYMBOL() + +Driver2 + | + +--> Access Allowed +``` + +This enables inter-module communication without modifying kernel source code. ([EmbeTronicX][1]) + +--- + +# Historical Background + +## Linux 2.4 + +All non-static symbols were exported automatically. + +```text +Global Function + | + +--> Visible Everywhere +``` + +--- + +## Linux 2.6 and Later + +Only explicitly exported symbols are visible. + +```text +Function + | + +--> EXPORT_SYMBOL() + | + +--> Visible +``` + +Otherwise: + +```text +Private Symbol +``` + +This reduced unnecessary symbol exposure. ([EmbeTronicX][1]) + +--- + +# EXPORT_SYMBOL Architecture + +```text +Driver1 + | + +--> Function + | + +--> EXPORT_SYMBOL() + | + v + Kernel Symbol Table + | + v +Driver2 + | + +--> extern Declaration + | + +--> Function Call +``` + +--- + +# Basic Syntax + +## Export Function + +```c +void shared_function(void) +{ + pr_info("Hello\n"); +} + +EXPORT_SYMBOL(shared_function); +``` + +--- + +## Export Variable + +```c +int device_count = 0; + +EXPORT_SYMBOL(device_count); +``` + +This makes both symbols available to other modules. ([EmbeTronicX][1]) + +--- + +# Exporting Functions + +## Provider Module + +```c +void etx_shared_func(void) +{ + pr_info("Shared function called\n"); +} + +EXPORT_SYMBOL(etx_shared_func); +``` + +--- + +## Consumer Module + +```c +void etx_shared_func(void); + +etx_shared_func(); +``` + +Execution: + +```text +Consumer Module + | + +--> Calls Function + | + v +Provider Module + | + +--> Function Executes +``` + +([EmbeTronicX][1]) + +--- + +# Exporting Variables + +## Provider + +```c +int etx_count = 0; + +EXPORT_SYMBOL(etx_count); +``` + +--- + +## Consumer + +```c +extern int etx_count; + +pr_info("%d\n", etx_count); +``` + +Execution: + +```text +Provider + | + +--> etx_count + +Consumer + | + +--> Read/Modify etx_count +``` + +([EmbeTronicX][1]) + +--- + +# Example Architecture + +```text ++------------------+ +| Driver 1 | ++------------------+ +| etx_shared_func | +| etx_count | ++------------------+ + | + | +EXPORT_SYMBOL() + | + v ++------------------+ +| Kernel Symbol | +| Table | ++------------------+ + | + | + v ++------------------+ +| Driver 2 | ++------------------+ +| Uses Function | +| Uses Variable | ++------------------+ +``` + +--- + +# Example Provider Driver + +## Shared Variable + +```c +int etx_count = 0; +``` + +--- + +## Shared Function + +```c +void etx_shared_func(void) +{ + pr_info("Shared function called\n"); + + etx_count++; +} +``` + +--- + +## Export Symbols + +```c +EXPORT_SYMBOL(etx_shared_func); + +EXPORT_SYMBOL(etx_count); +``` + +Now both become globally visible to loadable modules. ([EmbeTronicX][1]) + +--- + +# Example Consumer Driver + +## Import Variable + +```c +extern int etx_count; +``` + +--- + +## Import Function + +```c +void etx_shared_func(void); +``` + +--- + +## Use Them + +```c +etx_shared_func(); + +pr_info("%d\n", etx_count); +``` + +Execution: + +```text +Read Device + | + +--> Shared Function + | + +--> Increment Counter + | + +--> Print Counter +``` + +([EmbeTronicX][1]) + +--- + +# Module Loading Order + +This is extremely important. + +Correct: + +```text +insmod driver1.ko + +insmod driver2.ko +``` + +Because: + +```text +Driver1 + | + +--> Defines Symbol + +Driver2 + | + +--> Uses Symbol +``` + +--- + +Wrong: + +```text +insmod driver2.ko +``` + +Error: + +```text +Unknown symbol +``` + +Because the symbol does not exist in the kernel symbol table yet. ([EmbeTronicX][1]) + +--- + +# Module Unloading Order + +Correct: + +```text +rmmod driver2 + +rmmod driver1 +``` + +Wrong: + +```text +rmmod driver1 +``` + +while driver2 is using it. + +Error: + +```text +Module in use +``` + +([EmbeTronicX][1]) + +--- + +# Module.symvers + +After compilation: + +```bash +make +``` + +Kernel generates: + +```text +Module.symvers +``` + +Example: + +```text +0x1db7034a etx_shared_func EXPORT_SYMBOL + +0x6dcb135c etx_count EXPORT_SYMBOL +``` + +This file records exported symbols and version information. ([EmbeTronicX][1]) + +--- + +# Checking Exported Symbols + +## Using kallsyms + +```bash +cat /proc/kallsyms | grep etx_shared_func +``` + +or + +```bash +cat /proc/kallsyms | grep etx_count +``` + +If visible: + +```text +Symbol Successfully Exported +``` + +([EmbeTronicX][1]) + +--- + +# EXPORT_SYMBOL_GPL + +Linux also provides: + +```c +EXPORT_SYMBOL_GPL(symbol); +``` + +Difference: + +| Macro | Accessible By | +| ----------------- | ---------------- | +| EXPORT_SYMBOL | Any Module | +| EXPORT_SYMBOL_GPL | GPL Modules Only | + +Example: + +```c +EXPORT_SYMBOL_GPL(my_func); +``` + +Only GPL licensed modules can use it. ([EmbeTronicX][1]) + +--- + +# EXPORT_SYMBOL vs EXPORT_SYMBOL_GPL + +| Feature | EXPORT_SYMBOL | EXPORT_SYMBOL_GPL | +| ------------------ | ------------- | -------------------- | +| GPL Module | Yes | Yes | +| Proprietary Module | Yes | No | +| Kernel Enforcement | Normal | GPL Only | +| Common Usage | Generic APIs | Internal Kernel APIs | + +--- + +# Limitations + +## Cannot Export Static Symbols + +Wrong: + +```c +static int count; + +EXPORT_SYMBOL(count); +``` + +Reason: + +```text +static + | + +--> File Scope Only +``` + +Exported symbols must have global visibility. ([EmbeTronicX][1]) + +--- + +## Avoid Inline Functions + +Wrong: + +```c +inline void my_func(void) +{ +} +``` + +```c +EXPORT_SYMBOL(my_func); +``` + +Inline functions may not generate a standalone symbol. ([EmbeTronicX][1]) + +--- + +# Execution Flow + +```text +Load Driver1 + | + v +Export Symbols + | + v +Kernel Symbol Table + | + v +Load Driver2 + | + v +Resolve Symbols + | + v +Driver2 Uses Them +``` + +--- + +# Common Use Cases + +## Shared Utility Functions + +```text +Driver A + | + +--> CRC Calculation + +Driver B + | + +--> Reuse Same Function +``` + +--- + +## Shared Device State + +```text +Driver A + | + +--> Device Status + +Driver B + | + +--> Read Status +``` + +--- + +## Common Hardware Layer + +```text +GPIO Driver + | + +--> Export API + +Other Drivers + | + +--> Use API +``` + +--- + +## Platform Frameworks + +```text +Core Driver + | + +--> Export Services + +Child Drivers + | + +--> Consume Services +``` + +--- + +# Common Errors + +## Unknown Symbol + +```text +insmod: ERROR: +Unknown symbol +``` + +Possible reasons: + +* Provider module not loaded +* Symbol not exported +* Name mismatch + +--- + +## Static Symbol Export + +Wrong: + +```c +static int value; + +EXPORT_SYMBOL(value); +``` + +--- + +## Missing extern + +Wrong: + +```c +int etx_count; +``` + +Correct: + +```c +extern int etx_count; +``` + +--- + +# EXPORT_SYMBOL vs Header Files + +Many beginners ask: + +```text +Why not just include a header? +``` + +Header files provide: + +```text +Declaration +``` + +EXPORT_SYMBOL provides: + +```text +Kernel Module Linkage +``` + +Both are required. + +```text +Header File + | + +--> Compiler Knows Symbol + +EXPORT_SYMBOL + | + +--> Kernel Loader Resolves Symbol +``` + +([Reddit][2]) + +--- + +# Important APIs Summary + +## Export + +```c +EXPORT_SYMBOL() + +EXPORT_SYMBOL_GPL() +``` + +--- + +## Import + +```c +extern variable; + +function declaration; +``` + +--- + +## Verification + +```bash +cat /proc/kallsyms + +cat Module.symvers +``` + +--- + +# Key Takeaways + +* `EXPORT_SYMBOL()` exposes functions or variables to other kernel modules. +* Only exported symbols can be resolved by loadable modules. +* Provider module must be loaded before consumer module. +* Exported symbols appear in the kernel symbol table. +* `EXPORT_SYMBOL_GPL()` restricts usage to GPL-licensed modules. +* Symbols should not be `static` or `inline`. +* `Module.symvers` records exported symbol information. +* Commonly used for sharing APIs, utility functions, hardware abstraction layers, and global state between kernel modules. ([EmbeTronicX][1]) + +**Source:** EmbeTronicX Linux Device Driver Tutorial Part 29 – EXPORT_SYMBOL in Linux Device Driver. ([EmbeTronicX][1]) + +[1]: https://embetronicx.com/tutorials/linux/device-drivers/export_symbol-in-linux-device-driver/?utm_source=chatgpt.com "EXPORT_SYMBOL in Linux Kernel - Linux Device Driver Part 29" +[2]: https://www.reddit.com/r/kernel/comments/180ktd8?utm_source=chatgpt.com "Why do we need the EXPORT_SYMBOL() macro?" + + +## Conclusion +This is just a basic linux device driver which explains about the EXPORT_SYMBOL in linux device driver. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/export_symbol-in-linux-device-driver/ diff --git a/Linux/Device_Driver/EXPORT_SYMBOL/ReadMe.md b/Linux/Device_Driver/EXPORT_SYMBOL/ReadMe.md deleted file mode 100644 index 35f11bf..0000000 --- a/Linux/Device_Driver/EXPORT_SYMBOL/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains about the EXPORT_SYMBOL in linux device driver. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/export_symbol-in-linux-device-driver/ \ No newline at end of file diff --git a/Linux/Device_Driver/File_Operations/README.md b/Linux/Device_Driver/File_Operations/README.md new file mode 100644 index 0000000..338baa4 --- /dev/null +++ b/Linux/Device_Driver/File_Operations/README.md @@ -0,0 +1,168 @@ +# cdev Structure and File Operations of Character Drivers + +This repository demonstrates how to use the Linux kernel `cdev` structure and `file_operations` to implement a character device driver. It follows modern kernel practices for character drivers instead of the older `register_chrdev()` approach. + +## Goal + +The main goals of this example are: + +- To understand the role of the `cdev` structure in character drivers +- To define and register `file_operations` callbacks (`open`, `read`, `write`, `release`, etc.) +- To use the modern kernel API (`alloc_chrdev_region`, `cdev_init`, `cdev_add`, etc.) to register a character device +- To create a device file under `/dev` automatically using a device class + +## What Is `cdev`? + +In modern Linux kernels: + +- `cdev` represents a character device in the kernel +- Each `cdev` is associated with: + - A device number range (major/minor) + - A `file_operations` structure that defines how to handle I/O + +Instead of using the old `register_chrdev()` directly, you: + +1. Allocate a device number range with `alloc_chrdev_region()` +2. Initialize a `cdev` with `cdev_init()` +3. Set its `file_operations` +4. Add it to the system with `cdev_add()` +5. Clean up with `cdev_del()` and `free_chrdev_region()` + +This approach is more flexible and aligns with the kernel’s internal device model. + +## File Operations (`file_operations`) + +The `file_operations` structure defines callbacks that the kernel invokes when user space performs operations on the device file: + +Common callbacks include: + +| Callback | Purpose | +|----------------|----------------------------------------------| +| `open` | Called when the device file is opened | +| `read` | Called when data is read from the device | +| `write` | Called when data is written to the device | +| `release` | Called when the device file is closed | +| `unlocked_ioctl` | Optional: for ioctl commands (if needed) | + +Your driver defines these functions and assigns them to a `struct file_operations`: + +```c +static const struct file_operations fops = { + .open = my_open, + .read = my_read, + .write = my_write, + .release = my_release, +}; +``` + +Then this is linked to the `cdev` via `cdev_init()`. + +## Key Kernel APIs Used + +Typical APIs for a modern character driver: + +- `alloc_chrdev_region(&dev, 0, 1, MY_CLASS_NAME)` – Allocate device numbers +- `cdev_init(&cd, &fops)` – Initialize `cdev` with file operations +- `cdev_add(&cd, dev, 1)` – Register the character device +- `cdev_del(&cd)` – Remove the character device +- `free_chrdev_region(dev, 1)` – Free the allocated device numbers +- `class_create()` / `device_create()` – Create device class and node under `/dev` +- `class_destroy()` / `device_destroy()` – Cleanup + +Your exact code may use slightly different names depending on kernel version and the tutorial. + +## Files in this Repository + +- `cdev_fileops.c` – Driver source implementing a character device using `cdev` and `file_operations` +- `Makefile` – Builds the kernel module using the kernel build system +- `README.md` – This documentation + +Rename files to match your actual source. + +## Building the Module + +Ensure kernel headers and tools are installed. + +To build: + +```bash +make +``` + +This should produce a `.ko` file (for example `cdev_fileops.ko`). + +To clean: + +```bash +make clean +``` + +## Loading the Module + +Insert the module: + +```bash +sudo insmod cdev_fileops.ko +dmesg | tail +``` + +Check: + +- The driver prints the major number and confirms device registration +- A device node appears under `/dev` (e.g., `/dev/mydev`) +- The device is listed in `/proc/devices` and under `/sys/class/` + +## Unloading the Module + +To remove the module: + +```bash +sudo rmmod cdev_fileops +dmesg | tail +``` + +Ensure the driver: + +- Removes the device node (`device_destroy()`) +- Destroys the device class (`class_destroy()`) +- Deletes the `cdev` (`cdev_del()`) +- Frees the device number range (`free_chrdev_region()`) + +## Testing from User Space + +Once the device node exists: + +```bash +echo "hello" | sudo tee /dev/your_device_name +sudo cat /dev/your_device_name +``` + +This exercises `write` and `read` callbacks if they are implemented. Replace `/dev/your_device_name` with the actual device name. + +## Prerequisites + +- Linux system with kernel headers and build tools +- Basic understanding of character devices and kernel modules +- Familiarity with `make`, `insmod`, `rmmod`, `dmesg`, and `/proc/devices` +- Comfort reading and writing C code for the kernel + +## Conclusion +This is just a basic linux device driver which explains about the file operations. + +Please update your Beaglebone board's kernel directory in the Makefile. + +Build for Beaglebone: + sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- + +Build for Raspberry Pi or Virtualbox Ubuntu: + sudo make + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/cdev-structure-and-file-operations-of-character-drivers/ + +You can check the video tutorial of this example here (https://youtu.be/20dQsadVdII). + +The Linux Device Driver Video Playlist - https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k + +How to Setup Ubuntu and Raspberry PI - https://www.youtube.com/watch?v=e6gNeje3ljA +How to Setup BeagleBone and Cross compile the kernel - https://www.youtube.com/watch?v=am-dgmrMgYY&t diff --git a/Linux/Device_Driver/File_Operations/ReadMe.md b/Linux/Device_Driver/File_Operations/ReadMe.md deleted file mode 100644 index e24442d..0000000 --- a/Linux/Device_Driver/File_Operations/ReadMe.md +++ /dev/null @@ -1,19 +0,0 @@ -This is just a basic linux device driver which explains about the file operations. - -Please update your Beaglebone board's kernel directory in the Makefile. - -Build for Beaglebone: - sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- - -Build for Raspberry Pi or Virtualbox Ubuntu: - sudo make - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/cdev-structure-and-file-operations-of-character-drivers/ - -You can check the video tutorial of this example here (https://youtu.be/20dQsadVdII). - -The Linux Device Driver Video Playlist - https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k - -How to Setup Ubuntu and Raspberry PI - https://www.youtube.com/watch?v=e6gNeje3ljA -How to Setup BeagleBone and Cross compile the kernel - https://www.youtube.com/watch?v=am-dgmrMgYY&t diff --git a/Linux/Device_Driver/Hello_World/ReadMe.md b/Linux/Device_Driver/Hello_World/ReadMe.md index 9e2f6a2..6ecd210 100644 --- a/Linux/Device_Driver/Hello_World/ReadMe.md +++ b/Linux/Device_Driver/Hello_World/ReadMe.md @@ -1,20 +1,122 @@ -This is just a basic linux device driver. This kernel module will print some debug messages at the init and exit time. +# Linux Device Driver – Part 2: First Device Driver -Please update your Beaglebone board's kernel directory in the Makefile. +This repository contains a simple Linux character device driver, based on an introductory device-driver tutorial. It shows how to create, build, load, and test a basic driver implemented as a loadable kernel module. -Build for Beaglebone: - sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- +## Goal -Build for Raspberry Pi or Virtualbox Ubuntu: - sudo make +The main goals of this example are: -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-2-first-device-driver/ +- To register a simple character device with the kernel +- To expose a device file under `/dev` +- To implement minimal `open`, `close`, `read`, and `write` callbacks +- To learn how to build and insert a kernel module -You can check the video tutorial of this project. -https://youtu.be/hMsA1bA1Upk and https://youtu.be/xqsro29xQPo +## Files in this Repository + +- `hello_world.c` – The main driver source file implementing a character device +- `Makefile` – Uses the kernel build system to build the module +- `README.md` – This documentation + +You can rename these to match your actual filenames. + +## How the Driver Works + +This driver: + +- Registers a character device with the kernel using a major/minor number +- Creates a device class and device node so that a file appears under `/dev` +- Defines a `file_operations` structure with callbacks for: + - `open` + - `release` (close) + - `read` + - `write` +- Prints kernel log messages for each operation so you can see when user space accesses the driver + +You can view these messages using `dmesg` or `journalctl`, depending on your distribution. + +## Building the Module + +Make sure you have the kernel headers and build tools installed on your system. + +To build: + +```bash +make +``` + +If successful, this produces a `.ko` file (for example `first_driver.ko`). + +To clean: + +```bash +make clean +``` + +## Loading and Unloading the Module -The Linux Device Driver Video Playlist - https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k +Insert the module: -How to Setup Ubuntu and Raspberry PI - https://www.youtube.com/watch?v=e6gNeje3ljA -How to Setup BeagleBone and Cross compile the kernel - https://www.youtube.com/watch?v=am-dgmrMgYY&t +```bash +sudo insmod first_driver.ko +dmesg | tail +``` + +Check that: + +- The module is listed in `lsmod` +- A corresponding device node is created under `/dev` (possibly via `udev`) + +Remove the module: + +```bash +sudo rmmod first_driver +dmesg | tail +``` + +Always verify that resources (device number, class, device) are cleaned up in the module’s exit function. + +## Testing from User Space + +After loading the driver and confirming the `/dev` node exists: + +```bash +echo "test" | sudo tee /dev/your_device_name +sudo cat /dev/your_device_name +``` + +These commands exercise the `write` and `read` callbacks. Replace `/dev/your_device_name` with the actual node created by your driver. + +## Prerequisites + +- Linux system with kernel headers installed +- Basic C programming knowledge +- Familiarity with `make`, `insmod`, `rmmod`, and `dmesg` + +------------------------------------------------------------------------------------------------------------ +# Hello World LDD +- This is just a basic linux device driver. This kernel module will print some debug messages at the init and exit time. +- Please update your Beaglebone board's kernel directory in the Makefile. + +## Build for Beaglebone: +``` + sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- +``` + +## Build for Raspberry Pi or Virtualbox Ubuntu: +``` + sudo make +``` +- Please refer this URL for the complete tutorial of this source code. +``` +https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-2-first-device-driver/ +``` +- You can check the video tutorial of this project. +``` +https://youtu.be/hMsA1bA1Upk and https://youtu.be/xqsro29xQPo +``` +- The Linux Device Driver Video Playlist - + ``` + https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k + ``` +- How to Setup Ubuntu and Raspberry PI - ``` https://www.youtube.com/watch?v=e6gNeje3ljA ``` +- How to Setup BeagleBone and Cross compile the kernel - ``` https://www.youtube.com/watch?v=am-dgmrMgYY&t ``` diff --git a/Linux/Device_Driver/Hello_World/hello_world.c b/Linux/Device_Driver/Hello_World/hello_world.c index 7609096..61b8db1 100644 --- a/Linux/Device_Driver/Hello_World/hello_world.c +++ b/Linux/Device_Driver/Hello_World/hello_world.c @@ -1,5 +1,5 @@ /***************************************************************************//** -* \file driver.c +* \file hello_world.c * * \details Simple hello world driver * @@ -35,4 +35,4 @@ module_exit(hello_world_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("EmbeTronicX "); MODULE_DESCRIPTION("A simple hello world driver"); -MODULE_VERSION("2:1.0"); \ No newline at end of file +MODULE_VERSION("2:1.0"); diff --git a/Linux/Device_Driver/High_Resolution_Timer/README.md b/Linux/Device_Driver/High_Resolution_Timer/README.md new file mode 100644 index 0000000..a74cf93 --- /dev/null +++ b/Linux/Device_Driver/High_Resolution_Timer/README.md @@ -0,0 +1,878 @@ +# Linux Device Driver - High Resolution Timer (hrtimer) + +## Overview + +A High Resolution Timer (**HRT**, `hrtimer`) provides precise timer functionality in the Linux kernel with **nanosecond resolution**. + +Unlike traditional kernel timers that are based on **jiffies**, High Resolution Timers use **64-bit nanosecond time values** and are suitable for applications requiring accurate timing. ([EmbeTronicX][1]) + +--- + +# Why High Resolution Timer? + +## Kernel Timer Limitation + +Traditional kernel timers: + +```text +Kernel Timer + | + +--> Based on Jiffies + | + +--> Tick Resolution +``` + +Example: + +```text +HZ = 100 + +1 Tick = 10 ms +``` + +Cannot accurately schedule: + +```text +1 us +50 us +100 ns +``` + +operations. + +--- + +## High Resolution Timer + +```text +High Resolution Timer + | + +--> Nanosecond Resolution + | + +--> High Accuracy +``` + +Suitable for: + +* Multimedia +* Real-time systems +* Precision device drivers +* Industrial control +* Sensor sampling + +Kernel timers are jiffies-based, whereas hrtimers are based on high-resolution time values. ([EmbeTronicX][1]) + +--- + +# Kernel Requirements + +High-resolution timer support must be enabled: + +```text +CONFIG_HIGH_RES_TIMERS=y +``` + +Check: + +```bash +grep CONFIG_HIGH_RES_TIMERS \ +/boot/config-$(uname -r) +``` + +Expected: + +```text +CONFIG_HIGH_RES_TIMERS=y +``` + +High-resolution timers are available in Linux kernels beginning with version 2.6.21 when enabled in kernel configuration. ([EmbeTronicX][1]) + +--- + +# Kernel Timer vs High Resolution Timer + +| Feature | Kernel Timer | High Resolution Timer | +| ---------------- | ------------ | --------------------- | +| Resolution | Jiffies | Nanoseconds | +| Precision | Lower | Very High | +| Timing Base | Tick Driven | High Resolution Clock | +| Data Structure | Timer Wheel | Red-Black Tree | +| Real-Time Usage | Limited | Excellent | +| Multimedia Usage | Moderate | Excellent | + +Linux uses a red-black tree for hrtimers to maintain timers in time order efficiently. ([EmbeTronicX][1]) + +--- + +# Typical Use Cases + +## Multimedia + +```text +Audio Playback + | + +--> Precise Timing +``` + +--- + +## Sensor Sampling + +```text +Every 100 us + | + +--> Read Sensor +``` + +--- + +## Industrial Control + +```text +Motor Control + | + +--> Accurate Trigger +``` + +--- + +## Driver Timeout + +```text +Command Sent + | + +--> Start hrtimer + | + +--> Timeout Event +``` + +Primary users include multimedia subsystems and applications needing precise timed events. ([EmbeTronicX][1]) + +--- + +# Required Header Files + +```c +#include +#include +``` + +--- + +# hrtimer Structure + +```c +struct hrtimer +{ + struct rb_node node; + + ktime_t expires; + + int (*function)( + struct hrtimer *); + + struct hrtimer_base *base; +}; +``` + +Important fields: + +| Field | Purpose | +| -------- | ------------------- | +| node | Red-black tree node | +| expires | Expiration time | +| function | Callback | +| base | Timer base | + +([EmbeTronicX][1]) + +--- + +# ktime_t + +High Resolution Timers use: + +```c +ktime_t +``` + +which stores time values with nanosecond precision. + +Example: + +```c +ktime_t time; +``` + +--- + +# ktime_set() + +Creates a ktime value. + +```c +ktime_t ktime_set( + long secs, + long nsecs); +``` + +Example: + +```c +ktime_t ktime; + +ktime = + ktime_set( + 5, + 0); +``` + +Represents: + +```text +5 Seconds +``` + +Example: + +```c +ktime_set( + 4, + 1000000000); +``` + +Represents: + +```text +4 Seconds ++ +1 Second + += +5 Seconds +``` + +([EmbeTronicX][1]) + +--- + +# Timer Callback Function + +Callback executes when timer expires. + +```c +enum hrtimer_restart +timer_callback( + struct hrtimer *timer) +{ + return HRTIMER_NORESTART; +} +``` + +Return values: + +| Return Value | Meaning | +| ----------------- | -------------- | +| HRTIMER_NORESTART | One-shot timer | +| HRTIMER_RESTART | Periodic timer | + +([EmbeTronicX][1]) + +--- + +# Initialize High Resolution Timer + +## hrtimer_init() + +```c +void hrtimer_init( + struct hrtimer *timer, + clockid_t clock_id, + enum hrtimer_mode mode); +``` + +Parameters: + +| Parameter | Description | +| --------- | -------------------- | +| timer | Timer object | +| clock_id | Clock source | +| mode | Relative or absolute | + +Example: + +```c +hrtimer_init( + &etx_hr_timer, + CLOCK_MONOTONIC, + HRTIMER_MODE_REL); +``` + +([EmbeTronicX][1]) + +--- + +# Clock Sources + +## CLOCK_MONOTONIC + +```text +System Boot + | + +--> Time Always Increases +``` + +Never jumps backwards. + +Most common choice for drivers. + +--- + +## CLOCK_REALTIME + +```text +Wall Clock Time +``` + +Affected by: + +```text +date command +NTP adjustments +RTC updates +``` + +([EmbeTronicX][1]) + +--- + +# Timer Modes + +## Relative Mode + +```c +HRTIMER_MODE_REL +``` + +Timer expires: + +```text +Current Time + Interval +``` + +Example: + +```c +5 Seconds From Now +``` + +--- + +## Absolute Mode + +```c +HRTIMER_MODE_ABS +``` + +Expires at a specific timestamp. + +([EmbeTronicX][1]) + +--- + +# Assign Callback + +After initialization: + +```c +etx_hr_timer.function = + &timer_callback; +``` + +Associates callback function with timer. + +--- + +# Start Timer + +## hrtimer_start() + +```c +int hrtimer_start( + struct hrtimer *timer, + ktime_t time, + enum hrtimer_mode mode); +``` + +Example: + +```c +ktime_t ktime; + +ktime = ktime_set(5, 0); + +hrtimer_start( + &etx_hr_timer, + ktime, + HRTIMER_MODE_REL); +``` + +Meaning: + +```text +Fire After 5 Seconds +``` + +Returns: + +| Value | Meaning | +| ----- | -------------------- | +| 0 | Timer inactive | +| 1 | Timer already active | + +([EmbeTronicX][1]) + +--- + +# One-Shot Timer + +```c +enum hrtimer_restart +timer_callback( + struct hrtimer *timer) +{ + printk("Expired\n"); + + return HRTIMER_NORESTART; +} +``` + +Execution: + +```text +Start + | + v +Expire + | + v +Callback + | + v +Stop +``` + +--- + +# Periodic Timer + +For periodic behavior: + +```c +enum hrtimer_restart +timer_callback( + struct hrtimer *timer) +{ + hrtimer_forward_now( + timer, + interval); + + return HRTIMER_RESTART; +} +``` + +Execution: + +```text +Expire + | + v +Callback + | + v +Forward Timer + | + v +Restart +``` + +([EmbeTronicX][1]) + +--- + +# hrtimer_forward() + +Move timer forward. + +```c +u64 hrtimer_forward( + struct hrtimer *timer, + ktime_t now, + ktime_t interval); +``` + +Used for periodic timers. + +Returns: + +```text +Overrun Count +``` + +([EmbeTronicX][1]) + +--- + +# hrtimer_forward_now() + +Most common periodic timer API. + +```c +u64 hrtimer_forward_now( + struct hrtimer *timer, + ktime_t interval); +``` + +Example: + +```c +hrtimer_forward_now( + timer, + interval); +``` + +Schedules next expiration relative to current time. ([EmbeTronicX][1]) + +--- + +# Stop Timer + +## hrtimer_cancel() + +```c +int hrtimer_cancel( + struct hrtimer *timer); +``` + +Behavior: + +```text +Cancel Timer +Wait For Callback +Return +``` + +Returns: + +| Value | Meaning | +| ----- | -------------- | +| 0 | Timer inactive | +| 1 | Timer active | + +([EmbeTronicX][1]) + +--- + +# hrtimer_try_to_cancel() + +```c +int hrtimer_try_to_cancel( + struct hrtimer *timer); +``` + +Returns: + +| Value | Meaning | +| ----- | ---------------- | +| 1 | Stopped | +| 0 | Not Active | +| -1 | Callback Running | + +([EmbeTronicX][1]) + +--- + +# Timer Status APIs + +## hrtimer_get_remaining() + +```c +ktime_t +hrtimer_get_remaining( + const struct hrtimer *timer); +``` + +Returns remaining time. + +--- + +## hrtimer_callback_running() + +```c +int +hrtimer_callback_running( + struct hrtimer *timer); +``` + +Returns: + +```text +0 -> Not Running +1 -> Running +``` + +--- + +## hrtimer_cb_get_time() + +```c +ktime_t +hrtimer_cb_get_time( + struct hrtimer *timer); +``` + +Returns current timer time. + +([EmbeTronicX][1]) + +--- + +# Example Driver Flow + +## Initialization + +```c +ktime_t ktime; + +ktime = + ktime_set( + 5, + 0); + +hrtimer_init( + &etx_hr_timer, + CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + +etx_hr_timer.function = + &timer_callback; + +hrtimer_start( + &etx_hr_timer, + ktime, + HRTIMER_MODE_REL); +``` + +--- + +## Callback + +```c +enum hrtimer_restart +timer_callback( + struct hrtimer *timer) +{ + printk( + "Timer Fired\n"); + + hrtimer_forward_now( + timer, + interval); + + return HRTIMER_RESTART; +} +``` + +--- + +## Cleanup + +```c +hrtimer_cancel( + &etx_hr_timer); +``` + +--- + +# Execution Flow + +```text +Module Load + | + v +hrtimer_init() + | + v +hrtimer_start() + | + v +Timer Expires + | + v +Callback + | + +--> HRTIMER_NORESTART + | | + | +--> Stop + | + +--> HRTIMER_RESTART + | + +--> Restart +``` + +--- + +# Callback Context + +High-resolution timer callbacks execute in: + +```text +SoftIRQ Context +``` + +Therefore: + +### Allowed + +```c +spin_lock(); +atomic operations; +``` + +### Not Allowed + +```c +msleep(); +schedule(); +mutex_lock(); +copy_to_user(); +``` + +Callbacks must be short and non-blocking. This design is part of why hrtimers are suitable for precision timing. ([EmbeTronicX][1]) + +--- + +# Kernel Timer vs HRTimer vs Workqueue + +| Feature | Kernel Timer | HRTimer | Workqueue | +| --------------- | ------------ | ----------- | --------- | +| Resolution | Jiffies | Nanoseconds | N/A | +| Context | SoftIRQ | SoftIRQ | Process | +| Sleep Allowed | No | No | Yes | +| Precision | Low | High | N/A | +| Long Processing | No | No | Yes | + +--- + +# Common Embedded Linux Use Cases + +## Periodic Sensor Sampling + +```text +100 us + | + +--> Read ADC +``` + +--- + +## Multimedia Synchronization + +```text +Audio Frame + | + +--> Precise Playback +``` + +--- + +## Protocol Timing + +```text +Industrial Bus + | + +--> Precise Timeout +``` + +--- + +## Motor Control + +```text +Control Loop + | + +--> Accurate Period +``` + +--- + +# Important APIs Summary + +## Time Creation + +```c +ktime_set() +``` + +## Initialization + +```c +hrtimer_init() +``` + +## Start + +```c +hrtimer_start() +``` + +## Stop + +```c +hrtimer_cancel() + +hrtimer_try_to_cancel() +``` + +## Periodic Support + +```c +hrtimer_forward() + +hrtimer_forward_now() +``` + +## Status + +```c +hrtimer_get_remaining() + +hrtimer_callback_running() + +hrtimer_cb_get_time() +``` + +--- + +# Key Takeaways + +* High Resolution Timers provide **nanosecond-resolution timing**. +* They are not based on jiffies and are far more precise than traditional kernel timers. +* `ktime_t` is the primary time representation. +* `hrtimer_init()` initializes the timer. +* `hrtimer_start()` starts the timer. +* `hrtimer_cancel()` safely stops the timer. +* Returning `HRTIMER_RESTART` creates periodic behavior. +* Callbacks execute in SoftIRQ context and cannot sleep. +* HRTimers are commonly used in multimedia, real-time control, industrial automation, and precision driver timing. ([EmbeTronicX][1]) + +**Source:** EmbeTronicX Linux Device Driver Tutorial Part 27 – High Resolution Timer in Linux Device Driver. ([EmbeTronicX][1]) + +[1]: https://embetronicx.com/tutorials/linux/device-drivers/using-high-resolution-timer-in-linux-device-driver/?utm_source=chatgpt.com "High Resolution Timer - Linux Device Driver Tutorial Part 27" + + +## Conclusion +This is just a basic linux device driver which explains about the kernel timer in linux device driver. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/using-high-resolution-timer-in-linux-device-driver/ diff --git a/Linux/Device_Driver/High_Resolution_Timer/ReadMe.md b/Linux/Device_Driver/High_Resolution_Timer/ReadMe.md deleted file mode 100644 index 080dbe4..0000000 --- a/Linux/Device_Driver/High_Resolution_Timer/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains about the kernel timer in linux device driver. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/using-high-resolution-timer-in-linux-device-driver/ \ No newline at end of file diff --git a/Linux/Device_Driver/IOCTL/ReadMe.md b/Linux/Device_Driver/IOCTL/ReadMe.md index 2d768b6..d25b22c 100644 --- a/Linux/Device_Driver/IOCTL/ReadMe.md +++ b/Linux/Device_Driver/IOCTL/ReadMe.md @@ -1,4 +1,256 @@ +# ioctl Tutorial in Linux Device Drivers + +This repository demonstrates how to implement and use `ioctl` (Input/Output Control) in a Linux character device driver. It shows how to add custom commands beyond basic `read`/`write` and how user-space applications can invoke them via the `ioctl()` system call. + +`ioctl` is commonly used for: + +- Device configuration (e.g., setting modes, speeds, parameters) +- Control operations (e.g., start/stop, reset, enable/disable) +- Querying device status or capabilities + +Reference: Linux kernel documentation on ioctl-based interfaces [web:15]. + +## Goal + +The main goals of this example are: + +- To define custom `ioctl` commands using the kernel’s `_IO*` macros +- To implement an `unlocked_ioctl` (or `ioctl`) function in the driver +- To safely pass arguments between user space and kernel space +- To show how a user-space application calls `ioctl()` on a device file + +## What Is `ioctl`? + +`ioctl` is a system call for device-specific control operations that don’t fit naturally into `read`/`write`. + +In user space: + +```c +#include +int ioctl(int fd, unsigned long request, ...); +``` + +- `fd`: file descriptor of the device (from `open()`) +- `request`: ioctl command code +- Optional argument: pointer to data or an integer value + +In the kernel driver: + +```c +#include +#include + +static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + // handle cmd and arg +} +``` + +Reference: O’Reilly’s “The ioctl Method” in Linux Device Drivers [web:12]. + +## Defining ioctl Commands + +Custom ioctl commands are defined using macros from ``: + +```c +#define MY_IOC_MAGIC 'k' + +#define MY_IOC_HELLO _IO(MY_IOC_MAGIC, 0) // no argument +#define MY_IOC_SETVAL _IOW(MY_IOC_MAGIC, 1, int) // write (user -> kernel) +#define MY_IOC_GETVAL _IOR(MY_IOC_MAGIC, 2, int) // read (kernel -> user) +#define MY_IOC_BOTH _IOWR(MY_IOC_MAGIC, 3, int) // read-write +``` + +Key macros: + +- `_IO(magic, nr)` – no argument +- `_IOW(magic, nr, type)` – write (user → kernel) +- `_IOR(magic, nr, type)` – read (kernel → user) +- `_IOWR(magic, nr, type)` – read-write + +- `magic`: a unique character (e.g., `'k'`) to distinguish your driver +- `nr`: command number +- `type`: C type of the argument (e.g., `int`, `struct my_config`) + +Reference: Example ioctl command definitions in a Linux driver repo [web:11]. + +## File Operations with ioctl + +In the driver, you define a `file_operations` structure that includes `unlocked_ioctl`: + +```c +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = my_open, + .release = my_release, + .read = my_read, + .write = my_write, + .unlocked_ioctl = my_ioctl, +}; +``` + +The `unlocked_ioctl` prototype: + +```c +static long my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case MY_IOC_HELLO: + // no arg + break; + case MY_IOC_SETVAL: + { + int val; + if (copy_from_user(&val, (int __user *)arg, sizeof(int))) + return -EFAULT; + // use val in kernel + break; + } + case MY_IOC_GETVAL: + { + int val = /* some kernel value */; + if (copy_to_user((int __user *)arg, &val, sizeof(int))) + return -EFAULT; + break; + } + default: + return -EINVAL; + } + return 0; +} +``` + +Key points: + +- Use `copy_from_user()` to get data from user space. +- Use `copy_to_user()` to send data to user space. +- Return `-EINVAL` for unknown commands. +- Return `-EFAULT` if copy fails. + +Reference: Example `unlocked_ioctl` implementation in a Linux driver [web:11]. + +## Files in This Repository + +- `ioctl_driver.c` – Kernel driver with custom ioctl commands +- `ioctl_test.c` – User-space application that calls `ioctl()` +- `Makefile` – Builds both the kernel module and user app +- `README.md` – This documentation + +Rename files to match your actual source. + +## Building the Kernel Module + +Ensure kernel headers and tools are installed. + +To build the module: + +```bash +make module +``` + +This produces `ioctl_driver.ko`. + +To build the user-space app: + +```bash +make app +``` + +This produces `ioctl_test`. + +To clean: + +```bash +make clean +``` + +## Loading the Module + +Insert the module: + +```bash +sudo insmod ioctl_driver.ko +dmesg | tail +``` + +Check: + +- The driver prints major/minor numbers and confirms device creation +- A device node appears under `/dev` (e.g., `/dev/ioctl_dev`) + +## Running the User-Space Application + +```bash +sudo ./ioctl_test +``` + +Typical interactions: + +```text +1. Hello (no arg) +2. Set value +3. Get value +4. Exit +``` + +Examples: + +- **Hello**: calls `ioctl(fd, MY_IOC_HELLO, 0)` +- **Set value**: calls `ioctl(fd, MY_IOC_SETVAL, &val)` +- **Get value**: calls `ioctl(fd, MY_IOC_GETVAL, &val)` and prints the returned value + +The driver logs each ioctl command in `pr_info()`/`pr_err()`. Check with: + +```bash +dmesg | tail +``` + +## Unloading the Module + +```bash +sudo rmmod ioctl_driver +dmesg | tail +``` + +Ensure the driver: + +- Removes the device node +- Destroys the device class +- Deletes the `cdev` +- Unregisters the chrdev region + +## Example ioctl Commands + +Common patterns: + +| Command | Direction | Example Use | +|------------------|----------------|--------------------------------| +| `MY_IOC_HELLO` | none | Simple “hello” control | +| `MY_IOC_SETVAL` | user → kernel | Set a parameter (e.g., mode) | +| `MY_IOC_GETVAL` | kernel → user | Get current parameter value | +| `MY_IOC_BOTH` | both | Configure and query together | + +Reference: Example ioctl commands like `_IO`, `_IOW`, `_IOR` in a Linux driver [web:11]. + +## Prerequisites + +- Linux system with kernel headers and build tools +- Basic C programming knowledge +- Familiarity with: + - `make`, `gcc` + - `insmod`, `rmmod`, `dmesg` + - Device files under `/dev` + - `ioctl()` system call in user space + +## Minimal Build Environment (Ubuntu) + +```bash +sudo apt update +sudo apt install -y build-essential linux-headers-$(uname -r) +``` + +## Conclusion This is just a basic linux device driver which explains about the IOCTL. Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/ioctl-tutorial-in-linux/ \ No newline at end of file +https://embetronicx.com/tutorials/linux/device-drivers/ioctl-tutorial-in-linux/ diff --git a/Linux/Device_Driver/Interrupt-in-Linux-Kernel/README.md b/Linux/Device_Driver/Interrupt-in-Linux-Kernel/README.md new file mode 100644 index 0000000..b86535f --- /dev/null +++ b/Linux/Device_Driver/Interrupt-in-Linux-Kernel/README.md @@ -0,0 +1,1263 @@ +# Linux Device Driver - Interrupts + +## Overview + +Interrupts are signals generated by hardware or software that temporarily stop the current CPU execution and force the processor to execute a special function called an: + +* Interrupt Service Routine (ISR) +* Interrupt Handler + +After servicing the interrupt, the CPU resumes its previous execution. + +--- + +# Why Interrupts? + +Without interrupts, the CPU would continuously check devices for events. + +This method is called: + +```text +Polling +``` + +Interrupts allow devices to notify the CPU only when attention is required. + +--- + +# Polling vs Interrupts + +| Polling | Interrupts | +| ------------------------------------- | ----------------------------- | +| CPU continuously checks device status | Device notifies CPU | +| CPU intensive | CPU efficient | +| Wastes processor cycles | Better utilization | +| Higher latency | Faster response | +| Simpler implementation | More efficient implementation | + +Example: + +```text +Polling +-------- +CPU -> Keyboard? +CPU -> Keyboard? +CPU -> Keyboard? + +Interrupt +---------- +Keyboard -> CPU +"Key Pressed!" +``` + +--- + +# What Happens During an Interrupt? + +```text +Hardware Event + | + v +Interrupt Controller + | + v +CPU Receives Interrupt + | + v +Current Execution Paused + | + v +ISR Executes + | + v +Interrupt Serviced + | + v +Resume Previous Task +``` + +### Detailed Flow + +1. Hardware generates interrupt. +2. Interrupt controller receives request. +3. Controller signals CPU. +4. CPU saves current execution context. +5. CPU jumps to ISR. +6. ISR handles the event. +7. CPU restores previous context. +8. Execution resumes. + +--- + +# Interrupt Sources + +Common interrupt-generating devices: + +* Keyboard +* Mouse +* UART +* SPI Controller +* I2C Controller +* Network Interface Card +* USB Controller +* DMA Controller +* GPIO Events +* Timers + +--- + +# Interrupt Terminology + +## IRQ + +IRQ stands for: + +```text +Interrupt Request +``` + +Each interrupt source is assigned an IRQ number. + +Example: + +```text +IRQ 1 -> Keyboard +IRQ 14 -> SATA Controller +IRQ 16 -> Network Card +``` + +--- + +# Interrupts vs Exceptions + +## Interrupts + +Generated externally by hardware. + +Examples: + +* Keyboard press +* UART RX data +* Timer expiry + +## Exceptions + +Generated internally by CPU. + +Examples: + +* Divide by zero +* Page fault +* Segmentation fault +* Invalid instruction + +```text +Interrupt -> External Event +Exception -> Internal CPU Event +``` + +--- + +# Types of Interrupts + +## Maskable Interrupt + +Can be enabled or disabled. + +Examples: + +```text +UART +Keyboard +Network +GPIO +``` + +--- + +## Non-Maskable Interrupt (NMI) + +Cannot be disabled. + +Used for: + +```text +Hardware failure +Critical system errors +``` + +--- + +# Types of Exceptions + +## Fault + +Correctable exception. + +Examples: + +```text +Page Fault +Divide By Zero +Segmentation Fault +``` + +--- + +## Trap + +Generated after instruction execution. + +Examples: + +```text +Breakpoint +Debug Exception +``` + +--- + +## Abort + +Serious hardware failure. + +Examples: + +```text +Machine Check Error +Memory Failure +``` + +--- + +# Interrupt Handler (ISR) + +Interrupt Service Routine is a function executed when an interrupt occurs. + +Characteristics: + +* Executes immediately +* Must be fast +* Cannot sleep +* Runs in interrupt context + +Example: + +```c +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + printk("Interrupt Occurred\n"); + + return IRQ_HANDLED; +} +``` + +--- + +# Process Context vs Interrupt Context + +## Process Context + +Normal process execution. + +Examples: + +```text +read() +write() +open() +system calls +``` + +Properties: + +* Can sleep +* Can schedule +* Can use mutexes + +--- + +## Interrupt Context + +ISR execution. + +Properties: + +* Cannot sleep +* Cannot block +* Cannot acquire mutex +* Must finish quickly + +```text +Process Context + | + +--> Sleep Allowed + +Interrupt Context + | + +--> Sleep Not Allowed +``` + +--- + +# Restrictions Inside ISR + +The following operations are NOT allowed: + +```text +sleep() +msleep() +mutex_lock() +copy_to_user() +copy_from_user() +long processing loops +``` + +Reason: + +```text +Interrupts must complete quickly. +``` + +--- + +# Top Half and Bottom Half + +Large interrupt processing is split into two parts. + +```text +Interrupt + | + +----> Top Half + | + +----> Bottom Half +``` + +--- + +# Top Half + +Runs immediately after interrupt. + +Responsibilities: + +* Acknowledge interrupt +* Read hardware status +* Clear interrupt source +* Schedule bottom half + +Characteristics: + +```text +Very Fast +Time Critical +Interrupt Context +``` + +--- + +# Bottom Half + +Performs deferred processing. + +Responsibilities: + +* Data processing +* Packet handling +* Lengthy computations + +Characteristics: + +```text +Runs Later +Less Time Critical +Interrupts Enabled +``` + +--- + +# Bottom Half Mechanisms + +Linux provides several mechanisms: + +## Workqueue + +Runs in process context. + +Can sleep. + +```text +ISR -> Queue Work -> Worker Thread +``` + +--- + +## SoftIRQ + +High-performance deferred processing. + +Commonly used by: + +```text +Networking +Block Layer +``` + +--- + +## Tasklet + +Built on SoftIRQ. + +Lightweight deferred execution. + +--- + +## Threaded IRQ + +Interrupt handled by kernel thread. + +Can sleep. + +```text +ISR + | + +--> Kernel Thread +``` + +--- + +# Requesting an Interrupt + +Kernel API: + +```c +int request_irq( + unsigned int irq, + irq_handler_t handler, + unsigned long flags, + const char *name, + void *dev); +``` + +Parameters: + +| Parameter | Description | +| --------- | ----------------- | +| irq | IRQ number | +| handler | ISR function | +| flags | Interrupt flags | +| name | Device name | +| dev | Device identifier | + +--- + +# Example Request IRQ + +```c +ret = request_irq( + irq_number, + irq_handler, + IRQF_SHARED, + "my_device", + &device); +``` + +--- + +# Interrupt Handler Return Values + +## IRQ_HANDLED + +Interrupt successfully processed. + +```c +return IRQ_HANDLED; +``` + +--- + +## IRQ_NONE + +Interrupt not handled. + +```c +return IRQ_NONE; +``` + +--- + +# Freeing an Interrupt + +Always release IRQ during driver removal. + +```c +free_irq( + irq_number, + &device); +``` + +--- + +# Interrupt Processing Example + +```text +UART Receives Data + | + v +UART Generates IRQ + | + v +CPU Stops Current Task + | + v +UART ISR Executes + | + v +Data Copied To Buffer + | + v +Wake Waiting Process + | + v +Resume Previous Task +``` + +--- + +# Viewing Interrupts + +Linux provides interrupt statistics. + +```bash +cat /proc/interrupts +``` + +Example: + +```text +CPU0 + +16: 1024 IO-APIC eth0 +17: 512 IO-APIC uart0 +18: 250 IO-APIC usb1 +``` + +--- + +# Interrupt Flow in Device Driver + +```text +Hardware + | + v +IRQ Generated + | + v +Interrupt Controller + | + v +CPU + | + v +ISR (Top Half) + | + v +Bottom Half + | + v +Wake User Process +``` + +--- + +# Real Embedded Examples + +## UART Driver + +Interrupt on: + +```text +RX Complete +TX Complete +Error Detection +``` + +--- + +## SPI Driver + +Interrupt on: + +```text +Transfer Complete +FIFO Threshold +Error Event +``` + +--- + +## GPIO Driver + +Interrupt on: + +```text +Rising Edge +Falling Edge +Both Edges +``` + +--- + +## Network Driver + +Interrupt on: + +```text +Packet Arrival +Transmission Complete +Link Status Change +``` + +--- + +# Best Practices + +* Keep ISR extremely short. +* Avoid loops inside ISR. +* Never sleep inside ISR. +* Use Workqueues for long processing. +* Acknowledge hardware quickly. +* Protect shared data using spinlocks when required. + +--- + +# Key APIs Summary + +## Interrupt Registration + +```c +request_irq() +free_irq() +``` + +--- + +## Interrupt Return Values + +```c +IRQ_HANDLED +IRQ_NONE +``` + +--- + +## Deferred Processing + +```c +Workqueue +SoftIRQ +Tasklet +Threaded IRQ +``` + +--- + +# Key Takeaways + +* Interrupts are asynchronous events generated by hardware. +* Interrupts are more efficient than polling. +* ISR executes in interrupt context. +* ISR must be short and fast. +* ISR cannot sleep or block. +* Linux splits interrupt handling into Top Half and Bottom Half. +* Deferred work is typically handled using Workqueues, SoftIRQs, Tasklets, or Threaded IRQs. +* Device drivers register ISRs using `request_irq()`. +* Interrupt statistics can be viewed using `/proc/interrupts`. + +# Linux Device Driver - Interrupt Example Program + +## Overview + +This example demonstrates how to: + +* Register an Interrupt Service Routine (ISR) +* Handle interrupts in a Linux device driver +* Use `request_irq()` +* Use `free_irq()` +* Trigger a software-generated interrupt for testing +* Observe interrupt execution through kernel logs + +The example uses **IRQ 11** and a character device driver. Instead of real hardware, the interrupt is generated through software for learning purposes. + +--- + +# Learning Objectives + +After completing this example, you should understand: + +* IRQ registration +* IRQ release +* Interrupt handler implementation +* Shared interrupts +* Software-triggered interrupts +* Interrupt debugging using `dmesg` + +--- + +# Interrupt Handler + +An Interrupt Service Routine (ISR) executes whenever the registered IRQ occurs. + +```c +#define IRQ_NO 11 + +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + printk(KERN_INFO + "Shared IRQ: Interrupt Occurred\n"); + + return IRQ_HANDLED; +} +``` + +### Parameters + +| Parameter | Description | +| --------- | ------------------------- | +| irq | IRQ number that triggered | +| dev_id | Device-specific pointer | + +### Return Values + +```c +IRQ_HANDLED +``` + +Interrupt processed successfully. + +```c +IRQ_NONE +``` + +Interrupt not for this device. + +--- + +# Important Interrupt APIs + +## request_irq() + +Registers an interrupt handler. + +```c +int request_irq( + unsigned int irq, + irq_handler_t handler, + unsigned long flags, + const char *name, + void *dev_id +); +``` + +### Parameters + +| Parameter | Description | +| --------- | --------------------- | +| irq | IRQ number | +| handler | ISR function | +| flags | IRQ flags | +| name | Device name | +| dev_id | Unique device pointer | + +### Example + +```c +if (request_irq( + IRQ_NO, + irq_handler, + IRQF_SHARED, + "etx_device", + (void *)irq_handler)) +{ + printk(KERN_INFO + "Cannot register IRQ\n"); +} +``` + +`request_irq()` may sleep and therefore cannot be called from interrupt context. + +--- + +## free_irq() + +Releases a previously registered interrupt. + +```c +free_irq( + IRQ_NO, + (void *)irq_handler +); +``` + +Always call this during module removal. + +--- + +# Common IRQ Flags + +## IRQF_SHARED + +Allows multiple devices to share the same interrupt line. + +```c +IRQF_SHARED +``` + +When shared IRQs are used: + +* Every handler gets called +* Each handler determines if interrupt belongs to it +* Unique `dev_id` is required + +```c +request_irq( + IRQ_NO, + irq_handler, + IRQF_SHARED, + "etx_device", + dev_id); +``` + +--- + +# Driver Initialization Flow + +```text +Module Load + | + v +Register Character Device + | + v +Create Device Class + | + v +Create Device Node + | + v +Create Sysfs Entry + | + v +Register IRQ + | + v +Ready To Receive Interrupts +``` + +--- + +# Registering the Interrupt + +Inside driver initialization: + +```c +if (request_irq( + IRQ_NO, + irq_handler, + IRQF_SHARED, + "etx_device", + (void *)irq_handler)) +{ + printk(KERN_INFO + "Cannot register IRQ\n"); + + goto irq_error; +} +``` + +If successful: + +```text +IRQ 11 Registered +``` + +--- + +# Triggering Interrupt Without Hardware + +Normally: + +```text +Hardware + | + v +Interrupt Controller + | + v +CPU + | + v +ISR +``` + +For learning purposes, the tutorial uses a software interrupt. + +--- + +## x86 Interrupt Vector Mapping + +Linux maps IRQs to interrupt vectors. + +Example: + +```text +FIRST_EXTERNAL_VECTOR = 0x20 + +IRQ0 -> Vector 0x30 +IRQ11 -> Vector 0x3B +``` + +Therefore: + +```asm +int $0x3B +``` + +raises IRQ11 in the example. + +--- + +# Trigger Location + +Interrupt is generated when user reads: + +```bash +cat /dev/etx_device +``` + +Inside the driver's read function: + +```c +asm("int $0x3B"); +``` + +This invokes the ISR and demonstrates interrupt handling without external hardware. + +--- + +# Interrupt Execution Flow + +```text +User Executes: + +cat /dev/etx_device + + | + v + +Driver Read Function + + | + v + +Software Interrupt + + | + v + +IRQ 11 Triggered + + | + v + +irq_handler() + + | + v + +"Shared IRQ: Interrupt Occurred" + + | + v + +Return IRQ_HANDLED +``` + +--- + +# Driver Cleanup + +During module removal: + +```c +free_irq( + IRQ_NO, + (void *)irq_handler +); +``` + +Complete cleanup: + +```c +static void __exit etx_driver_exit(void) +{ + free_irq( + IRQ_NO, + (void *)irq_handler); + + kobject_put(kobj_ref); + + device_destroy( + dev_class, + dev); + + class_destroy(dev_class); + + cdev_del(&etx_cdev); + + unregister_chrdev_region( + dev, + 1); +} +``` + +--- + +# Build Driver + +## Makefile + +```make +obj-m += driver.o + +KDIR = /lib/modules/$(shell uname -r)/build + +all: + make -C $(KDIR) M=$(PWD) modules + +clean: + make -C $(KDIR) M=$(PWD) clean +``` + +--- + +## Compile + +```bash +make +``` + +--- + +# Load Driver + +```bash +sudo insmod driver.ko +``` + +Verify: + +```bash +dmesg +``` + +Expected: + +```text +Device Driver Insert...Done!!! +``` + +--- + +# Trigger Interrupt + +Read the device file: + +```bash +sudo cat /dev/etx_device +``` + +This executes: + +```text +Read Function + | + v +Software Interrupt + | + v +ISR +``` + +--- + +# Verify Using dmesg + +```bash +dmesg +``` + +Example Output: + +```text +Major = 246 Minor = 0 + +Device Driver Insert...Done!!! + +Device File Opened...!!! + +Read function + +Shared IRQ: Interrupt Occurred + +Device File Closed...!!! +``` + +This confirms: + +* Device opened +* Read executed +* Interrupt generated +* ISR executed +* Device closed + +--- + +# View System Interrupt Statistics + +```bash +cat /proc/interrupts +``` + +Example: + +```text +CPU0 + +11: 25 IO-APIC etx_device +``` + +Shows: + +* IRQ number +* Number of occurrences +* Device using IRQ + +--- + +# Important Rules for ISR + +### Allowed + +* Read hardware status +* Clear interrupt flag +* Wake wait queue +* Schedule workqueue +* Update counters + +### Avoid + +```c +msleep() +schedule() +mutex_lock() +copy_to_user() +copy_from_user() +``` + +Reason: + +```text +ISR executes in interrupt context. +Sleeping is not allowed. +``` + +--- + +# Real Hardware Equivalent + +Instead of: + +```asm +int $0x3B +``` + +a real driver receives interrupts from: + +```text +GPIO Edge + +UART RX Complete + +SPI Transfer Complete + +I2C Event + +DMA Completion + +Timer Expiry + +Network Packet Arrival + +USB Events +``` + +The same ISR registration process is used. + +--- + +# Key APIs Summary + +## Interrupt Registration + +```c +request_irq() +free_irq() +``` + +## Interrupt Status + +```c +IRQ_HANDLED +IRQ_NONE +``` + +## Common Flags + +```c +IRQF_SHARED +IRQF_TIMER +IRQF_DISABLED +``` + +## Debugging + +```bash +dmesg +cat /proc/interrupts +``` + +--- + +# Key Takeaways + +* `request_irq()` registers an ISR with the kernel. +* `free_irq()` releases the interrupt during cleanup. +* ISR must return `IRQ_HANDLED` or `IRQ_NONE`. +* ISR runs in interrupt context and must not sleep. +* The tutorial demonstrates interrupts without hardware using a software-generated interrupt. +* Reading `/dev/etx_device` triggers IRQ11. +* Interrupt activity can be verified using `dmesg`. +* Real device drivers use the same mechanism with GPIO, UART, SPI, I2C, DMA, and other hardware interrupts. + + +## Conclusion +This is just a basic linux device driver. This will explain about the Interrupts in the linux device driver. + +Please refer this URL for the complete tutorial of this source code. +https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-13-interrupt-example-program-in-linux-kernel/ diff --git a/Linux/Device_Driver/Interrupt-in-Linux-Kernel/ReadMe.md b/Linux/Device_Driver/Interrupt-in-Linux-Kernel/ReadMe.md deleted file mode 100644 index 79b72ba..0000000 --- a/Linux/Device_Driver/Interrupt-in-Linux-Kernel/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver. This will explain about the Interrupts in the linux device driver. - -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-13-interrupt-example-program-in-linux-kernel/ \ No newline at end of file diff --git a/Linux/Device_Driver/Kernel_Thread/README.md b/Linux/Device_Driver/Kernel_Thread/README.md new file mode 100644 index 0000000..62aa857 --- /dev/null +++ b/Linux/Device_Driver/Kernel_Thread/README.md @@ -0,0 +1,707 @@ +# Linux Device Driver - Kernel Thread (kthread) + +## Overview + +- A Kernel Thread (kthread) is a thread that runs entirely in kernel space and is managed by the Linux kernel scheduler. +- Unlike user-space threads, kernel threads do not belong to a user process and are typically used for background kernel activities, deferred processing, monitoring, and driver-specific tasks. + +Common kernel threads visible on a Linux system include: + +```bash +ps -eLf | grep kthread +``` + +Examples: + +```text +kthreadd +kworker/* +rcu_tasks_kthread +migration/* +``` + +These are created and managed entirely by the kernel. + +--- + +# Process vs Thread + +## Process + +A process is an executing instance of a program. + +Characteristics: + +* Separate address space +* Higher resource usage +* Expensive context switching + +```text +Process A + | + +-- Memory + +-- Files + +-- Resources +``` + +## Thread + +A thread is an execution path within a process. + +Characteristics: + +* Shares address space +* Lightweight +* Faster context switching + +```text +Process + | + +--> Thread 1 + +--> Thread 2 + +--> Thread 3 +``` + +Threads share memory and resources, making communication easier than between processes. + +--- + +# Types of Threads + +## User-Level Thread + +Managed by user-space libraries. + +Examples: + +```text +POSIX Threads (pthread) +std::thread +Java Threads +``` + +Characteristics: + +* User-space management +* Kernel unaware of thread implementation +* Faster creation + +--- + +## Kernel-Level Thread + +Managed directly by the kernel. + +Characteristics: + +* Scheduled by kernel +* Runs in kernel space +* Has access to kernel APIs +* Commonly used in device drivers + +Examples: + +```text +kworker +ksoftirqd +kthreadd +rcu_tasks_kthread +``` + +--- + +# Why Use Kernel Threads? + +Kernel threads are useful when a driver needs: + +* Continuous background processing +* Periodic monitoring +* Deferred work +* Polling hardware +* Watchdog functionality +* Data collection + +Example: + +```text +Temperature Sensor Driver + | + v +Kernel Thread + | + v +Read Temperature Every Second +``` + +--- + +# Kernel Thread Architecture + +```text +Driver Init + | + v +Create kthread + | + v +Scheduler + | + v +Thread Function + | + v +Periodic Work + | + v +Stop Request + | + v +Thread Exit +``` + +--- + +# Required Header Files + +```c +#include +#include +#include +``` + +--- + +# Thread Management APIs + +## Create Thread + +### kthread_create() + +Creates a kernel thread but does not start it. + +```c +struct task_struct * +kthread_create( + int (*threadfn)(void *data), + void *data, + const char namefmt[], + ...); +``` + +Parameters: + +| Parameter | Description | +| --------- | ------------------------- | +| threadfn | Thread function | +| data | Argument passed to thread | +| namefmt | Thread name | + +Returns: + +```text +task_struct pointer +or +ERR_PTR(-ENOMEM) +``` + +--- + +# Start Thread + +## wake_up_process() + +Starts a thread created using `kthread_create()`. + +```c +wake_up_process( + etx_thread); +``` + +Example: + +```c +etx_thread = +kthread_create( + thread_function, + NULL, + "eTx Thread"); + +wake_up_process( + etx_thread); +``` + +--- + +# Create and Start Together + +## kthread_run() + +Convenience wrapper around: + +```text +kthread_create() + + +wake_up_process() +``` + +Syntax: + +```c +struct task_struct * +kthread_run( + threadfn, + data, + namefmt, + ...); +``` + +Example: + +```c +etx_thread = +kthread_run( + thread_function, + NULL, + "eTx Thread"); +``` + +Preferred approach in modern drivers. + +--- + +# Thread Function + +Every kernel thread executes a function: + +```c +int thread_function( + void *data) +{ + return 0; +} +``` + +Requirements: + +* Return integer +* Accept void pointer argument +* Should periodically check stop condition + +--- + +# kthread_should_stop() + +Checks whether thread termination was requested. + +```c +while (!kthread_should_stop()) +{ + // Thread work +} +``` + +Returns: + +```text +0 -> Continue +1 -> Stop Requested +``` + +--- + +# Example Thread Function + +```c +int thread_function( + void *pv) +{ + int i = 0; + + while (!kthread_should_stop()) + { + printk( + KERN_INFO + "Thread %d\n", + i++); + + msleep(1000); + } + + return 0; +} +``` + +Behavior: + +```text +Print message + | + v +Sleep 1 second + | + v +Repeat +``` + +--- + +# Sleeping Inside Kernel Thread + +Unlike ISR or Tasklet, kernel threads run in process context. + +Allowed: + +```c +msleep() +schedule() +wait_event() +mutex_lock() +``` + +Not allowed in: + +```text +ISR +Tasklet +SoftIRQ +``` + +This is one major advantage of kernel threads. + +--- + +# Stopping a Thread + +## kthread_stop() + +Stops a kernel thread. + +```c +int kthread_stop( + struct task_struct *k); +``` + +Example: + +```c +kthread_stop( + etx_thread); +``` + +What happens internally: + +```text +Set stop flag + | + v +Wake thread + | + v +kthread_should_stop() +returns TRUE + | + v +Thread exits +``` + +--- + +# Thread Life Cycle + +```text +Module Load + | + v +kthread_run() + | + v +Thread Running + | + v +while(!kthread_should_stop()) + | + v +Periodic Work + | + v +kthread_stop() + | + v +Thread Exit + | + v +Module Unload +``` + +--- + +# CPU Affinity + +## kthread_bind() + +Bind thread to a specific CPU. + +```c +kthread_bind( + etx_thread, + cpu); +``` + +Example: + +```c +kthread_bind( + etx_thread, + 0); +``` + +Runs only on CPU0. + +Useful for: + +* Real-time workloads +* CPU-specific processing +* Performance tuning + +--- + +# Typical Driver Example + +## Driver Init + +```c +static struct task_struct *etx_thread; + +etx_thread = +kthread_run( + thread_function, + NULL, + "eTx Thread"); +``` + +--- + +## Thread Function + +```c +int thread_function( + void *data) +{ + while(!kthread_should_stop()) + { + printk("Running\n"); + + msleep(1000); + } + + return 0; +} +``` + +--- + +## Driver Exit + +```c +kthread_stop( + etx_thread); +``` + +--- + +# Execution Flow + +```text +insmod driver.ko + | + v +kthread_run() + | + v +Thread Created + | + v +Thread Function + | + v +Loop + | + v +msleep() + | + v +Repeat + | + v +rmmod driver + | + v +kthread_stop() + | + v +Thread Exit +``` + +--- + +# Sample dmesg Output + +```text +Device Driver Insert...Done!!! + +Kthread Created Successfully... + +In EmbeTronicX Thread Function 0 + +In EmbeTronicX Thread Function 1 + +In EmbeTronicX Thread Function 2 + +In EmbeTronicX Thread Function 3 + +Device Driver Remove...Done!!! +``` + +--- + +# Kernel Thread vs Workqueue + +| Feature | Kernel Thread | Workqueue | +| --------------------- | ------------- | --------------------------- | +| Dedicated Thread | Yes | No (usually shared kworker) | +| Continuous Loop | Yes | No | +| Can Sleep | Yes | Yes | +| Periodic Tasks | Excellent | Possible | +| Resource Usage | Higher | Lower | +| Background Monitoring | Excellent | Limited | +| Scheduler Managed | Yes | Yes | + +--- + +# Kernel Thread vs Tasklet + +| Feature | Kernel Thread | Tasklet | +| ----------------------- | --------------- | -------------- | +| Context | Process Context | Atomic Context | +| Sleep Allowed | Yes | No | +| Mutex Allowed | Yes | No | +| wait_event() | Yes | No | +| Long Processing | Yes | No | +| Interrupt Deferred Work | Possible | Yes | + +--- + +# Common Embedded Linux Use Cases + +## Sensor Monitoring + +```text +Kernel Thread + | + +--> Read Temperature + +--> Read Voltage + +--> Read Pressure +``` + +--- + +## Watchdog Driver + +```text +Kernel Thread + | + +--> Feed Watchdog + Every 1 Second +``` + +--- + +## UART Driver + +```text +Kernel Thread + | + +--> Poll RX Buffer + +--> Process Data +``` + +--- + +## Network Driver + +```text +Kernel Thread + | + +--> Statistics Collection + +--> Link Monitoring +``` + +--- + +# Best Practices + +* Use `kthread_run()` instead of separate create/start calls. +* Always check `kthread_should_stop()`. +* Always stop thread during module unload. +* Avoid busy waiting. +* Use `msleep()` or wait queues when idle. +* Keep thread logic simple and predictable. +* Use workqueues if a dedicated thread is unnecessary. + +--- + +# Important APIs Summary + +## Creation + +```c +kthread_create() +kthread_run() +``` + +## Start + +```c +wake_up_process() +``` + +## Stop + +```c +kthread_stop() +kthread_should_stop() +``` + +## CPU Affinity + +```c +kthread_bind() +``` + +## Sleeping + +```c +msleep() +schedule() +wait_event() +``` + +--- + +# Key Takeaways + +* Kernel threads execute entirely in kernel space. +* They are represented by `struct task_struct`. +* `kthread_run()` is the most common way to create and start a thread. +* `kthread_should_stop()` must be checked in long-running loops. +* `kthread_stop()` is used during cleanup. +* Kernel threads run in process context and can sleep. +* They are ideal for periodic monitoring, polling, watchdogs, and background driver tasks. +* Kernel threads are heavier but more flexible than tasklets and workqueues. + + +## Conclusion +This is just a basic linux device driver which explains about the kernel thread in linux device driver. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/linux-device-drivers-tutorial-kernel-thread/ diff --git a/Linux/Device_Driver/Kernel_Thread/ReadMe.md b/Linux/Device_Driver/Kernel_Thread/ReadMe.md deleted file mode 100644 index 7919af7..0000000 --- a/Linux/Device_Driver/Kernel_Thread/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains about the kernel thread in linux device driver. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/linux-device-drivers-tutorial-kernel-thread/ \ No newline at end of file diff --git a/Linux/Device_Driver/Kernel_Timer/README.md b/Linux/Device_Driver/Kernel_Timer/README.md new file mode 100644 index 0000000..d987be0 --- /dev/null +++ b/Linux/Device_Driver/Kernel_Timer/README.md @@ -0,0 +1,787 @@ +# Linux Device Driver - Kernel Timer in Linux + +## Overview + +A Kernel Timer allows a Linux device driver to execute a function after a specified time interval. + +Unlike hardware timers, Linux kernel timers are software timers managed by the kernel timer subsystem using **jiffies** and timer interrupts. They are useful for scheduling future work without blocking a thread. + +Common use cases: + +* Device polling +* Timeout detection +* Periodic status checks +* Retry mechanisms +* Delayed operations +* Heartbeat monitoring + +--- + +# What is a Timer? + +A timer measures a time interval. + +```text +Current Time + | + +-------> Timer Expires + | + +--> Callback Function Executes +``` + +In Linux, timers are driven by periodic kernel timer interrupts. The kernel maintains an internal counter that increments every clock tick since boot. This counter is represented through **jiffies**. + +--- + +# Kernel Timer Architecture + +```text +Driver Loads + | + v +Create Timer + | + v +Start Timer + | + v +Timer Expires + | + v +Callback Function + | + +--> Stop Timer + | + +--> Restart Timer +``` + +If the callback restarts the timer, it behaves like a periodic timer. + +--- + +# Uses of Kernel Timers + +## Device Polling + +```text +Timer + | + +--> Check Device Status +``` + +Used when hardware does not generate interrupts. + +--- + +## Communication Timeout + +```text +Send Request + | + +--> Start Timer + | + +--> No Response + | + +--> Timeout Error +``` + +Used in networking and protocol stacks. + +--- + +## Periodic Monitoring + +```text +Every 5 Seconds + | + +--> Read Sensor +``` + +Useful for watchdogs and health monitoring. + +--- + +# Header Files + +```c +#include +#include +``` + +Required for timer APIs and jiffies support. + +--- + +# Kernel Timer Structure + +```c +struct timer_list +{ + unsigned long expires; + + void (*function)(unsigned long); + + unsigned long data; +}; +``` + +Important Fields: + +| Field | Purpose | +| -------- | -------------------------- | +| expires | Expiration time in jiffies | +| function | Callback function | +| data | Data passed to callback | + +Modern kernels use `timer_setup()` and callback receives: + +```c +struct timer_list *timer +``` + +instead of `unsigned long data`. + +--- + +# Jiffies + +## What are Jiffies? + +Jiffies represent kernel clock ticks. + +```c +jiffies +``` + +Example: + +```text +System Boot + | + +--> jiffies = 0 + +1 Tick Later + | + +--> jiffies = 1 +``` + +Every timer expiration is specified relative to jiffies. + +--- + +# Time Conversion + +## Milliseconds to Jiffies + +```c +msecs_to_jiffies(5000) +``` + +Convert: + +```text +5000 ms + | + +--> Jiffies +``` + +Example: + +```c +jiffies + +msecs_to_jiffies(5000) +``` + +Means: + +```text +Current Time + 5 Seconds +``` + +--- + +# Timer Initialization Methods + +Linux provides multiple initialization APIs. + +--- + +# Method 1 - init_timer() + +```c +init_timer( + &etx_timer); +``` + +Older API. + +After initialization: + +```c +etx_timer.function = timer_callback; +etx_timer.data = 0; +``` + +Not recommended for modern kernels. + +--- + +# Method 2 - setup_timer() + +Older kernels: + +```c +setup_timer( + &etx_timer, + timer_callback, + 0); +``` + +Callback: + +```c +void timer_callback( + unsigned long data) +{ +} +``` + +Used in legacy kernels. + +--- + +# Method 3 - timer_setup() + +Recommended API. + +```c +timer_setup( + &etx_timer, + timer_callback, + 0); +``` + +Callback: + +```c +void timer_callback( + struct timer_list *t) +{ +} +``` + +Most modern kernels use this API. + +--- + +# Method 4 - DEFINE_TIMER() + +Static initialization. + +```c +DEFINE_TIMER( + etx_timer, + timer_callback, + 0, + 0); +``` + +Kernel creates and initializes timer automatically. + +--- + +# Starting a Timer + +## add_timer() + +```c +add_timer( + &etx_timer); +``` + +Starts timer. + +Before calling: + +```c +etx_timer.expires = + jiffies + + msecs_to_jiffies(5000); +``` + +--- + +# Recommended Start Method + +Modern drivers usually use: + +```c +mod_timer( + &etx_timer, + jiffies + + msecs_to_jiffies(5000)); +``` + +because it works for both: + +```text +New Timer +Existing Timer +``` + +--- + +# Modifying Timer Timeout + +## mod_timer() + +```c +mod_timer( + &etx_timer, + expires); +``` + +Example: + +```c +mod_timer( + &etx_timer, + jiffies + + msecs_to_jiffies(5000)); +``` + +Equivalent conceptually to: + +```c +del_timer(); + +update expires; + +add_timer(); +``` + +but much more efficient. + +--- + +# Return Value + +```c +ret = mod_timer(...) +``` + +| Value | Meaning | +| ----- | ------------------ | +| 0 | Timer was inactive | +| 1 | Timer was active | + +This is status information, not an error code. + +--- + +# Creating a Periodic Timer + +Kernel timers are: + +```text +One-Shot Timers +``` + +by default. + +To make them periodic: + +```c +void timer_callback( + struct timer_list *t) +{ + pr_info("Timer Fired\n"); + + mod_timer( + &etx_timer, + jiffies + + msecs_to_jiffies(5000)); +} +``` + +Execution: + +```text +5 Seconds + | + +--> Callback + + | + +--> Restart Timer + + | + +--> Callback Again +``` + +--- + +# Example Driver + +## Timer Declaration + +```c +static struct timer_list etx_timer; +``` + +--- + +## Timer Initialization + +```c +timer_setup( + &etx_timer, + timer_callback, + 0); +``` + +--- + +## Start Timer + +```c +mod_timer( + &etx_timer, + jiffies + + msecs_to_jiffies(5000)); +``` + +--- + +## Callback + +```c +void timer_callback( + struct timer_list *data) +{ + pr_info( + "Timer Callback\n"); + + mod_timer( + &etx_timer, + jiffies + + msecs_to_jiffies(5000)); +} +``` + +This creates a timer that fires every 5 seconds. + +--- + +# Stopping a Timer + +## del_timer() + +```c +del_timer( + &etx_timer); +``` + +Deactivates timer. + +Return: + +| Value | Meaning | +| ----- | ---------------------- | +| 0 | Timer already inactive | +| 1 | Active timer removed | + +--- + +# del_timer_sync() + +```c +del_timer_sync( + &etx_timer); +``` + +Removes timer and waits for callback completion. + +Useful during: + +```text +Module Removal +Driver Cleanup +``` + +Safer than `del_timer()` when timer callback may still be running. + +--- + +# Check Timer Status + +## timer_pending() + +```c +timer_pending( + &etx_timer); +``` + +Returns: + +| Value | Meaning | +| ----- | ----------- | +| 0 | Not Pending | +| 1 | Pending | + +Used to check whether timer is currently active. + +--- + +# Execution Flow + +```text +insmod driver.ko + | + v +timer_setup() + | + v +mod_timer() + | + v +5 Seconds + | + v +timer_callback() + | + v +mod_timer() + | + v +Repeat +``` + +--- + +# Sample Output + +```text +Device Driver Insert...Done!!! + +Timer Callback function Called [0] + +Timer Callback function Called [1] + +Timer Callback function Called [2] + +Timer Callback function Called [3] +``` + +Each callback occurs approximately every 5 seconds. + +--- + +# Important Limitation + +Timer callback executes in: + +```text +Interrupt Context +``` + +NOT process context. + +--- + +# Not Allowed Inside Timer Callback + +## Sleeping + +Wrong: + +```c +msleep(100); +``` + +--- + +## Mutex + +Wrong: + +```c +mutex_lock(&lock); +``` + +--- + +## Access User Space + +Wrong: + +```c +copy_to_user(...); +``` + +--- + +## Long Processing + +Wrong: + +```c +Huge Computation +Large File Operations +``` + +Timer callbacks should be short and fast. + +--- + +# Timer vs Kernel Thread + +| Feature | Kernel Timer | Kernel Thread | +| ------------------ | ------------ | ------------- | +| Context | Interrupt | Process | +| Sleep Allowed | No | Yes | +| Periodic Execution | Yes | Yes | +| Long Processing | No | Yes | +| CPU Usage | Low | Higher | + +--- + +# Timer vs Workqueue + +| Feature | Timer | Workqueue | +| ----------------- | --------- | --------- | +| Context | Interrupt | Process | +| Sleep Allowed | No | Yes | +| Delayed Execution | Yes | Yes | +| Long Processing | No | Yes | + +Common pattern: + +```text +Timer Expires + | + +--> Queue Workqueue + | + +--> Workqueue Does Heavy Work +``` + +--- + +# Common Driver Use Cases + +## Watchdog + +```text +Every 1 Second + | + +--> Feed Watchdog +``` + +--- + +## Network Timeout + +```text +Send Packet + | + +--> Start Timer + | + +--> No ACK + | + +--> Timeout +``` + +--- + +## Device Polling + +```text +Every 100 ms + | + +--> Read Status Register +``` + +--- + +## Heartbeat + +```text +Periodic Timer + | + +--> Health Check +``` + +--- + +# Important APIs Summary + +## Initialization + +```c +init_timer() + +setup_timer() + +timer_setup() + +DEFINE_TIMER() +``` + +--- + +## Start + +```c +add_timer() + +mod_timer() +``` + +--- + +## Stop + +```c +del_timer() + +del_timer_sync() +``` + +--- + +## Status + +```c +timer_pending() +``` + +--- + +## Time Conversion + +```c +jiffies + +msecs_to_jiffies() +``` + +--- + +# Key Takeaways + +* Kernel timers schedule execution of a callback in the future. +* Modern kernels should use `timer_setup()`. +* Timer expiration is based on **jiffies**. +* `mod_timer()` is the preferred API for starting and updating timers. +* Kernel timers are one-shot by default. +* Re-arming the timer inside the callback creates periodic behavior. +* Timer callbacks execute in interrupt context. +* Timer callbacks cannot sleep, acquire mutexes, or perform long operations. +* `del_timer_sync()` is preferred during driver cleanup. +* Timers are ideal for polling, watchdogs, retries, and timeout handling. + + +## Conclusion +This is just a basic linux device driver which explains about the kernel timer in linux device driver. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/using-kernel-timer-in-linux-device-driver/ diff --git a/Linux/Device_Driver/Kernel_Timer/ReadMe.md b/Linux/Device_Driver/Kernel_Timer/ReadMe.md deleted file mode 100644 index 926ed28..0000000 --- a/Linux/Device_Driver/Kernel_Timer/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains about the kernel timer in linux device driver. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/using-kernel-timer-in-linux-device-driver/ \ No newline at end of file diff --git a/Linux/Device_Driver/Linked_List/ReadMe.md b/Linux/Device_Driver/Linked_List/ReadMe.md index b72049e..d761929 100644 --- a/Linux/Device_Driver/Linked_List/ReadMe.md +++ b/Linux/Device_Driver/Linked_List/ReadMe.md @@ -1,4 +1,1389 @@ +# Linux Device Driver - Linked List in Linux Kernel + +## Overview + +The Linux kernel provides a built-in implementation of a **circular doubly linked list**. + +Instead of implementing custom linked list structures, kernel developers use: + +```c +#include +``` + +Benefits: + +* Reusable kernel implementation +* Efficient insertion and deletion +* Widely used throughout kernel subsystems +* Supports dynamic data structures + +--- + +# What is a Linked List? + +A linked list consists of nodes connected using pointers. + +Traditional implementation: + +```c +struct my_node +{ + int data; + + struct my_node *prev; + struct my_node *next; +}; +``` + +Kernel implementation embeds a `list_head` structure. + +```c +struct my_node +{ + struct list_head list; + + int data; +}; +``` + +--- + +# Linux Kernel Linked List + +Linux uses a **Circular Doubly Linked List**. + +```text ++------+ +------+ +------+ +|Node1 |<--->|Node2 |<--->|Node3 | ++------+ +------+ +------+ + ^ | + |___________________________| +``` + +Characteristics: + +* Circular +* Doubly linked +* No NULL termination +* Head node always exists + +--- + +# Header File + +```c +#include +``` + +Kernel implementation: + +```c +struct list_head +{ + struct list_head *next; + struct list_head *prev; +}; +``` + +--- + +# Creating a Linked List Node + +User-defined structure: + +```c +struct my_list +{ + struct list_head list; + int data; +}; +``` + +The `list_head` member links the node into the kernel linked list. + +--- + +# Creating the List Head + +Before creating nodes, create a list head. + +## LIST_HEAD() + +```c +LIST_HEAD(my_linked_list); +``` + +Example: + +```c +LIST_HEAD(etx_linked_list); +``` + +Internally: + +```c +struct list_head etx_linked_list = +{ + &etx_linked_list, + &etx_linked_list +}; +``` + +When: + +```text +head->next == head +head->prev == head +``` + +The list is empty. + +--- + +# Initializing a Node + +For dynamically or statically allocated nodes: + +```c +INIT_LIST_HEAD(&node.list); +``` + +Example: + +```c +struct my_list node; + +INIT_LIST_HEAD(&node.list); + +node.data = 10; +``` + +This initializes: + +```text +node.list.next = node.list +node.list.prev = node.list +``` + +--- + +# Adding Nodes + +## Add After Head + +Function: + +```c +list_add( + &node.list, + &etx_linked_list +); +``` + +Effect: + +```text +HEAD + | + v +NODE + | + v +OLD_FIRST_NODE +``` + +Used for: + +```text +Stack (LIFO) +``` + +--- + +## Add Before Head + +Function: + +```c +list_add_tail( + &node.list, + &etx_linked_list +); +``` + +Effect: + +```text +HEAD + | + v +... + | + v +NEW_NODE + | + v +HEAD +``` + +Used for: + +```text +Queue (FIFO) +``` + +--- + +# Deleting Nodes + +## list_del() + +Removes node from list. + +```c +list_del(&node.list); +``` + +Important: + +```text +Removes linkage only +Memory is NOT freed +``` + +--- + +## list_del_init() + +Deletes and reinitializes node. + +```c +list_del_init(&node.list); +``` + +Node becomes standalone again. + +--- + +# Replacing Nodes + +## list_replace() + +Replace old node with new node. + +```c +list_replace( + &old.list, + &new.list +); +``` + +--- + +## list_replace_init() + +Replace and reinitialize old node. + +```c +list_replace_init( + &old.list, + &new.list +); +``` + +--- + +# Moving Nodes + +## list_move() + +Move node after head. + +```c +list_move( + &node.list, + &etx_linked_list +); +``` + +--- + +## list_move_tail() + +Move node before head. + +```c +list_move_tail( + &node.list, + &etx_linked_list +); +``` + +--- + +# Rotating List + +## list_rotate_left() + +Rotate list left. + +```c +list_rotate_left( + &etx_linked_list +); +``` + +Example: + +Before: + +```text +HEAD -> A -> B -> C +``` + +After: + +```text +HEAD -> B -> C -> A +``` + +--- + +# Testing List State + +## list_empty() + +Check whether list is empty. + +```c +if(list_empty(&etx_linked_list)) +{ + printk("Empty\n"); +} +``` + +Returns: + +```text +1 -> Empty +0 -> Not Empty +``` + +--- + +## list_is_last() + +Check whether node is last. + +```c +list_is_last( + &node.list, + &etx_linked_list +); +``` + +--- + +## list_is_singular() + +Check whether list contains only one entry. + +```c +list_is_singular( + &etx_linked_list +); +``` + +--- + +# Splitting a List + +## list_cut_position() + +Split one list into two. + +```c +list_cut_position( + &new_list, + &old_list, + &entry->list +); +``` + +Example: + +Before: + +```text +HEAD + | + A -> B -> C -> D +``` + +After: + +```text +LIST1: +A -> B + +LIST2: +C -> D +``` + +--- + +# Joining Lists + +## list_splice() + +Join two linked lists. + +```c +list_splice( + &list2, + &list1 +); +``` + +Result: + +```text +LIST1 + LIST2 +``` + +Used commonly for: + +* Stack implementation +* Queue implementation +* Batch insertion + +--- + +# Traversing Linked Lists + +## list_entry() + +Convert list_head pointer to container structure. + +```c +list_entry( + ptr, + struct my_list, + list +); +``` + +Parameters: + +| Parameter | Description | +| --------- | ----------------- | +| ptr | list_head pointer | +| type | structure type | +| member | list_head member | + +--- + +# Forward Traversal + +## list_for_each() + +```c +struct list_head *pos; + +list_for_each( + pos, + &etx_linked_list) +{ +} +``` + +--- + +## list_for_each_entry() + +Preferred method. + +```c +struct my_list *node; + +list_for_each_entry( + node, + &etx_linked_list, + list) +{ + printk("%d\n", + node->data); +} +``` + +--- + +# Safe Traversal + +## list_for_each_entry_safe() + +Safe while deleting nodes. + +```c +struct my_list *node; +struct my_list *tmp; + +list_for_each_entry_safe( + node, + tmp, + &etx_linked_list, + list) +{ + list_del(&node->list); +} +``` + +Used when removing nodes during traversal. + +--- + +# Reverse Traversal + +## list_for_each_prev() + +```c +list_for_each_prev( + pos, + &etx_linked_list) +{ +} +``` + +--- + +## list_for_each_entry_reverse() + +```c +list_for_each_entry_reverse( + node, + &etx_linked_list, + list) +{ + printk("%d\n", + node->data); +} +``` + +--- + +# Typical Driver Use Cases + +Linux kernel linked lists are heavily used in: + +* Process management +* Device driver queues +* Workqueue management +* Timer management +* Networking subsystem +* USB subsystem +* Block layer +* Scheduler + +Kernel developers frequently embed `struct list_head` inside driver-specific structures rather than storing data directly in the list node. + +--- + +# Example Flow + +```text +Create List Head + | + v +Create Node + | + v +INIT_LIST_HEAD() + | + v +list_add() + | + v +Traverse + | + v +list_del() + | + v +Free Memory +``` + +--- + +# Important APIs Summary + +## Initialization + +```c +LIST_HEAD() +INIT_LIST_HEAD() +``` + +## Add + +```c +list_add() +list_add_tail() +``` + +## Delete + +```c +list_del() +list_del_init() +``` + +## Replace + +```c +list_replace() +list_replace_init() +``` + +## Move + +```c +list_move() +list_move_tail() +``` + +## Test + +```c +list_empty() +list_is_last() +list_is_singular() +``` + +## Split / Join + +```c +list_cut_position() +list_splice() +``` + +## Traversal + +```c +list_for_each() +list_for_each_entry() +list_for_each_entry_safe() +list_for_each_prev() +list_for_each_entry_reverse() +``` + +--- +# Linux Device Driver - Linked List Example Program + +## Overview + +This example combines multiple Linux kernel concepts: + +* Character Device Driver +* Interrupt Handling +* Workqueue +* Kernel Linked List +* Dynamic Memory Allocation (`kmalloc`) +* Safe List Traversal +* Safe Node Deletion + +The driver demonstrates how to: + +1. Receive data from userspace. +2. Trigger a software interrupt. +3. Schedule a Workqueue. +4. Create a linked list node inside the Workqueue. +5. Add the node to a kernel linked list. +6. Traverse and print the linked list. +7. Delete all nodes during module removal. + +--- + +# High-Level Architecture + +```text +echo 10 > /dev/etx_device + | + v + Write Function + | + v + Software Interrupt + | + v + ISR + | + v + queue_work() + | + v + Workqueue + | + v + Create Linked List Node + | + v + Add Node To List + | + v + cat /dev/etx_device + | + v + Traverse Linked List + | + v + Print All Nodes +``` + +--- + +# Core Data Structure + +## Linked List Node + +```c +struct my_list +{ + struct list_head list; + int data; +}; +``` + +### Purpose + +| Member | Description | +| ------ | ----------------------------- | +| list | Linux kernel linked list node | +| data | User supplied value | + +Each node stores one integer value. + +--- + +# Creating List Head + +```c +LIST_HEAD(Head_Node); +``` + +Creates and initializes the list head. + +Equivalent Concept: + +```text +Head_Node + | + +--> Empty List +``` + +Initially: + +```text +Head_Node.next = Head_Node +Head_Node.prev = Head_Node +``` + +--- + +# Workqueue Function + +The actual linked list node is created inside the Workqueue. + +```c +static void workqueue_fn( + struct work_struct *work) +{ + struct my_list *temp_node; + + temp_node = + kmalloc( + sizeof(struct my_list), + GFP_KERNEL); + + temp_node->data = etx_value; + + INIT_LIST_HEAD( + &temp_node->list); + + list_add_tail( + &temp_node->list, + &Head_Node); +} +``` + +--- + +# What Happens Here? + +## Step 1 + +Allocate memory. + +```c +kmalloc() +``` + +Creates: + +```text ++----------------+ +| struct my_list | ++----------------+ +``` + +--- + +## Step 2 + +Store received value. + +```c +temp_node->data = etx_value; +``` + +Example: + +```text +data = 10 +``` + +--- + +## Step 3 + +Initialize embedded list. + +```c +INIT_LIST_HEAD() +``` + +--- + +## Step 4 + +Insert into linked list. + +```c +list_add_tail() +``` + +Example: + +Before: + +```text +HEAD +``` + +After Writing 10: + +```text +HEAD <-> [10] +``` + +After Writing 20: + +```text +HEAD <-> [10] <-> [20] +``` + +After Writing 30: + +```text +HEAD <-> [10] <-> [20] <-> [30] +``` + +--- + +# Interrupt Handler + +```c +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + queue_work( + own_workqueue, + &work); + + return IRQ_HANDLED; +} +``` + +Purpose: + +```text +Interrupt + | + v +Schedule Workqueue +``` + +The ISR does not create nodes directly. + +It schedules deferred processing. + +--- + +# Write Operation + +```c +static ssize_t etx_write(...) +{ + sscanf(buf,"%d",&etx_value); + + asm("int $0x3B"); + + return len; +} +``` + +--- + +# Write Flow + +Example: + +```bash +echo 10 > /dev/etx_device +``` + +Execution: + +```text +User Writes 10 + | + v +etx_write() + | + v +etx_value = 10 + | + v +Software Interrupt + | + v +ISR + | + v +Workqueue + | + v +Create Node + | + v +Add Node +``` + +--- + +# Reading the Linked List + +## Traversal + +```c +list_for_each_entry( + temp, + &Head_Node, + list) +{ + printk( + "Node data=%d\n", + temp->data); +} +``` + +--- + +# Example + +Current List: + +```text +HEAD + | + +--> 10 + | + +--> 20 + | + +--> 30 +``` + +Output: + +```text +Node 0 data = 10 +Node 1 data = 20 +Node 2 data = 30 + +Total Nodes = 3 +``` + +--- + +# Traversal Macro + +```c +list_for_each_entry( + pos, + head, + member); +``` + +Parameters: + +| Parameter | Description | +| --------- | ------------------------- | +| pos | Node pointer | +| head | List head | +| member | Embedded list_head member | + +Preferred Linux kernel traversal method. + +--- + +# Example Session + +## Add First Node + +```bash +echo 10 > /dev/etx_device +``` + +List: + +```text +HEAD + | + +--> 10 +``` + +--- + +## Add Second Node + +```bash +echo 20 > /dev/etx_device +``` + +List: + +```text +HEAD + | + +--> 10 + | + +--> 20 +``` + +--- + +## Add Third Node + +```bash +echo 30 > /dev/etx_device +``` + +List: + +```text +HEAD + | + +--> 10 + | + +--> 20 + | + +--> 30 +``` + +--- + +## Read Nodes + +```bash +cat /dev/etx_device +``` + +Output: + +```text +Node 0 data = 10 +Node 1 data = 20 +Node 2 data = 30 + +Total Nodes = 3 +``` + +--- + +# Deleting All Nodes + +During module removal: + +```c +list_for_each_entry_safe( + cursor, + temp, + &Head_Node, + list) +{ + list_del( + &cursor->list); + + kfree(cursor); +} +``` + +--- + +# Why Safe Traversal? + +Unsafe: + +```c +list_for_each_entry() +``` + +Problem: + +```text +Delete Current Node + | + v +Next Pointer Invalid +``` + +May crash. + +--- + +Safe: + +```c +list_for_each_entry_safe() +``` + +Stores next node before deletion. + +Suitable for: + +```text +Delete While Traversing +``` + +--- + +# Module Removal Flow + +```text +rmmod driver + | + v +Traverse List + | + v +list_del() + | + v +kfree() + | + v +Destroy Workqueue + | + v +free_irq() + | + v +Driver Removed +``` + +--- + +# Memory Management + +## Allocation + +```c +kmalloc( + sizeof(struct my_list), + GFP_KERNEL); +``` + +Creates: + +```text +Kernel Heap + | + +--> Node +``` + +--- + +## Free + +```c +kfree(node); +``` + +Releases memory. + +Important: + +```text +list_del() +does NOT free memory +``` + +Both operations are required. + +--- + +# Why Linux Uses Embedded list_head? + +Instead of: + +```c +struct node +{ + int data; + struct node *next; +}; +``` + +Linux uses: + +```c +struct my_list +{ + struct list_head list; + int data; +}; +``` + +Advantages: + +* Generic implementation +* Reusable APIs +* Better cache locality +* Single memory allocation +* Used throughout kernel subsystems + +This intrusive-list design is a common Linux kernel pattern. + +--- + +# Important APIs Used + +## Linked List + +```c +LIST_HEAD() + +INIT_LIST_HEAD() + +list_add_tail() + +list_for_each_entry() + +list_for_each_entry_safe() + +list_del() +``` + +--- + +## Memory + +```c +kmalloc() + +kfree() +``` + +--- + +## Workqueue + +```c +create_workqueue() + +queue_work() + +destroy_workqueue() +``` + +--- + +## Interrupt + +```c +request_irq() + +free_irq() +``` + +--- + +# Driver Concept Demonstrated + +This example is valuable because it combines multiple Linux driver concepts in one workflow: + +```text +Userspace + | + v +Character Driver + | + v +Interrupt + | + v +ISR + | + v +Workqueue + | + v +Linked List + | + v +Kernel Memory Management +``` + +--- + +# Key Takeaways + +* Linux kernel provides a built-in circular doubly linked list implementation. +* `struct list_head` is embedded inside user-defined structures. +* List heads are created using `LIST_HEAD()`. +* Nodes are initialized using `INIT_LIST_HEAD()`. +* `list_add()` inserts after head (stack behavior). +* `list_add_tail()` inserts before head (queue behavior). +* `list_del()` removes nodes but does not free memory. +* `list_for_each_entry()` is the preferred traversal method. +* `list_for_each_entry_safe()` should be used when deleting nodes during traversal. +* Linked lists are extensively used throughout Linux kernel subsystems and device drivers. + +* `LIST_HEAD()` creates the linked list head. +* Each node embeds `struct list_head`. +* Nodes are dynamically allocated using `kmalloc()`. +* `list_add_tail()` appends nodes to the list. +* Interrupts trigger the Workqueue. +* Workqueue creates linked list nodes. +* `list_for_each_entry()` traverses the list. +* `list_for_each_entry_safe()` is used when deleting nodes. +* `list_del()` removes a node from the list but does not free memory. +* `kfree()` must be called to release allocated memory. +* This example demonstrates a complete producer-storage-traversal-cleanup workflow inside a Linux device driver. + + +# Conclusion This is just a basic linux device driver which explains about the linked list in linux device driver. Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/example-linked-list-in-linux-kernel/ \ No newline at end of file +https://embetronicx.com/tutorials/linux/device-drivers/example-linked-list-in-linux-kernel/ diff --git a/Linux/Device_Driver/Major_and_Minor_number/README.md b/Linux/Device_Driver/Major_and_Minor_number/README.md new file mode 100644 index 0000000..6e94c8b --- /dev/null +++ b/Linux/Device_Driver/Major_and_Minor_number/README.md @@ -0,0 +1,151 @@ +# Character Device Driver: Major and Minor Numbers + +This repository demonstrates how to create a Linux character device driver that explicitly uses and manages major and minor numbers. It covers static and dynamic major number allocation and how these numbers relate to device files under `/dev`. + +## Goal + +The main goals of this example are: + +- To understand the role of major and minor numbers in character devices +- To register a character device with a specific major number (static allocation) +- To demonstrate dynamic major number allocation using the kernel API +- To show how major/minor numbers are associated with device files in `/dev` + +## What Are Major and Minor Numbers? + +In Linux: + +- The **major number** identifies the driver that controls a device. +- The **minor number** is used by the driver to distinguish between multiple devices or instances controlled by the same driver. + +Example: + +```text +/dev/mydev0 → major=, minor=0 +/dev/mydev1 → major=, minor=1 +``` + +Multiple minor numbers can share the same major number, allowing one driver to manage several devices. + +Character devices historically had fixed major numbers in `/proc/devices` or header files, but modern kernels typically use dynamically allocated major numbers. + +## Key Kernel APIs Used + +This driver typically uses: + +- `register_chrdev()` or `register_chrdev_region()` to register a character device region +- `alloc_chrdev_region()` to allocate a range of device numbers dynamically +- `cdev_init()` and `cdev_add()` to create and add a kernel `cdev` structure +- `device_create()` (often via a device class) to create a device node under `/dev` +- Cleanup functions: `cdev_del()`, `put_device()`, and `unregister_chrdev_region()` or `free_chrdev_region()` + +Your exact code may use a simpler old-style `register_chrdev()`/`unregister_chrdev()` depending on the tutorial. + +## Files in this Repository + +- `char_dev_major_minor.c` – Driver source implementing a character device with major/minor handling +- `Makefile` – Build script using the kernel build system +- `README.md` – This documentation + +Rename files to match your actual source. + +## Building the Module + +Ensure kernel headers and tools are installed. + +To build: + +```bash +make +``` + +This should produce a `.ko` file (for example `char_dev_major_minor.ko`). + +To clean: + +```bash +make clean +``` + +## Viewing Registered Devices + +After building, you can inspect current devices: + +```bash +cat /proc/devices +``` + +Look for your driver’s name and its major number. + +## Loading the Module + +Insert the module: + +```bash +sudo insmod char_dev_major_minor.ko +dmesg | tail +``` + +Check: + +- The major number printed in kernel logs +- The device file created under `/dev` (e.g., `/dev/mydev`) +- The entry in `/proc/devices` + +You can also inspect dynamic device info: + +```bash +ls /sys/class// +ls /dev/ +``` + +## Unloading the Module + +To remove the module: + +```bash +sudo rmmod char_dev_major_minor +dmesg | tail +``` + +Ensure that all resources (device numbers, `cdev`, device nodes) are properly freed. + +## Testing from User Space + +Once the device node exists: + +```bash +echo "test" | sudo tee /dev/your_device_name +sudo cat /dev/your_device_name +``` + +This exercises the driver’s `read` and `write` callbacks if they are implemented. + +Replace `/dev/your_device_name` with the actual device name created by your driver. + +## Prerequisites + +- Linux system with kernel headers and build tools +- Basic understanding of character devices and kernel modules +- Familiarity with `make`, `insmod`, `rmmod`, `dmesg`, and `/proc/devices` + +----------------------------------------------------------------------------------------------------------- +This is just a basic linux device driver. This will explain major and minor number in the linux device driver. + +Please update your Beaglebone board's kernel directory in the Makefile. + +Build for Beaglebone: + sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- + +Build for Raspberry Pi or Virtualbox Ubuntu: + sudo make + +You can check the video tutorial of this example here (https://www.youtube.com/watch?v=TfUTkCMCyig) and (https://youtu.be/aTwBCUjtTnw). + +Please refer this URL for the complete tutorial of this source code. +https://embetronicx.com/tutorials/linux/device-drivers/character-device-driver-major-number-and-minor-number/ + +The Linux Device Driver Video Playlist - https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k + +How to Setup Ubuntu and Raspberry PI - https://www.youtube.com/watch?v=e6gNeje3ljA +How to Setup BeagleBone and Cross compile the kernel - https://www.youtube.com/watch?v=am-dgmrMgYY&t diff --git a/Linux/Device_Driver/Major_and_Minor_number/ReadMe.md b/Linux/Device_Driver/Major_and_Minor_number/ReadMe.md deleted file mode 100644 index e4a31d9..0000000 --- a/Linux/Device_Driver/Major_and_Minor_number/ReadMe.md +++ /dev/null @@ -1,19 +0,0 @@ -This is just a basic linux device driver. This will explain major and minor number in the linux device driver. - -Please update your Beaglebone board's kernel directory in the Makefile. - -Build for Beaglebone: - sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- - -Build for Raspberry Pi or Virtualbox Ubuntu: - sudo make - -You can check the video tutorial of this example here (https://www.youtube.com/watch?v=TfUTkCMCyig) and (https://youtu.be/aTwBCUjtTnw). - -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/character-device-driver-major-number-and-minor-number/ - -The Linux Device Driver Video Playlist - https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k - -How to Setup Ubuntu and Raspberry PI - https://www.youtube.com/watch?v=e6gNeje3ljA -How to Setup BeagleBone and Cross compile the kernel - https://www.youtube.com/watch?v=am-dgmrMgYY&t diff --git a/Linux/Device_Driver/Passing_arguments_to_Linux_device_driver/README.md b/Linux/Device_Driver/Passing_arguments_to_Linux_device_driver/README.md new file mode 100644 index 0000000..3e6686d --- /dev/null +++ b/Linux/Device_Driver/Passing_arguments_to_Linux_device_driver/README.md @@ -0,0 +1,125 @@ +# Linux Device Driver – Part 3: Passing Arguments to Device Driver + +This repository demonstrates how to pass parameters (arguments) from user space to a Linux kernel module (device driver) when it is loaded. + +## Goal + +The goals of this example are: + +- To define module parameters that can be set at load time +- To understand how different data types (for example `int`, `charp`, arrays) are handled as parameters +- To see how these values are used inside the driver code +- To practice loading a module with different argument values + +## Concepts Covered + +This example focuses on: + +- Declaring module parameters using kernel macros +- Specifying default values and permissions for these parameters +- Accessing the parameter values inside `init` and other driver functions +- Observing the values via kernel logs + +These concepts apply to both simple “hello world” style modules and more complex device drivers. + +## How Module Parameters Work + +In the Linux kernel, module parameters: + +- Are declared in the driver source using macros (for example for integers, strings, or arrays) +- Can be set from user space when inserting the module using `insmod` or `modprobe` +- Appear in `/sys/module//parameters/` (subject to permissions), allowing runtime inspection and sometimes modification + +This allows flexible configuration of driver behavior without recompiling. + +## Files in this Repository + +- `param_driver.c` – Example driver showing how to declare and use module parameters +- `Makefile` – Builds the kernel module using the kernel build system +- `README.md` – Documentation for this part + +Rename files as needed to match your actual source. + +## Building the Module + +Ensure kernel headers and build tools are installed. + +To build: + +```bash +make +``` + +This should produce a `.ko` file (for example `param_driver.ko`). + +To clean: + +```bash +make clean +``` + +## Loading the Module with Arguments + +Load the module with default parameters: + +```bash +sudo insmod param_driver.ko +dmesg | tail +``` + +Load the module with custom parameters (example): + +```bash +sudo insmod param_driver.ko myint=10 mystring="hello" myarray=1,2,3 +dmesg | tail +``` + +Check `dmesg` (or `journalctl`) to verify that the driver prints the values of the parameters when it initializes. + +If supported by your code, you can also inspect parameters via: + +```bash +ls /sys/module/param_driver/parameters +cat /sys/module/param_driver/parameters/myint +``` + +Replace names with those used in your actual module. + +## Unloading the Module + +To remove the module: + +```bash +sudo rmmod param_driver +dmesg | tail +``` + +Ensure your exit function runs cleanly and that no state depends on the module parameters after unload. + +## Prerequisites + +- Linux system with development tools and kernel headers +- Basic understanding of kernel modules from previous parts +- Familiarity with `insmod`, `rmmod`, `dmesg`, and possibly `modprobe` + +------------------------------------------------------------------------------------------------------------- +This is just a basic linux device driver. This will explain how to pass the arguments to the linux device driver. + +Please update your Beaglebone board's kernel directory in the Makefile. + +Build for Beaglebone: + sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- + +Build for Raspberry Pi or Virtualbox Ubuntu: + sudo make + +You can check the video tutorial of this example here (https://www.youtube.com/watch?v=Z4jwi8SP5zs). + +Please refer this URL for the complete tutorial of this source code. +https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-3-passing-arguments-to-device-driver/ + +The Linux Device Driver Video Playlist - https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k + +How to Setup Ubuntu and Raspberry PI - https://www.youtube.com/watch?v=e6gNeje3ljA +How to Setup BeagleBone and Cross compile the kernel - https://www.youtube.com/watch?v=am-dgmrMgYY&t + diff --git a/Linux/Device_Driver/Passing_arguments_to_Linux_device_driver/ReadMe.md b/Linux/Device_Driver/Passing_arguments_to_Linux_device_driver/ReadMe.md deleted file mode 100644 index d2723c5..0000000 --- a/Linux/Device_Driver/Passing_arguments_to_Linux_device_driver/ReadMe.md +++ /dev/null @@ -1,20 +0,0 @@ -This is just a basic linux device driver. This will explain how to pass the arguments to the linux device driver. - -Please update your Beaglebone board's kernel directory in the Makefile. - -Build for Beaglebone: - sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- - -Build for Raspberry Pi or Virtualbox Ubuntu: - sudo make - -You can check the video tutorial of this example here (https://www.youtube.com/watch?v=Z4jwi8SP5zs). - -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-3-passing-arguments-to-device-driver/ - -The Linux Device Driver Video Playlist - https://www.youtube.com/watch?v=BRVGchs9UUQ&list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k - -How to Setup Ubuntu and Raspberry PI - https://www.youtube.com/watch?v=e6gNeje3ljA -How to Setup BeagleBone and Cross compile the kernel - https://www.youtube.com/watch?v=am-dgmrMgYY&t - diff --git a/Linux/Device_Driver/README.md b/Linux/Device_Driver/README.md new file mode 100644 index 0000000..20c7baf --- /dev/null +++ b/Linux/Device_Driver/README.md @@ -0,0 +1,51 @@ +refer: https://embetronicx.com/linux-device-driver-tutorials/ + +Contents +1. [Introduction](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/1%20LDD%20Introduction) +2. [First Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Hello_World) +3. [Passing Arguments](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Passing_arguments_to_Linux_device_driver) +4. [Major & Minor Number](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Major_and_Minor_number) +5. [Creating Device File](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Device_File_Creation) +6. [File Operations](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/File_Operations) +7. [Real Device Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Real_device_driver) +8. [IOCTL Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/IOCTL) +9. [Procfs Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/procfs) +10. [Waitqueue Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Waitqueue-Tutorial) +11. [SysFS Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/sysfs) +12. [Interrupts Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Interrupt-in-Linux-Kernel) +13. [Interrupt Programming](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Interrupt-in-Linux-Kernel) +14. [Workqueue (Static Method)](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Waitqueue-Tutorial) +15. [Workqueue (Dynamic Method)](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Waitqueue-Tutorial) +16. [Own Workqueue](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Workqueue-in-Linux-kernel/Own_Workqueue) +17. [Linked List 1](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Linked_List) +18. [Linked List 2](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Linked_List) +19. [Kernel Thread](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Kernel_Thread) +20. [Tasklet (Static Method)](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Tasklet) +21. [Tasklet (Dynamic Method)](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Tasklet) +22. [Mutex Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/mutex) +23. [Spinlock Tutorial 1](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Spinlock) +24. [Spinlock Part 2 (Read/Write Spinlock)](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Spinlock) +25. [Sending Signals](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Signal_in_Linux_kernel) +26. [Kernel Timer Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Kernel_Timer) +27. [High Resolution Timer Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/High_Resolution_Timer) +28. [Completion Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Completion) +29. [EXPORT_SYMBOL](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/EXPORT_SYMBOL) +30. [Atomic Variables Tutorial](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Atomic_variable) +31. [Seqlock](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Seqlock) +32. [Misc Device Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Misc_Driver) +33. [USB Device Driver Basics](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/usb_device_driver) +34. USB Device Driver Example Program +35. [GPIO Driver Basic](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/GPIO-in-Linux-Device-Driver) +36. [GPIO Interrupt](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/GPIO-Interrupt-in-Linux-Device-Driver) +37. [I2C Linux Device Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/I2C-Linux-Device-Driver) +38. [Dummy I2C Bus Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/I2C-Linux-Device-Driver) +39. [Real I2C Bus Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/I2C-Linux-Device-Driver) +40. [I2C Bus Driver using I2C-GPIO](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/I2C-Linux-Device-Driver) +41. [SSD1306 I2C Linux Device Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/I2C-Linux-Device-Driver) +42. [Poll Linux Example](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Poll) +43. [Select Linux Example](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Poll) +44. [E-Poll Linux Example](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Poll) +45. [Softirq Linux Example](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/Softirq) +46. [Threaded IRQ in Linux](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/ThreadedIRQ) +47. [SPI Protocol Driver](https://github.com/darshankharbikar/embetronix-Tutorials/tree/master/Linux/Device_Driver/SPI_Driver/SPI-driver-example-protocol-driver) +48. BMP280 I2C Pressure sensor Driver diff --git a/Linux/Device_Driver/Real_device_driver/ReadMe.md b/Linux/Device_Driver/Real_device_driver/ReadMe.md index 4a2a518..fa14285 100644 --- a/Linux/Device_Driver/Real_device_driver/ReadMe.md +++ b/Linux/Device_Driver/Real_device_driver/ReadMe.md @@ -1,3 +1,290 @@ +# Linux Device Driver Tutorial – Programming (Real Device Driver) + +This repository contains a complete “real” Linux character device driver and a matching user-space test application, based on the EmbetronicX Linux Device Driver tutorial series. It demonstrates how to: + +- Implement kernel-space driver functions: `open`, `write`, `read`, and `close` (release) +- Allocate and free kernel memory with `kmalloc()` and `kfree()` +- Safely copy data between user space and kernel space using `copy_from_user()` and `copy_to_user()` +- Use `cdev`, major/minor numbers, and automatic device file creation under `/dev` +- Build a kernel module and a user-space application, and test them together + +Source code for the original examples is available at: +https://github.com/Embetronicx/Tutorials/tree/master/Linux/Device_Driver/Real_device_driver + +The full tutorial series is at: +https://www.youtube.com/playlist?list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k +Website: https://embetronicx.com/linux-device-driver-tutorials/ + +## Concept + +- A **user-space application** communicates with a **kernel-space driver** via a device file (e.g. `/dev/etx_device`). +- When the user writes data to the device file, the driver: + - Copies the data from user space to kernel space using `copy_from_user()` + - Stores it in a kernel-allocated buffer (via `kmalloc()`) +- When the user reads the device file, the driver: + - Copies the stored data from kernel space back to user space using `copy_to_user()` + - Returns it to the user-space application + +This implements a simple “write once, read later” buffer in kernel memory. + +## Files in This Repository + +- `driver.c` – Kernel-space character device driver source +- `test_app.c` – User-space application to test the driver +- `Makefile` – Builds the kernel module (`driver.ko`) +- `README.md` – This documentation + +You can download the original code from: +https://github.com/Embetronicx/Tutorials/tree/master/Linux/Device_Driver/Real_device_driver + +## Key Kernel Functions Used + +### `kmalloc()` + +Allocates memory in kernel space (like `malloc()` in user space). + +```c +#include +void *kmalloc(size_t size, gfp_t flags); +``` + +- `size`: number of bytes to allocate +- `flags`: memory type, e.g.: + - `GFP_KERNEL` – normal kernel memory (may sleep) + - `GFP_USER` – for user behalf (may sleep) + - `GFP_ATOMIC` – no sleep (e.g. interrupt handlers) + +The memory is physically contiguous and not cleared. + +### `kfree()` + +Frees previously allocated kernel memory: + +```c +void kfree(const void *objp); +``` + +- `objp`: pointer returned by `kmalloc()` + +### `copy_from_user()` + +Copies data from user space to kernel space: + +```c +#include +unsigned long copy_from_user(void *to, const void __user *from, unsigned long n); +``` + +- `to`: destination in kernel space +- `from`: source in user space +- `n`: number of bytes to copy + +Returns number of bytes that could not be copied (0 on success). + +### `copy_to_user()` + +Copies data from kernel space to user space: + +```c +unsigned long copy_to_user(const void __user *to, const void *from, unsigned long n); +``` + +- `to`: destination in user space +- `from`: source in kernel space +- `n`: number of bytes to copy + +Returns number of bytes that could not be copied (0 on success). + +## File Operations in the Driver + +The driver implements four main operations: + +1. **`open`** – Called when the device file is opened. + - In this tutorial’s sample, it logs a message; the original full driver also allocates memory in `init`. +2. **`write`** – Called when data is written to the device file. + - Uses `copy_from_user()` to copy data from user space into `kernel_buffer`. +3. **`read`** – Called when data is read from the device file. + - Uses `copy_to_user()` to copy data from `kernel_buffer` back to user space. +4. **`release` (close)** – Called when the device file is closed. + - Frees the kernel buffer with `kfree()`. + +## Driver Overview + +Key points from `driver.c`: + +- Uses modern kernel APIs: + - `alloc_chrdev_region()`, `cdev_init()`, `cdev_add()` + - `class_create()`, `device_create()` for automatic `/dev/etx_device` +- Allocates a kernel buffer of size `mem_size` (1024 bytes) with `kmalloc()`. +- Initializes the buffer with `"Hello_World"` in the init function. +- Implements: + - `etx_open()` + - `etx_release()` + - `etx_read()` using `copy_to_user()` + - `etx_write()` using `copy_from_user()` + +## Building the Device Driver + +### Native Build (Ubuntu / Raspberry Pi) + +```bash +sudo make +``` + +This produces `driver.ko`. + +### Cross-Compile for BeagleBone (ARM) + +```bash +sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- +``` + +Make sure your `Makefile` has the correct BeagleBone kernel build path set in `KDIR`. + +Download the original Makefile from: +https://github.com/Embetronicx/Tutorials/tree/master/Linux/Device_Driver/Real_device_driver + +Clean: + +```bash +sudo make clean +``` + +## Compiling the User-Space Application + +### Native (x86) + +```bash +gcc -o test_app test_app.c +``` + +### For BeagleBone (ARM) + +```bash +arm-linux-gnueabihf-gcc -o test_app test_app.c +``` + +## Execution (Output) + +1. Load the driver: + +```bash +sudo insmod driver.ko +dmesg | tail +``` + +You should see: + +- Major/minor number printed +- “Device Driver Insert...Done!!!” + +2. Run the application: + +```bash +sudo ./test_app +``` + +Example interaction: + +```text +** +**WWW.EmbeTronicX.com** +**Please Enter the Option** + 1. Write + 2. Read + 3. Exit +** +Select option 1 to write data to the driver and write the string (e.g. "embetronicx"). + +1 +Your Option = 1 +Enter the string to write into driver :embetronicx +Data Writing ...Done! +``` + +The string is passed to the driver and stored in kernel memory. + +Now select option 2 to read: + +```text +2 +Your Option = 2 +Data Reading ...Done! + +Data = embetronicx +``` + +You now see the same string read back from kernel space. + +3. Exit: + +```text +3 +``` + +The device file is closed and the application exits. + +Check `dmesg` to see: + +- “Device File Opened...!!!” +- “Data Write : Done!” +- “Data Read : Done!” +- “Device File Closed...!!!” + +To remove the driver: + +```bash +sudo rmmod driver +dmesg | tail +``` + +You should see “Device Driver Remove...Done!!!”. + +## Using `echo` and `cat` Instead of the Application + +Instead of `test_app`, you can use shell commands: + +Write: + +```bash +echo "embetronicx" | sudo tee /dev/etx_device +``` + +Read: + +```bash +sudo cat /dev/etx_device +``` + +This exercises the same `write` and `read` callbacks. + +## Prerequisites + +- Linux system (Ubuntu, Raspberry Pi, or BeagleBone) with kernel headers +- Basic C programming knowledge +- Familiarity with: + - `make`, `gcc` + - `insmod`, `rmmod`, `dmesg` + - Device files under `/dev` + +## Minimal Build Environment (Ubuntu) + +```bash +sudo apt update +sudo apt install -y build-essential linux-headers-$(uname -r) +``` + +## Credits and Note on Copyright + +This repository contains example code inspired by the EmbetronicX Linux Device Driver tutorial series. +Refer to the original tutorial for detailed explanations, diagrams, and exact source code: + +- Tutorial page: https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-programming/ +- YouTube playlist: https://www.youtube.com/playlist?list=PLArwqFvBIlwHq8WMKgsXSQdqIvymrEz9k +- GitHub source: https://github.com/Embetronicx/Tutorials/tree/master/Linux/Device_Driver/Real_device_driver + +Please respect the author’s intellectual property. Do not copy the tutorial’s code verbatim unless you have permission. Use these examples as learning aids and adapt them in your own words and style. +## Conclusion This is just a basic linux device driver which explains about the real read and write of the device file. Please update your Beaglebone board's kernel directory in the Makefile. diff --git a/Linux/Device_Driver/Seqlock/ReadMe.md b/Linux/Device_Driver/Seqlock/ReadMe.md index 41cd6f9..9a6ccd7 100644 --- a/Linux/Device_Driver/Seqlock/ReadMe.md +++ b/Linux/Device_Driver/Seqlock/ReadMe.md @@ -1,4 +1,918 @@ +# Linux Device Driver - Seqlock in Linux Kernel + +## Overview + +A **Seqlock (Sequential Lock)** is a synchronization mechanism that gives **priority to writers** while allowing readers to read data without taking a traditional lock. It was introduced in Linux 2.5.60 to solve the writer-starvation problem that can occur with Read-Write Spinlocks. ([EmbeTronicX][1]) + +In simple terms: + +```text +Spinlock + | + +--> Readers and Writers treated equally + +RW Spinlock + | + +--> Readers favored + +Seqlock + | + +--> Writers favored +``` + +--- + +# Why Seqlock? + +Consider a system where: + +```text +95% Reads +5% Writes +``` + +Using a RW Spinlock: + +```text +Reader1 +Reader2 +Reader3 +Reader4 +Reader5 + | + +--> Writer Waiting +``` + +New readers can continue entering, causing: + +```text +Writer Starvation +``` + +Seqlock solves this by allowing writers to proceed while readers retry if a write occurs during their read. ([EmbeTronicX][1]) + +--- + +# Core Idea + +Seqlock uses a **sequence counter**. + +```text +Sequence Number +``` + +Rules: + +```text +Even Number + | + +--> No Writer Active + +Odd Number + | + +--> Writer Active +``` + +Writer operation: + +```text +Acquire Lock + | + +--> seq++ + | (odd) + | + +--> Modify Data + | + +--> seq++ + (even) +``` + +Readers use the sequence number to determine whether the data they read was consistent. ([EmbeTronicX][1]) + +--- + +# How Seqlock Works + +## Writer Side + +Suppose: + +```text +seq = 10 +``` + +Writer enters: + +```text +seq = 11 +``` + +Now: + +```text +Odd Value + | + +--> Writing In Progress +``` + +After update: + +```text +seq = 12 +``` + +Now: + +```text +Even Value + | + +--> Write Completed +``` + +([EmbeTronicX][1]) + +--- + +## Reader Side + +Reader performs: + +```text +Step 1: +Read Sequence Number + +Step 2: +Read Data + +Step 3: +Read Sequence Number Again + +Step 4: +Compare +``` + +If: + +```text +Same Even Number +``` + +Data is valid. + +If: + +```text +Sequence Changed + OR +Sequence Odd +``` + +Reader retries. ([EmbeTronicX][1]) + +--- + +# Reader Example + +Initial state: + +```text +seq = 20 +``` + +Reader: + +```text +Read seq = 20 + +Read data + +Read seq = 20 +``` + +Result: + +```text +Valid Read +``` + +--- + +# Reader During Write + +```text +Read seq = 20 + +Writer Starts + +seq = 21 + +Writer Updates Data + +seq = 22 + +Reader Reads seq = 22 +``` + +Comparison: + +```text +20 != 22 +``` + +Result: + +```text +Retry Read +``` + +([EmbeTronicX][1]) + +--- + +# Advantages of Seqlock + +## Fast Readers + +Readers: + +```text +No Lock Acquisition +No Spinlock +No Mutex +``` + +Just: + +```text +Read +Verify +Retry If Needed +``` + +--- + +## No Writer Starvation + +Writer: + +```text +Never Waits For Readers +``` + +Readers retry instead. ([EmbeTronicX][1]) + +--- + +## Scales Well + +Read-heavy workloads: + +```text +Many Readers +Few Writers +``` + +perform very efficiently. ([Kernel Internals][2]) + +--- + +# Limitations + +Seqlock is **not a general-purpose lock**. + +--- + +## Cannot Protect Pointers + +Bad: + +```c +struct node *head; +``` + +Reason: + +```text +Reader May Follow Pointer + +Writer Changes Pointer + +Reader Crashes +``` + +Seqlocks are generally unsuitable for pointer-based data structures. ([EmbeTronicX][1]) + +--- + +## Reader May Retry Forever + +Heavy writes: + +```text +Writer +Writer +Writer +Writer +``` + +Reader: + +```text +Read +Retry + +Read +Retry + +Read +Retry +``` + +Possible starvation of readers. + +--- + +## Not Suitable For Large Data + +Best for: + +```text +Counters +Timestamps +Statistics +Simple Structures +``` + +Not ideal for complex structures. ([EmbeTronicX][1]) + +--- + +# When To Use Seqlock + +Good: + +```text +Timekeeping +Jiffies +Statistics +Configuration Values +Network Counters +Device Status +``` + +Bad: + +```text +Linked Lists +Trees +Pointer Structures +Dynamic Objects +``` + +([Kernel Internals][2]) + +--- + +# Header File + +```c +#include +``` + +--- + +# Seqlock Data Type + +```c +seqlock_t etx_seq_lock; +``` + +--- + +# Initialization + +## Dynamic + +```c +seqlock_t etx_seq_lock; + +seqlock_init( + &etx_seq_lock); +``` + +--- + +## Static + +```c +DEFINE_SEQLOCK( + etx_seq_lock); +``` + +([Kernel][3]) + +--- + +# Writer APIs + +Writers use an embedded spinlock. + +--- + +## write_seqlock() + +Acquire writer lock. + +```c +write_seqlock( + &etx_seq_lock); +``` + +Internally: + +```text +Lock Spinlock +Increment Sequence +``` + +([EmbeTronicX][1]) + +--- + +## write_sequnlock() + +Release writer lock. + +```c +write_sequnlock( + &etx_seq_lock); +``` + +Internally: + +```text +Increment Sequence +Unlock Spinlock +``` + +([EmbeTronicX][1]) + +--- + +## Example Writer + +```c +write_seqlock( + &etx_seq_lock); + +etx_global_variable++; + +write_sequnlock( + &etx_seq_lock); +``` + +--- + +# Non-Blocking Writer + +## write_tryseqlock() + +```c +if(write_tryseqlock( + &etx_seq_lock)) +{ + /* write */ +} +``` + +Returns: + +| Value | Meaning | +| -------- | ------------- | +| Non-zero | Lock Acquired | +| 0 | Lock Busy | + +([EmbeTronicX][1]) + +--- + +# IRQ Variants + +--- + +## write_seqlock_irq() + +```c +write_seqlock_irq( + &etx_seq_lock); +``` + +Disables interrupts. + +--- + +## write_sequnlock_irq() + +```c +write_sequnlock_irq( + &etx_seq_lock); +``` + +Enables interrupts. + +--- + +## write_seqlock_irqsave() + +```c +unsigned long flags; + +write_seqlock_irqsave( + &etx_seq_lock, + flags); +``` + +--- + +## write_sequnlock_irqrestore() + +```c +write_sequnlock_irqrestore( + &etx_seq_lock, + flags); +``` + +Restores original interrupt state. ([EmbeTronicX][1]) + +--- + +# Bottom Half Variants + +```c +write_seqlock_bh() +write_sequnlock_bh() +``` + +Used in: + +```text +Tasklets +SoftIRQs +``` + +([EmbeTronicX][1]) + +--- + +# Reader APIs + +Readers never acquire a lock. + +--- + +## read_seqbegin() + +Begin reading. + +```c +unsigned int seq; + +seq = +read_seqbegin( + &etx_seq_lock); +``` + +Returns current sequence number. ([EmbeTronicX][1]) + +--- + +## read_seqretry() + +Verify read consistency. + +```c +read_seqretry( + &etx_seq_lock, + seq); +``` + +Returns: + +| Value | Meaning | +| ----- | -------------- | +| 0 | Data Valid | +| 1 | Retry Required | + +([EmbeTronicX][1]) + +--- + +# Reader Example + +```c +unsigned int seq; + +do +{ + seq = + read_seqbegin( + &etx_seq_lock); + + value = + etx_global_variable; + +} while( + read_seqretry( + &etx_seq_lock, + seq)); +``` + +Execution: + +```text +Read Seq +Read Data +Verify Seq + +Changed? + | + +--> Retry + +Unchanged? + | + +--> Success +``` + +([EmbeTronicX][1]) + +--- + +# Complete Example + +## Shared Variable + +```c +unsigned long +etx_global_variable = 0; +``` + +--- + +## Seqlock + +```c +DEFINE_SEQLOCK( + etx_seq_lock); +``` + +--- + +## Writer Thread + +```c +while( + !kthread_should_stop()) +{ + write_seqlock( + &etx_seq_lock); + + etx_global_variable++; + + write_sequnlock( + &etx_seq_lock); + + msleep(1000); +} +``` + +--- + +## Reader Thread + +```c +unsigned int seq; +unsigned long value; + +do +{ + seq = + read_seqbegin( + &etx_seq_lock); + + value = + etx_global_variable; + +} while( + read_seqretry( + &etx_seq_lock, + seq)); +``` + +([EmbeTronicX][1]) + +--- + +# Execution Flow + +```text +Writer + | + +--> seq = 1 + | + +--> Update Data + | + +--> seq = 2 + +Reader + | + +--> Read seq = 2 + | + +--> Read Data + | + +--> Read seq = 2 + | + +--> Success +``` + +--- + +# Seqlock vs Spinlock + +| Feature | Spinlock | Seqlock | +| ---------------- | -------- | ------- | +| Readers Lock | Yes | No | +| Writers Lock | Yes | Yes | +| Reader Retry | No | Yes | +| Writer Priority | No | Yes | +| Pointer Safety | Yes | No | +| Read Performance | Moderate | High | + +([EmbeTronicX][1]) + +--- + +# Seqlock vs Read-Write Spinlock + +| Feature | RW Spinlock | Seqlock | +| ------------------ | ----------- | -------- | +| Multiple Readers | Yes | Yes | +| Reader Locking | Yes | No | +| Writer Starvation | Possible | No | +| Reader Starvation | Rare | Possible | +| Pointer Protection | Yes | No | +| Read Overhead | Higher | Lower | + +([EmbeTronicX][1]) + +--- + +# Common Kernel Uses + +Linux itself uses sequence counters and seqlocks extensively for: + +```text +Timekeeping +Jiffies +Clock Sources +Statistics +Kernel Counters +``` + +because reads are extremely frequent and writes are relatively rare. ([Kernel Internals][2]) + +--- + +# Common Mistakes + +## Using Pointers + +Wrong: + +```c +struct node *head; +``` + +Protected by seqlock. + +Potential: + +```text +Reader Uses Invalid Pointer +``` + +([EmbeTronicX][1]) + +--- + +## Long Write Sections + +Wrong: + +```c +write_seqlock(&lock); + +/* lengthy work */ + +write_sequnlock(&lock); +``` + +Readers may retry repeatedly. + +--- + +## Sleeping While Holding Write Lock + +Wrong: + +```c +write_seqlock(&lock); + +msleep(100); + +write_sequnlock(&lock); +``` + +Writer lock uses a spinlock internally. + +--- + +# Important APIs Summary + +## Initialization + +```c +seqlock_init() + +DEFINE_SEQLOCK() +``` + +--- + +## Writer + +```c +write_seqlock() + +write_tryseqlock() + +write_sequnlock() +``` + +--- + +## IRQ Variants + +```c +write_seqlock_irq() + +write_sequnlock_irq() + +write_seqlock_irqsave() + +write_sequnlock_irqrestore() +``` + +--- + +## Bottom Half Variants + +```c +write_seqlock_bh() + +write_sequnlock_bh() +``` + +--- + +## Reader + +```c +read_seqbegin() + +read_seqretry() +``` + +--- + +# Key Takeaways + +* Seqlock is a **writer-priority synchronization mechanism**. +* Readers never acquire a traditional lock. +* Writers use an embedded spinlock for mutual exclusion. +* Readers validate data using a sequence counter. +* If a write occurs during a read, the reader retries. +* Seqlocks eliminate writer starvation. +* Best suited for small, simple, read-mostly data. +* Not suitable for pointer-based or complex dynamic data structures. +* Frequently used in Linux timekeeping and statistics subsystems. ([EmbeTronicX][1]) + +[1]: https://embetronicx.com/tutorials/linux/device-drivers/seqlock-in-linux-kernel/?utm_source=chatgpt.com "Seqlock in Linux Kernel - Linux Device DriverTutorial Part 31" +[2]: https://kernel-internals.org/locking/seqlock/?utm_source=chatgpt.com "Seqlock - Linux Kernel Internals" +[3]: https://www.kernel.org/doc/html/latest/locking/seqlock.html?utm_source=chatgpt.com "Sequence counters and sequential locks — The Linux Kernel documentation" + + +## Conclusion This is just a basic linux device driver which explains about the Seqlock in linux device driver. Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/seqlock-in-linux-kernel/ \ No newline at end of file +https://embetronicx.com/tutorials/linux/device-drivers/seqlock-in-linux-kernel/ diff --git a/Linux/Device_Driver/Signal_in_Linux_kernel/README.md b/Linux/Device_Driver/Signal_in_Linux_kernel/README.md new file mode 100644 index 0000000..4b795d5 --- /dev/null +++ b/Linux/Device_Driver/Signal_in_Linux_kernel/README.md @@ -0,0 +1,680 @@ +# Linux Device Driver - Sending Signal from Kernel Driver to User Space + +## Overview + +Signals are one of the oldest Inter-Process Communication (IPC) mechanisms in Linux. + +They are used to notify a process that a specific event has occurred. + +Examples: + +```text +ATM Transaction + | + +--> SMS Notification + +Microwave Finished + | + +--> Beep Sound + +Linux Driver Event + | + +--> Signal To User Application +``` + +Linux drivers can send signals directly to user-space applications whenever an event occurs, such as: + +* Hardware interrupt +* GPIO event +* DMA completion +* Sensor threshold crossing +* Device error +* Data ready notification + +The EmbeTronicX example demonstrates sending a signal from a device driver to a user-space application when an interrupt occurs. + +--- + +# What is a Signal? + +A signal is an asynchronous notification sent to a process or thread. + +Examples of standard Linux signals: + +| Signal | Description | +| ------- | ------------------ | +| SIGINT | Ctrl+C | +| SIGKILL | Kill process | +| SIGTERM | Terminate | +| SIGSEGV | Segmentation fault | +| SIGUSR1 | User-defined | +| SIGUSR2 | User-defined | + +The tutorial uses a custom signal: + +```c +#define SIGETX 44 +``` + +Signal number 44 is used to notify the application from the driver. + +--- + +# Driver to User Communication Flow + +```text +User Application + | + | Register PID + v +Device Driver + | + | Interrupt Occurs + v +Interrupt Handler + | + | send_sig_info() + v +Linux Signal Subsystem + | + v +Signal Handler + | + v +User Application +``` + +--- + +# Steps Involved + +To send a signal from kernel space to user space: + +```text +1. Select Signal Number + +2. Register Application + +3. Save Process Information + +4. Event Occurs + +5. Driver Sends Signal + +6. User Application Handles Signal +``` + +--- + +# Step 1 - Select Signal Number + +Choose a signal number. + +Example: + +```c +#define SIGETX 44 +``` + +This signal will be delivered to the application whenever the event occurs. + +--- + +# Step 2 - Register User Application + +Before sending a signal, the driver must know: + +```text +Which Process Should Receive It? +``` + +The application registers itself using: + +```text +IOCTL +``` + +Application: + +```c +ioctl( + fd, + REG_CURRENT_TASK, + &number); +``` + +Driver: + +```c +#define REG_CURRENT_TASK \ + _IOW('a','a',int32_t *) +``` + +--- + +# Step 3 - Save Process Information + +The driver stores the current task. + +```c +static struct task_struct *task = NULL; +``` + +Inside IOCTL: + +```c +task = get_current(); +``` + +This returns: + +```text +Current User Process +``` + +Kernel now knows where to send the signal. + +--- + +# Driver Registration Code + +```c +static long etx_ioctl( + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + if(cmd == REG_CURRENT_TASK) + { + task = get_current(); + + signum = SIGETX; + } + + return 0; +} +``` + +Purpose: + +```text +Store User Process Context +``` + +--- + +# Event Trigger + +In the example: + +```text +Read Device File + | + v +Software Interrupt + | + v +Interrupt Handler + | + v +Send Signal +``` + +Driver read function: + +```c +static ssize_t etx_read(...) +{ + asm("int $0x3B"); + + return 0; +} +``` + +This generates IRQ 11. + +--- + +# Interrupt Handler + +When interrupt occurs: + +```c +static irqreturn_t +irq_handler( + int irq, + void *dev_id) +{ + ... +} +``` + +Purpose: + +```text +Prepare Signal Information +``` + +--- + +# Signal Information Structure + +Kernel uses: + +```c +struct siginfo info; +``` + +Initialize: + +```c +memset( + &info, + 0, + sizeof(info)); +``` + +Fill fields: + +```c +info.si_signo = SIGETX; + +info.si_code = SI_QUEUE; + +info.si_int = 1; +``` + +Meaning: + +| Field | Description | +| -------- | ---------------------- | +| si_signo | Signal number | +| si_code | Signal source | +| si_int | Custom integer payload | + +--- + +# Sending Signal + +Core API: + +```c +send_sig_info( + SIGETX, + &info, + task); +``` + +Example: + +```c +if(task != NULL) +{ + send_sig_info( + SIGETX, + &info, + task); +} +``` + +Parameters: + +| Parameter | Description | +| --------- | -------------- | +| SIGETX | Signal number | +| &info | Signal payload | +| task | Target process | + +--- + +# Driver Side Flow + +```text +IRQ Occurs + | + v +Create siginfo + | + v +Fill Data + | + v +send_sig_info() + | + v +Signal Delivered +``` + +--- + +# User Space Signal Handler + +Application installs handler: + +```c +struct sigaction act; +``` + +Register: + +```c +act.sa_flags = SA_SIGINFO; + +act.sa_sigaction = + sig_event_handler; + +sigaction( + SIGETX, + &act, + NULL); +``` + +--- + +# Signal Callback + +```c +void sig_event_handler( + int n, + siginfo_t *info, + void *unused) +{ + if(n == SIGETX) + { + check = info->si_int; + + printf( + "Received signal\n"); + } +} +``` + +This function executes automatically when the signal arrives. + +--- + +# Passing Data with Signal + +Driver: + +```c +info.si_int = 1; +``` + +User Space: + +```c +check = info->si_int; +``` + +Output: + +```text +Received signal from kernel +Value = 1 +``` + +This allows small event data to accompany the signal. + +--- + +# Complete Execution Flow + +```text +Application Starts + | + v +Open Device + | + v +IOCTL Registration + | + v +Driver Stores Task + | + v +Waiting... + | + v +Interrupt Occurs + | + v +ISR Executes + | + v +send_sig_info() + | + v +Signal Arrives + | + v +Signal Handler Executes +``` + +--- + +# Sample Application Output + +```text +Installed signal handler +for SIGETX = 44 + +Opening Driver + +Registering application... +Done!!! + +Waiting for signal... +``` + +After interrupt: + +```text +Received signal +from kernel : Value = 1 +``` + +--- + +# Sample Driver Logs + +```text +Device Driver Insert...Done!!! + +REG_CURRENT_TASK + +Read Function + +Shared IRQ: +Interrupt Occurred + +Sending signal to app +``` + +This confirms: + +```text +Application Registered + +Interrupt Triggered + +Signal Sent Successfully +``` + +--- + +# Common Use Cases + +## GPIO Interrupt + +```text +Button Pressed + | + +--> Signal Application +``` + +--- + +## Sensor Threshold + +```text +Temperature > Limit + | + +--> Notify User Space +``` + +--- + +## DMA Completion + +```text +DMA Transfer Done + | + +--> Signal Process +``` + +--- + +## Data Ready + +```text +UART Data Available + | + +--> Signal Reader +``` + +--- + +## Hardware Error + +```text +Device Fault + | + +--> Alert Application +``` + +--- + +# Advantages + +* Simple implementation +* Low overhead +* Asynchronous notification +* Fast event delivery +* No polling required + +--- + +# Limitations + +Signals are best for: + +```text +Notification +``` + +Not for: + +```text +Large Data Transfer +``` + +Bad use case: + +```text +Send 1 KB Data +``` + +Good use case: + +```text +Data Ready +Go Read It +``` + +For large transfers use: + +* read() +* mmap() +* Netlink +* Shared Memory +* Character Device Buffers + +--- + +# Signal vs Polling + +| Feature | Signal | Polling | +| --------------- | ------ | -------------------- | +| CPU Usage | Low | High | +| Latency | Low | Depends on Poll Rate | +| Event Driven | Yes | No | +| Power Efficient | Yes | No | + +--- + +# Signal vs Netlink + +| Feature | Signal | Netlink | +| ------------------- | --------- | --------- | +| Simple Notification | Excellent | Good | +| Large Data | Poor | Excellent | +| Bidirectional | Limited | Yes | +| Structured Messages | No | Yes | + +--- + +# Important APIs Summary + +## Driver Side + +```c +get_current() + +send_sig_info() +``` + +--- + +## Signal Information + +```c +struct siginfo +``` + +--- + +## User Space + +```c +sigaction() +``` + +--- + +## Registration + +```c +ioctl() +``` + +--- + +# Key Takeaways + +* Linux signals provide asynchronous notifications between kernel and user space. +* The user application must register itself with the driver first. +* The driver stores the application's `task_struct`. +* `send_sig_info()` is the primary kernel API used to send signals. +* `struct siginfo` can carry small payload data. +* Signal handlers are registered using `sigaction()`. +* Signals are ideal for event notification but not for large data transfer. +* Common embedded Linux use cases include GPIO interrupts, DMA completion, sensor events, and hardware fault notifications. +* A signal-based design avoids inefficient polling loops and provides immediate event notification. + + +## Conclusion +This is just a basic linux device driver which explains send signals from kernel to userspace. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/sending-signal-from-linux-device-driver-to-user-space/ diff --git a/Linux/Device_Driver/Signal_in_Linux_kernel/ReadMe.md b/Linux/Device_Driver/Signal_in_Linux_kernel/ReadMe.md deleted file mode 100644 index ec4e64b..0000000 --- a/Linux/Device_Driver/Signal_in_Linux_kernel/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains send signals from kernel to userspace. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/sending-signal-from-linux-device-driver-to-user-space/ \ No newline at end of file diff --git a/Linux/Device_Driver/Spinlock/Read_Write_Spinlock/README.md b/Linux/Device_Driver/Spinlock/Read_Write_Spinlock/README.md new file mode 100644 index 0000000..0e6cc8c --- /dev/null +++ b/Linux/Device_Driver/Spinlock/Read_Write_Spinlock/README.md @@ -0,0 +1,840 @@ +# Linux Device Driver - Read-Write Spinlock in Linux Kernel (Spinlock Part 2) + +## Overview + +A Read-Write Spinlock (`rwlock_t`) is an extension of a normal spinlock that differentiates between: + +* Readers +* Writers + +Unlike a normal spinlock where only one thread can enter the critical section, a Read-Write Spinlock allows: + +```text +Multiple Readers + OR +Single Writer +``` + +This improves performance in read-heavy workloads. + +--- + +# Why Read-Write Spinlock? + +Consider a shared variable: + +```c +unsigned long counter; +``` + +Access Pattern: + +```text +Thread1 -> Read +Thread2 -> Read +Thread3 -> Read +Thread4 -> Read +Thread5 -> Write +``` + +With a normal spinlock: + +```text +Reader1 + | + +--> Lock + +Reader2 + | + +--> Wait + +Reader3 + | + +--> Wait +``` + +All readers serialize unnecessarily. + +With Read-Write Spinlock: + +```text +Reader1 +Reader2 +Reader3 +Reader4 + | + +--> Run Together +``` + +Only writers require exclusive access. + +--- + +# What Problem Does It Solve? + +Normal Spinlock: + +```text +One Reader Allowed +``` + +Read-Write Spinlock: + +```text +Many Readers Allowed +One Writer Allowed +``` + +Suitable when: + +```text +Reads >>> Writes +``` + +Examples: + +* Statistics counters +* Configuration tables +* Routing tables +* Driver status structures +* Lookup tables + +--- + +# Working Principle + +## Case 1: No Lock Holder + +```text +Critical Section Empty + +Reader -> Allowed +Writer -> Allowed +``` + +--- + +## Case 2: Reader Holds Lock + +```text +Reader1 Running +``` + +New Reader: + +```text +Reader2 -> Allowed +Reader3 -> Allowed +Reader4 -> Allowed +``` + +Writer: + +```text +Writer -> Must Wait +``` + +--- + +## Case 3: Writer Holds Lock + +```text +Writer Running +``` + +Then: + +```text +Reader -> Blocked +Writer -> Blocked +``` + +No other thread can enter. + +--- + +# Reader Preference + +Read-Write Spinlock favors readers. + +Example: + +```text +Reader1 enters +Reader2 enters +Reader3 enters + +Writer waiting +``` + +While writer is waiting: + +```text +Reader4 enters +Reader5 enters +Reader6 enters +``` + +Writer continues waiting until all readers leave. + +This can cause: + +```text +Writer Starvation +``` + +For writer-priority workloads, Linux provides: + +```text +Seqlock +``` + +instead. + +--- + +# When To Use? + +Use Read Lock: + +```text +Only Reading Data +``` + +Use Write Lock: + +```text +Modifying Data +``` + +Ideal when: + +```text +90% Reads +10% Writes +``` + +Not useful when: + +```text +Frequent Writes +``` + +because writers block everyone. + +--- + +# Header File + +```c +#include +``` + +--- + +# Data Type + +```c +rwlock_t etx_rwlock; +``` + +Represents a Read-Write Spinlock. + +--- + +# Initialization + +## Static Method + +```c +DEFINE_RWLOCK(etx_rwlock); +``` + +Creates and initializes lock. + +Example: + +```c +static DEFINE_RWLOCK(etx_rwlock); +``` + +--- + +## Dynamic Method + +Declaration: + +```c +rwlock_t etx_rwlock; +``` + +Initialization: + +```c +rwlock_init(&etx_rwlock); +``` + +Used when runtime initialization is required. + +--- + +# Approach 1 - User Context Locking + +Used between: + +```text +Kernel Thread + ↔ +Kernel Thread +``` + +--- + +# Read Lock APIs + +## read_lock() + +Acquire reader lock. + +```c +read_lock( + &etx_rwlock); +``` + +Behavior: + +```text +No Writer Present + | + +--> Enter + +Writer Present + | + +--> Spin +``` + +--- + +## read_unlock() + +Release reader lock. + +```c +read_unlock( + &etx_rwlock); +``` + +--- + +# Write Lock APIs + +## write_lock() + +Acquire writer lock. + +```c +write_lock( + &etx_rwlock); +``` + +Behavior: + +```text +No Readers +No Writers + | + +--> Enter + +Otherwise + | + +--> Spin +``` + +--- + +## write_unlock() + +Release writer lock. + +```c +write_unlock( + &etx_rwlock); +``` + +--- + +# Example - Reader/Writer Threads + +## Writer Thread + +```c +int thread_function1( + void *pv) +{ + while( + !kthread_should_stop()) + { + write_lock( + &etx_rwlock); + + etx_global_variable++; + + write_unlock( + &etx_rwlock); + + msleep(1000); + } + + return 0; +} +``` + +--- + +## Reader Thread + +```c +int thread_function2( + void *pv) +{ + while( + !kthread_should_stop()) + { + read_lock( + &etx_rwlock); + + printk( + "Value=%lu\n", + etx_global_variable); + + read_unlock( + &etx_rwlock); + + msleep(1000); + } + + return 0; +} +``` + +--- + +# Approach 2 - Between Bottom Halves + +Used between: + +```text +Tasklet ↔ Tasklet + +SoftIRQ ↔ SoftIRQ +``` + +Use: + +```c +read_lock() +write_lock() +``` + +and corresponding unlock APIs. + +--- + +# Approach 3 - User Context + Bottom Half + +Used between: + +```text +Kernel Thread + ↔ +Tasklet + +Kernel Thread + ↔ +SoftIRQ +``` + +--- + +## Read Lock + +```c +read_lock_bh( + &etx_rwlock); +``` + +--- + +## Read Unlock + +```c +read_unlock_bh( + &etx_rwlock); +``` + +--- + +## Write Lock + +```c +write_lock_bh( + &etx_rwlock); +``` + +--- + +## Write Unlock + +```c +write_unlock_bh( + &etx_rwlock); +``` + +What `_bh` does: + +```text +Disable SoftIRQ +Acquire Lock +``` + +Unlock: + +```text +Release Lock +Enable SoftIRQ +``` + +--- + +# Approach 4 - Hard IRQ + Bottom Half + +Used between: + +```text +ISR + ↔ +Tasklet + +ISR + ↔ +SoftIRQ +``` + +--- + +## Read Lock + +```c +read_lock_irq( + &etx_rwlock); +``` + +--- + +## Read Unlock + +```c +read_unlock_irq( + &etx_rwlock); +``` + +--- + +## Write Lock + +```c +write_lock_irq( + &etx_rwlock); +``` + +--- + +## Write Unlock + +```c +write_unlock_irq( + &etx_rwlock); +``` + +These APIs: + +```text +Disable Local Interrupts +Acquire Lock +``` + +Then: + +```text +Release Lock +Enable Interrupts +``` + +--- + +# Approach 5 - IRQ Save/Restore Variant + +Preferred in many drivers. + +--- + +## Read Side + +```c +unsigned long flags; + +read_lock_irqsave( + &etx_rwlock, + flags); + +... + +read_unlock_irqrestore( + &etx_rwlock, + flags); +``` + +--- + +## Write Side + +```c +unsigned long flags; + +write_lock_irqsave( + &etx_rwlock, + flags); + +... + +write_unlock_irqrestore( + &etx_rwlock, + flags); +``` + +Advantage: + +```text +Restores Previous IRQ State +``` + +instead of blindly enabling interrupts. + +--- + +# Execution Example + +```text +Reader1 + | + +--> read_lock() + +Reader2 + | + +--> read_lock() + +Reader3 + | + +--> read_lock() +``` + +All three execute simultaneously. + +Writer: + +```text +Writer + | + +--> write_lock() + | + +--> Waiting +``` + +After all readers exit: + +```text +Writer Acquires Lock +``` + +--- + +# Read-Write Spinlock vs Spinlock + +| Feature | Spinlock | RW Spinlock | +| -------------------- | -------- | ----------- | +| Readers | 1 | Multiple | +| Writers | 1 | 1 | +| Read Parallelism | No | Yes | +| Write Parallelism | No | No | +| Read Heavy Workloads | Poor | Excellent | +| Complexity | Simple | Higher | + +--- + +# Read-Write Spinlock vs Mutex + +| Feature | RW Spinlock | Mutex | +| --------------------- | ----------- | ----- | +| Sleep Allowed | No | Yes | +| Busy Wait | Yes | No | +| IRQ Context | Yes | No | +| Read Sharing | Yes | No | +| Long Critical Section | No | Yes | + +--- + +# Typical Driver Use Cases + +## Statistics + +```c +read_lock(&stats_lock); +packets = stats.rx_packets; +read_unlock(&stats_lock); +``` + +--- + +## Routing Table + +```c +read_lock(&route_lock); +lookup_route(); +read_unlock(&route_lock); +``` + +--- + +## Device State + +```c +read_lock(&state_lock); +status = device_status; +read_unlock(&state_lock); +``` + +--- + +## Configuration Updates + +```c +write_lock(&cfg_lock); +update_configuration(); +write_unlock(&cfg_lock); +``` + +--- + +# Common Mistakes + +## Sleeping Inside Lock + +Wrong: + +```c +read_lock(&lock); + +msleep(100); + +read_unlock(&lock); +``` + +RW Spinlocks cannot sleep. + +--- + +## Using For Write-Heavy Workloads + +Wrong choice: + +```text +Writes >> Reads +``` + +Use normal spinlock or mutex instead. + +--- + +## Long Critical Sections + +Wrong: + +```c +write_lock(&lock); + +/* lengthy processing */ + +write_unlock(&lock); +``` + +Readers and writers remain blocked/spinning. + +--- + +# Important APIs Summary + +## Initialization + +```c +DEFINE_RWLOCK() + +rwlock_init() +``` + +--- + +## Reader + +```c +read_lock() +read_unlock() + +read_lock_bh() +read_unlock_bh() + +read_lock_irq() +read_unlock_irq() + +read_lock_irqsave() +read_unlock_irqrestore() +``` + +--- + +## Writer + +```c +write_lock() +write_unlock() + +write_lock_bh() +write_unlock_bh() + +write_lock_irq() +write_unlock_irq() + +write_lock_irqsave() +write_unlock_irqrestore() +``` + +--- + +# Key Takeaways + +* Read-Write Spinlock allows **multiple concurrent readers** but only **one writer**. +* Readers can run simultaneously if no writer holds the lock. +* Writers require exclusive access. +* RW spinlocks are best for **read-heavy workloads**. +* RW spinlocks run in atomic context and cannot sleep. +* `_bh` variants protect against SoftIRQ/Tasklet interference. +* `_irq` variants disable interrupts while holding the lock. +* `_irqsave` variants are usually safest because they restore the previous interrupt state. +* RW spinlocks favor readers and can potentially starve writers. +* Use RW spinlocks only when read parallelism provides a measurable benefit. + + +## Conclusion +This is just a basic linux device driver. This will explain read write spinlock in the linux device driver. + +Please refer this URL for the complete tutorial of this source code. +https://embetronicx.com/tutorials/linux/device-drivers/read-write-spinlock/ diff --git a/Linux/Device_Driver/Spinlock/Read_Write_Spinlock/ReadMe.md b/Linux/Device_Driver/Spinlock/Read_Write_Spinlock/ReadMe.md deleted file mode 100644 index d44b250..0000000 --- a/Linux/Device_Driver/Spinlock/Read_Write_Spinlock/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver. This will explain read write spinlock in the linux device driver. - -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/read-write-spinlock/ \ No newline at end of file diff --git a/Linux/Device_Driver/Spinlock/Spinlock/README.md b/Linux/Device_Driver/Spinlock/Spinlock/README.md new file mode 100644 index 0000000..5fb06ca --- /dev/null +++ b/Linux/Device_Driver/Spinlock/Spinlock/README.md @@ -0,0 +1,717 @@ +# Linux Device Driver - Spinlock in Linux Kernel (Part 1) + +## Overview + +A Spinlock is a lightweight synchronization mechanism used to protect shared resources from concurrent access. + +Like a mutex, it provides mutual exclusion, but unlike a mutex, a thread does **not sleep** when the lock is unavailable. + +Instead, it continuously checks the lock in a loop (**spins**) until the lock becomes available. + +--- + +# Why Spinlocks? + +Consider two kernel threads updating a shared variable. + +Without synchronization: + +```text +Thread1 Thread2 + | | +Read Value = 0 Read Value = 0 + | | +Increment Increment + | | +Write 1 Write 1 +``` + +Expected: + +```text +Global Variable = 2 +``` + +Actual: + +```text +Global Variable = 1 +``` + +This is a race condition. Spinlocks prevent this problem. + +--- + +# What is a Spinlock? + +A Spinlock is a: + +```text +Single Holder Lock +``` + +States: + +```text +UNLOCKED +LOCKED +``` + +When a thread attempts to acquire a locked spinlock: + +```text +Mutex: + Sleep + +Spinlock: + Keep Trying (Spin) +``` + +This makes spinlocks extremely fast for very short critical sections. + +--- + +# Mutex vs Spinlock + +| Feature | Mutex | Spinlock | +| ---------------- | ----- | ---------- | +| Waiting Method | Sleep | Busy Wait | +| Context Switch | Yes | No | +| Can Sleep | Yes | No | +| ISR Safe | No | Yes | +| Critical Section | Long | Very Short | +| Ownership | Yes | No | + +Spinlocks are preferred when: + +* Critical section is extremely short +* Sleeping is not allowed +* Interrupt context is involved + +--- + +# How Spinlock Works + +```text +CPU0 + | + +--> spin_lock() + | + +--> Critical Section + | + +--> spin_unlock() + +CPU1 + | + +--> spin_lock() + | + +--> Spins Until CPU0 Releases Lock +``` + +Only one CPU enters the protected section at a time. + +--- + +# When to Use Spinlock? + +Suitable for: + +* Shared kernel variables +* Shared linked lists +* Interrupt handlers +* Tasklets +* SoftIRQs +* Device driver state protection + +Example: + +```text +ISR + | + +--> Shared Buffer + +Kernel Thread + | + +--> Same Shared Buffer +``` + +A spinlock protects access to the shared buffer. + +--- + +# Important Rule + +Never hold a spinlock for a long time. + +Bad: + +```c +spin_lock(&lock); + +msleep(1000); + +spin_unlock(&lock); +``` + +Good: + +```c +spin_lock(&lock); + +shared_var++; + +spin_unlock(&lock); +``` + +Because while one CPU holds the lock, other CPUs waste CPU cycles spinning. + +--- + +# Kernel Configuration Behavior + +### SMP Disabled + +If: + +```text +CONFIG_SMP = n +CONFIG_PREEMPT = n +``` + +Spinlocks are effectively unnecessary because only one execution path can run at a time. + +--- + +### SMP Disabled + PREEMPT Enabled + +If: + +```text +CONFIG_SMP = n +CONFIG_PREEMPT = y +``` + +Spinlocks disable preemption to prevent races. + +--- + +# Header File + +```c +#include +``` + +Required for all spinlock APIs. + +--- + +# Spinlock Declaration + +## Static Initialization + +```c +DEFINE_SPINLOCK(etx_spinlock); +``` + +Expands conceptually to: + +```c +spinlock_t etx_spinlock = + __SPIN_LOCK_UNLOCKED( + etx_spinlock); +``` + +Creates a spinlock in unlocked state. + +--- + +# Dynamic Initialization + +Declare: + +```c +spinlock_t etx_spinlock; +``` + +Initialize: + +```c +spin_lock_init( + &etx_spinlock); +``` + +Used when initialization is required at runtime. + +--- + +# Approach 1: User Context Locking + +Used between: + +```text +Kernel Thread + ↔ +Kernel Thread +``` + +--- + +## spin_lock() + +Acquire lock. + +```c +spin_lock( + &etx_spinlock); +``` + +Behavior: + +```text +Unlocked + | + +--> Acquire + +Locked + | + +--> Spin Forever Until Free +``` + +--- + +## spin_trylock() + +Non-blocking lock attempt. + +```c +if(spin_trylock( + &etx_spinlock)) +{ + /* acquired */ +} +``` + +Returns: + +| Return | Meaning | +| -------- | ------- | +| Non-zero | Success | +| 0 | Failed | + +Does not spin. + +--- + +## spin_unlock() + +Release lock. + +```c +spin_unlock( + &etx_spinlock); +``` + +Allows another waiting CPU to acquire it. + +--- + +## spin_is_locked() + +Check lock state. + +```c +spin_is_locked( + &etx_spinlock); +``` + +Returns: + +| Return | Meaning | +| -------- | -------- | +| Non-zero | Locked | +| 0 | Unlocked | + +--- + +# Approach 2: Bottom Half Locking + +Used between: + +```text +Tasklet + ↔ +Tasklet + +SoftIRQ + ↔ +SoftIRQ +``` + +Standard spinlock APIs are typically sufficient. + +--- + +# Approach 3: User Context + Bottom Half + +Used when data is shared between: + +```text +Kernel Thread + ↔ +Tasklet + +Kernel Thread + ↔ +SoftIRQ +``` + +--- + +## spin_lock_bh() + +```c +spin_lock_bh( + &etx_spinlock); +``` + +What it does: + +```text +Disable SoftIRQ + + +Acquire Spinlock +``` + +The `_bh` suffix means: + +```text +Bottom Half +``` + +Prevents tasklets and softirqs from executing on the current CPU. + +--- + +## spin_unlock_bh() + +```c +spin_unlock_bh( + &etx_spinlock); +``` + +What it does: + +```text +Release Spinlock + + +Enable SoftIRQ +``` + +--- + +# Kernel Thread Example + +## Global Variable + +```c +unsigned long +etx_global_variable = 0; +``` + +Protected using: + +```c +DEFINE_SPINLOCK( + etx_spinlock); +``` + +--- + +## Thread 1 + +```c +int thread_function1( + void *pv) +{ + while( + !kthread_should_stop()) + { + spin_lock( + &etx_spinlock); + + etx_global_variable++; + + printk( + "Thread1 %lu\n", + etx_global_variable); + + spin_unlock( + &etx_spinlock); + } + + return 0; +} +``` + +--- + +## Thread 2 + +```c +int thread_function2( + void *pv) +{ + while( + !kthread_should_stop()) + { + spin_lock( + &etx_spinlock); + + etx_global_variable++; + + printk( + "Thread2 %lu\n", + etx_global_variable); + + spin_unlock( + &etx_spinlock); + } + + return 0; +} +``` + +Both threads safely update the shared variable. + +--- + +# Execution Flow + +```text +Thread1 + | + +--> spin_lock() + | + +--> Critical Section + | + +--> spin_unlock() + +Thread2 + | + +--> Waiting (Spinning) +``` + +After unlock: + +```text +Thread2 Acquires Lock +``` + +--- + +# Sample Output + +```text +Thread1 1 +Thread2 2 +Thread1 3 +Thread2 4 +Thread1 5 +Thread2 6 +``` + +The shared variable remains consistent because only one thread enters the critical section at a time. + +--- + +# Spinlock vs Read-Write Spinlock + +| Feature | Spinlock | Read-Write Spinlock | +| -------------------- | -------------- | ------------------- | +| Readers | One at a Time | Multiple Readers | +| Writers | One | One | +| Complexity | Simple | Higher | +| Read-Heavy Workloads | Less Efficient | Better | + +Read-write spinlocks allow multiple simultaneous readers but only one writer. + +--- + +# Common Driver Use Cases + +## Shared Buffer + +```c +spin_lock(&buffer_lock); + +/* access buffer */ + +spin_unlock(&buffer_lock); +``` + +--- + +## Interrupt Handler + +```c +irq_handler() +{ + spin_lock(&irq_lock); + + /* update shared data */ + + spin_unlock(&irq_lock); +} +``` + +--- + +## Linked List Protection + +```c +spin_lock(&list_lock); + +list_add_tail(...); + +spin_unlock(&list_lock); +``` + +--- + +## Statistics Counters + +```c +spin_lock(&stats_lock); + +packet_count++; + +spin_unlock(&stats_lock); +``` + +--- + +# Common Mistakes + +## Sleeping While Holding Spinlock + +Wrong: + +```c +spin_lock(&lock); + +msleep(100); + +spin_unlock(&lock); +``` + +Spinlocks must not sleep. + +--- + +## Large Critical Sections + +Wrong: + +```c +spin_lock(&lock); + +/* lengthy processing */ + +spin_unlock(&lock); +``` + +Keep critical sections extremely short. + +--- + +## Missing Unlock + +Wrong: + +```c +spin_lock(&lock); + +return; +``` + +Causes permanent lockup. + +Correct: + +```c +spin_lock(&lock); + +/* work */ + +spin_unlock(&lock); +``` + +--- + +# Important APIs Summary + +## Initialization + +```c +DEFINE_SPINLOCK() + +spin_lock_init() +``` + +--- + +## Lock + +```c +spin_lock() + +spin_trylock() +``` + +--- + +## Unlock + +```c +spin_unlock() +``` + +--- + +## Status + +```c +spin_is_locked() +``` + +--- + +## Bottom Half Protection + +```c +spin_lock_bh() + +spin_unlock_bh() +``` + +--- + +# Key Takeaways + +* Spinlock is a lightweight mutual exclusion mechanism. +* Waiting threads do not sleep; they continuously spin. +* Spinlocks are ideal for very short critical sections. +* Spinlocks can be used in interrupt context. +* Spinlocks must never sleep or block. +* `spin_lock()` acquires the lock and waits by spinning. +* `spin_trylock()` attempts acquisition without waiting. +* `spin_lock_bh()` protects shared data between user context and bottom halves. +* Use mutexes for long operations and spinlocks for short, low-latency critical sections. +* Excessive spinlock holding time can significantly hurt SMP system performance. + + +## Conclusion +This is just a basic linux device driver. This will explains spinlock in the linux device driver. + +Please refer this URL for the complete tutorial of this source code. +https://embetronicx.com/tutorials/linux/device-drivers/spinlock-in-linux-kernel-1/ diff --git a/Linux/Device_Driver/Spinlock/Spinlock/ReadMe.md b/Linux/Device_Driver/Spinlock/Spinlock/ReadMe.md deleted file mode 100644 index 2be6be5..0000000 --- a/Linux/Device_Driver/Spinlock/Spinlock/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver. This will explains spinlock in the linux device driver. - -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/spinlock-in-linux-kernel-1/ \ No newline at end of file diff --git a/Linux/Device_Driver/Tasklet/Dynamic_Method/README.md b/Linux/Device_Driver/Tasklet/Dynamic_Method/README.md new file mode 100644 index 0000000..ea606cf --- /dev/null +++ b/Linux/Device_Driver/Tasklet/Dynamic_Method/README.md @@ -0,0 +1,585 @@ +# Linux Device Driver - Tasklet (Dynamic Method) + +## Overview + +This tutorial demonstrates **dynamic creation and initialization of Tasklets** using `tasklet_init()`. + +Unlike the static method: + +```c +DECLARE_TASKLET(tasklet, tasklet_fn, 1); +``` + +the dynamic method allocates memory for the tasklet at runtime and initializes it later. + +--- + +# Static vs Dynamic Tasklet + +## Static Method + +```c +DECLARE_TASKLET( + tasklet, + tasklet_fn, + 1); +``` + +* Compile-time initialization +* Fixed declaration +* Simpler implementation + +--- + +## Dynamic Method + +```c +struct tasklet_struct *tasklet; + +tasklet = kmalloc( + sizeof(struct tasklet_struct), + GFP_KERNEL); + +tasklet_init( + tasklet, + tasklet_fn, + 0); +``` + +* Runtime initialization +* Flexible allocation +* Useful when tasklets are embedded in dynamically allocated structures + +--- + +# Why Dynamic Tasklets? + +Dynamic initialization is useful when: + +* Number of tasklets is unknown at compile time +* Driver allocates resources dynamically +* Tasklets are associated with dynamically created devices +* Tasklet lifetime depends on runtime events + +Example: + +```text +Device Detected + | + v +Allocate Memory + | + v +Create Tasklet + | + v +Use Tasklet + | + v +Free Tasklet +``` + +--- + +# Tasklet Architecture + +```text +Hardware Interrupt + | + v +Interrupt Handler + | + v +tasklet_schedule() + | + v +Tasklet Queue + | + v +Tasklet Function +``` + +The ISR schedules the tasklet and exits quickly. The actual processing happens later. + +--- + +# Required Header Files + +```c +#include +#include +``` + +Needed for: + +* tasklet APIs +* kmalloc() +* kfree() + +--- + +# Dynamic Tasklet Declaration + +Pointer declaration: + +```c +struct tasklet_struct *tasklet; +``` + +Initially: + +```c +tasklet = NULL; +``` + +--- + +# Memory Allocation + +Allocate tasklet structure dynamically: + +```c +tasklet = + kmalloc( + sizeof(struct tasklet_struct), + GFP_KERNEL); +``` + +Validation: + +```c +if(tasklet == NULL) +{ + printk( + KERN_INFO + "Cannot allocate memory"); +} +``` + +--- + +# Dynamic Initialization + +## tasklet_init() + +Initializes the dynamically allocated tasklet. + +Syntax: + +```c +void tasklet_init( + struct tasklet_struct *t, + void (*func)(unsigned long), + unsigned long data); +``` + +Parameters: + +| Parameter | Description | +| --------- | --------------------------- | +| t | Tasklet structure | +| func | Callback function | +| data | Argument passed to callback | + +Example: + +```c +tasklet_init( + tasklet, + tasklet_fn, + 0); +``` + +--- + +# What tasklet_init() Does Internally + +Conceptually: + +```c +tasklet->func = tasklet_fn; + +tasklet->data = 0; + +tasklet->state = TASKLET_STATE_SCHED; + +atomic_set( + &tasklet->count, + 0); +``` + +This: + +* Assigns callback function +* Stores argument +* Initializes tasklet state +* Enables tasklet + +--- + +# Tasklet Callback Function + +Executed when tasklet runs. + +```c +void tasklet_fn( + unsigned long arg) +{ + printk( + KERN_INFO + "Executing Tasklet Function : arg=%ld\n", + arg); +} +``` + +Parameter: + +```c +unsigned long arg +``` + +Receives the value passed through `tasklet_init()`. + +--- + +# Scheduling Tasklet + +## tasklet_schedule() + +Schedules tasklet for execution. + +```c +tasklet_schedule( + tasklet); +``` + +Execution: + +```text +ISR + | + +--> tasklet_schedule() + | + +--> Return + +Later... + +Tasklet Function Executes +``` + +--- + +# Interrupt + Dynamic Tasklet Example + +## Tasklet Function + +```c +void tasklet_fn( + unsigned long arg) +{ + printk( + KERN_INFO, + "Executing Tasklet Function : arg=%ld\n", + arg); +} +``` + +--- + +## Interrupt Handler + +```c +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + printk( + KERN_INFO, + "Shared IRQ: Interrupt Occurred"); + + tasklet_schedule( + tasklet); + + return IRQ_HANDLED; +} +``` + +ISR schedules the tasklet and exits immediately. + +--- + +# Driver Initialization + +## Register IRQ + +```c +request_irq( + IRQ_NO, + irq_handler, + IRQF_SHARED, + "etx_device", + (void *)irq_handler); +``` + +--- + +## Allocate Tasklet + +```c +tasklet = + kmalloc( + sizeof(struct tasklet_struct), + GFP_KERNEL); +``` + +--- + +## Initialize Tasklet + +```c +tasklet_init( + tasklet, + tasklet_fn, + 0); +``` + +Driver is now ready to schedule tasklets. + +--- + +# Execution Flow + +```text +insmod driver.ko + | + v +kmalloc() + | + v +tasklet_init() + | + v +Interrupt Occurs + | + v +ISR + | + v +tasklet_schedule() + | + v +Tasklet Queue + | + v +tasklet_fn() +``` + +--- + +# Driver Cleanup + +Before unloading: + +```c +tasklet_kill( + tasklet); +``` + +Release memory: + +```c +kfree(tasklet); +``` + +Complete cleanup: + +```c +tasklet_kill(tasklet); + +if(tasklet != NULL) +{ + kfree(tasklet); +} +``` + +Always kill the tasklet before freeing memory. + +--- + +# Sample dmesg Output + +After: + +```bash +cat /dev/etx_device +``` + +Output: + +```text +Device File Opened...!!! + +Read function + +Shared IRQ: Interrupt Occurred + +Executing Tasklet Function : arg = 0 + +Device File Closed...!!! +``` + +This confirms: + +1. Interrupt occurred +2. ISR executed +3. Tasklet scheduled +4. Tasklet callback executed + +--- + +# Dynamic vs Static Tasklet + +| Feature | Static Method | Dynamic Method | +| ----------------- | ----------------- | ----------------- | +| Initialization | DECLARE_TASKLET() | tasklet_init() | +| Allocation | Compile Time | Runtime | +| Memory Management | Automatic | kmalloc()/kfree() | +| Flexibility | Low | High | +| Runtime Creation | No | Yes | + +--- + +# Dynamic Tasklet vs Workqueue + +| Feature | Dynamic Tasklet | Workqueue | +| --------------- | --------------- | --------- | +| Context | Atomic | Process | +| Sleep Allowed | No | Yes | +| Mutex Allowed | No | Yes | +| Uses kworker | No | Yes | +| Deferred Work | Yes | Yes | +| Long Processing | No | Yes | + +--- + +# Common Driver Use Cases + +## UART Driver + +```text +RX Interrupt + | + +--> Tasklet + | + +--> Buffer Processing +``` + +--- + +## Network Driver + +```text +Packet Arrival + | + +--> Tasklet + | + +--> Protocol Processing +``` + +--- + +## SPI Driver + +```text +Transfer Complete + | + +--> Deferred Handling +``` + +--- + +## GPIO Driver + +```text +Button Interrupt + | + +--> Event Processing +``` + +--- + +# Best Practices + +* Keep ISR extremely short. +* Schedule tasklet from ISR. +* Never sleep inside tasklet. +* Use spinlocks if synchronization is required. +* Always call `tasklet_kill()` before freeing memory. +* Use workqueues instead when sleeping or blocking is required. + +--- + +# Important APIs Summary + +## Memory Management + +```c +kmalloc() +kfree() +``` + +--- + +## Initialization + +```c +tasklet_init() +``` + +--- + +## Scheduling + +```c +tasklet_schedule() +tasklet_hi_schedule() +tasklet_hi_schedule_first() +``` + +--- + +## Control + +```c +tasklet_enable() +tasklet_disable() +tasklet_disable_nosync() +``` + +--- + +## Cleanup + +```c +tasklet_kill() +tasklet_kill_immediate() +``` + +--- + +# Key Takeaways + +* Dynamic tasklets are created using `tasklet_init()`. +* Memory is allocated using `kmalloc()`. +* ISR schedules tasklets using `tasklet_schedule()`. +* Tasklets execute in atomic context. +* Tasklets cannot sleep or block. +* `tasklet_kill()` must be called before freeing tasklet memory. +* Dynamic tasklets provide greater flexibility than static tasklets. +* Use dynamic tasklets when tasklet lifetime depends on runtime allocation. + +## Conclusion +This is just a basic linux device driver. This will explain tasklet dynamic method in the linux device driver. + +Please refer this URL for the complete tutorial of this source code. +https://embetronicx.com/tutorials/linux/device-drivers/tasklets-dynamic-method/ diff --git a/Linux/Device_Driver/Tasklet/Dynamic_Method/ReadMe.md b/Linux/Device_Driver/Tasklet/Dynamic_Method/ReadMe.md deleted file mode 100644 index 7696bcf..0000000 --- a/Linux/Device_Driver/Tasklet/Dynamic_Method/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver. This will explain tasklet dynamic method in the linux device driver. - -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/tasklets-dynamic-method/ \ No newline at end of file diff --git a/Linux/Device_Driver/Tasklet/Static_Method/README.md b/Linux/Device_Driver/Tasklet/Static_Method/README.md new file mode 100644 index 0000000..8d67d35 --- /dev/null +++ b/Linux/Device_Driver/Tasklet/Static_Method/README.md @@ -0,0 +1,611 @@ +# Linux Device Driver - Tasklet (Static Method) + +## Overview + +A Tasklet is a Linux kernel **Bottom Half** mechanism used to defer interrupt-related work. + +When an interrupt occurs: + +```text +Hardware Interrupt + | + v +Interrupt Handler (Top Half) + | + v +Tasklet (Bottom Half) + | + v +Deferred Processing +``` + +Tasklets execute later with interrupts enabled and are designed for short deferred operations. + +--- + +# Why Tasklets? + +Interrupt handlers should execute as quickly as possible. + +Bad ISR: + +```text +Interrupt + | + +--> Long Processing + +--> Computation + +--> Buffer Handling +``` + +Good ISR: + +```text +Interrupt + | + +--> Acknowledge Device + +--> Schedule Tasklet + +--> Return +``` + +The tasklet performs the deferred processing later. + +--- + +# Linux Bottom Half Mechanisms + +Linux provides four common Bottom Half mechanisms: + +| Mechanism | Context | Sleep Allowed | +| ------------ | --------------- | ------------- | +| Workqueue | Process Context | Yes | +| Threaded IRQ | Process Context | Yes | +| SoftIRQ | Atomic Context | No | +| Tasklet | Atomic Context | No | + +Tasklets are implemented on top of SoftIRQs. + +--- + +# Important Characteristics + +## Atomic Context + +Tasklets run in atomic context. + +Not Allowed: + +```c +msleep(); +schedule(); +mutex_lock(); +down(); +wait_event(); +``` + +Allowed: + +```c +spin_lock(); +spin_unlock(); +``` + +Tasklets cannot block or sleep. + +--- + +## CPU Affinity + +A tasklet runs only on the CPU that scheduled it. + +```text +CPU0 + | + +--> tasklet_schedule() + | + +--> Tasklet executes on CPU0 +``` + +This improves cache locality. + +--- + +## No Self-Concurrency + +The same tasklet cannot run simultaneously on multiple CPUs. + +```text +Tasklet X + +CPU0 --> Running + +CPU1 --> Cannot Run Same Tasklet +``` + +However: + +```text +Tasklet A --> CPU0 + +Tasklet B --> CPU1 +``` + +Different tasklets may execute in parallel. + +--- + +# Tasklet Structure + +Header: + +```c +#include +``` + +Kernel structure: + +```c +struct tasklet_struct +{ + struct tasklet_struct *next; + unsigned long state; + atomic_t count; + void (*func)(unsigned long); + unsigned long data; +}; +``` + +Field Description: + +| Field | Purpose | +| ----- | --------------------------- | +| next | Next tasklet | +| state | Scheduled / Running | +| count | Disable count | +| func | Callback function | +| data | Argument passed to callback | + +--- + +# Creating Tasklet (Static Method) + +## DECLARE_TASKLET() + +Creates and initializes a tasklet. + +Syntax: + +```c +DECLARE_TASKLET( + name, + function, + data); +``` + +Example: + +```c +void tasklet_fn(unsigned long); + +DECLARE_TASKLET( + tasklet, + tasklet_fn, + 1); +``` + +Equivalent Concept: + +```c +struct tasklet_struct tasklet = +{ + NULL, + 0, + 0, + tasklet_fn, + 1 +}; +``` + +The tasklet is initially enabled. + +--- + +# Creating Disabled Tasklet + +## DECLARE_TASKLET_DISABLED() + +Creates tasklet in disabled state. + +```c +DECLARE_TASKLET_DISABLED( + tasklet, + tasklet_fn, + 1); +``` + +Enable later: + +```c +tasklet_enable(&tasklet); +``` + +--- + +# Tasklet Callback Function + +The deferred work executes here. + +```c +void tasklet_fn( + unsigned long arg) +{ + printk( + KERN_INFO, + "Executing Tasklet : %ld\n", + arg); +} +``` + +Parameter: + +```c +unsigned long arg +``` + +Receives the value specified during creation. + +--- + +# Enable Tasklet + +```c +tasklet_enable( + &tasklet); +``` + +Enables execution. + +--- + +# Disable Tasklet + +## tasklet_disable() + +Waits for completion. + +```c +tasklet_disable( + &tasklet); +``` + +--- + +## tasklet_disable_nosync() + +Disables immediately. + +```c +tasklet_disable_nosync( + &tasklet); +``` + +Does not wait for currently running execution. + +--- + +# Scheduling Tasklets + +## tasklet_schedule() + +Schedules tasklet with normal priority. + +```c +tasklet_schedule( + &tasklet); +``` + +Example: + +```c +tasklet_schedule( + &tasklet); +``` + +If already pending: + +```text +Request Ignored +``` + +The tasklet is not queued twice. + +--- + +# High Priority Tasklet + +## tasklet_hi_schedule() + +```c +tasklet_hi_schedule( + &tasklet); +``` + +Schedules tasklet with high priority. + +--- + +## tasklet_hi_schedule_first() + +```c +tasklet_hi_schedule_first( + &tasklet); +``` + +Special-purpose API rarely used in drivers. + +--- + +# Tasklet Queues + +Each CPU maintains: + +```text +CPU0 + | + +--> Normal Queue + | + +--> High Priority Queue + +CPU1 + | + +--> Normal Queue + | + +--> High Priority Queue +``` + +Tasklets are inserted into one of these queues. + +--- + +# Interrupt + Tasklet Example + +## Tasklet Declaration + +```c +void tasklet_fn(unsigned long); + +DECLARE_TASKLET( + tasklet, + tasklet_fn, + 1); +``` + +--- + +## ISR + +```c +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + printk( + KERN_INFO, + "Interrupt Occurred\n"); + + tasklet_schedule( + &tasklet); + + return IRQ_HANDLED; +} +``` + +ISR only schedules the tasklet. + +--- + +## Tasklet Function + +```c +void tasklet_fn( + unsigned long arg) +{ + printk( + KERN_INFO, + "Executing Tasklet Function : arg=%ld\n", + arg); +} +``` + +--- + +# Execution Flow + +```text +Interrupt + | + v +ISR + | + v +tasklet_schedule() + | + v +Tasklet Queue + | + v +Tasklet Function +``` + +--- + +# Killing Tasklet + +## tasklet_kill() + +Waits for completion and removes tasklet. + +```c +tasklet_kill( + &tasklet); +``` + +Driver cleanup: + +```c +static void __exit driver_exit(void) +{ + tasklet_kill(&tasklet); + + free_irq( + IRQ_NO, + dev_id); +} +``` + +Always call before unloading the module. + +--- + +# Sample dmesg Output + +```text +Device Driver Insert...Done!!! + +Read function + +Shared IRQ: Interrupt Occurred + +Executing Tasklet Function : arg = 1 + +Device Driver Remove...Done!!! +``` + +This verifies: + +1. Interrupt triggered +2. ISR executed +3. Tasklet scheduled +4. Tasklet callback executed + +--- + +# Tasklet vs Workqueue + +| Feature | Tasklet | Workqueue | +| ---------------- | ------- | --------- | +| Context | Atomic | Process | +| Sleep Allowed | No | Yes | +| Mutex Allowed | No | Yes | +| Uses kworker | No | Yes | +| Long Processing | No | Yes | +| Spinlock Allowed | Yes | Yes | + +--- + +# Common Driver Use Cases + +## UART Driver + +```text +RX Interrupt + | + +--> Tasklet + | + +--> Buffer Processing +``` + +--- + +## Network Driver + +```text +Packet Arrival + | + +--> Tasklet + | + +--> Packet Handling +``` + +--- + +## GPIO Driver + +```text +Button Interrupt + | + +--> Event Processing +``` + +--- + +## SPI Driver + +```text +Transfer Complete + | + +--> Deferred Processing +``` + +--- + +# Best Practices + +* Keep ISR extremely short. +* Use tasklets only for short deferred work. +* Never sleep inside tasklets. +* Use spinlocks instead of mutexes. +* Kill tasklets before unloading driver. +* Use workqueues when sleeping is required. + +--- + +# Important APIs Summary + +## Creation + +```c +DECLARE_TASKLET() +DECLARE_TASKLET_DISABLED() +``` + +## Control + +```c +tasklet_enable() +tasklet_disable() +tasklet_disable_nosync() +``` + +## Scheduling + +```c +tasklet_schedule() +tasklet_hi_schedule() +tasklet_hi_schedule_first() +``` + +## Removal + +```c +tasklet_kill() +tasklet_kill_immediate() +``` + +--- + +# Key Takeaways + +* Tasklets are Bottom Half mechanisms built on SoftIRQs. +* Tasklets execute in atomic context. +* Tasklets cannot sleep or block. +* Same tasklet never runs concurrently on multiple CPUs. +* ISR commonly schedules tasklets for deferred work. +* Tasklets are lighter than workqueues but more restricted. +* Use workqueues whenever deferred work requires sleeping or blocking operations. + + +## Conclusion +This is just a basic linux device driver. This will explain taklet static method in the linux device driver. + +Please refer this URL for the complete tutorial of this source code. +https://embetronicx.com/tutorials/linux/device-drivers/tasklet-static-method/ diff --git a/Linux/Device_Driver/Tasklet/Static_Method/ReadMe.md b/Linux/Device_Driver/Tasklet/Static_Method/ReadMe.md deleted file mode 100644 index 72eb0dc..0000000 --- a/Linux/Device_Driver/Tasklet/Static_Method/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver. This will explain taklet static method in the linux device driver. - -Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/tasklet-static-method/ \ No newline at end of file diff --git a/Linux/Device_Driver/Waitqueue-Tutorial/Dynamic_Method/README.md b/Linux/Device_Driver/Waitqueue-Tutorial/Dynamic_Method/README.md new file mode 100644 index 0000000..f4b1bf3 --- /dev/null +++ b/Linux/Device_Driver/Waitqueue-Tutorial/Dynamic_Method/README.md @@ -0,0 +1,483 @@ +# Linux Device Driver - Workqueue (Dynamic Creation Method) + +## Overview + +This tutorial demonstrates **dynamic initialization of a Workqueue work item** using `INIT_WORK()`. + +Unlike the static method: + +```c +DECLARE_WORK(work, work_fn); +``` + +the dynamic method creates a `work_struct` object first and initializes it later using `INIT_WORK()`. + +--- + +# Why Dynamic Workqueue? + +Dynamic initialization is useful when: + +* Work item is part of a structure +* Work item needs runtime initialization +* Driver requires flexible work creation +* Work object cannot be statically declared + +Example: + +```c +struct work_struct workqueue; + +INIT_WORK(&workqueue, workqueue_fn); +``` + +--- + +# Workqueue Architecture + +```text +Hardware Interrupt + | + v +Interrupt Handler (ISR) + | + v +schedule_work() + | + v +Kernel Global Workqueue + | + v +kworker Thread + | + v +workqueue_fn() +``` + +The ISR schedules work and returns immediately. + +The actual processing happens later inside a kernel worker thread. + +--- + +# Header File + +```c +#include +``` + +Required for: + +* work_struct +* INIT_WORK() +* schedule_work() + +--- + +# Workqueue Structure + +```c +struct work_struct workqueue; +``` + +Represents a deferred work item. + +Example: + +```c +static struct work_struct workqueue; +``` + +--- + +# Dynamic Work Initialization + +## INIT_WORK() + +Used to initialize a work item. + +Syntax: + +```c +INIT_WORK(work, function); +``` + +Parameters: + +| Parameter | Description | +| --------- | ------------------ | +| work | work_struct object | +| function | Callback function | + +Example: + +```c +INIT_WORK( + &workqueue, + workqueue_fn); +``` + +--- + +# Workqueue Callback Function + +The callback executes inside a kernel worker thread. + +```c +void workqueue_fn( + struct work_struct *work) +{ + printk(KERN_INFO + "Executing Workqueue Function\n"); +} +``` + +This function performs the deferred processing. + +--- + +# Scheduling Work + +## schedule_work() + +Queues work into the kernel global workqueue. + +Syntax: + +```c +int schedule_work( + struct work_struct *work); +``` + +Example: + +```c +schedule_work(&workqueue); +``` + +Return Value: + +| Value | Meaning | +| -------- | ------------------- | +| 0 | Already queued | +| Non-zero | Successfully queued | + +--- + +# Delayed Scheduling + +## schedule_delayed_work() + +Execute after a delay. + +Syntax: + +```c +schedule_delayed_work( + &dwork, + delay); +``` + +Example: + +```c +schedule_delayed_work( + &dwork, + msecs_to_jiffies(5000)); +``` + +Executes after 5 seconds. + +--- + +# CPU Specific Scheduling + +## schedule_work_on() + +Queues work on a specific CPU. + +Syntax: + +```c +schedule_work_on( + cpu, + &work); +``` + +Example: + +```c +schedule_work_on( + 0, + &workqueue); +``` + +Runs on CPU0. + +--- + +# Interrupt + Workqueue Example + +## Workqueue Function + +```c +void workqueue_fn( + struct work_struct *work) +{ + printk( + KERN_INFO + "Executing Workqueue Function\n"); +} +``` + +--- + +## Interrupt Handler + +```c +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + printk( + KERN_INFO + "Shared IRQ: Interrupt Occurred\n"); + + schedule_work( + &workqueue); + + return IRQ_HANDLED; +} +``` + +ISR schedules work and exits immediately. + +--- + +# Driver Initialization + +## Register Interrupt + +```c +request_irq( + IRQ_NO, + irq_handler, + IRQF_SHARED, + "etx_device", + (void *)irq_handler); +``` + +--- + +## Create Work Dynamically + +```c +INIT_WORK( + &workqueue, + workqueue_fn); +``` + +The work item is now ready for scheduling. + +--- + +# Driver Execution Flow + +```text +Driver Loaded + | + v +INIT_WORK() + | + v +Interrupt Occurs + | + v +IRQ Handler + | + v +schedule_work() + | + v +kworker Thread + | + v +workqueue_fn() +``` + +--- + +# Sample dmesg Output + +After: + +```bash +cat /dev/etx_device +``` + +Output: + +```text +Device File Opened...!!! + +Read function + +Shared IRQ: Interrupt Occurred + +Executing Workqueue Function + +Device File Closed...!!! +``` + +This confirms: + +1. Interrupt occurred +2. ISR executed +3. Work scheduled +4. Workqueue function executed + +--- + +# Build Driver + +## Makefile + +```make +obj-m += driver.o + +KDIR = /lib/modules/$(shell uname -r)/build + +all: + make -C $(KDIR) \ + M=$(shell pwd) modules + +clean: + make -C $(KDIR) \ + M=$(shell pwd) clean +``` + +--- + +# Build and Test + +Compile: + +```bash +make +``` + +Load: + +```bash +sudo insmod driver.ko +``` + +Trigger interrupt: + +```bash +sudo cat /dev/etx_device +``` + +View logs: + +```bash +dmesg +``` + +Unload: + +```bash +sudo rmmod driver +``` + +--- + +# Dynamic vs Static Workqueue Initialization + +| Static Method | Dynamic Method | +| --------------------------- | ----------------------------- | +| DECLARE_WORK() | INIT_WORK() | +| Compile-time initialization | Runtime initialization | +| Simpler | More flexible | +| Fixed declaration | Can be embedded in structures | +| Less configurable | More configurable | + +--- + +# Dynamic Workqueue vs Own Workqueue + +| Dynamic Method | Own Workqueue | +| --------------------- | ------------------------- | +| Uses global workqueue | Uses dedicated workqueue | +| Uses schedule_work() | Uses queue_work() | +| No worker creation | Must create worker thread | +| Simpler | Greater control | +| Shared kworker thread | Dedicated worker thread | + +--- + +# Advantages + +* Runtime initialization +* Flexible design +* Easy ISR integration +* No custom worker thread needed +* Uses kernel-managed kworker threads + +--- + +# Limitations + +* Uses shared global workqueue +* Heavy tasks may affect other queued work +* Less control than custom workqueues + +--- + +# Important APIs Summary + +## Initialization + +```c +INIT_WORK() +``` + +--- + +## Scheduling + +```c +schedule_work() +schedule_delayed_work() +schedule_work_on() +schedule_delayed_work_on() +``` + +--- + +## Work Structure + +```c +struct work_struct +``` + +--- + +## Interrupt Related + +```c +request_irq() +free_irq() +``` + +--- + +# Key Takeaways + +* `INIT_WORK()` dynamically initializes a work item. +* `schedule_work()` places work on the kernel global workqueue. +* Work executes in a `kworker` thread, not in interrupt context. +* Workqueue functions can perform longer operations than ISRs. +* The ISR should only schedule work and return quickly. +* Dynamic initialization provides more flexibility than `DECLARE_WORK()`. +* This method still uses the kernel global workqueue; it does not create a dedicated workqueue. diff --git a/Linux/Device_Driver/Waitqueue-Tutorial/ReadMe.md b/Linux/Device_Driver/Waitqueue-Tutorial/ReadMe.md index f708acc..aedb297 100644 --- a/Linux/Device_Driver/Waitqueue-Tutorial/ReadMe.md +++ b/Linux/Device_Driver/Waitqueue-Tutorial/ReadMe.md @@ -1,4 +1,462 @@ +# Linux Device Driver - Wait Queue + +## Overview + +A Wait Queue is a Linux kernel synchronization mechanism used when a process or kernel thread needs to sleep until a specific event occurs. + +Instead of continuously polling for an event, the task can: + +1. Sleep (block) +2. Release CPU resources +3. Wake up when the event occurs + +This improves CPU utilization and system efficiency. + +--- + +## Why Wait Queue? + +In Linux device drivers, a process may need to wait for: + +- Data arrival +- Hardware interrupt +- Completion of an operation +- Event notification from another thread + +Wait Queues provide a safe mechanism to: + +- Put tasks into sleep state +- Wake them when the required condition becomes true + +--- + +## Wait Queue Workflow + +```text +Initialize Wait Queue + | + v +Put Task To Sleep +(wait_event...) + | + v +Event Occurs + | + v +Wake Up Task +(wake_up...) + | + v +Task Continues Execution +``` + +--- + +## Header File + +```c +#include +``` + +--- + +# Step 1: Initialize Wait Queue + +There are two methods. + +## Static Initialization + +```c +DECLARE_WAIT_QUEUE_HEAD(wq); +``` + +### Example + +```c +DECLARE_WAIT_QUEUE_HEAD(my_waitqueue); +``` + +--- + +## Dynamic Initialization + +```c +wait_queue_head_t wq; + +init_waitqueue_head(&wq); +``` + +### Example + +```c +wait_queue_head_t my_waitqueue; + +init_waitqueue_head(&my_waitqueue); +``` + +--- + +# Step 2: Put Task To Sleep + +Linux provides several macros. + +--- + +## wait_event() + +Sleep until condition becomes true. + +```c +wait_event(wq, condition); +``` + +### State + +```text +TASK_UNINTERRUPTIBLE +``` + +### Example + +```c +wait_event(my_waitqueue, data_ready); +``` + +--- + +## wait_event_timeout() + +Sleep until: + +- Condition becomes true +OR +- Timeout expires + +```c +wait_event_timeout(wq, condition, timeout); +``` + +### Example + +```c +wait_event_timeout(my_waitqueue, + data_ready, + msecs_to_jiffies(5000)); +``` + +### Returns + +| Return Value | Meaning | +|-------------|----------| +| 0 | Timeout occurred | +| >0 | Remaining jiffies | +| 1 | Condition became true after timeout | + +--- + +## wait_event_cmd() + +Execute commands before and after sleep. + +```c +wait_event_cmd(wq, + condition, + cmd1, + cmd2); +``` + +### Example + +```c +wait_event_cmd(my_waitqueue, + data_ready, + printk("Before Sleep\n"), + printk("After Wakeup\n")); +``` + +--- + +## wait_event_interruptible() + +Sleep until: + +- Condition becomes true +OR +- Signal arrives + +```c +wait_event_interruptible(wq, condition); +``` + +### State + +```text +TASK_INTERRUPTIBLE +``` + +### Returns + +| Return Value | Meaning | +|-------------|----------| +| 0 | Condition became true | +| -ERESTARTSYS | Interrupted by signal | + +--- + +## wait_event_interruptible_timeout() + +Combination of: + +- Interruptible sleep +- Timeout + +```c +wait_event_interruptible_timeout( + wq, + condition, + timeout); +``` + +### Returns + +| Return Value | Meaning | +|-------------|----------| +| 0 | Timeout | +| >0 | Remaining jiffies | +| -ERESTARTSYS | Interrupted by signal | + +--- + +## wait_event_killable() + +Sleep until: + +- Condition becomes true +OR +- Fatal signal received + +```c +wait_event_killable(wq, condition); +``` + +### State + +```text +TASK_KILLABLE +``` + +--- + +# Step 3: Wake Up Sleeping Tasks + +When the event occurs, sleeping tasks must be awakened. + +--- + +## wake_up() + +Wake one task sleeping in uninterruptible state. + +```c +wake_up(&wq); +``` + +### Example + +```c +wake_up(&my_waitqueue); +``` + +--- + +## wake_up_all() + +Wake all tasks in the wait queue. + +```c +wake_up_all(&wq); +``` + +--- + +## wake_up_interruptible() + +Wake one task sleeping in interruptible state. + +```c +wake_up_interruptible(&wq); +``` + +--- + +## wake_up_sync() + +Wake task without immediately rescheduling CPU. + +```c +wake_up_sync(&wq); +``` + +--- + +## wake_up_interruptible_sync() + +Interruptible version of wake_up_sync(). + +```c +wake_up_interruptible_sync(&wq); +``` + +--- + +# Common Driver Usage + +## Waiting Thread + +```c +while (1) +{ + wait_event_interruptible( + my_waitqueue, + event_flag != 0); + + if(event_flag == EXIT_EVENT) + break; + + printk("Event Received\n"); + + event_flag = 0; +} +``` + +--- + +## Event Producer + +```c +event_flag = 1; + +wake_up_interruptible(&my_waitqueue); +``` + +--- + +# Driver Flow Example + +```text +Kernel Thread Started + | + v +Waiting For Event + | + v +User Reads Device + | + v +Driver Sets Flag + | + v +wake_up_interruptible() + | + v +Thread Wakes Up + | + v +Processes Event + | + v +Wait Again +``` + +--- + +# Important Sleep States + +| State | Description | +|---------|-------------| +| TASK_RUNNING | Executing | +| TASK_INTERRUPTIBLE | Can be interrupted by signals | +| TASK_UNINTERRUPTIBLE | Cannot be interrupted | +| TASK_KILLABLE | Interrupted only by fatal signals | + +--- + +# Advantages + +- Efficient CPU usage +- No busy waiting +- Event-driven synchronization +- Commonly used in device drivers +- Works well with kernel threads + +--- + +# Typical Driver Use Cases + +- Blocking read() +- Interrupt handling +- Data arrival notification +- Producer-consumer synchronization +- Driver event notification +- Hardware completion events + +--- + +# Wait Queue vs Busy Waiting + +| Wait Queue | Busy Waiting | +|------------|-------------| +| Sleeps task | Continuously polls | +| CPU efficient | CPU wastage | +| Event driven | Polling driven | +| Preferred in kernel | Generally avoided | + +--- + +# Important APIs Summary + +## Initialization + +```c +DECLARE_WAIT_QUEUE_HEAD(wq); + +wait_queue_head_t wq; +init_waitqueue_head(&wq); +``` + +## Sleep + +```c +wait_event() +wait_event_timeout() +wait_event_cmd() +wait_event_interruptible() +wait_event_interruptible_timeout() +wait_event_killable() +``` + +## Wake Up + +```c +wake_up() +wake_up_all() +wake_up_interruptible() +wake_up_sync() +wake_up_interruptible_sync() +``` + +--- + +# Key Takeaways + +- Wait Queue is a kernel mechanism for sleeping and waking tasks. +- A task sleeps until a condition becomes true. +- Sleeping tasks consume no CPU. +- Another task, ISR, or driver function wakes the waiting task. +- Frequently used in Linux device drivers for blocking operations and event synchronization. + +## Conclusion This is just a basic linux device driver. This will explain waitqueue in the linux device driver. Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/waitqueue-in-linux-device-driver-tutorial/ \ No newline at end of file +https://embetronicx.com/tutorials/linux/device-drivers/waitqueue-in-linux-device-driver-tutorial/ diff --git a/Linux/Device_Driver/Waitqueue-Tutorial/Static_Method/README.md b/Linux/Device_Driver/Waitqueue-Tutorial/Static_Method/README.md new file mode 100644 index 0000000..0208905 --- /dev/null +++ b/Linux/Device_Driver/Waitqueue-Tutorial/Static_Method/README.md @@ -0,0 +1,633 @@ +# Linux Device Driver - Tasklet (Static Method) + +## Overview + +A Tasklet is a Linux kernel **Bottom Half** mechanism used to defer work from an Interrupt Service Routine (ISR). + +When an interrupt occurs: + +* ISR (Top Half) executes immediately. +* Time-consuming work is deferred to a Tasklet (Bottom Half). +* Tasklets run later with interrupts enabled. + +Tasklets execute in **atomic context**, not process context. Therefore they cannot sleep or block. + +--- + +# Why Tasklets? + +Interrupt handlers should: + +* Execute quickly +* Avoid lengthy processing +* Return control to the kernel as soon as possible + +Tasklets allow deferred execution of non-critical interrupt-related work. + +--- + +# Top Half vs Bottom Half + +```text +Interrupt Occurs + | + v ++----------------+ +| ISR (Top Half) | ++----------------+ + | + v +Schedule Tasklet + | + v ++-------------------+ +| Tasklet | +| (Bottom Half) | ++-------------------+ + | + v +Deferred Processing +``` + +--- + +# Linux Bottom Half Mechanisms + +Linux provides multiple Bottom Half mechanisms: + +| Mechanism | Context | Sleep Allowed | +| ------------ | --------------- | ------------- | +| Workqueue | Process Context | Yes | +| Threaded IRQ | Process Context | Yes | +| SoftIRQ | Atomic Context | No | +| Tasklet | Atomic Context | No | + +Tasklets are built on top of SoftIRQs. + +--- + +# Important Characteristics + +## Tasklets are Atomic + +Not allowed: + +```c +sleep(); +msleep(); +mutex_lock(); +down(); +wait_event(); +``` + +Allowed: + +```c +spin_lock(); +spin_unlock(); +``` + +--- + +## CPU Affinity + +A tasklet executes only on the CPU that scheduled it. + +```text +CPU0 Schedules Tasklet + | + v +Tasklet Runs On CPU0 +``` + +--- + +## No Self-Concurrency + +A tasklet cannot execute simultaneously on multiple CPUs. + +```text +Same Tasklet + | + +--> CPU0 (Running) + +CPU1 Cannot Run Same Tasklet +Until CPU0 Completes +``` + +--- + +## Different Tasklets Can Run Concurrently + +```text +Tasklet A --> CPU0 + +Tasklet B --> CPU1 +``` + +Possible and supported. + +--- + +# Tasklet Structure + +Defined in: + +```c +#include +``` + +Structure: + +```c +struct tasklet_struct +{ + struct tasklet_struct *next; + unsigned long state; + atomic_t count; + void (*func)(unsigned long); + unsigned long data; +}; +``` + +Field Description: + +| Field | Purpose | +| ----- | ----------------------- | +| next | Next tasklet | +| state | Running/Scheduled state | +| count | Disable counter | +| func | Callback function | +| data | Data passed to callback | + +--- + +# Static Tasklet Creation + +## DECLARE_TASKLET() + +Creates and initializes a tasklet. + +Syntax: + +```c +DECLARE_TASKLET( + name, + function, + data); +``` + +Parameters: + +| Parameter | Description | +| --------- | --------------------------- | +| name | Tasklet object | +| function | Callback function | +| data | Argument passed to callback | + +Example: + +```c +void tasklet_fn(unsigned long); + +DECLARE_TASKLET( + tasklet, + tasklet_fn, + 1); +``` + +This tasklet is created in the enabled state. + +--- + +# Disabled Tasklet Creation + +## DECLARE_TASKLET_DISABLED() + +Creates a disabled tasklet. + +```c +DECLARE_TASKLET_DISABLED( + tasklet, + tasklet_fn, + 1); +``` + +Tasklet will not run until enabled. + +```c +tasklet_enable(&tasklet); +``` + +--- + +# Tasklet Callback Function + +This function performs deferred processing. + +```c +void tasklet_fn( + unsigned long arg) +{ + printk( + "Executing Tasklet : %ld\n", + arg); +} +``` + +Parameter: + +```c +unsigned long arg +``` + +Receives the value passed during creation. + +--- + +# Enable Tasklet + +```c +tasklet_enable( + &tasklet); +``` + +Enables execution of the tasklet. + +--- + +# Disable Tasklet + +## tasklet_disable() + +Waits for running tasklet to finish. + +```c +tasklet_disable( + &tasklet); +``` + +--- + +## tasklet_disable_nosync() + +Disables immediately. + +```c +tasklet_disable_nosync( + &tasklet); +``` + +Does not wait for completion. + +--- + +# Schedule Tasklet + +## tasklet_schedule() + +Schedules tasklet with normal priority. + +```c +tasklet_schedule( + &tasklet); +``` + +Example: + +```c +tasklet_schedule( + &tasklet); +``` + +If already scheduled but not executed: + +```text +New schedule request ignored +``` + +--- + +# High Priority Scheduling + +## tasklet_hi_schedule() + +Schedules tasklet with high priority. + +```c +tasklet_hi_schedule( + &tasklet); +``` + +--- + +## tasklet_hi_schedule_first() + +Special version used in specific kernel scenarios. + +```c +tasklet_hi_schedule_first( + &tasklet); +``` + +--- + +# Tasklet Priorities + +Linux supports: + +```text +Normal Priority +High Priority +``` + +Each CPU maintains its own tasklet queues. + +```text +CPU0 + | + +--> Normal Queue + | + +--> High Priority Queue + +CPU1 + | + +--> Normal Queue + | + +--> High Priority Queue +``` + +--- + +# Kill Tasklet + +## tasklet_kill() + +Waits for completion and removes tasklet. + +```c +tasklet_kill( + &tasklet); +``` + +Always call during module removal. + +Example: + +```c +tasklet_kill( + &tasklet); +``` + +--- + +## tasklet_kill_immediate() + +Used when CPU is dead/offline. + +```c +tasklet_kill_immediate( + &tasklet, + cpu); +``` + +--- + +# Interrupt + Tasklet Example + +## Tasklet Definition + +```c +void tasklet_fn( + unsigned long arg); + +DECLARE_TASKLET( + tasklet, + tasklet_fn, + 1); +``` + +--- + +## Tasklet Function + +```c +void tasklet_fn( + unsigned long arg) +{ + printk( + "Executing Tasklet Function : arg=%ld\n", + arg); +} +``` + +--- + +## Interrupt Handler + +```c +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + printk( + "Interrupt Occurred\n"); + + tasklet_schedule( + &tasklet); + + return IRQ_HANDLED; +} +``` + +ISR schedules the tasklet instead of performing lengthy processing. + +--- + +# Execution Flow + +```text +User Reads Device + | + v +Software Interrupt + | + v +IRQ Handler + | + v +tasklet_schedule() + | + v +Tasklet Queue + | + v +Tasklet Function Executes +``` + +--- + +# Driver Cleanup + +During module unload: + +```c +tasklet_kill( + &tasklet); + +free_irq( + IRQ_NO, + dev_id); +``` + +Tasklet must be killed before driver removal. + +--- + +# Sample dmesg Output + +```text +Major = 246 Minor = 0 + +Device Driver Insert...Done!!! + +Device File Opened...!!! + +Read function + +Shared IRQ: Interrupt Occurred + +Executing Tasklet Function : arg = 1 + +Device File Closed...!!! + +Device Driver Remove...Done!!! +``` + +This confirms: + +1. Interrupt occurred +2. ISR executed +3. Tasklet scheduled +4. Tasklet function executed + +--- + +# Tasklet vs Workqueue + +| Feature | Tasklet | Workqueue | +| ----------------- | ------- | --------- | +| Context | Atomic | Process | +| Sleep Allowed | No | Yes | +| Uses kworker | No | Yes | +| Can Block | No | Yes | +| ISR Deferred Work | Yes | Yes | +| Mutex Allowed | No | Yes | +| Spinlock Allowed | Yes | Yes | + +--- + +# Common Use Cases + +## Network Driver + +```text +Packet Arrival + | + +--> Tasklet Processing +``` + +--- + +## UART Driver + +```text +RX Interrupt + | + +--> Buffer Processing +``` + +--- + +## SPI Driver + +```text +Transfer Complete + | + +--> Deferred Processing +``` + +--- + +## GPIO Driver + +```text +Button Interrupt + | + +--> Event Handling +``` + +--- + +# Best Practices + +* Keep ISR extremely short. +* Schedule tasklets for deferred work. +* Never sleep inside tasklet. +* Use spinlocks instead of mutexes. +* Kill tasklets during driver cleanup. +* Use workqueues if sleeping is required. + +--- + +# Important APIs Summary + +## Creation + +```c +DECLARE_TASKLET() +DECLARE_TASKLET_DISABLED() +``` + +--- + +## Control + +```c +tasklet_enable() +tasklet_disable() +tasklet_disable_nosync() +``` + +--- + +## Scheduling + +```c +tasklet_schedule() +tasklet_hi_schedule() +tasklet_hi_schedule_first() +``` + +--- + +## Removal + +```c +tasklet_kill() +tasklet_kill_immediate() +``` + +--- + +# Key Takeaways + +* Tasklets are Linux Bottom Half mechanisms. +* Tasklets execute in atomic context. +* Tasklets cannot sleep or block. +* Same tasklet cannot run concurrently on multiple CPUs. +* Different tasklets can execute in parallel. +* ISR commonly schedules a tasklet for deferred processing. +* Tasklets are lighter than workqueues but more restricted. +* Use workqueues when deferred work requires sleeping. + diff --git a/Linux/Device_Driver/Workqueue-in-Linux-kernel/Own_Workqueue/ReadMe.md b/Linux/Device_Driver/Workqueue-in-Linux-kernel/Own_Workqueue/ReadMe.md index fa8ef0b..fa397c7 100644 --- a/Linux/Device_Driver/Workqueue-in-Linux-kernel/Own_Workqueue/ReadMe.md +++ b/Linux/Device_Driver/Workqueue-in-Linux-kernel/Own_Workqueue/ReadMe.md @@ -1,4 +1,657 @@ +# Linux Device Driver - Own Workqueue (Dedicated Workqueue) + +## Overview + +In previous Workqueue examples, work items were queued to the **kernel global workqueue** using: + +```c +schedule_work(&work); +``` + +In this tutorial, we create a **dedicated workqueue** for the driver and queue work to it using: + +```c +queue_work(own_workqueue, &work); +``` + +This provides better isolation and control over deferred processing. + +--- + +# Global Workqueue vs Own Workqueue + +## Global Workqueue + +```text +Driver + | + v +schedule_work() + | + v +Kernel Global Workqueue + | + v +kworker Thread +``` + +Characteristics: + +* Shared by many kernel subsystems +* No creation required +* Easy to use +* Less control + +--- + +## Own Workqueue + +```text +Driver + | + v +queue_work() + | + v +Own Workqueue + | + v +Dedicated Worker Thread +``` + +Characteristics: + +* Driver-specific workqueue +* Better isolation +* More control +* Suitable for heavy workloads + +--- + +# Workqueue Components + +## Workqueue Structure + +Represents the workqueue itself. + +```c +struct workqueue_struct *own_workqueue; +``` + +--- + +## Work Structure + +Represents a deferred work item. + +```c +struct work_struct work; +``` + +Usually initialized as: + +```c +DECLARE_WORK(work, workqueue_fn); +``` + +--- + +# Header File + +```c +#include +``` + +Required for: + +* work_struct +* workqueue_struct +* queue_work() +* create_workqueue() + +--- + +# Creating a Workqueue + +## create_workqueue() + +Creates a dedicated workqueue. + +```c +struct workqueue_struct * +create_workqueue(const char *name); +``` + +Example: + +```c +own_workqueue = + create_workqueue("own_wq"); +``` + +Creates: + +```text +own_wq +``` + +workqueue for this driver. + +--- + +# Single Thread Workqueue + +## create_singlethread_workqueue() + +Creates a workqueue with only one worker thread. + +```c +own_workqueue = +create_singlethread_workqueue( + "own_wq"); +``` + +Useful when: + +* Work items must execute sequentially +* Parallel execution is not desired + +--- + +# Internal Implementation + +Both macros use: + +```c +alloc_workqueue() +``` + +internally. + +```c +create_workqueue() + | + v +alloc_workqueue() +``` + +--- + +# alloc_workqueue() + +Low-level API. + +```c +alloc_workqueue( + fmt, + flags, + max_active); +``` + +Parameters: + +| Parameter | Description | +| ---------- | ------------------------- | +| fmt | Name format | +| flags | Workqueue flags | +| max_active | Maximum active work items | + +--- + +# Common Workqueue Flags + +## WQ_UNBOUND + +Workers are not tied to specific CPUs. + +```c +WQ_UNBOUND +``` + +Benefits: + +* Better scheduling flexibility +* Less CPU locality + +--- + +## WQ_MEM_RECLAIM + +Ensures forward progress during memory pressure. + +```c +WQ_MEM_RECLAIM +``` + +Used by many kernel workqueues. + +--- + +# Creating Work + +Work item: + +```c +static void workqueue_fn( + struct work_struct *work); + +static DECLARE_WORK( + work, + workqueue_fn); +``` + +--- + +# Workqueue Callback + +Executed by worker thread. + +```c +static void workqueue_fn( + struct work_struct *work) +{ + printk( + KERN_INFO + "Executing Workqueue Function\n"); +} +``` + +This function performs deferred processing. + +--- + +# Queueing Work + +## queue_work() + +Queues work into the dedicated workqueue. + +Syntax: + +```c +bool queue_work( + struct workqueue_struct *wq, + struct work_struct *work); +``` + +Example: + +```c +queue_work( + own_workqueue, + &work); +``` + +Returns: + +| Value | Meaning | +| ----- | ------------------- | +| false | Already queued | +| true | Successfully queued | + +--- + +# Queue Work On Specific CPU + +## queue_work_on() + +```c +queue_work_on( + cpu, + own_workqueue, + &work); +``` + +Example: + +```c +queue_work_on( + 0, + own_workqueue, + &work); +``` + +Queues work on CPU0. + +--- + +# Delayed Work + +## queue_delayed_work() + +Schedule after delay. + +```c +queue_delayed_work( + own_workqueue, + &dwork, + delay); +``` + +Example: + +```c +queue_delayed_work( + own_workqueue, + &dwork, + msecs_to_jiffies(5000)); +``` + +Runs after 5 seconds. + +--- + +# Delayed Work On Specific CPU + +## queue_delayed_work_on() + +```c +queue_delayed_work_on( + cpu, + own_workqueue, + &dwork, + delay); +``` + +Example: + +```c +queue_delayed_work_on( + 0, + own_workqueue, + &dwork, + delay); +``` + +--- + +# Interrupt + Own Workqueue Example + +## Interrupt Handler + +```c +static irqreturn_t irq_handler( + int irq, + void *dev_id) +{ + printk( + KERN_INFO + "Shared IRQ: Interrupt Occurred\n"); + + queue_work( + own_workqueue, + &work); + + return IRQ_HANDLED; +} +``` + +ISR only schedules work and returns immediately. + +--- + +# Driver Initialization + +## Create Workqueue + +```c +own_workqueue = + create_workqueue( + "own_wq"); +``` + +--- + +## Register IRQ + +```c +request_irq( + IRQ_NO, + irq_handler, + IRQF_SHARED, + "etx_device", + (void *)irq_handler); +``` + +--- + +## Work Initialization + +```c +DECLARE_WORK( + work, + workqueue_fn); +``` + +--- + +# Execution Flow + +```text +Driver Loaded + | + v +Create own_wq + | + v +Interrupt Occurs + | + v +ISR Executes + | + v +queue_work() + | + v +own_wq + | + v +Worker Thread + | + v +workqueue_fn() +``` + +--- + +# Driver Cleanup + +Always destroy the workqueue. + +```c +destroy_workqueue( + own_workqueue); +``` + +Complete cleanup: + +```c +destroy_workqueue( + own_workqueue); + +free_irq( + IRQ_NO, + dev_id); +``` + +--- + +# Sample dmesg Output + +```text +Device Driver Insert...Done!!! + +Read function + +Shared IRQ: Interrupt Occurred + +Executing Workqueue Function + +Device Driver Remove...Done!!! +``` + +This confirms: + +1. Interrupt occurred +2. ISR executed +3. Work queued +4. Worker thread executed callback + +--- + +# schedule_work() vs queue_work() + +| Feature | schedule_work() | queue_work() | +| ------------------ | --------------- | ------------ | +| Workqueue Used | Global | Custom | +| Workqueue Creation | Not Required | Required | +| Control | Limited | More Control | +| Isolation | Shared | Dedicated | +| API | schedule_work() | queue_work() | + +--- + +# Global Workqueue vs Own Workqueue + +| Feature | Global Workqueue | Own Workqueue | +| ---------------- | ---------------- | --------------------------------- | +| Setup Complexity | Simple | Moderate | +| Dedicated Thread | No | Yes | +| Isolation | Shared | Dedicated | +| Scalability | Good | Better for heavy driver workloads | +| Control | Limited | High | + +--- + +# Real Driver Use Cases + +## Network Driver + +```text +Packet Arrival + | + v +ISR + | + v +Dedicated Packet Processing Workqueue +``` + +--- + +## SPI Driver + +```text +Transfer Complete + | + v +Own Workqueue + | + v +Post Processing +``` + +--- + +## USB Driver + +```text +USB Event + | + v +Own Workqueue + | + v +Protocol Handling +``` + +--- + +## Storage Driver + +```text +DMA Completion + | + v +Dedicated Workqueue + | + v +Data Processing +``` + +--- + +# Advantages + +* Driver-specific execution context +* Better workload isolation +* Prevents blocking global workqueue +* Greater scheduling control +* Suitable for heavy deferred processing + +--- + +# Limitations + +* Additional worker threads +* More memory usage +* More management overhead + +--- + +# Important APIs Summary + +## Workqueue Creation + +```c +create_workqueue() +create_singlethread_workqueue() +alloc_workqueue() +``` + +--- + +## Queue Operations + +```c +queue_work() +queue_work_on() +queue_delayed_work() +queue_delayed_work_on() +``` + +--- + +## Cleanup + +```c +destroy_workqueue() +``` + +--- + +## Work Initialization + +```c +DECLARE_WORK() +INIT_WORK() +``` + +--- + +# Key Takeaways + +* Own Workqueue provides a dedicated execution context for a driver. +* `queue_work()` is used with custom workqueues. +* `schedule_work()` uses the kernel global workqueue. +* Workqueues execute in process context and can sleep. +* Dedicated workqueues are preferred for heavy or isolated deferred processing. +* Always destroy custom workqueues during module removal. +* Workqueues are commonly used with interrupts to move lengthy processing out of the ISR. + + +## Conclusion This is just a basic linux device driver. This will explain about the Own workqueue in the linux device driver. Please refer this URL for the complete tutorial of this source code. -https://embetronicx.com/tutorials/linux/device-drivers/work-queue-in-linux-own-workqueue/ \ No newline at end of file +https://embetronicx.com/tutorials/linux/device-drivers/work-queue-in-linux-own-workqueue/ diff --git a/Linux/Device_Driver/mutex/README.md b/Linux/Device_Driver/mutex/README.md new file mode 100644 index 0000000..3649354 --- /dev/null +++ b/Linux/Device_Driver/mutex/README.md @@ -0,0 +1,702 @@ +# Linux Device Driver - Mutex in Linux Kernel + +## Overview + +A Mutex (**Mutual Exclusion Lock**) is a synchronization mechanism used to protect shared resources from concurrent access by multiple threads. + +Only **one thread can own a mutex at a time**. + +Mutexes are used to prevent: + +* Race conditions +* Data corruption +* Inconsistent shared data access + +A mutex has **ownership semantics**: + +```text +Thread A locks Mutex + | + v +Thread A must unlock Mutex +``` + +A different thread cannot unlock a mutex it did not acquire. + +--- + +# Why Do We Need Mutex? + +Consider two kernel threads accessing the same global variable. + +Without synchronization: + +```text +Thread1 + | + +--> global_var++ + +Thread2 + | + +--> global_var++ +``` + +Possible execution: + +```text +global_var = 0 + +Thread1 reads 0 +Thread2 reads 0 + +Thread1 writes 1 +Thread2 writes 1 +``` + +Expected: + +```text +2 +``` + +Actual: + +```text +1 +``` + +This is called a **Race Condition**. + +--- + +# Race Condition + +A race condition occurs when: + +* Multiple threads access shared data +* At least one thread modifies the data +* Access is not synchronized + +Result depends on scheduling order. + +```text +Thread A + \ + \ + Shared Data + / + / +Thread B +``` + +Race conditions can cause: + +* Wrong values +* Kernel crashes +* Corrupted linked lists +* Memory corruption + +--- + +# What is a Mutex? + +Mutex stands for: + +```text +Mutual Exclusion +``` + +Concept: + +```text +Shared Resource + | + v + Mutex + | + v +Only One Thread Allowed +``` + +Workflow: + +```text +Lock Mutex + | + v +Access Shared Resource + | + v +Unlock Mutex +``` + +Any thread that arrives while the mutex is held will sleep until it becomes available. + +--- + +# Mutex vs Spinlock + +| Feature | Mutex | Spinlock | +| ---------------------- | --------------- | ----------- | +| Sleep Allowed | Yes | No | +| Context | Process Context | Any Context | +| Ownership | Yes | No | +| Long Critical Sections | Good | Bad | +| ISR Usage | Not Allowed | Allowed | +| Busy Waiting | No | Yes | + +Mutexes put the task to sleep when contention occurs, whereas spinlocks keep spinning while waiting. + +--- + +# Mutex Rules + +## Rule 1 + +Mutexes can sleep. + +Therefore: + +```text +Allowed: + Process Context + Kernel Thread + +Not Allowed: + ISR + Tasklet + SoftIRQ +``` + +--- + +## Rule 2 + +Mutex has ownership. + +```text +Thread A locks +Thread A unlocks +``` + +Not: + +```text +Thread A locks +Thread B unlocks +``` + +--- + +## Rule 3 + +Recursive locking is not allowed. + +Wrong: + +```c +mutex_lock(&lock); +mutex_lock(&lock); +``` + +This may deadlock. + +--- + +# Mutex Lifecycle + +```text +Create Mutex + | + v +mutex_init() + | + v +mutex_lock() + | + v +Critical Section + | + v +mutex_unlock() +``` + +--- + +# Header File + +```c +#include +``` + +Required for all mutex APIs. + +--- + +# Mutex Declaration + +## Static Method + +Used for global mutexes. + +```c +DEFINE_MUTEX(etx_mutex); +``` + +Equivalent concept: + +```text +Create + Initialize +``` + +in a single statement. + +--- + +# Dynamic Method + +Declare: + +```c +struct mutex etx_mutex; +``` + +Initialize: + +```c +mutex_init(&etx_mutex); +``` + +This initializes the mutex in unlocked state. + +--- + +# Locking a Mutex + +## mutex_lock() + +Acquire mutex. + +```c +mutex_lock(&etx_mutex); +``` + +Behavior: + +```text +Mutex Free + | + +--> Acquire Immediately + +Mutex Busy + | + +--> Sleep Until Available +``` + +The calling task sleeps until the mutex becomes available. + +--- + +# mutex_lock_interruptible() + +Interruptible version. + +```c +mutex_lock_interruptible( + &etx_mutex); +``` + +Return Values: + +| Return | Meaning | +| ------ | --------------------- | +| 0 | Lock Acquired | +| -EINTR | Interrupted by Signal | + +Useful when waiting should be interruptible. + +--- + +# mutex_trylock() + +Non-blocking attempt. + +```c +if(mutex_trylock(&etx_mutex)) +{ + /* lock acquired */ +} +``` + +Returns: + +| Return | Meaning | +| ------ | ------- | +| 1 | Success | +| 0 | Busy | + +Does not sleep. + +--- + +# Unlocking a Mutex + +## mutex_unlock() + +Release mutex. + +```c +mutex_unlock(&etx_mutex); +``` + +After unlocking: + +```text +Waiting Thread + | + v +Can Acquire Mutex +``` + +Must be unlocked by the same task that acquired it. + +--- + +# Checking Mutex State + +## mutex_is_locked() + +```c +mutex_is_locked( + &etx_mutex); +``` + +Returns: + +| Return | Meaning | +| ------ | -------- | +| 1 | Locked | +| 0 | Unlocked | + +--- + +# Kernel Thread Example + +## Shared Variable + +```c +unsigned long +etx_global_variable = 0; +``` + +Shared between: + +```text +Thread1 +Thread2 +``` + +--- + +# Thread 1 + +```c +int thread_function1(void *pv) +{ + while(!kthread_should_stop()) + { + mutex_lock( + &etx_mutex); + + etx_global_variable++; + + pr_info( + "Thread1 %lu\n", + etx_global_variable); + + mutex_unlock( + &etx_mutex); + + msleep(1000); + } + + return 0; +} +``` + +--- + +# Thread 2 + +```c +int thread_function2(void *pv) +{ + while(!kthread_should_stop()) + { + mutex_lock( + &etx_mutex); + + etx_global_variable++; + + pr_info( + "Thread2 %lu\n", + etx_global_variable); + + mutex_unlock( + &etx_mutex); + + msleep(1000); + } + + return 0; +} +``` + +--- + +# Execution Flow + +```text +Thread1 + | + +--> mutex_lock() + + Mutex Acquired + + | + +--> Increment Variable + + | + +--> mutex_unlock() + + | + +--> Sleep +``` + +Meanwhile: + +```text +Thread2 + | + +--> Waiting For Mutex +``` + +After unlock: + +```text +Thread2 Acquires Mutex +``` + +--- + +# Example Output + +```text +Thread1 1 +Thread2 2 +Thread1 3 +Thread2 4 +Thread1 5 +Thread2 6 +``` + +The value increments correctly because access is serialized by the mutex. + +--- + +# Mutex vs Semaphore + +| Feature | Mutex | Semaphore | +| -------------------- | ---------------- | -------------------------------- | +| Ownership | Yes | No | +| Unlock By Owner Only | Yes | No | +| Resource Count | One | Multiple | +| Purpose | Mutual Exclusion | Resource Counting | +| Priority Inheritance | Yes | Typically Yes (Kernel Dependent) | + +Mutex is used primarily for protecting critical sections. + +--- + +# Mutex vs Completion + +| Mutex | Completion | | +| ----------------------- | -------------- | -- | +| Protect Shared Resource | Wait For Event | | +| Lock/Unlock | Complete/Wait | | +| Ownership | Yes | No | +| Mutual Exclusion | Yes | No | + +--- + +# Common Driver Use Cases + +## Linked List Protection + +```c +mutex_lock(&list_mutex); + +list_add_tail(...); + +mutex_unlock(&list_mutex); +``` + +--- + +## Device Configuration + +```c +mutex_lock(&device_mutex); + +device_config(); + +mutex_unlock(&device_mutex); +``` + +--- + +## Shared Buffer Access + +```c +mutex_lock(&buffer_mutex); + +copy_data(); + +mutex_unlock(&buffer_mutex); +``` + +--- + +## File Operations + +```c +open() +read() +write() +ioctl() +``` + +Prevent simultaneous modification of shared driver data. + +--- + +# Common Mistakes + +## Forgetting Unlock + +Wrong: + +```c +mutex_lock(&lock); + +/* work */ + +return; +``` + +Correct: + +```c +mutex_lock(&lock); + +/* work */ + +mutex_unlock(&lock); + +return; +``` + +--- + +## Using Mutex in ISR + +Wrong: + +```c +irq_handler() +{ + mutex_lock(&lock); +} +``` + +Reason: + +```text +Mutex Can Sleep +ISR Cannot Sleep +``` + +--- + +## Double Locking + +Wrong: + +```c +mutex_lock(&lock); + +mutex_lock(&lock); +``` + +May cause deadlock. + +--- + +# Important APIs Summary + +## Initialization + +```c +DEFINE_MUTEX() + +mutex_init() +``` + +--- + +## Lock + +```c +mutex_lock() + +mutex_lock_interruptible() + +mutex_trylock() +``` + +--- + +## Unlock + +```c +mutex_unlock() +``` + +--- + +## Status + +```c +mutex_is_locked() +``` + +--- + +# Key Takeaways + +* Mutex provides mutual exclusion for shared resources. +* Only one thread can hold a mutex at a time. +* Mutexes prevent race conditions. +* Mutexes can sleep and therefore cannot be used in ISR, Tasklets, or SoftIRQs. +* `mutex_lock()` blocks until the lock becomes available. +* `mutex_trylock()` does not block. +* The thread that locks a mutex must unlock it. +* Mutexes are ideal for protecting linked lists, buffers, device state, and shared driver data. +* Kernel threads and file operations commonly use mutexes for synchronization. + + +## Conclusion +This is just a basic linux device driver which explains about the mutex in linux device driver. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-mutex-in-linux-kernel/ diff --git a/Linux/Device_Driver/mutex/ReadMe.md b/Linux/Device_Driver/mutex/ReadMe.md deleted file mode 100644 index c2568e8..0000000 --- a/Linux/Device_Driver/mutex/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains about the mutex in linux device driver. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-mutex-in-linux-kernel/ \ No newline at end of file diff --git a/Linux/Device_Driver/procfs/ReadMe.md b/Linux/Device_Driver/procfs/ReadMe.md index a07b618..224b380 100644 --- a/Linux/Device_Driver/procfs/ReadMe.md +++ b/Linux/Device_Driver/procfs/ReadMe.md @@ -1,4 +1,333 @@ +# procfs in Linux – Kernel Module Example + +- This repository demonstrates how to create and use entries in the **proc filesystem (procfs)** from a Linux kernel module. +- It shows how to expose kernel data to user space via `/proc` files and how to implement read/write operations for proc entries. + +> **Note:** For modern device drivers, `sysfs` is preferred over `procfs` for configuration and status. `procfs` is mainly intended for process-related and global kernel information, and its use by drivers is discouraged for new production code [web:22][web:23]. This example is primarily for learning and debugging. + +## What Is procfs? + +`procfs` is a virtual filesystem mounted at `/proc`. It: + +- Exists in RAM (not on disk) +- Provides kernel and system runtime information +- Acts as a bridge between kernel space and user space +- Allows user-space programs to read (and sometimes write) kernel data + +Common examples: + +- `/proc/cpuinfo` – CPU information +- `/proc/meminfo` – Memory statistics +- `/proc/[pid]` – Process-specific information [web:22] + +For kernel modules, you can create your own entries under `/proc` to: + +- Export driver variables or status +- Receive configuration from user space (by writing to the file) +- Debug kernel module behavior [web:22][web:26] + +## Types of proc Entries + +You can create: + +1. **Read-only entries** – Expose kernel data to user space +2. **Read-write entries** – Allow user space to both read and configure kernel data + +Reference: procfs as a kernel-to-user-space interface [web:27]. + +## Key procfs APIs + +### Creating a proc Directory + +```c +#include +#include + +struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent); +``` + +- `name`: directory name under `/proc` +- `parent`: parent directory (or `NULL` for root `/proc`) + +Example: + +```c +struct proc_dir_entry *my_dir = proc_mkdir("mydriver", NULL); +// Creates /proc/mydriver +``` + +### Creating a proc File (Modern API, kernel ≥ 3.10) + +```c +struct proc_dir_entry *proc_create( + const char *name, + umode_t mode, + struct proc_dir_entry *parent, + const struct proc_ops *ops // or file_operations in older kernels +); +``` + +- `name`: file name (e.g., `"status"`) +- `mode`: file permissions (e.g., `0444` for read-only, `0644` for read-write) +- `parent`: parent directory (or `NULL`) +- `ops`: pointer to `proc_ops` (or `file_operations` in older kernels) + +Example: + +```c +static struct proc_ops my_proc_ops = { + .proc_read = my_proc_read, + .proc_write = my_proc_write, +}; + +struct proc_dir_entry *entry = proc_create( + "mydriver_status", + 0644, + NULL, + &my_proc_ops +); +// Creates /proc/mydriver_status +``` + +For kernel ≥ 5.6, use `struct proc_ops` instead of `struct file_operations` [web:26]. + +### Older API (kernel < 3.10) + +Older kernels used: + +```c +static struct file_operations proc_fops = { + .open = open_proc, + .read = read_proc, + .write = write_proc, + .release = release_proc, +}; + +struct proc_dir_entry *entry = create_proc_read_entry( + "mydriver_status", + 0644, + NULL, + read_proc, + NULL +); +``` + +This is deprecated in modern kernels. + +### Removing proc Entries + +```c +void remove_proc_entry(const char *name, struct proc_dir_entry *parent); +void proc_remove(struct proc_dir_entry *entry); +``` + +- `remove_proc_entry()` removes a file or directory +- `proc_remove()` removes a directory and its contents + +Example in module exit: + +```c +remove_proc_entry("mydriver_status", NULL); +remove_proc_entry("mydriver", NULL); +``` + +Reference: Removing proc entries in module exit [web:21]. + +## Implementing proc Read and Write + +### `proc_ops` (Modern) + +```c +#include +#include + +static ssize_t my_proc_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) +{ + static const char *data = "Hello from procfs\n"; + size len = strlen(data); + + if (*offset >= len) + return 0; + if (count > len - *offset) + count = len - *offset; + + if (copy_to_user(buf, data + *offset, count)) + return -EFAULT; + + *offset += count; + return count; +} + +static ssize_t my_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char kernel_buf; + int len = min(count, 63); + + if (copy_from_user(kernel_buf, buf, len)) + return -EFAULT; + + kernel_buf[len] = '\0'; + pr_info("procfs write: %s\n", kernel_buf); + + return count; +} + +static struct proc_ops my_proc_ops = { + .proc_read = my_proc_read, + .proc_write = my_proc_write, +}; +``` + +Key points: + +- Use `copy_to_user()` and `copy_from_user()` for safety +- Return number of bytes handled +- Return `-EFAULT` on copy failure + +Reference: Creating procfs entries with read/write and removing them [web:26]. + +## Files in This Repository + +- `procfs_driver.c` – Kernel module that creates proc entries +- `Makefile` – Builds the kernel module +- `README.md` – This documentation + +Rename files to match your actual source. + +## Building the Module + +Ensure kernel headers and tools are installed. + +```bash +make +``` + +This produces `procfs_driver.ko`. + +Clean: + +```bash +make clean +``` + +## Loading the Module + +```bash +sudo insmod procfs_driver.ko +dmesg | tail +``` + +Check: + +- The module prints success messages +- A file appears under `/proc`, e.g. `/proc/mydriver_status` + +List proc entries: + +```bash +ls /proc/mydriver* +``` + +## Using the proc Entry + +### Read + +```bash +cat /proc/mydriver_status +``` + +This calls your `proc_read` function and returns the kernel data. + +### Write + +```bash +echo "test message" | sudo tee /proc/mydriver_status +``` + +This calls your `proc_write` function and logs the message via `pr_info()`. + +Check kernel logs: + +```bash +dmesg | tail +``` + +## Unloading the Module + +```bash +sudo rmmod procfs_driver +dmesg | tail +``` + +Ensure the module: + +- Removes all proc entries with `remove_proc_entry()` or `proc_remove()` +- Cleans up any allocated resources + +## Example: Read-Only proc Entry + +For a simple read-only entry: + +```c +static struct proc_ops my_proc_ops = { + .proc_read = my_proc_read, +}; + +struct proc_dir_entry *entry = proc_create( + "mydriver_info", + 0444, // read-only + NULL, + &my_proc_ops +); +``` + +User space can only read: + +```bash +cat /proc/mydriver_info +``` + +## Prerequisites + +- Linux system with kernel headers and build tools +- Basic C programming knowledge +- Familiarity with: + - `make`, `gcc` + - `insmod`, `rmmod`, `dmesg` + - `/proc` filesystem + +## Minimal Build Environment (Ubuntu) + +```bash +sudo apt update +sudo apt install -y build-essential linux-headers-$(uname -r) +``` + +## Best Practices and Modern Alternatives + +- For **device configuration and status**, prefer `sysfs` (`/sys`) over `procfs` +- Use `procfs` mainly for: + - Debug output + - Global kernel/runtime information + - Learning and experimentation + +Key differences: + +| Aspect | procfs (`/proc`) | sysfs (`/sys`) | +|-----------------|-----------------------------------|-------------------------------------| +| Purpose | Kernel/process internals | Device & driver attributes | +| Structure | Flat, unstructured | Hierarchical, object-based | +| Recommended for drivers | Legacy / debug only | Production interfaces | +| Example paths | `/proc/cpuinfo`, `/proc/meminfo` | `/sys/class/`, `/sys/devices/` | + +Reference: procfs vs sysfs for device drivers [web:22]. + + + +## Conslusion This is just a basic linux device driver which explains about the procfs. Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/procfs-in-linux/ \ No newline at end of file +https://embetronicx.com/tutorials/linux/device-drivers/procfs-in-linux/ diff --git a/Linux/Device_Driver/sysfs/README.md b/Linux/Device_Driver/sysfs/README.md new file mode 100644 index 0000000..ca47700 --- /dev/null +++ b/Linux/Device_Driver/sysfs/README.md @@ -0,0 +1,659 @@ +# Linux Device Driver - Sysfs + +## Overview + +Sysfs is a virtual filesystem provided by the Linux kernel that exposes kernel objects, devices, drivers, and configuration parameters to user space. + +It is mounted at: + +```bash +/sys +``` + +Sysfs allows communication between: + +```text +User Space <----> Sysfs <----> Kernel Space +``` + +Unlike regular filesystems, sysfs files do not store data on disk. Their contents are generated dynamically by the kernel when accessed. + +--- + +# Why Sysfs? + +Linux provides several mechanisms for communication between user space and kernel space: + +* IOCTL +* Procfs +* Sysfs +* Debugfs +* Configfs +* Sysctl +* Netlink Sockets +* UDP Sockets + +Among these: + +| Interface | Purpose | +| --------- | ---------------------------------- | +| Procfs | Process and system information | +| Sysfs | Device and driver information | +| Debugfs | Debugging information | +| IOCTL | Device-specific control operations | + +Sysfs is the preferred mechanism for exposing device attributes and configuration parameters. + +--- + +# Sysfs Filesystem Layout + +```text +/sys +├── block +├── bus +├── class +├── devices +├── firmware +├── kernel +└── module +``` + +Example: + +```bash +/sys/kernel/ +``` + +--- + +# What is a Kobject? + +The foundation of Sysfs is the Kernel Object (kobject). + +A kobject: + +* Represents a kernel object +* Creates directories in sysfs +* Maintains parent-child relationships +* Supports reference counting +* Connects kernel structures to sysfs + +Defined in: + +```c +#include +``` + +Simplified structure: + +```c +struct kobject +{ + char *name; + struct kobject *parent; + struct kset *kset; + struct kobj_type *ktype; + struct kref kref; +}; +``` + +Important fields: + +| Field | Purpose | +| ------ | ----------------- | +| name | Name of object | +| parent | Parent directory | +| kset | Group of kobjects | +| ktype | Object type | +| kref | Reference counter | + +--- + +# Sysfs Driver Workflow + +```text +Driver Loaded + | + v +Create Kobject + | + v +Create Sysfs File + | + v +User Reads/Writes + | + v +Show/Store Functions Called + | + v +Kernel Variable Updated +``` + +--- + +# Step 1 - Create Sysfs Directory + +Function: + +```c +struct kobject * +kobject_create_and_add( + const char *name, + struct kobject *parent); +``` + +Parameters: + +| Parameter | Description | +| --------- | -------------- | +| name | Directory name | +| parent | Parent kobject | + +--- + +## Common Parents + +### Under /sys + +```c +kobject_create_and_add("mydir", NULL); +``` + +Creates: + +```text +/sys/mydir +``` + +--- + +### Under /sys/kernel + +```c +kobject_create_and_add( + "mydir", + kernel_kobj); +``` + +Creates: + +```text +/sys/kernel/mydir +``` + +--- + +### Under /sys/firmware + +```c +kobject_create_and_add( + "mydir", + firmware_kobj); +``` + +Creates: + +```text +/sys/firmware/mydir +``` + +--- + +### Under /sys/fs + +```c +kobject_create_and_add( + "mydir", + fs_kobj); +``` + +Creates: + +```text +/sys/fs/mydir +``` + +--- + +## Example + +```c +struct kobject *kobj_ref; + +kobj_ref = +kobject_create_and_add( + "etx_sysfs", + kernel_kobj); +``` + +Result: + +```text +/sys/kernel/etx_sysfs +``` + +--- + +# Releasing Kobject + +Always release the kobject: + +```c +kobject_put(kobj_ref); +``` + +--- + +# Step 2 - Create Sysfs File + +After creating a directory, create files inside it. + +Sysfs files are called Attributes. + +Example: + +```text +/sys/kernel/etx_sysfs/etx_value +``` + +--- + +# Attribute Functions + +Every sysfs attribute requires: + +1. Show function (Read) +2. Store function (Write) + +--- + +## Show Function + +Called when: + +```bash +cat attribute_file +``` + +Prototype: + +```c +static ssize_t sysfs_show( + struct kobject *kobj, + struct kobj_attribute *attr, + char *buf); +``` + +Example: + +```c +static ssize_t sysfs_show( + struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf,"%d",etx_value); +} +``` + +--- + +## Store Function + +Called when: + +```bash +echo value > attribute_file +``` + +Prototype: + +```c +static ssize_t sysfs_store( + struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count); +``` + +Example: + +```c +static ssize_t sysfs_store( + struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + sscanf(buf,"%d",&etx_value); + return count; +} +``` + +--- + +# Create Attribute + +Macro: + +```c +__ATTR( + name, + permission, + show, + store +); +``` + +Example: + +```c +struct kobj_attribute etx_attr = + __ATTR( + etx_value, + 0660, + sysfs_show, + sysfs_store + ); +``` + +--- + +# File Permissions + +| Permission | Meaning | +| ---------- | --------------------- | +| 0444 | Read only | +| 0664 | Read/Write | +| 0660 | Owner & Group RW | +| 0644 | Owner RW, Others Read | + +Example: + +```c +0660 +``` + +Equivalent: + +```bash +-rw-rw---- +``` + +--- + +# Create Sysfs File + +Function: + +```c +sysfs_create_file( + kobj_ref, + &etx_attr.attr); +``` + +Example: + +```c +if(sysfs_create_file( + kobj_ref, + &etx_attr.attr)) +{ + pr_err("Sysfs creation failed\n"); +} +``` + +Result: + +```text +/sys/kernel/etx_sysfs/etx_value +``` + +--- + +# Remove Sysfs File + +```c +sysfs_remove_file( + kobj_ref, + &etx_attr.attr); +``` + +--- + +# Complete Example + +Kernel variable: + +```c +volatile int etx_value = 0; +``` + +Create directory: + +```c +kobj_ref = +kobject_create_and_add( + "etx_sysfs", + kernel_kobj); +``` + +Create file: + +```c +sysfs_create_file( + kobj_ref, + &etx_attr.attr); +``` + +--- + +# User Space Access + +## Read + +```bash +cat /sys/kernel/etx_sysfs/etx_value +``` + +Output: + +```text +0 +``` + +--- + +## Write + +```bash +echo 123 > \ +/sys/kernel/etx_sysfs/etx_value +``` + +--- + +## Verify + +```bash +cat /sys/kernel/etx_sysfs/etx_value +``` + +Output: + +```text +123 +``` + +--- + +# Build Driver + +Makefile: + +```make +obj-m += driver.o + +KDIR = /lib/modules/$(shell uname -r)/build + +all: + make -C $(KDIR) \ + M=$(shell pwd) modules + +clean: + make -C $(KDIR) \ + M=$(shell pwd) clean +``` + +--- + +# Load Driver + +```bash +sudo insmod driver.ko +``` + +Verify: + +```bash +ls /sys/kernel +``` + +Output: + +```text +etx_sysfs +``` + +--- + +# Unload Driver + +```bash +sudo rmmod driver +``` + +--- + +# Sysfs vs Procfs + +| Feature | Sysfs | Procfs | +| ------------------------ | ----------------- | ----------------------- | +| Purpose | Devices & Drivers | Processes & System Info | +| Location | /sys | /proc | +| Device Model Integration | Yes | No | +| Attribute Based | Yes | No | +| Driver Configuration | Yes | Limited | + +--- + +# Sysfs vs IOCTL + +| Sysfs | IOCTL | +| -------------------- | ------------------ | +| File-based | System call based | +| Human readable | Binary interface | +| Easy debugging | More flexible | +| Simple configuration | Complex operations | + +--- + +# Real Driver Use Cases + +Common sysfs attributes: + +```text +brightness +power +enable +frequency +temperature +fan_speed +voltage +gpio_value +``` + +Examples: + +```text +/sys/class/leds/ +/sys/class/gpio/ +/sys/class/pwm/ +/sys/class/thermal/ +``` + +--- + +# Advantages + +* Simple user-space interface +* Human-readable files +* Easy debugging +* Device model integration +* No custom application required +* Standard Linux mechanism + +--- + +# Limitations + +* One value per file +* Not suitable for large data transfer +* Not suitable for streaming data +* Not suitable for complex commands + +--- + +# Key APIs Summary + +## Kobject APIs + +```c +kobject_create_and_add() +kobject_put() +``` + +## Attribute APIs + +```c +__ATTR() +``` + +## Sysfs APIs + +```c +sysfs_create_file() +sysfs_remove_file() +``` + +## Callback Functions + +```c +show() +store() +``` + +--- + +# Key Takeaways + +* Sysfs is mounted under `/sys`. +* Sysfs exports kernel and device information to user space. +* Every sysfs file maps to a kernel attribute. +* Reading a file invokes the `show()` callback. +* Writing a file invokes the `store()` callback. +* Kobjects create directories in sysfs. +* Sysfs is the preferred interface for driver configuration and status reporting. +* Commonly used in Linux device driver development for exposing device parameters. + +``` +``` + + +## Conclusion +This is just a basic linux device driver which explains about the sysfs in linux device driver. + +Please refer this URL for the complete tutorial of this example source code. +https://embetronicx.com/tutorials/linux/device-drivers/sysfs-in-linux-kernel/ diff --git a/Linux/Device_Driver/sysfs/ReadMe.md b/Linux/Device_Driver/sysfs/ReadMe.md deleted file mode 100644 index 4dab190..0000000 --- a/Linux/Device_Driver/sysfs/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -This is just a basic linux device driver which explains about the sysfs in linux device driver. - -Please refer this URL for the complete tutorial of this example source code. -https://embetronicx.com/tutorials/linux/device-drivers/sysfs-in-linux-kernel/ \ No newline at end of file diff --git a/README.md b/README.md index de8ecfe..2138d7c 100644 --- a/README.md +++ b/README.md @@ -1 +1,6 @@ -# Tutorials \ No newline at end of file +# Tutorials +1. Linux Device Drivers +2. microcontrollers +3. RTOS/FreeRTOS +4. Unit Testing +