海思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  			//滅燈
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章