3-字符設(shè)備框架_創(chuàng)建設(shè)備文件

字符設(shè)備框架:
  • 字符設(shè)備類包含了同種的字符設(shè)備。/sys/class
  • 每種設(shè)備都有struct cdev來描述的:
struct file_operations *ops = &hello_op    
cdev_init(&cdev,&hello_op)
dev_t *dev = &devno                             
cdev_add(&cdev,devno,1)
用戶空間
  • 應用程序(用到設(shè)備文件名——mknod "/dev/設(shè)備文件名" c 主設(shè)備號 次設(shè)備號)
內(nèi)核空間
        系統(tǒng)調(diào)用
            |
           vfs  (struct cdev   struct file_operations)
            |
          驅(qū)動  設(shè)備號 = MKDEV(主設(shè)備號荆隘,次設(shè)備號);
//struct cdev cdev;
struct cdev *cdev;
3宽档、初始化file_operations結(jié)構(gòu)體
struct file_operations op = {
    .owner = THIS_MODULE,//代表了當前模塊的意思,不寫也沒錯
    .open = hello_open
};
加載函數(shù)
  • 1、申請設(shè)備號——dev_t devno = mar_num << 20 | min_num 或者調(diào)用 MKDEV(主設(shè)備號,次設(shè)備號)
  • 2、注冊設(shè)備號
    * 靜態(tài)注冊:register_chrdev_region(devno,需要注冊的設(shè)備個數(shù)尤蛮,設(shè)備文件名);
    * 動態(tài)注冊:alloc_chrdev_region(devno,起始次設(shè)備號,次設(shè)備號個數(shù)斯议,設(shè)備文件名);
    * 靜態(tài)注冊的優(yōu)點是設(shè)備啟動需要的時間短产捞,但是容易造成和已存在設(shè)備號沖突
    * cdev = kzalloc(sizeof(struct cdev),GFP_KERNEL);
  • 4、初始化字符設(shè)備 cdev_init(&cdev,&op);
  • 5哼御、添加字符設(shè)備到內(nèi)核中 cdev_add(&cdev,devno,1);
卸載函數(shù)
  • 6坯临、cdev_del(&cdev);
  • 7、注銷設(shè)備號
    • unregister_chrdev_region(devno,注銷的字符設(shè)備個數(shù));
規(guī)避版權(quán)
自動創(chuàng)建設(shè)備節(jié)點 描述時設(shè)備節(jié)點就是設(shè)備文件
  • 在講自動創(chuàng)建設(shè)備節(jié)點前恋昼,必須先了解另一部分知識點:sysfs這種文件系統(tǒng)默認被掛載到了/sys目錄下.
    它的作用是給用戶空間和內(nèi)核空間提供交互的接口——會將內(nèi)核中的信息(例如設(shè)備號)導出到用戶空間看靠,
    并且將信息存放到/sys/module/目錄名(以模塊名稱命名的)/uevent文件中.
    在用戶空間中有一個小程序叫做udev,每次開機的時候會去遍歷所有的uevent文件液肌,提取出設(shè)備號然后在
    /dev/目錄下創(chuàng)建設(shè)備文件挟炬,一旦創(chuàng)建完成后udev阻塞,當我們新加載一個驅(qū)動時嗦哆,sysfs會將新加載的驅(qū)動
    信息導出到用戶空間同時會產(chǎn)生新的uevent文件谤祖,一旦產(chǎn)生uevent文件,udev會喚醒吝秕。
struct class *cls;
cls = class_create(THIS_MODULE,"hello");
這里的hello代表了類的名字泊脐,會在/sys/class目錄下出現(xiàn)一個文件夾叫做hello,而hello的下面可能出現(xiàn)很多代表子設(shè)備的軟連接
創(chuàng)建設(shè)備文件接口
struct device *devs;
devs = device_create(類結(jié)構(gòu)體指針烁峭,父設(shè)備的結(jié)構(gòu)體指針,);

struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
  • 功能:在/dev目錄下自動創(chuàng)建設(shè)備文件
  • 參數(shù)1:類結(jié)構(gòu)體指針cls
  • 參數(shù)2:父設(shè)備的device結(jié)構(gòu)體指針,如果沒有要寫NULL
  • 參數(shù)3:設(shè)備號
  • 參數(shù)4:一些私有數(shù)據(jù)约郁,如果不用寫NULL
  • 參數(shù)5:設(shè)備文件名缩挑,但不一定是完整的設(shè)備文件名,如果不使用第六個參數(shù)鬓梅,那么第五個參數(shù)就是完整的設(shè)備文件名
    • 如果使用第六個參數(shù)供置,那么設(shè)備文件名則是由第五和第六兩個參數(shù)構(gòu)成的。
  • 參數(shù)6:不需要可以省略
