3 * - By John Hodge (thePowersGang)
11 #include "udi_internal.h"
14 * - Prevents multiple non-dispatched operations on one channel
15 * TODO: This should actually lock the GCB, not the channel
17 #define LOCK_CHANNELS 0
20 * - Emit a log message with names/indexes of both endpoints
22 #define TRACE_ENDPOINTS 1
24 #define MAX_SPAWN_IDX 6
26 struct sUDI_ChannelSide {
27 struct sUDI_Channel *BackPtr;
28 tUDI_DriverInstance *Instance;
29 udi_index_t RegionIdx;
30 udi_index_t MetaOpsNum;
32 void *AllocatedContext;
36 typedef struct sUDI_Channel
38 tUDI_MetaLang *MetaLang;
40 struct sUDI_ChannelSide Side[2];
41 struct sUDI_Channel *SpawnBinds[MAX_SPAWN_IDX];
45 udi_channel_t UDI_CreateChannel_Blank(tUDI_MetaLang *metalang)
47 tUDI_Channel *ret = NEW(tUDI_Channel,);
49 ret->MetaLang = metalang;
50 ret->Side[0].BackPtr = ret;
51 ret->Side[1].BackPtr = ret;
53 return (udi_channel_t)&ret->Side[0].BackPtr;
56 udi_channel_t UDI_CreateChannel_Linked(udi_channel_t orig, udi_ubit8_t spawn_idx)
58 tUDI_Channel *ch = *(tUDI_Channel**)orig;
60 ASSERTC(spawn_idx, <, MAX_SPAWN_IDX);
62 if( ch->SpawnBinds[spawn_idx] ) {
63 tUDI_Channel *ret = ch->SpawnBinds[spawn_idx];
64 ch->SpawnBinds[spawn_idx] = NULL;
66 return (udi_channel_t)&ret->Side[1].BackPtr;
68 udi_channel_t ret = UDI_CreateChannel_Blank( ch->MetaLang );
69 ch->SpawnBinds[spawn_idx] = *(tUDI_Channel**)ret;
74 struct sUDI_ChannelSide *UDI_int_ChannelGetSide(udi_channel_t channel, bool other_side)
76 tUDI_Channel *ch = *(tUDI_Channel**)channel;
79 int side_idx = (channel == (udi_channel_t)&ch->Side[0].BackPtr) ? 0 : 1;
81 side_idx = 1 - side_idx;
83 return &ch->Side[side_idx];
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)
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;
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)
99 tUDI_Channel *ch = *(tUDI_Channel**)channel;
101 tUDI_DriverRegion *rgn = inst->Regions[region];
103 udi_ops_init_t *ops = UDI_int_GetOps(inst, ops_idx);
105 Log_Warning("UDI", "Ops ID invalid for '%s' (%i)", inst->Module, ops_idx);
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);
117 // Use provided context pointer
118 LOG("context = provided %p", context);
120 else if( ops->chan_context_size )
123 ASSERTCR( ops->chan_context_size, >=, sizeof(udi_child_chan_context_t), 4 );
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;
129 ((udi_child_chan_context_t*)context)->child_ID = child_ID;
130 LOG("context = allocated %p", context);
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;
136 context = rgn->InitContext;
137 LOG("context = region %p", context);
140 UDI_BindChannel_Raw(channel, other_side, inst, region, ops->meta_ops_num, context, ops->ops_vector);
144 tUDI_DriverInstance *UDI_int_ChannelGetInstance(udi_cb_t *gcb, bool other_side, udi_index_t *region_idx)
146 struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(gcb->channel, other_side);
148 *region_idx = side->RegionIdx;
149 return side->Instance;
152 void UDI_int_ChannelSetContext(udi_channel_t channel, void *context)
154 struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, false);
155 side->Context = context;
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
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.
168 const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, tUDI_MetaLang *metalang, udi_index_t meta_ops_num)
171 ASSERT(gcb->channel);
172 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
175 // TODO: Allow calls without auto-lock
178 Log_Warning("UDI", "Channel %s:%i used while blocked (before handler was fired)",
179 ch->MetaLang->Name, meta_ops_num);
185 struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
186 if( metalang == NULL )
188 if( ch->MetaLang == &cMetaLang_Management ) {
189 Log_Warning("UDI", "Invalid udi_channel_event_ind on Management metalang");
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);
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);
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);
219 void UDI_int_ChannelFlip(udi_cb_t *gcb)
222 ASSERT(gcb->channel);
223 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
226 struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
228 gcb->channel = (udi_channel_t)&newside->BackPtr;
229 gcb->context = newside->Context;
232 void UDI_int_ChannelReleaseFromCall(udi_cb_t *gcb)
236 ASSERT(gcb->channel);
237 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
239 Log_Error("UDI", "Channel pointer of cb %p is NULL", gcb);