#include #include #include #include #include #include #include #include #define DEVICE_NAME "com12v" #define CLASS_NAME "com12v_class" #define MAJOR_NUM 56 #define MINOR_NUM 20 #define DEVICE_COUNT 1 #define NUM_COMPORTS 6 #define A_LONG_TIME 100 // 10.0 seconds #define DEFAULT_DELAY 20 // 2.0 second typedef enum { PPS_OFF = 0, PPS_AWAITING_ON, PPS_ON, NUM_POWER_PORT_STATES } ENUM_POWER_PORT_STATES; static struct class *com12v_class = NULL; static struct device *com12v_device = NULL; static struct cdev com12v_cdev; static dev_t dev_num; static unsigned int muiPowerOnDelayCount = DEFAULT_DELAY; static unsigned int mauiPowerPortState[NUM_COMPORTS]; static struct delayed_work delay_work1; static unsigned int com12v_ctl_phy_addr[NUM_COMPORTS] = {0xFD6E0750, 0xFD6E0760, 0xFD6E0770, 0xFD6E0780, 0xFD6D0920, 0xFD6D0930}; static void __iomem *com12v_ctl_vir_addr[NUM_COMPORTS]; static void com12v_delay_work_func(struct work_struct *work) { static unsigned int uiTimeSinceLastPowerOn = A_LONG_TIME; int i; unsigned int value = 0; // Keep incrementing the uiTimeSinceLastPowerOn until it is large enough // that we can safely assume a long time has passed since the last port // transitioned on. if (uiTimeSinceLastPowerOn < A_LONG_TIME) { ++uiTimeSinceLastPowerOn; } // If a port is waiting to power on and enough time has passed since the // last port powered on then go ahead and power on the waiting port. The // break statement insure we don't power on more than one port at a time. for (i=0; i < NUM_COMPORTS; ++i) { // printk("%d", mauiPowerPortState[i]); if ((mauiPowerPortState[i] == PPS_AWAITING_ON) && (uiTimeSinceLastPowerOn >= muiPowerOnDelayCount)) { mauiPowerPortState[i] = PPS_ON; uiTimeSinceLastPowerOn = 0; break; } } // printk("\n"); // Turn on/off hw power supply pins based on current states. for (i=0; i < NUM_COMPORTS; ++i) { if (mauiPowerPortState[i] == PPS_ON) { value = readl(com12v_ctl_vir_addr[i]); value &= 0xfffffffe; writel(value, com12v_ctl_vir_addr[i]); } else if (mauiPowerPortState[i] == PPS_OFF) { value = readl(com12v_ctl_vir_addr[i]); value |= 0x1; writel(value, com12v_ctl_vir_addr[i]); } } schedule_delayed_work(&delay_work1, msecs_to_jiffies(100)); } static int com12v_open(struct inode *inode, struct file *file) { // pr_info("com12v: device opened\n"); return 0; } static int com12v_release(struct inode *inode, struct file *file) { // pr_info("com12v: device closed\n"); return 0; } static ssize_t com12v_read(struct file *file, char __user *user_buf, size_t len, loff_t *offset) { size_t read_len; return read_len; } static ssize_t com12v_write(struct file *file, const char __user *user_buf, size_t len, loff_t *offset) { int i = 0; int iPortIndex; char buff[1024]; char c; size_t write_len; if (len > sizeof(buff)) { write_len = sizeof(buff); } else { write_len = len; } if (copy_from_user(buff, user_buf, write_len)) { return -EFAULT; } for (i = 0; i < write_len; i++) { c = buff[i]; if ((c >= 'A') && (c <= 'F')) { iPortIndex = c - 'A'; if (muiPowerOnDelayCount == 0) { mauiPowerPortState[iPortIndex] = PPS_ON; } else { if (mauiPowerPortState[iPortIndex] == PPS_OFF) { mauiPowerPortState[iPortIndex] = PPS_AWAITING_ON; } } } else if ((c >= 'a') && (c <= 'f')) { iPortIndex = c - 'a'; mauiPowerPortState[iPortIndex] = PPS_OFF; } else if ((c >= '0') && (c <= '9')) { muiPowerOnDelayCount = (c - '0') * 10; } else { printk("com12v: invalid character %c\n", c); } } return write_len; } static struct file_operations com12v_fops = { .owner = THIS_MODULE, .open = com12v_open, .release = com12v_release, .read = com12v_read, .write = com12v_write, }; int com12v_init(void) { int ret; int i = 0; for(i = 0; i < NUM_COMPORTS; i++) { com12v_ctl_vir_addr[i] = ioremap(com12v_ctl_phy_addr[i], 4); } pr_info("com12v: initializing driver\n"); memset(mauiPowerPortState, 0, sizeof(mauiPowerPortState)); INIT_DELAYED_WORK(&delay_work1, com12v_delay_work_func); schedule_delayed_work(&delay_work1, msecs_to_jiffies(100)); dev_num = MKDEV(MAJOR_NUM, MINOR_NUM); ret = register_chrdev_region(dev_num, DEVICE_COUNT, DEVICE_NAME); if (ret < 0) { pr_err("com12v: Failed to register device number %d:%d\n", MAJOR_NUM, MINOR_NUM); return ret; } cdev_init(&com12v_cdev, &com12v_fops); com12v_cdev.owner = THIS_MODULE; ret = cdev_add(&com12v_cdev, dev_num, DEVICE_COUNT); if (ret < 0) { pr_err("com12v: Failed to add cdev\n"); goto err_unregister_region; } com12v_class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(com12v_class)) { pr_err("com12v: Failed to create class\n"); ret = PTR_ERR(com12v_class); goto err_cdev_del; } com12v_device = device_create(com12v_class, NULL, dev_num, NULL, DEVICE_NAME); if (IS_ERR(com12v_device)) { pr_err("com12v: Failed to create device\n"); ret = PTR_ERR(com12v_device); goto err_class_destroy; } pr_info("com12v: driver initialized successfully, device node /dev/%s\n", DEVICE_NAME); return 0; err_class_destroy: class_destroy(com12v_class); err_cdev_del: cdev_del(&com12v_cdev); err_unregister_region: unregister_chrdev_region(dev_num, DEVICE_COUNT); return ret; } void com12v_exit(void) { int i = 0; cancel_delayed_work_sync(&delay_work1); device_destroy(com12v_class, dev_num); class_destroy(com12v_class); cdev_del(&com12v_cdev); unregister_chrdev_region(dev_num, DEVICE_COUNT); for(i = 0; i < NUM_COMPORTS; i++) { iounmap(com12v_ctl_vir_addr[i]); } pr_info("com12v: driver unloaded\n"); }