LED驱动程序的编写

内容:

1,模块传参和模块调用——模块的特征
2,完整的驱动程序组成
**3,在应用空间和内核空间(驱动)之间的数据交换
===============================================**

一,模块传参和模块调用

1,模块传参——-在加载模块时,同时给模块传参数

1> 在编写代码时,需要对模块中的参数进行声明:

    module_param(变量名,变量的类型,权限);

    例如:
        module_param(sno,int,0644);
        module_param(name,charp,0644);
2> 在开发板中,加载模块时,可以传参:
    [root@farsight /drv_module]# insmod hello_drv.ko sno=1002
    ---------hello_init------------
    sno = 1002,name = 小明
    [root@farsight /drv_module]# insmod hello_drv.ko sno=1002 name=farsight
    ---------hello_init------------
    sno = 1002,name = farsight

    加载模块后,在应用空间中,可以看到与模块中变量同名的文件被创建:
    [root@farsight /drv_module]# ls /sys/module/hello_drv/parameters/ -l
    total 0
    -rw-r--r--    1 0        0             4096 Jan  1 00:08 name
    -rw-r--r--    1 0        0             4096 Jan  1 00:08 sno

代码

hello_drv.c文件代码

#include <linux/init.h>
#include <linux/module.h>

int sno = 1001;
char *name = "小明";

static int __init hello_init(void)
{
    printk("---------%s------------\n",__FUNCTION__);
    printk("sno = %d,name = %s\n",sno,name);
    return 0;
}

static void __exit hello_exit(void)
{
    printk("---------%s------------\n",__FUNCTION__);
}

//声明

module_param(sno,int,0644);
module_param(name,charp,0644);

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");   //认证

Makefile文件代码

KERNEL_DIR = /home/lpf/1803/s5pv210/kernel/linux-3.0.8    #指定内核源码路径
CUR_DIR = $(shell pwd)     


all:
    #使make进入内核源码目录,并将当前目录下的源码作为内核的模块一起编译
    make -C $(KERNEL_DIR) M=$(CUR_DIR) modules

clean:
    #删除上面生成的文件
    make -C $(KERNEL_DIR) M=$(CUR_DIR) clean

install:
    cp *.ko /opt/rootfs/drv_module

obj-m = hello_drv.o

2,模块调用:
代码实现:

        调用模块------hello_drv.c                                   |
        int a,b;                                                    |
        static int __init hello_init(void)                          |
        {                                                           |   
            printk("---------%s------------\n",__FUNCTION__);       |
            printk("sno = %d,name = %s\n",sno,name);                |
                                                                    |
            printk("%d + %d = %d\n",a,b,myadd(a,b));                |
            return 0;                                               |
        }                                                           |
        --------------------------------------------------------
        被调用模块----myadd.c
        int myadd(int a,int b)
        {
            return a + b;
        }

        EXPORT_SYMBOL(myadd);

        MODULE_LICENSE("GPL");
    在开发板中:
        必须先加载被调用模块,然后加载调用模块:
        [root@farsight /drv_module]# insmod myadd.ko
        [root@farsight /drv_module]# insmod hello_drv.ko a=3 b=8
        ---------hello_init------------
        sno = 1001,name = 小明
        **3 + 8 = 11

==============================================================================**
代码
hello_drv.c文件代码

#include <linux/init.h>
#include <linux/module.h>
#include "myadd.h"

int sno = 1001;
char *name = "小明";

int a,b;

static int __init hello_init(void)
{
    printk("---------%s------------\n",__FUNCTION__);
    printk("sno = %d,name = %s\n",sno,name);

    printk("%d + %d = %d\n",a,b,myadd(a,b));
    return 0;
}

static void __exit hello_exit(void)
{
    printk("---------%s------------\n",__FUNCTION__);
}

//声明

module_param(a,int,0644);
module_param(b,int,0644);
module_param(sno,int,0644);
module_param(name,charp,0644);

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");   //认证

madd.h文件代码

#ifndef __MYADD__
#define __MYADD__

extern int myadd(int a,int b);


#endif

madd.c文件代码

#include <linux/init.h>
#include <linux/module.h>

int myadd(int a,int b)
{
    return a + b;
}

EXPORT_SYMBOL(myadd);

MODULE_LICENSE("GPL");

Makefile文件代码

KERNEL_DIR = /home/lpf/1803/s5pv210/kernel/linux-3.0.8    #指定内核源码路径
CUR_DIR = $(shell pwd)     


all:
    #使make进入内核源码目录,并将当前目录下的源码作为内核的模块一起编译
    make -C $(KERNEL_DIR) M=$(CUR_DIR) modules

clean:
    #删除上面生成的文件
    make -C $(KERNEL_DIR) M=$(CUR_DIR) clean

install:
    cp *.ko /opt/rootfs/drv_module

obj-m = hello_drv.o
obj-m += myadd.o

