17.1 开发板硬件介绍
假设我们使用的是一款基于ARM架构的开发板,板上集成了多种设备,如串口、GPIO、SPI、I2C等接口,以及网卡、USB控制器、PCI插槽等设备。不同的开发板硬件特性会有所不同,这里以通用的硬件配置为例进行说明。
17.2 字符设备驱动开发实例
字符设备是一种以字节流方式进行数据传输的设备,例如串口设备。下面是一个简单的字符设备驱动实例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
// 定义设备号
dev_t dev_num;
// 定义字符设备结构体
struct cdev my_cdev;
// 定义设备文件操作结构体
static struct file_operations my_fops = {
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
.open = my_open,
.release = my_release,
};
// 定义一个缓冲区用于存储数据
char buffer[1024];
// 字符设备的读函数
static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *off) {
// filp: 文件结构体指针,代表当前打开的设备文件
// buf: 用户空间缓冲区,用于接收数据
// count: 用户期望读取的字节数
// off: 文件偏移量
// 计算实际能读取的字节数
size_t read_count = min(count, sizeof(buffer) - *off);
if (copy_to_user(buf, buffer + *off, read_count)) {
// 如果数据从内核空间拷贝到用户空间失败,返回错误码
return -EFAULT;
}
*off += read_count;
return read_count;
}
// 字符设备的写函数
static ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *off) {
// filp: 文件结构体指针,代表当前打开的设备文件
// buf: 用户空间缓冲区,包含要写入的数据
// count: 用户期望写入的字节数
// off: 文件偏移量
// 计算实际能写入的字节数
size_t write_count = min(count, sizeof(buffer) - *off);
if (copy_from_user(buffer + *off, buf, write_count)) {
// 如果数据从用户空间拷贝到内核空间失败,返回错误码
return -EFAULT;
}
*off += write_count;
return write_count;
}
// 字符设备的打开函数
static int my_open(struct inode *inode, struct file *filp) {
// inode: 索引节点结构体指针,代表设备文件的索引节点
// filp: 文件结构体指针,代表当前打开的设备文件
// 初始化缓冲区
memset(buffer, 0, sizeof(buffer));
return 0;
}
// 字符设备的释放函数
static int my_release(struct inode *inode, struct file *filp) {
// inode: 索引节点结构体指针,代表设备文件的索引节点
// filp: 文件结构体指针,代表当前打开的设备文件
// 这里可以进行一些清理操作,例如关闭设备相关资源
return 0;
}
// 模块初始化函数
static int __init char_dev_init(void) {
int ret;
// 分配设备号
ret = alloc_chrdev_region(&dev_num, 0, 1, "my_char_dev");
if (ret) {
printk(KERN_ERR "Failed to allocate device number\n");
return ret;
}
// 初始化字符设备
cdev_init(&my_cdev, &my_fops);
my_cdev.owner = THIS_MODULE;
// 添加字符设备到系统
ret = cdev_add(&my_cdev, dev_num, 1);
if (ret) {
unregister_chrdev_region(dev_num, 1);
printk(KERN_ERR "Failed to add cdev\n");
return ret;
}
printk(KERN_INFO "Character device registered successfully\n");
return 0;
}
// 模块退出函数
static void __exit char_dev_exit(void) {
// 从系统移除字符设备
cdev_del(&my_cdev);
// 释放设备号
unregister_chrdev_region(dev_num, 1);
printk(KERN_INFO "Character device unregistered successfully\n");
}
module_init(char_dev_init);
module_exit(char_dev_exit);
MODULE_LICENSE("GPL");
17.3 块设备驱动开发实例
块设备以块为单位进行数据传输,通常支持随机访问,如硬盘设备。以下是一个简单的块设备驱动示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
// 定义设备号
dev_t dev_num;
// 定义块设备结构体
struct gendisk *my_disk;
// 定义请求队列
struct request_queue *my_queue;
// 块设备的请求处理函数
static void my_request_fn(struct request_queue *q) {
struct request *req;
// 循环处理请求队列中的请求
while ((req = elv_next_request(q))!= NULL) {
struct bio *bio;
// 遍历请求中的所有bio
for_each_bio(bio, req) {
sector_t sector = bio->bi_sector;
char *buffer = bio->bi_io_vec[0].bv_data;
unsigned int len = bio->bi_io_vec[0].bv_len;
// 这里模拟实际的I/O操作,例如根据扇区编号填充数据
for (unsigned int i = 0; i < len; i++) {
buffer[i] = sector + i;
}
// 标记bio完成
bio_endio(bio, 0);
}
// 标记请求完成
end_request(req, 0);
}
}
// 模块初始化函数
static int __init block_dev_init(void) {
int ret;
// 分配设备号
ret = alloc_chrdev_region(&dev_num, 0, 1, "my_block_dev");
if (ret) {
printk(KERN_ERR "Failed to allocate device number\n");
return ret;
}
// 创建请求队列
my_queue = blk_init_queue(my_request_fn, THIS_MODULE);
if (!my_queue) {
unregister_chrdev_region(dev_num, 1);
printk(KERN_ERR "Failed to create request queue\n");
return -ENOMEM;
}
// 创建gendisk结构体
my_disk = alloc_disk(16); // 假设设备有16个分区
if (!my_disk) {
blk_cleanup_queue(my_queue);
unregister_chrdev_region(dev_num, 1);
printk(KERN_ERR "Failed to allocate gendisk\n");
return -ENOMEM;
}
// 设置gendisk的主设备号和次设备号
my_disk->major = MAJOR(dev_num);
my_disk->first_minor = MINOR(dev_num);
// 设置gendisk的名称
sprintf(my_disk->disk_name, "my_disk");
// 设置gendisk的所有者为当前模块
my_disk->owner = THIS_MODULE;
// 设置gendisk的容量,这里假设为1024个扇区,每个扇区512字节
my_disk->capacity = 1024 * 512;
// 设置gendisk的请求队列
my_disk->queue = my_queue;
// 注册gendisk
add_disk(my_disk);
printk(KERN_INFO "Block device registered successfully\n");
return 0;
}
// 模块退出函数
static void __exit block_dev_exit(void) {
// 从系统移除gendisk
del_gendisk(my_disk);
// 释放gendisk结构体
put_disk(my_disk);
// 清理请求队列
blk_cleanup_queue(my_queue);
// 释放设备号
unregister_chrdev_region(dev_num, 1);
printk(KERN_INFO "Block device unregistered successfully\n");
}
module_init(block_dev_init);
module_exit(block_dev_exit);
MODULE_LICENSE("GPL");
17.4 网络设备驱动开发实例
网络设备驱动负责控制网络硬件设备,实现网络数据的发送和接收。以下是一个简单的网络设备驱动示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
// 定义网络设备结构体指针
struct net_device *my_netdev;
// 网络设备的发送函数
static netdev_tx_t my_netdev_xmit(struct sk_buff *skb, struct net_device *dev) {
// skb: 包含要发送数据的套接字缓冲区
// dev: 网络设备结构体指针
// 这里模拟实际的硬件发送操作,例如将skb数据发送到网络硬件
printk(KERN_INFO "Sending packet of length %zu\n", skb->len);
// 发送完成后释放套接字缓冲区
dev_kfree_skb(skb);
// 返回发送成功的标志
return NETDEV_TX_OK;
}
// 网络设备的接收函数(假设通过中断触发接收)
static void my_netdev_rx_interrupt(struct net_device *dev) {
// 这里模拟从硬件接收数据并创建套接字缓冲区
struct sk_buff *skb = dev_alloc_skb(2048);
if (!skb) {
printk(KERN_ERR "Failed to allocate skb for receiving\n");
return;
}
// 假设从硬件读取数据填充到skb中
// 这里省略实际的硬件读取操作
// 将接收到的数据传递给上层协议栈
netif_rx(skb);
}
// 网络设备的打开函数
static int my_netdev_open(struct net_device *dev) {
// 这里可以进行硬件相关的初始化操作,例如启动设备时钟
printk(KERN_INFO "Network device %s opened\n", dev->name);
return 0;
}
// 网络设备的关闭函数
static int my_netdev_close(struct net_device *dev) {
// 这里可以进行硬件相关的关闭操作,例如停止设备时钟
printk(KERN_INFO "Network device %s closed\n", dev->name);
return 0;
}
// 模块初始化函数
static int __init net_dev_init(void) {
my_netdev = alloc_netdev(0, "my_netdev", ether_setup);
if (!my_netdev) {
printk(KERN_ERR "Failed to allocate network device\n");
return -ENOMEM;
}
// 设置网络设备的打开和关闭回调函数
my_netdev->open = my_netdev_open;
my_netdev->stop = my_netdev_close;
// 设置网络设备的发送回调函数
my_netdev->hard_start_xmit = my_netdev_xmit;
// 设置网络设备的MAC地址,这里简单示例设置为全0
unsigned char mac_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
memcpy(my_netdev->dev_addr, mac_addr, ETH_ALEN);
// 注册网络设备
if (register_netdev(my_netdev)) {
free_netdev(my_netdev);
printk(KERN_ERR "Failed to register network device\n");
return -ENODEV;
}
printk(KERN_INFO "Network device registered successfully\n");
return 0;
}
// 模块退出函数
static void __exit net_dev_exit(void) {
// 注销网络设备
unregister_netdev(my_netdev);
// 释放网络设备结构体
free_netdev(my_netdev);
printk(KERN_INFO "Network device unregistered successfully\n");
}
module_init(net_dev_init);
module_exit(net_dev_exit);
MODULE_LICENSE("GPL");
17.5 USB设备驱动开发实例
USB设备驱动用于控制USB设备,实现设备的枚举、数据传输等功能。以下是一个简单的USB设备驱动示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
// 定义USB设备驱动结构体
static struct usb_driver my_usb_driver = {
.name = "my_usb_device",
.probe = my_probe,
.disconnect = my_disconnect,
.id_table = my_usb_ids,
};
// USB设备ID表,用于匹配设备
static struct usb_device_id my_usb_ids[] = {
{
.match_flags = USB_DEVICE_ID_VENDOR_AND_PRODUCT,
.idVendor = 0x1234, // 假设的厂商ID
.idProduct = 0x5678, // 假设的产品ID
},
{}
};
// 批量传输的缓冲区
char bulk_buffer[256];
// 批量传输的端点地址
static unsigned char bulk_in_endpoint;
static unsigned char bulk_out_endpoint;
// 当USB设备插入时调用的probe函数
static int my_probe(struct usb_interface *intf, const struct usb_device_id *id) {
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int i;
printk(KERN_INFO "My USB device inserted: vendor 0x%04x product 0x%04x\n", id->idVendor, id->idProduct);
interface = intf->cur_altsetting;
for (i = 0; i < interface->desc.bNumEndpoints; i++) {
endpoint = &interface->endpoint[i].desc;
if (usb_endpoint_is_bulk_in(endpoint)) {
bulk_in_endpoint = endpoint->bEndpointAddress;
} else if (usb_endpoint_is_bulk_out(endpoint)) {
bulk_out_endpoint = endpoint->bEndpointAddress;
}
}
// 这里可以进行其他初始化操作
return 0;
}
// 当USB设备拔出时调用的disconnect函数
static void my_disconnect(struct usb_interface *intf) {
printk(KERN_INFO "My USB device removed\n");
}
// 执行批量写操作的函数
static int my_bulk_write(struct usb_device *dev, const char *data, int len) {
int ret;
ret = usb_bulk_msg(dev, bulk_out_endpoint, (void *)data, len, NULL, 1000);
if (ret < 0) {
printk(KERN_ERR "Bulk write failed: %d\n", ret);
}
return ret;
}
// 执行批量读操作的函数
static int my_bulk_read(struct usb_device *dev, char *data, int len) {
int ret;
ret = usb_bulk_msg(dev, bulk_in_endpoint, data, len, NULL, 1000);
if (ret < 0) {
printk(KERN_ERR "Bulk read failed: %d\n", ret);
}
return ret;
}
// 模块初始化函数
static int __init usb_dev_init(void) {
// 注册USB设备驱动
if (usb_register(&my_usb_driver)) {
printk(KERN_ERR "Failed to register USB driver\n");
return -1;
}
printk(KERN_INFO "USB driver registered successfully\n");
return 0;
}
// 模块退出函数
static void __exit usb_dev_exit(void) {
// 注销USB设备驱动
usb_deregister(&my_usb_driver);
printk(KERN_INFO "USB driver unregistered successfully\n");
}
module_init(usb_dev_init);
module_exit(usb_dev_exit);
MODULE_LICENSE("GPL");
17.6 PCI设备驱动开发实例
PCI设备驱动用于控制PCI设备,实现设备的枚举、配置空间访问等功能。以下是一个简单的PCI设备驱动示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
// 定义PCI设备驱动结构体
static struct pci_driver my_pci_driver = {
.name = "my_pci_device",
.probe = my_probe,
.remove = my_remove,
.id_table = my_pci_ids,
};
// PCI设备ID表,用于匹配设备
static struct pci_device_id my_pci_ids[] = {
{
.vendor = 0x1234,
.device = 0x5678,
},
{}
};
// 当PCI设备插入时调用的probe函数
static int my_probe(struct pci_dev *pdev, const struct pci_device_id *id) {
u16 vendor_id, device_id;
u3
标签:struct,17,dev,static,Linux,device,驱动,my,设备
From: https://blog.csdn.net/qq_40844444/article/details/145024201