[設備驅動] 最簡單的內核設備驅動--字符驅動

[設備驅動] 最簡單的內核設備驅動--字符驅動

 概要:

x86平臺上(linux-2.6.34.14;Linux debian 3.2.0-3-686-pae)編寫一個256字節的字符驅動程序.在/dev下生成設備節點,供用戶訪問.


一.目錄結構

[root:simple_cdev] tree
.
├── demo.c
├── demo.h
├── example
│   ├── main.cpp
│   └── Makefile
└── Makefile


1 directory, 5 files

二.demo內核驅動模塊源碼和編譯:

2.1  字符驅動demo.c源碼:

[root:simple_cdev] cat demo.c
/// @file demo.c
/// @brief 
/// @author Easton Woo 
/// 0.01
/// @date 2013-09-02

#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include "demo.h"

#include <linux/device.h>


int DEMO_open(struct inode *innode, struct file *filep);
int DEMO_close(struct inode *innode, struct file *filp);
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t* p_pos);
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t* p_pos);
loff_t DEMO_seek(struct file *filp, loff_t offset, int where);
long DEMO_ioctl(struct file * filp, unsigned int cmd, unsigned long arg);

int DEMO_init_module(void);
void DEMO_cleanup_module(void);


struct DEMO_dev
{
    struct cdev cdev;
};

struct file_operations DEMO_fops = {
    .owner = THIS_MODULE,
    .open = DEMO_open,
    .release = DEMO_close,
    .read = DEMO_read,
    .write = DEMO_write,
    .llseek = DEMO_seek,
    .unlocked_ioctl = DEMO_ioctl,  //.ioctl 成員變量在我的這個內核版本中已經不能用了。
};

struct DEMO_dev* DEMO_devices;
static struct class* class_demo;
static unsigned char demo_inc = 0;
static u8 demoBuffer[256];
#define DEMO_MAJOR 224
#define DEMO_MINNOR 0


//////////////////////設備操作//////////////////////////////
int DEMO_open(struct inode *innode, struct file *filep)
{
    struct DEMO_dev * dev = NULL;


    if( demo_inc > 0 )
    {
        return -ERESTARTSYS;
    }
    demo_inc++; 

    dev = container_of(innode->i_cdev,struct DEMO_dev,cdev);
    filep->private_data = dev;

    printk(KERN_WARNING "DEMO: device is open!\n");
    return 0;
}

int DEMO_close(struct inode *innode, struct file *filp)
{
    if( demo_inc > 0 )
    {
        demo_inc--;
    }
    printk(KERN_WARNING "DEMO: device is close!\n");
    return 0;
}

ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t* p_pos)
{
    loff_t pos = *p_pos;
    int iReadCount = 0;

    if(pos >  256 - 1)
    {
        return -1;
    }

    if(256 - pos < count)
    {
        count = 256 -pos;
    }

    iReadCount = copy_to_user(buf, demoBuffer+pos,count);

    if(iReadCount == 0)
    {
        printk(KERN_INFO "DEMO: read %d data successful\n",count );
    }
    else
    {
        printk(KERN_INFO "DEMO: read %d data fail\n",count );
    }

    count = iReadCount;
    return count;
}

ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t* p_pos)
{
    loff_t pos = *p_pos;
    int iWriteCount = 0;

    if(pos >  256 - 1)
    {
        return -1;
    }

    if(256 - pos < count)
    {
        count = 256 -pos;
    }

    iWriteCount = copy_from_user(demoBuffer+pos,buf,count);

    if(iWriteCount == 0)
    {
        printk(KERN_INFO "DEMO: write %d data successful\n",count );
    }
    else
    {
        printk(KERN_INFO "DEMO: write %d data fail\n",count );
    }

    count = iWriteCount;
    return 0;
}

