Kernel/PTYs - NL->CR translation (disabled)
[tpg/acess2.git] / KernelLand / Modules / Interfaces / UDI / channels.c
index e173747..a3894bb 100644 (file)
 /*
  * Acess2 UDI Layer
  * - By John Hodge (thePowersGang)
+ *
+ * channels.c
+ * - Channel code
  */
 #define DEBUG  0
 #include <acess.h>
 #include <udi.h>
 #include "udi_internal.h"
+/*
+ * LOCK_CHANNELS
+ * - Prevents multiple non-dispatched operations on one channel
+ * TODO: This should actually lock the GCB, not the channel
+ */
+#define LOCK_CHANNELS  0
+/*
+ * TRACE_ENDPOINTS
+ * - Emit a log message with names/indexes of both endpoints
+ */
+#define TRACE_ENDPOINTS        1
+
+#define MAX_SPAWN_IDX  6
 
 struct sUDI_ChannelSide {
        struct sUDI_Channel     *BackPtr;
+       tUDI_DriverInstance     *Instance;
+       udi_index_t     RegionIdx;
+       udi_index_t     MetaOpsNum;
        const void      *Ops;
+       void    *AllocatedContext;
        void    *Context;
 };
 
 typedef struct sUDI_Channel
 {
-       enum eUDI_MetaLang      MetaLang;
+       tUDI_MetaLang   *MetaLang;
+       bool    Locked;
        struct sUDI_ChannelSide  Side[2];
+       struct sUDI_Channel     *SpawnBinds[MAX_SPAWN_IDX];
 } tUDI_Channel;
 
 // === CODE ===
-udi_channel_t UDI_CreateChannel(enum eUDI_MetaLang metalang, udi_index_t meta_ops_num,
-       tUDI_DriverInstance *ThisEnd, udi_index_t ThisOpsIndex,
-       tUDI_DriverInstance *OtherEnd, udi_index_t OtherOpsIndex)
+udi_channel_t UDI_CreateChannel_Blank(tUDI_MetaLang *metalang)
 {
        tUDI_Channel    *ret = NEW(tUDI_Channel,);
+
        ret->MetaLang = metalang;
-       ret->Side[0].BackPtr = ret;
-//     ret->Side[0].Ops = ThisEnd->Module->InitInfo->Op;
-       ret->Side[1].BackPtr = ret;
+       ret->Side[0].BackPtr = ret;     
+       ret->Side[1].BackPtr = ret;     
+
        return (udi_channel_t)&ret->Side[0].BackPtr;
 }
 
