Table of Contents
Peripherals
In Linux, peripherals are devices that are external to the computer's central processing unit (CPU). They can be classified into various categories based on their function. Each type of peripheral in Linux is typically accessed through a device file located in the /dev
directory. These device files are interfaces to the drivers that allow user-space programs to interact with the hardware. Here's an overview of common types of peripherals in Linux, along with examples of their device files and how they are generally numbered:
Block Devices
Block devices are storage devices that can contain filesystems, such as hard drives, SSDs, and USB flash drives. They are accessed in blocks of data, hence the name.
- Device File Examples:
/dev/sda
,/dev/sdb1
- Numbering: The first detected hard drive is
/dev/sda
, the second is/dev/sdb
, and so on. Partitions on these drives are numbered, e.g.,/dev/sda1
for the first partition on the first drive.
Character Devices
Character devices are accessed character by character. This category includes a wide variety of devices, including serial ports, keyboards, and more.
- Device File Examples:
/dev/tty1
(virtual console),/dev/ttyS0
(serial port),/dev/input/event0
(input device event interface) - Numbering: Numbering can vary based on the device type. Serial ports are numbered sequentially (
ttyS0
,ttyS1
, etc.). Input devices like keyboards and mice may have event numbers (event0
,event1
, etc.).
Network Interfaces
Network interfaces aren't typically represented as files in /dev
but are crucial peripherals. They are represented by interface names and configured using networking tools.
- Interface Examples:
eth0
(Ethernet),wlan0
(Wi-Fi) - Numbering: Numbering is sequential based on detection order or system configuration.
Audio and Video Devices
These devices include sound cards, webcams, and TV tuners. They are accessed through specific device files or API interfaces.
- Device File Examples:
/dev/video0
(video device),/dev/snd/controlC0
(sound device control interface) - Numbering: Video and audio devices are numbered sequentially starting from 0.
Printers
Printers in Linux can be accessed through various subsystems, including CUPS (Common UNIX Printing System). Direct interaction with device files for printers is less common with modern printing systems.
- Device File Examples:
/dev/usb/lp0
(USB printer) - Numbering: USB printers are numbered sequentially starting from 0.
USB Devices
USB devices, including storage devices, input devices, and others, can also be accessed directly for low-level interaction.
- Device File Examples:
/dev/bus/usb/001/001
(USB device file, bus 001, device 001) - Numbering: USB devices are numbered based on the bus and device number. The numbering resets when the device is unplugged and plugged back in.
Note: The actual device files and numbering can vary based on the Linux distribution, kernel version, and system configuration. For many peripherals, higher-level APIs and subsystems abstract away the need to interact directly with device files, providing a more user-friendly interface for accessing device functionality.
Handling devices in Linux
Managing peripherals in Linux using C involves direct interaction with hardware through the Linux kernel. This can be achieved via various methods, including system calls, direct memory access, and the use of specific Linux APIs. Here's a beginner-friendly tutorial to get you started with managing peripherals in Linux using C.
Understanding Linux Device Files
In Linux, everything is treated as a file, including hardware peripherals. These are accessible through device files located in the /dev
directory. For example, serial ports might be represented as /dev/ttyS0
, and USB devices might be listed as /dev/usb/hiddev0
.
Basic File Operations
To interact with a peripheral device, you typically open, read from/write to, and close the device file using standard C
file operations:
- Open: Use
open()
to get a file descriptor for the device. - Read/Write: Use
read()
andwrite()
with the file descriptor to communicate with the device. - Close: Use
close()
to release the file descriptor.
Example - Accessing a Serial Port
Here's how you can open a serial port and configure it using the termios
structure.
Required Headers
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
Opening a Serial Port
PlantUML diagrame for port manuplation in linux:
@startuml
!theme toy
start
:Declare serial_fd;
:Attempt to open serial port "/dev/ttyS0";
if (serial_fd == -1) then (true)
:perror("open");
stop
else (false)
endif
:Declare struct termios options;
:Get current serial port settings;
:Set read and write speed to 9600 baud;
:Enable the receiver and set local mode;
:Apply the settings to serial port;
:Read/Write to the serial port;
:Close the serial port;
stop
@enduml
C programming for managing serial ports in Linux:
int main() {
int serial_fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (serial_fd == -1) {
perror("open");
return -1;
}
struct termios options;
tcgetattr(serial_fd, &options); // Get current serial port settings
cfsetispeed(&options, B9600); // Set read and write speed to 9600 baud
cfsetospeed(&options, B9600);
options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode
tcsetattr(serial_fd, TCSANOW, &options); // Apply the settings
// Now you can read/write to the serial port using read() and write()
close(serial_fd); // Close the serial port
return 0;
}
Compiling Your Program
To compile your C program, use the gcc
compiler:
gcc -o myprogram myprogram.c
Replace myprogram.c
with the name of your source file. This command compiles your C code and outputs an executable named myprogram
.
Running Your Program
Run your compiled program from the terminal:
./myprogram
Important Considerations
- Permissions: Accessing device files usually requires root permissions. Run your program with
sudo
or set appropriate permissions on the device file. - Safety: Direct hardware access can lead to system instability or damage if not done carefully. Always double-check your code and understand the hardware specifications you're working with.
Going Further
- Explore IOCTLs: For more complex devices, you'll need to use IOCTL (input/output control) calls to perform specific control operations.
- Kernel Module Development: Sometimes, you might need to write a custom kernel module to manage a peripheral. This involves a deeper understanding of the Linux kernel and C programming.
- Documentation and Resources: Check out the Linux man pages (
man 2 open
,man 2 read
,man 2 write
,man 2 ioctl
) for more information on system calls. The Linux Kernel Module Programming Guide is also a great resource for diving deeper into how Linux interacts with hardware.
Sophisticated examples of interfacing with Linux devices using C
Copy a file from a directory to a USB flash drive in C
Creating a C program that copies files to a USB drive in Linux, including mounting and unmounting the USB drive, involves several steps and requires careful handling. This process can be divided into distinct phases: detecting the USB device, mounting it, copying the file(s), and unmounting the device. Below is a simplified example to demonstrate these steps.
Important Notes:
- Mounting and Unmounting USB Drives: This usually requires root privileges. Therefore, the program should be run with appropriate permissions.
- USB Detection: Automatically detecting and mounting USB drives is complex and typically handled by the operating system. For simplicity, this example assumes you know the device identifier (e.g.,
/dev/sdb1
) and mount point (e.g.,/mnt/usb
). - Error Handling: This example includes basic error handling for clarity. Ensure you expand this based on your requirements.
Compile with gcc
To use the system
function, compile the program with gcc
and link with the -l
option as necessary.
Example Program:
This program mounts a specified USB device, copies a file to it, and then unmounts it.
PlantUML diagram for mounting a USB in linux:
@startuml
!theme toy
start
:Declare sourceFile, destination, device, mountPoint;
:Prepare mount command;
if (Execute mount command) then (success)
:Prepare copy command;
if (Execute copy command) then (success)
:Prepare unmount command;
if (Execute unmount command) then (success)
:File copied successfully and USB drive unmounted;
else (failure)
:Failed to unmount;
endif
else (failure)
:Failed to copy file;
:Attempt to unmount;
endif
else (failure)
:Failed to mount;
endif
stop
@enduml
C code for mounting a USB in linux:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char *sourceFile = "/path/to/source/file";
char *destination = "/mnt/usb/destinationFilename";
char *device = "/dev/sdb1"; // Change this to your USB device
char *mountPoint = "/mnt/usb"; // Ensure this mount point exists
// Mount the USB drive
char mountCmd[256];
sprintf(mountCmd, "mount %s %s", device, mountPoint);
if (system(mountCmd) != 0) {
fprintf(stderr, "Failed to mount %s on %s\n", device, mountPoint);
return 1;
}
// Copy the file
char cpCmd[512];
sprintf(cpCmd, "cp %s %s", sourceFile, destination);
if (system(cpCmd) != 0) {
fprintf(stderr, "Failed to copy file from %s to %s\n", sourceFile, destination);
// Attempt to unmount before exiting due to failure
system("umount /mnt/usb");
return 2;
}
// Unmount the USB drive
char umountCmd[256];
sprintf(umountCmd, "umount %s", mountPoint);
if (system(umountCmd) != 0) {
fprintf(stderr, "Failed to unmount %s\n", mountPoint);
return 3;
}
printf("File copied successfully and USB drive unmounted.\n");
return 0;
}
Before running the program, ensure you have customized the paths according to your system settings:
- Adjust the file paths for copying
- Verify the existence of the /mnt/usb mount point, and create it if necessary
- Possess superuser permissions.
Important Considerations:
- Device and Mount Point: You need to replace
/dev/sdb1
and/mnt/usb
with your actual USB device identifier and mount point. The mount point directory must exist before running this program. - Running as Root: Operations like mounting and unmounting usually require root privileges. Run this program as root or use
sudo
. - Security Note: Using
system()
to execute commands that involve input strings can be insecure, especially if any part of the command is user-controlled. This can lead to shell injection vulnerabilities. Ensure that your inputs are sanitized or use more secure alternatives likefork()
andexec()
for production code. - Error Handling and Robustness: This example provides basic functionality. In a real-world application, more robust error handling, possibly involving direct system calls for file operations, and dynamic USB detection might be necessary.
Compile this program using gcc
and run it with root permissions to see it in action.
Interfacing with I2C and serial (UART) devices in Linux using C involves direct interaction with device files. For I2C, you'll usually interact with devices via the /dev/i2c-X
interface, where X
is the bus number. For serial communication, you'll use the /dev/ttyS0
(for COM1), /dev/ttyS1
(for COM2), etc., depending on which serial port you're working with.
Serial Communication Example in C
This example opens a serial port, configures it, and writes a simple message.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
int main() {
int serial_port = open("/dev/ttyS0", O_RDWR);
if (serial_port < 0) {
perror("open");
return 1;
}
struct termios tty;
memset(&tty, 0, sizeof tty);
// Get current serial port settings
if (tcgetattr(serial_port, &tty) != 0) {
perror("tcgetattr");
close(serial_port);
return 1;
}
// Set Baud Rate
cfsetospeed(&tty, B9600);
cfsetispeed(&tty, B9600);
// Setting other Port Stuff
tty.c_cflag &= ~PARENB; // Make 8n1
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS; // no flow control
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
tty.c_iflag &= ~(ICRNL | INLCR); // Disable CR to NL translation, etc.
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g., newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
// Apply the settings
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
perror("tcsetattr");
close(serial_port);
return 1;
}
// Write to serial port
char msg[] = "Hello, Serial!\n";
write(serial_port, msg, sizeof(msg));
// Close the serial port
close(serial_port);
}
I2C Communication Example in C
This example sends a byte to an I2C device. You'll need to know the device's I2C address and the bus number it's connected to.
PlantUML diagram first!
@startuml
!theme toy
start
:Declare device and address variables;
:Attempt to open I2C bus device;
if (open device success) then (yes)
:Set device as I2C slave;
if (set slave success) then (yes)
:Prepare data buffer;
:Write data to I2C device;
if (write success) then (yes)
-right-> [Success] :Close I2C bus device;
else (no)
:perror("Failed to write to the i2c device.");
-right-> [Fail] :Close I2C bus device;
endif
else (no)
:perror("Failed to acquire bus access and/or talk to slave.");
endif
else (no)
:perror("Failed to open the i2c bus");
endif
stop
@enduml
C code:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
int main() {
const char *device = "/dev/i2c-1"; // Change to your I2C bus
int address = 0x40; // Change to your device's I2C address
int file;
if ((file = open(device, O_RDWR)) < 0) {
perror("Failed to open the i2c bus");
exit(1);
}
if (ioctl(file, I2C_SLAVE, address) < 0) {
perror("Failed to acquire bus access and/or talk to slave.\n");
exit(1);
}
// Now you can use I2C_RDWR or I2C_SMBUS ioctls to communicate with the device
// Example: write a byte to the device
uint8_t buffer[1] = {0x00}; // Data to send
if (write(file, buffer, 1) != 1) {
perror("Failed to write to the i2c device.\n");
}
close(file);
return 0;
}
Compiling and Running
For both examples, compile with gcc
:
gcc -o serial_example serial_example.c
gcc -o i2c_example i2c_example.c -lrt
Run with appropriate permissions, potentially using sudo
, because accessing hardware devices typically requires root privileges.
🏷️ Author position : Embedded Software Engineer
🔗 Author LinkedIn : LinkedIn profile
Comments