zoukankan      html  css  js  c++  java
  • 字符设备驱动之Led驱动学习记录

    一、概述

    Linux内核就是由各种驱动组成的,内核源码中大约有85%的各种渠道程序的代码。一般来说,编写Linux设备驱动大致流程如下:

    1、查看原理图,数据手册,了解设备的操作方法。

    2、在内核中找到相近的驱动程序,以它为模板开发。

    3、实现驱动的初始化:比如像内核注册这个驱动程序

    4、设计要实现的操作:open,close,read,write等

    5、实现中断服务(不是必须的)

    6、编译该驱动程序到内核中,或insmod命令加载

    7、测试驱动程序。

    二、驱动程序的加载与卸载

    module_init(my_init);
    module_exit(my_clearup);

    三、字符设备驱动程序主要的数据结构

    1、系统调用:应用程序不能直接操作硬件,而是使用统一的接口函数调用硬件驱动程序。这些接口成为系统调用。在库函数中定义了,可以在gilbc的fcntl.h, unistd.h,sys/ioctl.h 等文件找到。open,wirite,read等。

    2、数据结构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);
    };

    3、设备的主次设备号-=

    内核靠这个寻找对应的驱动程序。应用程序在操作设备文件时,Linux系统就会根据设备文件的类型,主设备号在内核中注册的file_operation(对于块设备号是block_device_operations结构),次设备号来分辨它是同类设备中的第几个。

    4、注册函数与卸载函数

    register_chrdev;unregister_chrdev;

    四、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
    ");
        //配置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
    ");
    
        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");

    五、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
  • 相关阅读:
    POJ 1026 Cipher(置换群)
    POJ 1691 Painting A Board(DFS)
    URAL 1073 Square Country(DP)
    Spring Boot系列教程三:使用devtools实现热部署
    Spring Boot系列教程二:创建第一个web工程 hello world
    Spring Boot系列教程一:Eclipse安装spring-tool-suite插件
    java实现二叉树的构建以及3种遍历方法
    二叉树的java实现
    各种数据库的分页查询语句
    sql语句查询各门课程平均分的最大值
  • 原文地址:https://www.cnblogs.com/NEIL-X/p/4840436.html
Copyright © 2011-2022 走看看