linux驱动:[3]高级字符设备驱动之ioctl

linux驱动:[3]高级字符设备驱动之ioctl

测试平台: x86 PC linux-4.4.0

1.实验目的:

  • 学习并编写ioctl linux高级字符设备驱动程序。
  • 编写驱动 scull ,使用5个指令实现对设备数据的清零,读取,写入操作。

2.驱动代码:(解析见下方)

scull.c:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>

#include "scull.h"

//设备私有数据
struct scull_dev {
    int data;
    struct cdev cdev;
} dev;

//最大IOCTL命令号
#define SCULL_IOC_MAXNR 4
//默认自动分配主设备号
#define SCULL_DEV_MAJOR 0
static int scull_major = SCULL_DEV_MAJOR;
module_param(scull_major, int, S_IRUGO);

struct class *scull_class;
struct cdev cdev;

long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int err = 0, retval = 0;

//判断命令幻数是否匹配
    if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC)
        return -ENOTTY;
//判断命令序号是否非法
    if (_IOC_NR(cmd) > SCULL_IOC_MAXNR)
        return -ENOTTY;
//判断空间是否可访问
    /* VERIFY_WRITE 是 VERIFY_READ 超集 */
    if (_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
    if (err)
        return -EFAULT;

    switch (cmd) {
        case SCULL_IOC_CLEAR://数据清零
            dev.data = 0;
            printk("SCULL_IOC_CLEAR data: 0\n");
            break;
        case SCULL_IOC_GET://获取数据(通过指针)
            retval = __put_user(dev.data, (int __user *)arg);
            printk("SCULL_IOC_GET data: %d\n", dev.data);
            break;
        case SCULL_IOC_QUERY://获取数据(通过返回值)
            printk("SCULL_IOC_QUERY data: %d\n", dev.data);
            retval = dev.data;
            break;
        case SCULL_IOC_SET://设置数据(通过指针)
            retval = __get_user(dev.data, (int __user *)arg);
            printk("SCULL_IOC_SET data: %d\n", dev.data);
            break;
        case SCULL_IOC_TELL://设置数据(通过直接引用参数值)
            dev.data = arg;
            printk("SCULL_IOC_TELL data: %d\n", arg);
            break;
        default:
            retval = -EINVAL;
            break;
    }

    return retval;
}

static const struct file_operations scull_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = scull_ioctl,//linux 2.6.36内核之后unlocked_ioctl取代ioctl
};

static int scull_init(void)
{
    //设备号
    dev_t devno = MKDEV(scull_major, 0);
    int result;

    if (scull_major)//静态分配设备号
        result = register_chrdev_region(devno, 1, "scull");
    else {//动态分配设备号
        result = alloc_chrdev_region(&devno, 0, 1, "scull");
        scull_major = MAJOR(devno);
    }

    if (result < 0)
        return result;

//用于udev/mdev自动创建节点
    scull_class = class_create(THIS_MODULE, "scull");
    device_create(scull_class, NULL, devno, NULL, "scull");

//静态添加cdev
    cdev_init(&cdev, &scull_fops);
    cdev.owner = THIS_MODULE;
    cdev_add(&cdev, devno, 1);
    printk("scull init success\n");
    return 0;
}

static void scull_exit(void)
{
    cdev_del(&cdev);
    device_destroy(scull_class, MKDEV(scull_major, 0));
    class_destroy(scull_class);
    unregister_chrdev_region(MKDEV(scull_major, 0), 1);
    printk("scull exit success\n");
}

MODULE_AUTHOR("Ziping Chen <techping.chan@gmail.com>");
MODULE_LICENSE("GPL");

module_init(scull_init);
module_exit(scull_exit);

scull.h:

#ifndef SCULL_H_
#define SCULL_H_

//定义幻数
#define SCULL_IOC_MAGIC '$'

//定义命令->
//数据清零
#define SCULL_IOC_CLEAR _IO(SCULL_IOC_MAGIC, 0)
//获取数据(通过指针)
#define SCULL_IOC_GET   _IOR(SCULL_IOC_MAGIC, 1, int)
//获取数据(通过返回值)
#define SCULL_IOC_QUERY _IO(SCULL_IOC_MAGIC, 2)
//设置数据(通过指针)
#define SCULL_IOC_SET   _IOW(SCULL_IOC_MAGIC, 3, int)
//设置数据(通过直接引用参数值)
#define SCULL_IOC_TELL  _IO(SCULL_IOC_MAGIC, 4)

#endif

Makefile:

obj-m := scull.o #编译进模块
KERNELDIR := /lib/modules/4.4.0-59-generic/build #此处为linux内核库目录
PWD := $(shell pwd) #获取当前目录
OUTPUT := $(obj-m) $(obj-m:.o=.ko) $(obj-m:.o=.mod.o) $(obj-m:.o=.mod.c) modules.order Module.symvers
 
modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
 
clean:
    rm -rf $(OUTPUT)

linux c应用程序测试:

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

#include "scull.h"

int main(void)
{
    int fd;
    int data;

//打开设备
    fd = open("/dev/scull", O_RDWR);
    if (fd == -1) {
        printf("open scull device failed!\n");
        return -1;
    }

//数据清零  
    ioctl(fd, SCULL_IOC_CLEAR);

//直接传值测试
    data = ioctl(fd, SCULL_IOC_QUERY);
    printf("app data %d\n", data);
    data = 100;
    ioctl(fd, SCULL_IOC_TELL, data);

//指针传值测试
    ioctl(fd, SCULL_IOC_GET, &data);
    data = 122;
    ioctl(fd, SCULL_IOC_SET, &data);

    return 0;
}

测试结果:

result

3.代码解析:

代码的大部分解析都位于上面测试,这里我只是提一下程序编写过程中可能出现的问题:

  • error: unknown field 'ioctl' specified in initializer

    这个错误是因为在linux 2.6.36内核之后,去掉了原来的ioctl,添加两个新的成员:

    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

    将scull_fops中.ioctl替换为.unlocked_ioctl,另外scull_ioctl()要去掉inode参数,返回类型为long。

  • 没有编写scull_open()函数,设备默认成功打开。

  • 编译没有成功可能是没有包含对应的头文件,头文件可以通过查阅手册或者网络搜索得知。

  • 查看printk信息可通过 dmesg shell指令。

  • 每个函数的参数都是确定的不变的,不要自己擅自改动。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容