LCD驱动中的xxx_probe()函数

本文详细介绍了IMX6ULL平台的LCD驱动实现过程,包括fb_info结构体的申请与初始化、eLCDIF控制器的配置及Framebuffer的注册流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LCD驱动是一个标准的platform驱动。当驱动和设备匹配以后,mxsfb_probe 函数就会执行。
mxsfb_probe 函数的主要工作是:
1)、申请fb_info。

framebuffer_alloc()

2)、初始化fb_info结构体中,各个成员变量。
3)、初始化eLCDIF控制器。
4)、使用register_frambebuffer函数向Linux内核注册初始化好的fb_info结构体。
mxsfb_probe 函数中,重要函数/结构体的简要的说明:
1)、
Linux内核将所有的Framebuffer抽象为一个叫fb_info的结构体,fb_info结构体里面包含Framerbuffer设备完整的属性和操作集合,因此,每一个Framebuffer都必须有一个fb_info结构体。换言之,就是lcd驱动,实质上,就是构建fb_info结构体,并且向系统注册fb_info结构体的过程。

struct fb_info {
……
   struct fb_var_screeninfo var; /* 当前可变参数 */
struct fb_fix_screeninfo fix; /* 当前固定参数 */
   struct fb_ops *fbops; /* 帧缓冲操作函数集 */
   char __iomem *screen_base; /* 虚拟内存基地址(屏幕显存) */
unsigned long screen_size; /* 虚拟内存大小(屏幕显存大小) */
void *pseudo_palette; /* 伪 16 位调色板 */
......
};

这个结构体里面的成员变量很多,重要的有如上六个。
2)、register_framebuffer 函数

int register_framebuffer(struct fb_info *fb_info)

3)、mxsfb_probe 函数的主要梳理:
①、

struct mxsfb_info *host;

定义结构体指针变量host,表示IMX6ULL的LCD主控接口,这个结构体是自定义的,里面的成员变量记录了Framebuffer 设备详细信息,比如时钟、 eLCDIF控制器寄存器基地址、 fb_info(struct fb_info *fb_info) 等
②、

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

从设备树中,获取eLCDIF接口控制器的寄存器首地址。寄存器首地址信息,已经在设备树中的,对应的lcd节点中设置了。

lcdif: lcdif@021c8000 {
		compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";
	。。。。。。
	};

③、给host、fb_info申请内存

host = devm_kzalloc(&pdev->dev, sizeof(struct mxsfb_info), GFP_KERNEL);
fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);

④、通过这一步就将前面申请的 host 和 fb_info 联系在了一起。

host->fb_info = fb_info;
fb_info->par = host;  

⑤、对从设备树中获取到的寄存器首地址(res)进行内存映射,得到虚拟地址,并保存到host的base成员变量里面。之后,只要通过访问 host 的 base 成员即可访问 I.MX6ULL 的整个 eLCDIF寄存器。

host->base = devm_ioremap_resource(&pdev->dev, res);

⑥、给pseudo_palette申请内存。

fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16,GFP_KERNEL);

⑦、调用 mxsfb_init_fbinfo 函数初始化 fb_info,重点是 fb_info 的 var、 fix、 fbops(操作集),screen_base 和 screen_size。

mxsfb_init_fbinfo(host);
//从设备树中,获取LCD各个参数的信息
    -mxsfb_init_fbinfo_dt(host);
//申请LCD的帧缓冲内存(就是缓存)
    -mxsfb_map_videomem(fb_info)

类似于字符设备操作集file_operations(这里面的函数,已经由芯片厂商写好):

static struct fb_ops mxsfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = mxsfb_check_var,
.fb_set_par = mxsfb_set_par,
.fb_ioctl = mxsfb_ioctl,
.fb_blank = mxsfb_blank,
.fb_pan_display = mxsfb_pan_display,
.fb_mmap = mxsfb_mmap,
……
};

⑧、设置eLCDIF控制器相应的寄存器

mxsfb_set_par(fb_info); //函数里面包含大量的writel()函数
eg:
 {   writel(CTRL1_FIFO_CLEAR, host->base + REG_SET);
     writel(ctrl, host->base + LCDC_CTRL);  ……   }
mxsfb_enable_controller(fb_info);  //同上(大量的writel())

⑨、调用 register_framebuffer 函数向 Linux 内核注册 fb_info

