字符設備驅動之Led驅動學習記錄

1、概述node

Linux內核就是由各類驅動組成的,內核源碼中大約有85%的各類渠道程序的代碼。通常來講,編寫Linux設備驅動大體流程以下:linux

一、查看原理圖,數據手冊,瞭解設備的操做方法。數據結構

二、在內核中找到相近的驅動程序,以它爲模板開發。app

三、實現驅動的初始化:好比像內核註冊這個驅動程序async

四、設計要實現的操做:open,close,read,write等函數

五、實現中斷服務(不是必須的)測試

六、編譯該驅動程序到內核中,或insmod命令加載this

七、測試驅動程序。spa

 

2、驅動程序的加載與卸載設計

module_init(my_init);
module_exit(my_clearup);

 

3、字符設備驅動程序主要的數據結構

一、系統調用:應用程序不能直接操做硬件,而是使用統一的接口函數調用硬件驅動程序。這些接口成爲系統調用。在庫函數中定義了,能夠在gilbc的fcntl.h, unistd.h,sys/ioctl.h 等文件找到。open,wirite,read等。

二、數據結構file_operations是在內核中的,在include/linux/fs.h中。定義以下:

/*
 * NOTE:
 * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
 * can be called without the big kernel lock held in all filesystems.
 */
struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*dir_notify)(struct file *filp, unsigned long arg);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
};

三、設備的主次設備號-=

內核靠這個尋找對應的驅動程序。應用程序在操做設備文件時,Linux系統就會根據設備文件的類型,主設備號在內核中註冊的file_operation(對於塊設備號是block_device_operations結構),次設備號來分辨它是同類設備中的第幾個。

四、註冊函數與卸載函數

register_chrdev;unregister_chrdev;

 

4、LED驅動程序

#include <linux/module.h>
#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 <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

//定義寄存器
volatile unsigned long *gpfcon=NULL;
volatile unsigned long *gpfdat=NULL;


static struct class *myFirD_class;//爲了能自動建立mdev
static struct class_device    *myFirD_class_dev;

//打開
static int myFirD_open(struct inode *inode, struct file *file)
{
    printk("myFirD has open\n");
    //配置IO口
    *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));//相與
    *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));//相或
    return 0;
}
//
static ssize_t myFirD_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;

    //printk("first_drv_write\n");

    copy_from_user(&val, buf, count); //    copy_to_user();

    if (val == 1)
    {
        // 點燈
        *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
    }
    else
    {
        // 滅燈
        *gpfdat |= (1<<4) | (1<<5) | (1<<6);
    }
    
    return 0;
}
//定義一個結構
static struct file_operations myFirD_fops = {
    .owner  =   THIS_MODULE,    /* 這是一個宏,推向編譯模塊時自動建立的__this_module變量 */
    .open   =   myFirD_open,     
    .write    =    myFirD_write,       
};
//須要用函數把結構告訴內核。註冊驅動程序
int major; //主設備號
int myFirD_init(void)
{
    major=register_chrdev(0,"myFirD",&myFirD_fops);//告訴內核
    myFirD_class = class_create(THIS_MODULE, "myFirD");//建個類
    //設備
    myFirD_class_dev = class_device_create(myFirD_class, NULL, MKDEV(major, 0), NULL, "xyz"); 
    gpfcon=(volatile unsigned *)ioremap(0x56000050,16);
    gpfdat = gpfcon + 1;;
    return 0;
}
void myFirD_exit(void)
{
    unregister_chrdev(major,"myFirD");//告訴內核
    
    class_device_unregister(myFirD_class_dev);
    class_destroy(myFirD_class);
    iounmap(gpfcon);
}
module_init(myFirD_init);
module_exit(myFirD_exit);
MODULE_LICENSE("GPL");

 

5、Makefile文件

KERN_DIR = /work/system/linux-2.6.22.6#內核所在的目錄

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += myFirD.o
相關文章
相關標籤/搜索