Changeset 00aece0 in mainline for uspace/lib/drv/generic/driver.c


Ignore:
Timestamp:
2012-02-18T16:47:38Z (14 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
4449c6c
Parents:
bd5f3b7 (diff), f943dd3 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge mainline changes.

File:
1 edited

Legend:

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

    rbd5f3b7 r00aece0  
    6363
    6464/** Devices */
     65LIST_INITIALIZE(devices);
     66FIBRIL_MUTEX_INITIALIZE(devices_mutex);
     67
     68/** Functions */
    6569LIST_INITIALIZE(functions);
    6670FIBRIL_MUTEX_INITIALIZE(functions_mutex);
    6771
    68 /** Interrupts */
    69 static interrupt_context_list_t interrupt_contexts;
    70 
    71 static irq_cmd_t default_cmds[] = {
    72         {
    73                 .cmd = CMD_ACCEPT
    74         }
    75 };
    76 
    77 static irq_code_t default_pseudocode = {
    78         sizeof(default_cmds) / sizeof(irq_cmd_t),
    79         default_cmds
    80 };
    81 
    8272static ddf_dev_t *create_device(void);
    8373static void delete_device(ddf_dev_t *);
     74static void dev_add_ref(ddf_dev_t *);
     75static void dev_del_ref(ddf_dev_t *);
     76static void fun_add_ref(ddf_fun_t *);
     77static void fun_del_ref(ddf_fun_t *);
    8478static remote_handler_t *function_get_default_handler(ddf_fun_t *);
    8579static void *function_get_ops(ddf_fun_t *, dev_inferface_idx_t);
    86 
    87 static void driver_irq_handler(ipc_callid_t iid, ipc_call_t *icall)
    88 {
    89         int id = (int)IPC_GET_IMETHOD(*icall);
    90         interrupt_context_t *ctx;
    91        
    92         ctx = find_interrupt_context_by_id(&interrupt_contexts, id);
    93         if (ctx != NULL && ctx->handler != NULL)
    94                 (*ctx->handler)(ctx->dev, iid, icall);
    95 }
    96 
    97 interrupt_context_t *create_interrupt_context(void)
    98 {
    99         interrupt_context_t *ctx;
    100        
    101         ctx = (interrupt_context_t *) malloc(sizeof(interrupt_context_t));
    102         if (ctx != NULL)
    103                 memset(ctx, 0, sizeof(interrupt_context_t));
    104        
    105         return ctx;
    106 }
    107 
    108 void delete_interrupt_context(interrupt_context_t *ctx)
    109 {
    110         if (ctx != NULL)
    111                 free(ctx);
    112 }
    113 
    114 void init_interrupt_context_list(interrupt_context_list_t *list)
    115 {
    116         memset(list, 0, sizeof(interrupt_context_list_t));
    117         fibril_mutex_initialize(&list->mutex);
    118         list_initialize(&list->contexts);
    119 }
    120 
    121 void
    122 add_interrupt_context(interrupt_context_list_t *list, interrupt_context_t *ctx)
    123 {
    124         fibril_mutex_lock(&list->mutex);
    125         ctx->id = list->curr_id++;
    126         list_append(&ctx->link, &list->contexts);
    127         fibril_mutex_unlock(&list->mutex);
    128 }
    129 
    130 void remove_interrupt_context(interrupt_context_list_t *list,
    131     interrupt_context_t *ctx)
    132 {
    133         fibril_mutex_lock(&list->mutex);
    134         list_remove(&ctx->link);
    135         fibril_mutex_unlock(&list->mutex);
    136 }
    137 
    138 interrupt_context_t *
    139 find_interrupt_context_by_id(interrupt_context_list_t *list, int id)
    140 {
    141         interrupt_context_t *ctx;
    142        
    143         fibril_mutex_lock(&list->mutex);
    144        
    145         list_foreach(list->contexts, link) {
    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         }
    152        
    153         fibril_mutex_unlock(&list->mutex);
    154         return NULL;
    155 }
    156 
    157 interrupt_context_t *
    158 find_interrupt_context(interrupt_context_list_t *list, ddf_dev_t *dev, int irq)
    159 {
    160         interrupt_context_t *ctx;
    161        
    162         fibril_mutex_lock(&list->mutex);
    163        
    164         list_foreach(list->contexts, link) {
    165                 ctx = list_get_instance(link, interrupt_context_t, link);
    166                 if (ctx->irq == irq && ctx->dev == dev) {
    167                         fibril_mutex_unlock(&list->mutex);
    168                         return ctx;
    169                 }
    170         }
    171        
    172         fibril_mutex_unlock(&list->mutex);
    173         return NULL;
    174 }
    175 
    176 
    177 int
    178 register_interrupt_handler(ddf_dev_t *dev, int irq, interrupt_handler_t *handler,
    179     irq_code_t *pseudocode)
    180 {
    181         interrupt_context_t *ctx = create_interrupt_context();
    182        
    183         ctx->dev = dev;
    184         ctx->irq = irq;
    185         ctx->handler = handler;
    186        
    187         add_interrupt_context(&interrupt_contexts, ctx);
    188        
    189         if (pseudocode == NULL)
    190                 pseudocode = &default_pseudocode;
    191        
    192         int res = register_irq(irq, dev->handle, ctx->id, pseudocode);
    193         if (res != EOK) {
    194                 remove_interrupt_context(&interrupt_contexts, ctx);
    195                 delete_interrupt_context(ctx);
    196         }
    197 
    198         return res;
    199 }
    200 
    201 int unregister_interrupt_handler(ddf_dev_t *dev, int irq)
    202 {
    203         interrupt_context_t *ctx = find_interrupt_context(&interrupt_contexts,
    204             dev, irq);
    205         int res = unregister_irq(irq, dev->handle);
    206        
    207         if (ctx != NULL) {
    208                 remove_interrupt_context(&interrupt_contexts, ctx);
    209                 delete_interrupt_context(ctx);
    210         }
    211        
    212         return res;
    213 }
    21480
    21581static void add_to_functions_list(ddf_fun_t *fun)
     
    22793}
    22894
    229 static ddf_fun_t *driver_get_function(list_t *functions, devman_handle_t handle)
     95static ddf_dev_t *driver_get_device(devman_handle_t handle)
     96{
     97        ddf_dev_t *dev = NULL;
     98       
     99        assert(fibril_mutex_is_locked(&devices_mutex));
     100       
     101        list_foreach(devices, link) {
     102                dev = list_get_instance(link, ddf_dev_t, link);
     103                if (dev->handle == handle)
     104                        return dev;
     105        }
     106       
     107        return NULL;
     108}
     109
     110static ddf_fun_t *driver_get_function(devman_handle_t handle)
    230111{
    231112        ddf_fun_t *fun = NULL;
    232113       
    233         fibril_mutex_lock(&functions_mutex);
    234        
    235         list_foreach(*functions, link) {
     114        assert(fibril_mutex_is_locked(&functions_mutex));
     115       
     116        list_foreach(functions, link) {
    236117                fun = list_get_instance(link, ddf_fun_t, link);
    237                 if (fun->handle == handle) {
    238                         fibril_mutex_unlock(&functions_mutex);
     118                if (fun->handle == handle)
    239119                        return fun;
    240                 }
    241         }
    242        
    243         fibril_mutex_unlock(&functions_mutex);
     120        }
    244121       
    245122        return NULL;
    246123}
    247124
    248 static void driver_add_device(ipc_callid_t iid, ipc_call_t *icall)
     125static void driver_dev_add(ipc_callid_t iid, ipc_call_t *icall)
    249126{
    250127        char *dev_name = NULL;
     
    252129       
    253130        devman_handle_t dev_handle = IPC_GET_ARG1(*icall);
    254         devman_handle_t parent_fun_handle = IPC_GET_ARG2(*icall);
     131        devman_handle_t parent_fun_handle = IPC_GET_ARG2(*icall);
    255132       
    256133        ddf_dev_t *dev = create_device();
     134
     135        /* Add one reference that will be dropped by driver_dev_remove() */
     136        dev_add_ref(dev);
    257137        dev->handle = dev_handle;
    258138
     
    266146        (void) parent_fun_handle;
    267147       
    268         res = driver->driver_ops->add_device(dev);
    269         if (res != EOK)
    270                 delete_device(dev);
     148        res = driver->driver_ops->dev_add(dev);
     149       
     150        if (res != EOK) {
     151                dev_del_ref(dev);
     152                async_answer_0(iid, res);
     153                return;
     154        }
     155       
     156        fibril_mutex_lock(&devices_mutex);
     157        list_append(&dev->link, &devices);
     158        fibril_mutex_unlock(&devices_mutex);
    271159       
    272160        async_answer_0(iid, res);
     161}
     162
     163static void driver_dev_remove(ipc_callid_t iid, ipc_call_t *icall)
     164{
     165        devman_handle_t devh;
     166        ddf_dev_t *dev;
     167        int rc;
     168       
     169        devh = IPC_GET_ARG1(*icall);
     170       
     171        fibril_mutex_lock(&devices_mutex);
     172        dev = driver_get_device(devh);
     173        if (dev != NULL)
     174                dev_add_ref(dev);
     175        fibril_mutex_unlock(&devices_mutex);
     176       
     177        if (dev == NULL) {
     178                async_answer_0(iid, ENOENT);
     179                return;
     180        }
     181       
     182        if (driver->driver_ops->dev_remove != NULL)
     183                rc = driver->driver_ops->dev_remove(dev);
     184        else
     185                rc = ENOTSUP;
     186       
     187        if (rc == EOK)
     188                dev_del_ref(dev);
     189       
     190        async_answer_0(iid, (sysarg_t) rc);
     191}
     192
     193static void driver_dev_gone(ipc_callid_t iid, ipc_call_t *icall)
     194{
     195        devman_handle_t devh;
     196        ddf_dev_t *dev;
     197        int rc;
     198       
     199        devh = IPC_GET_ARG1(*icall);
     200       
     201        fibril_mutex_lock(&devices_mutex);
     202        dev = driver_get_device(devh);
     203        if (dev != NULL)
     204                dev_add_ref(dev);
     205        fibril_mutex_unlock(&devices_mutex);
     206       
     207        if (dev == NULL) {
     208                async_answer_0(iid, ENOENT);
     209                return;
     210        }
     211       
     212        if (driver->driver_ops->dev_gone != NULL)
     213                rc = driver->driver_ops->dev_gone(dev);
     214        else
     215                rc = ENOTSUP;
     216       
     217        if (rc == EOK)
     218                dev_del_ref(dev);
     219       
     220        async_answer_0(iid, (sysarg_t) rc);
     221}
     222
     223static void driver_fun_online(ipc_callid_t iid, ipc_call_t *icall)
     224{
     225        devman_handle_t funh;
     226        ddf_fun_t *fun;
     227        int rc;
     228       
     229        funh = IPC_GET_ARG1(*icall);
     230       
     231        /*
     232         * Look the function up. Bump reference count so that
     233         * the function continues to exist until we return
     234         * from the driver.
     235         */
     236        fibril_mutex_lock(&functions_mutex);
     237       
     238        fun = driver_get_function(funh);
     239        if (fun != NULL)
     240                fun_add_ref(fun);
     241       
     242        fibril_mutex_unlock(&functions_mutex);
     243       
     244        if (fun == NULL) {
     245                async_answer_0(iid, ENOENT);
     246                return;
     247        }
     248       
     249        /* Call driver entry point */
     250        if (driver->driver_ops->fun_online != NULL)
     251                rc = driver->driver_ops->fun_online(fun);
     252        else
     253                rc = ENOTSUP;
     254       
     255        fun_del_ref(fun);
     256       
     257        async_answer_0(iid, (sysarg_t) rc);
     258}
     259
     260static void driver_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
     261{
     262        devman_handle_t funh;
     263        ddf_fun_t *fun;
     264        int rc;
     265       
     266        funh = IPC_GET_ARG1(*icall);
     267       
     268        /*
     269         * Look the function up. Bump reference count so that
     270         * the function continues to exist until we return
     271         * from the driver.
     272         */
     273        fibril_mutex_lock(&functions_mutex);
     274       
     275        fun = driver_get_function(funh);
     276        if (fun != NULL)
     277                fun_add_ref(fun);
     278       
     279        fibril_mutex_unlock(&functions_mutex);
     280       
     281        if (fun == NULL) {
     282                async_answer_0(iid, ENOENT);
     283                return;
     284        }
     285       
     286        /* Call driver entry point */
     287        if (driver->driver_ops->fun_offline != NULL)
     288                rc = driver->driver_ops->fun_offline(fun);
     289        else
     290                rc = ENOTSUP;
     291       
     292        async_answer_0(iid, (sysarg_t) rc);
    273293}
    274294
     
    286306               
    287307                switch (IPC_GET_IMETHOD(call)) {
    288                 case DRIVER_ADD_DEVICE:
    289                         driver_add_device(callid, &call);
     308                case DRIVER_DEV_ADD:
     309                        driver_dev_add(callid, &call);
     310                        break;
     311                case DRIVER_DEV_REMOVE:
     312                        driver_dev_remove(callid, &call);
     313                        break;
     314                case DRIVER_DEV_GONE:
     315                        driver_dev_gone(callid, &call);
     316                        break;
     317                case DRIVER_FUN_ONLINE:
     318                        driver_fun_online(callid, &call);
     319                        break;
     320                case DRIVER_FUN_OFFLINE:
     321                        driver_fun_offline(callid, &call);
    290322                        break;
    291323                default:
    292                         async_answer_0(callid, ENOENT);
     324                        async_answer_0(callid, ENOTSUP);
    293325                }
    294326        }
     
    308340         */
    309341        devman_handle_t handle = IPC_GET_ARG2(*icall);
    310         ddf_fun_t *fun = driver_get_function(&functions, handle);
     342
     343        fibril_mutex_lock(&functions_mutex);
     344        ddf_fun_t *fun = driver_get_function(handle);
     345        fibril_mutex_unlock(&functions_mutex);
     346        /* XXX Need a lock on fun */
    311347       
    312348        if (fun == NULL) {
     
    466502        ddf_dev_t *dev;
    467503
    468         dev = malloc(sizeof(ddf_dev_t));
     504        dev = calloc(1, sizeof(ddf_dev_t));
    469505        if (dev == NULL)
    470506                return NULL;
    471507
    472         memset(dev, 0, sizeof(ddf_dev_t));
    473508        return dev;
    474509}
     
    498533static void delete_device(ddf_dev_t *dev)
    499534{
     535        if (dev->driver_data != NULL)
     536                free(dev->driver_data);
    500537        free(dev);
    501538}
    502539
    503 /** Delete device structure.
     540/** Delete function structure.
    504541 *
    505542 * @param dev           The device structure.
     
    508545{
    509546        clean_match_ids(&fun->match_ids);
     547        if (fun->driver_data != NULL)
     548                free(fun->driver_data);
    510549        if (fun->name != NULL)
    511550                free(fun->name);
     
    513552}
    514553
     554/** Increase device reference count. */
     555static void dev_add_ref(ddf_dev_t *dev)
     556{
     557        atomic_inc(&dev->refcnt);
     558}
     559
     560/** Decrease device reference count.
     561 *
     562 * Free the device structure if the reference count drops to zero.
     563 */
     564static void dev_del_ref(ddf_dev_t *dev)
     565{
     566        if (atomic_predec(&dev->refcnt) == 0)
     567                delete_device(dev);
     568}
     569
     570/** Increase function reference count.
     571 *
     572 * This also increases reference count on the device. The device structure
     573 * will thus not be deallocated while there are some associated function
     574 * structures.
     575 */
     576static void fun_add_ref(ddf_fun_t *fun)
     577{
     578        dev_add_ref(fun->dev);
     579        atomic_inc(&fun->refcnt);
     580}
     581
     582/** Decrease function reference count.
     583 *
     584 * Free the function structure if the reference count drops to zero.
     585 */
     586static void fun_del_ref(ddf_fun_t *fun)
     587{
     588        ddf_dev_t *dev = fun->dev;
     589
     590        if (atomic_predec(&fun->refcnt) == 0)
     591                delete_function(fun);
     592
     593        dev_del_ref(dev);
     594}
     595
     596/** Allocate driver-specific device data. */
     597void *ddf_dev_data_alloc(ddf_dev_t *dev, size_t size)
     598{
     599        void *data;
     600
     601        assert(dev->driver_data == NULL);
     602
     603        data = calloc(1, size);
     604        if (data == NULL)
     605                return NULL;
     606
     607        dev->driver_data = data;
     608        return data;
     609}
     610
    515611/** Create a DDF function node.
    516612 *
     
    544640                return NULL;
    545641
     642        /* Add one reference that will be dropped by ddf_fun_destroy() */
     643        fun->dev = dev;
     644        fun_add_ref(fun);
     645
    546646        fun->bound = false;
    547         fun->dev = dev;
    548647        fun->ftype = ftype;
    549648
     
    557656}
    558657
     658/** Allocate driver-specific function data. */
     659void *ddf_fun_data_alloc(ddf_fun_t *fun, size_t size)
     660{
     661        void *data;
     662
     663        assert(fun->bound == false);
     664        assert(fun->driver_data == NULL);
     665
     666        data = calloc(1, size);
     667        if (data == NULL)
     668                return NULL;
     669
     670        fun->driver_data = data;
     671        return data;
     672}
     673
    559674/** Destroy DDF function node.
    560675 *
     
    567682{
    568683        assert(fun->bound == false);
    569         delete_function(fun);
     684
     685        /*
     686         * Drop the reference added by ddf_fun_create(). This will deallocate
     687         * the function as soon as all other references are dropped (i.e.
     688         * as soon control leaves all driver entry points called in context
     689         * of this function.
     690         */
     691        fun_del_ref(fun);
    570692}
    571693
     
    614736 * the function invisible to the system.
    615737 *
    616  * @param fun           Function to bind
     738 * @param fun           Function to unbind
    617739 * @return              EOK on success or negative error code
    618740 */
     
    623745        assert(fun->bound == true);
    624746       
    625         add_to_functions_list(fun);
    626747        res = devman_remove_function(fun->handle);
    627748        if (res != EOK)
     
    631752       
    632753        fun->bound = false;
     754        return EOK;
     755}
     756
     757/** Online function.
     758 *
     759 * @param fun           Function to online
     760 * @return              EOK on success or negative error code
     761 */
     762int ddf_fun_online(ddf_fun_t *fun)
     763{
     764        int res;
     765       
     766        assert(fun->bound == true);
     767       
     768        res = devman_drv_fun_online(fun->handle);
     769        if (res != EOK)
     770                return res;
     771       
     772        return EOK;
     773}
     774
     775/** Offline function.
     776 *
     777 * @param fun           Function to offline
     778 * @return              EOK on success or negative error code
     779 */
     780int ddf_fun_offline(ddf_fun_t *fun)
     781{
     782        int res;
     783       
     784        assert(fun->bound == true);
     785       
     786        res = devman_drv_fun_offline(fun->handle);
     787        if (res != EOK)
     788                return res;
     789       
    633790        return EOK;
    634791}
     
    657814       
    658815        match_id->id = str_dup(match_id_str);
    659         match_id->score = 90;
     816        match_id->score = match_score;
    660817       
    661818        add_match_id(&fun->match_ids, match_id);
     
    693850        driver = drv;
    694851       
    695         /* Initialize the list of interrupt contexts. */
    696         init_interrupt_context_list(&interrupt_contexts);
    697        
    698         /* Set generic interrupt handler. */
    699         async_set_interrupt_received(driver_irq_handler);
     852        /* Initialize interrupt module */
     853        interrupt_init();
    700854       
    701855        /*
     
    703857         * incoming connections.
    704858         */
    705         rc = devman_driver_register(driver->name, driver_connection);
     859        async_set_client_connection(driver_connection);
     860        rc = devman_driver_register(driver->name);
    706861        if (rc != EOK) {
    707862                printf("Error: Failed to register driver with device manager "
Note: See TracChangeset for help on using the changeset viewer.