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

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