海思3559av100 led驱动练手

上一篇写的是如何用himm工具来控制寄存器点灯,这次写个驱动试试!

  • 驱动源码,利用网上模板修改:
/************************led_drv.c*******************************/
 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
 
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
 
#define HELLO_MAGIC 12
#define LED_NUM_ON _IOW(HELLO_MAGIC,0, int)//设置复位,这个命令不带参数
#define LED_NUM_OFF _IOW(HELLO_MAGIC,1, int)//获取当前设备的语言类型参数,参数是int型

//设计一个全局的设备对象类
struct hi3559av100_led
{
	int dev_major ;
	struct class *cls;
	struct device *dev;
	int value; // 用于存放用户的数据
};
 
//声明一个对象
struct hi3559av100_led *led_dev;
 
 //定义 gpio管脚的输出方向和data寄存器变量
volatile unsigned long *gpio3_6_conf;
volatile unsigned char *gpio3_6_data;
 
int led_drv_open(struct inode *inode, struct file *filp)
{
 
	printk("-------^_^ %s-------\n", __FUNCTION__);
 
	*gpio3_6_conf |= (0x1<<6);
		 
	return 0;
 
}
 
ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	int ret;
	printk("-------^_^ %s-------\n", __FUNCTION__);
	// 区分应用的需求
 
	//如果要从用户空间获取数据,需要用特定的函数
	//  从用户空间获取数据, 一般都用在驱动中写操作中-- xxx_write
	// 参数1---目标地址---内核中的空间的地址
	//参数2---原地址---用户空间的地址
	//参数3---拷贝数据个数
	//返回值--没有拷贝成功的数据个数, 0表示成功
	ret  = copy_from_user(&led_dev->value, buf, count);
	if(ret > 0)
	{
		printk(KERN_ERR "copy_from_user error\n");
		return -EFAULT;
	}
	
	printk("-------value:%d-------\n", led_dev->value);
 
	if(led_dev->value)
	{
		//点灯
		*gpio3_6_data |= (0x1<<6); 
		printk("-------^_^ 点灯-------\n");
	}
	else
	{
		//灭灯
		*gpio3_6_data &= ~(0x1<<6); 
		printk("-------^_^ 灭灯-------\n");
	
	}
 
	//返回传递的数据个数
	return count;
 
}
 
int led_drv_close(struct inode *inode, struct file *filp)
{
	printk("-------^_^ %s-------\n", __FUNCTION__);
	return 0;
}
 
