Ignore:
File:
1 edited

Legend:

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

    rb72efe8 r80a96d2  
    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);
     
    227235}
    228236
    229 static ddf_fun_t *driver_get_function(list_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)
    230253{
    231254        ddf_fun_t *fun = NULL;
    232255       
    233         fibril_mutex_lock(&functions_mutex);
    234        
    235         list_foreach(*functions, link) {
     256        assert(fibril_mutex_is_locked(&functions_mutex));
     257       
     258        list_foreach(functions, link) {
    236259                fun = list_get_instance(link, ddf_fun_t, link);
    237                 if (fun->handle == handle) {
    238                         fibril_mutex_unlock(&functions_mutex);
     260                if (fun->handle == handle)
    239261                        return fun;
    240                 }
    241         }
    242        
    243         fibril_mutex_unlock(&functions_mutex);
     262        }
    244263       
    245264        return NULL;
    246265}
    247266
    248 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)
    249268{
    250269        char *dev_name = NULL;
     
    255274       
    256275        ddf_dev_t *dev = create_device();
     276
     277        /* Add one reference that will be dropped by driver_dev_remove() */
     278        dev_add_ref(dev);
    257279        dev->handle = dev_handle;
    258280
     
    267289       
    268290        res = driver->driver_ops->add_device(dev);
    269         if (res != EOK)
    270                 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);
    271301       
    272302        async_answer_0(iid, res);
     303}
     304
     305static void driver_dev_remove(ipc_callid_t iid, ipc_call_t *icall)
     306{
     307        devman_handle_t devh;
     308        ddf_dev_t *dev;
     309        int rc;
     310       
     311        devh = IPC_GET_ARG1(*icall);
     312       
     313        fibril_mutex_lock(&devices_mutex);
     314        dev = driver_get_device(devh);
     315        if (dev != NULL)
     316                dev_add_ref(dev);
     317        fibril_mutex_unlock(&devices_mutex);
     318       
     319        if (dev == NULL) {
     320                async_answer_0(iid, ENOENT);
     321                return;
     322        }
     323       
     324        if (driver->driver_ops->dev_remove != NULL)
     325                rc = driver->driver_ops->dev_remove(dev);
     326        else
     327                rc = ENOTSUP;
     328       
     329        if (rc == EOK)
     330                dev_del_ref(dev);
     331       
     332        async_answer_0(iid, (sysarg_t) rc);
     333}
     334
     335static void driver_dev_gone(ipc_callid_t iid, ipc_call_t *icall)
     336{
     337        devman_handle_t devh;
     338        ddf_dev_t *dev;
     339        int rc;
     340       
     341        devh = IPC_GET_ARG1(*icall);
     342       
     343        fibril_mutex_lock(&devices_mutex);
     344        dev = driver_get_device(devh);
     345        if (dev != NULL)
     346                dev_add_ref(dev);
     347        fibril_mutex_unlock(&devices_mutex);
     348       
     349        if (dev == NULL) {
     350                async_answer_0(iid, ENOENT);
     351                return;
     352        }
     353       
     354        if (driver->driver_ops->dev_gone != NULL)
     355                rc = driver->driver_ops->dev_gone(dev);
     356        else
     357                rc = ENOTSUP;
     358       
     359        if (rc == EOK)
     360                dev_del_ref(dev);
     361       
     362        async_answer_0(iid, (sysarg_t) rc);
     363}
     364
     365static void driver_fun_online(ipc_callid_t iid, ipc_call_t *icall)
     366{
     367        devman_handle_t funh;
     368        ddf_fun_t *fun;
     369        int rc;
     370       
     371        funh = IPC_GET_ARG1(*icall);
     372       
     373        /*
     374         * Look the function up. Bump reference count so that
     375         * the function continues to exist until we return
     376         * from the driver.
     377         */
     378        fibril_mutex_lock(&functions_mutex);
     379       
     380        fun = driver_get_function(funh);
     381        if (fun != NULL)
     382                fun_add_ref(fun);
     383       
     384        fibril_mutex_unlock(&functions_mutex);
     385       
     386        if (fun == NULL) {
     387                async_answer_0(iid, ENOENT);
     388                return;
     389        }
     390       
     391        /* Call driver entry point */
     392        if (driver->driver_ops->fun_online != NULL)
     393                rc = driver->driver_ops->fun_online(fun);
     394        else
     395                rc = ENOTSUP;
     396       
     397        fun_del_ref(fun);
     398       
     399        async_answer_0(iid, (sysarg_t) rc);
     400}
     401
     402static void driver_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
     403{
     404        devman_handle_t funh;
     405        ddf_fun_t *fun;
     406        int rc;
     407       
     408        funh = IPC_GET_ARG1(*icall);
     409       
     410        /*
     411         * Look the function up. Bump reference count so that
     412         * the function continues to exist until we return
     413         * from the driver.
     414         */
     415        fibril_mutex_lock(&functions_mutex);
     416       
     417        fun = driver_get_function(funh);
     418        if (fun != NULL)
     419                fun_add_ref(fun);
     420       
     421        fibril_mutex_unlock(&functions_mutex);
     422       
     423        if (fun == NULL) {
     424                async_answer_0(iid, ENOENT);
     425                return;
     426        }
     427       
     428        /* Call driver entry point */
     429        if (driver->driver_ops->fun_offline != NULL)
     430                rc = driver->driver_ops->fun_offline(fun);
     431        else
     432                rc = ENOTSUP;
     433       
     434        async_answer_0(iid, (sysarg_t) rc);
    273435}
    274436
     
    286448               
    287449                switch (IPC_GET_IMETHOD(call)) {
    288                 case DRIVER_ADD_DEVICE:
    289                         driver_add_device(callid, &call);
     450                case DRIVER_DEV_ADD:
     451                        driver_dev_add(callid, &call);
     452                        break;
     453                case DRIVER_DEV_REMOVE:
     454                        driver_dev_remove(callid, &call);
     455                        break;
     456                case DRIVER_DEV_GONE:
     457                        driver_dev_gone(callid, &call);
     458                        break;
     459                case DRIVER_FUN_ONLINE:
     460                        driver_fun_online(callid, &call);
     461                        break;
     462                case DRIVER_FUN_OFFLINE:
     463                        driver_fun_offline(callid, &call);
    290464                        break;
    291465                default:
    292                         async_answer_0(callid, ENOENT);
     466                        async_answer_0(callid, ENOTSUP);
    293467                }
    294468        }
     
    308482         */
    309483        devman_handle_t handle = IPC_GET_ARG2(*icall);
    310         ddf_fun_t *fun = driver_get_function(&functions, handle);
     484
     485        fibril_mutex_lock(&functions_mutex);
     486        ddf_fun_t *fun = driver_get_function(handle);
     487        fibril_mutex_unlock(&functions_mutex);
     488        /* XXX Need a lock on fun */
    311489       
    312490        if (fun == NULL) {
     
    314492                    " %" PRIun " was found.\n", driver->name, handle);
    315493                async_answer_0(iid, ENOENT);
     494                return;
     495        }
     496       
     497        if (fun->conn_handler != NULL) {
     498                /* Driver has a custom connection handler. */
     499                (*fun->conn_handler)(iid, icall, (void *)fun);
    316500                return;
    317501        }
     
    422606static void driver_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
    423607{
     608        sysarg_t conn_type;
     609
     610        if (iid == 0) {
     611                /* Callback connection from devman */
     612                /* XXX Use separate handler for this type of connection */
     613                conn_type = DRIVER_DEVMAN;
     614        } else {
     615                conn_type = IPC_GET_ARG1(*icall);
     616        }
     617
    424618        /* Select interface */
    425         switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
     619        switch (conn_type) {
    426620        case DRIVER_DEVMAN:
    427621                /* Handle request from device manager */
     
    450644        ddf_dev_t *dev;
    451645
    452         dev = malloc(sizeof(ddf_dev_t));
     646        dev = calloc(1, sizeof(ddf_dev_t));
    453647        if (dev == NULL)
    454648                return NULL;
    455649
    456         memset(dev, 0, sizeof(ddf_dev_t));
    457650        return dev;
    458651}
     
    482675static void delete_device(ddf_dev_t *dev)
    483676{
     677        if (dev->driver_data != NULL)
     678                free(dev->driver_data);
    484679        free(dev);
    485680}
    486681
    487 /** Delete device structure.
     682/** Delete function structure.
    488683 *
    489684 * @param dev           The device structure.
     
    492687{
    493688        clean_match_ids(&fun->match_ids);
     689        if (fun->driver_data != NULL)
     690                free(fun->driver_data);
    494691        if (fun->name != NULL)
    495692                free(fun->name);
     
    497694}
    498695
     696/** Increase device reference count. */
     697static void dev_add_ref(ddf_dev_t *dev)
     698{
     699        atomic_inc(&dev->refcnt);
     700}
     701
     702/** Decrease device reference count.
     703 *
     704 * Free the device structure if the reference count drops to zero.
     705 */
     706static void dev_del_ref(ddf_dev_t *dev)
     707{
     708        if (atomic_predec(&dev->refcnt) == 0)
     709                delete_device(dev);
     710}
     711
     712/** Increase function reference count.
     713 *
     714 * This also increases reference count on the device. The device structure
     715 * will thus not be deallocated while there are some associated function
     716 * structures.
     717 */
     718static void fun_add_ref(ddf_fun_t *fun)
     719{
     720        dev_add_ref(fun->dev);
     721        atomic_inc(&fun->refcnt);
     722}
     723
     724/** Decrease function reference count.
     725 *
     726 * Free the function structure if the reference count drops to zero.
     727 */
     728static void fun_del_ref(ddf_fun_t *fun)
     729{
     730        ddf_dev_t *dev = fun->dev;
     731
     732        if (atomic_predec(&fun->refcnt) == 0)
     733                delete_function(fun);
     734
     735        dev_del_ref(dev);
     736}
     737
     738/** Allocate driver-specific device data. */
     739extern void *ddf_dev_data_alloc(ddf_dev_t *dev, size_t size)
     740{
     741        void *data;
     742
     743        assert(dev->driver_data == NULL);
     744
     745        data = calloc(1, size);
     746        if (data == NULL)
     747                return NULL;
     748
     749        dev->driver_data = data;
     750        return data;
     751}
     752
    499753/** Create a DDF function node.
    500754 *
     
    528782                return NULL;
    529783
     784        /* Add one reference that will be dropped by ddf_fun_destroy() */
     785        fun->dev = dev;
     786        fun_add_ref(fun);
     787
    530788        fun->bound = false;
    531         fun->dev = dev;
    532789        fun->ftype = ftype;
    533790
     
    541798}
    542799
     800/** Allocate driver-specific function data. */
     801extern void *ddf_fun_data_alloc(ddf_fun_t *fun, size_t size)
     802{
     803        void *data;
     804
     805        assert(fun->bound == false);
     806        assert(fun->driver_data == NULL);
     807
     808        data = calloc(1, size);
     809        if (data == NULL)
     810                return NULL;
     811
     812        fun->driver_data = data;
     813        return data;
     814}
     815
    543816/** Destroy DDF function node.
    544817 *
     
    551824{
    552825        assert(fun->bound == false);
    553         delete_function(fun);
     826
     827        /*
     828         * Drop the reference added by ddf_fun_create(). This will deallocate
     829         * the function as soon as all other references are dropped (i.e.
     830         * as soon control leaves all driver entry points called in context
     831         * of this function.
     832         */
     833        fun_del_ref(fun);
    554834}
    555835
     
    576856int ddf_fun_bind(ddf_fun_t *fun)
    577857{
     858        assert(fun->bound == false);
    578859        assert(fun->name != NULL);
    579860       
     
    592873}
    593874
     875/** Unbind a function node.
     876 *
     877 * Unbind the specified function from the system. This effectively makes
     878 * the function invisible to the system.
     879 *
     880 * @param fun           Function to unbind
     881 * @return              EOK on success or negative error code
     882 */
     883int ddf_fun_unbind(ddf_fun_t *fun)
     884{
     885        int res;
     886       
     887        assert(fun->bound == true);
     888       
     889        res = devman_remove_function(fun->handle);
     890        if (res != EOK)
     891                return res;
     892
     893        remove_from_functions_list(fun);
     894       
     895        fun->bound = false;
     896        return EOK;
     897}
     898
     899/** Online function.
     900 *
     901 * @param fun           Function to online
     902 * @return              EOK on success or negative error code
     903 */
     904int ddf_fun_online(ddf_fun_t *fun)
     905{
     906        int res;
     907       
     908        assert(fun->bound == true);
     909       
     910        res = devman_drv_fun_online(fun->handle);
     911        if (res != EOK)
     912                return res;
     913       
     914        return EOK;
     915}
     916
     917/** Offline function.
     918 *
     919 * @param fun           Function to offline
     920 * @return              EOK on success or negative error code
     921 */
     922int ddf_fun_offline(ddf_fun_t *fun)
     923{
     924        int res;
     925       
     926        assert(fun->bound == true);
     927       
     928        res = devman_drv_fun_offline(fun->handle);
     929        if (res != EOK)
     930                return res;
     931       
     932        return EOK;
     933}
     934
    594935/** Add single match ID to inner function.
    595936 *
     
    614955                return ENOMEM;
    615956       
    616         match_id->id = match_id_str;
     957        match_id->id = str_dup(match_id_str);
    617958        match_id->score = 90;
    618959       
     
    629970}
    630971
    631 /** Add exposed function to class.
     972/** Add exposed function to category.
    632973 *
    633974 * Must only be called when the function is bound.
    634975 */
    635 int ddf_fun_add_to_class(ddf_fun_t *fun, const char *class_name)
     976int ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
    636977{
    637978        assert(fun->bound == true);
    638979        assert(fun->ftype == fun_exposed);
    639980       
    640         return devman_add_device_to_class(fun->handle, class_name);
     981        return devman_add_device_to_category(fun->handle, cat_name);
    641982}
    642983
Note: See TracChangeset for help on using the changeset viewer.