linux內核字符設備驅動之設備文件自動創建

1.  linux內核字符設備驅動之設備文件的自動創建

設備文件創建:


手動創建:
mknod /dev/設備文件名 c/b 主設備號 次設備號

自動創建:
作用:每當加載完設備驅動以後,設備文件也會創建成功!
每當卸載驅動以後,設備文件也會自動刪除;


設備文件自動創建的編程步驟:
1.根文件系統要支持mdev可執行程序
mdev的作用就是將來創建設備文件;

mdev是udev的一個簡化版本,適用嵌入式系統;
which is mdev //查看mdev在哪個目錄下

2.根文件系統的etc目錄下要有fstab文件,此文件內 容必須添加以下三句話:
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
說明:
tmpfs,sysfs,procfs是三種虛擬文件系統,那麼對應的入口分別是/dev目錄,/sys目錄,/proc目錄,三個目錄下的內容都是存在內存上;
/dev/目錄將來存放創建的設備文件
/sys目錄將來存放創建設備文件所需的原材料:主設備號,次設備號,設備文件名字
/proc目錄存放hotplug配置文件,給驅動使用

3.根文件系統的啓動腳本etc/init.d/rcS文件中必須 添加以下句話:
mount -a #每當執行這句話時,fstab將會被使用
echo /sbin/mdev > /proc/sys/kernel/hotplug #告訴內核和驅動將來創建設備文件的人是mdev
mdev -s #系統啓動完畢,將系統默認支持的設備的設備文件自動創建完畢

4.設備驅動只需利用以下四個函數即可完成設備文件的最終創建
struct class *cls; //創建設備類指針
//定義初始化設備類(長樹枝,樹枝名叫tarena)
//結果是在/sys/class目錄下生成一個tarena目錄,存放創建設備文件所需的原材料
cls = class_create(THIS_MODULE, "tarena");
//創建設備文件(長蘋果)
//結果是在/dev/生成設備文件
device_create(cls, NULL, 申請的設備號,NULL,設備文件名);

//刪除設備文件(採摘蘋果)
device_destroy(cls, 申請的設備號);
//刪除設備類(砍樹枝)
class_destroy(cls);

案例:在以往的字符設備驅動中添加設備文件的自動創建功能

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> //struct file_operations
#include <linux/cdev.h> //struct cdev + 設備號
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/uaccess.h> //copy_to_user
#include <linux/device.h> //設備文件自動創建

//聲明LED操作的數據結構
struct led_cmd {
    int index;
    int cmd;
};

//聲明描述LED硬件相關的數據結構
struct led_resource {
    char *name;
    int gpio;
};

//定義初始化LED硬件信息
static struct led_resource led_info[] = {
    [0] = {
        .name = "LED1",
        .gpio = S5PV210_GPC0(3)
    },
    [1] = {
        .name = "LED2",
        .gpio = S5PV210_GPC0(4)
    }
};

//定義設備號
static dev_t dev;

//定義字符設備對象
static struct cdev led_cdev;

//定義設備類指針
static struct class *cls;

//調用關係:應用程序open->....->led_open
static int led_open(struct inode *inode, struct file *file)
{
    int i;
    
    for(i = 0; i < ARRAY_SIZE(led_info); i++)
        gpio_set_value(led_info[i].gpio, 1);
    
    printk("%s\n", __func__);
    return 0; //執行成功返回0,執行失敗返回負值    
}

//調用關係:應用程序close->...->led_close
static int led_close(struct inode *inode, struct file *file)
{
    int i;
    
    for(i = 0; i < ARRAY_SIZE(led_info); i++)
        gpio_set_value(led_info[i].gpio, 0);
    
    printk("%s\n", __func__);
    return 0; //執行成功返回0,執行失敗返回負值    
}

