6e59594105c61c0804ac8d62d4cccc15258ec7d3
[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
14  * - Prevents multiple non-dispatched operations on one channel
15  * TODO: This should actually lock the GCB, not the channel
16  */
17 #define LOCK_CHANNELS   1
18
19 #define MAX_SPAWN_IDX   6
20
21 struct sUDI_ChannelSide {
22         struct sUDI_Channel     *BackPtr;
23         tUDI_DriverInstance     *Instance;
24         udi_index_t     RegionIdx;
25         udi_index_t     MetaOpsNum;
26         const void      *Ops;
27         void    *AllocatedContext;
28         void    *Context;
29 };
30
31 typedef struct sUDI_Channel
32 {
33         tUDI_MetaLang   *MetaLang;
34         bool    Locked;
35         struct sUDI_ChannelSide  Side[2];
36         struct sUDI_Channel     *SpawnBinds[MAX_SPAWN_IDX];
37 } tUDI_Channel;
38
39 // === CODE ===
40 udi_channel_t UDI_CreateChannel_Blank(tUDI_MetaLang *metalang)
41 {
42         tUDI_Channel    *ret = NEW(tUDI_Channel,);
43
44         ret->MetaLang = metalang;
45         ret->Side[0].BackPtr = ret;     
46         ret->Side[1].BackPtr = ret;     
47
48         return (udi_channel_t)&ret->Side[0].BackPtr;
49 }
50
51 udi_channel_t UDI_CreateChannel_Linked(udi_channel_t orig, udi_ubit8_t spawn_idx)
52 {
53         tUDI_Channel    *ch = *(tUDI_Channel**)orig;
54         ASSERT(ch);
55         ASSERTC(spawn_idx, <, MAX_SPAWN_IDX);
56         // TODO: mutex
57         if( ch->SpawnBinds[spawn_idx] ) {
58                 tUDI_Channel    *ret = ch->SpawnBinds[spawn_idx];
59                 ch->SpawnBinds[spawn_idx] = NULL;
60                 // TODO: mutex
61                 return (udi_channel_t)&ret->Side[1].BackPtr;
62         }
63         udi_channel_t ret = UDI_CreateChannel_Blank( ch->MetaLang );
64         ch->SpawnBinds[spawn_idx] = *(tUDI_Channel**)ret;
65         // TODO: Mutex
66         return ret;
67 }
68
69 struct sUDI_ChannelSide *UDI_int_ChannelGetSide(udi_channel_t channel, bool other_side)
70 {
71         tUDI_Channel *ch = *(tUDI_Channel**)channel;
72         if(!ch) return NULL;
73
74         int side_idx = (channel == (udi_channel_t)&ch->Side[0].BackPtr) ? 0 : 1;
75         if( other_side )
76                 side_idx = 1 - side_idx;        
77
78         LOG("side_idx = %i, other_side=%b", side_idx, other_side);
79
80         return &ch->Side[side_idx];
81 }
82
83 int UDI_BindChannel_Raw(udi_channel_t channel, bool other_side, tUDI_DriverInstance *inst, udi_index_t region_idx, udi_index_t meta_ops_num,  void *context, const void *ops)
84 {
85         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, other_side);
86         side->Instance = inst;
87         side->RegionIdx = region_idx;
88         side->Context = context;
89         side->MetaOpsNum = meta_ops_num;
90         side->Ops = ops;
91         return 0;
92 }
93
94 int UDI_BindChannel(udi_channel_t channel, bool other_side, tUDI_DriverInstance *inst, udi_index_t ops_idx, udi_index_t region, void *context, bool is_child_bind, udi_ubit32_t child_ID)
95 {
96         tUDI_Channel *ch = *(tUDI_Channel**)channel;
97         
98         tUDI_DriverRegion       *rgn = inst->Regions[region];
99         
100         udi_ops_init_t *ops = UDI_int_GetOps(inst, ops_idx);
101         if( !ops ) {
102                 Log_Warning("UDI", "Ops ID invalid for '%s' (%i)", inst->Module, ops_idx);
103                 return 1;
104         }
105
106         tUDI_MetaLang *ops_ml = UDI_int_GetMetaLang(inst, ops->meta_idx);
107         if( ops_ml != ch->MetaLang ) {
108                 Log_Warning("UDI", "Attempt by %s to bind with mismatched channel '%s' op '%s' channel",
109                         inst->Module, ops_ml->Name, ch->MetaLang->Name);
110                 return 3;
111         }
112
113         if( context ) {
114                 // Use provided context pointer
115         }
116         else if( ops->chan_context_size )
117         {
118                 if( is_child_bind )
119                         ASSERTCR( ops->chan_context_size, >=, sizeof(udi_child_chan_context_t), 4 );
120                 else
121                         ASSERTCR( ops->chan_context_size, >=, sizeof(udi_chan_context_t), 4 );
122                 context = malloc( ops->chan_context_size );
123                 ((udi_chan_context_t*)context)->rdata = rgn->InitContext;
124                 if( is_child_bind )
125                         ((udi_child_chan_context_t*)context)->child_ID = child_ID;
126                 
127                 // TODO: The driver may change the channel context, but this must be freed by the environment
128                 UDI_int_ChannelGetSide(channel, other_side)->AllocatedContext = context;
129         }
130         else {
131                 context = rgn->InitContext;
132         }
133         
134         UDI_BindChannel_Raw(channel, other_side, inst, region, ops->meta_ops_num, context, ops->ops_vector);
135         return 0;
136 }
137
138 tUDI_DriverInstance *UDI_int_ChannelGetInstance(udi_cb_t *gcb, bool other_side, udi_index_t *region_idx)
139 {
140         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(gcb->channel, other_side);
141         if(region_idx)
142                 *region_idx = side->RegionIdx;
143         return side->Instance;
144 }
145
146 void UDI_int_ChannelSetContext(udi_channel_t channel, void *context)
147 {
148         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, false);
149         side->Context = context;
150 }
151
152 /**
153  * \brief Prepare a cb for a channel call
154  * \param gcb   Generic control block for this request
155  * \param metalang      UDI metalanguage (used for validation)
156  * \return Pointer to ops list
157  * \retval NULL Metalangage validation failed
158  *
159  * Updates the channel and context fields of the gcb, checks the metalanguage and returns
160  * the handler list for the other end of the channel.
161  */
162 const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, tUDI_MetaLang *metalang, udi_index_t meta_ops_num)
163 {
164         ASSERT(gcb);
165         ASSERT(gcb->channel);
166         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
167         ASSERT(ch);
168
169         // TODO: Allow calls without auto-lock
170         #if LOCK_CHANNELS
171         if( ch->Locked ) {
172                 Log_Warning("UDI", "Channel %s:%i used while blocked (before handler was fired)",
173                         ch->MetaLang->Name, meta_ops_num);
174                 return NULL;
175         }       
176         ch->Locked = true;
177         #endif
178         
179         struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
180         if( metalang == NULL )
181         {
182                 if( ch->MetaLang == &cMetaLang_Management ) {
183                         Log_Warning("UDI", "Invalid udi_channel_event_ind on Management metalang");
184                         return NULL;
185                 }
186         }
187         else
188         {
189                 if( ch->MetaLang != metalang || newside->MetaOpsNum != meta_ops_num ) {
190                         Log_Warning("UDI", "Metalanguage mismatch %s:%i req != %s:%i ch",
191                                 metalang->Name, meta_ops_num,
192                                 ch->MetaLang->Name, newside->MetaOpsNum);
193                         return NULL;
194                 }
195         }
196
197         gcb->channel = (udi_channel_t)&newside->BackPtr;
198         gcb->context = newside->Context;
199         if( !newside->Ops ) {
200                 Log_Warning("UDI", "Target end of %p(%s:%i) is unbound",
201                         ch, ch->MetaLang->Name, newside->MetaOpsNum);
202         }
203         return newside->Ops;
204 }
205
206 void UDI_int_ChannelFlip(udi_cb_t *gcb)
207 {
208         ASSERT(gcb);
209         ASSERT(gcb->channel);
210         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
211         ASSERT(ch);
212         
213         struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
214
215         gcb->channel = (udi_channel_t)&newside->BackPtr;
216         gcb->context = newside->Context;
217 }
218
219 void UDI_int_ChannelReleaseFromCall(udi_cb_t *gcb)
220 {
221         #if LOCK_CHANNELS
222         ASSERT(gcb);
223         ASSERT(gcb->channel);
224         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
225         if( !ch ) {
226                 Log_Error("UDI", "Channel pointer of cb %p is NULL", gcb);
227         }
228         ASSERT(ch);
229         
230         ch->Locked = false;
231         #endif
232 }
233

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