Merge commit '9276b3991ba20d5a5660887ba81b0bc7bed25a0c'
[unleashed.git] / share / man / man9f / ddi_cb_register.9f
blobda3ee4cb54766514d6cf35d13b7d369993132bf4
1 '\" te
2 .\" Copyright (c) 2009, Sun Microsystems, Inc.
3 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
4 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
5 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
6 .TH DDI_CB_REGISTER 9F "Jan 30, 2009"
7 .SH NAME
8 ddi_cb_register, ddi_cb_unregister \- register and unregister a device driver
9 callback handler
10 .SH SYNOPSIS
11 .LP
12 .nf
13 #include <sys/sunddi.h>
15 \fBint\fR \fBddi_cb_register\fR(\fBdev_info_t *\fR\fIdip\fR, \fBddi_cb_flags_t\fR \fIflags\fR,
16       \fBddi_cb_func_t\fR \fIcbfunc\fR, \fBvoid *\fR\fIarg1\fR, \fBvoid *\fR\fIarg2\fR,
17       \fBddi_cb_handle_t *\fR \fIret_hdlp\fR);
18 .fi
20 .LP
21 .nf
22 \fBint\fR \fBddi_cb_unregister\fR(\fBddi_cb_handle_t\fR \fIhdl\fR);
23 .fi
25 .SH INTERFACE LEVEL
26 .LP
27 Solaris DDI specific (Solaris DDI).
28 .SH PARAMETERS
29 .LP
30 \fBddi_cb_register()\fR
31 .sp
32 .ne 2
33 .na
34 \fB\fIdip\fR\fR
35 .ad
36 .RS 12n
37 Pointer to the \fBdev_info\fR structure.
38 .RE
40 .sp
41 .ne 2
42 .na
43 \fB\fIflags\fR\fR
44 .ad
45 .RS 12n
46 Flags to determine which callback events can be handled.
47 .RE
49 .sp
50 .ne 2
51 .na
52 \fB\fIcbfunc\fR\fR
53 .ad
54 .RS 12n
55 Callback handler function.
56 .RE
58 .sp
59 .ne 2
60 .na
61 \fB\fIarg1\fR\fR
62 .ad
63 .RS 12n
64 First argument to the callback handler.
65 .RE
67 .sp
68 .ne 2
69 .na
70 \fB\fIarg2\fR\fR
71 .ad
72 .RS 12n
73 Second (optional) argument to the callback handler.
74 .RE
76 .sp
77 .ne 2
78 .na
79 \fB\fIret_hdlp\fR\fR
80 .ad
81 .RS 12n
82 Pointer to return a handle to the registered callback.
83 .RE
85 .sp
86 .LP
87 \fBddi_cb_unregister()\fR
88 .sp
89 .ne 2
90 .na
91 \fB\fIhdl\fR\fR
92 .ad
93 .RS 7n
94 Handle to the registered callback handler that is to be unregistered.
95 .RE
97 .SH DESCRIPTION
98 .LP
99 The \fBddi_cb_register()\fR function installs a callback handler which
100 processes various actions that require the driver's attention while it is
101 attached. The driver specifies which callback actions it can handle through the
102 flags parameter. With each relevant action, the specified callback function
103 passes the \fIarg1\fR and \fIarg2\fR arguments along with the description of
104 each callback event to the driver.
107 The \fBddi_cb_unregister()\fR function removes a previously installed callback
108 handler and prevents future processing of actions.
111 The \fIflags\fR parameter consists of the following:
113 .ne 2
115 \fB\fBDDI_CB_FLAG_INTR\fR\fR
117 .RS 20n
118 The device driver participates in interrupt resource management. The device
119 driver may receive additional interrupt resources from the system, but only
120 because it can accept callback notices informing it when it has more or less
121 resources available. Callback notices can occur at anytime after the driver is
122 attached. Interrupt availability varies based on the overall needs of the
123 system.
128 The cdfunc is a callback handler with the following prototype:
130 .in +2
132 typedef int (*ddi_cb_func_t)(dev_info_t *dip,
133               ddi_cb_action_t action, void *cbarg,
134               void *arg1, void *arg2);
136 .in -2
141 The \fIcbfunc\fR routine with the arguments \fIdip\fR, \fIaction\fR,
142 \fIcbarg\fR, \fIarg1\fR and \fIarg2\fR is called upon receipt of any callbacks
143 for which the driver is registered.  The callback handler returns
144 \fBDDI_SUCCESS\fR if the callback was handled successfully, \fBDDI_ENOTSUP\fR
145 if it received a callback action that it did not know how to process, or
146 \fBDDI_FAILURE\fR if it has an internal failure while processing an action.
149 The \fIaction\fR parameter can be one of the following:
151 .ne 2
153 \fB\fIDDI_CB_INTR_ADD\fR\fR
155 .RS 22n
156 For interrupt resource management, the driver has more available interrupts.
157 The driver can allocate more interrupt vectors and then set up more interrupt
158 handling functions by using \fBddi_intr_alloc\fR(9F).
162 .ne 2
164 \fB\fIDDI_CB_INTR_REMOVE\fR\fR
166 .RS 22n
167 For interrupt resource management, the driver has fewer available interrupts.
168 The driver must release any previously allocated interrupts in excess of what
169 is now available by using \fBddi_intr_free\fR(9F).
174 The \fIcbarg\fR parameter points to an action-specific argument. Each class of
175 registered actions specifies its own data structure that a callback handler
176 should dereference when it receives those actions.
179 The \fIcbarg\fR parameter is defined as an integer in the case of
180 \fBDDI_CB_INTR_ADD\fR and \fBDDI_CB_INTR_REMOVE\fR actions. The callback
181 handler should cast the \fIcbarg\fR parameter to an integer. The integer
182 represents how many interrupts have been added or removed from the total number
183 available to the device driver.
186 If a driver participates in interrupt resource management, it must register a
187 callback with the \fBDDI_CB_FLAG_INTR\fR flag. The driver then receives the
188 actions \fBDDI_CB_INTR_ADD\fR and \fBDDI_CB_INTR_REMOVE\fR whenever its
189 interrupt availability has changed. The callback handler should use the
190 interrupt functions \fBddi_intr_alloc\fR(9F) and \fBddi_intr_free\fR(9F)
191 functions to respond accordingly. A driver is not required to allocate all
192 interrupts that are available to it, but it is required to manage its
193 allocations so that it never uses more interrupts than are currently available.
194 .SH RETURN VALUES
196 The \fBddi_cb_register()\fR and \fBddi_cb_unregister()\fR functions return:
198 .ne 2
200 \fB\fBDDI_SUCCESS\fR\fR
202 .RS 16n
203 on success
207 .ne 2
209 \fB\fBDDI_EINVAL\fR\fR
211 .RS 16n
212 An invalid parameter was given when registering a callback handler, or an
213 invalid handle was given when unregistering.
217 .ne 2
219 \fB\fBDDI_EALREADY\fR\fR
221 .RS 16n
222 An attempt was made to register a callback handler while a previous
223 registration still exists.
228 The \fIcbfunc\fR routine must return:
230 .ne 2
232 \fB\fBDDI_SUCCESS\fR\fR
234 .RS 15n
235 on success
239 .ne 2
241 \fB\fBDDI_ENOTSUP\fR\fR
243 .RS 15n
244 The device does not support the operation
248 .ne 2
250 \fB\fBDDI_FAILURE\fR\fR
252 .RS 15n
253 Implementation specific failure
256 .SH CONTEXT
258 These functions can be called from kernel, non-interrupt context.
259 .SH EXAMPLES
261 \fBExample 1 \fRddi_cb_register
263 .in +2
266     * attach(9E) routine.
267     *
268     * Creates soft state, registers callback handler, initializes
269     * hardware, and sets up interrupt handling for the driver.
270     */
271     xx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
272     {
273         xx_state_t              *statep = NULL;
274         xx_intr_t               *intrs = NULL;
275         ddi_intr_handle_t       *hdls;
276         ddi_cb_handle_t         cb_hdl;
277         int                     instance;
278         int                     type;
279         int                     types;
280         int                     nintrs;
281         int                     nactual;
282         int                     inum;
284         /* Get device instance */
285         instance = ddi_get_instance(dip);
287         switch (cmd) {
288         case DDI_ATTACH:
290              /* Get soft state */
291              if (ddi_soft_state_zalloc(state_list, instance) != 0)
292                      return (DDI_FAILURE);
293              statep = ddi_get_soft_state(state_list, instance);
294              ddi_set_driver_private(dip, (caddr_t)statep);
295              statep->dip = dip;
297              /* Initialize hardware */
298              xx_initialize(statep);
300              /* Register callback handler */
301              if (ddi_cb_register(dip, DDI_CB_FLAG_INTR, xx_cbfunc,
302                  statep, NULL, &cb_hdl) != 0) {
303                      ddi_soft_state_free(state_list, instance);
304                      return (DDI_FAILURE);
305              }
306              statep->cb_hdl = cb_hdl;
308              /* Select interrupt type */
309              ddi_intr_get_supported_types(dip, &types);
310              if (types & DDI_INTR_TYPE_MSIX) {
311                      type = DDI_INTR_TYPE_MSIX;
312              } else if (types & DDI_INTR_TYPE_MSI) {
313                      type = DDI_INTR_TYPE_MSI;
314              } else {
315                      type = DDI_INTR_TYPE_FIXED;
316              }
317              statep->type = type;
319              /* Get number of supported interrupts */
321              ddi_intr_get_nintrs(dip, type, &nintrs);
323              /* Allocate interrupt handle array */
324              statep->hdls_size = nintrs * sizeof (ddi_intr_handle_t);
325              hdls = kmem_zalloc(statep->hdls_size, KMEM_SLEEP);
327              /* Allocate interrupt setup array */
328              statep->intrs_size = nintrs * sizeof (xx_intr_t);
329              statep->intrs = kmem_zalloc(statep->intrs_size, KMEM_SLEEP);
331              /* Allocate interrupt vectors */
332              ddi_intr_alloc(dip, hdls, type, 0, nintrs, &nactual, 0);
333              statep->nactual = nactual;
335              /* Configure interrupt handling */
336              xx_setup_interrupts(statep, nactual, statep->intrs);
338              /* Install and enable interrupt handlers */
339              for (inum = 0; inum < nactual; inum++) {
340                      ddi_intr_add_handler(&statep->hdls[inum],
341                          statep->intrs[inum].inthandler,
342                          statep->intrs[inum].arg1,
343                          statep->intrs[inum].arg2);
344                      ddi_intr_enable(statep->hdls[inum]);
345              }
347              break;
349         case DDI_RESUME:
351                 /* Get soft state */
352                 statep = ddi_get_soft_state(state_list, instance);
353                 if (statep == NULL)
354                         return (DDI_FAILURE);
356                 /* Resume hardware */
357                 xx_resume(statep);
359                 break;
360         }
362         return (DDI_SUCCESS);
363     }
365     /*
366      * detach(9E) routine.
367      *
368      * Stops the hardware, disables interrupt handling, unregisters
369      * a callback handler, and destroys the soft state for the driver.
370      */
371     xx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
372     {
373         xx_state_t      *statep = NULL;
374         int             instance;
375         int             inum;
378         /* Get device instance */
379         instance = ddi_get_instance(dip);
381         switch (cmd) {
382         case DDI_DETACH:
384                 /* Get soft state */
385                 statep = ddi_get_soft_state(state_list, instance);
386                 if (statep == NULL)
387                         return (DDI_FAILURE);
389                 /* Stop device */
390                 xx_uninitialize(statep);
392                 /* Disable and free interrupts */
393                 for (inum = 0; inum < statep->nactual; inum++) {
394                         ddi_intr_disable(statep->hdls[inum]);
395                         ddi_intr_remove_handler(statep->hdls[inum]);
396                         ddi_intr_free(statep->hdls[inum]);
397                 }
399                 /* Unregister callback handler */
400                 ddi_cb_unregister(statep->cb_hdl);
402                 /* Free interrupt handle array */
403                 kmem_free(statep->hdls, statep->hdls_size);
405                 /* Free interrupt setup array */
406                 kmem_free(statep->intrs, statep->intrs_size);
408                 /* Free soft state */
409                 ddi_soft_state_free(state_list, instance);
411                 break;
413         case DDI_SUSPEND:
415                 /* Get soft state */
416                 statep = ddi_get_soft_state(state_list, instance);
417                 if (statep == NULL)
418                         return (DDI_FAILURE);
420                 /* Suspend hardware */
421                 xx_quiesce(statep);
423                 break;
424         }
426         return (DDI_SUCCESS);
427     }
429     /*
430      * (*ddi_cbfunc)() routine.
431      *
432      * Adapt interrupt usage when availability changes.
433      */
434     int
435     xx_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
436         void *arg1, void *arg2)
437     {
438         xx_state_t      *statep = (xx_state_t *)arg1;
439         int             count;
440         int             inum;
441         int             nactual;
443         switch (cbaction) {
444         case DDI_CB_INTR_ADD:
445         case DDI_CB_INTR_REMOVE:
447              /* Get change in availability */
448              count = (int)(uintptr_t)cbarg;
450              /* Suspend hardware */
451              xx_quiesce(statep);
453              /* Tear down previous interrupt handling */
454              for (inum = 0; inum < statep->nactual; inum++) {
455                      ddi_intr_disable(statep->hdls[inum]);
456                      ddi_intr_remove_handler(statep->hdls[inum]);
457              }
459              /* Adjust interrupt vector allocations */
460              if (cbaction == DDI_CB_INTR_ADD) {
462                      /* Allocate additional interrupt vectors */
463                      ddi_intr_alloc(dip, statep->hdls, statep->type,
464                          statep->nactual, count, &nactual, 0);
466                      /* Update actual count of available interrupts */
467                      statep->nactual += nactual;
469              } else {
471                      /* Free removed interrupt vectors */
472                      for (inum = statep->nactual - count;
473                          inum < statep->nactual; inum++) {
474                              ddi_intr_free(statep->hdls[inum]);
475                      }
477                      /* Update actual count of available interrupts */
478                      statep->nactual -= count;
479              }
481              /* Configure interrupt handling */
482              xx_setup_interrupts(statep, statep->nactual, statep->intrs);
484              /* Install and enable interrupt handlers */
485              for (inum = 0; inum < statep->nactual; inum++) {
486                      ddi_intr_add_handler(&statep->hdls[inum],
487                          statep->intrs[inum].inthandler,
488                          statep->intrs[inum].arg1,
489                          statep->intrs[inum].arg2);
490                      ddi_intr_enable(statep->hdls[inum]);
491              }
493              /* Resume hardware */
494              xx_resume(statep);
496              break;
498      default:
499              return (DDI_ENOTSUP);
500      }
502      return (DDI_SUCCESS);
505 .in -2
507 .SH ATTRIBUTES
509 See \fBattributes\fR(5) for descriptions of the following attributes:
514 box;
515 c | c
516 l | l .
517 ATTRIBUTE TYPE  ATTRIBUTE VALUE
519 Interface Stability     Private
521 MT-Level        Unsafe
524 .SH SEE ALSO
526 \fBattributes\fR(5), \fBddi_intr_alloc\fR(9F), \fBddi_intr_free\fR(9F),
527 \fBddi_intr_set_nreq\fR(9F)
528 .SH NOTES
530 Users of these interfaces that register for \fBDDI_CB_FLAG_INTR\fR become
531 participants in interrupt resource management. With that participation comes a
532 responsibility to properly adjust interrupt usage. In the case of a
533 \fBDDI_CB_INTR_ADD\fR action, the system guarantees that a driver can allocate
534 a total number of interrupt resources up to its new number of available
535 interrupts. The total number of interrupt resources is the sum of all resources
536 allocated by the function \fBddi_intr_alloc\fR(9F), minus all previously
537 released by the function \fBddi_intr_free\fR(9F). In the case of a
538 \fBDDI_CB_INTR_REMOVE\fR action, the driver might have more interrupts
539 allocated than are now currently available. It is necessary for the driver to
540 release the excess interrupts, or it will have a negative impact on the
541 interrupt availability for other drivers in the system.
544 A failure to release interrupts in response to a \fBDDI_CB_INTR_REMOVE\fR
545 callback generates the following warning on the system console:
547 .in +2
549 WARNING: <driver><instance>: failed to release interrupts for
550         IRM (nintrs = ##, navail=##).
552 .in -2
557 Participation in interrupt resource management ends when a driver uses the
558 \fBddi_cb_unregister()\fR function to unregister its callback function. The
559 callback function must still operate properly until after the call to the
560 \fBddi_cb_unregister()\fR function completes. If \fBaddinterrupts\fR were given
561 to the driver because of its participation, then a final use of the callback
562 function occurs to release the additional interrupts. The call to the
563 \fBddi_cb_unregister()\fR function blocks until the final  use of the
564 registered callback function is finished.