• <li id="00i08"><input id="00i08"></input></li>
  • <sup id="00i08"><tbody id="00i08"></tbody></sup>
    <abbr id="00i08"></abbr>
  • 新聞中心

    EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > linux 一個簡單的字符設(shè)備驅(qū)動例子

    linux 一個簡單的字符設(shè)備驅(qū)動例子

    作者: 時間:2011-06-30 來源:網(wǎng)絡(luò) 收藏

    先包含這些頭文件
    #include linux/module.h>
    #include linux/types.h>
    #include linux/fs.h>
    #include linux/errno.h>
    #include linux/mm.h>
    #include linux/sched.h>
    #include linux/init.h>
    #include linux/cdev.h>
    #include asm/io.h>
    #include asm/system.h>
    #include asm/uaccess.h>

    #define BUFFERSIZE 200
    #define DEVICE_MAJOR 250 /*設(shè)置一個主設(shè)備號*/


    static int device_major = DEVICE_MAJOR;
    定義一個與字符設(shè)備對應(yīng)的結(jié)構(gòu)體
    struct my_cdev
    {
    struct cdev cdev; /*cdev結(jié)構(gòu)體,與字符設(shè)備對應(yīng)*/

    /*下面可以定義一些與字符設(shè)備相關(guān)的數(shù)據(jù)*/
    usigned char mem[BUFFERSIZE];
    };

    struct my_cdev *my_cdevp; /*設(shè)備結(jié)構(gòu)體指針*/

    int my_cdev_open( struct inode *node, struct file *filp )
    {
    /*將設(shè)備結(jié)構(gòu)體指針賦給文件私有數(shù)據(jù)指針*/
    filp->private_data = my_cdevp /*這樣可以通過文件私有數(shù)據(jù)指針得到設(shè)備結(jié)構(gòu)體*/
    return 0;
    }

    int my_cdev_release( struct inode *node, struct file *filp )
    {
    return 0;
    }

    static size_t my_cdev_read( struct file *filp, char __user *buf, size_t size, loff_t *ppos )
    {
    unsigned long p = *ppos; /*文件當(dāng)前位置*/
    unsigned int count = size; /*要讀取的長度*/
    int ret = 0;

    struct my_cdev *dev = filp->private_data; /*通過文件私有數(shù)據(jù)指針得到設(shè)備結(jié)構(gòu)體,和前面的open對應(yīng)*/

    if ( p >= BUFFERSIZE )
    return count ? -ENXIO:0;
    if ( count > BUFFERSIZE - p )
    count = BUFFERSIZE - p;

    /*內(nèi)核空間->用戶空間*/
    if ( copy_to_user(buf, (void *)(dev->mem + p), count) )
    {
    ret = -EFAULT;
    }
    else
    {
    *ppos += count;
    ret = count;
    }

    return ret;
    }

    static size_t my_cdev_write( struct file *filp, const char __user *buf, size_t size, loff_t *ppos )
    {
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;

    struce my_cdev *dev = filp->private_data;

    if( p >= BUFFERSIZE )
    return count ? -ENIX : 0;
    if (count > BUFFERSIZE - p )
    count = BUFFERSIZE - p;

    if ( copy_from_user( dev->mem + p, buf, count))
    {
    ret = -EFAULT;
    }
    else
    {
    *ppos += count;
    ret = count;
    }

    return ret
    }

    static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig)
    {
    loff_t ret = 0;
    switch (orig)
    {
    case 0:
    if ( offset 0 )
    {
    ret = -EINVAL;
    break;
    }
    if (offset > BUFFERSIZE)
    {
    ret = -EINVAL;
    break;
    }
    filp->f_pos = (unsigned int)offset;
    ret = fips->f_pos;
    break;
    default:
    ret = -EINVAL;
    break;
    }

    return ret;
    }

    /*文件操作結(jié)構(gòu)體*/
    static const struct file_operstions my_cdev_fops =
    {
    .owner = THIS_MODULE,
    .open = my_cdev_open,
    .release = my_cdev_release,
    .read = my_cdev_read,
    .write = my_cdev_write,
    .llseek = my_cdev_llseek,
    };

    /*初始化并注冊cdev,就是注冊我們自己的字符設(shè)備*/
    static void my_cdev_setup( struct my_cdev *dev, int index )
    {
    int err;
    dev_t devno = MKDEV(DEVICE_MAJOR, index);

    cdev_init( dev->cdev, my_cdev_fops );
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = my_cdev_fops; /*我認(rèn)為在cdev_init里應(yīng)該做過賦值,應(yīng)該可以不用寫*/
    err = cdev_add( dev->cdev, devno, 1 );
    if (err)
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
    }

    static int __init my_cdev_init(void)
    {
    int result;
    dev_t devno = MKDEV(DEVICE_MAJOR, index);

    /*申請?jiān)O(shè)備號*/
    if ( device_major )
    {
    result = register_chrdev_region(devno, 1, "my_cdev");
    }
    else
    {
    result = alloc_chrdev_region( devno, 0, 1, "my_cdev");
    device_major = MAJOR(devno);
    }
    if ( result 0 )
    {
    return result;
    }

    my_cdevp = kmalloc(sizeof(struct my_cdev), GFP_KERNEL);
    if ( !my_cdevp )
    {
    result = -ENOMEM;
    goto fail_malloc;
    }
    memset(my_cdevp, 0, sizeof(struct my_cdev));

    my_cdev_setup(my_cdevp, 0);
    return 0;

    fail_malloc:
    unregister_chrdev_region(devno, 1);
    return result;
    }

    static void __exit my_cdev_exit(void)
    {
    cdev_del(my_cdevp->cdev);
    kfree(my_cdevp);
    unregister_chrdev_region(MKDEV(device_major, 0), 1);
    }

    MODULE_AUTHOR("Song Baohua");
    MODULE_LICENSE("Dual BSD/GPL");

    module_init(my_cdev_init);
    module_exit(my_cdev_exit);

    *********************************************************************************
    然后可以寫一個簡單的內(nèi)核模塊的Makefile,編譯make后生成mycdev.ko文件,insmod mycdev.ko,
    裝上我們自己的驅(qū)動,注意有可能在裝的時候提示說device busy什么的,這就是我們前面指定的主設(shè)備號現(xiàn)在
    有設(shè)備在用,我們就在重新指定一個在編譯。
    那怎么看有那些設(shè)備呢?可以用cat /proc/devices,就可以查看已經(jīng)有哪些主設(shè)備號已被占用。

    然后就可以自己先創(chuàng)建一個虛擬的字符設(shè)備mknod /dev/mycdev c 250 0

    ******************************************************
    下面就可以自己寫一個應(yīng)用程序來看我們自己的字符設(shè)備驅(qū)動是否OK。

    #include stdio.h>
    #include fctl.h>
    #include string.h>
    #include sys/stat.h>

    #define BUFFERSIZE 200

    int main( void )
    {
    int fp = 0 ;
    char str[BUFFERSIZE];

    fp = open( "/dev/mycdev", O_RDWR, S_IRUSR|S_IWUSR );
    if ( !fp )
    {
    printf("Open device failedn");
    return -1;
    }

    write( fp, "Hello, my devices", strlen("Hello, my devices") );

    lseek( fp, 0, 0 );/*修改字符設(shè)備里字符數(shù)組的位置,將字符數(shù)據(jù)位置設(shè)到開始的位置,不然下面的read操作將讀不到數(shù)據(jù)*/

    read( fp, str, BUFFERSIZE );

    printf("Read content: %sn", str );

    close(fp);
    }

    gcc -o sample sample.c

    最后運(yùn)行./sample

    應(yīng)該會輸出:Read content: Hello, my devices

    總結(jié)一下,我個人覺得字符設(shè)備驅(qū)動相關(guān)的API和數(shù)據(jù)結(jié)構(gòu)。
    1. struct cdev 一個設(shè)備對應(yīng)一個這個的數(shù)據(jù)結(jié)構(gòu),結(jié)構(gòu)體是重要的兩個字段ops 和dev(設(shè)備號)
    2. struct file_opertions 文件操作結(jié)構(gòu)體
    3. cdev_init(struct cdev *, struct file_opertions *) 主要就是把字符設(shè)備和對這個設(shè)備的文件操作結(jié)構(gòu)體對應(yīng)起來
    4. cdev_add(struct cdev *, dev_t, unsigned) 注冊設(shè)備
    5. register_chrdev_region(dev_t, unsigned, const char *name)/alloc_chrdev_region() 申請?jiān)O(shè)備號,為注冊設(shè)備(cdev_add())準(zhǔn)備
    6. 就是內(nèi)核空間的數(shù)據(jù)和用戶空間的數(shù)據(jù)交換

    本文有粵嵌教育(www.gec-edu.org)培訓(xùn)機(jī)構(gòu)提供。

    linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)

    linux相關(guān)文章:linux教程


    電機(jī)保護(hù)器相關(guān)文章:電機(jī)保護(hù)器原理


    評論


    相關(guān)推薦

    技術(shù)專區(qū)

    關(guān)閉
    主站蜘蛛池模板: 广德县| 仁寿县| 芜湖县| 当雄县| 息烽县| 新邵县| 通海县| 观塘区| 海南省| 遵义市| 黔江区| 牡丹江市| 自治县| 盐源县| 水富县| 鹤壁市| 潼南县| 邵武市| 宁德市| 金塔县| 江门市| 英吉沙县| 南宁市| 城固县| 读书| 响水县| 尉氏县| 湄潭县| 兴国县| 兴城市| 南宁市| 鹤山市| 开原市| 贺州市| 鄢陵县| 旬邑县| 卫辉市| 林周县| 梁河县| 丰台区| 西吉县|