- 编写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");
- 编写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
- 编写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
编译完成。
- 把编译好的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
- 加载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