通过ioctl操作硬件端口的Linux内核模块代码
2024-07-02
75
前言
在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文件进行编译。具体编译步骤如下:
- 编写驱动代码并保存到文件中(例如my_port_driver.c)。
- 在Linux内核源代码目录中创建一个Kbuild文件,内容如下:
obj-m := my_port_driver.o
- 在Linux内核源代码目录中打开终端,执行以下命令进行编译:
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
其中(uname−r)表示当前内核版本,(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。
更新于:6个月前赞一波!3
相关文章
- 【说站】python集合的并集操作
- 【说站】python in操作符是什么
- 【说站】python中capitalize的三种转换操作
- 【说站】判断水仙花数python代码
- 【说站】python美元转换成人民币转换代码
- 【说站】python99乘法表代码
- 【说站】python Tkinter模块是什么
- 【说站】java interrupt()打断阻塞的操作
- 【说站】python pyglet模块如何使用
- 【说站】python温度转换代码
- 【说站】python序列操作的整理
- 【说站】python列表操作符有哪些
- 【说站】python搜索模块如何查询
- 【说站】python如何定义索引模块类
- 【说站】python数据模块类如何定义
- 【说站】Python测试前置操作的方法
- JavaScript中字典的常用操作
- 【说站】python zipfile模块的文件操作
- 【说站】python shutil模块如何操作文件
- 【说站】python累加求和代码
文章评论
评论问答