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
  • 相关阅读:
    oracle中Blob和Clob类型的区别
    为什么要分库分表
    Enable file editing in Visual Studio's debug mode
    SQL Server Dead Lock Log
    Debug .NET Framework Source
    SQL Server text field里面有换行符的时候copy到excel数据会散乱
    诊断和修复Web测试记录器(Web Test Recorder)问题
    Can't load Microsoft.ReportViewer.ProcessingObjectModel.dll
    'telnet' is not recognized as an internal or external command
    Linq to XML
  • 原文地址:https://www.cnblogs.com/NEIL-X/p/4840436.html
Copyright © 2011-2022 走看看