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

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

測試平臺(tái): x86 PC linux-4.4.0

1.實(shí)驗(yàn)?zāi)康模?/h2>
  • 學(xué)習(xí)并編寫ioctl linux高級(jí)字符設(shè)備驅(qū)動(dòng)程序憎账。
  • 編寫驅(qū)動(dòng) scull 录肯,使用5個(gè)指令實(shí)現(xiàn)對(duì)設(shè)備數(shù)據(jù)的清零,讀取,寫入操作溜徙。

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

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命令號(hào)
#define SCULL_IOC_MAXNR 4
//默認(rèn)自動(dòng)分配主設(shè)備號(hào)
#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;
//判斷命令序號(hào)是否非法
    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è)備號(hào)
    dev_t devno = MKDEV(scull_major, 0);
    int result;

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

    if (result < 0)
        return result;

//用于udev/mdev自動(dòng)創(chuàng)建節(jié)點(diǎn)
    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 #編譯進(jìn)模塊
KERNELDIR := /lib/modules/4.4.0-59-generic/build #此處為linux內(nèi)核庫目錄
PWD := $(shell pwd) #獲取當(dāng)前目錄
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

    這個(gè)錯(cuò)誤是因?yàn)樵趌inux 2.6.36內(nèi)核之后,去掉了原來的ioctl拧篮,添加兩個(gè)新的成員:

    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è)備默認(rèn)成功打開芜壁。

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

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

  • 每個(gè)函數(shù)的參數(shù)都是確定的不變的顷牌,不要自己擅自改動(dòng)。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末塞淹,一起剝皮案震驚了整個(gè)濱河市窟蓝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌饱普,老刑警劉巖运挫,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異套耕,居然都是意外死亡谁帕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門冯袍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匈挖,“玉大人,你說我怎么就攤上這事康愤±苎” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵翘瓮,是天一觀的道長贮折。 經(jīng)常有香客問我,道長资盅,這世上最難降的妖魔是什么调榄? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任踊赠,我火速辦了婚禮,結(jié)果婚禮上每庆,老公的妹妹穿的比我還像新娘筐带。我一直安慰自己,他們只是感情好缤灵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布伦籍。 她就那樣靜靜地躺著,像睡著了一般腮出。 火紅的嫁衣襯著肌膚如雪帖鸦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天胚嘲,我揣著相機(jī)與錄音作儿,去河邊找鬼。 笑死馋劈,一個(gè)胖子當(dāng)著我的面吹牛攻锰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妓雾,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼娶吞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了械姻?” 一聲冷哼從身側(cè)響起妒蛇,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎策添,沒想到半個(gè)月后材部,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毫缆,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唯竹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苦丁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浸颓。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旺拉,靈堂內(nèi)的尸體忽然破棺而出产上,到底是詐尸還是另有隱情,我是刑警寧澤蛾狗,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布晋涣,位于F島的核電站,受9級(jí)特大地震影響沉桌,放射性物質(zhì)發(fā)生泄漏谢鹊。R本人自食惡果不足惜算吩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望佃扼。 院中可真熱鬧偎巢,春花似錦、人聲如沸兼耀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘤运。三九已至窍霞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拯坟,已是汗流浹背官撼。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留似谁,地道東北人傲绣。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像巩踏,于是被迫代替她去往敵國和親秃诵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容