Linux 內核驅動自動創建設備節點並掛載設備


*注:本文來自http://blog.csdn.net/lwj103862095/article/details/17470573

 

一、首先需要在最開始定義兩個數據結構:

static struct class *firstdrv_class;  
static struct device *firstdrv_device; 

二、在init函數里通過class_create和device_create函數創建相應的設備節點,示例代碼如下:

static int first_drv_init(void)  
{  
    /* 主設備號設置為0表示由系統自動分配主設備號 */  
    major = register_chrdev(0, "first_drv", &first_drv_fops);  
  
    /* 創建firstdrv類 */  
    firstdrv_class = class_create(THIS_MODULE, "firstdrv");  
  
    /* 在firstdrv類下創建xxx設備,供應用程序打開設備*/  
    firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xxx");  
    return 0;  
}  

三、在exit函數里通過device_destroy、class_unregister、class_destroy函數對設備進行卸載

static void first_drv_exit(void)  
{  
    device_destroy(firstdrv_class,MKDEV(major,0));
    class_unregister(firstdrv_class);
    class_destroy(firstdrv_class);
    unregister_chrdev(major, "first_drv");  
}  

 

參考代碼一、

    #include <linux/kernel.h>  
    #include <linux/fs.h>  
    #include <linux/init.h>  
    #include <linux/delay.h>  
    #include <asm/uaccess.h>  
    #include <asm/irq.h>  
    #include <asm/io.h>  
    #include <linux/module.h>  
    #include <linux/device.h>     //class_create  
      
    static struct class *firstdrv_class;  
    static struct device *firstdrv_device;  
      
    int major;  
    static int first_drv_open(struct inode * inode, struct file * filp)  
    {  
        printk("first_drv_open\n");  
        return 0;  
    }  
    static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)  
    {  
        printk("first_drv_write\n");  
        return 0;  
    }  
      
    /* File operations struct for character device */  
    static const struct file_operations first_drv_fops = {  
        .owner      = THIS_MODULE,  
        .open       = first_drv_open,  
        .write      = first_drv_write,  
    };  
      
    /* 驅動入口函數 */  
    static int first_drv_init(void)  
    {  
        /* 主設備號設置為0表示由系統自動分配主設備號 */  
        major = register_chrdev(0, "first_drv", &first_drv_fops);  
      
        /* 創建firstdrv類 */  
        firstdrv_class = class_create(THIS_MODULE, "firstdrv");  
      
        /* 在firstdrv類下創建xxx設備,供應用程序打開設備*/  
        firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xxx");  
        return 0;  
    }  
      
    /* 驅動出口函數 */  
    static void first_drv_exit(void)  
    {  
        unregister_chrdev(major, "first_drv");  
        device_unregister(firstdrv_device);  //卸載類下的設備  
        class_destroy(firstdrv_class);      //卸載類  
    }  
      
    module_init(first_drv_init);  //用於修飾入口函數  
    module_exit(first_drv_exit);  //用於修飾出口函數      
      
    MODULE_AUTHOR("LWJ");  
    MODULE_DESCRIPTION("Just for Demon");  
    MODULE_LICENSE("GPL");  //遵循GPL協議  

參考代碼二

#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <mach/regs-gpio.h>
#include <linux/device.h>
#include <mach/hardware.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/errno.h>


#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/gpio.h>
#include <linux/io.h>

#include <mach/hardware.h>
#include <mach/map.h> 
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-e.h>
#include <mach/regs-clock.h> 
#include <asm/irq.h>

#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>

//#define DEBUG
/* 相關引腳定義,方便以后移植 */
#define DEVICE_NAME "ds18b20"
#define DQ         2
#define CFG_IN     0
#define CFG_OUT    1

// ds18b20主次設備號(動態分配)
int ds18b20_major = 0;
int ds18b20_minor = 0;
int ds18b20_nr_devs = 1;

// 定義設備類型
static struct ds18b20_device {
    struct cdev cdev;
};
struct ds18b20_device ds18b20_dev;

static struct class *ds18b20_class;

/* 函數聲明 */
static int ds18b20_open(struct inode *inode, struct file *filp);
static int ds18b20_init(void);
static void write_byte(unsigned char data);
static unsigned char read_byte(void);
static ssize_t ds18b20_read(struct file *filp, char __user *buf,
                            size_t count, loff_t *f_pos);
void ds18b20_setup_cdev(struct ds18b20_device *dev, int index);



void s3c6410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
    //s3c_gpio_cfgpin(pin,function);
        unsigned int tmp; 
        tmp = readl(S3C64XX_GPECON); 
        tmp = (tmp & ~(0XF<<pin*4))|(function<<pin*4); 
        writel(tmp, S3C64XX_GPECON); 
}

