Linux学习笔记(15)——LED设备驱动

  1. 编写LED字符驱动程序,如下:
/* 
 * 文件名   : led_drv.c
 * 作者     : glen  
 * 描述     : led_drv驱动文件
 */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

/* 主设备号 */
#define LED_DRV_MAJOR    200

/* 设备驱动名称 */
#define LED_DRV_NAME     "led_drv"

/* 关灯 */
#define LEDOFF              'C'

/* 开灯 */
#define LEDON               'O'

/* 寄存器物理地址 */
#define CCM_CCGR1_BASE           (0x020C406C)
#define SW_MUX_GPIO1_IO03_BASE   (0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE   (0x020E02F4)
#define GPIO1_DR_BASE            (0x0209C000)
#define GPIO1_GDIR_BASE          (0x0209C004)

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *p_ccm_ccgr1;
static void __iomem *p_sw_mux_gpio1_io03;
static void __iomem *p_sw_pad_gpio1_io03;
static void __iomem *p_gpio1_dr;
static void __iomem *p_gpio1_gdir;

/**
 * @brief   : 打开/关闭LED 
 * @par     : status    LEDON('O') 打开LED, LEDOFF('C') 关闭LED
 * @retval  : 无
 */
void led_ctrl(u8 status)
{
    u32 val = 0;

    if (status == LEDON) {
        val = readl(p_gpio1_dr);
        val &= ~(1 << 3);
        writel(val, p_gpio1_dr);
        printk("Openned led!\r\n");
    } else if (status == LEDOFF) {
        val = readl(p_gpio1_dr);
        val |= (1 << 3);
        writel(val, p_gpio1_dr);
        printk("Closed led!\r\n");
    } else {
        printk("Recived parameter is error!\r\n");
    }
}

/**
 * @brief   : 打开设备 
 * @par     : inode 传递给驱动的inode
 *            filp  设备文件, file结构体有个private_data的成员变量, 一般
 *            在open的时候将private_data指向设备结构体
 * @retval  : 0 成功, 其它 失败
 */
static int led_drv_open(struct inode *inode, struct file *filp)
{
    /* user implement */
    return 0;
}

/**
 * @brief   : 从设备读取数据 
 * @par     : filp  要打开的设备文件(文件描述符)
 *            buf   返回给用户空间的数据缓冲区
 *            cnt   要读取的数据长度
 *            offt  相对于文件首地址的偏移
 * @retval  : 读取数据长度, 若为负值则表示读取失败
 */
static ssize_t led_drv_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

/**
 * @brief   : 向字符设备写入数据 
 * @par     : filp  设备文件, 表示打开的文件描述符
 *            buf   要写给设备的数据
 *            cnt   要写入的数据长度
 *            offt  相对于文件首地址的偏移
 * @retval  : 写入数据长度, 若为负值则表示读取失败
 */
static ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    u8 data_buf[1];
    u8 led_sta;

    ret = copy_from_user(data_buf, buf, 1);

    if (ret < 0) {
        printk("kenel recive data: %s\r\n", buf);
        return -EFAULT;
    }

    /* 获取状态值 */
    led_sta = data_buf[0];

    led_ctrl(led_sta);

    return 0;

}

/**
 * @brief   : 打开设备 
 * @par     : inode 传递给驱动的inode
 *            filp  要关闭的设备文件(文件描述符)
 * @retval  : 0 成功, 其它 失败
 */
static int led_drv_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations led_drv_fops = {
    .owner = THIS_MODULE,
    .open = led_drv_open,
    .read = led_drv_read,
    .write = led_drv_write,
    .release = led_drv_release,
};


/**
 * @brief   : 驱动初始化函数 
 * @par     : 无
 * @retval  : 无
 */
static int __init led_drv_init(void)
{
    int ret = 0;
    u32 val = 0;

    /* 寄存器地址映射 */
    p_ccm_ccgr1 = ioremap(CCM_CCGR1_BASE, 4);
    p_sw_mux_gpio1_io03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    p_sw_pad_gpio1_io03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    p_gpio1_dr = ioremap(GPIO1_DR_BASE, 4);
    p_gpio1_gdir = ioremap(GPIO1_GDIR_BASE, 4);

    /* 使能GPIO1时钟 */
    val = readl(p_ccm_ccgr1);
    val |= (3 << 26);
    writel(val, p_ccm_ccgr1);

    /* 设置gpio1_io03的复用功能 */
    writel(5, p_sw_mux_gpio1_io03);

    /* 寄存器sw_mux_gpio1_io03设置IO属性 */
    writel(0x10B0, p_sw_pad_gpio1_io03);

    /* 设置gpio1_gdir为输出功能 */
    val = readl(p_gpio1_gdir);
    val |= (1 << 3);
    writel(val, p_gpio1_gdir);

    /* 关闭led */
    val = readl(p_gpio1_dr);
    val |= (1 << 3);
    writel(val, p_gpio1_dr);

    /* register char device driver */
    ret = register_chrdev(LED_DRV_MAJOR, LED_DRV_NAME, &led_drv_fops);

    if (ret < 0) {
        printk("register led_drv chrdrv failed!\r\n");
        return -EIO;
    }

    printk("register led_drv chrdrv successfully!\r\n");

    return 0;
}


