socket.c

Go to the documentation of this file.
00001 /* $Id: socket.c 22311 2005-09-23 12:42:39Z fm3 $ */
00002 /*****************************************************************************/
00003 /**
00004  * \file dsi/lib/src/socket.c
00005  *
00006  * \brief Component interface, socket implementation
00007  *
00008  * \author      Lars Reuther <reuther@os.inf.tu-dresden.de>
00009  * \date        07/01/2000
00010  */
00011 /*****************************************************************************/
00012 
00013 /* L4/DROPS includes */
00014 #include <l4/sys/types.h>
00015 #include <l4/sys/consts.h>
00016 #include <l4/env/errno.h>
00017 #include <l4/util/atomic.h>
00018 #include <l4/util/macros.h>
00019 
00020 /* library includes */
00021 #include <l4/dsi/dsi.h>
00022 #include "__socket.h"
00023 #include "__dataspace.h"
00024 #include "__thread.h"
00025 #include "__sync.h"
00026 #include "__event.h"
00027 #include "__config.h"
00028 #include "__debug.h"
00029 
00030 /*****************************************************************************
00031  * global types/structures
00032  *****************************************************************************/
00033 
00034 /***
00035  * \brief field of socket descriptors
00036  */
00037 static dsi_socket_t sockets[DSI_MAX_SOCKETS];
00038 
00039 /**
00040  * \brief next index to search for unused socket
00041  * \ingroup internal
00042  */
00043 static int next_socket = 0;
00044 
00045 /**
00046  * socket->flags contains various types of flags, the upper 4 bits are 
00047  * used for internal stuff.
00048  */
00049 #define SOCKET_FLAGS_USER     0x0FFFFFFFUL
00050 
00051 /*****************************************************************************
00052  * helpers
00053  *****************************************************************************/
00054 
00055 /*****************************************************************************/
00056 /**
00057  * \brief Initialize socket table
00058  * \ingroup internal
00059  */
00060 /*****************************************************************************/
00061 void
00062 dsi_init_sockets(void)
00063 {
00064   int i;
00065 
00066   for (i = 0; i < DSI_MAX_SOCKETS; i++)
00067     sockets[i].flags = DSI_SOCKET_UNUSED;
00068 }
00069 
00070 /*****************************************************************************/
00071 /**
00072  * \brief Find and allocate unused socket in socket table
00073  * \ingroup internal
00074  *
00075  * \return index of socket in socket table, -1 if no socket available
00076  */
00077 /*****************************************************************************/ 
00078 static int
00079 __allocate_socket(void)
00080 {
00081   int i = next_socket;
00082 
00083   /* search unused socket descriptor */
00084   do
00085     {
00086       if (l4util_cmpxchg32(&sockets[i].flags,
00087                            DSI_SOCKET_UNUSED,DSI_SOCKET_USED))
00088         {
00089           /* found */
00090           next_socket = (i + 1) % DSI_MAX_SOCKETS;
00091           break;
00092         }
00093       
00094       i = (i + 1) % DSI_MAX_SOCKETS;
00095     }
00096   while (i != next_socket);
00097 
00098   if (i == next_socket)
00099     return -1;
00100   else
00101     return i;
00102 }
00103 
00104 /*!\brief Check if socket is valid socket descriptor.
00105  * \ingroup component
00106  *
00107  * \param socket        socket descriptor
00108  * \retval              != 0 if socket points to valid socket descriptor
00109  * \retval              0 otherwise
00110  */
00111 int
00112 dsi_is_valid_socket(dsi_socket_t * socket)
00113 {
00114 #if 0
00115   LOGdL(DEBUG_SOCKET,"socket at 0x%08x",(dword_t) socket);
00116 #endif
00117 
00118   if (socket == NULL)
00119     return 0;
00120 
00121   return ((socket >= sockets) &&
00122           (socket < &sockets[DSI_MAX_SOCKETS]) &&
00123           (socket->flags != DSI_SOCKET_UNUSED));
00124 }
00125 
00126 /*!\brief Create new send/receive socket.
00127  * \ingroup socket
00128  *
00129  * This function allocates the necessary dataspaces, maps them, and creates
00130  * the synchronisation thread using dsi_create_sync_thread(). The dataspace
00131  * for the data area can be defined by the caller. Specify DSI_PACKET_MAP to
00132  * indicate a preallocated dataspace.  If no (invalid) control area or
00133  * synchronization thread are specified, they are created.
00134  *
00135  * \param jcp_stream    stream description
00136  * \param cfg           low level stream configuration
00137  * \param ctrl_ds       control area (if invalid, allocate new area)
00138  * \param data_ds       data area
00139  * \param work_id       work thread id
00140  * \param sync_id       synchronization thread id (if invalid, create thread)
00141  * \param flags         socket flags:
00142  *                      - DSI_SOCKET_SEND       create send socket
00143  *                      - DSI_SOCKET_RECEIVE    create receive socket
00144  *                      - DSI_SOCKET_BLOCK      block if no packet is available
00145  *                                              in dsi_packet_get, the default 
00146  *                                              behavior is to return an error
00147  *                      - for more see include/l4/dsi/types.h
00148  *
00149  * \retval ctrl_ds      contains control area
00150  * \retval sync_id      synchronization thread id of newly created thread
00151  * \retval socket       socket descriptor
00152  * \retval 0            success, created socket
00153  * \retval -L4_EINVAL   invalid data area or work thread
00154  * \retval -L4_ENOSOCKET no socket descriptor available
00155  */
00156 int
00157 dsi_socket_create(dsi_jcp_stream_t jcp_stream, dsi_stream_cfg_t cfg,
00158                   l4dm_dataspace_t * ctrl_ds, l4dm_dataspace_t * data_ds,
00159                   l4_threadid_t work_id, l4_threadid_t * sync_id,
00160                   l4_uint32_t flags, dsi_socket_t ** socket)
00161 {
00162   dsi_socket_t * s;
00163   int sid,ret,i;
00164 
00165   *socket = NULL;
00166 
00167   /* sanity checks */
00168   if (l4dm_is_invalid_ds(*data_ds))
00169     {
00170       LOG_Error("DSI: invalid data area!");
00171       return -L4_EINVAL;
00172     }
00173 
00174   LOGdL(DEBUG_SOCKET,"data_ds: %d at "l4util_idfmt, 
00175         data_ds->id, l4util_idstr(data_ds->manager));
00176 
00177   if (l4_is_invalid_id(work_id))
00178     {
00179       LOG_Error("DSI: invalid work thread");
00180       return -L4_EINVAL;
00181     }
00182 
00183   /* allocate new socket descriptor */
00184   sid = __allocate_socket();
00185   if (sid == -1)
00186     {
00187       /* no socket descriptor available */
00188       LOG_Error("DSI: no socket available");
00189       return -DSI_ENOSOCKET;
00190     }
00191   s = &sockets[sid];
00192   s->socket_id = sid;
00193 
00194   /* set user flags */
00195   s->flags |= (flags & SOCKET_FLAGS_USER);
00196 
00197   /* setup synchronization thread */
00198   if (l4_is_invalid_id(*sync_id))
00199     {
00200       /* create new synchronization thread */
00201       ret = dsi_create_sync_thread(s);
00202       if (ret)
00203         {
00204           /* creation failed, release socket */
00205           LOG_Error("DSI: create synchronization thread failed: %s (%d)",
00206                     l4env_errstr(ret), ret);
00207           s->flags = DSI_SOCKET_UNUSED;
00208           return ret;
00209         }
00210       s->flags |=  DSI_SOCKET_FREE_SYNC;
00211       *sync_id = s->sync_th;
00212     }
00213   else
00214     /* use existing synchronization thread */
00215     s->sync_th = *sync_id;
00216 
00217   /* setup control area */
00218   if (l4dm_is_invalid_ds(*ctrl_ds))
00219     {
00220       /* create new one */
00221       LOGdL(DEBUG_SOCKET,"create control area");
00222 
00223       ret = dsi_create_ctrl_area(s,jcp_stream,cfg);
00224       if(ret)
00225         {
00226           LOG_Error("DSI: error creating control area: %s (%d)",
00227                     l4env_errstr(ret), ret);
00228           return ret;
00229         }
00230       s->flags |= DSI_SOCKET_FREE_CTRL;
00231       *ctrl_ds = s->ctrl_ds;
00232 
00233       LOGdL(DEBUG_SOCKET,"control dataspace: %d at "l4util_idfmt, 
00234             ctrl_ds->id, l4util_idstr(ctrl_ds->manager));
00235     }
00236   else 
00237     {
00238       /* use existing */
00239       ret = dsi_set_ctrl_area(s,*ctrl_ds,jcp_stream,cfg);
00240     }
00241   
00242   if (ret)
00243     {
00244       /* creation/setup failed, shutdown sync thread, release socket */
00245       LOG_Error("DSI: create/setup control area failed: %s (%d)",
00246                 l4env_errstr(ret), ret);
00247       if (s->flags & DSI_SOCKET_FREE_SYNC)
00248         dsi_shutdown_sync_thread(s);
00249       s->flags = DSI_SOCKET_UNUSED;
00250       return ret;
00251     }
00252 
00253   /* setup data area */
00254   LOGdL(DEBUG_SOCKET,"setting data area (%d at "l4util_idfmt")...", 
00255         data_ds->id, l4util_idstr(data_ds->manager));
00256 
00257   ret = dsi_set_data_area(s,*data_ds);
00258   if (ret)
00259     {
00260       /* setup failed */
00261       LOG_Error("DSI: setup data area failed: %s (%d)",
00262                 l4env_errstr(ret), ret);
00263       if (s->flags & DSI_SOCKET_FREE_SYNC)
00264         dsi_shutdown_sync_thread(s);
00265       if (s->flags & DSI_SOCKET_FREE_CTRL)
00266         dsi_release_ctrl_area(s);
00267       s->flags = DSI_SOCKET_UNUSED;
00268       return ret;
00269     }
00270 
00271   /* reset event counter */
00272   for (i = 0; i < DSI_MAX_EVENTS; i++)
00273     s->events[i] = 0;
00274 
00275   /* finish socket setup */
00276   s->work_th = work_id;
00277   s->sync_callback = NULL;
00278   s->release_callback = NULL;
00279   s->packet_count = 0;
00280   s->next_packet = 0;
00281   s->next_sg_elem = 0;
00282   s->waiting = 0;
00283   s->clients = NULL;
00284 
00285   /* done */
00286   *socket = s;
00287 
00288   return 0;
00289 }
00290 
00291 /*!\brief Close a socket.
00292  * \ingroup socket
00293  *
00294  * This function stops the synchronisation thread and frees the allocated
00295  * dataspaces. If the data dataspace was allocated prior to calling
00296  * dsi_socket_create(), it will not be deleted.
00297  *
00298  * \param socket        socket descriptor
00299  * \retval 0            success, closed socket
00300  * \retval -L4_EINVAL   invalid socket descriptor
00301  *
00302  */
00303 int
00304 dsi_socket_close(dsi_socket_t * socket)
00305 {
00306   /* sanity check */
00307   if (!dsi_is_valid_socket(socket))
00308     return -L4_EINVAL;
00309 
00310   /* shutdown synchronization thread */
00311   if (socket->flags & DSI_SOCKET_FREE_SYNC)
00312     dsi_shutdown_sync_thread(socket);
00313 
00314   /* detach/close control dataspace */
00315   dsi_release_ctrl_area(socket);
00316 
00317   /* detach data dataspace */
00318   dsi_release_data_area(socket);
00319 
00320   /* mark socket unused */
00321   socket->flags = DSI_SOCKET_UNUSED;
00322 
00323   /* done */
00324   return 0;
00325 }
00326 
00327 /*!\brief Stop a socket.
00328  * \ingroup socket
00329  *
00330  * This function stops the synchronisation thread. This ensures that
00331  * the synchronization thread does not touch the control area any
00332  * longer. This would be a problem if the sender sends a
00333  * commit-message and destroys the control area afterwards: The
00334  * receiver is scheduled (depending on the priorities), sees the received
00335  * message and tries to access the (unmapped) control area.
00336  *
00337  * \param socket        socket descriptor
00338  * \retval 0            success, closed socket
00339  * \retval -L4_EINVAL   invalid socket descriptor
00340  * */
00341 int
00342 dsi_socket_stop(dsi_socket_t * socket)
00343 {
00344   /* sanity check */
00345   if (!dsi_is_valid_socket(socket))
00346     return -L4_EINVAL;
00347 
00348   /* shutdown synchronization thread */
00349   if (socket->flags & DSI_SOCKET_FREE_SYNC)
00350     dsi_shutdown_sync_thread(socket);
00351   socket->flags &= ~DSI_SOCKET_FREE_SYNC;
00352 
00353   /* done */
00354   return 0;
00355 }
00356 
00357 /*!\brief Connect the socket to a partner
00358  * \ingroup socket
00359  *
00360  * This function enters the data describing a remote endpoint into the socket
00361  * structure. No start IPC or similar things are performed here.
00362  *
00363  * \param socket        local socket descriptor
00364  * \param remote_socket remote socket reference
00365  *
00366  * \retval 0            success, socket connected to remote socket
00367  * \retval -L4_EINVAL   invalid socket reference
00368  *
00369  * \see dsi_socket_connect_ref().
00370  */
00371 int 
00372 dsi_socket_connect(dsi_socket_t * socket,
00373                    const dsi_socket_ref_t * remote_socket)
00374 {
00375   int ret;
00376 
00377   /* sanity check */
00378   if (!dsi_is_valid_socket(socket))
00379     {
00380       LOG_Error("DSI: invalid socket");
00381       return -L4_EINVAL;
00382     }
00383   
00384   if (l4_is_invalid_id(remote_socket->work_th) || 
00385       l4_is_invalid_id(remote_socket->sync_th) || 
00386       !l4_task_equal(remote_socket->work_th,remote_socket->sync_th))
00387     {
00388       LOG_printf("work "l4util_idfmt", sync "l4util_idfmt"\n",
00389              l4util_idstr(remote_socket->work_th),
00390              l4util_idstr(remote_socket->sync_th));
00391       LOG_Error("DSI: invalid data in socket descriptor");
00392       return -L4_EINVAL;
00393     }
00394 
00395   LOGdL(DEBUG_CONNECT,"connecting socket %d",socket->socket_id);
00396   LOGdL(DEBUG_CONNECT,"remote: %d, "l4util_idfmt", "l4util_idfmt,
00397         remote_socket->socket,
00398         l4util_idstr(remote_socket->work_th),
00399         l4util_idstr(remote_socket->sync_th));
00400 
00401   /* set remote socket */
00402   socket->remote_socket.socket = remote_socket->socket;
00403   socket->remote_socket.work_th = remote_socket->work_th;
00404   socket->remote_socket.sync_th = remote_socket->sync_th;
00405 
00406   /* wakeup synchronization thread */
00407   ret = dsi_start_sync_thread(socket);
00408   if (ret)
00409     {
00410       LOG_Error("DSI: connect: wakeup synchronization thread failed!");
00411       return ret;
00412     }
00413 
00414   /* done */
00415   return 0;
00416 }
00417 
00418 /*!\brief Set callback for syncronisation events.
00419  * \ingroup socket
00420  *
00421  * The specified function will be called if synchronisation IPC is necessary
00422  * due to blocking. The call will be performed inside packet_get(),
00423  * immediately before the request for sync-IPC is sent to the peer.
00424  *
00425  * \param socket        socket descriptor
00426  * \param func          synchronization callback function
00427  *
00428  * \retval 0            success, registered new callback function
00429  * \retval -L4_EINVAL   invalid socket descriptor / callback function
00430  */
00431 int
00432 dsi_socket_set_sync_callback(dsi_socket_t * socket, 
00433                              dsi_sync_callback_fn_t func)
00434 {
00435   /* sanity check */
00436   if (!dsi_is_valid_socket(socket) || (func == NULL))
00437     return -L4_EINVAL;
00438 
00439   /* set callback function */
00440   socket->sync_callback = func; 
00441   socket->flags |= DSI_SOCKET_SYNC_CALLBACK;
00442 
00443   /* done */
00444   return 0;
00445 }
00446 
00447 /*!\brief Set callback for release events.
00448  * \ingroup socket
00449  *
00450  * The specified function will be called if the release event is triggered.
00451  * The release event will be triggered when the partner commits a packet
00452  * with dsi_packet_commit() and release notification is set for this packet
00453  * or the entire stream (flag #DSI_PACKET_RELEASE_CALLBACK).
00454  *
00455  * \param socket        socket descriptor
00456  * \param func          packet release callback function
00457  *
00458  * \retval 0            success, registered new callback function
00459  * \retval -L4_EINVAL   invalid socket descriptor / callback function
00460  */
00461 int
00462 dsi_socket_set_release_callback(dsi_socket_t * socket,
00463                                 dsi_release_callback_fn_t func)
00464 {
00465   /* sanity check */
00466   if (!dsi_is_valid_socket(socket) || (func == NULL))
00467     return -L4_EINVAL;
00468 
00469   /* set callback function */
00470   socket->release_callback = func;
00471   socket->flags |= DSI_SOCKET_RELEASE_CALLBACK;
00472 
00473   /* done */
00474   return 0;
00475 }
00476 
00477 /*!\brief Obtain a reference to a socket
00478  * \ingroup socket
00479  *
00480  * This function returns a reference to a socket which can be passed to
00481  * another task. The other task can use the reference to communicate with this
00482  * socket (see dsi_socket_connect()).
00483  *
00484  * \param socket        socket descriptor
00485  *
00486  * \retval ref          contains socket reference
00487  * \retval 0            success
00488  * \retval -L4_EINVAL   invalid socket descriptor
00489  */
00490 int
00491 dsi_socket_get_ref(dsi_socket_t * socket, dsi_socket_ref_t * ref)
00492 {
00493   /* sanity check */
00494   if (!dsi_is_valid_socket(socket))
00495     return -L4_EINVAL;
00496   
00497   /* create external reference */
00498   ref->socket = socket->socket_id;
00499   ref->work_th = socket->work_th;
00500   ref->sync_th = socket->sync_th;
00501   ref->event_th = dsi_get_event_thread_id();
00502 
00503   return 0;
00504 }
00505 
00506 /*!\brief Get the socket of a given socket-ID.
00507  * \ingroup socket
00508  *
00509  * To get the socket of a socket reference \c x (\a dsi_socket_ref_t), use
00510  * \c dsi_socket_get_descriptor(x.socket,&s). Note, the socket
00511  * reference must refer to a socket in the own task.
00512  *
00513  * \param id            socket id
00514  *
00515  * \retval socket     contains the socket
00516  * \retval 0          success
00517  * \retval -L4_EINVAL invalid socket id
00518  */
00519 int
00520 dsi_socket_get_descriptor(dsi_socketid_t id, dsi_socket_t ** socket)
00521 {
00522   /* check socket id */
00523   if ((id < 0) || (id >= DSI_MAX_SOCKETS))
00524     return -L4_EINVAL;
00525 
00526   if (sockets[id].flags == DSI_SOCKET_UNUSED)
00527     return -L4_EINVAL;
00528 
00529   /* return socket descriptor */
00530   *socket = &sockets[id];
00531   return 0;
00532 }
00533 
00534 /*!\brief Return start address and size of data area.
00535  * \ingroup socket
00536  *        
00537  * On socket creation, the data area is mapped into the local address space.
00538  * Use this function should be used to get a pointer to the mapped area.
00539  *
00540  * \param socket        socket descriptor
00541  *
00542  * \retval data_area  start address of data area
00543  * \retval area_size  size of data area
00544  * \retval 0          success
00545  * \retval -L4_EINVAL invalid socket descriptor
00546  */
00547 int
00548 dsi_socket_get_data_area(dsi_socket_t * socket, void **data_area,
00549                          l4_size_t * area_size)
00550 {
00551   /* check socket descriptor */
00552   if (!dsi_is_valid_socket(socket))
00553     return -L4_EINVAL;
00554 
00555   /* return data area start address */
00556   *data_area = socket->data_area;
00557   *area_size = socket->data_size;
00558 
00559   return 0;
00560 }
00561 
00562 /*****************************************************************************/
00563 /**
00564  * \brief Set socket flags.
00565  * \ingroup socket
00566  * 
00567  * \param  socket        Socket descriptor
00568  * \param  flags         Flags to set
00569  *      
00570  * \return 0 on success (modified socket flags), error code otherwise:
00571  *         - \c -L4_EINVAL  invalid socket descriptor
00572  */
00573 /*****************************************************************************/ 
00574 int
00575 dsi_socket_set_flags(dsi_socket_t * socket, l4_uint32_t flags)
00576 {
00577   /* check socket descriptor */
00578   if (!dsi_is_valid_socket(socket))
00579     return -L4_EINVAL;
00580 
00581   /* modify flags */
00582   socket->flags |= (flags & DSI_SOCKET_USER_FLAGS);
00583 
00584   /* done */
00585   return 0;
00586 }
00587 
00588 /*****************************************************************************/
00589 /**
00590  * \brief Clear socket flags.
00591  * \ingroup socket
00592  * 
00593  * \param  socket        Socket descriptor
00594  * \param  flags         Flags to clear
00595  *      
00596  * \return 0 on success (modified socket flags), error code otherwise:
00597  *         - \c -L4_EINVAL  invalid socket descriptor
00598  */
00599 /*****************************************************************************/ 
00600 int
00601 dsi_socket_clear_flags(dsi_socket_t * socket, l4_uint32_t flags)
00602 {
00603   /* check socket descriptor */
00604   if (!dsi_is_valid_socket(socket))
00605     return -L4_EINVAL;
00606 
00607   /* modify flags */
00608   socket->flags &= ~(flags & DSI_SOCKET_USER_FLAGS);
00609 
00610   /* done */
00611   return 0;
00612 }
00613 
00614 /*****************************************************************************/
00615 /**
00616  * \brief Test socket flag.
00617  * \ingroup socket
00618  * 
00619  * \param  socket        Socket descriptor
00620  * \param  flag          Flag
00621  *      
00622  * \return != 0 if flag is set, 0 if flag is not set or invalid socket 
00623  *         descriptor
00624  */
00625 /*****************************************************************************/ 
00626 int
00627 dsi_socket_test_flag(dsi_socket_t * socket, l4_uint32_t flag)
00628 {
00629   /* check socket descriptor */
00630   if (!dsi_is_valid_socket(socket))
00631     return 0;
00632   
00633   /* test flag */
00634   return (socket->flags & (flag & DSI_SOCKET_USER_FLAGS));
00635 }
00636 
00637 /*****************************************************************************/
00638 /**
00639  * \brief Set event
00640  * \ingroup socket
00641  * 
00642  * \param  socket        Socket descriptor
00643  * \param  events        Event mask
00644  *      
00645  * \return 0 on success (called event signalling thread), error code otherwise:
00646  *         - \c -L4_EINVAL  invalid socket descriptor
00647  *         - \c -L4_EIPC    IPC error calling signalling thread
00648  *
00649  * This function queues a given event for the socket. The event can be
00650  * waited for at the client application using the dsi_event_wait()
00651  * function.
00652  *
00653  * This function just calls dsi_event_set().
00654  */
00655 /****************************************************************************/ 
00656 int 
00657 dsi_socket_set_event(dsi_socket_t * socket, l4_uint32_t events)
00658 {
00659   /* check socket descriptor */
00660   if (!dsi_is_valid_socket(socket))
00661     return -L4_EINVAL;
00662 
00663   LOGdL(DEBUG_EVENT,"set events 0x%08x",events);
00664 
00665   /* set event */
00666   return dsi_event_set(socket->socket_id,events);
00667 }
00668 
00669 /*****************************************************************************/
00670 /**
00671  * \brief   Return number of packets in ring list
00672  * \ingroup socket
00673  * 
00674  * \param   socket       Socket descriptor
00675  *      
00676  * \return  Number of packest in socket ring list (> 0), error code otherwise:
00677  *          - -#L4_EINVAL invalid socket descriptor
00678  */
00679 /*****************************************************************************/ 
00680 int
00681 dsi_socket_get_packet_num(dsi_socket_t * socket)
00682 {
00683   /* check socket descriptor */
00684   if (!dsi_is_valid_socket(socket))
00685     return -L4_EINVAL;
00686 
00687   return socket->num_packets;
00688 }
00689 
00690 /*****************************************************************************/
00691 /**
00692  * \brief   Return number of committed send packets in ring list
00693  * \ingroup socket
00694  *
00695  * \param   socket       Socket descriptor
00696  *      
00697  * \return  Number of committed send packets in ring list (> 0),
00698  *          error code otherwise:
00699  *          - -#L4_EINVAL invalid socket descriptor
00700  */
00701 /*****************************************************************************/ 
00702 int
00703 dsi_socket_get_num_committed_packets(dsi_socket_t * socket)
00704 {
00705   /* check socket descriptor */
00706   if (!dsi_is_valid_socket(socket))
00707     return -L4_EINVAL;
00708 
00709   return socket->header->packets_committed;
00710 }

Generated on Wed Apr 11 06:40:13 2012 for DSI - Drops Streaming Interface by  doxygen 1.5.6