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"
8 ddi_cb_register, ddi_cb_unregister \- register and unregister a device driver
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);
22 \fBint\fR \fBddi_cb_unregister\fR(\fBddi_cb_handle_t\fR \fIhdl\fR);
27 Solaris DDI specific (Solaris DDI).
30 \fBddi_cb_register()\fR
37 Pointer to the \fBdev_info\fR structure.
46 Flags to determine which callback events can be handled.
55 Callback handler function.
64 First argument to the callback handler.
73 Second (optional) argument to the callback handler.
82 Pointer to return a handle to the registered callback.
87 \fBddi_cb_unregister()\fR
94 Handle to the registered callback handler that is to be unregistered.
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:
115 \fB\fBDDI_CB_FLAG_INTR\fR\fR
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
128 The cdfunc is a callback handler with the following prototype:
132 typedef int (*ddi_cb_func_t)(dev_info_t *dip,
133 ddi_cb_action_t action, void *cbarg,
134 void *arg1, void *arg2);
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:
153 \fB\fIDDI_CB_INTR_ADD\fR\fR
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).
164 \fB\fIDDI_CB_INTR_REMOVE\fR\fR
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.
196 The \fBddi_cb_register()\fR and \fBddi_cb_unregister()\fR functions return:
200 \fB\fBDDI_SUCCESS\fR\fR
209 \fB\fBDDI_EINVAL\fR\fR
212 An invalid parameter was given when registering a callback handler, or an
213 invalid handle was given when unregistering.
219 \fB\fBDDI_EALREADY\fR\fR
222 An attempt was made to register a callback handler while a previous
223 registration still exists.
228 The \fIcbfunc\fR routine must return:
232 \fB\fBDDI_SUCCESS\fR\fR
241 \fB\fBDDI_ENOTSUP\fR\fR
244 The device does not support the operation
250 \fB\fBDDI_FAILURE\fR\fR
253 Implementation specific failure
258 These functions can be called from kernel, non-interrupt context.
261 \fBExample 1 \fRddi_cb_register
266 * attach(9F) routine.
268 * Creates soft state, registers callback handler, initializes
269 * hardware, and sets up interrupt handling for the driver.
271 xx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
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;
284 /* Get device instance */
285 instance = ddi_get_instance(dip);
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);
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);
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;
315 type = DDI_INTR_TYPE_FIXED;
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]);
352 statep = ddi_get_soft_state(state_list, instance);
354 return (DDI_FAILURE);
356 /* Resume hardware */
362 return (DDI_SUCCESS);
366 * detach(9F) routine.
368 * Stops the hardware, disables interrupt handling, unregisters
369 * a callback handler, and destroys the soft state for the driver.
371 xx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
373 xx_state_t *statep = NULL;
378 /* Get device instance */
379 instance = ddi_get_instance(dip);
385 statep = ddi_get_soft_state(state_list, instance);
387 return (DDI_FAILURE);
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]);
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);
416 statep = ddi_get_soft_state(state_list, instance);
418 return (DDI_FAILURE);
420 /* Suspend hardware */
426 return (DDI_SUCCESS);
430 * (*ddi_cbfunc)() routine.
432 * Adapt interrupt usage when availability changes.
435 xx_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
436 void *arg1, void *arg2)
438 xx_state_t *statep = (xx_state_t *)arg1;
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 */
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]);
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;
471 /* Free removed interrupt vectors */
472 for (inum = statep->nactual - count;
473 inum < statep->nactual; inum++) {
474 ddi_intr_free(statep->hdls[inum]);
477 /* Update actual count of available interrupts */
478 statep->nactual -= count;
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]);
493 /* Resume hardware */
499 return (DDI_ENOTSUP);
502 return (DDI_SUCCESS);
509 See \fBattributes\fR(5) for descriptions of the following attributes:
517 ATTRIBUTE TYPE ATTRIBUTE VALUE
519 Interface Stability Private
526 \fBattributes\fR(5), \fBddi_intr_alloc\fR(9F), \fBddi_intr_free\fR(9F),
527 \fBddi_intr_set_nreq\fR(9F)
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:
549 WARNING: <driver><instance>: failed to release interrupts for
550 IRM (nintrs = ##, navail=##).
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.