Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Character, Block, and Network Drivers | Foundations of Device Drivers
Practice
Projects
Quizzes & Challenges
Quizzes
Challenges
/
C Device Drivers Basics

bookCharacter, Block, and Network Drivers

Understanding device drivers requires recognizing that not all drivers interact with hardware in the same way. The three primary types of device drivers in a typical operating system are character drivers, block drivers, and network drivers. Each serves a distinct class of devices and exposes a specific interface to the kernel and user space.

Character drivers manage devices that handle data as a stream of bytes, such as keyboards, serial ports, and mice. These drivers provide unbuffered, sequential access to hardware. Block drivers are suited for devices that store data in fixed-size blocks, like hard drives and SSDs, allowing random access to any block. Network drivers handle network interfaces, providing packet-based data transfer and integrating with the kernel's networking stack.

basic_char_device.c

basic_char_device.c

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
// basic_char_device.c #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #define DEVICE_NAME "basic_char_dev" static int dev_open(struct inode *inodep, struct file *filep) { return 0; } static ssize_t dev_read(struct file *filep, char __user *buffer, size_t len, loff_t *offset) { return 0; } static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset) { return len; } static int dev_release(struct inode *inodep, struct file *filep) { return 0; } static struct file_operations fops = { .open = dev_open, .read = dev_read, .write = dev_write, .release = dev_release, }; static int major_number; static int __init char_dev_init(void) { major_number = register_chrdev(0, DEVICE_NAME, &fops); if (major_number < 0) { printk(KERN_ALERT "Failed to register a major number\n"); return major_number; } printk(KERN_INFO "Registered char device with major number %d\n", major_number); return 0; } static void __exit char_dev_exit(void) { unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_INFO "Char device unregistered\n"); } module_init(char_dev_init); module_exit(char_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Device Driver Example"); MODULE_DESCRIPTION("A simple character device driver example");

The file_operations structure in the example above is central to how character drivers interact with user space. Each function pointer in this structureβ€”such as read, write, open, and releaseβ€”corresponds to a system call that user programs can make. Character drivers expose a simple, sequential interface, where data is read and written as a stream of bytes. This contrasts with block drivers, which must manage random access to fixed-size blocks, and with network drivers, which focus on handling network packets instead of raw data streams. The sequential, byte-oriented nature of character drivers makes them ideal for devices where order and immediacy of data matter, while block and network drivers must accommodate different data access patterns and buffering strategies.

block_device_register.c

block_device_register.c

copy
12345678910111213141516171819202122232425262728293031
#include <linux/module.h> #include <linux/blkdev.h> #define DEVICE_NAME "basic_block_dev" static int major_number; static struct block_device_operations blkdev_ops = { .owner = THIS_MODULE, }; static int __init block_dev_init(void) { major_number = register_blkdev(0, DEVICE_NAME); if (major_number < 0) { printk(KERN_ALERT "Failed to register block device\n"); return major_number; } printk(KERN_INFO "Registered block device with major number %d\n", major_number); return 0; } static void __exit block_dev_exit(void) { unregister_blkdev(major_number, DEVICE_NAME); printk(KERN_INFO "Block device unregistered\n"); } module_init(block_dev_init); module_exit(block_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Device Driver Example"); MODULE_DESCRIPTION("A simple block device registration example");
question mark

Which type of driver is typically used for devices like keyboards and serial ports?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 1. ChapterΒ 3

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

Suggested prompts:

Can you explain the main differences between character, block, and network drivers in more detail?

What are some common examples of devices managed by each type of driver?

How does the `file_operations` structure work in practice for character drivers?

bookCharacter, Block, and Network Drivers

Swipe to show menu

Understanding device drivers requires recognizing that not all drivers interact with hardware in the same way. The three primary types of device drivers in a typical operating system are character drivers, block drivers, and network drivers. Each serves a distinct class of devices and exposes a specific interface to the kernel and user space.

Character drivers manage devices that handle data as a stream of bytes, such as keyboards, serial ports, and mice. These drivers provide unbuffered, sequential access to hardware. Block drivers are suited for devices that store data in fixed-size blocks, like hard drives and SSDs, allowing random access to any block. Network drivers handle network interfaces, providing packet-based data transfer and integrating with the kernel's networking stack.

basic_char_device.c

basic_char_device.c

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
// basic_char_device.c #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #define DEVICE_NAME "basic_char_dev" static int dev_open(struct inode *inodep, struct file *filep) { return 0; } static ssize_t dev_read(struct file *filep, char __user *buffer, size_t len, loff_t *offset) { return 0; } static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset) { return len; } static int dev_release(struct inode *inodep, struct file *filep) { return 0; } static struct file_operations fops = { .open = dev_open, .read = dev_read, .write = dev_write, .release = dev_release, }; static int major_number; static int __init char_dev_init(void) { major_number = register_chrdev(0, DEVICE_NAME, &fops); if (major_number < 0) { printk(KERN_ALERT "Failed to register a major number\n"); return major_number; } printk(KERN_INFO "Registered char device with major number %d\n", major_number); return 0; } static void __exit char_dev_exit(void) { unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_INFO "Char device unregistered\n"); } module_init(char_dev_init); module_exit(char_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Device Driver Example"); MODULE_DESCRIPTION("A simple character device driver example");

The file_operations structure in the example above is central to how character drivers interact with user space. Each function pointer in this structureβ€”such as read, write, open, and releaseβ€”corresponds to a system call that user programs can make. Character drivers expose a simple, sequential interface, where data is read and written as a stream of bytes. This contrasts with block drivers, which must manage random access to fixed-size blocks, and with network drivers, which focus on handling network packets instead of raw data streams. The sequential, byte-oriented nature of character drivers makes them ideal for devices where order and immediacy of data matter, while block and network drivers must accommodate different data access patterns and buffering strategies.

block_device_register.c

block_device_register.c

copy
12345678910111213141516171819202122232425262728293031
#include <linux/module.h> #include <linux/blkdev.h> #define DEVICE_NAME "basic_block_dev" static int major_number; static struct block_device_operations blkdev_ops = { .owner = THIS_MODULE, }; static int __init block_dev_init(void) { major_number = register_blkdev(0, DEVICE_NAME); if (major_number < 0) { printk(KERN_ALERT "Failed to register block device\n"); return major_number; } printk(KERN_INFO "Registered block device with major number %d\n", major_number); return 0; } static void __exit block_dev_exit(void) { unregister_blkdev(major_number, DEVICE_NAME); printk(KERN_INFO "Block device unregistered\n"); } module_init(block_dev_init); module_exit(block_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Device Driver Example"); MODULE_DESCRIPTION("A simple block device registration example");
question mark

Which type of driver is typically used for devices like keyboards and serial ports?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

SectionΒ 1. ChapterΒ 3
some-alt