1ddi_cb_register(9F)      Kernel Functions for Drivers      ddi_cb_register(9F)
2
3
4

NAME

6       ddi_cb_register,  ddi_cb_unregister  - register and unregister a device
7       driver callback handler
8

SYNOPSIS

10       #include <sys/sunddi.h>
11
12       int ddi_cb_register(dev_info_t *dip, ddi_cb_flags_t flags,
13             ddi_cb_func_t cbfunc, void *arg1, void *arg2,
14             ddi_cb_handle_t * ret_hdlp);
15
16
17       int ddi_cb_unregister(ddi_cb_handle_t hdl);
18
19

INTERFACE LEVEL

21       Solaris DDI specific (Solaris DDI).
22

PARAMETERS

24       ddi_cb_register()
25
26       dip         Pointer to the dev_info structure.
27
28
29       flags       Flags to determine which callback events can be handled.
30
31
32       cbfunc      Callback handler function.
33
34
35       arg1        First argument to the callback handler.
36
37
38       arg2        Second (optional) argument to the callback handler.
39
40
41       ret_hdlp    Pointer to return a handle to the registered callback.
42
43
44
45       ddi_cb_unregister()
46
47       hdl    Handle to the registered callback handler that is to be unregis‐
48              tered.
49
50

DESCRIPTION

52       The  ddi_cb_register()  function installs a callback handler which pro‐
53       cesses various actions that require the driver's attention while it  is
54       attached.  The  driver  specifies  which callback actions it can handle
55       through the flags parameter. With each relevant action,  the  specified
56       callback  function  passes  the  arg1 and arg2 arguments along with the
57       description of each callback event to the driver.
58
59
60       The ddi_cb_unregister() function removes a previously  installed  call‐
61       back handler and prevents future processing of actions.
62
63
64       The flags parameter consists of the following:
65
66       DDI_CB_FLAG_INTR    The   device   driver   participates  in  interrupt
67                           resource management. The device driver may  receive
68                           additional interrupt resources from the system, but
69                           only because it can accept callback notices inform‐
70                           ing  it  when  it has more or less resources avail‐
71                           able. Callback notices can occur at  anytime  after
72                           the  driver  is  attached.  Interrupt  availability
73                           varies based on the overall needs of the system.
74
75
76
77       The cdfunc is a callback handler with the following prototype:
78
79         typedef int (*ddi_cb_func_t)(dev_info_t *dip,
80                       ddi_cb_action_t action, void *cbarg,
81                       void *arg1, void *arg2);
82
83
84
85
86       The cbfunc routine with the arguments dip, action, cbarg, arg1 and arg2
87       is  called upon receipt of any callbacks for which the driver is regis‐
88       tered.  The callback handler returns DDI_SUCCESS if  the  callback  was
89       handled successfully, DDI_ENOTSUP if it received a callback action that
90       it did not know how to process, or DDI_FAILURE if it  has  an  internal
91       failure while processing an action.
92
93
94       The action parameter can be one of the following:
95
96       DDI_CB_INTR_ADD       For interrupt resource management, the driver has
97                             more available interrupts. The driver  can  allo‐
98                             cate  more interrupt vectors and then set up more
99                             interrupt    handling    functions    by    using
100                             ddi_intr_alloc(9F).
101
102
103       DDI_CB_INTR_REMOVE    For interrupt resource management, the driver has
104                             fewer  available  interrupts.  The  driver   must
105                             release  any  previously  allocated interrupts in
106                             excess  of  what  is  now  available   by   using
107                             ddi_intr_free(9F).
108
109
110
111       The  cbarg  parameter points to an action-specific argument. Each class
112       of registered actions specifies its own data structure that a  callback
113       handler should dereference when it receives those actions.
114
115
116       The   cbarg  parameter  is  defined  as  an  integer  in  the  case  of
117       DDI_CB_INTR_ADD and DDI_CB_INTR_REMOVE actions.  The  callback  handler
118       should  cast  the cbarg parameter to an integer. The integer represents
119       how many interrupts have been added or removed from  the  total  number
120       available to the device driver.
121
122
123       If a driver participates in interrupt resource management, it must reg‐
124       ister a callback  with  the  DDI_CB_FLAG_INTR  flag.  The  driver  then
125       receives  the  actions  DDI_CB_INTR_ADD and DDI_CB_INTR_REMOVE whenever
126       its interrupt availability has changed. The callback handler should use
127       the  interrupt functions ddi_intr_alloc(9F) and ddi_intr_free(9F) func‐
128       tions to respond accordingly. A driver is not required to allocate  all
129       interrupts  that  are available to it, but it is required to manage its
130       allocations so that it never uses more interrupts  than  are  currently
131       available.
132