-const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, enum eUDI_MetaLang metalang)
+udi_channel_t UDI_CreateChannel_Linked(udi_channel_t orig, udi_ubit8_t spawn_idx)
+{
+       tUDI_Channel    *ch = *(tUDI_Channel**)orig;
+       ASSERT(ch);
+       ASSERTC(spawn_idx, <, MAX_SPAWN_IDX);
+       // TODO: mutex
+       if( ch->SpawnBinds[spawn_idx] ) {
+               tUDI_Channel    *ret = ch->SpawnBinds[spawn_idx];
+               ch->SpawnBinds[spawn_idx] = NULL;
+               // TODO: mutex
+               return (udi_channel_t)&ret->Side[1].BackPtr;
+       }
+       udi_channel_t ret = UDI_CreateChannel_Blank( ch->MetaLang );
+       ch->SpawnBinds[spawn_idx] = *(tUDI_Channel**)ret;
+       // TODO: Mutex
+       return ret;
+}
+
+struct sUDI_ChannelSide *UDI_int_ChannelGetSide(udi_channel_t channel, bool other_side)
+{
+       tUDI_Channel *ch = *(tUDI_Channel**)channel;
+       if(!ch) return NULL;
+
+       int side_idx = (channel == (udi_channel_t)&ch->Side[0].BackPtr) ? 0 : 1;
+       if( other_side )
+               side_idx = 1 - side_idx;        
+
+       return &ch->Side[side_idx];
+}
+
+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)
+{
+       struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, other_side);
+       side->Instance = inst;
+       side->RegionIdx = region_idx;
+       side->Context = context;
+       side->MetaOpsNum = meta_ops_num;
+       side->Ops = ops;
+       return 0;
+}
+
+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)
+{
+       tUDI_Channel *ch = *(tUDI_Channel**)channel;
+       
+       tUDI_DriverRegion       *rgn = inst->Regions[region];
+       
+       udi_ops_init_t *ops = UDI_int_GetOps(inst, ops_idx);
+       if( !ops ) {
+               Log_Warning("UDI", "Ops ID invalid for '%s' (%i)", inst->Module, ops_idx);
+               return 1;
+       }
+
+       tUDI_MetaLang *ops_ml = UDI_int_GetMetaLang(inst->Module, ops->meta_idx);
+       if( ops_ml != ch->MetaLang ) {
+               Log_Warning("UDI", "Attempt by %s to bind with mismatched channel '%s' op '%s' channel",
+                       inst->Module, ops_ml->Name, ch->MetaLang->Name);
+               return 3;
+       }
+
+       if( context ) {
+               // Use provided context pointer
+               LOG("context = provided %p", context);
+       }
+       else if( ops->chan_context_size )
+       {
+               if( is_child_bind )
+                       ASSERTCR( ops->chan_context_size, >=, sizeof(udi_child_chan_context_t), 4 );
+               else
+                       ASSERTCR( ops->chan_context_size, >=, sizeof(udi_chan_context_t), 4 );
+               context = calloc( 1, ops->chan_context_size );
+               ((udi_chan_context_t*)context)->rdata = rgn->InitContext;
+               if( is_child_bind )
+                       ((udi_child_chan_context_t*)context)->child_ID = child_ID;
+               LOG("context = allocated %p", context);
+               
+               // TODO: The driver may change the channel context, but this must be freed by the environment
+               UDI_int_ChannelGetSide(channel, other_side)->AllocatedContext = context;
+       }
+       else {
+               context = rgn->InitContext;
+               LOG("context = region %p", context);
+       }
+       
+       UDI_BindChannel_Raw(channel, other_side, inst, region, ops->meta_ops_num, context, ops->ops_vector);
+       return 0;
+}
+
+tUDI_DriverInstance *UDI_int_ChannelGetInstance(udi_cb_t *gcb, bool other_side, udi_index_t *region_idx)
+{
+       struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(gcb->channel, other_side);
+       if(region_idx)
+               *region_idx = side->RegionIdx;
+       return side->Instance;
+}
+
+void UDI_int_ChannelSetContext(udi_channel_t channel, void *context)
 {
+       struct sUDI_ChannelSide *side = UDI_int_ChannelGetSide(channel, false);
+       side->Context = context;
+}
+
+/**
+ * \brief Prepare a cb for a channel call
+ * \param gcb  Generic control block for this request
+ * \param metalang     UDI metalanguage (used for validation)
+ * \return Pointer to ops list
+ * \retval NULL        Metalangage validation failed
+ *
+ * Updates the channel and context fields of the gcb, checks the metalanguage and returns
+ * the handler list for the other end of the channel.
+ */
+const void *UDI_int_ChannelPrepForCall(udi_cb_t *gcb, tUDI_MetaLang *metalang, udi_index_t meta_ops_num)
+{
+       ASSERT(gcb);
+       ASSERT(gcb->channel);
        tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
-       ASSERTCR(ch->MetaLang, ==, metalang, NULL);
+       ASSERT(ch);
 
-       struct sUDI_ChannelSide *newside = (gcb->channel == (udi_channel_t)&ch->Side[0].BackPtr ? &ch->Side[1] : &ch->Side[0]);
+       // TODO: Allow calls without auto-lock
+       #if LOCK_CHANNELS
+       if( ch->Locked ) {
+               Log_Warning("UDI", "Channel %s:%i used while blocked (before handler was fired)",
+                       ch->MetaLang->Name, meta_ops_num);
+               return NULL;
+       }       
+       ch->Locked = true;
+       #endif
+       
+       struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
+       if( metalang == NULL )
+       {
+               if( ch->MetaLang == &cMetaLang_Management ) {
+                       Log_Warning("UDI", "Invalid udi_channel_event_ind on Management metalang");
+                       return NULL;
+               }
+       }
+       else
+       {
+               if( ch->MetaLang != metalang || newside->MetaOpsNum != meta_ops_num ) {
+                       Log_Warning("UDI", "Metalanguage mismatch %s:%i req != %s:%i ch",
+                               metalang->Name, meta_ops_num,
+                               ch->MetaLang->Name, newside->MetaOpsNum);
+                       return NULL;
+               }
+       }
+
+       #if TRACE_ENDPOINTS
+       struct sUDI_ChannelSide *thisside = UDI_int_ChannelGetSide(gcb->channel, false);
+       Log("%s:%i -> %s:%i",
+               (thisside->Instance ? thisside->Instance->Module->ModuleName : "MA"), thisside->RegionIdx,
+               (newside->Instance ? newside->Instance->Module->ModuleName : "MA"), newside->RegionIdx);
+       #endif
 
-//     gcb->initiator_context = gcb->context;
        gcb->channel = (udi_channel_t)&newside->BackPtr;
        gcb->context = newside->Context;
+       if( !newside->Ops ) {
+               Log_Warning("UDI", "Target end of %p(%s:%i) is unbound",
+                       ch, ch->MetaLang->Name, newside->MetaOpsNum);
+       }
        return newside->Ops;
 }
 
+void UDI_int_ChannelFlip(udi_cb_t *gcb)
+{
+       ASSERT(gcb);
+       ASSERT(gcb->channel);
+       tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
+       ASSERT(ch);
+       
+       struct sUDI_ChannelSide *newside = UDI_int_ChannelGetSide(gcb->channel, true);
+
+       gcb->channel = (udi_channel_t)&newside->BackPtr;
+       gcb->context = newside->Context;
+}
+
+void UDI_int_ChannelReleaseFromCall(udi_cb_t *gcb)
+{
+       #if LOCK_CHANNELS
+       ASSERT(gcb);
+       ASSERT(gcb->channel);
+       tUDI_Channel *ch = *(tUDI_Channel**)(gcb->channel);
+       if( !ch ) {
+               Log_Error("UDI", "Channel pointer of cb %p is NULL", gcb);
+       }
+       ASSERT(ch);
+       
+       ch->Locked = false;
+       #endif
+}
+

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