1ddi_cb_register(9F) Kernel Functions for Drivers ddi_cb_register(9F)
2
3
4
6 ddi_cb_register, ddi_cb_unregister - register and unregister a device
7 driver callback handler
8
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
21 Solaris DDI specific (Solaris DDI).
22
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
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
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
161 These functions can be called from kernel, non-interrupt context.
162
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
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
422 attributes(5), ddi_intr_alloc(9F), ddi_intr_free(9F),
423 ddi_intr_set_nreq(9F)
424
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)