|
|
@@ -1,289 +1,405 @@
|
|
|
-#include <linux/init.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/fs.h>
|
|
|
#include <linux/cdev.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/uaccess.h>
|
|
|
-#include <linux/device.h>
|
|
|
-#include <linux/stat.h>
|
|
|
+#include <linux/wait.h>
|
|
|
+#include <linux/sched.h>
|
|
|
#include <linux/poll.h>
|
|
|
+#include <linux/delay.h>
|
|
|
|
|
|
#include <linux/sched.h>
|
|
|
#include <linux/timer.h>
|
|
|
#include <linux/workqueue.h>
|
|
|
|
|
|
#include <linux/io.h>
|
|
|
+
|
|
|
#include "gpioregs.h"
|
|
|
|
|
|
#define DEVICE_NAME "power"
|
|
|
-#define CLASS_NAME "power_class"
|
|
|
-#define BUFFER_SIZE 1024
|
|
|
-
|
|
|
-// 可以通过模块参数指定主设备号和次设备号
|
|
|
-static int power_major = 56; // 默认主设备号
|
|
|
-static int power_minor = 70; // 默认次设备号
|
|
|
-module_param(power_major, int, S_IRUGO);
|
|
|
-module_param(power_minor, int, S_IRUGO);
|
|
|
-MODULE_PARM_DESC(power_major, "Major device number");
|
|
|
-MODULE_PARM_DESC(power_minor, "Minor device number");
|
|
|
-
|
|
|
-static struct class *char_class = NULL;
|
|
|
-static struct device *char_device = NULL;
|
|
|
-static struct cdev my_cdev;
|
|
|
-static dev_t dev_num;
|
|
|
-
|
|
|
-// 设备结构体
|
|
|
-struct power_dev
|
|
|
-{
|
|
|
- char *buffer;
|
|
|
- size_t size;
|
|
|
- struct mutex lock;
|
|
|
+#define CLASS_NAME "power"
|
|
|
+#define BUFFER_SIZE 4096
|
|
|
+#define MAJOR_NUM 56
|
|
|
+#define MINOR_NUM 70
|
|
|
+#define TIMEOUT_LOOPS 100000
|
|
|
+
|
|
|
+static int major_num = MAJOR_NUM;
|
|
|
+static int minor_num = MINOR_NUM;
|
|
|
+static struct class *device_class = NULL;
|
|
|
+static struct device *device = NULL;
|
|
|
+
|
|
|
+/* 设备结构体 */
|
|
|
+struct chardev_device {
|
|
|
struct cdev cdev;
|
|
|
- wait_queue_head_t read_wait;
|
|
|
+ char *buffer; /* 数据缓冲区 */
|
|
|
+ size_t data_len; /* 当前数据长度 */
|
|
|
+ wait_queue_head_t read_queue; /* 读等待队列 */
|
|
|
+ struct mutex lock; /* 互斥锁 */
|
|
|
+ bool nonblock; /* 非阻塞标志(可选) */
|
|
|
struct delayed_work delay_work1;
|
|
|
+ char status;
|
|
|
};
|
|
|
|
|
|
-static struct power_dev *dev = NULL;
|
|
|
+static struct chardev_device *chardev_dev;
|
|
|
|
|
|
-static void delay_work_func(struct work_struct *work)
|
|
|
-{
|
|
|
- printk(KERN_INFO "delay_work_func\n");
|
|
|
-}
|
|
|
|
|
|
-// 文件打开操作
|
|
|
-static int power_open(struct inode *inode, struct file *filp)
|
|
|
+static int wait_ibf(void)
|
|
|
{
|
|
|
- struct power_dev *dev;
|
|
|
-
|
|
|
- dev = container_of(inode->i_cdev, struct power_dev, cdev);
|
|
|
- filp->private_data = dev;
|
|
|
+ int i = 0;
|
|
|
+ while (inb(EC_CMD_PORT) & EC_IBF)
|
|
|
+ {
|
|
|
+ if (++i > TIMEOUT_LOOPS)
|
|
|
+ {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ udelay(1);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- printk(KERN_INFO "power: Device opened (major=%d, minor=%d)\n",
|
|
|
- imajor(inode), iminor(inode));
|
|
|
+static int wait_obf(void)
|
|
|
+{
|
|
|
+ int i = 0;
|
|
|
+ while (!(inb(EC_CMD_PORT) & EC_OBF))
|
|
|
+ {
|
|
|
+ if (++i > TIMEOUT_LOOPS)
|
|
|
+ {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ udelay(1);
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-// 文件释放操作
|
|
|
-static int power_release(struct inode *inode, struct file *filp)
|
|
|
+static int oem_ec_read_ram(uint8_t page, uint8_t offset, uint8_t *data)
|
|
|
{
|
|
|
- // release_region(PORT_80, 1);
|
|
|
- printk(KERN_INFO "power: Device closed\n");
|
|
|
+ unsigned char WEC, REC;
|
|
|
+ switch(page)
|
|
|
+ {
|
|
|
+ case 0:
|
|
|
+ {
|
|
|
+ WEC = 0x96;
|
|
|
+ REC = 0x95;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case 1:
|
|
|
+ {
|
|
|
+ WEC = 0x98;
|
|
|
+ REC = 0x97;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ {
|
|
|
+ WEC = EC_VERSION_WEC;
|
|
|
+ REC = EC_VERSION_REC;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (wait_ibf() < 0)
|
|
|
+ return -1;
|
|
|
+ outb(REC, EC_CMD_PORT);
|
|
|
+
|
|
|
+ if (wait_ibf() < 0)
|
|
|
+ return -1;
|
|
|
+ outb(offset, EC_DATA_PORT);
|
|
|
+
|
|
|
+ if (wait_obf() < 0)
|
|
|
+ return -1;
|
|
|
+ *data = inb(EC_DATA_PORT);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-// 读操作 - 支持cat命令
|
|
|
-static ssize_t power_read(struct file *filp, char __user *buf,
|
|
|
- size_t count, loff_t *f_pos)
|
|
|
+uint8_t ac_present(void) //read GPIO of DC IN
|
|
|
{
|
|
|
- struct power_dev *dev = filp->private_data;
|
|
|
- ssize_t retval = 0;
|
|
|
- size_t available;
|
|
|
- int read_count = 0;
|
|
|
- int ret = 0;
|
|
|
+ uint8_t val = 0x00;
|
|
|
+ if (oem_ec_read_ram(2, 0x36, &val) < 0)
|
|
|
+ return 0;
|
|
|
|
|
|
- if (mutex_lock_interruptible(&dev->lock))
|
|
|
- return -ERESTARTSYS;
|
|
|
+ printk(" AC state is : %d\n", (val & 0x03) ? 1 : 0);
|
|
|
+ return (val & 0x03) ? 1 : 0;
|
|
|
+}
|
|
|
|
|
|
- // 这里read_count没有固定为4个字节,是为了便于后续扩展
|
|
|
- if (count > dev->size)
|
|
|
- {
|
|
|
- read_count = dev->size;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- read_count = count;
|
|
|
- }
|
|
|
- ret = copy_to_user(buf, dev->buffer, read_count);
|
|
|
- if (ret != 0)
|
|
|
- {
|
|
|
- printk(KERN_INFO "power: copy_to_user failed\n");
|
|
|
- goto out;
|
|
|
- }
|
|
|
+uint8_t soft_rest_btn(void)//read GPIO of btn SW_HRST1
|
|
|
+{
|
|
|
+ uint8_t val = 0x00;
|
|
|
+ if (oem_ec_read_ram(2, 0x34, &val) < 0)
|
|
|
+ return 0;
|
|
|
|
|
|
- printk(KERN_INFO "power: Read %zu bytes\n", count);
|
|
|
+ printk(" btn state is : %d\n", (val & 0x01) ? 1 : 0);
|
|
|
+ return (val & 0x01) ? 1 : 0;
|
|
|
+}
|
|
|
|
|
|
-out:
|
|
|
- mutex_unlock(&dev->lock);
|
|
|
- return ret;
|
|
|
+/* 打开设备 */
|
|
|
+static int chardev_open(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct chardev_device *dev = container_of(inode->i_cdev,
|
|
|
+ struct chardev_device, cdev);
|
|
|
+ filp->private_data = dev;
|
|
|
+ dev_info(device, "Device opened\n");
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-// 写操作
|
|
|
-static ssize_t power_write(struct file *filp, const char __user *buf,
|
|
|
- size_t count, loff_t *f_pos)
|
|
|
+/* 释放设备 */
|
|
|
+static int chardev_release(struct inode *inode, struct file *filp)
|
|
|
{
|
|
|
- struct power_dev *dev = filp->private_data;
|
|
|
- ssize_t retval = 0;
|
|
|
- size_t available;
|
|
|
- int ret = 0;
|
|
|
+ dev_info(device, "Device released\n");
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- // 加锁
|
|
|
- if (mutex_lock_interruptible(&dev->lock))
|
|
|
- {
|
|
|
- return -ERESTARTSYS;
|
|
|
+/* read 系统调用 */
|
|
|
+static ssize_t chardev_read(struct file *filp, char __user *buf,
|
|
|
+ size_t count, loff_t *f_pos)
|
|
|
+{
|
|
|
+ struct chardev_device *dev = filp->private_data;
|
|
|
+ ssize_t ret = 0;
|
|
|
+ size_t available;
|
|
|
+
|
|
|
+ mutex_lock(&dev->lock);
|
|
|
+
|
|
|
+ /* 等待数据可用(支持非阻塞和超时) */
|
|
|
+ while (dev->data_len == 0) {
|
|
|
+ if (filp->f_flags & O_NONBLOCK) {
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+ return -EAGAIN;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+
|
|
|
+ /* 等待数据(可中断) */
|
|
|
+ if (wait_event_interruptible(dev->read_queue, dev->data_len > 0)) {
|
|
|
+ return -ERESTARTSYS;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&dev->lock);
|
|
|
}
|
|
|
-
|
|
|
- // 计算可写数据量
|
|
|
- if (count > BUFFER_SIZE)
|
|
|
- {
|
|
|
- count = BUFFER_SIZE;
|
|
|
+
|
|
|
+ /* 确定可读取的数据量 */
|
|
|
+ available = min(count, dev->data_len);
|
|
|
+
|
|
|
+ /* 拷贝数据到用户空间 */
|
|
|
+ if (copy_to_user(buf, &chardev_dev->status, available)) {
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+ return -EFAULT;
|
|
|
}
|
|
|
-
|
|
|
- // 拷贝数据
|
|
|
- ret = copy_from_user(dev->buffer, buf, count);
|
|
|
- if (ret != 0)
|
|
|
- {
|
|
|
- printk(KERN_INFO "power: copy_from_user failed\n");
|
|
|
- goto out;
|
|
|
+
|
|
|
+ /* 更新缓冲区(简单实现:读取后清空所有数据) */
|
|
|
+ if (available == dev->data_len) {
|
|
|
+ dev->data_len = 0;
|
|
|
+ } else {
|
|
|
+ /* 这里简单处理:移动剩余数据到开头 */
|
|
|
+ memmove(dev->buffer, dev->buffer + available, dev->data_len - available);
|
|
|
+ dev->data_len -= available;
|
|
|
}
|
|
|
-
|
|
|
- dev->size = count;
|
|
|
-
|
|
|
- printk(KERN_INFO "power: Written %zu bytes\n", count);
|
|
|
-
|
|
|
-out:
|
|
|
+
|
|
|
+ ret = available;
|
|
|
mutex_unlock(&dev->lock);
|
|
|
+
|
|
|
+ dev_dbg(device, "Read %zd bytes\n", ret);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
|
|
|
- // 调度延迟工作
|
|
|
- if (ret == 0)
|
|
|
- {
|
|
|
- schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(10));
|
|
|
+/* poll 系统调用(支持 select) */
|
|
|
+static __poll_t chardev_poll(struct file *filp, struct poll_table_struct *wait)
|
|
|
+{
|
|
|
+ struct chardev_device *dev = filp->private_data;
|
|
|
+ __poll_t mask = 0;
|
|
|
+
|
|
|
+ poll_wait(filp, &dev->read_queue, wait);
|
|
|
+
|
|
|
+ mutex_lock(&dev->lock);
|
|
|
+ if (dev->data_len > 0) {
|
|
|
+ mask |= EPOLLIN | EPOLLRDNORM; /* 数据可读 */
|
|
|
}
|
|
|
- return count;
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+
|
|
|
+ return mask;
|
|
|
}
|
|
|
|
|
|
-static unsigned int power_poll(struct file *filp, poll_table *wait)
|
|
|
+/* 可选:提供写接口供测试用 */
|
|
|
+static ssize_t chardev_write(struct file *filp, const char __user *buf,
|
|
|
+ size_t count, loff_t *f_pos)
|
|
|
{
|
|
|
- // poll_wait(filp, &dev->read_wait, wait);
|
|
|
- return 0;
|
|
|
+ struct chardev_device *dev = filp->private_data;
|
|
|
+ ssize_t ret;
|
|
|
+ size_t to_write;
|
|
|
+
|
|
|
+ mutex_lock(&dev->lock);
|
|
|
+
|
|
|
+ to_write = min(count, BUFFER_SIZE - dev->data_len);
|
|
|
+ if (to_write == 0) {
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+ return -ENOSPC;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (copy_from_user(dev->buffer + dev->data_len, buf, to_write)) {
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+ return -EFAULT;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev->data_len += to_write;
|
|
|
+ ret = to_write;
|
|
|
+
|
|
|
+ /* 唤醒等待读的进程 */
|
|
|
+ wake_up_interruptible(&dev->read_queue);
|
|
|
+
|
|
|
+ mutex_unlock(&dev->lock);
|
|
|
+
|
|
|
+ dev_dbg(device, "Written %zd bytes\n", ret);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-// 文件操作结构体
|
|
|
-static struct file_operations fops = {
|
|
|
+/* file_operations 结构 */
|
|
|
+static struct file_operations chardev_fops = {
|
|
|
.owner = THIS_MODULE,
|
|
|
- .open = power_open,
|
|
|
- .release = power_release,
|
|
|
- .read = power_read,
|
|
|
- .write = power_write,
|
|
|
- .poll = power_poll,
|
|
|
+ .open = chardev_open,
|
|
|
+ .release = chardev_release,
|
|
|
+ .read = chardev_read,
|
|
|
+ .write = chardev_write,
|
|
|
+ .poll = chardev_poll,
|
|
|
};
|
|
|
|
|
|
-// 模块初始化
|
|
|
-int power_init(void)
|
|
|
+static void delay_work_func(struct work_struct *work)
|
|
|
{
|
|
|
- int result;
|
|
|
-
|
|
|
- printk(KERN_INFO "power: Initializing driver with major=%d, minor=%d\n",
|
|
|
- power_major, power_minor);
|
|
|
+ static int ac_flag = 0;
|
|
|
+ static int rst_flag = 0;
|
|
|
+ uint8_t ret = 0;
|
|
|
|
|
|
- // 检查主设备号是否有效
|
|
|
- if (power_major <= 0)
|
|
|
+ ret = ac_present();
|
|
|
+ if(ret == 1)
|
|
|
{
|
|
|
- printk(KERN_ALERT "power: Invalid major number %d\n", power_major);
|
|
|
- return -EINVAL;
|
|
|
+ ac_flag++;
|
|
|
}
|
|
|
-
|
|
|
- // 构建设备号
|
|
|
- dev_num = MKDEV(power_major, power_minor);
|
|
|
-
|
|
|
- // 注册设备号 - 使用指定的主设备号
|
|
|
- result = register_chrdev_region(dev_num, 1, DEVICE_NAME);
|
|
|
- if (result < 0)
|
|
|
+ else
|
|
|
{
|
|
|
- printk(KERN_ALERT "power: Failed to register major number %d\n", power_major);
|
|
|
- printk(KERN_ALERT "power: Try using a different major number\n");
|
|
|
- return result;
|
|
|
+ ac_flag = 0;
|
|
|
}
|
|
|
|
|
|
- printk(KERN_INFO "power: Registered with major=%d, minor=%d\n",
|
|
|
- MAJOR(dev_num), MINOR(dev_num));
|
|
|
-
|
|
|
- // 分配设备结构体
|
|
|
- dev = kmalloc(sizeof(struct power_dev), GFP_KERNEL);
|
|
|
- if (!dev)
|
|
|
+ ret = soft_rest_btn();
|
|
|
+ if(ret == 1)
|
|
|
{
|
|
|
- result = -ENOMEM;
|
|
|
- goto fail_malloc;
|
|
|
+ rst_flag++;
|
|
|
}
|
|
|
- memset(dev, 0, sizeof(struct power_dev));
|
|
|
- init_waitqueue_head(&dev->read_wait);
|
|
|
-
|
|
|
- // 分配缓冲区
|
|
|
- dev->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
|
|
|
- if (!dev->buffer)
|
|
|
+ else
|
|
|
{
|
|
|
- result = -ENOMEM;
|
|
|
- goto fail_buffer;
|
|
|
+ rst_flag = 0;
|
|
|
}
|
|
|
|
|
|
- // 初始化互斥锁
|
|
|
- mutex_init(&dev->lock);
|
|
|
-
|
|
|
- INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func);
|
|
|
-
|
|
|
- // 初始化字符设备
|
|
|
- cdev_init(&dev->cdev, &fops);
|
|
|
- dev->cdev.owner = THIS_MODULE;
|
|
|
-
|
|
|
- // 添加字符设备到系统
|
|
|
- result = cdev_add(&dev->cdev, dev_num, 1);
|
|
|
- if (result)
|
|
|
+ if(ac_flag >= 16)
|
|
|
{
|
|
|
- printk(KERN_ALERT "power: Failed to add cdev\n");
|
|
|
- goto fail_cdev;
|
|
|
+ chardev_dev->status = 'p';
|
|
|
+ chardev_dev->data_len = 1;
|
|
|
+ wake_up_interruptible(&chardev_dev->read_queue);
|
|
|
}
|
|
|
-
|
|
|
- // 创建设备类
|
|
|
- char_class = class_create(THIS_MODULE, CLASS_NAME);
|
|
|
- if (IS_ERR(char_class))
|
|
|
+
|
|
|
+ if(rst_flag >= 3)
|
|
|
{
|
|
|
- result = PTR_ERR(char_class);
|
|
|
- printk(KERN_ALERT "power: Failed to create class\n");
|
|
|
- goto fail_class;
|
|
|
+ chardev_dev->status = 'r';
|
|
|
+ chardev_dev->data_len = 1;
|
|
|
+ wake_up_interruptible(&chardev_dev->read_queue);
|
|
|
}
|
|
|
+ printk("ac_flag = %d, rst_flag = %d\n", ac_flag, rst_flag);
|
|
|
+ schedule_delayed_work(&chardev_dev->delay_work1, msecs_to_jiffies(500));
|
|
|
+}
|
|
|
|
|
|
- // 创建设备
|
|
|
- char_device = device_create(char_class, NULL, dev_num, NULL, DEVICE_NAME);
|
|
|
- if (IS_ERR(char_device))
|
|
|
- {
|
|
|
- result = PTR_ERR(char_device);
|
|
|
- printk(KERN_ALERT "power: Failed to create device\n");
|
|
|
- goto fail_device;
|
|
|
+/* 初始化设备 */
|
|
|
+int power_interface_init(void)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ dev_t dev_num;
|
|
|
+
|
|
|
+ /* 使用指定的主设备号和次设备号 */
|
|
|
+ dev_num = MKDEV(MAJOR_NUM, MINOR_NUM);
|
|
|
+
|
|
|
+ /* 注册设备号(指定固定主设备号) */
|
|
|
+ ret = register_chrdev_region(dev_num, 1, DEVICE_NAME);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("Failed to register device number %d:%d\n", MAJOR_NUM, MINOR_NUM);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ major_num = MAJOR_NUM;
|
|
|
+ minor_num = MINOR_NUM;
|
|
|
+ pr_info("Registered device number %d:%d\n", major_num, minor_num);
|
|
|
+
|
|
|
+ /* 分配设备结构体 */
|
|
|
+ chardev_dev = kzalloc(sizeof(struct chardev_device), GFP_KERNEL);
|
|
|
+ if (!chardev_dev) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_alloc_dev;
|
|
|
}
|
|
|
|
|
|
- printk(KERN_INFO "power: Driver initialized successfully\n");
|
|
|
- printk(KERN_INFO "power: Device node: /dev/%s (major=%d, minor=%d)\n",
|
|
|
- DEVICE_NAME, power_major, power_minor);
|
|
|
+ INIT_DELAYED_WORK(&chardev_dev->delay_work1, delay_work_func);
|
|
|
+
|
|
|
+ schedule_delayed_work(&chardev_dev->delay_work1, msecs_to_jiffies(500));
|
|
|
+
|
|
|
+ /* 分配数据缓冲区 */
|
|
|
+ chardev_dev->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
|
|
|
+ if (!chardev_dev->buffer) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_alloc_buffer;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 初始化互斥锁和等待队列 */
|
|
|
+ mutex_init(&chardev_dev->lock);
|
|
|
+ init_waitqueue_head(&chardev_dev->read_queue);
|
|
|
+ chardev_dev->data_len = 0;
|
|
|
+
|
|
|
+ /* 初始化 cdev */
|
|
|
+ cdev_init(&chardev_dev->cdev, &chardev_fops);
|
|
|
+ chardev_dev->cdev.owner = THIS_MODULE;
|
|
|
+ ret = cdev_add(&chardev_dev->cdev, dev_num, 1);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("Failed to add cdev\n");
|
|
|
+ goto err_cdev_add;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 创建设备类 */
|
|
|
+ device_class = class_create(THIS_MODULE, CLASS_NAME);
|
|
|
+ if (IS_ERR(device_class)) {
|
|
|
+ ret = PTR_ERR(device_class);
|
|
|
+ pr_err("Failed to create class\n");
|
|
|
+ goto err_class_create;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 创建设备节点 */
|
|
|
+ device = device_create(device_class, NULL, dev_num, NULL, DEVICE_NAME);
|
|
|
+ if (IS_ERR(device)) {
|
|
|
+ ret = PTR_ERR(device);
|
|
|
+ pr_err("Failed to create device\n");
|
|
|
+ goto err_device_create;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_info("Char device demo loaded: major=%d, minor=%d\n", major_num, minor_num);
|
|
|
return 0;
|
|
|
-
|
|
|
-fail_device:
|
|
|
- class_destroy(char_class);
|
|
|
-fail_class:
|
|
|
- cdev_del(&dev->cdev);
|
|
|
-fail_cdev:
|
|
|
- kfree(dev->buffer);
|
|
|
-fail_buffer:
|
|
|
- kfree(dev);
|
|
|
-fail_malloc:
|
|
|
+
|
|
|
+err_device_create:
|
|
|
+ class_destroy(device_class);
|
|
|
+err_class_create:
|
|
|
+ cdev_del(&chardev_dev->cdev);
|
|
|
+err_cdev_add:
|
|
|
+ kfree(chardev_dev->buffer);
|
|
|
+err_alloc_buffer:
|
|
|
+ kfree(chardev_dev);
|
|
|
+err_alloc_dev:
|
|
|
unregister_chrdev_region(dev_num, 1);
|
|
|
- return result;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-// 模块退出
|
|
|
-void power_exit(void)
|
|
|
+/* 退出模块 */
|
|
|
+void power_interface_exit(void)
|
|
|
{
|
|
|
- cancel_delayed_work_sync(&dev->delay_work1);
|
|
|
- device_destroy(char_class, dev_num);
|
|
|
- class_destroy(char_class);
|
|
|
-
|
|
|
- if (dev)
|
|
|
- {
|
|
|
- cdev_del(&dev->cdev);
|
|
|
- if (dev->buffer)
|
|
|
- kfree(dev->buffer);
|
|
|
- kfree(dev);
|
|
|
- }
|
|
|
-
|
|
|
+ dev_t dev_num = MKDEV(major_num, minor_num);
|
|
|
+
|
|
|
+ cancel_delayed_work_sync(&chardev_dev->delay_work1);
|
|
|
+
|
|
|
+ device_destroy(device_class, dev_num);
|
|
|
+ class_destroy(device_class);
|
|
|
+ cdev_del(&chardev_dev->cdev);
|
|
|
+ kfree(chardev_dev->buffer);
|
|
|
+ kfree(chardev_dev);
|
|
|
unregister_chrdev_region(dev_num, 1);
|
|
|
- printk(KERN_INFO "power: Driver removed (major=%d, minor=%d)\n",
|
|
|
- power_major, power_minor);
|
|
|
+
|
|
|
+ pr_info("Char device demo unloaded\n");
|
|
|
}
|