h // =========================================================================== // cyio.c // Copyright (C) 2008-2010 Bookeen - All rights reserved // =========================================================================== #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CYEV_MAX_EVENT 20 #define CYEV_REPEAT_DELAY (HZ/2); /* ~500ms */ #define CYEV_CURRENT_VERSION 0x10 /* 1.0 */ enum { INVALID = 0, VALID }; struct { char valid; char wantRepeat; char seen; char unique; int repeatDelay; CyEvent_t payload; } CyEv_PayList; CyEv_PayList *cyev_eventList[CYEV_MAX_EVENT]; struct task_struct *openingTask = 0; /* TODO: Add a locking method for accessing the eventList */ // =========================================================================== // List management void CyEv_ClearEventList(void) { int i; for (i = 0; i < CYEV_MAX_EVENT; i++) { cyev_eventList[i].wantRepeat = false; cyev_eventList[i].seen = false; cyev_eventList[i].unique = false; cyev_eventList[i].repeatDelay = 0; cyev_eventList[i].valid = INVALID; } } /* Get Next Event will invalidate the event if it is not marked as repeat */ /* After the event is selected, it will point to the next one */ /* We first start by the first event of the list then search for the first valid and return it */ CyEvent_t *CyEv_GetNextEvent(void) { static int listPos = 0; int i; CyEvent_t ret = NULL; /* Scan the list starting from the last position stored */ for(i = listPos; i < (CYEV_MAX_EVENT + listPos); i++) { /* Stop on the first "VALID" event */ if (cyev_eventList[i % CYEV_MAX_EVENT].valid == VALID) { /* We already send this repeatable event. Did the delay occur ? if not, ignore it and go to the next one */ if ( (cyev_eventList[i % CYEV_MAX_EVENT].seen == true) && (cyev_eventList[i % CYEV_MAX_EVENT].wantRepeat == true) ) { if (cyev_eventList[i % CYEV_MAX_EVENT].repeatDelay < jiffies) ) { continue; } else /* if (cyev_eventList[i % CYEV_MAX_EVENT].repeatDelay >= jiffies) ) */ { /* Set the Repeat flag to true */ cyev_eventList[i % CYEV_MAX_EVENT].payload.flags |= CYEVENT_FLAG_REPEATEVENT; } } cyev_eventList[i % CYEV_MAX_EVENT].seen = true; /* Return the event */ ret = &(cyev_eventList[i % CYEV_MAX_EVENT].payload); /* If the event is not a repeatable event, invalidate it! */ if (cyev_eventList[i % CYEV_MAX_EVENT].wantRepeat == false) { cyev_eventList[i % CYEV_MAX_EVENT].valid = INVALID; } /* Update the index for the next element to scan */ listPos = (listPos + i + 1) % CYEV_MAX_EVENT; break; } } return ret; /* We will return NULL if no valid payload */ } int isEventNotDuplicate(CyEvent_t *CyEvent) { int i; CyEvent_t *cur: for(i = 0; i < CYEV_MAX_EVENT; i++) { cur = &(cyev_eventList[i].payload); if (cyev_eventList[i].unique == true) { if (cur->type == CyEvent->type) /* To be verified */ return false; } } return true; } int CyEv_AddNewEvent(CyEvent_t *CyEvent, char wantRepeat, char wantUnique) { /* Search for the first "invalid" event, then put the given CyEvent in it. */ /* If no slot is valid, return an error */ int i; int ret = -EIO; /* TODO: find a better error code.*/ /* If wantUnique, first check that there is not another unique event */ if ((wantUnique) && !isEventNotDuplicate(CyEvent)) goto exit; /* Scan the list starting from the last position stored */ for(i = 0; i < CYEV_MAX_EVENT; i++) { /* Stop on the first "VALID" event */ if (cyev_eventList[i].valid == INVALID) { /* Store the event */ memcpy(&(cyev_eventList[i].payload), CyEvent, sizeof(CyEvent_t)); /* Force version in the structure */ cyev_eventList[i].payload.version = CYEV_CURRENT_VERSION; cyev_eventList[i].valid = VALID; if (wantUnique) cyev_eventList[i].unique = true; else cyev_eventList[i].unique = false; if (wantRepeat) { cyev_eventList[i].wantRepeat = true; cyev_eventList[i].repeatDelay = jiffies + CYEV_REPEAT_DELAY; } else cyev_eventList[i].wantRepeat = false; if (openingTask) wake_up_process(openingTask); ret = 0; break; } } exit: return ret; } /* Return 0 if event found and updated, or anything else */ /* We could ONLY update event that are declared as unique */ int CyEv_UpdateUniqueEvent(CyEvent_t *CyEvent, char wantRepeat) { int i; int ret = -1; /* Search for the event */ for(i = 0; i < CYEV_MAX_EVENT; i++) { if ( (cyev_eventList[i].unique == true) && (cyev_eventList[i].payload.type == CyEvent.type) ) { /* Found it ! Now update the fields */ memcpy(&(cyev_eventList[i].payload), CyEvent, sizeof(CyEvent_t)); if (wantRepeat) { cyev_eventList[i].wantRepeat = true; cyev_eventList[i].repeatDelay = jiffies + CYEV_REPEAT_DELAY; } else cyev_eventList[i].wantRepeat = false; if (openingTask) wake_up_process(openingTask); ret = 0; } } return ret; } int CyEv_InvalidateRepeatableEvent(CyEvent_t *CyEvent) { int i; int ret = -1; /* Search for the event */ for(i = 0; i < CYEV_MAX_EVENT; i++) { if ( (cyev_eventList[i].wantRepeat == true) && (cyev_eventList[i].payload.type == CyEvent.type) ) { /* Found it ! Now update the fields */ /* In case the event has not been eaten, just remove the "repeat want" ie: do not invalidate it */ cyev_eventList[i].wantRepeat = false; if (cyev_eventList[i].seen == true) { /* set the event "end of repeat" flag */ cyev_eventList[i].payload.flags |= CYEVENT_FLAG_ENDOFREPEAT; } if (openingTask) wake_up_process(openingTask); ret = 0; break; } } return ret; } // =========================================================================== // =========================================================================== // External event managment int CyEvent_PushNewEvent(CyEvent_t *CyEvent, char wantRepeat) { if (wantRepeat) return CyEv_AddNewEvent(CyEvent, wantRepeat, true); else return CyEv_AddNewEvent(CyEvent, wantRepeat, false); } EXPORT_SYMBOL(CyEvent_PushNewEvent); int CyEvent_PushOrUpdateUniqueEvent(CyEvent_t *CyEvent, char wantRepeat) { int ret; /* For now a simple call to AddNewEvent */ ret = CyEv_UpdateUniqueEvent(CyEvent, wantRepeat); if (ret != 0) /* The event is not present */ ret = CyEv_AddNewEvent(CyEvent, wantRepeat, true); return ret; } EXPORT_SYMBOL(CyEvent_PushOrUpdateUniqueEvent); int CyEvent_InvalidateRepeatableEvent(CyEvent_t *CyEvent) { ret = CyEv_InvalidateRepeatableEvent(CyEvent); } EXPORT_SYMBOL(CyEvent_InvalidateRepeatableEvent); // =========================================================================== static int ev_open(struct inode *inode, struct file *file) { /* Clear pending event list... */ CyEv_ClearEventList(); return 0; } // --------------------------------------------------------------------------- static int ev_release(struct inode *inode, struct file *file) { /* Clear pending event list... */ CyEv_ClearEventList(); return 0; } // --------------------------------------------------------------------------- ssize_t ev_read(struct file *file, char *buf, size_t count, loff_t *ppos) { int nBytes = sizeof(CyEvent_t); int ret = -EIO; CyEvent_t *CyEvent = NULL; if (count < nBytes) { ret = -EINVAL; goto exit; } while (1) { if ((CyEvent = CyEv_GetNextEvent()) != NULL) { break; } openingTask = current; set_current_state(TASK_INTERRUPTIBLE); schedule(); set_current_state(TASK_RUNNING); openingTask = 0; if (signal_pending(current)) { ret = -ERESTARTSYS; break; } } if (CyEvent != NULL) { nRes = copy_to_user(buf, &CyEvent, nBytes); if (!nRes) nRes = nBytes; } return ret; } // --------------------------------------------------------------------------- static int ev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int ret = -EINVAL; unsigned long value; switch(cmd) { default: printk(KERN_ERR "Bad IOCTL\n"); break; case CYIO_CTL_LED_CMD: DBG("CYIO_CTL_LED_CMD arg[0x%03X]", arg); if ((arg & 0x2) && (arg & 0x1)) /* Power LED */ { MSG("\n----------------> POWER LED ON\n"); //GPH12 __raw_writel(__raw_readl(S3C2410_GPHDAT) | (1 << 12), S3C2410_GPHDAT); } else if (arg & 0x2) { MSG("\n----------------> POWER LED OFF\n"); __raw_writel(__raw_readl(S3C2410_GPHDAT) & ~(1 << 12), S3C2410_GPHDAT); } if ((arg & 0x20) && (arg & 0x10)) /* Wifi LED */ { MSG("\n----------------> POWER WFI ON\n"); //GPK8 __raw_writel(__raw_readl(S3C2416_GPKDAT) | (1 << 8), S3C2416_GPKDAT); } else if (arg & 0x20) { MSG("\n----------------> POWER WFI OFF\n"); __raw_writel(__raw_readl(S3C2416_GPKDAT) & ~(1 << 8), S3C2416_GPKDAT); } if ((arg & 0x200) && (arg & 0x100)) /* Bluetooth LED */ { MSG("\n----------------> POWER BTH ON\n"); //GPK9 __raw_writel(__raw_readl(S3C2416_GPKDAT) | (1 << 9), S3C2416_GPKDAT); } else if (arg & 0x200) { MSG("\n----------------> POWER BTH OFF\n"); __raw_writel(__raw_readl(S3C2416_GPKDAT) & ~(1 << 9), S3C2416_GPKDAT); } ret = 0; break; case CYIO_CTL_USB_STATUS: value = gpio_get_value(S3C2410_GPG1); put_user(value, (unsigned long __user *)arg); ret = 0; break; case CYIO_CTL_SD_STATUS: value = gpio_get_value(S3C2410_GPF1); put_user(value, (unsigned long __user *)arg); ret = 0; break; } return ret; } // =========================================================================== static struct file_operations s_io_fops = { owner: THIS_MODULE, read: ev_read, ioctl: ev_ioctl, open: ev_open, release: ev_release, }; gstatic struct miscdevice s_ev_dev = { .minor = 250, .name = "cyio", .fops = &s_ev_fops, }; // =========================================================================== // --------------------------------------------------------------------------- static int io_probe(struct platform_device *dev) { return 0; } // --------------------------------------------------------------------------- static int io_remove(struct platform_device *dev) { misc_deregister(&s_ev_dev); return 0; } // -------------------------------------------------------------------------- static int io_resume(struct platform_device *dev) { return 0; } // --------------------------------------------------------------------------- static struct platform_driver cyev_driver = { .driver = { .name = "cyevent", .owner = THIS_MODULE, }, .probe = ev_probe, .remove = ev_remove, .suspend = NULL, .resume = ev_resume, }; // --------------------------------------------------------------------------- // =========================================================================== static int __init cyEv_init(void) { if (misc_register(&s_ev_dev)) return -EBUSY; platform_driver_register(&cyev_driver); return 0; } // --------------------------------------------------------------------------- static void __exit cyEv_exit(void) { platform_driver_unregister(&cyev_driver); misc_deregister(&s_ev_dev); } // --------------------------------------------------------------------------- module_init(cyEv_init); module_exit(cyEv_exit); // --------------------------------------------------------------------------- MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bookeen "); MODULE_DESCRIPTION("Cybook Event Manager"); MODULE_VERSION("3.0"); // ===========================================================================