sturct inode
{
    umode_t i_mode 判斷設(shè)備類型
    struct cdev *i_cdev 存放的是驅(qū)動層中cdev結(jié)構(gòu)體的首地址
}
inode結(jié)構(gòu)體是靜態(tài)的绽快,最初是存放到磁盤上的芥丧,第一次打開文件時會被加載到內(nèi)核中。inode結(jié)構(gòu)體對于一個文件來講只有一個坊罢。


struct file {
    const struct file_operations *f_op; 存放了驅(qū)動中的file_operations結(jié)構(gòu)體的首地址
}
用來描述文件的動態(tài)信息的续担,只要打開一次文件就會出現(xiàn)一個新的struct file結(jié)構(gòu)體

int (*open) (struct inode *, struct file *);
驅(qū)動層中的open完成的功能:打開文件,申請資源活孩,識別次設(shè)備號物遇,存放私有數(shù)據(jù)
用戶空間
  • 系統(tǒng)調(diào)用接口open 上層的open通過系統(tǒng)調(diào)用號來匹配系統(tǒng)調(diào)用源碼
內(nèi)核空間
  • 系統(tǒng)調(diào)用
內(nèi)核中:
  • 1、
    vi arch/arm/include/uapi/asm/unistd.h
    33 #define __NR_open           (__NR_SYSCALL_BASE+  5)
    5是系統(tǒng)調(diào)用號
    
  • 2憾儒、
    跟進
    __Nr_open
    713 __SYSCALL(__NR_open, sys_open)
    通過系統(tǒng)調(diào)用找到了sys_open 內(nèi)核中一個函數(shù)
    
  • 3询兴、
    SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
    
             return do_sys_open(AT_FDCWD, filename, flags, mode);
                        ||
                        \/
            struct file *f = do_filp_open(dfd, tmp, &op);
                                ||
                                \/
                filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
                            ||
                            \/
                3181     error = do_last(nd, &path, file, op, &opened, pathname);
                                    ||
                                    \/
                        2882         goto finish_open;
                                            ||
                                            \/
                                794     error = do_dentry_open(file, open, current_cred());
                                                    ||
                                                    \/
                                         727         open = f->f_op->open;
                                        其中f->f_op->open;    我們在驅(qū)動中自己實現(xiàn)的函數(shù)接口,比如.open = hello_open
    
無論應用層還是底層所謂的讀寫起趾,都是站在應用層的角度來看待的诗舰。
  • 讀:數(shù)據(jù)從內(nèi)核空間流向用戶空間
  • 寫:相反
讀 :
  • 功能:將內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間
  • 參數(shù)1:第一個參數(shù)內(nèi)核創(chuàng)建。
  • 參數(shù)2:用戶空間地址
  • 參數(shù)3:從內(nèi)核空間傳遞給用戶空間的數(shù)據(jù)大小
  • 參數(shù)4:偏移量
    size_t read(struct file *,char __user *,size_t ,loff_t *)
    {
        將內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間 copy_to_user();
    }

static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n);
  • 返回值:成功返回0,失敗返回錯誤碼
  • 參數(shù)1:用戶空間的某個地址
  • 參數(shù)2: 內(nèi)核空間的某個地址
  • 參數(shù)3: 需要給用戶空間拷貝的字節(jié)數(shù)

    size_t write(struct file *,const char __user *,size_t ,loff_t *)
    {
        將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間 copy_from_user();
    }

    static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
  • 返回值:成功返回0训裆,失敗返回錯誤碼
  • 參數(shù)1:內(nèi)核空間的某個地址
  • 參數(shù)2:用戶空間某個地址
  • 參數(shù)3:用戶空間給內(nèi)核空間傳遞的數(shù)據(jù)的字節(jié)數(shù)

應用層:
  • int ioctl(int fd,int cmd,...);

  • 參數(shù)1:文件描述符

  • 參數(shù)2: 命令

  • 參數(shù)3:如果不需要則省略始衅,如果需要可能是一個普通變量也可能是一個地址

    • 如果參數(shù)3需要傳遞一個結(jié)構(gòu),這個時候需要傳遞這個結(jié)構(gòu)的首地址缭保,同時驅(qū)動層要想獲取數(shù)據(jù)必須調(diào)用copy_from_user或者copy_to_user
    • 如果參數(shù)3只是傳遞一個基本類型的數(shù)據(jù)汛闸,驅(qū)動層直接通過第三個參數(shù)來接收應用層的數(shù)據(jù)娘扩。
  • 驅(qū)動層:

    • long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    • 參數(shù)2:命令
    • 參數(shù)3:接收應用程序傳過來的數(shù)據(jù)
    cmd是一個32位的無符號整數(shù)齐婴,這個整數(shù)分成4個部分
                 8位                       8位          2位            14位
       幻數(shù)(代表某個設(shè)備,通常用字符)    代表了序號      方向     傳遞的參數(shù)的類型大小
    

