码迷,mamicode.com
首页 > 其他好文 > 详细

字符设备驱动之Led驱动学习记录

时间:2015-09-26 12:06:02      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:

一、概述

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\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");

 

五、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

 

字符设备驱动之Led驱动学习记录

标签:

原文地址:http://www.cnblogs.com/NEIL-X/p/4840436.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!