驱动开发 作业5

题目

使用驱动代码实现如下要求:

  1. 使用驱动通过阻塞 io 模型读取 number 变量的值
  2. number 是内核驱动中的一个变量
  3. number 的值随着按键按下而改变(按键中断)
    • 例如 number = 0 按下按键 number = 1,再次按下按键 number = 0
  4. 在按下按键的时候需要同时将 led1 的状态取反>
  5. 驱动中需要编写字符设备驱动
  6. 驱动中需要自动创建设备节点
  7. 在这个驱动需要的所有设备信息放在设备树的同一个节点中

Repo

设备树文件

c 复制代码
/dts-v1/;

#include "stm32mp157.dtsi"
#include "stm32mp15xa.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxac-pinctrl.dtsi"
#include "stm32mp15xx-fsmp1x.dtsi" 

/ {
  model = "HQYJ STM32MP157 FSMP1A Discovery Board";
  compatible = "st,stm32mp157a-dk1", "st,stm32mp157";

  aliases {
    serial0 = &uart4;
    serial5 = &usart3;
  };

  chosen {
    stdout-path = "serial0:115200n8";
  };

  reserved-memory {
    gpu_reserved: gpu@d4000000 {
                    reg = <0xd4000000 0x4000000>;
                    no-map;
                  };

    optee_memory: optee@0xde000000 {
                    reg = <0xde000000 0x02000000>;
                    no-map;
                  };
  };
  
  //自定义设备树 
  mynode@0x12345678{ 
		compatible = "hqyj,mynode"; 
    	astring="hello 23031"; 
		uint  =<0xaabbccdd 0x11223344>; 
    	binarry=[00 0c 29 7b f9 be]; 
    	mixed ="hello",[11 22],<0x12345678>; 
	 };

  mychrdev {
   led=<&gpioe 10 0>;    
   interrupt-parent=<&gpiof>;
   interrupts=<9 0>;
  };
};

驱动代码

c 复制代码
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>

int major;
struct class *cls;
struct device *dev;

int number = 0;
unsigned int condition = 0;
wait_queue_head_t wq_head;

// 所有的设备都在同一个节点下
struct device_node *dnode;
unsigned int irqno;
struct gpio_desc *gpiono;

ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
	int ret;
	wait_event_interruptible(wq_head, condition);

	ret = copy_to_user(ubuf, &number, sizeof(number));
	if (ret) {
		printk("copy_to_user failed\n");
		return -EIO;
	}
	condition = 0;
	return 0;
}

struct file_operations fops = {
	.read = mycdev_read,
};

// 中断处理函数
irqreturn_t myirq_handler(int irq, void *dev_id)
{
	number = !number;
	gpiod_set_value(gpiono, number);
	printk("key pressed, number=%d\n", number);
	return IRQ_HANDLED;
}

static int __init mycdev_init(void)
{
	int ret;
	// 初始化等待队列头
	init_waitqueue_head(&wq_head);
	// 字符设备注册
	major = register_chrdev(0, "mycdev", &fops);
	if (major < 0) {
		printk("register_chrdev failed\n");
		return major;
	}
	// 向上提交目录
	cls = class_create(THIS_MODULE, "mycdev");
	if (IS_ERR(cls)) {
		printk("class_create failed\n");
		return -PTR_ERR(cls);
	}
	// 创建设备文件
	dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "led");
	if (IS_ERR(dev)) {
		printk("device_create failed\n");
		return -PTR_ERR(dev);
	}
	dev = device_create(cls, NULL, MKDEV(major, 1), NULL, "key");
	if (IS_ERR(dev)) {
		printk("device_create failed\n");
		return -PTR_ERR(dev);
	}
	dnode = of_find_node_by_path("/mychrdev");
	if (!dnode) {
		printk("of_find_node_by_path failed\n");
		return -ENXIO;
	}
	irqno = irq_of_parse_and_map(dnode, 0);
	if (!irqno) {
		printk("irq_of_parse_and_map failed\n");
		return -ENXIO;
	}
	ret = request_irq(irqno, myirq_handler, IRQF_TRIGGER_FALLING, "key",
			  NULL);
	if (ret) {
		printk("request_irq failed\n");
		return ret;
	}
	gpiono = gpiod_get_from_of_node(dnode, "led", 0, GPIOD_OUT_LOW, NULL);
	if (IS_ERR(gpiono)) {
		printk("gpiod_get_from_of_node failed\n");
		return -PTR_ERR(gpiono);
	}
	return 0;
}

static void __exit mycdev_exit(void)
{
	// 注销设备文件
	device_destroy(cls, MKDEV(major, 0));
	device_destroy(cls, MKDEV(major, 1));
	// 注销目录
	class_destroy(cls);
	// 注销字符设备
	unregister_chrdev(major, "mycdev");
	// 注销中断号
	free_irq(irqno, NULL);
	// 释放gpio
	gpiod_put(gpiono);
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

测试代码

c 复制代码
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>

int main(int argc, char const *argv[])
{
  int number;
	int fd = open("/dev/led", O_RDWR);
	if (fd < 0) {
		printf("打开设备文件失败\n");
		exit(-1);
	}
	while (1) {
		read(fd, &number, sizeof(number)); //读取数据
		printf("read:%d\n", number);
	}

	close(fd);
	return 0;
}
相关推荐
三菱-Liu1 小时前
三菱MR-J4-B伺服连接器和信号排列
网络·驱动开发·硬件工程·制造·mr
挨踢小明5 小时前
arm64架构的linux 配置vm_page_prot方式
驱动开发
lishing616 小时前
Linux驱动开发(4):Linux的设备模型
linux·运维·驱动开发
TeYiToKu2 天前
笔记整理—linux驱动开发部分(10)input子系统与相关框架
linux·驱动开发·笔记·嵌入式硬件·arm·1024程序员节
TMC_LAI2 天前
采用 EtherCAT 的磁场定向控制 (FOC) 伺服运动控制器 IC-TMC8670-BI
人工智能·驱动开发·嵌入式硬件
三菱-Liu2 天前
三菱MR-J4伺服绝对位置检测系统
运维·驱动开发·嵌入式硬件·硬件工程·制造·mr
这个菠萝好咸2 天前
内核源码+vscode+bear+clang实现函数任意跳转,无缝跳转,无缝阅读,无缝开发
linux·c语言·驱动开发·后端·arm单片机
TeYiToKu3 天前
笔记整理—linux驱动开发部分(7)misc类设备(杂项设备)
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件
TeYiToKu5 天前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
学习嵌入式的小羊~6 天前
linux驱动-i2c子系统框架学习(1)
linux·驱动开发