簡單的字符驅動

簡單的字符驅動

設備驅動程序,簡稱驅動程序,是一個允許高級電腦軟件與硬件交互的程序,這種程序創建了一個硬件與硬件,或硬件與軟件溝通的界面,經由主板上的總線或其它溝通子系統與硬件形成連接的機制,這樣的機制使得硬件設備上的數據交換成爲可能。(摘自維基百科–驅動程序

在Linux中,設備分三種:字符設備、塊設備和網絡設備。下面主要介紹字符設備。字符設備是能夠像字節流(類似文件)一樣被訪問的設備,有字符設備驅動程序來實現這種特性。字符設備驅動程序通常至少要實現open、close、read、write系統調用。字符設備可以通過文件系統節點來訪問,這些設備文件和普通文件之間的唯一差別在於對普通文件的訪問可以前後移動訪問位置,而大多數字符設備是一個只能順序訪問的數據通道。一個字符設備是一種字節流設備,對設備的存取只能按順序按字節的存取而不能隨機訪問,字符設備沒有請求緩衝區,所有的訪問請求都是按順序執行的。

這次我們的實驗要求是寫一個簡單的字符設備驅動,動態地裝載和卸載該字符設備驅動程序。同時,編寫一個測試程序,對該設備驅動程序進行測試,以證明設備驅動程序能正常工作。

下面是驅動程序devDrv.c的代碼:

#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/init.h"
#include "linux/types.h"
#include "linux/errno.h"
#include "linux/uaccess.h"
#include "linux/kdev_t.h"
#define MAX_SIZE 1024

static int my_open(struct inode *inode, struct file *file);
static int my_release(struct inode *inode, struct file *file);
static ssize_t my_read(struct file *file, char __user *user, size_t t, loff_t *f);
static ssize_t my_write(struct file *file, const char __user *user, size_t t, loff_t *f);

static char message[MAX_SIZE] = "-------congratulations--------!";
static int device_num = 0;
static int mutex = 0;
static char* devName = "myDevice";
struct file_operations pStruct =
{ open:my_open, release:my_release, read:my_read, write:my_write, };
int init_module()
{
    int ret;
    ret = register_chrdev(0, devName, &pStruct);
    if (ret < 0)
    {
        printk("regist failure!\n");
        return -1;
    }
    else
    {
        printk("the device has been registered!\n");
        device_num = ret;
        printk("<1>the virtual device's major number %d.\n", device_num);
        printk("<1>Or you can see it by using\n");
        printk("<1>------more /proc/devices-------\n");
        printk("<1>To talk to the driver,create a dev file with\n");
        printk("<1>------'mknod /dev/myDevice c %d 0'-------\n", device_num);
        printk("<1>Use \"rmmode\" to remove the module\n");

        return 0;
    }
}
void cleanup_module()
{
    unregister_chrdev(device_num, devName);
    printk("unregister it success!\n");
}

static int my_open(struct inode *inode, struct file *file)
{
        if(mutex)
                return -EBUSY;
        mutex = 1;
    printk("<1>main  device : %d\n", MAJOR(inode->i_rdev));
    printk("<1>slave device : %d\n", MINOR(inode->i_rdev));
    printk("<1>%d times to call the device\n", ++counter);
    try_module_get(THIS_MODULE);
    return 0;
}
static int my_release(struct inode *inode, struct file *file)
{
    printk("Device released!\n");
    module_put(THIS_MODULE);
        mutex = 0; return 0;
}
static ssize_t my_read(struct file *file, char __user *user, size_t t, loff_t *f)
{
    if(copy_to_user(user,message,sizeof(message)))
    {
        return -EFAULT;
    }
    return sizeof(message);
}
static ssize_t my_write(struct file *file, const char __user *user, size_t t, loff_t *f)
{
    if(copy_from_user(message,user,sizeof(message)))
    {
        return -EFAULT;
    }
    return sizeof(message);

下面是makefile文件的主要代碼:

# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifeq ($(KERNELRELEASE),)
    # Assume the source tree is where the running kernel was built
    # You should set KERNELDIR in the environment if it's elsewhere
    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    # The current directory is passed to sub-makes as argument
    PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
    # called from kernel build system: just declare what our modules are
    obj-m := devDrv.oif

下面是測試驅動程序是否正常的測試代碼test.c的核心代碼

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define MAX_SIZE 1024
int main(void)
{
    int fd;
    char buf[MAX_SIZE];
    char get[MAX_SIZE];
    char devName[20], dir[50] = "/dev/";
    system("ls /dev/");
    printf("Please input the device's name you wanna to use :");
    gets(devName);
    strcat(dir, devName);
    fd = open(dir, O_RDWR | O_NONBLOCK);
    if (fd != -1)
    {
        read(fd, buf, sizeof(buf));
        printf("The device was inited with a string : %s\n", buf);
        printf("Please input a string  :\n");
        gets(get);
        write(fd, get, sizeof(get));
        read(fd, buf, sizeof(buf)); 
        system("dmesg");
        printf("\nThe string in the device now is : %s\n", buf);
        close(fd);
        return 0;
    }
    else
    {
        printf("Device open failed\n");
        return -1;
    }
}

在代碼完成之後將這三個文件放在一個文件夾裏

這裏寫圖片描述
進入文件夾所在目錄,在終端中打開,以root用戶輸入make命令開始編譯模塊
這裏寫圖片描述
make完之後,利用ls命令查看當前目錄下的文件,發現多了一些.ko之類的模塊,說明make成功編譯了模塊
這裏寫圖片描述
接下來先利用lsmod命令查看系統中已有的模塊
這裏寫圖片描述

接着利用insmod devDrv.ko將編譯好的模塊裝載如系統中,再利用lsmod查看模塊,發現了devDrv,說明裝載成功。
這裏寫圖片描述
利用 cat /proc/devices 查看主設備號,發現myDevice的主設備號爲250。(什麼是主設備號和此設備號呢?簡單的來說主設備號對應着一個驅動程序,次設備號對應着該驅動程序下的每個硬件設備。打個比方:主設備號對應着你電腦裏的U盤驅動程序,而次設備號就對應着你電腦上插着的各個U盤)
這裏寫圖片描述
接着是利用mknod /dev/myDevice c 250 0分配從設備號,用ls /dev/查看是否有myDevice
這裏寫圖片描述
在發現設備中有myDevice之後就可以對字符設備進行測試了。編譯首先生成測試程序:gcc test.c –o tl
得到tl可執行程序,再執行:./tl
測試程序首先列出所有的設備名,讓我們選中一個,輸入myDevice
這裏寫圖片描述
正確讀出之前存放在設備中的字符串!然後讓我們輸入一個字符串
這裏寫圖片描述
輸入自己想要輸入的字符就可以成功將輸入的字符讀取出來並顯示在屏幕上面

從上面的結果可以看出,該字符設備成功的讀取了輸入的內容,並將其顯示出來。所以接下來要做的就是看能不能將該字符設備刪除,並將驅動卸載。

首先刪除設備,就像刪除普通文件一樣:rm /dev/myDevice
刪除後,看看/dev/目錄下是否還有myDevice:ls /dev/
這裏寫圖片描述
發現dev目錄下面沒有了myDevice設備了,接着刪除模塊:rmmod devDrv.
看看模塊列表中是否已經沒有devDrv模塊了:lsmod
這裏寫圖片描述

下面是博主在學習相關內容時的參考鏈接

http://blog.csdn.net/creazyapple/article/details/7290680

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