b3865077b5fa40b418f0bd9c1238c5415b72379e
[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   0
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         return &ch->Side[side_idx];
79 }
80
81 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)
82 {
83         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, other_side);
84         side->Instance = inst;
85         side->RegionIdx = region_idx;
86         side->Context = context;
87         side->MetaOpsNum = meta_ops_num;
88         side->Ops = ops;
89         return 0;
90 }
91
92 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)
93 {
94         tUDI_Channel *ch = *(tUDI_Channel**)channel;
95         
96         tUDI_DriverRegion       *rgn = inst->Regions[region];
97         
98         udi_ops_init_t *ops = UDI_int_GetOps(inst, ops_idx);
99         if( !ops ) {
100                 Log_Warning("UDI", "Ops ID invalid for '%s' (%i)", inst->Module, ops_idx);
101                 return 1;
102         }
103
104         tUDI_MetaLang *ops_ml = UDI_int_GetMetaLang(inst->Module, ops->meta_idx);
105         if( ops_ml != ch->MetaLang ) {
106                 Log_Warning("UDI", "Attempt by %s to bind with mismatched channel '%s' op '%s' channel",
107                         inst->Module, ops_ml->Name, ch->MetaLang->Name);
108                 return 3;
109         }
110
111         if( context ) {
112                 // Use provided context pointer
113                 LOG("context = provided %p", context);
114         }
115         else if( ops->chan_context_size )
116         {
117                 if( is_child_bind )
118                         ASSERTCR( ops->chan_context_size, >=, sizeof(udi_child_chan_context_t), 4 );
119                 else
120                         ASSERTCR( ops->chan_context_size, >=, sizeof(udi_chan_context_t), 4 );
121                 context = calloc( 1, ops->chan_context_size );
122                 ((udi_chan_context_t*)context)->rdata = rgn->InitContext;
123                 if( is_child_bind )
124                         ((udi_child_chan_context_t*)context)->child_ID = child_ID;
125                 LOG("context = allocated %p", context);
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                 LOG("context = region %p", context);
133         }
134         
135         UDI_BindChannel_Raw(channel, other_side, inst, region, ops->meta_ops_num, context, ops->ops_vector);
136         return 0;
137 }
138
139 tUDI_DriverInstance *UDI_int_ChannelGetInstance(udi_cb_t *gcb, bool other_side, udi_index_t *region_idx)
140 {
141         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(gcb->channel, other_side);
142         if(region_idx)
143                 *region_idx = side->RegionIdx;
144         return side->Instance;
145 }
146
147 void UDI_int_ChannelSetContext(udi_channel_t channel, void *context)
148 {
149         struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, false);
150         side->Context = context;
151 }
152
153 /**
154  * \brief Prepare a cb for a channel call
155  * \param gcb   Generic control block for this request
156  * \param metalang      UDI metalanguage (used for validation)
157  * \return Pointer to ops list
158  * \retval NULL Metalangage validation failed
159  *
160  * Updates the channel and context fields of the gcb, checks the metalanguage and returns
161  * the handler list for the other end of the channel.
162  */
163 const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, tUDI_MetaLang *metalang, udi_index_t meta_ops_num)
164 {
165         ASSERT(gcb);
166         ASSERT(gcb->channel);
167         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
168         ASSERT(ch);
169
170         // TODO: Allow calls without auto-lock
171         #if LOCK_CHANNELS
172         if( ch->Locked ) {
173                 Log_Warning("UDI", "Channel %s:%i used while blocked (before handler was fired)",
174                         ch->MetaLang->Name, meta_ops_num);
175                 return NULL;
176         }       
177         ch->Locked = true;
178         #endif
179         
180         struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
181         if( metalang == NULL )
182         {
183                 if( ch->MetaLang == &cMetaLang_Management ) {
184                         Log_Warning("UDI", "Invalid udi_channel_event_ind on Management metalang");
185                         return NULL;
186                 }
187         }
188         else
189         {
190                 if( ch->MetaLang != metalang || newside->MetaOpsNum != meta_ops_num ) {
191                         Log_Warning("UDI", "Metalanguage mismatch %s:%i req != %s:%i ch",
192                                 metalang->Name, meta_ops_num,
193                                 ch->MetaLang->Name, newside->MetaOpsNum);
194                         return NULL;
195                 }
196         }
197
198         gcb->channel = (udi_channel_t)&newside->BackPtr;
199         gcb->context = newside->Context;
200         if( !newside->Ops ) {
201                 Log_Warning("UDI", "Target end of %p(%s:%i) is unbound",
202                         ch, ch->MetaLang->Name, newside->MetaOpsNum);
203         }
204         return newside->Ops;
205 }
206
207 void UDI_int_ChannelFlip(udi_cb_t *gcb)
208 {
209         ASSERT(gcb);
210         ASSERT(gcb->channel);
211         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
212         ASSERT(ch);
213         
214         struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
215
216         gcb->channel = (udi_channel_t)&newside->BackPtr;
217         gcb->context = newside->Context;
218 }
219
220 void UDI_int_ChannelReleaseFromCall(udi_cb_t *gcb)
221 {
222         #if LOCK_CHANNELS
223         ASSERT(gcb);
224         ASSERT(gcb->channel);
225         tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
226         if( !ch ) {
227                 Log_Error("UDI", "Channel pointer of cb %p is NULL", gcb);
228         }
229         ASSERT(ch);
230         
231         ch->Locked = false;
232         #endif
233 }
234

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