首页 程序笔记 通过ioctl操作硬件端口的Linux内核模块代码

通过ioctl操作硬件端口的Linux内核模块代码

前言

在Linux中,如果要对特定的硬件端口进行操作,用户空间是没有足够的权限的,可以在内核模块中实现端口的读写操作,然后用户空间中的程序通过内核模块的ioctl进行操作,相关的代码实现和操作记录备忘。

内核模块代码

以下是一个简单的Linux内核驱动,可以实现对特定IO端口进行读写操作。这个驱动可以将一个整数写入端口,并从端口读取一个整数,并将结果返回给用户空间。具体实现如下:

#include linux/init.h
#include linux/module.h
#include linux/kernel.h
#include linux/fs.h
#include asm/uaccess.h
#include linux/cdev.h
#include linux/ioctl.h
#include asm/io.h

#define DRIVER_NAME my_port_driver
#define IOCTL_WRITE_PORT _IOW(k, 1, int)
#define IOCTL_READ_PORT _IOR(k, 2, int)

MODULE_LICENSE(GPL);

static int my_port_driver_open(struct inode *inode, struct file *file);
static int my_port_driver_release(struct inode *inode, struct file *file);
static long my_port_driver_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

static int port = 0x378;

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = my_port_driver_open,
    .release = my_port_driver_release,
    .unlocked_ioctl = my_port_driver_ioctl,
};

static dev_t dev;
static struct cdev my_cdev;

static int my_port_driver_init(void) {
    int ret = alloc_chrdev_region(dev, 0, 1, DRIVER_NAME);
    if (ret) {
        printk(KERN_ERR Failed to allocate char device region\n);
        return ret;
    }

    cdev_init(my_cdev, fops);
    my_cdev.owner = THIS_MODULE;

    ret = cdev_add(my_cdev, dev, 1);
    if (ret) {
        printk(KERN_ERR Failed to add char device\n);
        unregister_chrdev_region(dev, 1);
        return ret;
    }

    printk(KERN_INFO my_port_driver loaded\n);

    return 0;
}

static void my_port_driver_exit(void) {
    cdev_del(my_cdev);
    unregister_chrdev_region(dev, 1);
    printk(KERN_INFO my_port_driver unloaded\n);
}

static int my_port_driver_open(struct inode *inode, struct file *file) {
    return 0;
}

static int my_port_driver_release(struct inode *inode, struct file *file) {
    return 0;
}

static long my_port_driver_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    int tmp;

    switch (cmd) {
        case IOCTL_WRITE_PORT:
            if (copy_from_user(tmp, (int *)arg, sizeof(int))) {
                return -EFAULT;
            }

            outb(tmp, port);
            break;
        case IOCTL_READ_PORT:
            tmp = inb(port);

            if (copy_to_user((int *)arg, tmp, sizeof(int))) {
                return -EFAULT;
            }

            break;
        default:
            return -ENOTTY;
    }

    return 0;
}

module_init(my_port_driver_init);
module_exit(my_port_driver_exit);

这个驱动会注册一个字符设备,并且实现了open、release和ioctl三个函数。在用户空间,可以使用ioctl系统调用来调用驱动中的ioctl函数。这个驱动实现了两个命令:IOCTL_WRITE_PORT和IOCTL_READ_PORT。前者会将一个整数写入端口,后者会从端口读取一个整数。这个驱动代码中的inb和outb函数是内联汇编实现的,用于向端口发送和接收数据。inb函数会从指定端口读取一个字节,而outb函数会将一个字节写入指定端口。port变量表示要读写的IO端口的地址。需要注意的是,这个驱动没有进行足够的错误处理,例如没有检查用户空间指针是否合法。在实际的驱动开发中,需要进行更严格的错误处理。

如何使用

驱动编译时需要使用Linux内核源代码中提供的Makefile文件进行编译。具体编译步骤如下:

  1. 编写驱动代码并保存到文件中(例如my_port_driver.c)。
  2. 在Linux内核源代码目录中创建一个Kbuild文件,内容如下:
