Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
[tpg/acess2.git] / KernelLand / Modules / Interfaces / UDI / management_agent.c
1 /*
2  * Acess2 UDI Layer
3  * - By John Hodge (thePowersGang)
4  * 
5  * management_agent.c
6  * - Managment Agent
7  */
8 #define DEBUG   1
9 #include <udi.h>
10 #include <acess.h>
11 #include <udi_internal.h>
12 #include <udi_internal_ma.h>
13
14 // === CONSTANTS ===
15
16 // === PROTOTYPES ===
17
18 // === GLOBALS ===
19 tUDI_DriverInstance     *gpUDI_ActiveInstances;
20
21 // === CODE ===
22 tUDI_DriverInstance *UDI_MA_CreateInstance(tUDI_DriverModule *DriverModule,
23         tUDI_DriverInstance *ParentInstance, tUDI_ChildBinding *ChildBinding)
24 {
25         tUDI_DriverInstance     *inst = NEW_wA(tUDI_DriverInstance, Regions, DriverModule->nRegions);
26         udi_primary_init_t      *pri_init = DriverModule->InitInfo->primary_init_info;
27         inst->Module = DriverModule;
28         inst->Regions[0] = UDI_MA_InitRegion(inst, 0, 0, pri_init->rdata_size);
29         if( !inst->Regions[0] ) goto _error;
30         udi_secondary_init_t    *sec_init = DriverModule->InitInfo->secondary_init_list;
31         if( sec_init )
32         {
33                 for( int i = 0; sec_init[i].region_idx; i ++ )
34                 {
35                         inst->Regions[1+i] = UDI_MA_InitRegion(inst, i,
36                                 sec_init[i].region_idx, sec_init[i].rdata_size);
37                         if( !inst->Regions[1+i] )       goto _error;
38                 }
39         }
40
41         if( ParentInstance ) {
42                 ASSERT(ChildBinding);
43                 ChildBinding->BoundInstance = inst;
44         }
45         inst->Parent = ParentInstance;
46         inst->ParentChildBinding = ChildBinding;
47
48         inst->ManagementChannel = UDI_CreateChannel_Blank(&cMetaLang_Management);
49         UDI_BindChannel_Raw(inst->ManagementChannel, true,
50                 inst, 0, 0, inst->Regions[0]->InitContext, pri_init->mgmt_ops);
51 //      UDI_BindChannel_Raw(inst->ManagementChannel, false,
52 //              NULL, 1, inst, &cUDI_MgmtOpsList);      // TODO: ops list for management agent?
53
54 //      for( int i = 0; i < DriverModule->nRegions; i ++ )
55 //              Log("Rgn %i: %p", i, inst->Regions[i]);
56
57         LOG("Inst %s %p MA state =%i",
58                 inst->Module->ModuleName, inst, UDI_MASTATE_USAGEIND);
59         inst->CurState = UDI_MASTATE_USAGEIND;
60         // Next State: _SECBIND
61
62         // Add to global list of active instances
63         inst->Next = gpUDI_ActiveInstances;
64         gpUDI_ActiveInstances = inst;
65
66         // Send usage indication
67         udi_usage_cb_t *cb = (void*)udi_cb_alloc_internal_v(&cMetaLang_Management, UDI_MGMT_USAGE_CB_NUM,
68                 0, pri_init->mgmt_scratch_requirement, inst->ManagementChannel
69                 );
70         UDI_GCB(cb)->initiator_context = inst;
71         udi_usage_ind(cb, UDI_RESOURCES_NORMAL);
72         // udi_usage_res causes next state transition
73         
74         return inst;
75 _error:
76         for( int i = 0; i < DriverModule->nRegions; i++ )
77                 free(inst->Regions[i]);
78         free(inst);
79         return NULL;
80 }
81
82 tUDI_DriverRegion *UDI_MA_InitRegion(tUDI_DriverInstance *Inst,
83         udi_ubit16_t Index, udi_ubit16_t Type, size_t RDataSize)
84 {
85 //      ASSERTCR( RDataSize, <=, UDI_MIN_ALLOC_LIMIT, NULL );
86         ASSERTCR( RDataSize, >=, sizeof(udi_init_context_t), NULL );
87         tUDI_DriverRegion       *rgn = NEW(tUDI_DriverRegion,+RDataSize);
88         rgn->InitContext = (void*)(rgn+1);
89         rgn->InitContext->region_idx = Type;
90 //      rgn->InitContext->limits
91         return rgn;
92 }
93
94 void UDI_MA_BeginEnumeration(tUDI_DriverInstance *Inst)
95 {
96         udi_primary_init_t *pri_init = Inst->Module->InitInfo->primary_init_info;
97         udi_enumerate_cb_t      *ecb = (void*)udi_cb_alloc_internal_v(
98                 &cMetaLang_Management, UDI_MGMT_ENUMERATE_CB_NUM,
99                 0, pri_init->mgmt_scratch_requirement, Inst->ManagementChannel);
100         UDI_GCB(ecb)->initiator_context = Inst;
101         ecb->child_data = malloc(pri_init->child_data_size);
102         ecb->attr_list = NEW(udi_instance_attr_list_t, *pri_init->enumeration_attr_list_length);
103         ecb->attr_valid_length = 0;
104         udi_enumerate_req(ecb, UDI_ENUMERATE_START);
105         Threads_Yield();        // Yield to allow udi_enumerate_req to run
106 }
107
108 /*
109  * Returns number of matching attributes
110  * If an attribute differs, returns 0
111  */
112 int UDI_MA_CheckDeviceMatch(int nDevAttr, udi_instance_attr_list_t *DevAttrs,
113         int nEnumAttr, udi_instance_attr_list_t *EnumAttrs)
114 {
115         // TODO: Ask metalangauge instead
116         int n_matches = 0;
117         for( int i = 0; i < nDevAttr; i ++ )
118         {
119                 udi_instance_attr_list_t *dev_attr = &DevAttrs[i];
120                 udi_instance_attr_list_t *enum_attr = NULL;
121                 for( int j = 0; j < nEnumAttr; j ++ )
122                 {
123                         if( strcmp(dev_attr->attr_name, EnumAttrs[j].attr_name) == 0 ) {
124                                 enum_attr = &EnumAttrs[j];
125                                 break;
126                         }
127                 }
128                 if( enum_attr )
129                 {
130                         //LOG("Match = '%s' (%i %x == %i %x)",
131                         //      dev_attr->attr_name,
132                         //      dev_attr->attr_length, UDI_ATTR32_GET(dev_attr->attr_value),
133                         //      enum_attr->attr_length, UDI_ATTR32_GET(enum_attr->attr_value)
134                         //      );
135                         if( enum_attr->attr_length != dev_attr->attr_length )
136                                 return 0;
137                         if( memcmp(enum_attr->attr_value, dev_attr->attr_value, dev_attr->attr_length) != 0 )
138                                 return 0;
139                         n_matches ++;
140                 }
141                 else
142                 {
143                         // Attribute desired is missing, error?
144                         //LOG("attr '%s' missing", dev_attr->attr_name);
145                 }
146         }
147         //LOG("n_matches = %i", n_matches);
148         return n_matches;
149 }
150
151 void UDI_MA_AddChild(udi_enumerate_cb_t *cb, udi_index_t ops_idx)
152 {
153         // Current side is MA, other is instance
154         // TODO: Get region index too?
155         tUDI_DriverInstance *inst = UDI_int_ChannelGetInstance( UDI_GCB(cb), true, NULL );
156         //LOG("inst = %p", inst);
157         
158         // Search for existing child with same child_ID and ops
159         for( tUDI_ChildBinding *child = inst->FirstChild; child; child = child->Next )
160         {
161                 if( child->ChildID == cb->child_ID && child->Ops->ops_idx == ops_idx ) {
162                         LOG("duplicate, not creating");
163                         return;
164                 }
165         }
166         
167         // Create a new child
168         tUDI_ChildBinding *child = NEW_wA(tUDI_ChildBinding, Attribs, cb->attr_valid_length);
169         child->Next = NULL;
170         child->ChildID = cb->child_ID;
171         child->Ops = UDI_int_GetOps(inst, ops_idx);
172         child->BoundInstance = NULL;    // currently unbound
173         child->BindOps = NULL;
174         child->nAttribs = cb->attr_valid_length;
175         memcpy(child->Attribs, cb->attr_list, sizeof(cb->attr_list[0])*cb->attr_valid_length);
176
177         // - Locate child_bind_ops definition
178         for( int i = 0; i < inst->Module->nChildBindOps; i ++ )
179         {
180                 if( inst->Module->ChildBindOps[i].ops_idx == ops_idx ) {
181                         child->BindOps = &inst->Module->ChildBindOps[i];
182                         break;
183                 }
184         }
185         if( !child->BindOps ) {
186                 Log_Error("UDI", "Driver '%s' doesn't have a 'child_bind_ops' for ops_idx %i",
187                         inst->Module->ModuleName, ops_idx);
188                 free(child);
189                 return ;
190         }
191
192         child->Next = inst->FirstChild;
193         inst->FirstChild = child;
194         
195         // and search for a handler
196         child->Metalang = UDI_int_GetMetaLang(inst->Module, child->Ops->meta_idx);
197          int    best_level = 0;
198         tUDI_DriverModule *best_module = NULL;
199         for( tUDI_DriverModule *module = gpUDI_LoadedModules; module; module = module->Next )
200         {
201                 for( int i = 0; i < module->nDevices; i ++ )
202                 {
203                         //LOG("%s:%i %p ?== %p",
204                         //      module->ModuleName, i,
205                         //      module->Devices[i]->Metalang, metalang);
206                         if( module->Devices[i]->Metalang != child->Metalang )
207                                 continue ;
208                         
209                         int level = UDI_MA_CheckDeviceMatch(
210                                 module->Devices[i]->nAttribs, module->Devices[i]->Attribs,
211                                 child->nAttribs, child->Attribs
212                                 );
213                         if( level > best_level ) {
214                                 best_level = level;
215                                 best_module = module;
216                         }
217                 }
218         }
219         if( best_module != NULL )
220         {
221                 UDI_MA_CreateInstance(best_module, inst, child);
222         }
223 }
224
225 void UDI_MA_BindParents(tUDI_DriverModule *Module)
226 {
227         // Scan active instances for enumerated children that can be handled by this module
228         for( int i = 0; i < Module->nDevices; i ++ )
229         {
230                 // TODO: Have list of unbound enumerated children
231                 for( tUDI_DriverInstance *inst = gpUDI_ActiveInstances; inst; inst = inst->Next )
232                 {
233                         // Loop children
234                         for( tUDI_ChildBinding *child = inst->FirstChild; child; child = child->Next )
235                         {
236                                 if( child->BoundInstance )
237                                         continue ;
238                                 if( Module->Devices[i]->Metalang != child->Metalang )
239                                         continue ;
240                                 // Check for match
241                                 int level = UDI_MA_CheckDeviceMatch(
242                                         Module->Devices[i]->nAttribs, Module->Devices[i]->Attribs,
243                                         child->nAttribs, child->Attribs
244                                         );
245                                 // No match: Continue
246                                 if( level == 0 )
247                                         continue ;
248                                 // Found a match, so create an instance (binding happens async)
249                                 UDI_MA_CreateInstance(Module, inst, child);
250                         }
251                 }
252         }
253 }
254
255 void UDI_MA_TransitionState(tUDI_DriverInstance *Inst, enum eUDI_MAState Src, enum eUDI_MAState Dst)
256 {
257         ASSERT(Inst);
258         if( Inst->CurState != Src )
259                 return ;
260         
261         LOG("Inst %s %p MA state %i->%i",
262                 Inst->Module->ModuleName, Inst, Src, Dst);
263         
264         switch(Dst)
265         {
266         case UDI_MASTATE_USAGEIND:
267                 ASSERT(Dst != UDI_MASTATE_USAGEIND);
268                 break;
269         case UDI_MASTATE_SECBIND:
270                 Inst->CurState = UDI_MASTATE_SECBIND;
271                 for( int i = 1; i < Inst->Module->nRegions; i ++ )
272                 {
273                         // TODO: Bind secondaries to primary
274                         Log_Warning("UDI", "TODO: Bind secondary channels");
275                         //inst->Regions[i]->PriChannel = UDI_CreateChannel_Blank(
276                 }
277                 //UDI_MA_TransitionState(Inst, UDI_MASTATE_SECBIND, UDI_MASTATE_PARENTBIND);
278                 //break;
279         case UDI_MASTATE_PARENTBIND:
280                 Inst->CurState = UDI_MASTATE_PARENTBIND;
281                 if( Inst->Parent )
282                 {
283                         tUDI_DriverModule       *Module = Inst->Module;
284                         tUDI_ChildBinding       *parent_bind = Inst->ParentChildBinding;
285                         // TODO: Handle multi-parent drivers
286                         ASSERTC(Module->nParents, ==, 1);
287                         
288                         // Bind to parent
289                         tUDI_BindOps    *parent = &Module->Parents[0];
290                         udi_channel_t channel = UDI_CreateChannel_Blank(UDI_int_GetMetaLang(Inst->Module, parent->meta_idx));
291                         
292                         UDI_BindChannel(channel,true,  Inst, parent->ops_idx, parent->region_idx, NULL,false,0);
293                         UDI_BindChannel(channel,false,
294                                 Inst->Parent, parent_bind->Ops->ops_idx, parent_bind->BindOps->region_idx,
295                                 NULL, true, parent_bind->ChildID);
296
297                         udi_cb_t        *bind_cb;
298                         if( parent->bind_cb_idx == 0 )
299                                 bind_cb = NULL;
300                         else {
301                                 bind_cb = udi_cb_alloc_internal(Inst, parent->bind_cb_idx, channel);
302                                 if( !bind_cb ) {
303                                         Log_Warning("UDI", "Bind CB index is invalid");
304                                         break;
305                                 }
306                                 UDI_int_ChannelFlip( bind_cb );
307                         }
308
309                          int    n_handles = Module->InitInfo->primary_init_info->per_parent_paths;
310                         udi_buf_path_t  handles[n_handles];
311                         for( int i = 0; i < n_handles; i ++ ) {
312                                 //handles[i] = udi_buf_path_alloc_internal(Inst);
313                                 handles[i] = 0;
314                         }
315                         
316                         udi_channel_event_cb_t *ev_cb = (void*)udi_cb_alloc_internal_v(&cMetaLang_Management,
317                                 UDI_MGMT_CHANNEL_EVENT_CB_NUM, 0, 0, channel);
318                         UDI_GCB(ev_cb)->initiator_context = Inst;
319                         ev_cb->event = UDI_CHANNEL_BOUND;
320                         ev_cb->params.parent_bound.bind_cb = bind_cb;
321                         ev_cb->params.parent_bound.parent_ID = 1;
322                         ev_cb->params.parent_bound.path_handles = handles;
323                         
324                         udi_channel_event_ind(ev_cb);
325                         break;
326                 }
327                 //UDI_MA_TransitionState(Inst, UDI_MASTATE_PARENTBIND, UDI_MASTATE_ENUMCHILDREN);
328                 //break;
329         case UDI_MASTATE_ENUMCHILDREN:
330                 Inst->CurState = UDI_MASTATE_ENUMCHILDREN;
331                 UDI_MA_BeginEnumeration(Inst);
332                 break;
333         case UDI_MASTATE_ACTIVE:
334                 Inst->CurState = UDI_MASTATE_ACTIVE;
335                 Log_Log("UDI", "Driver instance %s %p entered active state",
336                         Inst->Module->ModuleName, Inst);
337                 break;
338         }
339 }
340

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