void s3c6410_gpio_pullup(unsigned int pin, unsigned int to)
{
        //s3c_gpio_setpull(pin,to);
        unsigned int tmp; 
        tmp = readl(S3C64XX_GPEPUD); 
        tmp = (tmp & ~(3<<pin*2))|(to<<pin*2); 
        writel(tmp, S3C64XX_GPEPUD); 
}

unsigned int s3c6410_gpio_getpin(unsigned int pin) 
{ 
        unsigned int tmp;
    tmp = readl(S3C64XX_GPEDAT); 
    tmp = tmp & (1 << (pin));  

    return tmp; 
}

void s3c6410_gpio_setpin(unsigned int pin, unsigned int dat)
{
        unsigned int tmp; 
    tmp = readl(S3C64XX_GPEDAT); 
    tmp &= ~(1 << (pin)); 
    tmp |= ( (dat) << (pin) ); 
    writel(tmp, S3C64XX_GPEDAT); ;
}



/******************************************************************************************************
** 函數名稱: ds18b20_open()
** 函數功能: 打開設備,初始化ds18b20
** 入口參數: inode:設備文件信息; filp: 被打開的文件的信息
** 出口參數: 成功時返回0,失敗返回-1
** 備    注:
******************************************************************************************************/
static int ds18b20_open(struct inode *inode, struct file *filp)
{
    int flag = 0;
    /*struct ds18b20_device *dev;
    dev = container_of(inode->i_cdev, struct ds18b20_device, cdev);
    filp->private_data = dev;*/

    flag = ds18b20_init();
    if(flag & 0x01)
    {
#ifdef DEBUG
        printk(KERN_WARNING "open ds18b20 failed\n");
#endif
    return -1;
    }
#ifdef DEBUG
    printk(KERN_NOTICE "open ds18b20 successful\n");
#endif
    return 0;
}

/******************************************************************************************************
** 函數名稱: ds18b20_init()
** 函數功能: 復位ds18b20
** 入口參數: 無
** 出口參數: retval:成功返回0,失敗返回1
** 備    注: 操作時序見ds18b20 datasheet
******************************************************************************************************/
static int ds18b20_init(void)
{
    int retval = 0;

    s3c6410_gpio_cfgpin(DQ, CFG_OUT);
    s3c6410_gpio_pullup(DQ, 0);

    s3c6410_gpio_setpin(DQ, 1);
    udelay(2);
    s3c6410_gpio_setpin(DQ, 0);        // 拉低ds18b20總線,復位ds18b20
    udelay(500);                       // 保持復位電平500us

    s3c6410_gpio_setpin(DQ, 1);        // 釋放ds18b20總線
    udelay(60);

    // 若復位成功,ds18b20發出存在脈沖(低電平,持續60~240us)
    s3c6410_gpio_cfgpin(DQ, CFG_IN);
    retval = s3c6410_gpio_getpin(DQ);

    udelay(500);
    s3c6410_gpio_cfgpin(DQ, CFG_OUT);
    s3c6410_gpio_pullup(DQ, 0);
    s3c6410_gpio_setpin(DQ, 1);        // 釋放總線
    
    return retval;
}

/******************************************************************************************************
** 函數名稱: write_byte()
** 函數功能: 向18b20寫入一個字節數據
** 入口參數: data
** 出口參數: 無
** 備    注:
******************************************************************************************************/
static void write_byte(unsigned char data)
{
    int i = 0;

    s3c6410_gpio_cfgpin(DQ, CFG_OUT);
    s3c6410_gpio_pullup(DQ, 1);

    for (i = 0; i < 8; i ++)
    {
        // 總線從高拉至低電平時,就產生寫時隙
        s3c6410_gpio_setpin(DQ, 1);
        udelay(2);
        s3c6410_gpio_setpin(DQ, 0);
        s3c6410_gpio_setpin(DQ, data & 0x01);
        udelay(60);
    data >>= 1;
    }
    s3c6410_gpio_setpin(DQ, 1);        // 重新釋放ds18b20總線
}