//調用關係:應用程序read->...->led_read
static ssize_t led_read(struct file *file,
                        char __user *buf,
                        size_t count,
                        loff_t *ppos)
{
    //定義初始化內核緩衝區(存儲空間再後1G虛擬內存中)
    int kdata = 0x5555;

    //將內核數據上報給用戶
    //切記:buf雖然保存的用戶緩衝區的首地址,但不能直接訪問
    //*(int *)buf = kdata;錯誤
    copy_to_user(buf, &kdata, sizeof(kdata));
    printk("%s\n", __func__);    
    return sizeof(kdata); //失敗返回負值,成功返回實際讀取的字節數
}

//調用關係:應用程序write->...->最終調用led_write
static ssize_t led_write(struct file *file,
                        const char __user *buf,
                        size_t count,
                        loff_t *ppos)
{
    //定義內核緩衝區
    struct led_cmd kdata;

    //拷貝用戶數據到內核
    copy_from_user(&kdata, buf, sizeof(kdata));
    
    //開或者關燈
    gpio_set_value(led_info[kdata.index - 1].gpio, kdata.cmd);
    printk("%s:第%d燈被%s\n", 
            __func__, kdata.index, kdata.cmd?"打開":"關閉");
    return count; //失敗返回負值,成功返回寫入的字節數
}

#define LED_ON  0x100001 //開燈命令
#define LED_OFF 0x100002 //關燈命令

static long led_ioctl(struct file *file,
                        unsigned int cmd,
                        unsigned long arg)
{
    //定義內核緩衝區,保存用戶緩衝區的數據
    int kindex;

    //拷貝用戶數據到內核
    copy_from_user(&kindex, (int *)arg, sizeof(kindex));
    
    //解析處理用戶命令
    switch(cmd) {
        case LED_ON:
            gpio_set_value(led_info[kindex-1].gpio, 1);
            printk("%s:第%d個燈被打開!\n", __func__, kindex);
            break;
        case LED_OFF:
            gpio_set_value(led_info[kindex-1].gpio, 0);
            printk("%s:第%d個燈被關閉!\n", __func__, kindex);
            break;
        default:
            return -1;
    }
    return 0; //成功返回0,失敗返回-1
}

//定義初始化硬件操作方法
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_open, //打開設備
    .release = led_close, //關閉設備
    .read = led_read, //讀取設備
    .write = led_write, //寫設備
    .unlocked_ioctl = led_ioctl //向設備發送命令和完成讀寫
};

static int led_init(void)
{
    int i;
    //申請設備號
    alloc_chrdev_region(&dev, 0, 1, "tarena");
    
    //初始化字符設備對象
    cdev_init(&led_cdev, &led_fops);
    
    //註冊字符設備對象到內核
    cdev_add(&led_cdev, dev, 1);
    
    //申請GPIO資源和配置GPIO爲輸出口,輸出0(省電)
    for (i = 0; i < ARRAY_SIZE(led_info); i++) {
        gpio_request(led_info[i].gpio, led_info[i].name); 
        gpio_direction_output(led_info[i].gpio, 0);
    }

    //創建設備類(長樹枝)
    //結果/sys/class/tarena(新創建)
    cls = class_create(THIS_MODULE, "tarena");
   
    //創建設備文件(長蘋果)
    //結果自動創建設備文件/dev/myled
    device_create(cls, NULL, dev, NULL, "myled");
    return 0;
}

static void led_exit(void)
{
    int i;
    
    //刪除設備文件(採摘蘋果)
    device_destroy(cls, dev);
    //刪除設備類(砍樹枝)
    class_destroy(cls);
    
    //輸出0,釋放GPIO資源
    for (i = 0; i < ARRAY_SIZE(led_info); i++) {
        gpio_set_value(led_info[i].gpio, 0);
        gpio_free(led_info[i].gpio);
    }
    //卸載字符設備對象
    cdev_del(&led_cdev);
    //釋放設備號
    unregister_chrdev_region(dev, 1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");


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