|
@@ -3,45 +3,118 @@
|
|
|
#include <linux/of.h>
|
|
#include <linux/of.h>
|
|
|
#include <linux/fs.h>
|
|
#include <linux/fs.h>
|
|
|
#include <linux/cdev.h>
|
|
#include <linux/cdev.h>
|
|
|
-#include <linux/uaccess.h> // for copy_to_user() and copy_from_user()
|
|
|
|
|
-const char *TAG = "MYNEWNODE";\
|
|
|
|
|
|
|
+#include <linux/uaccess.h>
|
|
|
|
|
+#include <linux/gpio.h>
|
|
|
|
|
+#include <linux/of_gpio.h>
|
|
|
|
|
+
|
|
|
|
|
+const char *TAG = "MYNEWNODE";
|
|
|
|
|
|
|
|
static int major_number = 0; // 默认自动分配主设备号
|
|
static int major_number = 0; // 默认自动分配主设备号
|
|
|
static struct cdev my_cdev;
|
|
static struct cdev my_cdev;
|
|
|
static struct class *my_class;
|
|
static struct class *my_class;
|
|
|
-
|
|
|
|
|
|
|
+static int gpio_pin = -1; // GPIO 引脚号
|
|
|
|
|
|
|
|
// 打开字符设备
|
|
// 打开字符设备
|
|
|
static int gpio_led_open(struct inode *inode, struct file *file)
|
|
static int gpio_led_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
{
|
|
|
|
|
+ int ret;
|
|
|
|
|
+
|
|
|
printk("%s:open!\n", TAG);
|
|
printk("%s:open!\n", TAG);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果 GPIO 引脚没有初始化,先进行初始化
|
|
|
|
|
+ if (gpio_pin < 0) {
|
|
|
|
|
+ printk("%s:GPIO not initialized correctly.\n", TAG);
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 设置 GPIO 为输出模式,初始化时设置为低电平(关闭 LED)
|
|
|
|
|
+ ret = gpio_direction_output(gpio_pin, 0); // 默认设置为低电平(LED 关闭)
|
|
|
|
|
+ if (ret) {
|
|
|
|
|
+ printk("%s:Failed to set GPIO direction\n", TAG);
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 可以根据需要设置 GPIO 的初始电平,比如点亮 LED
|
|
|
|
|
+ gpio_set_value(gpio_pin, 1); // 设置为高电平,点亮 LED(假设低电平为关闭)
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 关闭字符设备
|
|
|
|
|
|
|
+// 释放字符设备
|
|
|
static int gpio_led_release(struct inode *inode, struct file *file)
|
|
static int gpio_led_release(struct inode *inode, struct file *file)
|
|
|
{
|
|
{
|
|
|
printk("%s:release!\n", TAG);
|
|
printk("%s:release!\n", TAG);
|
|
|
|
|
+
|
|
|
|
|
+ // 在设备释放时设置 GPIO 为低电平(关闭 LED)
|
|
|
|
|
+ gpio_set_value(gpio_pin, 0); // 设置为低电平,关闭 LED
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 读取数据(此示例没有实际硬件操作)
|
|
// 读取数据(此示例没有实际硬件操作)
|
|
|
|
|
+// 读取数据(控制 LED)
|
|
|
static ssize_t gpio_led_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
|
|
static ssize_t gpio_led_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
|
|
|
{
|
|
{
|
|
|
|
|
+ int gpio_value = 0;
|
|
|
|
|
+ char read_buf[2]; // 用来存储读取的数据
|
|
|
|
|
+
|
|
|
printk("%s:read!\n", TAG);
|
|
printk("%s:read!\n", TAG);
|
|
|
- // 这里可以填充实际的读取逻辑,如果需要从硬件读取数据
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 模拟 LED 状态控制,循环写入 '0' 和 '1'
|
|
|
|
|
+ if (*offset % 2 == 0) {
|
|
|
|
|
+ gpio_value = 0; // 关闭 LED
|
|
|
|
|
+ read_buf[0] = '0';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ gpio_value = 1; // 点亮 LED
|
|
|
|
|
+ read_buf[0] = '1';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 设置 GPIO 引脚电平,控制 LED 开关
|
|
|
|
|
+ gpio_set_value(gpio_pin, gpio_value);
|
|
|
|
|
+
|
|
|
|
|
+ // 将 LED 状态写入到用户空间
|
|
|
|
|
+ if (copy_to_user(buf, read_buf, 1)) {
|
|
|
|
|
+ return -EFAULT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新偏移量,每次切换一次
|
|
|
|
|
+ *offset += 1;
|
|
|
|
|
+
|
|
|
|
|
+ return 1; // 返回读取的字节数
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 写入数据(此示例没有实际硬件操作)
|
|
|
|
|
|
|
+
|
|
|
|
|
+// 写入数据(控制 GPIO)
|
|
|
static ssize_t gpio_led_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
|
|
static ssize_t gpio_led_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
|
|
|
{
|
|
{
|
|
|
|
|
+ char user_buf[10];
|
|
|
|
|
+ int gpio_value = 0;
|
|
|
|
|
+
|
|
|
printk("%s:write!\n", TAG);
|
|
printk("%s:write!\n", TAG);
|
|
|
- // 这里可以填充实际的写入逻辑,如果需要控制硬件
|
|
|
|
|
- return len; // 返回实际写入的字节数
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (len > sizeof(user_buf) - 1) {
|
|
|
|
|
+ len = sizeof(user_buf) - 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (copy_from_user(user_buf, buf, len)) {
|
|
|
|
|
+ return -EFAULT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ user_buf[len] = '\0';
|
|
|
|
|
+
|
|
|
|
|
+ if (user_buf[0] == '1') {
|
|
|
|
|
+ gpio_value = 1;
|
|
|
|
|
+ } else if (user_buf[0] == '0') {
|
|
|
|
|
+ gpio_value = 0;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ gpio_set_value(gpio_pin, gpio_value);
|
|
|
|
|
+
|
|
|
|
|
+ return len;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 字符设备操作结构体
|
|
|
|
|
static const struct file_operations gpio_led_fops = {
|
|
static const struct file_operations gpio_led_fops = {
|
|
|
.owner = THIS_MODULE,
|
|
.owner = THIS_MODULE,
|
|
|
.open = gpio_led_open,
|
|
.open = gpio_led_open,
|
|
@@ -50,18 +123,34 @@ static const struct file_operations gpio_led_fops = {
|
|
|
.write = gpio_led_write,
|
|
.write = gpio_led_write,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 设备probe函数
|
|
|
static int gpio_led_probe(struct platform_device *pdev)
|
|
static int gpio_led_probe(struct platform_device *pdev)
|
|
|
-{ int ret;
|
|
|
|
|
- // 这里可以添加初始化代码
|
|
|
|
|
- printk("%s:probe!\n",TAG);
|
|
|
|
|
|
|
+{
|
|
|
|
|
+ int ret;
|
|
|
|
|
+
|
|
|
|
|
+ printk("%s:probe!\n", TAG);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取设备树中的 GPIO 引脚
|
|
|
|
|
+ gpio_pin = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
|
|
|
|
|
+ if (gpio_pin < 0) {
|
|
|
|
|
+ printk("%s:Failed to get GPIO from device tree!\n", TAG);
|
|
|
|
|
+ return gpio_pin;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 申请 GPIO 引脚
|
|
|
|
|
+ ret = gpio_request(gpio_pin, "gpio_led");
|
|
|
|
|
+ if (ret) {
|
|
|
|
|
+ printk("%s:Failed to request GPIO %d\n", TAG, gpio_pin);
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ret = alloc_chrdev_region(&major_number,0,1,"gpio_led");
|
|
|
|
|
- if(ret < 0){
|
|
|
|
|
|
|
+ // 初始化设备文件
|
|
|
|
|
+ ret = alloc_chrdev_region(&major_number, 0, 1, "gpio_led");
|
|
|
|
|
+ if (ret < 0) {
|
|
|
printk("%s:alloc_chrdev_region failed!\n", TAG);
|
|
printk("%s:alloc_chrdev_region failed!\n", TAG);
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 注册字符设备
|
|
|
|
|
cdev_init(&my_cdev, &gpio_led_fops);
|
|
cdev_init(&my_cdev, &gpio_led_fops);
|
|
|
my_cdev.owner = THIS_MODULE;
|
|
my_cdev.owner = THIS_MODULE;
|
|
|
ret = cdev_add(&my_cdev, major_number, 1);
|
|
ret = cdev_add(&my_cdev, major_number, 1);
|
|
@@ -89,7 +178,7 @@ static int gpio_led_probe(struct platform_device *pdev)
|
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return 0; // 返回 0 表示成功
|
|
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 设备remove函数
|
|
// 设备remove函数
|
|
@@ -97,6 +186,9 @@ static int gpio_led_remove(struct platform_device *pdev)
|
|
|
{
|
|
{
|
|
|
printk("%s:remove!\n", TAG);
|
|
printk("%s:remove!\n", TAG);
|
|
|
|
|
|
|
|
|
|
+ // 释放 GPIO 引脚
|
|
|
|
|
+ gpio_free(gpio_pin);
|
|
|
|
|
+
|
|
|
// 注销字符设备
|
|
// 注销字符设备
|
|
|
device_destroy(my_class, major_number);
|
|
device_destroy(my_class, major_number);
|
|
|
class_destroy(my_class);
|
|
class_destroy(my_class);
|
|
@@ -106,6 +198,7 @@ static int gpio_led_remove(struct platform_device *pdev)
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 关机处理函数
|
|
|
static void gpio_led_shutdown(struct platform_device *pdev)
|
|
static void gpio_led_shutdown(struct platform_device *pdev)
|
|
|
{
|
|
{
|
|
|
printk("%s:shutdown!\n", TAG);
|
|
printk("%s:shutdown!\n", TAG);
|
|
@@ -123,7 +216,7 @@ static struct platform_driver gpio_led_driver = {
|
|
|
.remove = gpio_led_remove,
|
|
.remove = gpio_led_remove,
|
|
|
.shutdown = gpio_led_shutdown,
|
|
.shutdown = gpio_led_shutdown,
|
|
|
.driver = {
|
|
.driver = {
|
|
|
- .name = "led-user",
|
|
|
|
|
|
|
+ .name = "gpio_led_driver",
|
|
|
.of_match_table = of_gpio_leds_match,
|
|
.of_match_table = of_gpio_leds_match,
|
|
|
},
|
|
},
|
|
|
};
|
|
};
|
|
@@ -132,4 +225,4 @@ module_platform_driver(gpio_led_driver);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_LICENSE("GPL");
|
|
|
MODULE_AUTHOR("Your Name");
|
|
MODULE_AUTHOR("Your Name");
|
|
|
-MODULE_DESCRIPTION("Simple GPIO LEDs driver");
|
|
|
|
|
|
|
+MODULE_DESCRIPTION("GPIO control driver with character device");
|