/******************************************************************************************************
** 函數名稱: read_byte()
** 函數功能: 從ds18b20讀出一個字節數據
** 入口參數: 無
** 出口參數: 讀出的數據
** 備    注:
******************************************************************************************************/
static unsigned char read_byte(void)
{
    int i;
    unsigned char data = 0;

    for (i = 0; i < 8; i++)
    {
        // 總線從高拉至低,只需維持低電平17ts,再把總線拉高,就產生讀時隙
        s3c6410_gpio_cfgpin(DQ, CFG_OUT);
        s3c6410_gpio_pullup(DQ, 0);
        s3c6410_gpio_setpin(DQ, 1);
        udelay(2);
        s3c6410_gpio_setpin(DQ, 0);
        udelay(2);
    s3c6410_gpio_setpin(DQ, 1);
        udelay(8);
        data >>= 1;
    s3c6410_gpio_cfgpin(DQ, CFG_IN);
    if (s3c6410_gpio_getpin(DQ))
        data |= 0x80;
    udelay(50);
    }
    s3c6410_gpio_cfgpin(DQ, CFG_OUT);
    s3c6410_gpio_pullup(DQ, 0);
    s3c6410_gpio_setpin(DQ, 1);        // 釋放ds18b20總線
    return data;
}
/******************************************************************************************************
** 函數名稱: ds18b20_read()
** 函數功能: 讀出18b20的溫度
** 入口參數: 
** 出口參數: 
** 備    注:
******************************************************************************************************/
static ssize_t ds18b20_read(struct file *filp, char __user *buf,
                            size_t count, loff_t *f_pos)
{
    int flag;
    unsigned long err;
    unsigned char result[2] = {0x00, 0x00};
    //struct ds18b20_device *dev = filp->private_data;

    flag = ds18b20_init();
    if (flag)
    {
#ifdef DEBUG
        printk(KERN_WARNING "ds18b20 init failed\n");
#endif
        return -1;
    }
    
    write_byte(0xcc);
    write_byte(0x44);

    flag = ds18b20_init();
    if (flag)
        return -1;

    write_byte(0xcc);
    write_byte(0xbe);

    result[0] = read_byte();    // 溫度低八位
    result[1] = read_byte();    // 溫度高八位
    
    err = copy_to_user(buf, &result, sizeof(result));
    return err ? -EFAULT : min(sizeof(result),count);
}

/**************************************************************
 * 字符驅動程序的核心,應用程序所調用的open,read等函數最終會
 * 調用這個結構中的對應函數
 *************************************************************/
static struct file_operations ds18b20_dev_fops = {
    .owner = THIS_MODULE,
    .open = ds18b20_open,
    .read = ds18b20_read,
};

/******************************************************************************************************
** 函數名稱: ds18b20_setup_cdev()
** 函數功能: 初始化cdev
** 入口參數: dev:設備結構體; index:
** 出口參數: 無
** 備    注:
******************************************************************************************************/
void ds18b20_setup_cdev(struct ds18b20_device *dev, int index)
{
    int err, devno = MKDEV(ds18b20_major, ds18b20_minor + index);

    cdev_init(&dev->cdev, &ds18b20_dev_fops);
    dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&(dev->cdev), devno, 1);
    if (err)
    {
#ifdef DEBUG
        printk(KERN_NOTICE "ERROR %d add ds18b20\n", err);
#endif
    }
}

/******************************************************************************************************
** 函數名稱: ds18b20_dev_init()
** 函數功能: 為溫度傳感器分配注冊設備號,初始化cdev
** 入口參數: 無
** 出口參數: 若成功執行,返回0
** 備    注:
******************************************************************************************************/
static int __init ds18b20_dev_init(void)
{
    ds18b20_major = register_chrdev(ds18b20_major, DEVICE_NAME, &ds18b20_dev_fops);
    if (ds18b20_major<0)
    {
    printk(DEVICE_NAME " Can't register major number!\n");
    return -EIO;
    }

    ds18b20_class = class_create(THIS_MODULE, DEVICE_NAME);
    device_create(ds18b20_class, NULL, MKDEV(ds18b20_major, ds18b20_minor), NULL, DEVICE_NAME);
#ifdef DEBUG
    printk(KERN_WARNING "register ds18b20 driver successful!\n");
#endif
    return 0;
}

/******************************************************************************************************
** 函數名稱: ds18b20_dev_exit()
** 函數功能: 注銷設備
** 入口參數: 無
** 出口參數: 無
** 備    注:
******************************************************************************************************/
static void __exit ds18b20_dev_exit(void)
{
    device_destroy(ds18b20_class, MKDEV(ds18b20_major,ds18b20_minor));
    class_unregister(ds18b20_class);
    class_destroy(ds18b20_class);
    unregister_chrdev(ds18b20_major, DEVICE_NAME);
#ifdef DEBUG
    printk(KERN_WARNING "Exit ds18b20 driver!\n");
#endif
}

module_init(ds18b20_dev_init);
module_exit(ds18b20_dev_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("xinli_whut@163.com");
/******************************************************************************************************
**                                         文件到此結束
******************************************************************************************************/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM