驱动文件
分为两个文件,一个字符设备添加和设备树相关节点的处理文件,一个负责LED的相关操作处理
dts_led.c
#include "dts_led.h"
#include "led_operator.h"
struct dtsled_dev dtsled;
static int dtsled_open (struct inode *inode, struct file *filp)
{
filp->private_data = &dtsled;
return 0;
}
static int dtsled_release (struct inode *inode, struct file *filp)
{
struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;
return 0;
}
static ssize_t dtsled_write (struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;
int retval = 0;
unsigned char buffer[1] = { 0 };
retval = copy_from_user(buffer, buf, count);
if ( retval < 0 ) {
printk("kernel write failed\r\n");
return -EFAULT;
}
printk("read sta = %d\r\n", buffer[0]);
//判断开灯还是关灯
led_switch(buffer[0]);
return 0;
}
static struct file_operations dtsled_fops = {
.owner = THIS_MODULE,
.open = dtsled_open,
.release = dtsled_release,
.write = dtsled_write,
};
static int __init dts_led_start(void)
{
int ret = 0;
char *str = NULL;
u32 regdata[10];
int i = 0;
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (dtsled.major) { /* 定义了设备号 */
dtsled.devid = MKDEV(dtsled.major, 0);
ret = register_chrdev_region(dtsled.major, DTSLED_CNT, DTSLED_NAME);
}
else { /* 未定义设备号 */
ret = alloc_chrdev_region(&(dtsled.devid), 0, DTSLED_CNT, DTSLED_NAME);
dtsled.major = MAJOR(dtsled.devid);
dtsled.minor = MINOR(dtsled.devid);
}
if (ret < 0) {
goto FAILED_DEVID;
}
printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor);
/* 2. 添加字符设备 */
cdev_init(&(dtsled.ledCdev), &dtsled_fops);
ret = cdev_add(&(dtsled.ledCdev), dtsled.devid, DTSLED_CNT);
if (ret < 0) {
printk("添加字符设备错误\r\n");
goto FAILED_CDEV;
}
/* 自动创建设备节点 */
dtsled.ledClass = class_create(THIS_MODULE, DTSLED_NAME);
if (IS_ERR(dtsled.ledClass)) {
ret = PTR_ERR(dtsled.ledClass);
goto FAILED_CLASS;
}
dtsled.ledDevice = device_create(dtsled.ledClass, NULL, dtsled.devid, NULL, DTSLED_NAME);
if (IS_ERR(dtsled.ledDevice)) {
ret = PTR_ERR(dtsled.ledDevice);
goto FAILED_DEVICE;
}
/* 获取设备树属性内容 */
dtsled.nd = of_find_node_by_path("/qinmianled");
if (dtsled.nd == NULL) {
ret = -EINVAL;
goto FAILED_GETDTSNODE;
}
/* 判断获取的节点的正确性 */
ret = of_property_read_string(dtsled.nd, "status", (const char **)&str);
if (ret < 0) {
goto FAILED_GETDTSNODE;
}
else {
printk("status = %s\r\n", str);
}
ret = of_property_read_string(dtsled.nd, "compatible", (const char **)&str);
if (ret < 0) {
goto FAILED_GETDTSNODE;
}
else {
printk("compatible = %s\r\n", str);
}
ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
if (ret < 0) {
goto FAILED_GETDTSNODE;
}
else {
printk("reg data:\r\n");
for (i = 0; i < 10; i++) {
printk("%#X ", regdata[i]);
}
printk("\r\n");
}
/* led 初始化 */
dts_led_init(regdata, 10);
return 0;
FAILED_GETDTSNODE:
device_destroy(dtsled.ledClass, dtsled.devid);
FAILED_DEVICE:
class_destroy(dtsled.ledClass);
FAILED_CLASS:
cdev_del(&(dtsled.ledCdev));
FAILED_CDEV:
unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
FAILED_DEVID:
return ret;
}
static void __exit dts_led_exit(void)
{
led_free();
/* 销毁设备节点 */
device_destroy(dtsled.ledClass, dtsled.devid);
class_destroy(dtsled.ledClass);
/* 删除字符设备 */
cdev_del(&(dtsled.ledCdev));
/* 释放设备号 */
unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
}
module_init(dts_led_start);
module_exit(dts_led_exit);
MODULE_LICENSE("GPL");
led_operator.c
#include "dts_led.h"
/* 静态变量定义 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
void dts_led_init(u32 *regdata, int num)
{
unsigned int val = 0;
#if 1
/* 直接使用设备树的of函数来映射虚拟内存 */
IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
GPIO1_DR = of_iomap(dtsled.nd, 3);
GPIO1_GDIR = of_iomap(dtsled.nd, 4);
#else
/* 传统方法使用ioremap来映射 */
IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
GPIO1_DR = ioremap(regdata[6], regdata[7]);
GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
#endif
/* 2、使能 GPIO1 时钟 */
val = readl(IMX6U_CCM_CCGR1);
val &= ~(0x3 << 26);
val |= (0x3 << 26);
writel(val, IMX6U_CCM_CCGR1);
/* 3、设置 GPIO1_IO03 的复用功能,将其复用为GPIO1_IO03 */
writel(5, SW_MUX_GPIO1_IO03);
/* 寄存器 SW_PAD_GPIO1_IO03 设置 IO电气属性 */
writel(0x10B0, SW_PAD_GPIO1_IO03);
/* 4、设置 GPIO1_IO03 为输出功能 */
val = readl(GPIO1_GDIR);
val &= ~(1 << 3);
val |= (1 << 3);
writel(val, GPIO1_GDIR);
/* 5、默认关闭 LED */
val = readl(GPIO1_DR);
val |= (1 << 3);
writel(val, GPIO1_DR);
return ;
}
void led_switch(u8 sta)
{
unsigned int val = 0;
if ( sta == LEDON ) {
val = readl(GPIO1_DR);
val &= ~(0x1 << 3); //bit3清0, 打开LED
writel(val, GPIO1_DR);
}
else if ( sta == LEDOFF ) {
val = readl(GPIO1_DR);
val |= (0x1 << 3); //bit3置1, 关闭LED灯
writel(val, GPIO1_DR);
}
return ;
}
void led_free(void)
{
unsigned int val = 0;
val = readl(GPIO1_DR);
val |= (0x1 << 3); //bit3置1, 关闭LED灯
writel(val, GPIO1_DR);
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
return;
}
头文件
dts_led.h
#ifndef __DTS_LED_H
#define __DTS_LED_H
#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/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define DTSLED_CNT 1 /* 设备号个数 */
#define DTSLED_NAME "dtsled" /* 设备名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* 映射后的寄存器虚拟地址指针 */
/* dtsled 设备结构体 */
struct dtsled_dev{
dev_t devid;
struct cdev ledCdev;
struct class *ledClass;
struct device *ledDevice;
int major;
int minor;
struct device_node *nd; /* 设备节点 */
};
extern struct dtsled_dev dtsled;
#endif
led_operator.h
#ifndef __LED_OPERATOR_H
#define __LED_OPERATOR_H
extern void dts_led_init(u32 *regdata, int num);
extern void led_switch(u8 sta);
extern void led_free(void);
#endif
测试文件
dtsled_test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
unsigned char sta = 0;
int i;
fd = open("/dev/dtsled", O_RDWR);
if (fd == -1) {
printf("/dev/dtsled open failed\r\n");
return -1;
}
for (i = 0; i < 10; i++) {
sta = i % 2;
printf("sta = %d\n", sta);
write(fd, &sta, sizeof(sta));
sleep(1);
}
close(fd);
return 0;
}
Makefile
KERNELDIR := /home//work/linux-kernel
CURRENT_PATH := $(shell pwd)
obj-m := dtsled.o
dtsled-objs = led_operator.o dts_led.o
build : kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
rm -rf *.o *.ko *.mod.c *.order *.symvers