long led_drv_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
	switch(cmd)
	{
		case LED_NUM_ON :
			//点灯
			printk("点灯 cmd\n");
			*gpio3_6_data |= (0x1<<6);
			break;
	
		case LED_NUM_OFF:
			//灭灯
			printk("灭灯 cmd\n");
			*gpio3_6_data &= ~(0x1<<6); 
			break;
		default : 
			printk("unkown cmd\n");
			return -EINVAL;
		
	}
	
	return 0;
}
 
 
const struct file_operations led_fops = {
	.open = led_drv_open,
	.write = led_drv_write,
	.release = led_drv_close,
	.unlocked_ioctl = led_drv_ioctl,
};
 
 
static int __init led_drv_init(void)
{
 
	/*
		编写驱动的套路
		0, 实例化全局的设备对象-- kzalloc
		1,  申请主设备号---register_chrdev
		2, 自动创建设备节点---class_create, device_create
		3, 初始化硬件--ioremap
		4,实现 file_operation
	*/ 
	
	// 模块加载函数中主要完成系统资源的申请
	printk("-------^_^ %s-------\n", __FUNCTION__);
	int ret;
 
	// 0, 实例化全局的设备对象
	//参数1---分配大小
	//参数2--分配的标志, GFP_KERNEL--如果当前暂时没有内存,会尝试等待
	led_dev = kzalloc(sizeof(struct hi3559av100_led), GFP_KERNEL);
	if(led_dev == NULL)
	{
		printk(KERN_ERR"kzalloc error\n");
		return -ENOMEM;
	}
	
	
	// 1,  申请主设备号
	led_dev->dev_major = 0;
	ret = register_chrdev(led_dev->dev_major, "led_drv",  &led_fops);
	if(ret < 0)
	{
		printk("register_chrdev error\n");
		ret = -EINVAL;
		goto err_free;
	}
 
	// 2 ---自动创建设备节点
 
	//创建一个类
	// 参数1---当前模块--THIS_MODULE
	// 参数2---字符串,表示类的名字
	//返回值--struct class指针类型
	led_dev->cls = class_create(THIS_MODULE,"led_cls");
	if(IS_ERR(led_dev->cls))
	{
		printk("class_create error\n");
		ret = PTR_ERR(led_dev->cls);
		goto err_unregister;
		
	}
 
	//创建一个设备节点
	// 参数1---class_create返回的指针
	// 参数2---该设备非父类--一般都是填NULL
	//参数3--设备号--包含了主设备号major和次设备号minor 
	//参数4---私有数据指针---一般都是填NULL
	//参数5---设备节点的名字
	//结果  /dev/led
	// 返回值--struct device指针
	led_dev->dev = device_create(led_dev->cls, NULL,MKDEV(led_dev->dev_major, 0), NULL, "led");
	if(IS_ERR(led_dev->dev))
	{
		printk("device_create error\n");
		ret = PTR_ERR(led_dev->dev);
		goto err_class_destroy;
		
	}
 
	// 3, 初始化硬件
	//参数1---物理地址
	//参数2--映射的长度
	//返回值--映射之后的虚拟地址
	gpio3_6_conf = ioremap(0x180D3400, 16);
	gpio3_6_data = ioremap(0x180D3100, 8);
 
	*gpio3_6_conf |= (0x1<<6);
	*gpio3_6_data |= (0x1<<6); 

	return 0;
 
 
err_class_destroy:
	class_destroy(led_dev->cls);
 
err_unregister:
	unregister_chrdev(led_dev->dev_major, "led_drv");
 
err_free:
	kfree(led_dev);
	return ret;
 
 
}
 
 
static void __exit led_drv_exit(void)
{
 
	printk("-------^_^ %s-------\n", __FUNCTION__);
	*gpio3_6_data &= ~(0x1<<6);
	iounmap(gpio3_6_conf);
	iounmap(gpio3_6_data);
	// 模块卸载函数中主要完成系统资源的释放
	device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
	
	class_destroy(led_dev->cls);
	
	//参数1---已经申请到的设备号
	//参数2--字符串--描述设备驱动信息--自定义
	unregister_chrdev(led_dev->dev_major, "led_drv");
 
	kfree(led_dev);
 
}
 
 
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
  • makefile :
# Makefile 4.0

# CC = aarch64-himix100-linux-gcc
ARCH        := arm64
CROSS_COMPILE    := aarch64-himix100-linux-gcc
CURRENT_PATH := $(shell pwd)
# LINUX_KERNEL := $(shell uname -r)
#LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
LINUX_KERNEL_PATH := /home/xiangang/work/haisi/Hi3559AV100R001C02SPC030/01.software/board/Hi3559AV100_SDK_V2.0.3.0/osdrv/opensource/kernel/linux-4.9.y_multi-core
 
obj-m := hello_ko.o
 
all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
  • 测试应用程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

#include <linux/fs.h>
#include <linux/gpio.h>

#define HELLO_MAGIC 12
#define LED_NUM_ON _IOW(HELLO_MAGIC,0,int)//
#define LED_NUM_OFF _IOW(HELLO_MAGIC,1,int)//

int main(int argc, char **argv)
{
    int fd;
    char* filename=NULL;
    char val;
    
    filename = argv[1];
    
    fd = open(filename, O_RDWR);//打开dev/led设备文件
    if (fd < 0)//小于0说明没有成功
    {
        printf("error, can't open %s\n", filename);
        return 0;
    }


    if(!strcmp(argv[2], "on"))
	{
        val = 1;
		ioctl(fd, LED_NUM_ON, &val);
	}
	else
	{
		val = 0;
		ioctl(fd, LED_NUM_OFF, &val);
	}
    // write(fd, &val, 1);//操作LED
    
   return 0;
}
  • 实验

insmod hello_ko.ko        		//加载驱动
cat /proc/devices   			//查看驱动主设备号
mknod /dev/led c 253 0  		//创建设备节点
//应用测试
./test /dev/led on  			//点灯
./test /dev/led off  			//灭灯
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章