ipcp.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <adt/hash_table.h>
00038 #include <sys/typefmt.h>
00039 
00040 #include "ipc_desc.h"
00041 #include "proto.h"
00042 #include "trace.h"
00043 #include "ipcp.h"
00044 
00045 #define IPCP_CALLID_SYNC 0
00046 
00047 typedef struct {
00048         sysarg_t phone_hash;
00049         ipc_call_t question;
00050         oper_t *oper;
00051 
00052         ipc_callid_t call_hash;
00053 
00054         link_t link;
00055 } pending_call_t;
00056 
00057 typedef struct {
00058         int server;
00059         proto_t *proto;
00060 } connection_t;
00061 
00062 #define MAX_PHONE 64
00063 connection_t connections[MAX_PHONE];
00064 int have_conn[MAX_PHONE];
00065 
00066 #define PCALL_TABLE_CHAINS 32
00067 hash_table_t pending_calls;
00068 
00069 /*
00070  * Pseudo-protocols
00071  */
00072 proto_t *proto_system;          
00073 proto_t *proto_unknown;         
00075 static hash_index_t pending_call_hash(unsigned long key[]);
00076 static int pending_call_compare(unsigned long key[], hash_count_t keys,
00077     link_t *item);
00078 static void pending_call_remove_callback(link_t *item);
00079 
00080 hash_table_operations_t pending_call_ops = {
00081         .hash = pending_call_hash,
00082         .compare = pending_call_compare,
00083         .remove_callback = pending_call_remove_callback
00084 };
00085 
00086 
00087 static hash_index_t pending_call_hash(unsigned long key[])
00088 {
00089 //      printf("pending_call_hash\n");
00090         return key[0] % PCALL_TABLE_CHAINS;
00091 }
00092 
00093 static int pending_call_compare(unsigned long key[], hash_count_t keys,
00094     link_t *item)
00095 {
00096         pending_call_t *hs;
00097 
00098 //      printf("pending_call_compare\n");
00099         hs = hash_table_get_instance(item, pending_call_t, link);
00100 
00101         // FIXME: this will fail if sizeof(long) < sizeof(void *).
00102         return key[0] == hs->call_hash;
00103 }
00104 
00105 static void pending_call_remove_callback(link_t *item)
00106 {
00107 //      printf("pending_call_remove_callback\n");
00108 }
00109 
00110 
00111 void ipcp_connection_set(int phone, int server, proto_t *proto)
00112 {
00113         if (phone <0 || phone >= MAX_PHONE) return;
00114         connections[phone].server = server;
00115         connections[phone].proto = proto;
00116         have_conn[phone] = 1;
00117 }
00118 
00119 void ipcp_connection_clear(int phone)
00120 {
00121         have_conn[phone] = 0;
00122         connections[phone].server = 0;
00123         connections[phone].proto = NULL;
00124 }
00125 
00126 static void ipc_m_print(proto_t *proto, sysarg_t method)
00127 {
00128         oper_t *oper;
00129 
00130         /* Try system methods first */
00131         oper = proto_get_oper(proto_system, method);
00132 
00133         if (oper == NULL && proto != NULL) {
00134                 /* Not a system method, try the user protocol. */
00135                 oper = proto_get_oper(proto, method);
00136         }
00137 
00138         if (oper != NULL) {
00139                 printf("%s (%" PRIun ")", oper->name, method);
00140                 return;
00141         }
00142 
00143         printf("%" PRIun, method);
00144 }
00145 
00146 void ipcp_init(void)
00147 {
00148         ipc_m_desc_t *desc;
00149         oper_t *oper;
00150 
00151         val_type_t arg_def[OPER_MAX_ARGS] = {
00152                 V_INTEGER,
00153                 V_INTEGER,
00154                 V_INTEGER,
00155                 V_INTEGER,
00156                 V_INTEGER               
00157         };
00158 
00159         /*
00160          * Create a pseudo-protocol 'unknown' that has no known methods.
00161          */
00162         proto_unknown = proto_new("unknown");
00163 
00164         /*
00165          * Create a pseudo-protocol 'system' defining names of system IPC
00166          * methods.
00167          */
00168         proto_system = proto_new("system");
00169 
00170         desc = ipc_methods;
00171         while (desc->number != 0) {
00172                 oper = oper_new(desc->name, OPER_MAX_ARGS, arg_def, V_INTEGER,
00173                         OPER_MAX_ARGS, arg_def);
00174                 proto_add_oper(proto_system, desc->number, oper);
00175 
00176                 ++desc;
00177         }
00178 
00179         hash_table_create(&pending_calls, PCALL_TABLE_CHAINS, 1, &pending_call_ops);
00180 }
00181 
00182 void ipcp_cleanup(void)
00183 {
00184         proto_delete(proto_system);
00185         hash_table_destroy(&pending_calls);
00186 }
00187 
00188 void ipcp_call_out(int phone, ipc_call_t *call, ipc_callid_t hash)
00189 {
00190         pending_call_t *pcall;
00191         proto_t *proto;
00192         unsigned long key[1];
00193         oper_t *oper;
00194         sysarg_t *args;
00195         int i;
00196 
00197         if (have_conn[phone]) proto = connections[phone].proto;
00198         else proto = NULL;
00199 
00200         args = call->args;
00201 
00202         if ((display_mask & DM_IPC) != 0) {
00203                 printf("Call ID: %p, phone: %d, proto: %s, method: ",
00204                     (void *) hash, phone,
00205                     (proto ? proto->name : "n/a"));
00206                 ipc_m_print(proto, IPC_GET_IMETHOD(*call));
00207                 printf(" args: (%" PRIun ", %" PRIun ", %" PRIun ", "
00208                     "%" PRIun ", %" PRIun ")\n",
00209                     args[1], args[2], args[3], args[4], args[5]);
00210         }
00211 
00212 
00213         if ((display_mask & DM_USER) != 0) {
00214 
00215                 if (proto != NULL) {
00216                         oper = proto_get_oper(proto, IPC_GET_IMETHOD(*call));
00217                 } else {
00218                         oper = NULL;
00219                 }
00220 
00221                 if (oper != NULL) {
00222 
00223                         printf("%s(%d).%s", (proto ? proto->name : "n/a"),
00224                             phone, (oper ? oper->name : "unknown"));
00225 
00226                         putchar('(');
00227                         for (i = 1; i <= oper->argc; ++i) {
00228                                 if (i > 1) printf(", ");
00229                                 val_print(args[i], oper->arg_type[i - 1]);
00230                         }
00231                         putchar(')');
00232 
00233                         if (oper->rv_type == V_VOID && oper->respc == 0) {
00234                                 /*
00235                                  * No response data (typically the task will
00236                                  * not be interested in the response).
00237                                  * We will not display response.
00238                                  */
00239                                 putchar('.');
00240                         }
00241 
00242                         putchar('\n');
00243                 }
00244         } else {
00245                 oper = NULL;
00246         }
00247 
00248         /* Store call in hash table for response matching */
00249 
00250         pcall = malloc(sizeof(pending_call_t));
00251         pcall->phone_hash = phone;
00252         pcall->question = *call;
00253         pcall->call_hash = hash;
00254         pcall->oper = oper;
00255 
00256         key[0] = hash;
00257 
00258         hash_table_insert(&pending_calls, key, &pcall->link);
00259 }
00260 
00261 static void parse_answer(ipc_callid_t hash, pending_call_t *pcall,
00262     ipc_call_t *answer)
00263 {
00264         sysarg_t phone;
00265         sysarg_t method;
00266         sysarg_t service;
00267         sysarg_t retval;
00268         proto_t *proto;
00269         int cphone;
00270 
00271         sysarg_t *resp;
00272         oper_t *oper;
00273         int i;
00274 
00275 //      printf("parse_answer\n");
00276 
00277         phone = pcall->phone_hash;
00278         method = IPC_GET_IMETHOD(pcall->question);
00279         retval = IPC_GET_RETVAL(*answer);
00280 
00281         resp = answer->args;
00282 
00283         if ((display_mask & DM_IPC) != 0) {
00284                 printf("Response to %p: retval=%" PRIdn ", args = (%" PRIun ", "
00285                     "%" PRIun ", %" PRIun ", %" PRIun ", %" PRIun ")\n",
00286                     (void *) hash, retval, IPC_GET_ARG1(*answer),
00287                     IPC_GET_ARG2(*answer), IPC_GET_ARG3(*answer),
00288                     IPC_GET_ARG4(*answer), IPC_GET_ARG5(*answer));
00289         }
00290 
00291         if ((display_mask & DM_USER) != 0) {
00292                 oper = pcall->oper;
00293 
00294                 if (oper != NULL && (oper->rv_type != V_VOID || oper->respc > 0)) {
00295                         printf("->");
00296 
00297                         if (oper->rv_type != V_VOID) {
00298                                 putchar(' ');
00299                                 val_print(retval, oper->rv_type);
00300                         }
00301                         
00302                         if (oper->respc > 0) {
00303                                 putchar(' ');
00304                                 putchar('(');
00305                                 for (i = 1; i <= oper->respc; ++i) {
00306                                         if (i > 1) printf(", ");
00307                                         val_print(resp[i], oper->resp_type[i - 1]);
00308                                 }
00309                                 putchar(')');
00310                         }
00311 
00312                         putchar('\n');
00313                 }
00314         }
00315 
00316         if (phone == 0 && method == IPC_M_CONNECT_ME_TO && retval == 0) {
00317                 /* Connected to a service (through NS) */
00318                 service = IPC_GET_ARG1(pcall->question);
00319                 proto = proto_get_by_srv(service);
00320                 if (proto == NULL) proto = proto_unknown;
00321 
00322                 cphone = IPC_GET_ARG5(*answer);
00323                 if ((display_mask & DM_SYSTEM) != 0) {
00324                         printf("Registering connection (phone %d, protocol: %s)\n", cphone,
00325                                 proto->name);
00326                 }
00327                 ipcp_connection_set(cphone, 0, proto);
00328         }
00329 }
00330 
00331 void ipcp_call_in(ipc_call_t *call, ipc_callid_t hash)
00332 {
00333         link_t *item;
00334         pending_call_t *pcall;
00335         unsigned long key[1];
00336 
00337 //      printf("ipcp_call_in()\n");
00338 
00339         if ((hash & IPC_CALLID_ANSWERED) == 0 && hash != IPCP_CALLID_SYNC) {
00340                 /* Not a response */
00341                 if ((display_mask & DM_IPC) != 0) {
00342                         printf("Not a response (hash %p)\n", (void *) hash);
00343                 }
00344                 return;
00345         }
00346 
00347         hash = hash & ~IPC_CALLID_ANSWERED;
00348         key[0] = hash;
00349 
00350         item = hash_table_find(&pending_calls, key);
00351         if (item == NULL) return; // No matching question found
00352 
00353         /*
00354          * Response matched to question.
00355          */
00356         
00357         pcall = hash_table_get_instance(item, pending_call_t, link);
00358         hash_table_remove(&pending_calls, key, 1);
00359 
00360         parse_answer(hash, pcall, call);
00361         free(pcall);
00362 }
00363 
00364 void ipcp_call_sync(int phone, ipc_call_t *call, ipc_call_t *answer)
00365 {
00366         ipcp_call_out(phone, call, IPCP_CALLID_SYNC);
00367         ipcp_call_in(answer, IPCP_CALLID_SYNC);
00368 }
00369 
00370 void ipcp_hangup(int phone, int rc)
00371 {
00372         if ((display_mask & DM_SYSTEM) != 0) {
00373                 printf("Hang phone %d up -> %d\n", phone, rc);
00374                 ipcp_connection_clear(phone);
00375         }
00376 }
00377 

Generated on Thu Jun 2 07:45:43 2011 for HelenOS/USB by  doxygen 1.4.7