// =========================================================================== // Cybook Event Manager - cyevent.c // Copyright (C) 2008-2010 Bookeen - All rights reserved // =========================================================================== #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ============================================================================= //#define DEBUG_MESSAGES //#define DEBUG_TRACEFUNC //#define VERBOSE_LEVEL INFO_DEBUG #define MODULE_NAME "CyEvent" // ============================================================================= #define CYEV_MAX_EVENT 20 #define CYEV_MAX_DEV 5 #define CYEV_REPEAT_DELAY (HZ/2) /* ~500ms */ // ============================================================================= enum { INVALID = 0, VALID }; // ============================================================================= typedef struct sCyEv_PayList { char valid; char wantRepeat; char seen; char repeat; char unique; unsigned long repeatDelay; CyEvent_t payload; } CyEv_PayList; CyEv_PayList cyev_eventList[CYEV_MAX_EVENT]; // ============================================================================= /* We should change this to a linked list... */ struct cyevent_device *DevList[CYEV_MAX_DEV]; // ============================================================================= struct task_struct *openingTask = 0; static atomic_t waitingRepeat; // ============================================================================= #ifdef DEBUG_MESSAGES enum InfoLevel { INFO_ERROR = 0, INFO_WARNING, INFO_NORMAL, INFO_DEBUG, INFO_VERBOSE, }; # ifndef VERBOSE_LEVEL # define VERBOSE_LEVEL INFO_WARNING # endif # ifdef DEBUG_TRACEFUNC static int _dbg_FunctionLevel = 0; # define MSG(str) {\ int __i;\ printk(KERN_ALERT "+");\ for (__i = 0; __i < _dbg_FunctionLevel; __i++)\ printk("-");\ printk("||" str "\n");\ } # define DBG(str, ...) {\ int __i;\ printk(KERN_ALERT "+");\ for (__i = 0; __i < _dbg_FunctionLevel; __i++)\ printk("-");\ printk("||" str "\n", __VA_ARGS__);\ } # define INFOL(level, s) do {\ if (level <= VERBOSE_LEVEL) {\ int __i;\ printk(KERN_ALERT "+");\ for (__i = 0; __i < _dbg_FunctionLevel; __i++)\ printk("-");\ printk("<%d>%s:%s(): ", level, __FILE__, __func__); printk s; printk("\n");\ }\ } while(0) # define FUNC_IN() {\ int __i;\ _dbg_FunctionLevel++;\ printk(KERN_ALERT "+");\ for (__i = 0; __i < _dbg_FunctionLevel; __i++)\ printk("-");\ printk(">> %s() >>\n", __func__);\ } # define FUNC_OUT() {\ int __i;\ printk(KERN_ALERT "+");\ for (__i = 0; __i < _dbg_FunctionLevel; __i++)\ printk("-");\ printk("<< %s() <<\n", __func__);\ _dbg_FunctionLevel--;\ } # define FUNC_OUTR(val) {\ int __i;\ printk(KERN_ALERT "+");\ for (__i = 0; __i < _dbg_FunctionLevel; __i++)\ printk("-");\ printk("<< %s() = %d <<\n", __func__, val);\ _dbg_FunctionLevel--;\ } # else /* DEBUG_TRACEFUNC */ # define MSG(str) do {\ printk(KERN_ALERT MODULE_NAME ": " str "\n");\ } while(0) # define DBG(str, ...) do {\ printk(KERN_ALERT MODULE_NAME ": " str "\n", __VA_ARGS__);\ } while(0) # define FUNC_IN() do {\ } while(0) # define FUNC_OUT() do {\ } while(0) # define FUNC_OUTR(val) do {\ printk(KERN_ALERT MODULE_NAME ": %s() return %d\n", __func__, val);\ } while(0) # define INFOL(level, s) do {\ if (level <= VERBOSE_LEVEL) {\ printk("<%d>%s:%s(): ", level, __FILE__, __func__); printk s; printk("\n");\ }\ } while(0) # endif /* DEBUG_TRACEFUNC */ #else /* DEBUG_MESSAGES */ # define MSG(str) # define DBG(str, ...) # define FUNC_IN() # define FUNC_OUT() # define FUNC_OUTR(val) # define INFOL(level, s) # define INFO(s) #endif /* DEBUG_MESSAGES */ #define DUMP_CYEVENT(lvl, ev) INFOL(lvl, ("Event: .type=%c, .flags=%02X, .version=%02X\n" \ ".data = { 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X }",\ ev.type, ev.flags, ev.version, ev.data.raw[0], ev.data.raw[1], ev.data.raw[2], ev.data.raw[3], ev.data.raw[4], ev.data.raw[5],\ ev.data.raw[6], ev.data.raw[7], ev.data.raw[8], ev.data.raw[9], ev.data.raw[10], ev.data.raw[11], ev.data.raw[12])) #define DUMP_EVITEM(lvl, evi) INFOL(lvl, ("EvItem: .valid = %d, .wantRepeat = %d, .seen = %d, .repeat = %d, .unique = %d, .repeatDelay = %lu .payload:",\ evi.valid, evi.wantRepeat, evi.seen, evi.repeat, evi.unique, evi.repeatDelay)); DUMP_CYEVENT(lvl, evi.payload); // ============================================================================= rwlock_t cyev_list_rwlock = RW_LOCK_UNLOCKED; // ============================================================================= static void publish_read_event(unsigned char type); static void publish_empty_event(void); // ============================================================================= // List management void CyEv_ClearEventList(void) { int i; FUNC_IN(); atomic_set(&waitingRepeat, 0); write_lock(&cyev_list_rwlock); for (i = 0; i < CYEV_MAX_EVENT; i++) { cyev_eventList[i].wantRepeat = false; cyev_eventList[i].repeat = false; cyev_eventList[i].seen = false; cyev_eventList[i].unique = false; cyev_eventList[i].repeatDelay = 0; cyev_eventList[i].valid = INVALID; } write_unlock(&cyev_list_rwlock); FUNC_OUT(); } int listPos = 0; /* 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(CyEvent_t *ret) { int i; char foundPayload = 0; CyEv_PayList *cur; FUNC_IN(); write_lock(&cyev_list_rwlock); /* Scan the list starting from the last position stored */ for(i = listPos; i < (CYEV_MAX_EVENT + listPos); i++) { /* Stop on the first "VALID" event */ cur = &(cyev_eventList[i % CYEV_MAX_EVENT]); //INFOL(INFO_VERBOSE, ("Position %d / %d", i % CYEV_MAX_EVENT, CYEV_MAX_EVENT)); if (cur->valid == VALID) { DUMP_EVITEM(INFO_DEBUG, (*cur)); /* We already send this repeatable event. Did the delay occur ? if not, ignore it and go to the next one */ if ( (cur->seen == true) && (cur->wantRepeat == true) ) { if (cur->repeatDelay > jiffies) { INFOL(INFO_DEBUG, ("Event delay not occured [%lu vs %lu (%lu)]...", cur->repeatDelay, jiffies, cur->repeatDelay - jiffies)); continue; } else /* if (cur->repeatDelay >= jiffies) */ { /* Set the Repeat flag to true */ INFOL(INFO_DEBUG, ("Event repeating... (%d)", atomic_read(&waitingRepeat))); if (cur->repeat == false) { atomic_dec(&waitingRepeat); /* Ack the repeat event */ } INFOL(INFO_DEBUG, ("waiting (%d) (repeat:%d)", atomic_read(&waitingRepeat), cur->repeat)); cur->payload.flags |= CYEVENT_FLAG_REPEATEVENT; cur->repeat = true; } } cur->seen = true; /* Return the event and do not give real access to the list */ memcpy(ret, &(cur->payload), sizeof(CyEvent_t)); foundPayload = 1; /* If the event is not a repeatable event, invalidate it! */ if (cur->wantRepeat == false) { INFOL(INFO_VERBOSE, ("Non-repeat event, will invalidate it")); cur->valid = INVALID; } /* Update the index for the next element to scan */ listPos = (i + 1) % CYEV_MAX_EVENT; INFOL(INFO_DEBUG, ("Next event slot: %d", listPos)); break; } } write_unlock(&cyev_list_rwlock); FUNC_OUT(); if (foundPayload == 1) return ret; /* We will return NULL if no valid payload */ return NULL; } int isEventNotDuplicate(CyEvent_t *CyEvent, unsigned char flagMask) { int i; int ret = true; CyEvent_t *cur; FUNC_IN(); read_lock(&cyev_list_rwlock); for(i = 0; i < CYEV_MAX_EVENT; i++) { cur = &(cyev_eventList[i].payload); /* The event must be valid to be treated as unique, else invalid event are ignored */ if ( (cyev_eventList[i].valid == VALID) && (cyev_eventList[i].unique == true) ) { if ( (cur->type == CyEvent->type) && ((cyev_eventList[i].payload.flags & flagMask) == (CyEvent->flags & flagMask)) ) { ret = false; goto exit; } } } exit: read_unlock(&cyev_list_rwlock); FUNC_OUTR(ret); return ret; } int CyEv_AddNewEvent(CyEvent_t *CyEvent, char wantRepeat, char wantUnique, unsigned char flagMask) { /* 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.*/ FUNC_IN(); /* If wantUnique, first check that there is not another unique event */ if ((wantUnique) && !isEventNotDuplicate(CyEvent, flagMask)) goto exit; write_lock(&cyev_list_rwlock); /* Scan the list starting from the last position stored */ for(i = listPos; i < (CYEV_MAX_EVENT + listPos); i++) { /* Stop on the first "INVALID" 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; cyev_eventList[i].seen = false; cyev_eventList[i].repeat = false; if (wantUnique) cyev_eventList[i].unique = true; else cyev_eventList[i].unique = false; if (wantRepeat) { atomic_inc(&waitingRepeat); 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; } } write_unlock(&cyev_list_rwlock); exit: FUNC_OUTR(ret); 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, unsigned char flagMask) { int i; int ret = -1; FUNC_IN(); /* Search for the event */ write_lock(&cyev_list_rwlock); for(i = 0; i < CYEV_MAX_EVENT; i++) { if ( (cyev_eventList[i].valid == VALID) && (cyev_eventList[i].unique == true) && ((cyev_eventList[i].payload.flags & flagMask) == (CyEvent->flags & flagMask)) && (cyev_eventList[i].payload.type == CyEvent->type) ) { /* Found it ! Now update the fields */ memcpy(&(cyev_eventList[i].payload), CyEvent, sizeof(CyEvent_t)); cyev_eventList[i].seen = false; if (wantRepeat) { cyev_eventList[i].wantRepeat = true; cyev_eventList[i].repeat = false; cyev_eventList[i].repeatDelay = jiffies + CYEV_REPEAT_DELAY; if (cyev_eventList[i].repeat == true) atomic_inc(&waitingRepeat); } else cyev_eventList[i].wantRepeat = false; if (openingTask) wake_up_process(openingTask); ret = 0; } } write_unlock(&cyev_list_rwlock); FUNC_OUTR(ret); return ret; } int CyEv_InvalidateUniqueEvent (CyEvent_t *CyEvent) { int i; int ret = -1; FUNC_IN(); /* Search for the event */ write_lock(&cyev_list_rwlock); for ( i = 0; i < CYEV_MAX_EVENT; i++ ) { if ( (cyev_eventList[i].valid == VALID) && (cyev_eventList[i].unique == true) && (cyev_eventList[i].payload.type == CyEvent->type) ) { /* Found it ! Now update the fields */ INFOL(INFO_DEBUG, ("Found event...")); cyev_eventList[i].valid = INVALID; ret = 0; break; } } write_unlock(&cyev_list_rwlock); FUNC_OUTR(ret); return ret; } int CyEv_InvalidateRepeatableEvent(CyEvent_t *CyEvent) { int i; int ret = -1; FUNC_IN(); /* Search for the event */ write_lock(&cyev_list_rwlock); for(i = 0; i < CYEV_MAX_EVENT; i++) { if ( (cyev_eventList[i].valid == VALID) && (cyev_eventList[i].wantRepeat == true) && (cyev_eventList[i].payload.type == CyEvent->type) ) { /* Found it ! Now update the fields */ INFOL(INFO_DEBUG, ("Found event...")); /* 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].repeat == true) { INFOL(INFO_DEBUG, ("Was repeating...")); /* set the event "end of repeat" flag */ cyev_eventList[i].payload.flags |= CYEVENT_FLAG_ENDOFREPEAT; cyev_eventList[i].payload.flags &= ~(CYEVENT_FLAG_REPEATEVENT); } else { INFOL(INFO_DEBUG, ("Repeat not done... (%d)", atomic_read(&waitingRepeat))); atomic_dec(&waitingRepeat); /* One less waiting event... */ INFOL(INFO_DEBUG, ("waiting (%d)", atomic_read(&waitingRepeat))); if (cyev_eventList[i].seen == true) cyev_eventList[i].valid = INVALID; } cyev_eventList[i].seen = false; cyev_eventList[i].repeat = false; if (openingTask) wake_up_process(openingTask); ret = 0; break; } } write_unlock(&cyev_list_rwlock); FUNC_OUTR(ret); return ret; } // =========================================================================== // =========================================================================== // External event managment int CyEvent_PushNewEvent(CyEvent_t *CyEvent, char wantRepeat) { int ret = -1; FUNC_IN(); if (wantRepeat) ret = CyEv_AddNewEvent(CyEvent, wantRepeat, true, 0); else ret = CyEv_AddNewEvent(CyEvent, wantRepeat, false, 0); FUNC_OUTR(ret); return ret; } EXPORT_SYMBOL(CyEvent_PushNewEvent); int CyEvent_PushNewUniqueEvent(CyEvent_t *CyEvent) { return CyEv_AddNewEvent(CyEvent, false, true, 0); } EXPORT_SYMBOL(CyEvent_PushNewUniqueEvent); int CyEvent_PushOrUpdateUniqueEventFlaged(CyEvent_t *CyEvent, char wantRepeat, unsigned char flagMask) { int ret; FUNC_IN(); /* For now a simple call to AddNewEvent */ ret = CyEv_UpdateUniqueEvent(CyEvent, wantRepeat, flagMask); if (ret != 0) /* The event is not present */ ret = CyEv_AddNewEvent(CyEvent, wantRepeat, true, flagMask); FUNC_OUTR(ret); return ret; } EXPORT_SYMBOL(CyEvent_PushOrUpdateUniqueEventFlaged); int CyEvent_PushOrUpdateUniqueEvent(CyEvent_t *CyEvent, char wantRepeat) { int ret; FUNC_IN(); /* For now a simple call to AddNewEvent */ ret = CyEv_UpdateUniqueEvent(CyEvent, wantRepeat, 0); if (ret != 0) /* The event is not present */ ret = CyEv_AddNewEvent(CyEvent, wantRepeat, true, 0); FUNC_OUTR(ret); return ret; } EXPORT_SYMBOL(CyEvent_PushOrUpdateUniqueEvent); int CyEvent_InvalidateUniqueEvent(CyEvent_t *CyEvent) { int ret; FUNC_IN(); ret = CyEv_InvalidateUniqueEvent(CyEvent); FUNC_OUTR(ret); return ret; } EXPORT_SYMBOL(CyEvent_InvalidateUniqueEvent); int CyEvent_InvalidateRepeatableEvent(CyEvent_t *CyEvent) { int ret; FUNC_IN(); ret = CyEv_InvalidateRepeatableEvent(CyEvent); FUNC_OUTR(ret); return ret; } EXPORT_SYMBOL(CyEvent_InvalidateRepeatableEvent); // =========================================================================== static int ev_open(struct inode *inode, struct file *file) { FUNC_IN(); /* Clear pending event list... */ CyEv_ClearEventList(); FUNC_OUT(); return 0; } // --------------------------------------------------------------------------- static int ev_release(struct inode *inode, struct file *file) { FUNC_IN(); /* Clear pending event list... */ CyEv_ClearEventList(); FUNC_OUT(); 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; int waitCount; CyEvent_t CyEvent; //FUNC_IN(); if ( count < nBytes ) { ret = -EINVAL; goto exit; } while ( 1 ) { //printk(KERN_ERR "CyEvent: Check new event...\n"); if ( CyEv_GetNextEvent(&CyEvent) != NULL ) { break; } waitCount = atomic_read(&waitingRepeat); if (waitCount == 0) /* If there are some waiting event, the list is not empty */ publish_empty_event(); openingTask = current; set_current_state(TASK_INTERRUPTIBLE); /* If there are some waiting repeat, schedule with a timeout, or else, just go out of the schedule queue */ INFOL(INFO_DEBUG, ("Waiting repeat: %d", waitCount)); if ( waitCount > 0 ) schedule_timeout(CYEV_REPEAT_DELAY / 10); else schedule(); set_current_state(TASK_RUNNING); openingTask = 0; if ( signal_pending(current) ) { ret = -ERESTARTSYS; break; } } if ( ret != -ERESTARTSYS ) { ret = copy_to_user(buf, &CyEvent, nBytes); if ( !ret ) ret = nBytes; publish_read_event(CyEvent.type); } exit: //FUNC_OUTR(ret); return ret; } // --------------------------------------------------------------------------- static int ev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int ret = -EINVAL; unsigned long value; int i; FUNC_IN(); switch(cmd) { default: for (i = 0; i < CYEV_MAX_DEV; i++ ) { if ((DevList[i] != NULL) && (DevList[i]->ioctl != NULL)) { if (GET_IOCTL_PREFIX(cmd) == DevList[i]->ioctl_prefix) { ret = DevList[i]->ioctl(cmd, arg); /* if the ioctl called didn't return 0, try to see if there is someone else for this one */ if (ret == 0) break; } } } printk(KERN_ERR "Bad IOCTL\n"); break; } FUNC_OUT(); return ret; } // ============================================================================= int CyEvent_RegisterDevice(cyevent_device *dev) { int i; for (i = 0; i < CYEV_MAX_DEV; i++ ) { if (DevList[i] == NULL) { DevList[i] = dev; return 0; } } return -1; /* Too many devices... */ } // ----------------------------------------------------------------------------- int CyEvent_DeregisterDevice(cyevent_device *dev) { int i; for (i = 0; i < CYEV_MAX_DEV; i++ ) { if (DevList[i] == dev) { DevList[i] = NULL; return 0; } } return -1; /* Not found...*/ } // ----------------------------------------------------------------------------- static void publish_read_event(unsigned char type) { int i; for (i = 0; i < CYEV_MAX_DEV; i++ ) { if ((DevList[i] != NULL) && (DevList[i]->event_read != NULL)) { if ((DevList[i]->event_read_listen == 0) || (type == DevList[i]->event_read_listen)) DevList[i]->event_read(type); } } } // ----------------------------------------------------------------------------- static void publish_empty_event(void) { int i; for (i = 0; i < CYEV_MAX_DEV; i++ ) { if ((DevList[i] != NULL) && (DevList[i]->event_listempty != NULL)) { DevList[i]->event_listempty(); } } } // ============================================================================= static struct file_operations s_ev_fops = { owner: THIS_MODULE, read: ev_read, ioctl: ev_ioctl, open: ev_open, release: ev_release, }; static struct miscdevice s_ev_dev = { .minor = 250, .name = "cyio", .fops = &s_ev_fops, }; // =========================================================================== // --------------------------------------------------------------------------- static int ev_probe(struct platform_device *dev) { FUNC_IN(); FUNC_OUT(); return 0; } // --------------------------------------------------------------------------- static int ev_remove(struct platform_device *dev) { FUNC_IN(); misc_deregister(&s_ev_dev); FUNC_OUT(); return 0; } // -------------------------------------------------------------------------- static int ev_suspend(struct platform_device *dev) { FUNC_IN(); FUNC_OUT(); return 0; } // -------------------------------------------------------------------------- static int ev_resume(struct platform_device *dev) { FUNC_IN(); FUNC_OUT(); return 0; } // --------------------------------------------------------------------------- static struct platform_driver cyev_driver = { .driver = { .name = "cyevent", .owner = THIS_MODULE, }, .probe = ev_probe, .remove = ev_remove, .suspend = NULL, //ev_suspend, .resume = ev_resume, }; // --------------------------------------------------------------------------- // =========================================================================== static int __init cyEv_init(void) { FUNC_IN(); if (misc_register(&s_ev_dev)) return -EBUSY; platform_driver_register(&cyev_driver); FUNC_OUT(); return 0; } // --------------------------------------------------------------------------- static void __exit cyEv_exit(void) { FUNC_IN(); platform_driver_unregister(&cyev_driver); misc_deregister(&s_ev_dev); FUNC_OUT(); } // --------------------------------------------------------------------------- module_init(cyEv_init); module_exit(cyEv_exit); // --------------------------------------------------------------------------- MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bookeen "); MODULE_DESCRIPTION("Cybook Event Manager"); MODULE_VERSION("3.0"); // ===========================================================================