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
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 return &ch->Side[side_idx];
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)
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;
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)
94 tUDI_Channel *ch = *(tUDI_Channel**)channel;
96 tUDI_DriverRegion *rgn = inst->Regions[region];
98 udi_ops_init_t *ops = UDI_int_GetOps(inst, ops_idx);
100 Log_Warning("UDI", "Ops ID invalid for '%s' (%i)", inst->Module, ops_idx);
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);
112 // Use provided context pointer
113 LOG("context = provided %p", context);
115 else if( ops->chan_context_size )
118 ASSERTCR( ops->chan_context_size, >=, sizeof(udi_child_chan_context_t), 4 );
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;
124 ((udi_child_chan_context_t*)context)->child_ID = child_ID;
125 LOG("context = allocated %p", context);
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;
132 LOG("context = region %p", context);
135 UDI_BindChannel_Raw(channel, other_side, inst, region, ops->meta_ops_num, context, ops->ops_vector);
139 tUDI_DriverInstance *UDI_int_ChannelGetInstance(udi_cb_t *gcb, bool other_side, udi_index_t *region_idx)
141 struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(gcb->channel, other_side);
143 *region_idx = side->RegionIdx;
144 return side->Instance;
147 void UDI_int_ChannelSetContext(udi_channel_t channel, void *context)
149 struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, false);
150 side->Context = context;
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
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.
163 const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, tUDI_MetaLang *metalang, udi_index_t meta_ops_num)
166 ASSERT(gcb->channel);
167 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
170 // TODO: Allow calls without auto-lock
173 Log_Warning("UDI", "Channel %s:%i used while blocked (before handler was fired)",
174 ch->MetaLang->Name, meta_ops_num);
180 struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
181 if( metalang == NULL )
183 if( ch->MetaLang == &cMetaLang_Management ) {
184 Log_Warning("UDI", "Invalid udi_channel_event_ind on Management metalang");
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);
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);
207 void UDI_int_ChannelFlip(udi_cb_t *gcb)
210 ASSERT(gcb->channel);
211 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
214 struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
216 gcb->channel = (udi_channel_t)&newside->BackPtr;
217 gcb->context = newside->Context;
220 void UDI_int_ChannelReleaseFromCall(udi_cb_t *gcb)
224 ASSERT(gcb->channel);
225 tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
227 Log_Error("UDI", "Channel pointer of cb %p is NULL", gcb);