<think>嗯,用户这次问的是Linux内核开发中unlocked_ioctl和compat_ioctl的区别和使用方法。从引用内容看,用户已经查阅了相关资料,但需要更系统化的解释。用户可能是内核驱动开发者,正面临兼容性处理问题。注意到用户引用了三个关键资料:引用1提到内核2.6.35后淘汰了传统ioctl;引用2说明内核社区对ioctl的态度;引用3展示了驱动代码中的典型用法。用户真正需要的是实践指导而非理论概述。之前用户的问题集中在系统编程接口演变,这次更聚焦具体函数实现差异。需要特别注意:1)锁机制差异必须强调BigKernelLock的淘汰背景;2)32/64位兼容性问题要举出指针转换实例;3)必须澄清常见误解——compat_ioctl不只服务于32位用户空间。用户代码片段drv_ioctl同时挂到两个钩子,这很典型但需提醒CONFIG_COMPAT条件编译的风险。还应该补充注意事项,比如cmd编号定义技巧(避免混用旧版命令)。用户明显需要立即应用到代码中,所以示例比理论更重要。要不要提ioctl替代方案?比如sysfs或netlink?虽然用户没问,但引用2提到内核开发者不推荐ioctl,或许值得在注意事项暗示。不过当前问题焦点还是在这两个接口区别上。</think> 理解正确,`unlocked_ioctl` 和 `compat_ioctl` 是 Linux 内核驱动程序 `struct file_operations` 中的关键方法接口。它们主要用于处理设备相关的控制命令(`ioctl`)。以下是它们的核心区别和使用方法: --- ### **1. `unlocked_ioctl`(核心接口)** * **用途**:处理设备控制命令的主接口。 * **参数**: ```c long (*unlocked_ioctl)(struct file *file, unsigned int cmd, unsigned long arg); ``` - `file`:打开的文件描述符 - `cmd`:用户空间传递的命令号 - `arg`:命令参数(用户空间指针) * **特点**: - **无大内核锁(BKL)**:与旧版 `ioctl` 不同,它无需持有 BKL(Big Kernel Lock),因此性能更好且支持细粒度锁策略[^1][^2]。 - **运行在调用进程的上下文**:可直接访问用户空间内存(需用 `copy_from_user()`/`copy_to_user()`)。 - **现代驱动必须实现**:自内核 2.6.35 后,它是所有新型驱动程序的默认接口。 --- ### **2. `compat_ioctl`(兼容接口)** * **用途**:处理 **32位用户程序** 在 **64位内核** 上发出的 `ioctl` 请求。 * **参数**: ```c long (*compat_ioctl)(struct file *file, unsigned int cmd, unsigned long arg); ``` - 参数形式与 `unlocked_ioctl` 相同,但 `arg` 是 **32位地址**。 * **核心问题**: - 当 64 位内核运行 32 位程序时,用户空间指针为 32 位(4 字节),但内核期待 64 位指针。 - 若不加处理,直接传递 `arg` 给 `unlocked_ioctl` 会导致指针截断错误。 * **解决方案**: - 实现 `compat_ioctl`,在其中将 32 位地址转换并复制到内核可识别的 64 位结构体。 - 使用兼容函数如 `compat_ptr(arg)` 将 `arg` 转为有效 64 位指针。 - 需为每个 `cmd` 额外实现兼容数据处理(如定义 `struct foo_compat`)。 --- ### **3. 关键区别总结** | **特性** | `unlocked_ioctl` | `compat_ioctl` | |----------------------|--------------------------------|--------------------------------| | 目标用户空间 | 所有程序(原生架构) | 32 位程序(在 64 位内核运行) | | `arg` 指针大小 | 原生地址大小(64 位系统为 8 字节) | 固定 4 字节(32 位地址) | | 是否需数据转换 | 否 | 是(需处理 32/64 位数据差异) | | BKL 锁 | 不使用 | 不使用 | | 内核版本要求 | ≥ 2.6.11 | 需开启 `CONFIG_COMPAT` | --- ### **4. 驱动实现示例** ```c // 处理通用 ioctl 请求 long my_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case MY_CMD_1: // 处理原生数据(64 位或 32 位内核上的本地程序) struct data d; copy_from_user(&d, (void __user *)arg, sizeof(d)); // ... 执行操作 break; // ... 其他命令 } return 0; } // 处理 32 位程序的 ioctl 请求 #ifdef CONFIG_COMPAT long my_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case MY_CMD_1: // 1. 将 arg 转为兼容指针 struct compat_data __user *udata32 = compat_ptr(arg); // 2. 定义 32 位兼容结构体 struct compat_data d32; // 3. 从 32 位用户空间拷贝数据 copy_from_user(&d32, udata32, sizeof(d32)); // 4. 将数据转换为原生格式(例如扩宽整数) struct data d = { .field1 = d32.field1, // ... 其他字段转换 }; // 5. 复用通用处理逻辑 return my_unlocked_ioctl(file, cmd, (unsigned long)&d); break; } return -ENOTTY; } #endif struct file_operations fops = { .owner = THIS_MODULE, .unlocked_ioctl = my_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = my_compat_ioctl, // 注册兼容接口 #endif }; ``` --- ### **5. 注意事项** 1. **命令号兼容性**: - 使用 `_IOC()`/`_IOR()` 等宏定义 `cmd` 时需确保其数值在 32/64 位架构下一致。 - 可显式指定命令号类型为 `unsigned int`。 2. **数据对齐差异**: - 32 位结构体可能有不同的对齐方式(例如 `u64` 在 32 位系统可能为 4 字节对齐)。 - 使用 `__attribute__((packed))` 或内核提供的兼容数据类型(如 `compat_ulong_t`)。 3. **可选实现**: - 若驱动不涉及指针或数据结构变化(如只传递整数的命令),可省略 `compat_ioctl`。 - 若不实现 `compat_ioctl`,内核会尝试将 32 位参数直接传递给 `unlocked_ioctl`,**可能导致崩溃或数据错误!** 4. **优先使用标准框架**: - 对于常规设备控制,推荐优先使用 `sysfs`、`debugfs` 或 `netlink` 等更现代的内核接口(替代过度依赖 `ioctl`)[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

One Piece&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值