虛擬地址 = ioremap(物理地址,物理地址占用字節(jié)數(shù))

練習 :

Makefile
ifeq ($(KERNELRELEASE),)
#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
KERNELDIR ?= /home/linux/linux-3.14/
PWD ?= $(shell pwd)
modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    cp *.ko /rootfs
app:
    arm-none-linux-gnueabi-gcc test.c -o test
    cp test /rootfs
clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
.PHONY: modules clean
else
    obj-m += led.o
endif

head.h
#ifndef     __HEAD_H_
#define     __HEAD_H_

#define     MAGIC   'l'

#define     LED2_ON     _IO(MAGIC,0)
#define     LED2_OFF    _IO(MAGIC,1)
#define     LED3_ON     _IO(MAGIC,2)
#define     LED3_OFF    _IO(MAGIC,3)
#define     LED4_ON     _IO(MAGIC,4)
#define     LED4_OFF    _IO(MAGIC,5)
#define     LED5_ON     _IO(MAGIC,6)
#define     LED5_OFF    _IO(MAGIC,7)
#define     LEDALL_ON   _IO(MAGIC,8)
#define     LEDALL_OFF  _IO(MAGIC,9)

#endif

led.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "head.h"

#define     LED_MAJOR   505
#define     LED_MINOR   0
#define     LED_NUM     1
#define     LED_NAME    "ledx"
#define     CLS_NAME    "led_cls"
#define     DEV_NAME    "led"

#define     GPX2CON     0x11000c40
#define     GPX1CON     0x11000c20
#define     GPF3CON     0x114001e0

volatile unsigned int * gpx2con;
volatile unsigned int * gpx1con;
volatile unsigned int * gpf3con;
volatile unsigned int * gpx2dat;
volatile unsigned int * gpx1dat;
volatile unsigned int * gpf3dat;

dev_t devno;
struct cdev led_cdev;
struct class * cls;
int led_open (struct inode *inode, struct file *file)
{
     printk(" led_open!!!\n");
     return 0;
}
int led_release (struct inode *inode, struct file *file)
{
     printk(" led_release!!!\n");
     return 0;
}
long led_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
    printk(" led_ioctl !!!\n");
    switch (cmd)
    {
    case LED2_ON:
        *gpx2dat = (*gpx2dat)|(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)&~(0x3 << 4);
        break;
    case LED2_OFF:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
        break;
    case LED3_ON:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)|(0x1 << 0);
        *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
        break;
    case LED3_OFF:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
        break;
    case LED4_ON:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)|(0x1 << 4);
        *gpf3dat = (*gpf3dat)&~(0x1 << 5);  
        break;
    case LED4_OFF:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
        break;
    case LED5_ON:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)|(0x1 << 5);
        *gpf3dat = (*gpf3dat)&~(0x1 << 4);  
        break;
    case LED5_OFF:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
        break;
    case LEDALL_ON:
        *gpx2dat = (*gpx2dat)|(0x1 << 7);
        *gpx1dat = (*gpx1dat)|(0x1 << 0);
        *gpf3dat = (*gpf3dat)|(0x3 << 4);  
        break;
    case LEDALL_OFF:
        *gpx2dat = (*gpx2dat)&~(0x1 << 7);
        *gpx1dat = (*gpx1dat)&~(0x1 << 0);
        *gpf3dat = (*gpf3dat)&~(0x3 << 4);  
        break;
    default:
        printk(" fault cmd!!!\n");
        return -EFAULT;
        break;
    }
    return 0;
}
struct file_operations led_fops={
     .owner          = THIS_MODULE,
     .open           = led_open,
     .release        = led_release,
     .unlocked_ioctl = led_ioctl,
};


