字符设备驱动开发

在Linux系统中,用户空间和内核空间之间是相互隔离开的。驱动程序运行在内核空间中,给出的地址也是在内核空间中的地址,运行在用户空间下的用户程序即使拿到这个地址,也不能访问内核空间。这时,我们需要使用到copy_to_user()函数,将要传递的内容从内核空间拷贝到用户空间,用户程序再访问用户空间中的该内容即可。

copy_to_user函数的原型如下。

unsigned long copy_to_user(void *to, const void *from, unsigned long n)

1

参数含义:

1)to:目标地址(用户空间)

2)from:源地址(内核空间)

3)n:需要拷贝的数据的字节数

返回值:成功返回0,失败返回没有拷贝成功的数据字节数。

同理,也有从用户空间向内核空间拷贝的函数copy_from_user(),原型如下。

unsigned long copy_from_user(void *to, const void *from, unsigned long n)

1

参数含义:

1)to:目标地址(内核空间)

2)from:源地址(用户空间)

3)n:需要拷贝的数据的字节数

返回值:成功返回0,失败返回没有拷贝成功的数据字节数。

下面举个实例,来详细介绍如何在用户空间和内核空间中通过传递地址参数的方法来传递复杂参数。这里我们传递两个参数char arg1和int arg2,将这两个参数打包进一个结构体struct IOC_ARGS中。这个结构体在用户程序和驱动程序中也需要保持一致。

同样我们还需要定义用户程序和驱动程序之间命令码,我们定义两个命令码,分别用来读参数和写参数。

用户程序的头文件user_ioctl.h。

#include <sys/ioctl.h>

struct IOC_ARGS {

char arg1;

int arg2;

};

#define CMD_IOC_MAGIC 'a'

#define CMD_IOC_0 _IOR(CMD_IOC_MAGIC, 0, struct IOC_ARGS)

#define CMD_IOC_1 _IOW(CMD_IOC_MAGIC, 1, struct IOC_ARGS)

内核程序的头文件ioctl_test.h。

#include <linux/ioctl.h>

typedef struct IOC_ARGS {

char arg1;

int arg2;

}IOC_ARGS;

#define CMD_IOC_MAGIC 'a'

#define CMD_IOC_0 _IOR(CMD_IOC_MAGIC, 0, struct IOC_ARGS)

#define CMD_IOC_1 _IOW(CMD_IOC_MAGIC, 1, struct IOC_ARGS)

用户程序user_ioctl.c。

#include <stdio.h>

#include <fcntl.h>

#include <unistd.h>

#include <errno.h>

#include <string.h>

#include "user_ioctl.h"

int main()

{

int rc;

struct IOC_ARGS args_r;

struct IOC_ARGS args_w = {'u', 233};

int fd = open("/dev/test_chr_dev", O_RDWR);

rc = ioctl(fd, CMD_IOC_0, &args_r);

if (rc < 0)

printf("ioctl: %s\n", strerror(errno));

else

printf("ioc read arg1 = %c, arg2 = %d.\n", args_r.arg1, args_r.arg2);

rc = ioctl(fd, CMD_IOC_1, &args_w);

if (rc < 0)

printf("ioctl: %s\n", strerror(errno));

else

printf("ioc write arg1 = %c, arg2 = %d.\n", args_w.arg1, args_w.arg2);

close(fd);

return 0;

}

内核驱动程序ioctl_test.c。

#include <linux/init.h>

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/uaccess.h>

#include "ioctl_test.h"

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zz");

static dev_t devno;

static int demo_open(struct inode *ind, struct file *fp)

{

printk("demo open\n");

return 0;

}

static int demo_release(struct inode *ind, struct file *fp)

{

printk("demo release\n");

return 0;

}

static long demo_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)

{

int rc = 0;

struct IOC_ARGS args_r = {'k', 566};

struct IOC_ARGS args_w;

if (_IOC_TYPE(cmd) != CMD_IOC_MAGIC) {

pr_err("%s: command type [%c] error.\n", func, _IOC_TYPE(cmd));

return -ENOTTY;

}

switch(cmd) {

case CMD_IOC_0:

rc = copy_to_user((char __user *)arg, &args_r, sizeof(IOC_ARGS));

if (rc) {

pr_err("%s: copy_to_user failed", func);

return rc;

}

printk("%s: ioc read arg1 = %c, arg2 = %d", func, args_r.arg1, args_r.arg2);

break;

break;

case CMD_IOC_1:

rc = copy_from_user(&args_w, (char __user *)arg, sizeof(IOC_ARGS));

if (rc) {

pr_err("%s: copy_from_user failed", func);

return rc;

}

printk("%s: ioc write arg1 = %c, arg2 = %d", func, args_w.arg1, args_w.arg2);

break;

default:

pr_err("%s: invalid command.\n", func);

return -ENOTTY;

}

return rc;

}

static struct file_operations fops = {

.open = demo_open,

.release = demo_release,

.unlocked_ioctl = demo_ioctl,

};

static struct cdev cd;

static int demo_init(void)

{

int rc;

rc = alloc_chrdev_region(&devno, 0, 1, "test");

if(rc < 0) {

pr_err("alloc_chrdev_region failed!");

return rc;

}

printk("MAJOR is %d\n", MAJOR(devno));

printk("MINOR is %d\n", MINOR(devno));

cdev_init(&cd, &fops);

rc = cdev_add(&cd, devno, 1);

if (rc < 0) {

pr_err("cdev_add failed!");

return rc;

}

return 0;

}

static void demo_exit(void)

{

cdev_del(&cd);

unregister_chrdev_region(devno, 1);

return;

}

module_init(demo_init);

module_exit(demo_exit);

Makefile 文件:

ifneq ($(KERNELRELEASE),)

obj-m := ioctl_test.o

else

KDIR := /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

all:

make -C (KDIR) M=(PWD) modules

gcc user_ioctl.c -o user

clean:

make -C (KDIR) M=(PWD) clean

rm -rf user

endif

运行结果。首先编译代码。

进入root模式,安装模块,打印内核信息。

注册设备节点,运行用户程序。

打印内核输出。

删除设备节点,移除模块,结束。

相关推荐
自由的好好干活11 小时前
PCI9x5x驱动移植支持PCI9054在win7下使用1
驱动开发
比奇堡派星星11 小时前
Linux OOM Killer
linux·开发语言·arm开发·驱动开发
Ghost Face...14 小时前
网卡驱动开发与移植实战指南
驱动开发
Ghost Face...14 小时前
深入解析网卡驱动开发与移植
linux·驱动开发
DeeplyMind19 小时前
第7章:DRM内核调试技术:7.1 DRM DebugFS的使用
linux·驱动开发·drm·debugfs·drm debugfs
chen_mangoo2 天前
HDMI简介
android·linux·驱动开发·单片机·嵌入式硬件
食咗未2 天前
Linux microcom工具的使用
linux·运维·服务器·驱动开发·串口调试
云雾J视界2 天前
硬件试错成本太高?用Simulink构建电力电子“数字孪生体”
驱动开发·数字孪生·电力电子·simulink·sic·obc·800v
KL's pig/猪头/爱心/猪头4 天前
写一个rv1106的led驱动1-整体架构
linux·驱动开发
进击大厂的小白4 天前
68.range属性
驱动开发