Ignore:
File:
1 edited

Legend:

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

    r26fa82bc r8a363ab3  
    6363
    6464/** Devices */
     65LIST_INITIALIZE(devices);
     66FIBRIL_MUTEX_INITIALIZE(devices_mutex);
     67
     68/** Functions */
    6569LIST_INITIALIZE(functions);
    6670FIBRIL_MUTEX_INITIALIZE(functions_mutex);
     
    8286static ddf_dev_t *create_device(void);
    8387static void delete_device(ddf_dev_t *);
     88static void dev_add_ref(ddf_dev_t *);
     89static void dev_del_ref(ddf_dev_t *);
     90static void fun_add_ref(ddf_fun_t *);
     91static void fun_del_ref(ddf_fun_t *);
    8492static remote_handler_t *function_get_default_handler(ddf_fun_t *);
    8593static void *function_get_ops(ddf_fun_t *, dev_inferface_idx_t);
     
    139147find_interrupt_context_by_id(interrupt_context_list_t *list, int id)
    140148{
     149        interrupt_context_t *ctx;
     150       
    141151        fibril_mutex_lock(&list->mutex);
    142152       
    143         link_t *link = list->contexts.next;
    144         interrupt_context_t *ctx;
    145        
    146         while (link != &list->contexts) {
     153        list_foreach(list->contexts, link) {
    147154                ctx = list_get_instance(link, interrupt_context_t, link);
    148155                if (ctx->id == id) {
     
    150157                        return ctx;
    151158                }
    152                 link = link->next;
    153159        }
    154160       
     
    160166find_interrupt_context(interrupt_context_list_t *list, ddf_dev_t *dev, int irq)
    161167{
     168        interrupt_context_t *ctx;
     169       
    162170        fibril_mutex_lock(&list->mutex);
    163171       
    164         link_t *link = list->contexts.next;
    165         interrupt_context_t *ctx;
    166        
    167         while (link != &list->contexts) {
     172        list_foreach(list->contexts, link) {
    168173                ctx = list_get_instance(link, interrupt_context_t, link);
    169174                if (ctx->irq == irq && ctx->dev == dev) {
     
    171176                        return ctx;
    172177                }
    173                 link = link->next;
    174178        }
    175179       
     
    231235}
    232236
    233 static ddf_fun_t *driver_get_function(link_t *functions, devman_handle_t handle)
     237static ddf_dev_t *driver_get_device(devman_handle_t handle)
     238{
     239        ddf_dev_t *dev = NULL;
     240       
     241        assert(fibril_mutex_is_locked(&devices_mutex));
     242       
     243        list_foreach(devices, link) {
     244                dev = list_get_instance(link, ddf_dev_t, link);
     245                if (dev->handle == handle)
     246                        return dev;
     247        }
     248       
     249        return NULL;
     250}
     251
     252static ddf_fun_t *driver_get_function(devman_handle_t handle)
    234253{
    235254        ddf_fun_t *fun = NULL;
    236255       
    237         fibril_mutex_lock(&functions_mutex);
    238         link_t *link = functions->next;
    239        
    240         while (link != functions) {
     256        assert(fibril_mutex_is_locked(&functions_mutex));
     257       
     258        list_foreach(functions, link) {
    241259                fun = list_get_instance(link, ddf_fun_t, link);
    242                 if (fun->handle == handle) {
    243                         fibril_mutex_unlock(&functions_mutex);
     260                if (fun->handle == handle)
    244261                        return fun;
    245                 }
    246                
    247                 link = link->next;
    248         }
    249        
    250         fibril_mutex_unlock(&functions_mutex);
     262        }
    251263       
    252264        return NULL;
    253265}
    254266
    255 static void driver_add_device(ipc_callid_t iid, ipc_call_t *icall)
     267static void driver_dev_add(ipc_callid_t iid, ipc_call_t *icall)
    256268{
    257269        char *dev_name = NULL;
     
    259271       
    260272        devman_handle_t dev_handle = IPC_GET_ARG1(*icall);
    261         devman_handle_t parent_fun_handle = IPC_GET_ARG2(*icall);
     273        devman_handle_t parent_fun_handle = IPC_GET_ARG2(*icall);
    262274       
    263275        ddf_dev_t *dev = create_device();
     276
     277        /* Add one reference that will be dropped by driver_dev_remove() */
     278        dev_add_ref(dev);
    264279        dev->handle = dev_handle;
    265280
     
    274289       
    275290        res = driver->driver_ops->add_device(dev);
    276         if (res != EOK)
    277                 delete_device(dev);
     291       
     292        if (res != EOK) {
     293                dev_del_ref(dev);
     294                async_answer_0(iid, res);
     295                return;
     296        }
     297       
     298        fibril_mutex_lock(&devices_mutex);
     299        list_append(&dev->link, &devices);
     300        fibril_mutex_unlock(&devices_mutex);
    278301       
    279302        async_answer_0(iid, res);
     303}
     304
     305static void driver_dev_added(ipc_callid_t iid, ipc_call_t *icall)
     306{
     307        fibril_mutex_lock(&devices_mutex);
     308        ddf_dev_t *dev = driver_get_device(IPC_GET_ARG1(*icall));
     309        fibril_mutex_unlock(&devices_mutex);
     310       
     311        if (dev != NULL && driver->driver_ops->device_added != NULL)
     312                driver->driver_ops->device_added(dev);
     313}
     314
     315static void driver_dev_remove(ipc_callid_t iid, ipc_call_t *icall)
     316{
     317        devman_handle_t devh;
     318        ddf_dev_t *dev;
     319        int rc;
     320       
     321        devh = IPC_GET_ARG1(*icall);
     322       
     323        fibril_mutex_lock(&devices_mutex);
     324        dev = driver_get_device(devh);
     325        if (dev != NULL)
     326                dev_add_ref(dev);
     327        fibril_mutex_unlock(&devices_mutex);
     328       
     329        if (dev == NULL) {
     330                async_answer_0(iid, ENOENT);
     331                return;
     332        }
     333       
     334        if (driver->driver_ops->dev_remove != NULL)
     335                rc = driver->driver_ops->dev_remove(dev);
     336        else
     337                rc = ENOTSUP;
     338       
     339        if (rc == EOK)
     340                dev_del_ref(dev);
     341       
     342        async_answer_0(iid, (sysarg_t) rc);
     343}
     344
     345static void driver_dev_gone(ipc_callid_t iid, ipc_call_t *icall)
     346{
     347        devman_handle_t devh;
     348        ddf_dev_t *dev;
     349        int rc;
     350       
     351        devh = IPC_GET_ARG1(*icall);
     352       
     353        fibril_mutex_lock(&devices_mutex);
     354        dev = driver_get_device(devh);
     355        if (dev != NULL)
     356                dev_add_ref(dev);
     357        fibril_mutex_unlock(&devices_mutex);
     358       
     359        if (dev == NULL) {
     360                async_answer_0(iid, ENOENT);
     361                return;
     362        }
     363       
     364        if (driver->driver_ops->dev_gone != NULL)
     365                rc = driver->driver_ops->dev_gone(dev);
     366        else
     367                rc = ENOTSUP;
     368       
     369        if (rc == EOK)
     370                dev_del_ref(dev);
     371       
     372        async_answer_0(iid, (sysarg_t) rc);
     373}
     374
     375static void driver_fun_online(ipc_callid_t iid, ipc_call_t *icall)
     376{
     377        devman_handle_t funh;
     378        ddf_fun_t *fun;
     379        int rc;
     380       
     381        funh = IPC_GET_ARG1(*icall);
     382       
     383        /*
     384         * Look the function up. Bump reference count so that
     385         * the function continues to exist until we return
     386         * from the driver.
     387         */
     388        fibril_mutex_lock(&functions_mutex);
     389       
     390        fun = driver_get_function(funh);
     391        if (fun != NULL)
     392                fun_add_ref(fun);
     393       
     394        fibril_mutex_unlock(&functions_mutex);
     395       
     396        if (fun == NULL) {
     397                async_answer_0(iid, ENOENT);
     398                return;
     399        }
     400       
     401        /* Call driver entry point */
     402        if (driver->driver_ops->fun_online != NULL)
     403                rc = driver->driver_ops->fun_online(fun);
     404        else
     405                rc = ENOTSUP;
     406       
     407        fun_del_ref(fun);
     408       
     409        async_answer_0(iid, (sysarg_t) rc);
     410}
     411
     412static void driver_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
     413{
     414        devman_handle_t funh;
     415        ddf_fun_t *fun;
     416        int rc;
     417       
     418        funh = IPC_GET_ARG1(*icall);
     419       
     420        /*
     421         * Look the function up. Bump reference count so that
     422         * the function continues to exist until we return
     423         * from the driver.
     424         */
     425        fibril_mutex_lock(&functions_mutex);
     426       
     427        fun = driver_get_function(funh);
     428        if (fun != NULL)
     429                fun_add_ref(fun);
     430       
     431        fibril_mutex_unlock(&functions_mutex);
     432       
     433        if (fun == NULL) {
     434                async_answer_0(iid, ENOENT);
     435                return;
     436        }
     437       
     438        /* Call driver entry point */
     439        if (driver->driver_ops->fun_offline != NULL)
     440                rc = driver->driver_ops->fun_offline(fun);
     441        else
     442                rc = ENOTSUP;
     443       
     444        async_answer_0(iid, (sysarg_t) rc);
    280445}
    281446
     
    285450        async_answer_0(iid, EOK);
    286451       
    287         bool cont = true;
    288         while (cont) {
     452        while (true) {
    289453                ipc_call_t call;
    290454                ipc_callid_t callid = async_get_call(&call);
    291455               
     456                if (!IPC_GET_IMETHOD(call))
     457                        break;
     458               
    292459                switch (IPC_GET_IMETHOD(call)) {
    293                 case IPC_M_PHONE_HUNGUP:
    294                         cont = false;
    295                         continue;
    296                 case DRIVER_ADD_DEVICE:
    297                         driver_add_device(callid, &call);
     460                case DRIVER_DEV_ADD:
     461                        driver_dev_add(callid, &call);
     462                        break;
     463                case DRIVER_DEV_ADDED:
     464                        async_answer_0(callid, EOK);
     465                        driver_dev_added(callid, &call);
     466                        break;
     467                case DRIVER_DEV_REMOVE:
     468                        driver_dev_remove(callid, &call);
     469                        break;
     470                case DRIVER_DEV_GONE:
     471                        driver_dev_gone(callid, &call);
     472                        break;
     473                case DRIVER_FUN_ONLINE:
     474                        driver_fun_online(callid, &call);
     475                        break;
     476                case DRIVER_FUN_OFFLINE:
     477                        driver_fun_offline(callid, &call);
    298478                        break;
    299479                default:
    300                         async_answer_0(callid, ENOENT);
     480                        async_answer_0(callid, ENOTSUP);
    301481                }
    302482        }
    303483}
    304484
    305 /**
    306  * Generic client connection handler both for applications and drivers.
    307  *
    308  * @param drv           True for driver client, false for other clients
    309  *                      (applications, services etc.).
     485/** Generic client connection handler both for applications and drivers.
     486 *
     487 * @param drv True for driver client, false for other clients
     488 *            (applications, services, etc.).
     489 *
    310490 */
    311491static void driver_connection_gen(ipc_callid_t iid, ipc_call_t *icall, bool drv)
     
    316496         */
    317497        devman_handle_t handle = IPC_GET_ARG2(*icall);
    318         ddf_fun_t *fun = driver_get_function(&functions, handle);
    319 
     498
     499        fibril_mutex_lock(&functions_mutex);
     500        ddf_fun_t *fun = driver_get_function(handle);
     501        fibril_mutex_unlock(&functions_mutex);
     502        /* XXX Need a lock on fun */
     503       
    320504        if (fun == NULL) {
    321505                printf("%s: driver_connection_gen error - no function with handle"
     
    325509        }
    326510       
     511        if (fun->conn_handler != NULL) {
     512                /* Driver has a custom connection handler. */
     513                (*fun->conn_handler)(iid, icall, (void *)fun);
     514                return;
     515        }
    327516       
    328517        /*
     
    340529                return;
    341530       
    342         while (1) {
     531        while (true) {
    343532                ipc_callid_t callid;
    344533                ipc_call_t call;
    345534                callid = async_get_call(&call);
    346535                sysarg_t method = IPC_GET_IMETHOD(call);
    347                 int iface_idx;
    348                
    349                 switch  (method) {
    350                 case IPC_M_PHONE_HUNGUP:
     536               
     537                if (!method) {
    351538                        /* Close device function */
    352539                        if (fun->ops != NULL && fun->ops->close != NULL)
     
    354541                        async_answer_0(callid, EOK);
    355542                        return;
    356                 default:
    357                         /* convert ipc interface id to interface index */
    358                        
    359                         iface_idx = DEV_IFACE_IDX(method);
    360                        
    361                         if (!is_valid_iface_idx(iface_idx)) {
    362                                 remote_handler_t *default_handler =
    363                                     function_get_default_handler(fun);
    364                                 if (default_handler != NULL) {
    365                                         (*default_handler)(fun, callid, &call);
    366                                         break;
    367                                 }
    368                                
    369                                 /*
    370                                  * Function has no such interface and
    371                                  * default handler is not provided.
    372                                  */
    373                                 printf("%s: driver_connection_gen error - "
    374                                     "invalid interface id %d.",
    375                                     driver->name, iface_idx);
    376                                 async_answer_0(callid, ENOTSUP);
    377                                 break;
    378                         }
    379                        
    380                         /* calling one of the function's interfaces */
    381                        
    382                         /* Get the interface ops structure. */
    383                         void *ops = function_get_ops(fun, iface_idx);
    384                         if (ops == NULL) {
    385                                 printf("%s: driver_connection_gen error - ",
    386                                     driver->name);
    387                                 printf("Function with handle %" PRIun " has no interface "
    388                                     "with id %d.\n", handle, iface_idx);
    389                                 async_answer_0(callid, ENOTSUP);
    390                                 break;
     543                }
     544               
     545                /* Convert ipc interface id to interface index */
     546               
     547                int iface_idx = DEV_IFACE_IDX(method);
     548               
     549                if (!is_valid_iface_idx(iface_idx)) {
     550                        remote_handler_t *default_handler =
     551                            function_get_default_handler(fun);
     552                        if (default_handler != NULL) {
     553                                (*default_handler)(fun, callid, &call);
     554                                continue;
    391555                        }
    392556                       
    393557                        /*
    394                          * Get the corresponding interface for remote request
    395                          * handling ("remote interface").
     558                         * Function has no such interface and
     559                         * default handler is not provided.
    396560                         */
    397                         remote_iface_t *rem_iface = get_remote_iface(iface_idx);
    398                         assert(rem_iface != NULL);
    399                        
    400                         /* get the method of the remote interface */
    401                         sysarg_t iface_method_idx = IPC_GET_ARG1(call);
    402                         remote_iface_func_ptr_t iface_method_ptr =
    403                             get_remote_method(rem_iface, iface_method_idx);
    404                         if (iface_method_ptr == NULL) {
    405                                 /* The interface has not such method */
    406                                 printf("%s: driver_connection_gen error - "
    407                                     "invalid interface method.", driver->name);
    408                                 async_answer_0(callid, ENOTSUP);
    409                                 break;
    410                         }
    411                        
    412                         /*
    413                          * Call the remote interface's method, which will
    414                          * receive parameters from the remote client and it will
    415                          * pass it to the corresponding local interface method
    416                          * associated with the function by its driver.
    417                          */
    418                         (*iface_method_ptr)(fun, ops, callid, &call);
    419                         break;
     561                        printf("%s: driver_connection_gen error - "
     562                            "invalid interface id %d.",
     563                            driver->name, iface_idx);
     564                        async_answer_0(callid, ENOTSUP);
     565                        continue;
    420566                }
     567               
     568                /* Calling one of the function's interfaces */
     569               
     570                /* Get the interface ops structure. */
     571                void *ops = function_get_ops(fun, iface_idx);
     572                if (ops == NULL) {
     573                        printf("%s: driver_connection_gen error - ", driver->name);
     574                        printf("Function with handle %" PRIun " has no interface "
     575                            "with id %d.\n", handle, iface_idx);
     576                        async_answer_0(callid, ENOTSUP);
     577                        continue;
     578                }
     579               
     580                /*
     581                 * Get the corresponding interface for remote request
     582                 * handling ("remote interface").
     583                 */
     584                remote_iface_t *rem_iface = get_remote_iface(iface_idx);
     585                assert(rem_iface != NULL);
     586               
     587                /* get the method of the remote interface */
     588                sysarg_t iface_method_idx = IPC_GET_ARG1(call);
     589                remote_iface_func_ptr_t iface_method_ptr =
     590                    get_remote_method(rem_iface, iface_method_idx);
     591                if (iface_method_ptr == NULL) {
     592                        /* The interface has not such method */
     593                        printf("%s: driver_connection_gen error - "
     594                            "invalid interface method.", driver->name);
     595                        async_answer_0(callid, ENOTSUP);
     596                        continue;
     597                }
     598               
     599                /*
     600                 * Call the remote interface's method, which will
     601                 * receive parameters from the remote client and it will
     602                 * pass it to the corresponding local interface method
     603                 * associated with the function by its driver.
     604                 */
     605                (*iface_method_ptr)(fun, ops, callid, &call);
    421606        }
    422607}
     
    433618
    434619/** Function for handling connections to device driver. */
    435 static void driver_connection(ipc_callid_t iid, ipc_call_t *icall)
    436 {
     620static void driver_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
     621{
     622        sysarg_t conn_type;
     623
     624        if (iid == 0) {
     625                /* Callback connection from devman */
     626                /* XXX Use separate handler for this type of connection */
     627                conn_type = DRIVER_DEVMAN;
     628        } else {
     629                conn_type = IPC_GET_ARG1(*icall);
     630        }
     631
    437632        /* Select interface */
    438         switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
     633        switch (conn_type) {
    439634        case DRIVER_DEVMAN:
    440635                /* Handle request from device manager */
     
    463658        ddf_dev_t *dev;
    464659
    465         dev = malloc(sizeof(ddf_dev_t));
     660        dev = calloc(1, sizeof(ddf_dev_t));
    466661        if (dev == NULL)
    467662                return NULL;
    468663
    469         memset(dev, 0, sizeof(ddf_dev_t));
    470664        return dev;
    471665}
     
    495689static void delete_device(ddf_dev_t *dev)
    496690{
     691        if (dev->driver_data != NULL)
     692                free(dev->driver_data);
    497693        free(dev);
    498694}
    499695
    500 /** Delete device structure.
     696/** Delete function structure.
    501697 *
    502698 * @param dev           The device structure.
     
    505701{
    506702        clean_match_ids(&fun->match_ids);
     703        if (fun->driver_data != NULL)
     704                free(fun->driver_data);
    507705        if (fun->name != NULL)
    508706                free(fun->name);
     
    510708}
    511709
     710/** Increase device reference count. */
     711static void dev_add_ref(ddf_dev_t *dev)
     712{
     713        atomic_inc(&dev->refcnt);
     714}
     715
     716/** Decrease device reference count.
     717 *
     718 * Free the device structure if the reference count drops to zero.
     719 */
     720static void dev_del_ref(ddf_dev_t *dev)
     721{
     722        if (atomic_predec(&dev->refcnt) == 0)
     723                delete_device(dev);
     724}
     725
     726/** Increase function reference count.
     727 *
     728 * This also increases reference count on the device. The device structure
     729 * will thus not be deallocated while there are some associated function
     730 * structures.
     731 */
     732static void fun_add_ref(ddf_fun_t *fun)
     733{
     734        dev_add_ref(fun->dev);
     735        atomic_inc(&fun->refcnt);
     736}
     737
     738/** Decrease function reference count.
     739 *
     740 * Free the function structure if the reference count drops to zero.
     741 */
     742static void fun_del_ref(ddf_fun_t *fun)
     743{
     744        ddf_dev_t *dev = fun->dev;
     745
     746        if (atomic_predec(&fun->refcnt) == 0)
     747                delete_function(fun);
     748
     749        dev_del_ref(dev);
     750}
     751
     752/** Allocate driver-specific device data. */
     753extern void *ddf_dev_data_alloc(ddf_dev_t *dev, size_t size)
     754{
     755        void *data;
     756
     757        assert(dev->driver_data == NULL);
     758
     759        data = calloc(1, size);
     760        if (data == NULL)
     761                return NULL;
     762
     763        dev->driver_data = data;
     764        return data;
     765}
     766
    512767/** Create a DDF function node.
    513768 *
     
    541796                return NULL;
    542797
     798        /* Add one reference that will be dropped by ddf_fun_destroy() */
     799        fun->dev = dev;
     800        fun_add_ref(fun);
     801
    543802        fun->bound = false;
    544         fun->dev = dev;
    545803        fun->ftype = ftype;
    546804
     
    554812}
    555813
     814/** Allocate driver-specific function data. */
     815extern void *ddf_fun_data_alloc(ddf_fun_t *fun, size_t size)
     816{
     817        void *data;
     818
     819        assert(fun->bound == false);
     820        assert(fun->driver_data == NULL);
     821
     822        data = calloc(1, size);
     823        if (data == NULL)
     824                return NULL;
     825
     826        fun->driver_data = data;
     827        return data;
     828}
     829
    556830/** Destroy DDF function node.
    557831 *
     
    564838{
    565839        assert(fun->bound == false);
    566         delete_function(fun);
     840
     841        /*
     842         * Drop the reference added by ddf_fun_create(). This will deallocate
     843         * the function as soon as all other references are dropped (i.e.
     844         * as soon control leaves all driver entry points called in context
     845         * of this function.
     846         */
     847        fun_del_ref(fun);
    567848}
    568849
     
    589870int ddf_fun_bind(ddf_fun_t *fun)
    590871{
     872        assert(fun->bound == false);
    591873        assert(fun->name != NULL);
    592874       
     
    605887}
    606888
     889/** Unbind a function node.
     890 *
     891 * Unbind the specified function from the system. This effectively makes
     892 * the function invisible to the system.
     893 *
     894 * @param fun           Function to unbind
     895 * @return              EOK on success or negative error code
     896 */
     897int ddf_fun_unbind(ddf_fun_t *fun)
     898{
     899        int res;
     900       
     901        assert(fun->bound == true);
     902       
     903        res = devman_remove_function(fun->handle);
     904        if (res != EOK)
     905                return res;
     906
     907        remove_from_functions_list(fun);
     908       
     909        fun->bound = false;
     910        return EOK;
     911}
     912
     913/** Online function.
     914 *
     915 * @param fun           Function to online
     916 * @return              EOK on success or negative error code
     917 */
     918int ddf_fun_online(ddf_fun_t *fun)
     919{
     920        int res;
     921       
     922        assert(fun->bound == true);
     923       
     924        res = devman_drv_fun_online(fun->handle);
     925        if (res != EOK)
     926                return res;
     927       
     928        return EOK;
     929}
     930
     931/** Offline function.
     932 *
     933 * @param fun           Function to offline
     934 * @return              EOK on success or negative error code
     935 */
     936int ddf_fun_offline(ddf_fun_t *fun)
     937{
     938        int res;
     939       
     940        assert(fun->bound == true);
     941       
     942        res = devman_drv_fun_offline(fun->handle);
     943        if (res != EOK)
     944                return res;
     945       
     946        return EOK;
     947}
     948
    607949/** Add single match ID to inner function.
    608950 *
     
    627969                return ENOMEM;
    628970       
    629         match_id->id = match_id_str;
    630         match_id->score = 90;
     971        match_id->id = str_dup(match_id_str);
     972        match_id->score = match_score;
    631973       
    632974        add_match_id(&fun->match_ids, match_id);
     
    642984}
    643985
    644 /** Add exposed function to class.
     986/** Add exposed function to category.
    645987 *
    646988 * Must only be called when the function is bound.
    647989 */
    648 int ddf_fun_add_to_class(ddf_fun_t *fun, const char *class_name)
     990int ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
    649991{
    650992        assert(fun->bound == true);
    651993        assert(fun->ftype == fun_exposed);
    652994       
    653         return devman_add_device_to_class(fun->handle, class_name);
     995        return devman_add_device_to_category(fun->handle, cat_name);
    654996}
    655997
Note: See TracChangeset for help on using the changeset viewer.