Ignore:
File:
1 edited

Legend:

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

    r8a363ab3 r26fa82bc  
    6363
    6464/** Devices */
    65 LIST_INITIALIZE(devices);
    66 FIBRIL_MUTEX_INITIALIZE(devices_mutex);
    67 
    68 /** Functions */
    6965LIST_INITIALIZE(functions);
    7066FIBRIL_MUTEX_INITIALIZE(functions_mutex);
     
    8682static ddf_dev_t *create_device(void);
    8783static void delete_device(ddf_dev_t *);
    88 static void dev_add_ref(ddf_dev_t *);
    89 static void dev_del_ref(ddf_dev_t *);
    90 static void fun_add_ref(ddf_fun_t *);
    91 static void fun_del_ref(ddf_fun_t *);
    9284static remote_handler_t *function_get_default_handler(ddf_fun_t *);
    9385static void *function_get_ops(ddf_fun_t *, dev_inferface_idx_t);
     
    147139find_interrupt_context_by_id(interrupt_context_list_t *list, int id)
    148140{
     141        fibril_mutex_lock(&list->mutex);
     142       
     143        link_t *link = list->contexts.next;
    149144        interrupt_context_t *ctx;
    150145       
    151         fibril_mutex_lock(&list->mutex);
    152        
    153         list_foreach(list->contexts, link) {
     146        while (link != &list->contexts) {
    154147                ctx = list_get_instance(link, interrupt_context_t, link);
    155148                if (ctx->id == id) {
     
    157150                        return ctx;
    158151                }
     152                link = link->next;
    159153        }
    160154       
     
    166160find_interrupt_context(interrupt_context_list_t *list, ddf_dev_t *dev, int irq)
    167161{
     162        fibril_mutex_lock(&list->mutex);
     163       
     164        link_t *link = list->contexts.next;
    168165        interrupt_context_t *ctx;
    169166       
    170         fibril_mutex_lock(&list->mutex);
    171        
    172         list_foreach(list->contexts, link) {
     167        while (link != &list->contexts) {
    173168                ctx = list_get_instance(link, interrupt_context_t, link);
    174169                if (ctx->irq == irq && ctx->dev == dev) {
     
    176171                        return ctx;
    177172                }
     173                link = link->next;
    178174        }
    179175       
     
    235231}
    236232
    237 static 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         }
     233static ddf_fun_t *driver_get_function(link_t *functions, devman_handle_t handle)
     234{
     235        ddf_fun_t *fun = NULL;
     236       
     237        fibril_mutex_lock(&functions_mutex);
     238        link_t *link = functions->next;
     239       
     240        while (link != functions) {
     241                fun = list_get_instance(link, ddf_fun_t, link);
     242                if (fun->handle == handle) {
     243                        fibril_mutex_unlock(&functions_mutex);
     244                        return fun;
     245                }
     246               
     247                link = link->next;
     248        }
     249       
     250        fibril_mutex_unlock(&functions_mutex);
    248251       
    249252        return NULL;
    250253}
    251254
    252 static ddf_fun_t *driver_get_function(devman_handle_t handle)
    253 {
    254         ddf_fun_t *fun = NULL;
    255        
    256         assert(fibril_mutex_is_locked(&functions_mutex));
    257        
    258         list_foreach(functions, link) {
    259                 fun = list_get_instance(link, ddf_fun_t, link);
    260                 if (fun->handle == handle)
    261                         return fun;
    262         }
    263        
    264         return NULL;
    265 }
    266 
    267 static void driver_dev_add(ipc_callid_t iid, ipc_call_t *icall)
     255static void driver_add_device(ipc_callid_t iid, ipc_call_t *icall)
    268256{
    269257        char *dev_name = NULL;
     
    271259       
    272260        devman_handle_t dev_handle = IPC_GET_ARG1(*icall);
    273         devman_handle_t parent_fun_handle = IPC_GET_ARG2(*icall);
     261        devman_handle_t parent_fun_handle = IPC_GET_ARG2(*icall);
    274262       
    275263        ddf_dev_t *dev = create_device();
    276 
    277         /* Add one reference that will be dropped by driver_dev_remove() */
    278         dev_add_ref(dev);
    279264        dev->handle = dev_handle;
    280265
     
    289274       
    290275        res = driver->driver_ops->add_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);
     276        if (res != EOK)
     277                delete_device(dev);
    301278       
    302279        async_answer_0(iid, res);
    303 }
    304 
    305 static 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 
    315 static 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 
    345 static 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 
    375 static 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 
    412 static 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);
    445280}
    446281
     
    450285        async_answer_0(iid, EOK);
    451286       
    452         while (true) {
     287        bool cont = true;
     288        while (cont) {
    453289                ipc_call_t call;
    454290                ipc_callid_t callid = async_get_call(&call);
    455291               
    456                 if (!IPC_GET_IMETHOD(call))
    457                         break;
    458                
    459292                switch (IPC_GET_IMETHOD(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);
     293                case IPC_M_PHONE_HUNGUP:
     294                        cont = false;
     295                        continue;
     296                case DRIVER_ADD_DEVICE:
     297                        driver_add_device(callid, &call);
    478298                        break;
    479299                default:
    480                         async_answer_0(callid, ENOTSUP);
     300                        async_answer_0(callid, ENOENT);
    481301                }
    482302        }
    483303}
    484304
    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  *
     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.).
    490310 */
    491311static void driver_connection_gen(ipc_callid_t iid, ipc_call_t *icall, bool drv)
     
    496316         */
    497317        devman_handle_t handle = IPC_GET_ARG2(*icall);
    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        
     318        ddf_fun_t *fun = driver_get_function(&functions, handle);
     319
    504320        if (fun == NULL) {
    505321                printf("%s: driver_connection_gen error - no function with handle"
     
    509325        }
    510326       
    511         if (fun->conn_handler != NULL) {
    512                 /* Driver has a custom connection handler. */
    513                 (*fun->conn_handler)(iid, icall, (void *)fun);
    514                 return;
    515         }
    516327       
    517328        /*
     
    529340                return;
    530341       
    531         while (true) {
     342        while (1) {
    532343                ipc_callid_t callid;
    533344                ipc_call_t call;
    534345                callid = async_get_call(&call);
    535346                sysarg_t method = IPC_GET_IMETHOD(call);
     347                int iface_idx;
    536348               
    537                 if (!method) {
     349                switch  (method) {
     350                case IPC_M_PHONE_HUNGUP:
    538351                        /* Close device function */
    539352                        if (fun->ops != NULL && fun->ops->close != NULL)
     
    541354                        async_answer_0(callid, EOK);
    542355                        return;
    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;
     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;
    555391                        }
    556392                       
    557393                        /*
    558                          * Function has no such interface and
    559                          * default handler is not provided.
     394                         * Get the corresponding interface for remote request
     395                         * handling ("remote interface").
    560396                         */
    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;
     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;
    566420                }
    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);
    606421        }
    607422}
     
    618433
    619434/** Function for handling connections to device driver. */
    620 static 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 
     435static void driver_connection(ipc_callid_t iid, ipc_call_t *icall)
     436{
    632437        /* Select interface */
    633         switch (conn_type) {
     438        switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
    634439        case DRIVER_DEVMAN:
    635440                /* Handle request from device manager */
     
    658463        ddf_dev_t *dev;
    659464
    660         dev = calloc(1, sizeof(ddf_dev_t));
     465        dev = malloc(sizeof(ddf_dev_t));
    661466        if (dev == NULL)
    662467                return NULL;
    663468
     469        memset(dev, 0, sizeof(ddf_dev_t));
    664470        return dev;
    665471}
     
    689495static void delete_device(ddf_dev_t *dev)
    690496{
    691         if (dev->driver_data != NULL)
    692                 free(dev->driver_data);
    693497        free(dev);
    694498}
    695499
    696 /** Delete function structure.
     500/** Delete device structure.
    697501 *
    698502 * @param dev           The device structure.
     
    701505{
    702506        clean_match_ids(&fun->match_ids);
    703         if (fun->driver_data != NULL)
    704                 free(fun->driver_data);
    705507        if (fun->name != NULL)
    706508                free(fun->name);
     
    708510}
    709511
    710 /** Increase device reference count. */
    711 static 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  */
    720 static 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  */
    732 static 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  */
    742 static 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. */
    753 extern 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 
    767512/** Create a DDF function node.
    768513 *
     
    796541                return NULL;
    797542
    798         /* Add one reference that will be dropped by ddf_fun_destroy() */
     543        fun->bound = false;
    799544        fun->dev = dev;
    800         fun_add_ref(fun);
    801 
    802         fun->bound = false;
    803545        fun->ftype = ftype;
    804546
     
    812554}
    813555
    814 /** Allocate driver-specific function data. */
    815 extern 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 
    830556/** Destroy DDF function node.
    831557 *
     
    838564{
    839565        assert(fun->bound == false);
    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);
     566        delete_function(fun);
    848567}
    849568
     
    870589int ddf_fun_bind(ddf_fun_t *fun)
    871590{
    872         assert(fun->bound == false);
    873591        assert(fun->name != NULL);
    874592       
     
    887605}
    888606
    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  */
    897 int 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  */
    918 int 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  */
    936 int 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 
    949607/** Add single match ID to inner function.
    950608 *
     
    969627                return ENOMEM;
    970628       
    971         match_id->id = str_dup(match_id_str);
    972         match_id->score = match_score;
     629        match_id->id = match_id_str;
     630        match_id->score = 90;
    973631       
    974632        add_match_id(&fun->match_ids, match_id);
     
    984642}
    985643
    986 /** Add exposed function to category.
     644/** Add exposed function to class.
    987645 *
    988646 * Must only be called when the function is bound.
    989647 */
    990 int ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
     648int ddf_fun_add_to_class(ddf_fun_t *fun, const char *class_name)
    991649{
    992650        assert(fun->bound == true);
    993651        assert(fun->ftype == fun_exposed);
    994652       
    995         return devman_add_device_to_category(fun->handle, cat_name);
     653        return devman_add_device_to_class(fun->handle, class_name);
    996654}
    997655
Note: See TracChangeset for help on using the changeset viewer.