练习
基于platform实现
在根节点下,增加设备树
myplatform {
compatible="hqyj,myplatform";
interrupts-extended=<&gpiof 9 0>, <&gpiof 7 0>, <&gpiof 8 0>;
led1-gpio=<&gpioe 10 0>;
reg=<0x12345678 59>;
};
驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
// 中断
struct device_node *dnode;
unsigned int key_irqno;
struct gpio_desc *gpiono;
// 字符设备驱动
int major;
// 自动创建设备节点
struct class *cls;
struct device *dev;
struct resource *res;
int number = 0;
unsigned int condition = 0;
wait_queue_head_t wq_head; // 等待队列头
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
int ret;
char kbuf[128] = {0};
sprintf(kbuf, "number = %d", number);
if (file->f_flags & O_NONBLOCK)
{
// 非阻塞
return -EINVAL;
}
else
{
// 阻塞
ret = wait_event_interruptible(wq_head, condition);
if (ret < 0)
{
printk("receive signal.... \n");
return ret;
}
}
// 拷贝数据到用户空间
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("copy_to_user err \n");
return -EIO;
}
// condition清零
condition = 0;
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
// 定义操作方法结构体遍历并且初始化
struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
irqreturn_t key_handler(int irq, void *dev)
{
if (number == 0)
{
number = 1;
}
else
{
number = 0;
}
gpiod_set_value(gpiono, number);
condition = 1;
wake_up_interruptible(&wq_head);
return IRQ_HANDLED;
}
// 封装probe函数和remove函数
int pdrv_probe(struct platform_device *pdev)
{
printk("pdrv_probe --------------------------- \n");
int ret;
/**
* 1. 注册设备驱动
*/
major = register_chrdev(0, "mychrdev", &fops);
if (major < 0)
{
printk("字符设备驱动注册失败 \n");
return major;
}
printk("字符设备驱动注册成功\n");
/**
* 2. 自动创建设备节点
*/
// 向上提交目录信息
cls = class_create(THIS_MODULE, "mycdev");
if (IS_ERR(cls))
{
printk("向上提交目录信息失败\n");
return -PTR_ERR(cls);
}
printk("向上提交目录信息成功\n");
// 向上提交设备信息
dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "mycdev0");
if (IS_ERR(dev))
{
printk("向上提交设备节点失败\n");
return -PTR_ERR(cls);
}
// 3. 初始化等待队列头
init_waitqueue_head(&wq_head);
// 获取MEM类型的资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL)
{
printk("获取MEM类型资源失败\n");
return -ENXIO;
}
/**
* 中断
*/
// 获取软中断号
key_irqno = platform_get_irq(pdev, 0);
if (key_irqno < 0) {
printk("获取软中断号失败 \n");
return -ENXIO;
}
printk("获取软中断号成功 \n");
// 注册中断
ret = request_irq(key_irqno, key_handler, IRQF_TRIGGER_FALLING, "key1", NULL);
if (ret < 0)
{
printk("注册中断失败\n");
return -1;
}
printk("注册中断成功\n");
/**
* LED灯
*/
// 获取gpio编号
gpiono=gpiod_get_from_of_node(pdev->dev.of_node,"led1-gpio",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(gpiono))
{
printk("解析gpio信息失败\n");
return -PTR_ERR(gpiono);
}
printk("解析gpio编号成功\n");
return 0;
}
int pdrv_remove(struct platform_device *pdev)
{
free_irq(key_irqno, NULL);
gpiod_set_value(gpiono, 0);
gpiod_put(gpiono);
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "mychrdev");
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
// 构建设备树匹配表
struct of_device_id oftable[] = {
{
.compatible="hqyj,myplatform",
},
{},
};
// 分配驱动信息对象并初始化
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name="aaaaa",
.of_match_table = oftable,
},
};
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
应用层代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/epoll.h>
int main(int argc, const char* argv[])
{
int fd;
char buf[128];
if ((fd = open("/dev/mycdev0", O_RDWR)) == -1) {
perror("open error");
exit(EXIT_FAILURE);
}
while (1) {
bzero(buf, sizeof(buf));
read(fd, buf, sizeof(buf));
printf("status = %s\n", buf);
}
close(fd);
return 0;
}
效果展示