loff_t DEMO_seek(struct file *filp, loff_t offset, int where)
{
    loff_t pos = filp->f_pos;
    switch(where)
    {
        case 0:
            pos = offset;
            break;
        case 1:
            pos += offset;
            break;
        case 2:
            pos = 256-1 - offset; 
            break;
    }

    if(pos > 256 -1 || pos < 0)
    {
        return -1;
    }

    printk(KERN_INFO "DEMO: seek move %llu is OK!\n",offset);
    return filp->f_pos = pos;
}

long DEMO_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
        case DEMO_IO_INFO:
            printk(KERN_INFO "DEMO: run commod \"DEMO_IO_INFO\" successful !\n");
            break;
        case DEMO_IO_OK:
            printk(KERN_INFO "DEMO: run commod \"DEMO_IO_OK\" successful !\n");
            break;
        default:
            printk(KERN_INFO "DEMO: error commod %d!\n",cmd);
            return -1;
            break;
    }
    return 0;
}


//////////////////////註冊銷燬//////////////////////////////
int DEMO_init_module(void)
{
    int result;
    dev_t dev = 0;
    dev = MKDEV(DEMO_MAJOR,DEMO_MINNOR);

    result = register_chrdev_region(dev, 1, "DEMO");
    if(result < 0)
    {
        printk(KERN_WARNING "DEMO: can't get major %d\n",DEMO_MAJOR); 
        return result;
    }

    DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL);
    if(!DEMO_devices)
    {
        result = -ENOMEM;
        goto fail;
    }
    memset(DEMO_devices, 0, sizeof(struct DEMO_dev));

    cdev_init(&DEMO_devices->cdev, &DEMO_fops);
    DEMO_devices->cdev.owner = THIS_MODULE;
    DEMO_devices->cdev.ops = &DEMO_fops;

    result = cdev_add(&DEMO_devices->cdev, dev, 1);
    if (result)
    {
        printk(KERN_WARNING "DEMO: Error %d adding DEMO\n", result);
        goto fail;
    }

    class_demo = class_create(THIS_MODULE, "demo_cdev");//在/sys目錄下建立了一個demo_cdev的目錄
    if (IS_ERR(class_demo)) {
        printk(KERN_INFO "DEMO: create class error\n");
        return -1;
    }
    device_create(class_demo,NULL,dev,NULL,"demo_inode" "%d", MINOR(dev)); 

    memset(demoBuffer,0,sizeof(demoBuffer));

    printk(KERN_WARNING "DEMO:[test] add to kerner!\n");
    return 0;

fail:
    DEMO_cleanup_module();
    return result;
}

void DEMO_cleanup_module(void)
{
    dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINNOR);
    unregister_chrdev_region(devno, 1);

    if (DEMO_devices)
    {
        cdev_del(&DEMO_devices->cdev);
        kfree(DEMO_devices);
        device_destroy(class_demo, devno);//在2.6早期的版本應該使用class_device_destroy,早於2.6.27? 2.6.29? 忘記了。
        class_destroy(class_demo);

    }

    printk(KERN_WARNING "DEMO:[test] remove in kerner!\n");
}

module_init(DEMO_init_module);
module_exit(DEMO_cleanup_module);
MODULE_LICENSE("GPL"); //解決 Error: could not insert module demo.ko: Unknown symbol in module



2.2 編寫MakeFile編譯:


[root:simple_cdev] cat Makefile 
AR = ar
# ARCH = i386 
CC = gcc

# 解決方法:在Makefile中將"CFLAGS"替換成"EXTRA_CFLAGS"。 
EXTRA_CFLAGS += -Wall
# $(MAKE)

ifneq ($(KERNELRELEASE),)
obj-m := demo.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
	@rm -rf *.o *.ko *.mod.c *.order *.symvers
endif


2.3 編譯出demo.ko內核驅動模塊:

