#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
struct resource* res; //
unsigned int irqno; // 软中断号
struct gpio_desc* gpiono[3]; // GPIO信息结构体
struct class* cls; // 目录信息结构体
struct device* dev; // 设备信息结构体
int i; // 循环变量
int ret = 0; // 返回值
struct cdev* cdev; // 字符设备驱动结构体
unsigned int major = 500; // 主设备号
unsigned int minor = 0; // 次设备号
unsigned int count = 1; // 申请设备资源个数
dev_t devno; // 设备号
long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
int which; // 用户层传递的第三个参数
int ret = copy_from_user(&which, (void*)arg, 4);
if (ret) {
printk("copy_from_user filed\n");
return ret;
}
switch (cmd) {
case 1: // 开灯
switch (which) {
case 1:
gpiod_set_value(gpiono[which - 1], 1);
break;
case 2:
gpiod_set_value(gpiono[which - 1], 1);
break;
case 3:
gpiod_set_value(gpiono[which - 1], 1);
break;
}
break;
case 0: // 关灯
switch (which) {
case 1:
gpiod_set_value(gpiono[which - 1], 0);
break;
case 2:
gpiod_set_value(gpiono[which - 1], 0);
break;
case 3:
gpiod_set_value(gpiono[which - 1], 0);
break;
}
break;
}
return 0;
}
// 定义操作方法结构体变量
struct file_operations fops = {
.unlocked_ioctl = mycdev_ioctl,
};
// 定义相关成员函数
// probe函数用于匹配设备成功后执行
int pdrv_probe(struct platform_device* pdev) // 当和设备匹配成功之后执行probe
{
// 1.分配对象空间
cdev = cdev_alloc();
if (cdev == NULL) {
printk("分配字符设备驱动对象失败\n");
ret = -EFAULT;
goto OUT1;
}
printk("分配对象空间成功\n");
// 2.初始化对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (major > 0) // 静态指定设备号
{
ret = register_chrdev_region(MKDEV(major, minor), count, "myled");
if (ret) {
printk("静态指定设备号失败\n");
goto OUT2;
}
} else if (major == 0) // 动态申请设备号
{
ret = alloc_chrdev_region(&devno, minor, count, "myled");
if (ret) {
printk("动态申请设备号失败\n");
goto OUT2;
}
// 获取主设备号和次设备号
major = MAJOR(devno);
minor = MINOR(devno);
}
printk("申请设备号成功\n");
// 4.注册字符设备驱动对象
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret) {
printk("注册字符设备驱动对象失败\n");
goto OUT3;
}
printk("注册字符设备驱动对象成功\n");
// 向上提交目录
cls = class_create(THIS_MODULE, "myled");
if (IS_ERR(cls)) {
printk("向上提交目录失败\n");
ret = -PTR_ERR(cls);
goto OUT4;
}
printk("向上提交目录成功\n");
// 向上提交设备节点信息
for (i = 0; i < count; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev)) {
ret = -PTR_ERR(dev);
goto OUT5;
}
}
printk("向上提交设备节点成功\n");
// 解析MEM类型的资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
printk("解析资源失败%d\n", __LINE__);
return -ENOMEM;
}
// 解析irq类型的资源
irqno = platform_get_irq(pdev, 0);
if (irqno < 0) {
printk("解析资源失败%d\n", __LINE__);
return -ENOMEM;
}
printk("mem:%x irq:%d\n", res->start, irqno);
// 解析GPIO编号
for (i = 0; i < ARRAY_SIZE(gpiono); i++) {
gpiono[i] = gpiod_get_from_of_node(pdev->dev.of_node, "led", i, GPIOD_OUT_LOW, NULL);
if (IS_ERR(gpiono[i])) {
printk("GPIO解析失败\n");
return PTR_ERR(gpiono[i]);
}
}
printk("GPIO解析成功\n");
return 0;
OUT5:
// 释放已经申请的设备节点信息
for (--i; i >= 0; i--) {
device_destroy(cls, MKDEV(major, i));
}
// 释放目录空间
class_destroy(cls);
OUT4:
// 注销字符设备驱动对象
cdev_del(cdev);
OUT3:
// 释放设备号
unregister_chrdev_region(MKDEV(major, minor), count);
OUT2:
// 释放对象空间
kfree(cdev);
OUT1:
return ret;
}
// remove用于和设备分离时执行
int pdrv_remove(struct platform_device* pdev) // 当设备和驱动分离时执行remove
{
for (i = 0; i < ARRAY_SIZE(gpiono); i++) {
// 灭灯
gpiod_set_value(gpiono[i], 0);
// 释放GPIO编号
gpiod_put(gpiono[i]);
}
// 销毁设备节点
for (i = 0; i < count; i++) {
device_destroy(cls, MKDEV(major, i));
}
// 释放目录空间
class_destroy(cls);
// 1.注销字符设备驱动对象
cdev_del(cdev);
// 2.释放设备号
unregister_chrdev_region(MKDEV(major, minor), count);
// 3.释放对象空间
kfree(cdev);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
// 在驱动中创建一个用于设备树匹配的表
struct of_device_id oftable[] = {
{
.compatible = "hqyj,myplatform",
},
{
.compatible = "hqyj,myplatform1",
},
{
.compatible = "hqyj,myplatform2",
},
{},
};
// 分配对象并初始化
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name = "aaaaa",
.of_match_table = oftable, // 指定设备树匹配表的首地址
},
};
// 一键注册注销宏
module_platform_driver(pdrv);
// static int __init mycdev_init(void)
// {
// // 对象的注册
// platform_driver_register(&pdrv);
// return 0;
// }
// static void __exit mycdev_exit(void)
// {
// // 对象的注销
// platform_driver_unregister(&pdrv);
// }
// module_init(mycdev_init);
// module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const* argv[])
{
int fd = open("/dev/myled0", O_RDWR);
if (fd < 0) {
printf("打开设备文件失败\n");
exit(-1);
}
int on_off; // 开关选项 0(关) 1(开)
int device; // 设备选项 1~3
while (1) {
printf("请输入:1(开) 0(关)>");
scanf("%d", &on_off);
getchar();
if (on_off != 0 && on_off != 1) {
printf("输入错误,请重新输入\n");
continue;
}
printf("请输入控制的设备:1(LED1) 2(LED2) 3(LED3) >");
scanf("%d", &device);
getchar();
if (device != 1 && device != 2 && device != 3) {
printf("设备输入错误,请重新输入\n");
continue;
}
// 根据on_off和device,开关对应的灯
ioctl(fd, on_off, &device);
}
close(fd);
return 0;
}