/*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AVM_EVENT_INTERNAL #include #include /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ extern spinlock_t avm_event_lock; static struct _avm_event_open_data *first, *last; static struct _avm_event_source *avm_event_source[MAX_AVM_EVENT_SOURCES]; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void avm_event_source_notify(enum _avm_event_id id); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int atomic_test_and_set(unsigned int *ptr, unsigned int value) { int ret = 0; unsigned long flags; spin_lock_irqsave(&avm_event_lock, flags); if(*ptr == 0) { *ptr = value; ret = 1; } spin_unlock_irqrestore(&avm_event_lock, flags); return ret; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void atomic_set_bits_long_long(unsigned long long *Dest, unsigned long long or_mask) { unsigned long flags; spin_lock_irqsave(&avm_event_lock, flags); *Dest |= or_mask; spin_unlock_irqrestore(&avm_event_lock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void atomic_clear_bits_long_long(unsigned long long *Dest, unsigned long long or_mask) { unsigned long flags; spin_lock_irqsave(&avm_event_lock, flags); *Dest &= ~or_mask; spin_unlock_irqrestore(&avm_event_lock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void atomic_add_to_front_of_queue(struct _avm_event_open_data *open_data) { unsigned long flags; open_data->next = open_data->prev = NULL; spin_lock_irqsave(&avm_event_lock, flags); if(last == NULL) { /*--- aller erster Eintrag ---*/ last = open_data; } else { first->prev = open_data; open_data->next = first; } first = open_data; spin_unlock_irqrestore(&avm_event_lock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void atomic_rm_from_queue(struct _avm_event_open_data *open_data) { unsigned long flags; spin_lock_irqsave(&avm_event_lock, flags); if(open_data->prev) { open_data->prev->next = open_data->next; } else { /*--- (first == open_data) wenn es keinen Vorgaenger gibt, muss dieser Eintrag der erste sein ---*/ first = open_data->next; } if(open_data->next) { open_data->next->prev = open_data->prev; } else { /*--- (last == open_data) wenn es keine Nachfolger, muss dieser Eintrag der letzte sein ---*/ last = open_data->prev; } spin_unlock_irqrestore(&avm_event_lock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_event_register(struct _avm_event_open_data *open_data, struct _avm_event_cmd_param_register *data) { int i; DEB_INFO("[avm_event_register]: Name=%s Mask=%LX\n", data->Name, data->mask); atomic_add_to_front_of_queue(open_data); DEB_INFO("[avm_event_register]: open_data=0x%p first=0x%p last=0x%p\n", open_data, first, last); strcpy(open_data->Name, data->Name); open_data->event_mask_registered = data->mask; for(i = 0 ; i < avm_event_last ; i++) { if(open_data->event_mask_registered & ((unsigned long long)1 << i)) { avm_event_source_notify((enum _avm_event_id)i); } } DEB_INFO("[avm_event_register]: success\n"); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_event_release(struct _avm_event_open_data *open_data, struct _avm_event_cmd_param_release *data) { unsigned int i; DEB_INFO("[avm_event_release]: first=0x%p last=0x%p\n", first, last); atomic_rm_from_queue(open_data); for(i = 0 ; i < sizeof(unsigned long long) * 8 ; i++) { if(open_data->event_mask_registered & ((unsigned long long)1 << i)) { while(open_data->item) { avm_event_commit(open_data, i); } } } DEB_INFO("[avm_event_release]: (first=%x last=%x) success\n", (unsigned int)first, (unsigned int)last); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_event_trigger(struct _avm_event_open_data *open_data, struct _avm_event_cmd_param_trigger *data) { DEB_INFO("[avm_event_trigger]: Id %u\n", data->id); if(open_data->event_mask_registered & ((unsigned long long)1 << data->id)) { avm_event_source_notify((enum _avm_event_id)data->id); } else { DEB_WARN("[avm_event_trigger]: Id %u event mask not registered\n", data->id); } return 0; } /*------------------------------------------------------------------------------------------*\ * Den ersten Eintrag für den man benachrichtigt wurde holen \*------------------------------------------------------------------------------------------*/ int avm_event_get(struct _avm_event_open_data *open_data, unsigned char **rx_buffer, unsigned int *rx_buffer_length, unsigned int *event_pos) { DEB_INFO("[avm_event_get]: \n"); if(open_data->item) { *rx_buffer = open_data->item->data->data; *rx_buffer_length = open_data->item->data->data_length; DEB_INFO("[avm_event_get]: success\n"); return 0; } DEB_INFO("[avm_event_get]: empty\n"); return 0; } /*------------------------------------------------------------------------------------------*\ * Ruecksetzen der notified Maske, herunterzaehlen des link_count und ggf. freigaben * der event_data \*------------------------------------------------------------------------------------------*/ void avm_event_commit(struct _avm_event_open_data *open_data, unsigned int event_pos) { unsigned long flags; struct _avm_event_item *I = open_data->item; DEB_INFO("[avm_event_commit]: Name=%s\n", open_data->Name); if(I) { /*--- wenn ein event item vorhanden ---*/ /*----------------------------------------------------------------------------------*\ * geschuetztes manipulieren der Liste (erstes Element endfernen) \*----------------------------------------------------------------------------------*/ spin_lock_irqsave(&avm_event_lock, flags); open_data->item = I->next; /*--- das naechste Element ---*/ spin_unlock_irqrestore(&avm_event_lock, flags); /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ /*--- avm_event_free_data(I->data); ---*/ avm_event_free_item(I); } return; } /*------------------------------------------------------------------------------------------*\ * wird NUR im Kontext der sich registrierenden Applikation aufgerufen, somit ist die * 'avm_event_sema' gesetzt \*------------------------------------------------------------------------------------------*/ static void avm_event_source_notify(enum _avm_event_id id) { int i; for( i = 0 ; i < MAX_AVM_EVENT_SOURCES ; i++) { if(avm_event_source[i] == NULL) continue; if(avm_event_source[i]->notify && (avm_event_source[i]->event_mask & ((unsigned long long)1 << id))) { (*avm_event_source[i]->notify)(avm_event_source[i]->Context, id); break; /*--- eine id kann nur von einem geliefert werden ---*/ } } } /*------------------------------------------------------------------------------------------*\ * wird von beiden Kontexten aufgerufen \*------------------------------------------------------------------------------------------*/ void *avm_event_source_register(char *name, unsigned long long id_mask, void (*notify)(void *, enum _avm_event_id), void *Context) { unsigned int i; struct _avm_event_source *S; DEB_INFO("[avm_event_source_register]: name=%s mask=%LX\n", name, id_mask); if(id_mask & avm_event_source_mask) { DEB_ERR("[avm_event_source_register]: overlapping event_mask current=%LX new=%LX\n", id_mask, avm_event_source_mask); return NULL; } S = vmalloc(sizeof(struct _avm_event_source)); if(S == NULL) { DEB_ERR("[avm_event_source_register]: out of memory \n"); return NULL; } strcpy(S->Name, name); S->signatur = AVM_EVENT_SIGNATUR; S->notify = notify; S->Context = Context; S->event_mask = id_mask; for( i = 0 ; i < MAX_AVM_EVENT_SOURCES ; i++) { if(atomic_test_and_set((unsigned int *)&avm_event_source[i], (unsigned int)S)) { break; } } if(i == MAX_AVM_EVENT_SOURCES) { vfree(S); DEB_ERR("[avm_event_source_register]: out of resources\n"); return NULL; } atomic_set_bits_long_long(&avm_event_source_mask, id_mask); DEB_INFO("[avm_event_source_register]: handle=0x%X success\n", (unsigned int)S); return (void *)S; } /*------------------------------------------------------------------------------------------*\ * wird von beiden Kontexten aufgerufen \*------------------------------------------------------------------------------------------*/ void avm_event_source_release(void *handle) { struct _avm_event_source *S = (struct _avm_event_source *)handle; unsigned int i; unsigned long flags; DEB_INFO("[avm_event_source_release]: handle=0x%X\n", (unsigned int)handle); if(handle == NULL) { DEB_ERR("[avm_event_source_release]: invalid handle NULL\n"); return; } if(S->signatur != AVM_EVENT_SIGNATUR) { DEB_ERR("[avm_event_source_release]: missing signatur\n"); return; } atomic_clear_bits_long_long(&avm_event_source_mask, S->event_mask); spin_lock_irqsave(&avm_event_lock, flags); for( i = 0 ; i < MAX_AVM_EVENT_SOURCES ; i++) { if(S == avm_event_source[i]) { avm_event_source[i] = NULL; vfree(S); break; } } spin_unlock_irqrestore(&avm_event_lock, flags); DEB_INFO("[avm_event_source_release]: success\n"); return; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_event_source_check_id(void *handle, enum _avm_event_id id) { struct _avm_event_open_data *O = first; DEB_INFO("[avm_event_source_check_id]: handle=0x%x id=%u\n", (unsigned int)handle, id); if(handle == NULL) { DEB_ERR("[avm_event_source_trigger]: not registered\n"); return 0; } while(O) { /*--- alle geoeffneten handles durchgehen ---*/ /*--- jeder der auf diese id ein bit in seiner Maske gesetzt hat, wird mit informationen versorgt und anschliessend geweckt ---*/ if(O->event_mask_registered & ((unsigned long long)1 << id)) { return 1; } O = O->next; } return 0; } /*------------------------------------------------------------------------------------------*\ * wird von beiden Kontexten aufgerufen \*------------------------------------------------------------------------------------------*/ int avm_event_source_trigger(void *handle, enum _avm_event_id id, unsigned int data_length, void *data) { struct _avm_event_data *D; struct _avm_event_open_data *O = first; struct _avm_event_header *H; int status; DEB_INFO("[avm_event_source_trigger]: handle=0x%x id=%u data=0x%p len=%u\n", (unsigned int)handle, id, data, data_length); H = (struct _avm_event_header *)data; if(handle == NULL) { DEB_ERR("[avm_event_source_trigger]: not registered\n"); return 0; } if(!H || H->id != id) { DEB_ERR("[avm_event_source_trigger]: avm_event_header incorrect !\n"); return 0; } D = avm_event_alloc_data(); if(D == NULL) { DEB_ERR("[avm_event_source_trigger]: out of memory\n"); return 0; } D->link_count = 1; D->data = data; D->data_length = data_length; status = 0; while(O) { /*--- alle geoeffneten handles durchgehen ---*/ /*--- jeder der auf diese id ein bit in seiner Maske gesetzt hat, wird mit informationen versorgt und anschliessend geweckt ---*/ if(O->event_mask_registered & ((unsigned long long)1 << id)) { if(avm_event_source_trigger_one(O, D)) break; status++; } O = O->next; } avm_event_free_data(D); DEB_INFO("[avm_event_source_trigger]: success (%u instances notified)\n", D ? D->link_count : 0); return status; /*--- anzahl der benachrichtigten empfaenger ---*/ } /*------------------------------------------------------------------------------------------*\ * wird von beiden Kontexten aufgerufen \*------------------------------------------------------------------------------------------*/ int avm_event_source_trigger_one(struct _avm_event_open_data *O, struct _avm_event_data *D) { unsigned long flags; struct _avm_event_item *I; I = avm_event_alloc_item(); if(I == NULL) { DEB_ERR("[avm_event_source_trigger_one]: out of memory\n"); return 1; } I->data = D; I->next = NULL; /*--------------------------------------------------------------------------------------*\ * geschuetztes verketten (einhaengen) \*--------------------------------------------------------------------------------------*/ spin_lock_irqsave(&avm_event_lock, flags); D->link_count++; if(O->item == NULL) { /*--- erster ---*/ O->item = I; } else { /*--- ende finden und anhängen ---*/ struct _avm_event_item *i = O->item; while(i->next) /*--- bis zum Ende die schlange abarbeiten ---*/ i = i->next; i->next = I; } spin_unlock_irqrestore(&avm_event_lock, flags); /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ DEB_INFO("[avm_event_source_trigger_one]: wake up %s\n", O->Name); wake_up(&(O->wait_queue)); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ EXPORT_SYMBOL(avm_event_source_check_id); EXPORT_SYMBOL(avm_event_source_trigger); EXPORT_SYMBOL(avm_event_source_release); EXPORT_SYMBOL(avm_event_source_register);