obj-m := my_port_driver.o
  1. 在Linux内核源代码目录中打开终端,执行以下命令进行编译:
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

其中(unamer)表示当前内核版本,(uname -r)表示当前内核版本,(unamer)(pwd)表示当前目录。
4. 如果编译成功,会生成一个my_port_driver.ko文件。
加载驱动模块:

sudo insmod my_port_driver.ko

查看内核日志,确认驱动已经加载成功:

dmesg

可以使用ioctl系统调用进行读写操作。例如,向端口0x378写入数值100:

#include stdio.h
#include fcntl.h
#include sys/ioctl.h

#define DEVICE_NAME /dev/my_port_driver
#define IOCTL_WRITE_PORT _IOW(k, 1, int)

int main() {
    int fd = open(DEVICE_NAME, O_RDWR);

    if (fd  0) {
        perror(Failed to open device file);
        return -1;
    }

    int value = 100;
    if (ioctl(fd, IOCTL_WRITE_PORT, value)  0) {
        perror(Failed to write to port);
        return -1;
    }

    close(fd);

    return 0;
}

这个程序会向端口0x378写入数值100。

3

站心网

前言 在Linux中,如果要对特定的硬件端口进行操作,用户空间是没有足够的权限的,可以在内核模块中实现端口..

为您推荐

代码照进现实:对公司管理策略的技术性解构

上学的时候觉得计算机专业的一些理论晦涩难懂,跟现实世界的关联太少,每当遇到一些精妙的设计时都会发出一种感叹:究竟是什么脑袋才能想出这么有意思的东西。一晃工作十年,阅历渐丰,隐约发现其实社会中的一些现象..

编写优秀 CSS 代码的 8 个策略

编写基本的CSS和HTML是我们作为Web开发人员学习的首要事情之一。然而,我遇到的很多应用程序显然没有人花时间真正考虑前端开发的长久性和可维护性。我认为这主要是因为许多开发人员对组织CSS / HTML和JavaScript的策..

天天写业务代码,如何成为技术大牛?

不管是开发、测试、运维,每个技术人员心理多多少少都有一个成为技术大牛的梦,毕竟"梦想总是要有的,万一实现了呢"!正是对技术梦的追求,促使我们不断地努力和提升自己。然而"梦想是美好的,现实却是残酷的",很多..

如何选择更适合你的 Linux 发行版?

很多人经常会问我这样一个问题:“嘿,你是用Linux的,对吧?我应该使用Linux的哪个版本?我有这个朋友推荐我_____,你觉得怎么样?”我通常会以这样的问题回复:这取决于你想做什么?今天我决定写一篇关于如何选择L..

记我经历的一次公司破产经历,一行代码害死一家公司

前言这是一篇亲身经历的真实记录,事情发生在2010年。狗血剧情一再上演,使我的程序员生涯变得跌宕起伏,也从中学到了很多。在写这篇文章之前,我还专门去查了这家公司的资料。有如下事实:1.官网已经打不开了。2.天..

如何处理前任程序员留下的代码

作为软件工程师不可避免会遇到的一个场景是:我们在改变或添加一个功能到不是我们创建的、我们不熟悉的、与我们负责的系统部分无关的代码中时,会遇到麻烦。虽然这可能会是一个繁琐而艰巨的任务,但是由于使用其他开..

对码农而言什么样的代码才能叫做好代码?

好的代码,就像是好的笑话——无需解释就能让别人明白。如果你的代码能够做到不解自明,在大多数时候,你根本无需为其配备说明文档。好的代码,就像是一辆配备了优秀音响和杯架的汽车,这辆车在行驶到最高速度的时候..

当一个程序员写不出代码了,该怎么办?

你已经对着电脑n个小时了。不知道该写什么代码,或者一种摔键盘的冲动正在你的胸中酝酿。咖啡一杯接着一杯。不敢再喝了,因为搞不好要有副作用了,心跳加速,身体不由自主地颤抖,出冷汗,但还是无法产出任何代码。..

