不允许修改文件定位偏移量nonseekable_open

本文探讨了字符设备如LEDs中open函数的nonseekable_open特性,解释了为何某些设备如串口和键盘不适合使用seek操作,以及这背后的内存安全考量。

       在字符字符设备中的open函数中(例如leds_open())常常会看到nonseekable_open(inode, file);这个函数,意思是不允许重定位文件的指针。

        在C语言中有函数feek()和ftell()函数,能够让我们想对待数组那样对待一个文件,在fopen()打开的文件中直接移动到任意字节处。但像串口或键盘一类设备,使用的是数据流,所以定位这些设备没有意义,有可能还会引起内存错误,所以要拒绝使用seek()函数。这样即使在file_operations中声明了llseek()函数时,也不能使用。

参考博客:

https://blog.youkuaiyun.com/gongmin856/article/details/8273545

https://blog.youkuaiyun.com/sybnfkn040601/article/details/73718332

#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/ktime.h> #include <linux/reboot.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/clk.h> #include <linux/spinlock.h> #include <linux/timer.h> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt // 看门狗寄存器定义 #define INTERNAL_DOG_BASE_ADDR 0xaaaa0000 #define INTERNAL_DOG_OFFSET 0x1234 #define FEED_DOG_BASE_ADDR 0x1a000 #define FEED_DOG_OFFSET 0x011 #define WCV_L_OFFSET 0x00042 #define WCV_H_OFFSET 0x00023 #define WOR_OFFSET 0x00065 #define WRR_OFFSET 0x10011 #define WCS_REG_OFFSET 0x0 // 看门狗状态位 #define WDT_WS0 (1 << 0) #define WDT_WS1 (1 << 1) // 看门狗使能寄存器 static unsigned long wdt_is_open; static char expect_close; static int heartbeat = 60; module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default=60)"); static bool nowayout = false; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=0)"); struct wdt_device { struct watchdog_device wdd; struct device *dev; void __iomem *base; struct clk *clk; int irq; unsigned long status; bool nowayout; u32 wdog_offset_value; u64 last_feed_time; u64 timeout_ns; u64 last_feed; u32 wcs_value; u32 wor_value; spinlock_t lock; // 添加自旋锁保护共享资源 }; static int wdog_start(struct watchdog_device *wdd) { struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); iowrite32(0x1, wd->base + INTERNAL_DOG_OFFSET); return 0; } static int wdog_stop(struct watchdog_device *wdd) { struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); iowrite32(0x0, wd->base + INTERNAL_DOG_OFFSET); return 0; } static int wdog_feed(struct watchdog_device *wdd) { u64 current_time; u64 new_compare_value; unsigned long flags; struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); spin_lock_irqsave(&wd->lock, flags); iowrite32(0x1, wd->base + FEED_DOG_OFFSET); current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); return 0; } static u32 read_WRR(struct wdt_device *wd) { u32 wrr_value; u64 current_time; u64 new_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); wrr_value = ioread32(wd->base + WRR_OFFSET); current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); return wrr_value; } static void write_WOR(struct wdt_device *wd, u32 value) { u64 current_time; u64 new_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); iowrite32(value, wd->base + WOR_OFFSET); wd->wor_value = value; current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); } static void write_WCS(struct wdt_device *wd, u32 value) { u64 current_time; u64 new_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); iowrite32(value, wd->base + WCS_REG_OFFSET); wd->wcs_value = value; current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); } static void write_WCV(struct wdt_device *wd, u64 new_compare_value) { u64 current_time; u64 final_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); current_time = ktime_get_ns(); if (new_compare_value > current_time) { final_compare_value = new_compare_value; } else { final_compare_value = current_time + wd->wdog_offset_value; } iowrite32((u32)(final_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(final_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); } static irqreturn_t wdt_interrupt(int irq, void *dev_id) { struct wdt_device *wd = dev_id; u32 status; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); // 读取状态寄存器 status = ioread32(wd->base + WCS_REG_OFFSET); // 检查状态位 if (!(status & WDT_WS0)) { status |= WDT_WS0; // 设置WS0位 iowrite32(status, wd->base + WCS_REG_OFFSET); } else if ((status & WDT_WS0) && !(status & WDT_WS1)) { status |= WDT_WS1; // 设置WS1位 iowrite32(status, wd->base + WCS_REG_OFFSET); spin_unlock_irqrestore(&wd->lock, flags); emergency_restart(); // 触发系统重启 return IRQ_HANDLED; } spin_unlock_irqrestore(&wd->lock, flags); return IRQ_HANDLED; } // 实现文件操作函数 static int wdt_open(struct inode *inode, struct file *file) { struct wdt_device *wd = container_of(file->private_data, struct wdt_device, wdd); if (test_and_set_bit(0, &wdt_is_open)) return -EBUSY; file->private_data = wd; return nonseekable_open(inode, file); } static ssize_t wdt_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { return 0; } static ssize_t wdt_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wdt_device *wd = file->private_data; if (len) { if (!nowayout) { size_t i; expect_close = 0; for (i = 0; i != len; i++) { char c; if (get_user(c, buf + i)) return -EFAULT; if (c == 'V') expect_close = 42; } } wdog_feed(&wd->wdd); } return len; } static int wdt_release(struct inode *inode, struct file *file) { struct wdt_device *wd = file->private_data; if (expect_close == 42) { wdog_stop(&wd->wdd); } else { pr_crit("Unexpected close, not stopping watchdog!\n"); wdog_feed(&wd->wdd); } clear_bit(0, &wdt_is_open); expect_close = 0; return 0; } static long wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd, unsigned long arg) { struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); int ret = 0; int time_val; void __user *argp = (void __user *)arg; int __user *p = argp; switch (cmd) { case WDIOC_KEEPALIVE: wdog_feed(wdd); break; case WDIOC_SETTIMEOUT: if (get_user(time_val, p)) return -EFAULT; heartbeat = time_val; wdd->timeout = heartbeat; wd->wdog_offset_value = (u64)heartbeat * NSEC_PER_SEC; wdog_feed(wdd); // 更新超时设置后立即喂狗 break; case WDIOC_GETTIMEOUT: return put_user(wdd->timeout, p); case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: if (get_user(time_val, p)) return -EFAULT; if (time_val & WDIOS_DISABLECARD) ret = wdog_stop(wdd); if (time_val & WDIOS_ENABLECARD) ret = wdog_start(wdd); break; default: ret = -ENOTTY; break; } return ret; } static const struct watchdog_info wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "Watchdog Timer", }; static const struct watchdog_ops wdt_ops = { .owner = THIS_MODULE, .start = wdog_start, .stop = wdog_stop, .ping = wdog_feed, .ioctl = wdt_ioctl, }; static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = wdt_open, .read = wdt_read, .write = wdt_write, .release = wdt_release, .unlocked_ioctl = wdt_ioctl, }; static int wdt_probe(struct platform_device *pdev) { struct wdt_device *wd; struct resource *res; int ret; u64 current_time; u64 initial_compare_value; u32 wcs_readback; wd = devm_kzalloc(&pdev->dev, sizeof(*wd), GFP_KERNEL); if (!wd) return -ENOMEM; // 获取内存资源 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "No memory resource\n"); return -ENODEV; } // 映射寄存器区域 wd->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(wd->base)) { dev_err(&pdev->dev, "Failed to map registers\n"); return PTR_ERR(wd->base); } // 初始化自旋锁 spin_lock_init(&wd->lock); // 获取中断 wd->irq = platform_get_irq(pdev, 0); if (wd->irq < 0) { dev_err(&pdev->dev, "No IRQ resource\n"); return wd->irq; } // 注册中断处理函数 ret = devm_request_irq(&pdev->dev, wd->irq, wdt_interrupt, IRQF_SHARED, "watchdog", wd); if (ret) { dev_err(&pdev->dev, "Failed to request IRQ\n"); return ret; } // 初始化看门狗寄存器 wd->wcs_value = ioread32(wd->base + WCS_REG_OFFSET); wd->wcs_value &= ~(1 << 0); // 禁用看门狗 iowrite32(wd->wcs_value, wd->base + WCS_REG_OFFSET); // 设置超时值 (纳秒) wd->wdog_offset_value = (u64)heartbeat * NSEC_PER_SEC; wd->wor_value = wd->wdog_offset_value; iowrite32(wd->wor_value, wd->base + WOR_OFFSET); // 设置比较值 current_time = ktime_get_ns(); initial_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(initial_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(initial_compare_value >> 32), wd->base + WCV_H_OFFSET); // 启用看门狗 wd->wcs_value |= (1 << 0); iowrite32(wd->wcs_value, wd->base + WCS_REG_OFFSET); wcs_readback = ioread32(wd->base + WCS_REG_OFFSET); wd->wcs_value = wcs_readback; // 初始化看门狗设备结构 wd->wdd.ops = &wdt_ops; wd->wdd.info = &wdt_info; wd->wdd.timeout = heartbeat; wd->wdd.min_timeout = 1; wd->wdd.max_timeout = 3600; wd->dev = &pdev->dev; wd->wdd.parent = &pdev->dev; wd->nowayout = nowayout; // 注册看门狗设备 ret = devm_watchdog_register_device(&pdev->dev, &wd->wdd); if (ret) { dev_err(&pdev->dev, "Failed to register watchdog device (err=%d)\n", ret); return ret; } // 设置驱动数据 platform_set_drvdata(pdev, wd); dev_info(&pdev->dev, "WDT driver initialized (heartbeat=%d sec, nowayout=%d)\n", heartbeat, nowayout); return 0; } static int wdt_remove(struct platform_device *pdev) { struct wdt_device *wd = platform_get_drvdata(pdev); // 停止看门狗 wdog_stop(&wd->wdd); dev_info(&pdev->dev, "Watchdog driver removed\n"); return 0; } #ifdef CONFIG_PM_SLEEP static int wdt_suspend(struct device *dev) { struct wdt_device *wd = dev_get_drvdata(dev); // 禁用看门狗 wdog_stop(&wd->wdd); return 0; } static int wdt_resume(struct device *dev) { struct wdt_device *wd = dev_get_drvdata(dev); // 恢复看门狗 wdog_start(&wd->wdd); wdog_feed(&wd->wdd); return 0; } static const struct dev_pm_ops wdt_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(wdt_suspend, wdt_resume) }; #endif static const struct of_device_id wdt_match[] = { { .compatible = "a,watchdog" }, {}, }; MODULE_DEVICE_TABLE(of, wdt_match); static struct platform_driver wdt_driver = { .probe = wdt_probe, .remove = wdt解释一下这个代码,每一步都在干什么
最新发布
12-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值