补充:sourceinsight的使用
1> 将建好的工程文件拷贝到 \192.168.7.x\farsight\1803\s5pv210\kernel\linux-3.0.8
2> 在linux中解压:
lpf@ubuntu:~/1803/s5pv210/kernel/linux-3.0.8$ tar -xvf si_linux308-ori.tgz
linux308-ori.IAB
linux308-ori.IAD
linux308-ori.IMB
linux308-ori.IMD
linux308-ori.PFI
linux308-ori.PO
linux308-ori.PR
linux308-ori.PRI
linux308-ori.PS
linux308-ori.WK3
**3> 在共享目录中,双击linux308-ori.PR 打开工程即可
===================================================================================**

二,一个完整的驱动程序的组成

1,需要一个设备号
1>概念:
在linux内核中,设备号用一个32位的整数表示:—–dev_t
分两部分:
主设备号: 表示一类设备—-高12位
次设备号: 表示具体的某个设备的编号 ——— 低20位
2>申请设备号
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
//功能:申请主设备号
//参数1: mojor大于0,表示静态指定主设备号,major等于0,表示让系统动态分配一个主设备号,并通过返回值返回
//参数2: name—字符串,表示驱动的描述信息,由我们自定义
//参数3: fops —- struct file_operations结构体类型的变量的地址
//返回值:
//如果静态指定主设备号:成功返回–0,失败返回–错误码
//如果动态分配主设备号:成功返回–主设备号,失败返回—错误码

    卸载函数:
        static inline void unregister_chrdev(unsigned int major, const char *name)

3> 当驱动加载成功后,可以在文件系统中看到申请的主设备号:
    [root@farsight /drv_module]# insmod hello_drv.ko
    ---------hello_init------------
    [root@farsight /drv_module]# cat /proc/devices
    Character devices:
      1 mem
    256 hello_drv
      2 pty
      3 ttyp
      4 /dev/vc/0
      4 tty
      4 ttyS
      5 /dev/tty
      5 /dev/console
      5 /dev/ptmx

2,需要创建设备节点 —–设备文件,可以在应用层文件系统中可见:/dev/
1>手动创建设备文件
mknod 设备文件名称 类型 主设备号 次设备号
例如:
[root@farsight /drv_module]# mknod /dev/hello c 254 0
[root@farsight /drv_module]# ls -l /dev/hello
crw-r–r– 1 0 0 254, 0 Jan 1 00:01 /dev/hello
2>自动创建设备文件(设备节点)

    #define class_create(owner, name)   
    //参数1: 当前模块
    //参数2: 字符串----描述信息,自定义
    //返回值:成功--struct class结构体的地址,失败:NULL
                                                                                            printf(const char *fmt, ...);
    struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...)
    //参数1: class_create返回的指针
    //参数2: 父类,一般为:NULL
    //参数3: 设备号:dev_t
                #define MINORBITS   20
                #define MINORMASK   ((1U << MINORBITS) - 1)

                #define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))
                #define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))
                #define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))
    //参数4: 私有数据,一般为:NULL
    //参数5: 设备文件的名称
    //变参:  配合参数5使用,共同定义设备文件的名称  ,例如;   "hello%d" , 1            表示设备文件的名字为:hello1
    //返回值: 成功-----struct device 结构体的地址,失败---NULL

    开发板中:
        [root@farsight /drv_module]# insmod hello_drv.ko        //加载驱动,会自动创建设备文件
        ---------hello_init------------
        [root@farsight /drv_module]# ls /dev/hello5 -l
        crw-rw----    1 0        0         254,   1 Jan  1 00:29 /dev/hello5
        [root@farsight /drv_module]# ./test             //运行应用程序,打开设备文件
        ---------hello_open------------

3,需要实现操作设备的方法(接口)
1> 实现操作方法
int hello_open(struct inode *inode , struct file *filp)
{
printk(“———%s————\n”,FUNCTION);

            return 0;
        }
        int hello_close(struct inode *inode, struct file *filp)
        {
            printk("---------%s------------\n",__FUNCTION__);

            return 0;
        }

    2> 将操作方法封装到:struct file_operations
        struct file_operations fops = {
            .open = hello_open,
            .release = hello_close,
        };

4,硬件初始化—-地址映射

ioremap(cookie,size)
//参数1: 要映射的物理空间的起始地址
//参数2: 要映射的物理空间的大小
//返回值:
    成功---- 虚拟空间的起始地址
    失败 ---- NULL

三,应用空间和内核空间(驱动)之间的数据交换
linux中:
驱动程序负责:实现机制 —–能不能做
应用程序负责:实现策略 —– 怎么做

所以,通常在编写驱动程序时,需要由应用空间传递数据给驱动程序,也就是应用空间和内核空间的数据交换.

应用空间  ----- >  内核空间 ------实现write接口函数
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
//参数1: 目标地址---内核空间地址
//参数2: 源地址 ---- 应用空间的数据地址
//参数3: 传递给内核的数据长度
//返回值:成功---0,失败----没有传递成功的剩余数据的个数