VS创建.NET Core项目使用Docker方式部署到Linux服务器

在 Visual Studio(VS) 中,使用 Docker 方式部署 .NET Core 项目 到 Linux 服务器,可以简化环境管理并提高部署效率。以下是完整教程:1. 在 VS 创建 .NET Core 项目并启用 Docker新建 ASP.NET Core 项目打开 Visu..

Mysql查询的一些操作(查表名,查字段名,查当月,查一周,查当天)

查询数据库中所有表名select table_name from information_schema.tables where table_schema='tools' and table_type='base table';查询指定数据库中指定表的所有字段名column_nameselect column_n..

.net 通过 HttpClient 下载文件同时报告进度的方法

通过 HttpClient 的 ContentLength 很多时候都可以拿到下载的内容的长度,通过 ReadAsync 可以返回当前读到的长度,将读取到的长度加起来就是已经下载的长度看起来很简单,于是直接给代码private static async Task ..

ASP.NET MVC最常用的设计模式代码示例

ASP.NET MVC 是一个基于分层架构的框架,其核心架构本身已经实现了 MVC 模式(Model-View-Controller)。除了 MVC 模式,开发者在使用 ASP.NET MVC 开发应用时,通常会结合其他设计模式以提高代码的可维护性、可扩展..

分享5个开源的.NET Excel读写操作库

本文给大家分享 5 个开源的 .NET Excel 读写操作库,它们广泛用于处理 Excel 文件,包括读取、写入、导入和导出数据。1. EPPlus简介:EPPlus 是功能强大的 .NET 库,用于创建和读取 Excel 文件(.xlsx 和 .xlsm 格式..

JavaScript中字典的常用操作

字典是一种以键值对存在的数据结构,他的底层是Array数组字典初始化和数组初始化的区别:数组的初始化:var arr = [1,2,3,4,5];//使用中括号字典的初始化: var names = {“a”:“aaa”,“b”:“bbb”,“c”:“ccc”}..

10款.NET开发中推荐的代码分析和质量工具

以下是10款.NET开发中常用的代码分析和质量工具列表,以及它们的主要功能和使用场景:1. SonarQube简介:一个流行的开源静态代码分析平台,用于检测代码中的漏洞、错误、技术债务等问题。主要功能:支持代码质量监测..

通过js修改tinymce的编辑器的内容

在网页开发中,TinyMCE是一个流行的富文本编辑器。它允许用户轻松地创建和编辑HTML内容,而无需直接操作代码。然而,有时我们可能需要通过JavaScript来动态修改编辑器中的内容。本文将介绍如何使用JavaScript来修改T..

.NET C# EntityFramework(EF)连接SQLite代码示例

在.NET C#中使用Entity Framework(EF)连接SQLite数据库是一种常见的做法,可以有效地管理和操作数据。以下是一个简单的示例代码,展示了如何使用EF Core连接到SQLite数据库并执行基本的CRUD操作。首先,确保你已经..

设计模式之高质量代码

0,什么是高质量代码我觉得回答这个问题,应该从两个方面考虑。从业务角度考虑。首先,在公司开发一款软件,应该是业务在驱动。所以,从这个角度来说,代码第一个应该满足的是业务需求,如果连最基本的业务需求都满..

C#发送邮件代码简洁示例(附源码下载)

C#发送邮件,主要使用的是System.Net.Mail命名空间下的方法实现,方法很简单,短短十几行代码即可完成发送,具体代码如下。 try { MailMessage myMail = new MailMessage(); myMail.From = new MailAddress..

AutoMapper.AutoMapperMappingException”类型的异常在 AutoMapper.dll 中发生,但未在用户代码中进行处理

今天修改别人的代码抛出了这样的异常: AutoMapper.AutoMapperMappingException”类型的异常在 AutoMapper.dll 中发生,但未在用户代码中进行处理。进行了调试,往下走的时候直接报错了,百度之~中文网站上没..

发表回复

返回顶部

微信分享

微信分享二维码

扫描二维码分享到微信或朋友圈

链接已复制