|
|
@@ -0,0 +1,177 @@
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
+#include <linux/of.h>
|
|
|
+#include <linux/fs.h>
|
|
|
+#include <linux/cdev.h>
|
|
|
+#include <linux/uaccess.h> // for copy_to_user() and copy_from_user()
|
|
|
+
|
|
|
+const char *TAG = "MYNEWNODE";
|
|
|
+
|
|
|
+static int major_number = 0; // 默认自动分配主设备号
|
|
|
+static struct cdev my_cdev;
|
|
|
+static struct class *my_class;
|
|
|
+char data_buf[100] = "Hello from kernel space! .";
|
|
|
+char write_buf[100 = "Hello from user space!";
|
|
|
+// 打开字符设备
|
|
|
+static int gpio_led_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ printk("%s: open!\n", TAG);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭字符设备
|
|
|
+static int gpio_led_release(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ printk("%s: release!\n", TAG);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+// 读取数据(从内核空间的 data_buf[] 数组中读取数据)
|
|
|
+static ssize_t gpio_led_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
|
|
|
+{
|
|
|
+ ssize_t ret;
|
|
|
+ size_t to_read;
|
|
|
+
|
|
|
+ printk("%s: read!\n", TAG);
|
|
|
+
|
|
|
+ if (*offset >= sizeof(data_buf)) {
|
|
|
+ return 0; // 没有更多数据可以读取
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算最多读取的数据长度,并确保不会越界
|
|
|
+ to_read = min(len, (size_t)(sizeof(data_buf) - *offset)); // 强制转换为 size_t 类型
|
|
|
+
|
|
|
+ // 从内核空间复制数据到用户空间
|
|
|
+ ret = copy_to_user(buf, data_buf + *offset, to_read);
|
|
|
+ if (ret != 0) {
|
|
|
+ printk("%s: copy_to_user failed!\n", TAG);
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新偏移量
|
|
|
+ *offset += to_read;
|
|
|
+
|
|
|
+ return to_read; // 返回实际读取的字节数
|
|
|
+}
|
|
|
+
|
|
|
+// 写入数据(将数据保存到内核空间的 data_buf[] 数组)
|
|
|
+static ssize_t gpio_led_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
|
|
|
+{
|
|
|
+ ssize_t ret;
|
|
|
+
|
|
|
+ printk("%s: write!\n", TAG);
|
|
|
+
|
|
|
+ // 确保写入的数据大小不超过缓冲区
|
|
|
+ if (len > sizeof(data_buf)) {
|
|
|
+ printk("%s: write data_buf too large!\n", TAG);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从用户空间复制数据到内核空间
|
|
|
+ ret = copy_from_user(data_buf, buf, len);
|
|
|
+ if (ret != 0) {
|
|
|
+ printk("%s: copy_from_user failed!\n", TAG);
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ return len; // 返回实际写入的字节数
|
|
|
+}
|
|
|
+
|
|
|
+// 字符设备操作结构体
|
|
|
+static const struct file_operations gpio_led_fops = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = gpio_led_open,
|
|
|
+ .release = gpio_led_release,
|
|
|
+ .read = gpio_led_read,
|
|
|
+ .write = gpio_led_write,
|
|
|
+};
|
|
|
+
|
|
|
+// probe函数:设备初始化时调用
|
|
|
+static int gpio_led_probe(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ printk("%s: probe!\n", TAG);
|
|
|
+
|
|
|
+ // 为字符设备分配主设备号
|
|
|
+ ret = alloc_chrdev_region(&major_number, 0, 1, "gpio_led");
|
|
|
+ if (ret < 0) {
|
|
|
+ printk("%s: alloc_chrdev_region failed!\n", TAG);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 注册字符设备
|
|
|
+ cdev_init(&my_cdev, &gpio_led_fops);
|
|
|
+ my_cdev.owner = THIS_MODULE;
|
|
|
+ ret = cdev_add(&my_cdev, major_number, 1);
|
|
|
+ if (ret) {
|
|
|
+ printk("%s: cdev_add failed!\n", TAG);
|
|
|
+ unregister_chrdev_region(major_number, 1);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建设备类
|
|
|
+ my_class = class_create(THIS_MODULE, "gpio_led_class");
|
|
|
+ if (IS_ERR(my_class)) {
|
|
|
+ ret = PTR_ERR(my_class);
|
|
|
+ printk("%s: class_create failed!\n", TAG);
|
|
|
+ cdev_del(&my_cdev);
|
|
|
+ unregister_chrdev_region(major_number, 1);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建设备节点
|
|
|
+ if (IS_ERR(device_create(my_class, NULL, major_number, NULL, "gpio_led"))) {
|
|
|
+ printk("%s: device_create failed!\n", TAG);
|
|
|
+ class_destroy(my_class);
|
|
|
+ cdev_del(&my_cdev);
|
|
|
+ unregister_chrdev_region(major_number, 1);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+// remove函数:设备卸载时调用
|
|
|
+static int gpio_led_remove(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ printk("%s: remove!\n", TAG);
|
|
|
+
|
|
|
+ // 注销字符设备
|
|
|
+ device_destroy(my_class, major_number);
|
|
|
+ class_destroy(my_class);
|
|
|
+ cdev_del(&my_cdev);
|
|
|
+ unregister_chrdev_region(major_number, 1);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+// shutdown函数:设备关闭时调用
|
|
|
+static void gpio_led_shutdown(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ printk("%s: shutdown!\n", TAG);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct of_device_id of_gpio_leds_match[] = {
|
|
|
+ { .compatible = "led-user", },
|
|
|
+ {}
|
|
|
+};
|
|
|
+
|
|
|
+MODULE_DEVICE_TABLE(of, of_gpio_leds_match);
|
|
|
+
|
|
|
+// 定义平台驱动
|
|
|
+static struct platform_driver gpio_led_driver = {
|
|
|
+ .probe = gpio_led_probe,
|
|
|
+ .remove = gpio_led_remove,
|
|
|
+ .shutdown = gpio_led_shutdown,
|
|
|
+ .driver = {
|
|
|
+ .name = "led-user",
|
|
|
+ .of_match_table = of_gpio_leds_match,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+module_platform_driver(gpio_led_driver);
|
|
|
+
|
|
|
+MODULE_LICENSE("GPL");
|
|
|
+MODULE_AUTHOR("Your Name");
|
|
|
+MODULE_DESCRIPTION("Simple GPIO LEDs driver");
|