4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Sun NDI hotplug interfaces
31 #include <sys/sysmacros.h>
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
36 #include <sys/cmn_err.h>
37 #include <sys/debug.h>
38 #include <sys/avintr.h>
39 #include <sys/autoconf.h>
40 #include <sys/sunndi.h>
41 #include <sys/ndi_impldefs.h>
45 #include <sys/callb.h>
46 #include <sys/sysevent.h>
47 #include <sys/sysevent/eventdefs.h>
48 #include <sys/sysevent/dr.h>
49 #include <sys/taskq.h>
51 /* Local functions prototype */
52 static void ddihp_cn_run_event(void *arg
);
53 static int ddihp_cn_req_handler(ddi_hp_cn_handle_t
*hdlp
,
54 ddi_hp_cn_state_t target_state
);
57 * Global functions (called by hotplug controller or nexus drivers)
61 * Register the Hotplug Connection (CN)
64 ndi_hp_register(dev_info_t
*dip
, ddi_hp_cn_info_t
*info_p
)
66 ddi_hp_cn_handle_t
*hdlp
;
69 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_register: dip %p, info_p %p\n",
70 (void *)dip
, (void *)info_p
));
72 ASSERT(!servicing_interrupt());
73 if (servicing_interrupt())
76 /* Validate the arguments */
77 if ((dip
== NULL
) || (info_p
== NULL
))
80 if (!NEXUS_HAS_HP_OP(dip
)) {
83 /* Lock before access */
84 ndi_devi_enter(dip
, &count
);
86 hdlp
= ddihp_cn_name_to_handle(dip
, info_p
->cn_name
);
88 /* This cn_name is already registered. */
89 ndi_devi_exit(dip
, count
);
94 * Create and initialize hotplug Connection handle
96 hdlp
= (ddi_hp_cn_handle_t
*)kmem_zalloc(
97 (sizeof (ddi_hp_cn_handle_t
)), KM_SLEEP
);
99 /* Copy the Connection information */
101 bcopy(info_p
, &(hdlp
->cn_info
), sizeof (*info_p
));
104 hdlp
->cn_info
.cn_name
= ddi_strdup(info_p
->cn_name
, KM_SLEEP
);
106 if (ddihp_cn_getstate(hdlp
) != DDI_SUCCESS
) {
107 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_register: dip %p, hdlp %p"
108 "ddi_cn_getstate failed\n", (void *)dip
, (void *)hdlp
));
114 * Append the handle to the list
116 DDIHP_LIST_APPEND(ddi_hp_cn_handle_t
, (DEVI(dip
)->devi_hp_hdlp
),
119 ndi_devi_exit(dip
, count
);
121 return (NDI_SUCCESS
);
124 kmem_free(hdlp
->cn_info
.cn_name
, strlen(hdlp
->cn_info
.cn_name
) + 1);
125 kmem_free(hdlp
, sizeof (ddi_hp_cn_handle_t
));
126 ndi_devi_exit(dip
, count
);
128 return (NDI_FAILURE
);
132 * Unregister a Hotplug Connection (CN)
135 ndi_hp_unregister(dev_info_t
*dip
, char *cn_name
)
137 ddi_hp_cn_handle_t
*hdlp
;
141 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_unregister: dip %p, cn name %s\n",
142 (void *)dip
, cn_name
));
144 ASSERT(!servicing_interrupt());
145 if (servicing_interrupt())
146 return (NDI_FAILURE
);
148 /* Validate the arguments */
149 if ((dip
== NULL
) || (cn_name
== NULL
))
152 ndi_devi_enter(dip
, &count
);
154 hdlp
= ddihp_cn_name_to_handle(dip
, cn_name
);
156 ndi_devi_exit(dip
, count
);
160 switch (ddihp_cn_unregister(hdlp
)) {
175 ndi_devi_exit(dip
, count
);
181 * Notify the Hotplug Connection (CN) to change state.
183 * DDI_HP_REQ_SYNC Return after the change is finished.
184 * DDI_HP_REQ_ASYNC Return after the request is dispatched.
187 ndi_hp_state_change_req(dev_info_t
*dip
, char *cn_name
,
188 ddi_hp_cn_state_t state
, uint_t flag
)
190 ddi_hp_cn_async_event_entry_t
*eventp
;
192 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_state_change_req: dip %p "
193 "cn_name: %s, state %x, flag %x\n",
194 (void *)dip
, cn_name
, state
, flag
));
196 /* Validate the arguments */
197 if (dip
== NULL
|| cn_name
== NULL
)
200 if (!NEXUS_HAS_HP_OP(dip
)) {
201 return (NDI_ENOTSUP
);
204 * If the request is to handle the event synchronously, then call
205 * the event handler without queuing the event.
207 if (flag
& DDI_HP_REQ_SYNC
) {
208 ddi_hp_cn_handle_t
*hdlp
;
212 ASSERT(!servicing_interrupt());
213 if (servicing_interrupt())
214 return (NDI_FAILURE
);
216 ndi_devi_enter(dip
, &count
);
218 hdlp
= ddihp_cn_name_to_handle(dip
, cn_name
);
220 ndi_devi_exit(dip
, count
);
225 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_state_change_req: hdlp %p "
226 "calling ddihp_cn_req_handler() directly to handle "
227 "target_state %x\n", (void *)hdlp
, state
));
229 ret
= ddihp_cn_req_handler(hdlp
, state
);
231 ndi_devi_exit(dip
, count
);
236 eventp
= kmem_zalloc(sizeof (ddi_hp_cn_async_event_entry_t
),
241 eventp
->cn_name
= ddi_strdup(cn_name
, KM_NOSLEEP
);
242 if (eventp
->cn_name
== NULL
) {
243 kmem_free(eventp
, sizeof (ddi_hp_cn_async_event_entry_t
));
247 eventp
->target_state
= state
;
250 * Hold the parent's ref so that it won't disappear when the taskq is
255 if (!taskq_dispatch(system_taskq
, ddihp_cn_run_event
, eventp
,
258 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_state_change_req: "
259 "taskq_dispatch failed! dip %p "
260 "target_state %x\n", (void *)dip
, state
));
264 return (NDI_CLAIMED
);
268 * Walk the link of Hotplug Connection handles of a dip:
269 * DEVI(dip)->devi_hp_hdlp->[link of connections]
272 ndi_hp_walk_cn(dev_info_t
*dip
, int (*f
)(ddi_hp_cn_info_t
*,
276 ddi_hp_cn_handle_t
*head
, *curr
, *prev
;
278 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_walk_cn: dip %p arg %p\n",
281 ASSERT(!servicing_interrupt());
282 if (servicing_interrupt())
285 /* Validate the arguments */
289 ndi_devi_enter(dip
, &count
);
291 head
= DEVI(dip
)->devi_hp_hdlp
;
294 while (curr
!= NULL
) {
295 DDI_HP_NEXDBG((CE_CONT
, "ndi_hp_walk_cn: dip %p "
296 "current cn_name: %s\n",
297 (void *)dip
, curr
->cn_info
.cn_name
));
298 switch ((*f
)(&(curr
->cn_info
), arg
)) {
299 case DDI_WALK_TERMINATE
:
300 ndi_devi_exit(dip
, count
);
303 case DDI_WALK_CONTINUE
:
305 if (DEVI(dip
)->devi_hp_hdlp
!= head
) {
307 * The current node is head and it is removed
308 * by last call to (*f)()
310 head
= DEVI(dip
)->devi_hp_hdlp
;
313 } else if (prev
&& prev
->next
!= curr
) {
315 * The current node is a middle node or tail
316 * node and it is removed by last call to
321 /* no removal accurred on curr node */
327 ndi_devi_exit(dip
, count
);
331 * Local functions (called within this file)
335 * Wrapper function for ddihp_cn_req_handler() called from taskq
338 ddihp_cn_run_event(void *arg
)
340 ddi_hp_cn_async_event_entry_t
*eventp
=
341 (ddi_hp_cn_async_event_entry_t
*)arg
;
342 dev_info_t
*dip
= eventp
->dip
;
343 ddi_hp_cn_handle_t
*hdlp
;
346 /* Lock before access */
347 ndi_devi_enter(dip
, &count
);
349 hdlp
= ddihp_cn_name_to_handle(dip
, eventp
->cn_name
);
351 (void) ddihp_cn_req_handler(hdlp
, eventp
->target_state
);
353 DDI_HP_NEXDBG((CE_CONT
, "ddihp_cn_run_event: no handle for "
354 "cn_name: %s dip %p. Request for target_state %x is"
356 eventp
->cn_name
, (void *)dip
, eventp
->target_state
));
359 ndi_devi_exit(dip
, count
);
361 /* Release the devi's ref that is held from interrupt context. */
362 ndi_rele_devi((dev_info_t
*)DEVI(dip
));
363 kmem_free(eventp
->cn_name
, strlen(eventp
->cn_name
) + 1);
364 kmem_free(eventp
, sizeof (ddi_hp_cn_async_event_entry_t
));
368 * Handle state change request of a Hotplug Connection (CN)
371 ddihp_cn_req_handler(ddi_hp_cn_handle_t
*hdlp
,
372 ddi_hp_cn_state_t target_state
)
374 dev_info_t
*dip
= hdlp
->cn_dip
;
375 int ret
= DDI_SUCCESS
;
377 DDI_HP_NEXDBG((CE_CONT
, "ddihp_cn_req_handler:"
378 " hdlp %p, target_state %x\n",
379 (void *)hdlp
, target_state
));
381 ASSERT(DEVI_BUSY_OWNED(dip
));
383 if (ddihp_cn_getstate(hdlp
) != DDI_SUCCESS
) {
384 DDI_HP_NEXDBG((CE_CONT
, "ddihp_cn_req_handler: dip %p, "
385 "hdlp %p ddi_cn_getstate failed\n", (void *)dip
,
388 return (NDI_UNCLAIMED
);
390 if (hdlp
->cn_info
.cn_state
!= target_state
) {
391 ddi_hp_cn_state_t result_state
= 0;
393 DDIHP_CN_OPS(hdlp
, DDI_HPOP_CN_CHANGE_STATE
,
394 (void *)&target_state
, (void *)&result_state
, ret
);
396 DDI_HP_NEXDBG((CE_CONT
, "ddihp_cn_req_handler: dip %p, "
397 "hdlp %p changed state to %x, ret=%x\n",
398 (void *)dip
, (void *)hdlp
, result_state
, ret
));
401 if (ret
== DDI_SUCCESS
)
402 return (NDI_CLAIMED
);
404 return (NDI_UNCLAIMED
);