linux驅(qū)動:[3]高級字符設(shè)備驅(qū)動之ioctl

linux驅(qū)動:[3]高級字符設(shè)備驅(qū)動之ioctl

測試平臺: x86 PC linux-4.4.0

1.實驗?zāi)康模?/h2>
  • 學習并編寫ioctl linux高級字符設(shè)備驅(qū)動程序。
  • 編寫驅(qū)動 scull ,使用5個指令實現(xiàn)對設(shè)備數(shù)據(jù)的清零,讀取,寫入操作。

2.驅(qū)動代碼:(解析見下方)

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"

//設(shè)備私有數(shù)據(jù)
struct scull_dev {
    int data;
    struct cdev cdev;
} dev;

//最大IOCTL命令號
#define SCULL_IOC_MAXNR 4
//默認自動分配主設(shè)備號
#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;

//判斷命令幻數(shù)是否匹配
    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://數(shù)據(jù)清零
            dev.data = 0;
            printk("SCULL_IOC_CLEAR data: 0\n");
            break;
        case SCULL_IOC_GET://獲取數(shù)據(jù)(通過指針)
            retval = __put_user(dev.data, (int __user *)arg);
            printk("SCULL_IOC_GET data: %d\n", dev.data);
            break;
        case SCULL_IOC_QUERY://獲取數(shù)據(jù)(通過返回值)
            printk("SCULL_IOC_QUERY data: %d\n", dev.data);
            retval = dev.data;
            break;
        case SCULL_IOC_SET://設(shè)置數(shù)據(jù)(通過指針)
            retval = __get_user(dev.data, (int __user *)arg);
            printk("SCULL_IOC_SET data: %d\n", dev.data);
            break;
        case SCULL_IOC_TELL://設(shè)置數(shù)據(jù)(通過直接引用參數(shù)值)
            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內(nèi)核之后unlocked_ioctl取代ioctl
};

static int scull_init(void)
{
    //設(shè)備號
    dev_t devno = MKDEV(scull_major, 0);
    int result;

    if (scull_major)//靜態(tài)分配設(shè)備號
        result = register_chrdev_region(devno, 1, "scull");
    else {//動態(tài)分配設(shè)備號
        result = alloc_chrdev_region(&devno, 0, 1, "scull");
        scull_major = MAJOR(devno);
    }

    if (result < 0)
        return result;

//用于udev/mdev自動創(chuàng)建節(jié)點
    scull_class = class_create(THIS_MODULE, "scull");
    device_create(scull_class, NULL, devno, NULL, "scull");

//靜態(tài)添加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_

//定義幻數(shù)
#define SCULL_IOC_MAGIC '$'

//定義命令->
//數(shù)據(jù)清零
#define SCULL_IOC_CLEAR _IO(SCULL_IOC_MAGIC, 0)
//獲取數(shù)據(jù)(通過指針)
#define SCULL_IOC_GET   _IOR(SCULL_IOC_MAGIC, 1, int)
//獲取數(shù)據(jù)(通過返回值)
#define SCULL_IOC_QUERY _IO(SCULL_IOC_MAGIC, 2)
//設(shè)置數(shù)據(jù)(通過指針)
#define SCULL_IOC_SET   _IOW(SCULL_IOC_MAGIC, 3, int)
//設(shè)置數(shù)據(jù)(通過直接引用參數(shù)值)
#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內(nèi)核庫目錄
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應(yīng)用程序測試:

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

#include "scull.h"

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

//打開設(shè)備
    fd = open("/dev/scull", O_RDWR);
    if (fd == -1) {
        printf("open scull device failed!\n");
        return -1;
    }

//數(shù)據(jù)清零  
    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;
}

測試結(jié)果:

result

3.代碼解析:

代碼的大部分解析都位于上面測試,這里我只是提一下程序編寫過程中可能出現(xiàn)的問題:

  • error: unknown field 'ioctl' specified in initializer

    這個錯誤是因為在linux 2.6.36內(nèi)核之后,去掉了原來的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參數(shù),返回類型為long。

  • 沒有編寫scull_open()函數(shù),設(shè)備默認成功打開。

  • 編譯沒有成功可能是沒有包含對應(yīng)的頭文件,頭文件可以通過查閱手冊或者網(wǎng)絡(luò)搜索得知。

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

  • 每個函數(shù)的參數(shù)都是確定的不變的,不要自己擅自改動。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容