/**
 * @brief   : 驱动退出函数 
 * @par     : 无
 * @retval  : 无
 */
static void __exit led_drv_exit(void)
{
    /* 取消地址映射 */
    iounmap(p_ccm_ccgr1);
    iounmap(p_sw_mux_gpio1_io03);
    iounmap(p_sw_pad_gpio1_io03);
    iounmap(p_gpio1_dr);
    iounmap(p_gpio1_gdir);

    /* 注销字符设备驱动 */
    unregister_chrdev(LED_DRV_MAJOR, LED_DRV_NAME);
}

/* assign the functions as driver's import function and export function*/

module_init(led_drv_init);
module_exit(led_drv_exit);

/* insert license for module */
MODULE_LICENSE("GPL");

/* insert author information for module */
MODULE_AUTHOR("glen");
  1. 编写Makefile文件,并编译驱动程序
    Makefile文件内容如下:
KERNELDIR	:= /home/glen/linux/imx6ull/linux/glen_linux
CURRENT_PATH	:= $(shell pwd)
obj-m	:= led_drv.o

build:	kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

执行编译命令:glen@ubuntu:~/linux/imx6ull/linux/driver/2_led_drv$ make
产生如下编译信息,编译LED驱动模块成功

make -C /home/glen/linux/imx6ull/linux/glen_linux M=/home/glen/linux/imx6ull/linux/driver/2_led_drv modules
make[1]: 进入目录“/home/glen/linux/imx6ull/linux/glen_linux”
  CC [M]  /home/glen/linux/imx6ull/linux/driver/2_led_drv/led_drv.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/glen/linux/imx6ull/linux/driver/2_led_drv/led_drv.mod.o
  LD [M]  /home/glen/linux/imx6ull/linux/driver/2_led_drv/led_drv.ko
make[1]: 离开目录“/home/glen/linux/imx6ull/linux/glen_linux
  1. 编写LED测试驱动程序,并进行编译
/*
 * 文件名   :  led_drv_test.c
 * 作者     :  glen
 * 描述     :  chrdev应用程序
 */

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

/**
 * @brief   : main函数
 * @par     : argc  argv数组元素的个数
 *            argv  参数数组
 * @retval  : 0 成功    其它 失败
 */
int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    char data_buf[1];

    if (argc != 3) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    /* 要执行的操作 */
    data_buf[0] = argv[2][0];

    /* 向/dev/led文件写入数据 */
    ret = write(fd, data_buf, sizeof(data_buf));
    if (ret < 0) {
        printf("LED control failed!\r\n");
        close(fd);
        return -1;
    } else {
        printf("Write parameter is %s\r\n", data_buf);
    }

    /* 关闭文件 */
    ret = close(fd);
    if (ret < 0) {
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}

在ubuntu 终端下执行命令glen@ubuntu:~/linux/imx6ull/linux/driver/2_led_drv$ arm-linux-gnueabihf-gcc led_drv_test.c -o led_drv_test编译完成。

  1. 把编译好的LED驱动模块和LED测试程序拷贝到NFS根文件系统目录下sudo cp led_drv.ko led_drv_test ../../../../nfs/rootfs/lib/modules/4.1.15/ -f
    启动目标板,通过ls命令查询LED的测试和驱动程序
/lib/modules/4.1.15 # ls
first_chr_dev.ko   led_drv.ko         modules.alias      modules.symbols
first_chr_dev_app  led_drv_test       modules.dep
  1. 加载LED驱动模块并进行测试
    5.1 加载驱动模块
    /lib/modules/4.1.15 # insmod led_drv.ko
    register led_drv chrdrv successfully!
    5.2 创建设备节点并查询
    /lib/modules/4.1.15 # mknod /dev/led_drv c 200 0
    /lib/modules/4.1.15 # ls /dev/led_drv -l
    crw-r--r-- 1 0 0 200, 0 Jan 1 12:15 /dev/led_drv
    5.3 查询驱动模块,发现驱动设备号200存在。OK
/lib/modules/4.1.15 # cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 29 fb
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 led_drv
207 ttymxc
226 drm
250 ttyLP
251 watchdog
252 ptp
253 pps
254 rtc

Block devices:
  1 ramdisk
259 blkext
  7 loop
  8 sd
 31 mtdblock
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc

5.4 测试LED驱动程序,发现目标板LED与测试输出信息表现一致。至此LED完整的驱动程序编写测试完成!

/lib/modules/4.1.15 # ./led_drv_test /dev/led_drv O
Openned led!
Write parameter is O
/lib/modules/4.1.15 # ./led_drv_test /dev/led_drv C
Closed led!
Write parameter is C
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值