Modules/UDI - Implementing proper enumeration framework
[tpg/acess2.git] / KernelLand / Modules / Interfaces / UDI / channels.c
1 /*
2  * Acess2 UDI Layer
3  * - By John Hodge (thePowersGang)
4  *
5  * channels.c
6  * - Channel code
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <udi.h>
11 #include "udi_internal.h"
12 /*
13  * LOCK_CHANNELS - Prevents 
14  */
15 #define LOCK_CHANNELS   1
16
17 struct sUDI_ChannelSide {
18         struct sUDI_Channel     *BackPtr;
19         tUDI_DriverInstance     *Instance;
20         udi_index_t     MetaOpsNum;
21         const void      *Ops;
22         void    *Context;
23 };
24
25 typedef struct sUDI_Channel
26 {
27         tUDI_MetaLang   *MetaLang;
28         bool    Locked;
29         struct sUDI_ChannelSide  Side[2];
30 } tUDI_Channel;
31
32 // === CODE ===
33 udi_channel_t UDI_CreateChannel_Blank(tUDI_MetaLang *metalang)
34 {
35         tUDI_Channel    *ret = NEW(tUDI_Channel,);
36
37         ret->MetaLang = metalang;
38         ret->Side[0].BackPtr = ret;     
39         ret->Side[1].BackPtr = ret;     
40
41         return (udi_channel_t)&ret->Side[0].BackPtr;
42 }
43
44 struct sUDI_ChannelSide *UDI_int_ChannelGetSide(udi_channel_t channel, bool other_side)
45 {
46         tUDI_Channel *ch = *(tUDI_Channel**)channel;
47         if(!ch) return NULL;
48
49         int side_idx = (channel == (udi_channel_t)&ch->Side[0].BackPtr) ? 0 : 1;
50         if( other_side )
51                 side_idx = 1 - side_idx;        
52
53         LOG("side_idx = %i, other_side=%b", side_idx, other_side);
54
55         return &ch->Side[side_idx];
56 }
57
58 int UDI_BindChannel_Raw(udi_channel_t channel, bool other_side, tUDI_DriverInstance *inst, udi_index_t meta_ops_num,  void *context, const void *ops)
59 {
60         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, other_side);
61         side->Instance = inst;
62         side->Context = context;
63         side->MetaOpsNum = meta_ops_num;
64         side->Ops = ops;
65         return 0;
66 }
67
68 int UDI_BindChannel(udi_channel_t channel, bool other_side, tUDI_DriverInstance *inst, udi_index_t ops_idx, udi_index_t region)
69 {
70         tUDI_Channel *ch = *(tUDI_Channel**)channel;
71         
72         tUDI_DriverRegion       *rgn = inst->Regions[region];
73         
74         udi_ops_init_t *ops = UDI_int_GetOps(inst, ops_idx);
75         if( !ops ) {
76                 Log_Warning("UDI", "Ops ID invalid for '%s' (%i)", inst->Module, ops_idx);
77                 return 1;
78         }
79
80         tUDI_MetaLang *ops_ml = UDI_int_GetMetaLang(inst, ops->meta_idx);
81         if( ops_ml != ch->MetaLang ) {
82                 Log_Warning("UDI", "Attempt by %s to bind with mismatched channel '%s' op '%s' channel",
83                         inst->Module, ops_ml->Name, ch->MetaLang->Name);
84                 return 3;
85         }
86
87         void *context;
88         if( ops->chan_context_size ) {
89                 context = malloc( ops->chan_context_size );
90                 ((udi_chan_context_t*)context)->rdata = rgn->InitContext;
91         }
92         else {
93                 context = rgn->InitContext;
94         }
95         
96         UDI_BindChannel_Raw(channel, other_side, inst, ops->meta_ops_num, context, ops->ops_vector);
97         return 0;
98 }
99
100 tUDI_DriverInstance *UDI_int_ChannelGetInstance(udi_cb_t *gcb, bool other_side)
101 {
102         ASSERT(gcb);
103         ASSERT(gcb->channel);
104         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
105         ASSERT(ch);
106         
107         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(gcb->channel, other_side);
108         
109         return side->Instance;
110 }
111
112 /**
113  * \brief Prepare a cb for a channel call
114  * \param gcb   Generic control block for this request
115  * \param metalang      UDI metalanguage (used for validation)
116  * \return Pointer to ops list
117  * \retval NULL Metalangage validation failed
118  *
119  * Updates the channel and context fields of the gcb, checks the metalanguage and returns
120  * the handler list for the other end of the channel.
121  */
122 const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, tUDI_MetaLang *metalang, udi_index_t meta_ops_num)
123 {
124         ASSERT(gcb);
125         ASSERT(gcb->channel);
126         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
127         ASSERT(ch);
128
129         // TODO: Allow calls without auto-lock
130         #if LOCK_CHANNELS
131         if( ch->Locked ) {
132                 Log_Warning("UDI", "Channel %s:%i used while blocked (before handler was fired)",
133                         ch->MetaLang->Name, meta_ops_num);
134                 return NULL;
135         }       
136         ch->Locked = true;
137         #endif
138         
139         struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
140         if( metalang == NULL )
141         {
142                 if( ch->MetaLang == &cMetaLang_Management ) {
143                         Log_Warning("UDI", "Invalid udi_channel_event_ind on Management metalang");
144                         return NULL;
145                 }
146         }
147         else
148         {
149                 if( ch->MetaLang != metalang || newside->MetaOpsNum != meta_ops_num ) {
150                         Log_Warning("UDI", "Metalanguage mismatch %s:%i req != %s:%i ch",
151                                 metalang->Name, meta_ops_num,
152                                 ch->MetaLang->Name, newside->MetaOpsNum);
153                         return NULL;
154                 }
155         }
156
157         gcb->channel = (udi_channel_t)&newside->BackPtr;
158         gcb->context = newside->Context;
159         if( !newside->Ops ) {
160                 Log_Warning("UDI", "Target end of %p(%s:%i) is unbound",
161                         ch, ch->MetaLang->Name, newside->MetaOpsNum);
162         }
163         return newside->Ops;
164 }
165
166 void UDI_int_ChannelFlip(udi_cb_t *gcb)
167 {
168         ASSERT(gcb);
169         ASSERT(gcb->channel);
170         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
171         ASSERT(ch);
172         
173         struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
174
175         gcb->channel = (udi_channel_t)&newside->BackPtr;
176         gcb->context = newside->Context;
177 }
178
179 void UDI_int_ChannelReleaseFromCall(udi_cb_t *gcb)
180 {
181         #if LOCK_CHANNELS
182         ASSERT(gcb);
183         ASSERT(gcb->channel);
184         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
185         if( !ch ) {
186                 Log_Error("UDI", "Channel pointer of cb %p is NULL", gcb);
187         }
188         ASSERT(ch);
189         
190         ch->Locked = false;
191         #endif
192 }
193

UCC git Repository :: git.ucc.asn.au