[root:simple_cdev] ls
demo.c  demo.h  example/  Makefile
[root:simple_cdev] make
make -C /lib/modules/3.2.0-3-686-pae/build M=/work/my_test/driver/simple_cdev modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-3-686-pae'
  CC [M]  /work/my_test/driver/simple_cdev/demo.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /work/my_test/driver/simple_cdev/demo.mod.o
  LD [M]  /work/my_test/driver/simple_cdev/demo.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-3-686-pae'
[root:simple_cdev] ls
demo.c  demo.h  demo.ko  demo.mod.c  demo.mod.o  demo.o  example/  Makefile  modules.order  Module.symvers
[root:simple_cdev] 


三.測試代碼和編譯:

3.1 測試代碼 main.cpp 和Makfile

[user:example] cat main.cpp 
/// @file main.cpp
/// @brief 
//
/// @author Easton Woo 
/// 0.01
/// @date 2013-09-02


#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "../demo.h"

int main()
{
    int fd = -1;
    fd = open("/dev/demo_inode0",O_RDWR);
    if(fd < 0)
    {
        printf("open /dev/demo_inode0 is error!\n");
        return -1;
    }

    write(fd, "hello", sizeof("hello"));

    lseek(fd, 0, SEEK_SET);

    char buf[256];
    memset(buf,0,sizeof(buf));
    read(fd, buf, sizeof(buf));
    printf("%s\n",buf);


    ioctl(fd, DEMO_IO_INFO, 0);
    // ioctl(fd, DEMO_IO_OK, 0);
    ioctl(fd, 3, 0);

    close(fd); fd = -1;

    return 0;
}
[user:example]
[user:example] cat Makefile 
all:
	g++ -o test_demo_app.i386.elf main.cpp


.PHONY: clean
clean:
	rm *.elf 



3.2 編譯出測試程序test_demo_app.i386.elf:

[root:simple_cdev] cd example/
[root:example] ls
main.cpp  Makefile
[root:example] make
g++ -o test_demo_app.i386.elf main.cpp
[root:example] ls
main.cpp  Makefile  test_demo_app.i386.elf*
[root:example] 

四.驅動加載內核,測試結果:

[root:simple_cdev] ls
demo.c  demo.h  demo.ko  demo.mod.c  demo.mod.o  demo.o  example/  Makefile  modules.order  Module.symvers

[root:simple_cdev] ls /dev/demo_inode0 
ls: 無法訪問/dev/demo_inode0: 沒有那個文件或目錄
[root:simple_cdev] dmesg --clear
[root:simple_cdev] 
[root:simple_cdev] dmesg 
[root:simple_cdev] insmod demo.ko 
[root:simple_cdev] lsmod | grep 'demo'
demo                   12592  0 

[root:simple_cdev] ls /dev/demo_inode0 
/dev/demo_inode0
[root:simple_cdev] dmesg 
[170724.473397] DEMO:[test] add to kerner!
[root:simple_cdev] ./example/test_demo_app.i386.elf 
hello
[root:simple_cdev] dmesg 
[170724.473397] DEMO:[test] add to kerner!
[170780.200620] DEMO: device is open!
[170780.200687] DEMO: write 6 data successful
[170780.200716] DEMO: seek move 0 is OK!
[170780.200783] DEMO: read 256 data successful
[170780.201758] DEMO: run commod "DEMO_IO_INFO" successful !
[170780.201768] DEMO: error commod 3!
[170780.201779] DEMO: device is close!

[root:simple_cdev] rmmod demo 
[root:simple_cdev] dmesg 
[170724.473397] DEMO:[test] add to kerner!
[170780.200620] DEMO: device is open!
[170780.200687] DEMO: write 6 data successful
[170780.200716] DEMO: seek move 0 is OK!
[170780.200783] DEMO: read 256 data successful
[170780.201758] DEMO: run commod "DEMO_IO_INFO" successful !
[170780.201768] DEMO: error commod 3!
[170780.201779] DEMO: device is close!
[170816.292639] DEMO:[test] remove in kerner!
[root:simple_cdev] lsmod | grep 'demo'
[root:simple_cdev] 




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章