RETURN VALUES

134       The ddi_cb_register() and ddi_cb_unregister() functions return:
135
136       DDI_SUCCESS     on success
137
138
139       DDI_EINVAL      An invalid parameter was given when registering a call‐
140                       back handler, or  an  invalid  handle  was  given  when
141                       unregistering.
142
143
144       DDI_EALREADY    An  attempt  was  made  to  register a callback handler
145                       while a previous registration still exists.
146
147
148
149       The cbfunc routine must return:
150
151       DDI_SUCCESS    on success
152
153
154       DDI_ENOTSUP    The device does not support the operation
155
156
157       DDI_FAILURE    Implementation specific failure
158
159

CONTEXT

161       These functions can be called from kernel, non-interrupt context.
162

EXAMPLES

164       Example 1 ddi_cb_register
165
166         /*
167             * attach(9F) routine.
168             *
169             * Creates soft state, registers callback handler, initializes
170             * hardware, and sets up interrupt handling for the driver.
171             */
172             xx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
173             {
174                 xx_state_t              *statep = NULL;
175                 xx_intr_t               *intrs = NULL;
176                 ddi_intr_handle_t       *hdls;
177                 ddi_cb_handle_t         cb_hdl;
178                 int                     instance;
179                 int                     type;
180                 int                     types;
181                 int                     nintrs;
182                 int                     nactual;
183                 int                     inum;
184
185                 /* Get device instance */
186                 instance = ddi_get_instance(dip);
187
188                 switch (cmd) {
189                 case DDI_ATTACH:
190
191                      /* Get soft state */
192                      if (ddi_soft_state_zalloc(state_list, instance) != 0)
193                              return (DDI_FAILURE);
194                      statep = ddi_get_soft_state(state_list, instance);
195                      ddi_set_driver_private(dip, (caddr_t)statep);
196                      statep->dip = dip;
197
198                      /* Initialize hardware */
199                      xx_initialize(statep);
200
201                      /* Register callback handler */
202                      if (ddi_cb_register(dip, DDI_CB_FLAG_INTR, xx_cbfunc,
203                          statep, NULL, &cb_hdl) != 0) {
204                              ddi_soft_state_free(state_list, instance);
205                              return (DDI_FAILURE);
206                      }
207                      statep->cb_hdl = cb_hdl;
208
209                      /* Select interrupt type */
210                      ddi_intr_get_supported_types(dip, &types);
211                      if (types & DDI_INTR_TYPE_MSIX) {
212                              type = DDI_INTR_TYPE_MSIX;
213                      } else if (types & DDI_INTR_TYPE_MSI) {
214                              type = DDI_INTR_TYPE_MSI;
215                      } else {
216                              type = DDI_INTR_TYPE_FIXED;
217                      }
218                      statep->type = type;
219
220                      /* Get number of supported interrupts */
221
222                      ddi_intr_get_nintrs(dip, type, &nintrs);
223
224                      /* Allocate interrupt handle array */
225                      statep->hdls_size = nintrs * sizeof (ddi_intr_handle_t);
226                      hdls = kmem_zalloc(statep->hdls_size, KMEM_SLEEP);
227
228                      /* Allocate interrupt setup array */
229                      statep->intrs_size = nintrs * sizeof (xx_intr_t);
230                      statep->intrs = kmem_zalloc(statep->intrs_size, KMEM_SLEEP);
231
232                      /* Allocate interrupt vectors */
233                      ddi_intr_alloc(dip, hdls, type, 0, nintrs, &nactual, 0);
234                      statep->nactual = nactual;
235
236                      /* Configure interrupt handling */
237                      xx_setup_interrupts(statep, nactual, statep->intrs);
238
239                      /* Install and enable interrupt handlers */
240                      for (inum = 0; inum < nactual; inum++) {
241                              ddi_intr_add_handler(&statep->hdls[inum],
242                                  statep->intrs[inum].inthandler,
243                                  statep->intrs[inum].arg1,
244                                  statep->intrs[inum].arg2);
245                              ddi_intr_enable(statep->hdls[inum]);
246                      }
247
248                      break;
249
250                 case DDI_RESUME:
251
252                         /* Get soft state */
253                         statep = ddi_get_soft_state(state_list, instance);
254                         if (statep == NULL)
255                                 return (DDI_FAILURE);
256
257                         /* Resume hardware */
258                         xx_resume(statep);
259
260                         break;
261                 }
262
263                 return (DDI_SUCESS);
264             }
265
266             /*
267              * detach(9F) routine.
268              *
269              * Stops the hardware, disables interrupt handling, unregisters
270              * a callback handler, and destroys the soft state for the driver.
271              */
272             xx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
273             {
274                 xx_state_t      *statep = NULL;
275                 int             instance;
276                 int             inum;
277
278
279                 /* Get device instance */
280                 instance = ddi_get_instance(dip);
281
282                 switch (cmd) {
283                 case DDI_DETACH:
284
285                         /* Get soft state */
286                         statep = ddi_get_soft_state(state_list, instance);
287                         if (statep == NULL)
288                                 return (DDI_FAILURE);
289
290                         /* Stop device */
291                         xx_uninitialize(statep);
292
293                         /* Disable and free interrupts */
294                         for (inum = 0; inum < statep->nactual; inum++) {
295                                 ddi_intr_disable(statep->hdls[inum]);
296                                 ddi_intr_remove_handler(statep->hdls[inum]);
297                                 ddi_intr_free(statep->hdls[inum]);
298                         }
299
300                         /* Unregister callback handler */
301                         ddi_cb_unregister(statep->cb_hdl);
302
303                         /* Free interrupt handle array */
304                         kmem_free(statep->hdls, statep->hdls_size);
305
306                         /* Free interrupt setup array */
307                         kmem_free(statep->intrs, statep->intrs_size);
308
309                         /* Free soft state */
310                         ddi_soft_state_free(state_list, instance);
311
312                         break;
313
314                 case DDI_SUSPEND:
315
316                         /* Get soft state */
317                         statep = ddi_get_soft_state(state_list, instance);
318                         if (statep == NULL)
319                                 return (DDI_FAILURE);
320
321                         /* Suspend hardware */
322                         xx_quiesce(statep);
323
324                         break;
325                 }
326
327                 return (DDI_SUCCESS);
328             }
329
330             /*
331              * (*ddi_cbfunc)() routine.
332              *
333              * Adapt interrupt usage when availability changes.
334              */
335             int
336             xx_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
337                 void *arg1, void *arg2)
338             {
339                 xx_state_t      *statep = (xx_state_t *)arg1;
340                 int             count;
341                 int             inum;
342                 int             nactual;
343
344                 switch (cbaction) {
345                 case DDI_CB_INTR_ADD:
346                 case DDI_CB_INTR_REMOVE:
347
348                      /* Get change in availability */
349                      count = (int)(uintptr_t)cbarg;
350
351                      /* Suspend hardware */
352                      xx_quiesce(statep);
353
354                      /* Tear down previous interrupt handling */
355                      for (inum = 0; inum < statep->nactual; inum++) {
356                              ddi_intr_disable(statep->hdls[inum]);
357                              ddi_intr_remove_handler(statep->hdls[inum]);
358                      }
359
360                      /* Adjust interrupt vector allocations */
361                      if (cbaction == DDI_CB_INTR_ADD) {
362
363                              /* Allocate additional interrupt vectors */
364                              ddi_intr_alloc(dip, statep->hdls, statep->type,
365                                  statep->nactual, count, &nactual, 0);
366
367                              /* Update actual count of available interrupts */
368                              statep->nactual += nactual;
369
370                      } else {
371
372                              /* Free removed interrupt vectors */
373                              for (inum = statep->nactual - count;
374                                  inum < statep->nactual; inum++) {
375                                      ddi_intr_free(statep->hdls[inum]);
376                              }
377
378                              /* Update actual count of available interrupts */
379                              statep->nactual -= count;
380                      }
381
382                      /* Configure interrupt handling */
383                      xx_setup_interrupts(statep, statep->nactual, statep->intrs);
384
385                      /* Install and enable interrupt handlers */
386                      for (inum = 0; inum < statep->nactual; inum++) {
387                              ddi_intr_add_handler(&statep->hdls[inum],
388                                  statep->intrs[inum].inthandler,
389                                  statep->intrs[inum].arg1,
390                                  statep->intrs[inum].arg2);
391                              ddi_intr_enable(statep->hdls[inum]);
392                      }
393
394                      /* Resume hardware */
395                      xx_resume(statep);
396
397                      break;
398
399              default:
400                      return (DDI_ENOTSUP);
401              }
402
403              return (DDI_SUCCESS);
404          }
405
406

