#include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpioregs.h" #define DEVICE_NAME "power" #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; 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 chardev_device *chardev_dev; static int wait_ibf(void) { int i = 0; while (inb(EC_CMD_PORT) & EC_IBF) { if (++i > TIMEOUT_LOOPS) { return -1; } udelay(1); } return 0; } 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 oem_ec_read_ram(uint8_t page, uint8_t offset, uint8_t *data) { 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; } uint8_t ac_present(void) //read GPIO of DC IN { uint8_t val = 0x00; if (oem_ec_read_ram(2, 0x36, &val) < 0) return 0; // printk(" AC state is : %d\n", (val & 0x03) ? 1 : 0); return (val & 0x03) ? 1 : 0; } 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(" btn state is : %d\n", (val & 0x01) ? 1 : 0); return (val & 0x01) ? 1 : 0; } 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 int chardev_release(struct inode *inode, struct file *filp) { dev_info(device, "Device released\n"); return 0; } 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); } available = min(count, dev->data_len); if (copy_to_user(buf, &chardev_dev->status, available)) { mutex_unlock(&dev->lock); return -EFAULT; } if (available == dev->data_len) { dev->data_len = 0; } else { memmove(dev->buffer, dev->buffer + available, dev->data_len - available); dev->data_len -= available; } ret = available; mutex_unlock(&dev->lock); dev_dbg(device, "Read %zd bytes\n", ret); return ret; } 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; } mutex_unlock(&dev->lock); return mask; } static ssize_t chardev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { 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 chardev_fops = { .owner = THIS_MODULE, .open = chardev_open, .release = chardev_release, .read = chardev_read, .write = chardev_write, .poll = chardev_poll, }; static void delay_work_func(struct work_struct *work) { static int ac_flag = 0; static int rst_flag = 0; uint8_t ret = 0; ret = ac_present(); if(ret == 1) { ac_flag++; } else { ac_flag = 0; } ret = soft_rest_btn(); if(ret == 1) { rst_flag++; } else { rst_flag = 0; } if(ac_flag >= 16) { chardev_dev->status = 'p'; chardev_dev->data_len = 1; wake_up_interruptible(&chardev_dev->read_queue); } if(rst_flag >= 3) { 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)); } 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; } 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_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; 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 ret; } void power_interface_exit(void) { 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); pr_info("Char device demo unloaded\n"); }