Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/drv/generic/driver.c

    r2a770a35 r228e490  
    11/*
    22 * Copyright (c) 2010 Lenka Trochtova
    3  * Copyright (c) 2011 Jiri Svoboda
    43 * All rights reserved.
    54 *
     
    5049#include <errno.h>
    5150#include <inttypes.h>
    52 #include <devman.h>
    5351
    5452#include <ipc/driver.h>
    5553
    56 #include "dev_iface.h"
    57 #include "ddf/driver.h"
    58 #include "ddf/interrupt.h"
    59 
    60 /** Driver structure */
     54#include "driver.h"
     55
     56/* driver structure */
     57
    6158static driver_t *driver;
    6259
    63 /** Devices */
    64 LIST_INITIALIZE(functions);
    65 FIBRIL_MUTEX_INITIALIZE(functions_mutex);
    66 
    67 /** Interrupts */
     60/* devices */
     61
     62LIST_INITIALIZE(devices);
     63FIBRIL_MUTEX_INITIALIZE(devices_mutex);
     64
     65/* interrupts */
     66
    6867static interrupt_context_list_t interrupt_contexts;
    6968
     
    7978};
    8079
    81 static ddf_dev_t *create_device(void);
    82 static void delete_device(ddf_dev_t *);
    83 static remote_handler_t *function_get_default_handler(ddf_fun_t *);
    84 static void *function_get_ops(ddf_fun_t *, dev_inferface_idx_t);
    8580
    8681static void driver_irq_handler(ipc_callid_t iid, ipc_call_t *icall)
     
    9085       
    9186        ctx = find_interrupt_context_by_id(&interrupt_contexts, id);
    92         if (ctx != NULL && ctx->handler != NULL)
     87        if (NULL != ctx && NULL != ctx->handler)
    9388                (*ctx->handler)(ctx->dev, iid, icall);
    9489}
    9590
    96 interrupt_context_t *create_interrupt_context(void)
    97 {
    98         interrupt_context_t *ctx;
    99        
    100         ctx = (interrupt_context_t *) malloc(sizeof(interrupt_context_t));
    101         if (ctx != NULL)
    102                 memset(ctx, 0, sizeof(interrupt_context_t));
    103        
    104         return ctx;
    105 }
    106 
    107 void delete_interrupt_context(interrupt_context_t *ctx)
    108 {
    109         if (ctx != NULL)
    110                 free(ctx);
    111 }
    112 
    113 void init_interrupt_context_list(interrupt_context_list_t *list)
    114 {
    115         memset(list, 0, sizeof(interrupt_context_list_t));
    116         fibril_mutex_initialize(&list->mutex);
    117         list_initialize(&list->contexts);
    118 }
    119 
    120 void
    121 add_interrupt_context(interrupt_context_list_t *list, interrupt_context_t *ctx)
    122 {
    123         fibril_mutex_lock(&list->mutex);
    124         ctx->id = list->curr_id++;
    125         list_append(&ctx->link, &list->contexts);
    126         fibril_mutex_unlock(&list->mutex);
    127 }
    128 
    129 void remove_interrupt_context(interrupt_context_list_t *list,
    130     interrupt_context_t *ctx)
    131 {
    132         fibril_mutex_lock(&list->mutex);
    133         list_remove(&ctx->link);
    134         fibril_mutex_unlock(&list->mutex);
    135 }
    136 
    137 interrupt_context_t *
    138 find_interrupt_context_by_id(interrupt_context_list_t *list, int id)
    139 {
    140         fibril_mutex_lock(&list->mutex);
    141        
    142         link_t *link = list->contexts.next;
    143         interrupt_context_t *ctx;
    144        
    145         while (link != &list->contexts) {
    146                 ctx = list_get_instance(link, interrupt_context_t, link);
    147                 if (ctx->id == id) {
    148                         fibril_mutex_unlock(&list->mutex);
    149                         return ctx;
    150                 }
    151                 link = link->next;
    152         }
    153        
    154         fibril_mutex_unlock(&list->mutex);
    155         return NULL;
    156 }
    157 
    158 interrupt_context_t *
    159 find_interrupt_context(interrupt_context_list_t *list, ddf_dev_t *dev, int irq)
    160 {
    161         fibril_mutex_lock(&list->mutex);
    162        
    163         link_t *link = list->contexts.next;
    164         interrupt_context_t *ctx;
    165        
    166         while (link != &list->contexts) {
    167                 ctx = list_get_instance(link, interrupt_context_t, link);
    168                 if (ctx->irq == irq && ctx->dev == dev) {
    169                         fibril_mutex_unlock(&list->mutex);
    170                         return ctx;
    171                 }
    172                 link = link->next;
    173         }
    174        
    175         fibril_mutex_unlock(&list->mutex);
    176         return NULL;
    177 }
    178 
    179 
    18091int
    181 register_interrupt_handler(ddf_dev_t *dev, int irq, interrupt_handler_t *handler,
     92register_interrupt_handler(device_t *dev, int irq, interrupt_handler_t *handler,
    18293    irq_code_t *pseudocode)
    18394{
     
    190101        add_interrupt_context(&interrupt_contexts, ctx);
    191102       
    192         if (pseudocode == NULL)
     103        if (NULL == pseudocode)
    193104                pseudocode = &default_pseudocode;
    194105       
    195         int res = register_irq(irq, dev->handle, ctx->id, pseudocode);
    196         if (res != EOK) {
     106        int res = ipc_register_irq(irq, dev->handle, ctx->id, pseudocode);
     107        if (0 != res) {
    197108                remove_interrupt_context(&interrupt_contexts, ctx);
    198109                delete_interrupt_context(ctx);
     
    202113}
    203114
    204 int unregister_interrupt_handler(ddf_dev_t *dev, int irq)
     115int unregister_interrupt_handler(device_t *dev, int irq)
    205116{
    206117        interrupt_context_t *ctx = find_interrupt_context(&interrupt_contexts,
    207118            dev, irq);
    208         int res = unregister_irq(irq, dev->handle);
    209        
    210         if (ctx != NULL) {
     119        int res = ipc_unregister_irq(irq, dev->handle);
     120
     121        if (NULL != ctx) {
    211122                remove_interrupt_context(&interrupt_contexts, ctx);
    212123                delete_interrupt_context(ctx);
    213124        }
    214        
    215125        return res;
    216126}
    217127
    218 static void add_to_functions_list(ddf_fun_t *fun)
    219 {
    220         fibril_mutex_lock(&functions_mutex);
    221         list_append(&fun->link, &functions);
    222         fibril_mutex_unlock(&functions_mutex);
    223 }
    224 
    225 static void remove_from_functions_list(ddf_fun_t *fun)
    226 {
    227         fibril_mutex_lock(&functions_mutex);
    228         list_remove(&fun->link);
    229         fibril_mutex_unlock(&functions_mutex);
    230 }
    231 
    232 static ddf_fun_t *driver_get_function(link_t *functions, devman_handle_t handle)
    233 {
    234         ddf_fun_t *fun = NULL;
    235        
    236         fibril_mutex_lock(&functions_mutex);
    237         link_t *link = functions->next;
    238        
    239         while (link != functions) {
    240                 fun = list_get_instance(link, ddf_fun_t, link);
    241                 if (fun->handle == handle) {
    242                         fibril_mutex_unlock(&functions_mutex);
    243                         return fun;
     128static void add_to_devices_list(device_t *dev)
     129{
     130        fibril_mutex_lock(&devices_mutex);
     131        list_append(&dev->link, &devices);
     132        fibril_mutex_unlock(&devices_mutex);
     133}
     134
     135static void remove_from_devices_list(device_t *dev)
     136{
     137        fibril_mutex_lock(&devices_mutex);
     138        list_remove(&dev->link);
     139        fibril_mutex_unlock(&devices_mutex);
     140}
     141
     142static device_t * driver_get_device(link_t *devices, devman_handle_t handle)
     143{
     144        device_t *dev = NULL;
     145       
     146        fibril_mutex_lock(&devices_mutex);
     147        link_t *link = devices->next;
     148        while (link != devices) {
     149                dev = list_get_instance(link, device_t, link);
     150                if (handle == dev->handle) {
     151                        fibril_mutex_unlock(&devices_mutex);
     152                        return dev;
    244153                }
    245                
    246154                link = link->next;
    247155        }
    248        
    249         fibril_mutex_unlock(&functions_mutex);
    250        
     156        fibril_mutex_unlock(&devices_mutex);
     157
    251158        return NULL;
    252159}
     
    255162{
    256163        char *dev_name = NULL;
    257         int res;
    258        
    259         devman_handle_t dev_handle = IPC_GET_ARG1(*icall);
    260         devman_handle_t parent_fun_handle = IPC_GET_ARG2(*icall);
    261        
    262         ddf_dev_t *dev = create_device();
     164        int res = EOK;
     165       
     166        devman_handle_t dev_handle =  IPC_GET_ARG1(*icall);
     167        devman_handle_t parent_dev_handle = IPC_GET_ARG2(*icall);
     168   
     169        device_t *dev = create_device();
    263170        dev->handle = dev_handle;
    264 
     171       
    265172        async_data_write_accept((void **) &dev_name, true, 0, 0, 0, 0);
    266173        dev->name = dev_name;
    267 
    268         /*
    269          * Currently not used, parent fun handle is stored in context
    270          * of the connection to the parent device driver.
    271          */
    272         (void) parent_fun_handle;
     174       
     175        add_to_devices_list(dev);
     176        dev->parent = driver_get_device(&devices, parent_dev_handle);
    273177       
    274178        res = driver->driver_ops->add_device(dev);
    275         if (res == EOK) {
     179        if (0 == res) {
    276180                printf("%s: new device with handle=%" PRIun " was added.\n",
    277181                    driver->name, dev_handle);
     
    279183                printf("%s: failed to add a new device with handle = %" PRIun ".\n",
    280184                    driver->name, dev_handle);
     185                remove_from_devices_list(dev);
    281186                delete_device(dev);
    282187        }
    283188       
    284         async_answer_0(iid, res);
     189        ipc_answer_0(iid, res);
    285190}
    286191
     
    288193{
    289194        /* Accept connection */
    290         async_answer_0(iid, EOK);
    291        
     195        ipc_answer_0(iid, EOK);
     196
    292197        bool cont = true;
    293198        while (cont) {
    294199                ipc_call_t call;
    295200                ipc_callid_t callid = async_get_call(&call);
    296                
     201
    297202                switch (IPC_GET_IMETHOD(call)) {
    298203                case IPC_M_PHONE_HUNGUP:
     
    303208                        break;
    304209                default:
    305                         async_answer_0(callid, ENOENT);
     210                        ipc_answer_0(callid, ENOENT);
    306211                }
    307212        }
     
    321226         */
    322227        devman_handle_t handle = IPC_GET_ARG2(*icall);
    323         ddf_fun_t *fun = driver_get_function(&functions, handle);
    324 
    325         if (fun == NULL) {
    326                 printf("%s: driver_connection_gen error - no function with handle"
     228        device_t *dev = driver_get_device(&devices, handle);
     229
     230        if (dev == NULL) {
     231                printf("%s: driver_connection_gen error - no device with handle"
    327232                    " %" PRIun " was found.\n", driver->name, handle);
    328                 async_answer_0(iid, ENOENT);
     233                ipc_answer_0(iid, ENOENT);
    329234                return;
    330235        }
     
    335240         * use the device.
    336241         */
    337        
     242
    338243        int ret = EOK;
    339         /* Open device function */
    340         if (fun->ops != NULL && fun->ops->open != NULL)
    341                 ret = (*fun->ops->open)(fun);
    342        
    343         async_answer_0(iid, ret);
    344         if (ret != EOK)
     244        /* open the device */
     245        if (NULL != dev->ops && NULL != dev->ops->open)
     246                ret = (*dev->ops->open)(dev);
     247       
     248        ipc_answer_0(iid, ret);
     249        if (EOK != ret)
    345250                return;
    346        
     251
    347252        while (1) {
    348253                ipc_callid_t callid;
     
    353258               
    354259                switch  (method) {
    355                 case IPC_M_PHONE_HUNGUP:
    356                         /* Close device function */
    357                         if (fun->ops != NULL && fun->ops->close != NULL)
    358                                 (*fun->ops->close)(fun);
    359                         async_answer_0(callid, EOK);
     260                case IPC_M_PHONE_HUNGUP:               
     261                        /* close the device */
     262                        if (NULL != dev->ops && NULL != dev->ops->close)
     263                                (*dev->ops->close)(dev);
     264                        ipc_answer_0(callid, EOK);
    360265                        return;
    361                 default:
     266                default:               
    362267                        /* convert ipc interface id to interface index */
    363268                       
     
    366271                        if (!is_valid_iface_idx(iface_idx)) {
    367272                                remote_handler_t *default_handler =
    368                                     function_get_default_handler(fun);
    369                                 if (default_handler != NULL) {
    370                                         (*default_handler)(fun, callid, &call);
     273                                    device_get_default_handler(dev);
     274                                if (NULL != default_handler) {
     275                                        (*default_handler)(dev, callid, &call);
    371276                                        break;
    372277                                }
    373                                
    374278                                /*
    375                                  * Function has no such interface and
     279                                 * This is not device's interface and the
    376280                                 * default handler is not provided.
    377281                                 */
     
    379283                                    "invalid interface id %d.",
    380284                                    driver->name, iface_idx);
    381                                 async_answer_0(callid, ENOTSUP);
     285                                ipc_answer_0(callid, ENOTSUP);
    382286                                break;
    383287                        }
     288
     289                        /* calling one of the device's interfaces */
    384290                       
    385                         /* calling one of the function's interfaces */
    386                        
    387                         /* Get the interface ops structure. */
    388                         void *ops = function_get_ops(fun, iface_idx);
    389                         if (ops == NULL) {
     291                        /* get the device interface structure */
     292                        void *iface = device_get_iface(dev, iface_idx);
     293                        if (NULL == iface) {
    390294                                printf("%s: driver_connection_gen error - ",
    391295                                    driver->name);
    392                                 printf("Function with handle %" PRIun " has no interface "
     296                                printf("device with handle %" PRIun " has no interface "
    393297                                    "with id %d.\n", handle, iface_idx);
    394                                 async_answer_0(callid, ENOTSUP);
     298                                ipc_answer_0(callid, ENOTSUP);
    395299                                break;
    396300                        }
    397                        
     301
    398302                        /*
    399303                         * Get the corresponding interface for remote request
    400304                         * handling ("remote interface").
    401305                         */
    402                         remote_iface_t *rem_iface = get_remote_iface(iface_idx);
    403                         assert(rem_iface != NULL);
    404                        
     306                        remote_iface_t* rem_iface = get_remote_iface(iface_idx);
     307                        assert(NULL != rem_iface);
     308
    405309                        /* get the method of the remote interface */
    406310                        sysarg_t iface_method_idx = IPC_GET_ARG1(call);
    407311                        remote_iface_func_ptr_t iface_method_ptr =
    408312                            get_remote_method(rem_iface, iface_method_idx);
    409                         if (iface_method_ptr == NULL) {
     313                        if (NULL == iface_method_ptr) {
    410314                                // the interface has not such method
    411315                                printf("%s: driver_connection_gen error - "
    412316                                    "invalid interface method.", driver->name);
    413                                 async_answer_0(callid, ENOTSUP);
     317                                ipc_answer_0(callid, ENOTSUP);
    414318                                break;
    415319                        }
     
    419323                         * receive parameters from the remote client and it will
    420324                         * pass it to the corresponding local interface method
    421                          * associated with the function by its driver.
     325                         * associated with the device by its driver.
    422326                         */
    423                         (*iface_method_ptr)(fun, ops, callid, &call);
     327                        (*iface_method_ptr)(dev, iface, callid, &call);
    424328                        break;
    425329                }
     
    436340        driver_connection_gen(iid, icall, false);
    437341}
     342
    438343
    439344/** Function for handling connections to device driver. */
     
    443348        switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
    444349        case DRIVER_DEVMAN:
    445                 /* Handle request from device manager */
     350                /* handle PnP events from device manager */
    446351                driver_connection_devman(iid, icall);
    447352                break;
    448353        case DRIVER_DRIVER:
    449                 /* Handle request from drivers of child devices */
     354                /* handle request from drivers of child devices */
    450355                driver_connection_driver(iid, icall);
    451356                break;
    452357        case DRIVER_CLIENT:
    453                 /* Handle request from client applications */
     358                /* handle requests from client applications */
    454359                driver_connection_client(iid, icall);
    455360                break;
     361
    456362        default:
    457363                /* No such interface */
    458                 async_answer_0(iid, ENOENT);
    459         }
    460 }
    461 
    462 /** Create new device structure.
     364                ipc_answer_0(iid, ENOENT);
     365        }
     366}
     367
     368int child_device_register(device_t *child, device_t *parent)
     369{
     370        assert(NULL != child->name);
     371
     372        int res;
     373       
     374        add_to_devices_list(child);
     375        res = devman_child_device_register(child->name, &child->match_ids,
     376            parent->handle, &child->handle);
     377        if (EOK == res)
     378                return res;
     379        remove_from_devices_list(child);       
     380        return res;
     381}
     382
     383/** Wrapper for child_device_register for devices with single match id.
    463384 *
    464  * @return              The device structure.
    465  */
    466 static ddf_dev_t *create_device(void)
    467 {
    468         ddf_dev_t *dev;
    469 
    470         dev = malloc(sizeof(ddf_dev_t));
    471         if (dev == NULL)
    472                 return NULL;
    473 
    474         memset(dev, 0, sizeof(ddf_dev_t));
    475         return dev;
    476 }
    477 
    478 /** Create new function structure.
    479  *
    480  * @return              The device structure.
    481  */
    482 static ddf_fun_t *create_function(void)
    483 {
    484         ddf_fun_t *fun;
    485 
    486         fun = calloc(1, sizeof(ddf_fun_t));
    487         if (fun == NULL)
    488                 return NULL;
    489 
    490         init_match_ids(&fun->match_ids);
    491         link_initialize(&fun->link);
    492 
    493         return fun;
    494 }
    495 
    496 /** Delete device structure.
    497  *
    498  * @param dev           The device structure.
    499  */
    500 static void delete_device(ddf_dev_t *dev)
    501 {
    502         free(dev);
    503 }
    504 
    505 /** Delete device structure.
    506  *
    507  * @param dev           The device structure.
    508  */
    509 static void delete_function(ddf_fun_t *fun)
    510 {
    511         clean_match_ids(&fun->match_ids);
    512         if (fun->name != NULL)
    513                 free(fun->name);
    514         free(fun);
    515 }
    516 
    517 /** Create a DDF function node.
    518  *
    519  * Create a DDF function (in memory). Both child devices and external clients
    520  * communicate with a device via its functions.
    521  *
    522  * The created function node is fully formed, but only exists in the memory
    523  * of the client task. In order to be visible to the system, the function
    524  * must be bound using ddf_fun_bind().
    525  *
    526  * This function should only fail if there is not enough free memory.
    527  * Specifically, this function succeeds even if @a dev already has
    528  * a (bound) function with the same name.
    529  *
    530  * Type: A function of type fun_inner indicates that DDF should attempt
    531  * to attach child devices to the function. fun_exposed means that
    532  * the function should be exported to external clients (applications).
    533  *
    534  * @param dev           Device to which we are adding function
    535  * @param ftype         Type of function (fun_inner or fun_exposed)
    536  * @param name          Name of function
    537  *
    538  * @return              New function or @c NULL if memory is not available
    539  */
    540 ddf_fun_t *ddf_fun_create(ddf_dev_t *dev, fun_type_t ftype, const char *name)
    541 {
    542         ddf_fun_t *fun;
    543 
    544         fun = create_function();
    545         if (fun == NULL)
    546                 return NULL;
    547 
    548         fun->bound = false;
    549         fun->dev = dev;
    550         fun->ftype = ftype;
    551 
    552         fun->name = str_dup(name);
    553         if (fun->name == NULL) {
    554                 delete_function(fun);
    555                 return NULL;
    556         }
    557 
    558         return fun;
    559 }
    560 
    561 /** Destroy DDF function node.
    562  *
    563  * Destroy a function previously created with ddf_fun_create(). The function
    564  * must not be bound.
    565  *
    566  * @param fun           Function to destroy
    567  */
    568 void ddf_fun_destroy(ddf_fun_t *fun)
    569 {
    570         assert(fun->bound == false);
    571         delete_function(fun);
    572 }
    573 
    574 static void *function_get_ops(ddf_fun_t *fun, dev_inferface_idx_t idx)
    575 {
    576         assert(is_valid_iface_idx(idx));
    577         if (fun->ops == NULL)
    578                 return NULL;
    579         return fun->ops->interfaces[idx];
    580 }
    581 
    582 /** Bind a function node.
    583  *
    584  * Bind the specified function to the system. This effectively makes
    585  * the function visible to the system (uploads it to the server).
    586  *
    587  * This function can fail for several reasons. Specifically,
    588  * it will fail if the device already has a bound function of
    589  * the same name.
    590  *
    591  * @param fun           Function to bind
    592  * @return              EOK on success or negative error code
    593  */
    594 int ddf_fun_bind(ddf_fun_t *fun)
    595 {
    596         assert(fun->name != NULL);
    597        
    598         int res;
    599        
    600         add_to_functions_list(fun);
    601         res = devman_add_function(fun->name, fun->ftype, &fun->match_ids,
    602             fun->dev->handle, &fun->handle);
    603         if (res != EOK) {
    604                 remove_from_functions_list(fun);
    605                 return res;
    606         }
    607        
    608         fun->bound = true;
    609         return res;
    610 }
    611 
    612 /** Add single match ID to inner function.
    613  *
    614  * Construct and add a single match ID to the specified function.
    615  * Cannot be called when the function node is bound.
    616  *
    617  * @param fun                   Function
    618  * @param match_id_str          Match string
    619  * @param match_score           Match score
    620  * @return                      EOK on success, ENOMEM if out of memory.
    621  */
    622 int ddf_fun_add_match_id(ddf_fun_t *fun, const char *match_id_str,
    623     int match_score)
    624 {
    625         match_id_t *match_id;
    626        
    627         assert(fun->bound == false);
    628         assert(fun->ftype == fun_inner);
    629        
     385 * @param parent Parent device.
     386 * @param child_name Child device name.
     387 * @param child_match_id Child device match id.
     388 * @param child_match_score Child device match score.
     389 * @return Error code.
     390 */
     391int child_device_register_wrapper(device_t *parent, const char *child_name,
     392    const char *child_match_id, int child_match_score)
     393{
     394        device_t *child = NULL;
     395        match_id_t *match_id = NULL;
     396        int rc;
     397
     398        child = create_device();
     399        if (child == NULL) {
     400                rc = ENOMEM;
     401                goto failure;
     402        }
     403
     404        child->name = child_name;
     405
    630406        match_id = create_match_id();
    631         if (match_id == NULL)
    632                 return ENOMEM;
    633        
    634         match_id->id = match_id_str;
    635         match_id->score = 90;
    636        
    637         add_match_id(&fun->match_ids, match_id);
     407        if (match_id == NULL) {
     408                rc = ENOMEM;
     409                goto failure;
     410        }
     411
     412        match_id->id = child_match_id;
     413        match_id->score = child_match_score;
     414        add_match_id(&child->match_ids, match_id);
     415
     416        rc = child_device_register(child, parent);
     417        if (EOK != rc)
     418                goto failure;
     419
    638420        return EOK;
    639 }
    640 
    641 /** Get default handler for client requests */
    642 static remote_handler_t *function_get_default_handler(ddf_fun_t *fun)
    643 {
    644         if (fun->ops == NULL)
    645                 return NULL;
    646         return fun->ops->default_handler;
    647 }
    648 
    649 /** Add exposed function to class.
    650  *
    651  * Must only be called when the function is bound.
    652  */
    653 int ddf_fun_add_to_class(ddf_fun_t *fun, const char *class_name)
    654 {
    655         assert(fun->bound == true);
    656         assert(fun->ftype == fun_exposed);
    657        
    658         return devman_add_device_to_class(fun->handle, class_name);
    659 }
    660 
    661 int ddf_driver_main(driver_t *drv)
     421
     422failure:
     423        if (match_id != NULL) {
     424                match_id->id = NULL;
     425                delete_match_id(match_id);
     426        }
     427
     428        if (child != NULL) {
     429                child->name = NULL;
     430                delete_device(child);
     431        }
     432
     433        return rc;
     434}
     435
     436int driver_main(driver_t *drv)
    662437{
    663438        /*
     
    666441         */
    667442        driver = drv;
    668        
     443
    669444        /* Initialize the list of interrupt contexts. */
    670445        init_interrupt_context_list(&interrupt_contexts);
     
    678453         */
    679454        devman_driver_register(driver->name, driver_connection);
    680        
     455
    681456        async_manager();
    682        
     457
    683458        /* Never reached. */
    684459        return 0;
Note: See TracChangeset for help on using the changeset viewer.