内核空间  ----- > 应用空间  ------ 实现read接口函数
static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
//参数1: 目标地址---应用空间地址
//参数2: 源地址 ---- 内核空间的数据地址
//参数3: 传递给应用空间的数据长度
//返回值:成功---0,失败----没有传递成功的剩余数据的个数

led_drv.c文件代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>

#include <asm/io.h>
#include <asm/uaccess.h>

struct s5pv210_led{
    unsigned int major;
    struct class *cls; 
    struct device * dev;
    int data;
};
struct s5pv210_led *led_dev;

unsigned long *gpc0_conf;
unsigned long *gpc0_data;

int led_drv_open(struct inode *inode , struct file *filp)
{
    printk("---------%s------------\n",__FUNCTION__);

    //将对应的管脚设置为输出
    *gpc0_conf  &= ~(0xff<<12);
    *gpc0_conf  |= 0x11<<12;
    return 0;
}

ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{
    int ret;
    printk("---------%s------------\n",__FUNCTION__);

    // 1,将应用空间的数据转化为内核空间的数据
    ret = copy_from_user(&led_dev->data, buf, size);
    if(ret > 0){
        printk("copy_from_user error\n");
        return -EFAULT;
    }

    //操作设备
    if(led_dev->data){
        //亮灯
        *gpc0_data |= 0x3<<3;
    }else{
        //灭灯
        *gpc0_data  &= ~(0x3<<3);
    }
    return size;
}
int led_drv_close(struct inode *inode, struct file *filp)
{
    printk("---------%s------------\n",__FUNCTION__);
    //灭灯
    *gpc0_data  &= ~(0x3<<3);
    return 0;
}
struct file_operations fops = {
    .open = led_drv_open,
    .write = led_drv_write,
    .release = led_drv_close,
};
static int __init led_init(void)
{
    int ret;
    printk("---------%s------------\n",__FUNCTION__);
    // 0, 初始化对象
    led_dev = kzalloc(sizeof(struct s5pv210_led), GFP_KERNEL);
    if(IS_ERR(led_dev)){
        printk("kzalloc error\n");
        return  -ENOMEM;
    }
    // 1,申请设备号 
    #if 0
    ret = register_chrdev(led_major, "led_drv", &fops); //静态指定主设备号
    if(ret < 0){
        printk("register_chrdev error!\n");
        return -EINVAL;
    }
    #else
    led_dev->major= register_chrdev(0, "led_drv", &fops); //动态分配主设备号
    if(led_dev->major < 0){
        printk("register_chrdev error!\n");
        ret = -EINVAL;
        goto err_free;
    }
    #endif

    // 2,创建设备文件

    led_dev->cls= class_create(THIS_MODULE,"led_class");
    if(IS_ERR(led_dev->cls)){
        printk("class_create error\n");
        ret = PTR_ERR(led_dev->cls);
        goto err_unregister;
    }

    led_dev->dev= device_create(led_dev->cls, NULL, MKDEV(led_dev->major,1), NULL, "led%d",5);
    if(IS_ERR(led_dev->dev)){
        printk("class_create error\n");
        ret = PTR_ERR(led_dev->dev);
        goto err_class;
    }

    // 3,硬件初始化----地址映射
    gpc0_conf = ioremap(0xE0200060,8);
    gpc0_data = gpc0_conf + 1;


    return 0;

err_class:
    class_destroy(led_dev->cls);
err_unregister:
    unregister_chrdev(led_dev->major, "led_drv");
err_free:
    kfree(led_dev);
    return ret;
}

static void __exit led_exit(void)
{
    printk("---------%s------------\n",__FUNCTION__);
    iounmap(gpc0_conf );
    device_destroy(led_dev->cls, MKDEV(led_dev->major,1));
    class_destroy(led_dev->cls);
    unregister_chrdev(led_dev->major, "led_drv");
    kfree(led_dev);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");   //认证

test.c应用层文件代码

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


int main(void)
{
    int fd;
    int i,on;

    fd = open("/dev/led5",O_RDWR);
    if(fd < 0){
    printf("open");
    exit(1);
    }


    for(i = 0; i<10; i++){
    on = 1;
    write(fd,&on,sizeof(on));
    sleep(1);

    on = 0;
    write(fd,&on,sizeof(on));
    sleep(1);
    }

    close(fd);
    return 0;
}

Makefile文件代码

KERNEL_DIR = /home/lpf/1803/s5pv210/kernel/linux-3.0.8    #指定内核源码路径
CUR_DIR = $(shell pwd)     
SRC = test.c
MYAPP = test

all:
    #使make进入内核源码目录,并将当前目录下的源码作为内核的模块一起编译
    make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
    arm-none-linux-gnueabi-gcc -o $(MYAPP) $(SRC)

clean:
    #删除上面生成的文件
    make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
    rm $(MYAPP) .*.sw?
install:
    cp *.ko $(MYAPP) /opt/rootfs/drv_module

obj-m = led_drv.o

led1
2
3
4
这里写图片描述

猜你喜欢

转载自blog.csdn.net/MrDongShiYi/article/details/81563316