学习笔记——Linux IOCTL与平台设备驱动
·
Linux IOCTL与平台设备驱动
一、IOCTL机制详解
1.1 IOCTL函数基本使用
// 用户空间调用 ioctl(fd, cmd, arg); // 参数说明: // fd: 文件描述符 // cmd: 命令码(32位整数) // arg: 参数(用户空间传递的数据)
1.2 命令码(cmd)的32位结构
32位命令码的组成: | 31 24 | 23 16 | 15 14 | 13 0 | |------------|------------|----------|--------------| | type | nr | dir | size | | 8位 | 8位 | 2位 | 14位 | 各部分含义: 1. type(8位):设备类型/魔幻数(Magic Number) - 代表设备类型,防止命令冲突 - 常用字母表示,如 'x', 'l', 'k' 等 2. nr(8位):命令编号 - 设备内的具体命令编号 - 0-255,定义具体的操作 3. dir(2位):数据流向 - 00: 无数据传输 - 01: 读操作(从驱动到用户) - 10: 写操作(从用户到驱动) - 11: 读写操作 4. size(14位):参数大小 - arg参数的大小(字节数) - 0-16383字节
1.3 命令码宏定义
// 内核头文件中的宏 #define _IO(type,nr) // 无数据传输的命令 #define _IOR(type,nr,size) // 读操作命令 #define _IOW(type,nr,size) // 写操作命令 #define _IOWR(type,nr,size) // 读写操作命令 // 实际使用示例 #define MAGIC_NUM 'x' // 魔幻数(设备类型) #define LED_ON 0 // 命令编号 #define LED_OFF 1 // 命令编号 #define CMD_LED_ON _IO(MAGIC_NUM, LED_ON) // 无数据传输 #define CMD_LED_OFF _IO(MAGIC_NUM, LED_OFF) // 无数据传输
二、LED驱动示例代码分析
2.1 内核驱动代码(led_ioctl.c)
2.1.1 头文件包含
#include <linux/init.h> // 模块初始化和退出 #include <linux/printk.h> // 内核打印函数 #include <linux/fs.h> // 文件系统操作 #include <linux/export.h> // 符号导出 #include <linux/types.h> // 类型定义 #include <asm/uaccess.h> // 用户空间数据拷贝 #include <asm/io.h> // IO内存操作 #include <asm/string.h> // 字符串操作 #include <linux/miscdevice.h>// 杂项设备 #include <linux/module.h> // 模块相关
2.1.2 硬件寄存器地址定义
#define DEV_NAME "led" #define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 0x020E0068 // 复用控制寄存器 #define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 0x020E02F4 // 电气特性寄存器 #define GPIO1_GDIR 0x0209C004 // GPIO方向寄存器 #define GPIO1_DR 0x0209C000 // GPIO数据寄存器
2.1.3 硬件操作函数
// LED初始化
static void led1_init(void)
{
*iomuxc_mux_ctl = 0x05; // 设置复用功能为GPIO
*iomuxc_pad_ctl = 0x10B0; // 设置电气特性
*gpio1_gdir |= (1 << 3); // 设置GPIO1_IO03为输出模式
}
// 打开LED
static void led_on(void)
{
*gpio1_dr &= ~(1 << 3); // GPIO输出低电平,LED亮
}
// 关闭LED
static void led_off(void)
{
*gpio1_dr |= (1 << 3); // GPIO输出高电平,LED灭
}
2.1.4 文件操作函数
// 打开设备
static int open(struct inode *inode, struct file *file)
{
led1_init(); // 初始化LED硬件
printk("led open\n");
return 0;
}
// 读取设备
static ssize_t read(struct file *file, char __user *buf, size_t size, loff_t *loff)
{
printk("led read\n");
return 0; // 这里没有实现实际读取功能
}
// 写入设备
static ssize_t write(struct file *file, const char __user *buf, size_t size, loff_t *loff)
{
char data[20] = {0};
long len = sizeof(data) < size ? sizeof(data) : size;
long ret = copy_from_user(data, buf, len); // 从用户空间拷贝数据
if(!strcmp(data, "led_on"))
led_on(); // 写入"led_on"字符串,打开LED
else if(!strcmp(data, "led_off"))
led_off(); // 写入"led_off"字符串,关闭LED
else
ret = -EINVAL; // 无效命令
printk("led write\n");
return ret;
}
// IOCTL控制
static long ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
long ret = 0;
printk("cmd = %d args = %ld\n", cmd, args);
switch(cmd)
{
case CMD_LED_ON:
led_on(); // 收到LED_ON命令,打开LED
break;
case CMD_LED_OFF:
led_off(); // 收到LED_OFF命令,关闭LED
break;
default:
ret = -EINVAL; // 无效命令
break;
}
return ret;
}
// 关闭设备
static int close(struct inode *inode, struct file *file)
{
led_off(); // 关闭时自动关闭LED
printk("led close\n");
return 0;
}
2.1.5 文件操作结构体
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = open,
.read = read,
.write = write,
.unlocked_ioctl = ioctl, // IOCTL处理函数
.release = close
};
2.1.6 杂项设备定义
static struct miscdevice misc_dev =
{
.minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号
.name = DEV_NAME, // 设备名
.fops = &fops // 文件操作函数
};
2.1.7 模块初始化和退出
// 模块初始化
static int __init led_init(void)
{
int ret = misc_register(&misc_dev); // 注册杂项设备
if(ret)
goto err_misc_register;
// 映射硬件寄存器到虚拟地址
iomuxc_mux_ctl = ioremap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03, 4);
iomuxc_pad_ctl = ioremap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03, 4);
gpio1_gdir = ioremap(GPIO1_GDIR, 4);
gpio1_dr = ioremap(GPIO1_DR, 4);
printk("######################### misc led_init\n");
return 0;
err_misc_register:
printk("misc led_init failed ret = %d\n", ret);
return ret;
}
// 模块退出
static void __exit led_exit(void)
{
// 取消映射
iounmap(iomuxc_mux_ctl);
iounmap(iomuxc_pad_ctl);
iounmap(gpio1_gdir);
iounmap(gpio1_dr);
misc_deregister(&misc_dev); // 注销设备
printk("######################### led_exit\n");
}
2.2 用户空间应用程序(led_ioctl_app.c)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
// 定义与内核驱动相同的命令码
#define MAGIC_NUM 'x'
#define LED_ON 0
#define LED_OFF 1
#define CMD_LED_ON _IO(MAGIC_NUM, LED_ON)
#define CMD_LED_OFF _IO(MAGIC_NUM, LED_OFF)
int main(int argc, const char *argv[])
{
int fd = open("/dev/led", O_RDWR); // 打开设备文件
if(fd < 0)
{
perror("open led failed");
return 1;
}
while(1)
{
int ret = 0;
ioctl(fd, CMD_LED_ON, 100); // 发送LED_ON命令,参数100
sleep(1);
ioctl(fd, CMD_LED_OFF, 200); // 发送LED_OFF命令,参数200
sleep(1);
ret = ioctl(fd, 4, 400); // 发送无效命令4
if(ret < 0)
{
perror("ioctl led"); // 会打印错误,因为命令4无效
}
sleep(1);
}
close(fd);
return 0;
}
三、平台设备驱动架构
3.1 设备驱动结构
设备驱动 = 操作方法 + 设备资源 操作方法: open, read, write, ioctl等函数 设备资源: 寄存器地址、中断号、内存区域等
3.2 总线类型(bus_type)
总线类型包括: - I2C总线 - SPI总线 - Platform总线(平台总线)
3.3 设备匹配机制
设备树: 驱动: key ←→ key ←→ 匹配 → probe → 注册字符设备驱动 led ←→ led adc ←→ adc 当driver或device卸载时执行remove函数 → 注销字符设备驱动
3.4 关键结构体
3.4.1 platform_device结构体
struct platform_device {
const char *name; // 设备名称,用于和driver匹配
int id; // 设备ID
bool id_auto;
struct device dev; // 设备结构体
u32 num_resources; // 资源数量
struct resource *resource; // 资源数组
const struct platform_device_id *id_entry;
char *driver_override; // 强制匹配的驱动名
struct mfd_cell *mfd_cell;
struct pdev_archdata archdata; // 架构特定数据
};
3.4.2 resource结构体(设备资源)
struct resource {
resource_size_t start; // 资源起始地址
resource_size_t end; // 资源结束地址
const char *name; // 资源名称
unsigned long flags; // 资源标志
struct resource *parent, *sibling, *child; // 资源树
};
3.4.3 platform_driver结构体
struct platform_driver {
int (*probe)(struct platform_device *); // 探测函数
int (*remove)(struct platform_device *); // 移除函数
void (*shutdown)(struct platform_device *); // 关闭函数
int (*suspend)(struct platform_device *, pm_message_t state); // 挂起
int (*resume)(struct platform_device *); // 恢复
struct device_driver driver; // 驱动结构体,包含name字段
const struct platform_device_id *id_table; // ID表
bool prevent_deferred_probe; // 防止延迟探测
};
3.5 注册和注销函数
// 注册平台设备
extern int platform_device_register(struct platform_device *);
// 注销平台设备
extern void platform_device_unregister(struct platform_device *);
// 注册平台驱动
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
extern int __platform_driver_register(struct platform_driver *, struct module *);
// 注销平台驱动
extern void platform_driver_unregister(struct platform_driver *);
四、驱动开发流程总结
4.1 IOCTL驱动开发步骤
-
定义命令码:使用_IO、_IOR、_IOW、_IOWR宏
-
实现ioctl函数:在file_operations中设置.unlocked_ioctl
-
处理命令:在ioctl函数中使用switch-case处理不同命令
-
用户空间调用:使用与内核相同的命令码调用ioctl
4.2 平台驱动开发步骤
-
定义设备资源:使用resource结构体描述硬件资源
-
定义平台设备:使用platform_device结构体
-
实现驱动函数:probe、remove等函数
-
注册平台驱动:使用platform_driver_register
4.3 关键要点
-
魔幻数:防止不同设备间的命令冲突
-
命令编号:0-255,定义具体操作
-
数据流向:dir位控制数据传输方向
-
设备匹配:通过name字段匹配设备和驱动
-
资源管理:使用resource结构体描述硬件资源
-
内存映射:使用ioremap映射物理地址到虚拟地址
更多推荐

所有评论(0)