ATTRIBUTES

408       See attributes(5) for descriptions of the following attributes:
409
410
411
412
413       ┌─────────────────────────────┬─────────────────────────────┐
414       │      ATTRIBUTE TYPE         │      ATTRIBUTE VALUE        │
415       ├─────────────────────────────┼─────────────────────────────┤
416       │Interface Stability          │Private                      │
417       ├─────────────────────────────┼─────────────────────────────┤
418       │MT-Level                     │Unsafe                       │
419       └─────────────────────────────┴─────────────────────────────┘
420

SEE ALSO

422       attributes(5),          ddi_intr_alloc(9F),          ddi_intr_free(9F),
423       ddi_intr_set_nreq(9F)
424

NOTES

426       Users  of  these  interfaces  that register for DDI_CB_FLAG_INTR become
427       participants in interrupt resource management. With that  participation
428       comes  a responsibility to properly adjust interrupt usage. In the case
429       of a DDI_CB_INTR_ADD action, the system guarantees that  a  driver  can
430       allocate  a total number of interrupt resources up to its new number of
431       available interrupts. The total number of interrupt  resources  is  the
432       sum  of  all  resources  allocated  by the function ddi_intr_alloc(9F),
433       minus all previously released by the function ddi_intr_free(9F). In the
434       case  of a DDI_CB_INTR_REMOVE action, the driver might have more inter‐
435       rupts allocated than are now currently available. It is  necessary  for
436       the driver to release the excess interrupts, or it will have a negative
437       impact on the interrupt availability for other drivers in the system.
438
439
440       A failure to release interrupts in  response  to  a  DDI_CB_INTR_REMOVE
441       callback generates the following warning on the system console:
442
443         WARNING: <driver><instance>: failed to release interrupts for
444                 IRM (nintrs = ##, navail=##).
445
446
447
448
449       Participation  in interrupt resource management ends when a driver uses
450       the ddi_cb_unregister() function to unregister its  callback  function.
451       The  callback function must still operate properly until after the call
452       to the ddi_cb_unregister() function completes.  If  addinterrupts  were
453       given  to  the driver because of its participation, then a final use of
454       the callback function occurs to release the additional interrupts.  The
455       call to the ddi_cb_unregister() function blocks until the final  use of
456       the registered callback function is finished.
457
458
459
460SunOS 5.11                        30 Jan 2009              ddi_cb_register(9F)
Impressum