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 1
19 #define MAX_SPAWN_IDX 6
21 struct sUDI_ChannelSide {
22 struct sUDI_Channel *BackPtr;
23 tUDI_DriverInstance *Instance;
24 udi_index_t RegionIdx;
25 udi_index_t MetaOpsNum;
27 void *AllocatedContext;
31 typedef struct sUDI_Channel
33 tUDI_MetaLang *MetaLang;
35 struct sUDI_ChannelSide Side[2];
36 struct sUDI_Channel *SpawnBinds[MAX_SPAWN_IDX];
40 udi_channel_t UDI_CreateChannel_Blank(tUDI_MetaLang *metalang)
42 tUDI_Channel *ret = NEW(tUDI_Channel,);
44 ret->MetaLang = metalang;
45 ret->Side[0].BackPtr = ret;
46 ret->Side[1].BackPtr = ret;
48 return (udi_channel_t)&ret->Side[0].BackPtr;
51 udi_channel_t UDI_CreateChannel_Linked(udi_channel_t orig, udi_ubit8_t spawn_idx)
53 tUDI_Channel *ch = *(tUDI_Channel**)orig;
55 ASSERTC(spawn_idx, <, MAX_SPAWN_IDX);
57 if( ch->SpawnBinds[spawn_idx] ) {
58 tUDI_Channel *ret = ch->SpawnBinds[spawn_idx];
59 ch->SpawnBinds[spawn_idx] = NULL;
61 return (udi_channel_t)&ret->Side[1].BackPtr;
63 udi_channel_t ret = UDI_CreateChannel_Blank( ch->MetaLang );
64 ch->SpawnBinds[spawn_idx] = *(tUDI_Channel**)ret;
69 struct sUDI_ChannelSide *UDI_int_ChannelGetSide(udi_channel_t channel, bool other_side)
71 tUDI_Channel *ch = *(tUDI_Channel**)channel;
74 int side_idx = (channel == (udi_channel_t)&ch->Side[0].BackPtr) ? 0 : 1;
76 side_idx = 1 - side_idx;
78 LOG("side_idx = %i, other_side=%b", side_idx, other_side);
80 return &ch->Side[side_idx];
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)
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;
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)
96 tUDI_Channel *ch = *(tUDI_Channel**)channel;
98 tUDI_DriverRegion *rgn = inst->Regions[region];
100 udi_ops_init_t *ops = UDI_int_GetOps(inst, ops_idx);
102 Log_Warning("UDI", "Ops ID invalid for '%s' (%i)", inst->Module, ops_idx);
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);
114 // Use provided context pointer
116 else if( ops->chan_context_size )
119 ASSERTCR( ops->chan_context_size, >=, sizeof(udi_child_chan_context_t), 4 );
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;
125 ((udi_child_chan_context_t*)context)->child_ID = child_ID;
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;
131 context = rgn->InitContext;
134 UDI_BindChannel_Raw(channel, other_side, inst, region, ops->meta_ops_num, context, ops->ops_vector);
138 tUDI_DriverInstance *UDI_int_ChannelGetInstance(udi_cb_t *gcb, bool other_side, udi_index_t *region_idx)
140 struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(gcb->channel, other_side);
142 *region_idx = side->RegionIdx;
143 return side->Instance;
146 void UDI_int_ChannelSetContext(udi_channel_t channel, void *context)
148 struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, false);
149 side->Context = context;
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
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.
162 const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, tUDI_MetaLang *metalang, udi_index_t meta_ops_num)
165 ASSERT(gcb->channel);
166 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
169 // TODO: Allow calls without auto-lock
172 Log_Warning("UDI", "Channel %s:%i used while blocked (before handler was fired)",
173 ch->MetaLang->Name, meta_ops_num);
179 struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
180 if( metalang == NULL )
182 if( ch->MetaLang == &cMetaLang_Management ) {
183 Log_Warning("UDI", "Invalid udi_channel_event_ind on Management metalang");
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);
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);
206 void UDI_int_ChannelFlip(udi_cb_t *gcb)
209 ASSERT(gcb->channel);
210 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
213 struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
215 gcb->channel = (udi_channel_t)&newside->BackPtr;
216 gcb->context = newside->Context;
219 void UDI_int_ChannelReleaseFromCall(udi_cb_t *gcb)
223 ASSERT(gcb->channel);
224 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
226 Log_Error("UDI", "Channel pointer of cb %p is NULL", gcb);