zoukankan      html  css  js  c++  java
  • liunx中字符驱动编写的简单模板


    下面是关于字符驱动两个程序,主要是说明驱动编写的思想,理解驱动是怎么一步一步被实现的。

    驱动的第一个实现程序,是相对于裸机编程的,主要是体会一下驱动编程思想:

    cdev.h: 所包含的头文件

    #ifndef CDEV_H_
    #define CDEV_H_

    #define MAX_CDEVS 1024

    struct cdev {
    struct file_operations *ops;
    char *name;
    int id;
    };

    fs.h : 包含的另一个头文件

    #ifndef FS_H_
    #define FS_H_

    struct file_operations {
    void (*open)(void);
    void (*read)(void);
    void (*write)(void);
    void (*close)(void);
    };

    void open(int id);
    void read(int id);
    void write(int id);
    void close(int id);
    #endif /* FS_H_ */


    extern struct cdev *cdev_map[MAX_CDEVS];

    #endif /* CDEV_H_ */


    cdev.c:

    #include "cdev.h"

    struct cdev *cdev_map[MAX_CDEVS];

    void (struct cdev *dev, struct file_operations *fops)
    {
    dev->ops = fops;
    }

    void cdev_add(struct cdev *dev, int id)
    {
    cdev_map[id] = dev;
    }


    fs.c: 这里面是具体函数的实现,通过调用结构体执行不同的功能

    /*
    * fs.c
    *
    * Created on: 2015-4-3
    * Author: Administrator
    */
    #include "cdev.h"
    #include "fs.h"

    void open(int id)
    {
    struct cdev *tmp_dev = cdev_map[id];
    tmp_dev->ops->open();
    }

    void write(int id)
    {
    struct cdev *tmp_dev = cdev_map[id];
    tmp_dev->ops->write();
    }

    void read(id)
    {
    struct cdev *tmp_dev = cdev_map[id];
    tmp_dev->ops->read();
    }

    void close(int id)
    {
    struct cdev *tmp_dev = cdev_map[id];
    tmp_dev->ops->close();
    }

    led.c: 驱动功能的实现,将特定的功能传给结构体,并给一个标号,方便后面查找函数和调用

    /*
    * led.c
    *
    * Created on: 2015-4-3
    * Author: Administrator
    */
    #include "fs.h"
    #include "cdev.h"

    #define GPG3CON (*(volatile unsigned int *)0xe03001c0)
    #define GPG3DAT (*(volatile unsigned int *)0xe03001c4)


    void led_init(void)
    {
    GPG3CON = 0x1111;
    }

    void led_on(void)
    {
    GPG3DAT = 0xf;
    }

    void led_off(void)
    {
    GPG3DAT = 0x0;
    }


    struct file_operations led_ops = {
    .open = led_init,
    .write = led_on,
    .close = led_off,
    };


    struct cdev led_cdev;
    int led_dev_num = 0; //led设备的设备号

    void led_driver_init(void)
    {
    //初始化led 字符设备驱动
    cdev_init(&led_cdev, &led_ops);

    //注册进内核
    cdev_add(&led_cdev, led_dev_num);

    }


    kernel_main.c 主函数调用写好的驱动

    /*
    * kernel_main.c
    *
    * Created on: 2015-4-3
    * Author: Administrator
    */

    void start_kernel(void)
    {
    //内核各种机制的初始化以及模块入口初始化
    led_driver_init();


    //运行用户空间进程(应用)
    main();
    }

    main.c 主函数:

    #include "cdev.h"
    #include "fs.h"
    /*
    * 延时函数
    */
    void soft_delay(unsigned int loops)
    {
    while (loops > 0)
    loops--;
    }


    /*
    * led 测试c入口
    */
    void main()
    {
    //获取led的设备号 0
    open(0);
    write(0);
    }

    对应于上面程序的makefile:

    ##############################################################
    CROSS_COMPILE = arm-none-eabi-

    CC = $(CROSS_COMPILE)gcc
    LD = $(CROSS_COMPILE)ld
    OBJCOPY = $(CROSS_COMPILE)objcopy
    OBJDUMP = $(CROSS_COMPILE)objdump

    INCLUDE_PATH = ./include

    CFLAGS = -I$(INCLUDE_PATH) -mabi=apcs-gnu -c -g -O0 -o
    ##############################################################

    objs = init/start.o kernel/kernel_main.o cdev/fs.o cdev/cdev.o drivers/led.o main.o

    pro_name = kernel
    ##############################################################

    all:$(objs)
    $(LD) $(objs) -Tmap.lds -o $(pro_name).elf #链接
    $(OBJCOPY) -O binary $(pro_name).elf $(pro_name).bin #二进制提取
    $(OBJDUMP) -D $(pro_name).elf > $(pro_name).dis #反汇编


    %.o : %.s
    $(CC) $(CFLAGS) $@ $< #-c 只编译不链接 -g 调试 -O0优化等级 -O1 -O2 -o 输出
    %.o : %.c
    $(CC) $(CFLAGS) $@ $< #$@:目标 $<:第一个依赖 $^: 多个依赖


    clean:
    rm -rf *.bin *.elf *.dis $(objs)

    驱动的第二个实现,这个是在内核里面编写的程序:

    hello_char.c:这个C程序里面包含了很多内核里面的语句,这个要查一下他们的用法

    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>

    struct cdev hello_cdev; 先创建一个对象
    int hello_major = 250; 主设备号 (前12位)
    int hello_minor = 0; 次设备号 (后20位)
    int num_of_hellos = 1; 注册这个设备的个数,这里表示就一个
    dev_t hello_devno; 最终构成的设备号记做hello_devno

    MODULE_LICENSE("GPL");

    static int hello_open(struct inode *nodp, struct file *filp)
    {
    printk(KERN_INFO "hello open! ");
    return 0;
    }
    static int hello_release(struct inode *nodp, struct file *filp)
    {
    printk(KERN_INFO "hello release! ");
    return 0;
    }

    struct file_operations hello_ops = {
    .open = hello_open,
    .release = hello_release,
    };

    static int __init hello_init(void)
    {
    /*construct cdev number*/
    hello_devno = MKDEV(hello_major, hello_minor); //hello_major << 20 | hello_minor; 构成设备号
    register_chrdev_region(hello_devno, num_of_hellos, "hello_char");注册设备号,后面三个参数分别表示(设备号,有几个,设备名字)

    /*cdev initialize*/
    cdev_init(&hello_cdev, &hello_ops); 初始化设备

    /*add to kernel*/
    cdev_add(&hello_cdev, hello_devno, num_of_hellos); 将设备号和设备联系起来

    printk(KERN_INFO "hello_init ! ");
    return 0;
    }


    static void __exit hello_exit(void) 卸载的函数
    {
    cdev_del(&hello_cdev); 卸载的时候删除对象
    unregister_chrdev_region(hello_devno, num_of_hellos);
    printk("hello_exit ! ");
    }

    module_init(hello_init);
    module_exit(hello_exit);

    MODULE_AUTHOR("minj@farsight.com.cn");
    MODULE_DESCRIPTION("just for test!");


    test.c内核调用驱动的程序:

    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/stat.h>

    int main(int argc, const char *argv[])
    {
    int fd;

    fd = open("/dev/hello", O_RDWR); 打开设备,在这之前linux里面创建了一个节点->sudo mknod /dev/hello c 250 0 对应前面的主次设备号
    if (fd < 0) {
    perror("open");
    return -1;
    }


    close(fd);
    return 0;
    }

    对应于上面程序的makefile :

    MOD_NAME = hello_char

    obj-m = $(MOD_NAME).o

    #KERN_DIR = /home/linux/linux-2.6.35-farsight
    KERN_DIR = /lib/modules/$(shell uname -r)/build

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


    clean:
    rm -rf *.o *.ko *.mod.c *.order *.symvers .*.cmd .*versions


    backup:
    tar cvf ../$(MOD_NAME).tar.gz ../$(MOD_NAME)
    cp ../$(MOD_NAME).tar.gz /mnt/hgfs/ubuntu_share

  • 相关阅读:
    MyEclipse数据库反向生成实体类
    Struts2部分标签
    如何使用 Jmeter 发送 Json 请求
    [转]Perfmon
    Jmeter 施压 SQL server数据库的时候,如何设置?
    Tomcat 安装之后,双击Tomcat.exe,无法运行成功,怎么办?
    Jmeter 在什么情况下定义多个thread group?
    Watir RAutomation VS AutoIt to deal with popup
    RAutomation 在 Watir中的使用
    AutoIt:如何处理应用程序端口被占用的情况
  • 原文地址:https://www.cnblogs.com/cnlg/p/4394154.html
Copyright © 2011-2022 走看看