int __init led_init(void)
{
     int ret;
     printk(" led_init!!!\n");
     devno = MKDEV(LED_MAJOR,LED_MINOR);
     ret = register_chrdev_region(devno,LED_NUM,LED_NAME);
     if (ret < 0)
     {
         printk(" register_chrdev_region fail!!!\n");
         return -EFAULT;
     }
     printk(" register_chrdev_region success!!!\n");
     printk(" major=%d,minor=%d\n",MAJOR(devno),MINOR(devno));

    cdev_init(&led_cdev,&led_fops);
    led_cdev.owner = THIS_MODULE;

    cdev_add(&led_cdev,devno,LED_NUM);

    cls = class_create(THIS_MODULE,CLS_NAME);
    if (IS_ERR(cls))
    {
        printk(" class_create fail!!!\n");
        return -EFAULT;
    }
    device_create(cls,NULL,devno,NULL,DEV_NAME);

    gpx2con = ioremap(GPX2CON,0x4);
    if (NULL == gpx2con)
    {
        printk(" gpx2con ioremap fail!!!\n");
        return -EFAULT;
    }
    gpx1con = ioremap(GPX1CON,0x4);
    if (NULL == gpx1con )
    {
        printk(" gpx1con ioremap fail!!!\n");
        return -EFAULT;
    }
    gpf3con = ioremap(GPF3CON,0x4);
    if (NULL == gpf3con)
    {
        printk(" gpf3con ioremap fail!!!\n");
        return -EFAULT;
    }
    gpx2dat = gpx2con + 1;
    //gpx2dat = ioremap(0x1100c44,0x4);
    gpx1dat = gpx1con + 1;
    gpf3dat = gpf3con + 1;


    *gpx2con = ((*gpx2con)&~(0xf << 28))|(0x1 << 28);
    *gpx1con = ((*gpx1con)&~(0xf <<  0))|(0x1 <<  0);
    *gpf3con = ((*gpf3con)&~(0xff <<16))|(0x11 << 16);

    *gpx2dat =(*gpx2dat)|(0x1 << 7);
    *gpx1dat =(*gpx1dat)|(0x1 << 0);
    *gpf3dat =(*gpf3dat)|(0x3 << 4);



    return 0;
}
module_init(led_init);

void __exit led_exit(void)
{
     printk(" led_exit!!!\n");
     iounmap(gpf3con);
     iounmap(gpx1con);
     iounmap(gpx2con);

     device_destroy(cls,devno);
     class_destroy(cls);

     cdev_del(&led_cdev);
     unregister_chrdev_region(devno,LED_NUM);
}
module_exit(led_exit);

MODULE_LICENSE("GPL");

led.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "head.h"

int main(int argc, char *argv[])
{
    int fd;
    fd = open("/dev/led",O_RDWR);
    if (fd < 0)
    {
        printf(" open fail!!!\n");
        return -1;
    }
    for(;;)
    {
        ioctl(fd,LED2_ON);
        sleep(1);
        ioctl(fd,LED3_ON);
        sleep(1);
        ioctl(fd,LED4_ON);
        sleep(1);
        ioctl(fd,LED5_ON);
        sleep(1);
        ioctl(fd,LEDALL_ON);
        sleep(1);
    }
    close(fd);
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宗雇,一起剝皮案震驚了整個濱河市钳恕,隨后出現(xiàn)的幾起案子别伏,更是在濱河造成了極大的恐慌,老刑警劉巖忧额,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厘肮,死亡現(xiàn)場離奇詭異,居然都是意外死亡睦番,警方通過查閱死者的電腦和手機类茂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門耍属,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人巩检,你說我怎么就攤上這事厚骗。” “怎么了兢哭?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵领舰,是天一觀的道長。 經(jīng)常有香客問我迟螺,道長冲秽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任矩父,我火速辦了婚禮锉桑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浙垫。我一直安慰自己刨仑,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布夹姥。 她就那樣靜靜地躺著杉武,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辙售。 梳的紋絲不亂的頭發(fā)上轻抱,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音旦部,去河邊找鬼祈搜。 笑死,一個胖子當著我的面吹牛士八,可吹牛的內(nèi)容都是我干的容燕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼婚度,長吁一口氣:“原來是場噩夢啊……” “哼蘸秘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蝗茁,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤醋虏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后哮翘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颈嚼,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年饭寺,在試婚紗的時候發(fā)現(xiàn)自己被綠了阻课。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叫挟。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柑肴,靈堂內(nèi)的尸體忽然破棺而出霞揉,到底是詐尸還是另有隱情旬薯,我是刑警寧澤晰骑,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站绊序,受9級特大地震影響硕舆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骤公,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一抚官、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧阶捆,春花似錦凌节、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至垒棋,卻和暖如春卒煞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叼架。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工畔裕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乖订。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓扮饶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乍构。 傳聞我的和親對象是個殘疾皇子甜无,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

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