上一篇写的是如何用himm工具来控制寄存器点灯,这次写个驱动试试!
/************************led_drv.c*******************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 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
#include
#include
#include
#include
#include
#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 //灭灯