1e2529962Sae112802 /*
2e2529962Sae112802  * CDDL HEADER START
3e2529962Sae112802  *
4e2529962Sae112802  * The contents of this file are subject to the terms of the
5e2529962Sae112802  * Common Development and Distribution License (the "License").
6e2529962Sae112802  * You may not use this file except in compliance with the License.
7e2529962Sae112802  *
8e2529962Sae112802  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e2529962Sae112802  * or http://www.opensolaris.org/os/licensing.
10e2529962Sae112802  * See the License for the specific language governing permissions
11e2529962Sae112802  * and limitations under the License.
12e2529962Sae112802  *
13e2529962Sae112802  * When distributing Covered Code, include this CDDL HEADER in each
14e2529962Sae112802  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e2529962Sae112802  * If applicable, add the following below this CDDL HEADER, with the
16e2529962Sae112802  * fields enclosed by brackets "[]" replaced with your own identifying
17e2529962Sae112802  * information: Portions Copyright [yyyy] [name of copyright owner]
18e2529962Sae112802  *
19e2529962Sae112802  * CDDL HEADER END
20e2529962Sae112802  */
21e2529962Sae112802 /*
2208c92e0eSMary Beale  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23e2529962Sae112802  * Use is subject to license terms.
24e2529962Sae112802  */
25e2529962Sae112802 
26e2529962Sae112802 /*
27e2529962Sae112802  *  PICL Ontario platform plug-in to message the SC to light
28e2529962Sae112802  *  or extinguish the hdd 'OK2RM' ready-to-service light in
29e2529962Sae112802  *  the event of a soft unconfigure or configure, respectively.
3008c92e0eSMary Beale  *
3108c92e0eSMary Beale  *  Erie platforms (T1000) do not have ok-to-remove LEDs
3208c92e0eSMary Beale  *  so they do not need handlers for the SBL events.
33e2529962Sae112802  */
34e2529962Sae112802 
35e2529962Sae112802 #include <picl.h>
36e2529962Sae112802 #include <picltree.h>
37e2529962Sae112802 #include <picldefs.h>
38e2529962Sae112802 #include <stdio.h>
39e2529962Sae112802 #include <umem.h>
40e2529962Sae112802 #include <unistd.h>
41e2529962Sae112802 #include <libnvpair.h>
42e2529962Sae112802 #include <strings.h>
43e2529962Sae112802 #include <syslog.h>
44e2529962Sae112802 #include <dlfcn.h>
45e2529962Sae112802 #include <link.h>
46e2529962Sae112802 #include <signal.h>
47e2529962Sae112802 #include <sys/types.h>
48e2529962Sae112802 #include <sys/stat.h>
49e2529962Sae112802 #include <fcntl.h>
50e2529962Sae112802 #include <sys/param.h>
51e2529962Sae112802 #include <sys/raidioctl.h>
5208c92e0eSMary Beale #include <sys/utsname.h>
5308c92e0eSMary Beale #include <sys/systeminfo.h>
54e2529962Sae112802 #include <libpcp.h>
55e2529962Sae112802 #include "piclsbl.h"
56e2529962Sae112802 
57e2529962Sae112802 #include "errno.h"
58e2529962Sae112802 
59e2529962Sae112802 #pragma init(piclsbl_register)
60e2529962Sae112802 
61e2529962Sae112802 static void *pcp_handle;
62e2529962Sae112802 
63e2529962Sae112802 static char hba_devctl[MAXPATHLEN];
64e2529962Sae112802 
65e2529962Sae112802 static int (* pcp_init_ptr)();
66e2529962Sae112802 static int (* pcp_send_recv_ptr)();
67e2529962Sae112802 static int (* pcp_close_ptr)();
68e2529962Sae112802 
69e2529962Sae112802 static int load_pcp_libs(void);
70e2529962Sae112802 static void piclsbl_init(void);
71e2529962Sae112802 static void piclsbl_fini(void);
72e2529962Sae112802 static void piclsbl_register(void);
73e2529962Sae112802 static void piclsbl_handler(const char *ename, const void *earg,
74e2529962Sae112802 				size_t size, void *cookie);
75e2529962Sae112802 
76e2529962Sae112802 static picld_plugin_reg_t piclsbl_reg = {
77e2529962Sae112802 	PICLD_PLUGIN_VERSION_1,
78e2529962Sae112802 	PICLD_PLUGIN_CRITICAL,
79e2529962Sae112802 	"piclsbl",
80e2529962Sae112802 	piclsbl_init,
81e2529962Sae112802 	piclsbl_fini
82e2529962Sae112802 };
83e2529962Sae112802 
84e2529962Sae112802 /*
85e2529962Sae112802  * called from init to load the pcp library
86e2529962Sae112802  */
87e2529962Sae112802 static int
load_pcp_libs()88e2529962Sae112802 load_pcp_libs()
89e2529962Sae112802 {
90e2529962Sae112802 	char pcp_dl_lib[80];
91e2529962Sae112802 
92e2529962Sae112802 	(void) snprintf(pcp_dl_lib, sizeof (pcp_dl_lib), "%s%s",
93e2529962Sae112802 	    LIB_PCP_PATH, PCPLIB);
94e2529962Sae112802 
95e2529962Sae112802 	/* load the library and set up function pointers */
96e2529962Sae112802 	if ((pcp_handle = dlopen(pcp_dl_lib, RTLD_NOW)) == (void *) NULL)
97e2529962Sae112802 		return (1);
98e2529962Sae112802 
99e2529962Sae112802 	pcp_init_ptr = (int(*)())dlsym(pcp_handle, "pcp_init");
100e2529962Sae112802 	pcp_close_ptr = (int(*)())dlsym(pcp_handle, "pcp_close");
101e2529962Sae112802 	pcp_send_recv_ptr = (int(*)())dlsym(pcp_handle, "pcp_send_recv");
102e2529962Sae112802 
103e2529962Sae112802 	if (pcp_init_ptr == NULL || pcp_send_recv_ptr == NULL ||
104e2529962Sae112802 	    pcp_close_ptr == NULL)
105e2529962Sae112802 		return (1);
106e2529962Sae112802 
107e2529962Sae112802 	return (0);
108e2529962Sae112802 }
109e2529962Sae112802 
110e2529962Sae112802 /*
111e2529962Sae112802  * callback routine for ptree_walk_tree_by_class()
112e2529962Sae112802  */
113e2529962Sae112802 static int
cb_find_disk(picl_nodehdl_t node,void * args)114e2529962Sae112802 cb_find_disk(picl_nodehdl_t node, void *args)
115e2529962Sae112802 {
116e2529962Sae112802 	disk_lookup_t *lookup  = (disk_lookup_t *)args;
117e2529962Sae112802 	int status = -1;
118e2529962Sae112802 	char *n;
119e2529962Sae112802 	char path[PICL_PROPNAMELEN_MAX];
120e2529962Sae112802 
121e2529962Sae112802 	status = ptree_get_propval_by_name(node, "Path", (void *)&path,
122e2529962Sae112802 	    PICL_PROPNAMELEN_MAX);
123e2529962Sae112802 	if (status != PICL_SUCCESS) {
124e2529962Sae112802 		return (PICL_WALK_CONTINUE);
125e2529962Sae112802 	}
126e2529962Sae112802 
127e2529962Sae112802 	if (strcmp(path, lookup->path) == 0) {
128e2529962Sae112802 		lookup->disk = node;
129e2529962Sae112802 		lookup->result = DISK_FOUND;
130e2529962Sae112802 
131e2529962Sae112802 		/* store the HBA's device path for use in check_raid() */
132e2529962Sae112802 		n = strstr(path, "/sd");
133e2529962Sae112802 		strncpy(n, "\0", 1);
134e2529962Sae112802 		(void) snprintf(hba_devctl, MAXPATHLEN, "/devices%s:devctl",
135e2529962Sae112802 		    path);
136e2529962Sae112802 
137e2529962Sae112802 		return (PICL_WALK_TERMINATE);
138e2529962Sae112802 	}
139e2529962Sae112802 
140e2529962Sae112802 	return (PICL_WALK_CONTINUE);
141e2529962Sae112802 }
142e2529962Sae112802 
143e2529962Sae112802 /*
144e2529962Sae112802  * check a target for RAID membership
145e2529962Sae112802  */
146e2529962Sae112802 static int
check_raid(int target)147e2529962Sae112802 check_raid(int target)
148e2529962Sae112802 {
149e2529962Sae112802 	raid_config_t config;
150e2529962Sae112802 	int fd;
151e2529962Sae112802 	int numvols;
152e2529962Sae112802 	int i;
153e2529962Sae112802 	int j;
154e2529962Sae112802 
155e2529962Sae112802 	/*
156e2529962Sae112802 	 * hba_devctl is set to the onboard hba, so it will
157e2529962Sae112802 	 * always house any onboard RAID volumes
158e2529962Sae112802 	 */
159e2529962Sae112802 	if ((fd = open(hba_devctl, O_RDONLY)) < 0) {
160e2529962Sae112802 		syslog(LOG_ERR, "%s", strerror(errno));
161e2529962Sae112802 		return (0);
162e2529962Sae112802 	}
163e2529962Sae112802 
164e2529962Sae112802 	/*
165e2529962Sae112802 	 * look up the RAID configurations for the onboard
166e2529962Sae112802 	 * HBA and check target against all member targets
167e2529962Sae112802 	 */
168e2529962Sae112802 	if (ioctl(fd, RAID_NUMVOLUMES, &numvols)) {
169e2529962Sae112802 		syslog(LOG_ERR, "%s", strerror(errno));
170e2529962Sae112802 		(void) close(fd);
171e2529962Sae112802 		return (0);
172e2529962Sae112802 	}
173e2529962Sae112802 
174e2529962Sae112802 	for (i = 0; i < numvols; i++) {
175e2529962Sae112802 		config.unitid = i;
176e2529962Sae112802 		if (ioctl(fd, RAID_GETCONFIG, &config)) {
177e2529962Sae112802 			syslog(LOG_ERR, "%s", strerror(errno));
178e2529962Sae112802 			(void) close(fd);
179e2529962Sae112802 			return (0);
180e2529962Sae112802 		}
181e2529962Sae112802 
182e2529962Sae112802 		for (j = 0; j < config.ndisks; j++) {
183e2529962Sae112802 			if (config.disk[j] == target) {
184e2529962Sae112802 				(void) close(fd);
185e2529962Sae112802 				return (1);
186e2529962Sae112802 			}
187e2529962Sae112802 		}
188e2529962Sae112802 	}
189e2529962Sae112802 	(void) close(fd);
190e2529962Sae112802 	return (0);
191e2529962Sae112802 }
192e2529962Sae112802 
193e2529962Sae112802 /*
194e2529962Sae112802  * Ontario SBL event handler, subscribed to:
195e2529962Sae112802  *	PICLEVENT_SYSEVENT_DEVICE_ADDED
196e2529962Sae112802  *	PICLEVENT_SYSEVENT_DEVICE_REMOVED
197e2529962Sae112802  */
198e2529962Sae112802 static void
piclsbl_handler(const char * ename,const void * earg,size_t size,void * cookie)199e2529962Sae112802 piclsbl_handler(const char *ename, const void *earg, size_t size,
200e2529962Sae112802     void *cookie)
201e2529962Sae112802 {
202e2529962Sae112802 	char		*devfs_path;
203e2529962Sae112802 	char		hdd_location[PICL_PROPNAMELEN_MAX];
204e2529962Sae112802 	nvlist_t	*nvlp = NULL;
205e2529962Sae112802 	pcp_msg_t	send_msg;
206e2529962Sae112802 	pcp_msg_t	recv_msg;
207e2529962Sae112802 	pcp_sbl_req_t	*req_ptr = NULL;
208e2529962Sae112802 	pcp_sbl_resp_t	*resp_ptr = NULL;
209e2529962Sae112802 	int		status = -1;
210e2529962Sae112802 	int		target;
211e2529962Sae112802 	disk_lookup_t	lookup;
212e2529962Sae112802 	int		channel_fd;
213e2529962Sae112802 
214e2529962Sae112802 	/*
215e2529962Sae112802 	 * setup the request data to attach to the libpcp msg
216e2529962Sae112802 	 */
217e2529962Sae112802 	if ((req_ptr = (pcp_sbl_req_t *)umem_zalloc(sizeof (pcp_sbl_req_t),
218e2529962Sae112802 	    UMEM_DEFAULT)) == NULL)
219e2529962Sae112802 		goto sbl_return;
220e2529962Sae112802 
221e2529962Sae112802 	/*
222e2529962Sae112802 	 * This plugin serves to enable or disable the blue RAS
223e2529962Sae112802 	 * 'ok-to-remove' LED that is on each of the 4 disks on the
224e2529962Sae112802 	 * Ontario.  We catch the event via the picl handler, and
225e2529962Sae112802 	 * if the event is DEVICE_ADDED for one of our onboard disks,
226e2529962Sae112802 	 * then we'll be turning off the LED. Otherwise, if the event
227e2529962Sae112802 	 * is DEVICE_REMOVED, then we turn it on.
228e2529962Sae112802 	 */
229e2529962Sae112802 	if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0)
230e2529962Sae112802 		req_ptr->sbl_action = PCP_SBL_DISABLE;
231e2529962Sae112802 	else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0)
232e2529962Sae112802 		req_ptr->sbl_action = PCP_SBL_ENABLE;
233e2529962Sae112802 	else
234e2529962Sae112802 		goto sbl_return;
235e2529962Sae112802 
236e2529962Sae112802 	/*
237e2529962Sae112802 	 * retrieve the device's physical path from the event payload
238e2529962Sae112802 	 */
239*ada2da53SToomas Soome 	if (nvlist_unpack((char *)earg, size, &nvlp, 0))
240e2529962Sae112802 		goto sbl_return;
241e2529962Sae112802 	if (nvlist_lookup_string(nvlp, "devfs-path", &devfs_path))
242e2529962Sae112802 		goto sbl_return;
243e2529962Sae112802 
244e2529962Sae112802 	/*
245e2529962Sae112802 	 * look for this disk in the picl tree, and if it's
246e2529962Sae112802 	 * location indicates that it's one of our internal
247e2529962Sae112802 	 * disks, then set sbl_id to incdicate which one.
248e2529962Sae112802 	 * otherwise, return as it is not one of our disks.
249e2529962Sae112802 	 */
250e2529962Sae112802 	lookup.path = strdup(devfs_path);
251*ada2da53SToomas Soome 	lookup.disk = 0;
252e2529962Sae112802 	lookup.result = DISK_NOT_FOUND;
253e2529962Sae112802 
254e2529962Sae112802 	/* first, find the disk */
255e2529962Sae112802 	status = ptree_walk_tree_by_class(root_node, "disk", (void *)&lookup,
256e2529962Sae112802 	    cb_find_disk);
257e2529962Sae112802 	if (status != PICL_SUCCESS)
258e2529962Sae112802 		goto sbl_return;
259e2529962Sae112802 
260e2529962Sae112802 	if (lookup.result == DISK_FOUND) {
261e2529962Sae112802 		/* now, lookup it's location in the node */
262e2529962Sae112802 		status = ptree_get_propval_by_name(lookup.disk, "Location",
263e2529962Sae112802 		    (void *)&hdd_location, PICL_PROPNAMELEN_MAX);
264e2529962Sae112802 		if (status != PICL_SUCCESS) {
265e2529962Sae112802 			syslog(LOG_ERR, "piclsbl: failed hdd discovery");
266e2529962Sae112802 			goto sbl_return;
267e2529962Sae112802 		}
268e2529962Sae112802 	}
269e2529962Sae112802 
270e2529962Sae112802 	/*
271e2529962Sae112802 	 * Strip off the target from the NAC name.
272e2529962Sae112802 	 * The disk NAC will always be HDD#
273e2529962Sae112802 	 */
274e2529962Sae112802 	if (strncmp(hdd_location, NAC_DISK_PREFIX,
275e2529962Sae112802 	    strlen(NAC_DISK_PREFIX)) == 0) {
276e2529962Sae112802 		(void) sscanf(hdd_location, "%*3s%d", &req_ptr->sbl_id);
277e2529962Sae112802 		target = (int)req_ptr->sbl_id;
278e2529962Sae112802 	} else {
279e2529962Sae112802 		/* this is not one of the onboard disks */
280e2529962Sae112802 		goto sbl_return;
281e2529962Sae112802 	}
282e2529962Sae112802 
283e2529962Sae112802 	/*
284e2529962Sae112802 	 * check the onboard RAID configuration for this disk. if it is
285e2529962Sae112802 	 * a member of a RAID and is not the RAID itself, ignore the event
286e2529962Sae112802 	 */
287e2529962Sae112802 	if (check_raid(target))
288e2529962Sae112802 		goto sbl_return;
289e2529962Sae112802 
290e2529962Sae112802 	/*
291e2529962Sae112802 	 * we have the information we need, init the platform channel.
292e2529962Sae112802 	 * the platform channel driver will only allow one connection
293e2529962Sae112802 	 * at a time on this socket. on the offchance that more than
294e2529962Sae112802 	 * one event comes in, we'll retry to initialize this connection
295e2529962Sae112802 	 * up to 3 times
296e2529962Sae112802 	 */
297e2529962Sae112802 	if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) < 0) {
298e2529962Sae112802 		/* failed to init; wait and retry up to 3 times */
299e2529962Sae112802 		int s = PCPINIT_TIMEOUT;
300e2529962Sae112802 		int retries = 0;
301e2529962Sae112802 		while (++retries) {
302e2529962Sae112802 			(void) sleep(s);
303e2529962Sae112802 			if ((channel_fd = (*pcp_init_ptr)(LED_CHANNEL)) >= 0)
304e2529962Sae112802 				break;
305e2529962Sae112802 			else if (retries == 3) {
306e2529962Sae112802 				syslog(LOG_ERR, "piclsbl: ",
307e2529962Sae112802 				    "SC channel initialization failed");
308e2529962Sae112802 				goto sbl_return;
309e2529962Sae112802 			}
310e2529962Sae112802 			/* continue */
311e2529962Sae112802 		}
312e2529962Sae112802 	}
313e2529962Sae112802 
314e2529962Sae112802 	/*
315e2529962Sae112802 	 * populate the message for libpcp
316e2529962Sae112802 	 */
317e2529962Sae112802 	send_msg.msg_type = PCP_SBL_CONTROL;
318*ada2da53SToomas Soome 	send_msg.sub_type = 0;
319e2529962Sae112802 	send_msg.msg_len = sizeof (pcp_sbl_req_t);
320e2529962Sae112802 	send_msg.msg_data = (uint8_t *)req_ptr;
321e2529962Sae112802 
322e2529962Sae112802 	/*
323e2529962Sae112802 	 * send the request, receive the response
324e2529962Sae112802 	 */
325e2529962Sae112802 	if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
326e2529962Sae112802 	    PCPCOMM_TIMEOUT) < 0) {
327e2529962Sae112802 		/* we either timed out or erred; either way try again */
328e2529962Sae112802 		int s = PCPCOMM_TIMEOUT;
329e2529962Sae112802 		(void) sleep(s);
330e2529962Sae112802 		if ((*pcp_send_recv_ptr)(channel_fd, &send_msg, &recv_msg,
331e2529962Sae112802 		    PCPCOMM_TIMEOUT) < 0) {
332e2529962Sae112802 			syslog(LOG_ERR, "piclsbl: communication failure");
333e2529962Sae112802 			goto sbl_return;
334e2529962Sae112802 		}
335e2529962Sae112802 	}
336e2529962Sae112802 
337e2529962Sae112802 	/*
338e2529962Sae112802 	 * validate that this data was meant for us
339e2529962Sae112802 	 */
340e2529962Sae112802 	if (recv_msg.msg_type != PCP_SBL_CONTROL_R) {
341e2529962Sae112802 		syslog(LOG_ERR, "piclsbl: unbound packet received");
342e2529962Sae112802 		goto sbl_return;
343e2529962Sae112802 	}
344e2529962Sae112802 
345e2529962Sae112802 	/*
346e2529962Sae112802 	 * verify that the LED action has taken place
347e2529962Sae112802 	 */
348e2529962Sae112802 	resp_ptr = (pcp_sbl_resp_t *)recv_msg.msg_data;
349e2529962Sae112802 	if (resp_ptr->status == PCP_SBL_ERROR) {
350e2529962Sae112802 		syslog(LOG_ERR, "piclsbl: OK2RM LED action error");
351e2529962Sae112802 		goto sbl_return;
352e2529962Sae112802 	}
353e2529962Sae112802 
354e2529962Sae112802 	/*
355e2529962Sae112802 	 * ensure the LED action taken is the one requested
356e2529962Sae112802 	 */
357e2529962Sae112802 	if ((req_ptr->sbl_action == PCP_SBL_DISABLE) &&
358e2529962Sae112802 	    (resp_ptr->sbl_state != SBL_STATE_OFF))
359e2529962Sae112802 		syslog(LOG_ERR, "piclsbl: OK2RM LED not OFF after disk "
360e2529962Sae112802 		    "configuration");
361e2529962Sae112802 	else if ((req_ptr->sbl_action == PCP_SBL_ENABLE) &&
362e2529962Sae112802 	    (resp_ptr->sbl_state != SBL_STATE_ON))
363e2529962Sae112802 		syslog(LOG_ERR, "piclsbl: OK2RM LED not ON after disk "
364e2529962Sae112802 		    "unconfiguration");
365e2529962Sae112802 	else if (resp_ptr->sbl_state == SBL_STATE_UNKNOWN)
366e2529962Sae112802 		syslog(LOG_ERR, "piclsbl: OK2RM LED set to unknown state");
367e2529962Sae112802 
368e2529962Sae112802 sbl_return:
369e2529962Sae112802 
370e2529962Sae112802 	(*pcp_close_ptr)(channel_fd);
371e2529962Sae112802 	if (req_ptr != NULL)
372e2529962Sae112802 		umem_free(req_ptr, sizeof (pcp_sbl_req_t));
373e2529962Sae112802 	if (resp_ptr != NULL)
374e2529962Sae112802 		free(resp_ptr);
375e2529962Sae112802 	nvlist_free(nvlp);
376e2529962Sae112802 }
377e2529962Sae112802 
378e2529962Sae112802 static void
piclsbl_init(void)379e2529962Sae112802 piclsbl_init(void)
380e2529962Sae112802 {
38108c92e0eSMary Beale 	char	platbuf[SYS_NMLN];
38208c92e0eSMary Beale 
38308c92e0eSMary Beale 	/* check for Erie platform name */
38408c92e0eSMary Beale 	if ((sysinfo(SI_PLATFORM, platbuf, SYS_NMLN) != -1) &&
38508c92e0eSMary Beale 	    ((strcmp(platbuf, ERIE_PLATFORM) == 0) ||
38608c92e0eSMary Beale 	    (strcmp(platbuf, ERIE_PLATFORM2) == 0)))
38708c92e0eSMary Beale 		return;
38808c92e0eSMary Beale 
389e2529962Sae112802 	/* retrieve the root node for lookups in the event handler */
390*ada2da53SToomas Soome 	if ((ptree_get_root(&root_node)) != 0)
391e2529962Sae112802 		return;
392e2529962Sae112802 
393e2529962Sae112802 	/* load libpcp */
394e2529962Sae112802 	if (load_pcp_libs()) {
395e2529962Sae112802 		syslog(LOG_ERR, "piclsbl: failed to load libpcp");
396e2529962Sae112802 		syslog(LOG_ERR, "piclsbl: aborting");
397e2529962Sae112802 		return;
398e2529962Sae112802 	}
399e2529962Sae112802 
400e2529962Sae112802 	/*
401e2529962Sae112802 	 * register piclsbl_handler for both "sysevent-device-added" and
402e2529962Sae112802 	 * and for "sysevent-device-removed" PICL events
403e2529962Sae112802 	 */
404e2529962Sae112802 	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
405e2529962Sae112802 	    piclsbl_handler, NULL);
406e2529962Sae112802 	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
407e2529962Sae112802 	    piclsbl_handler, NULL);
408e2529962Sae112802 }
409e2529962Sae112802 
410e2529962Sae112802 static void
piclsbl_fini(void)411e2529962Sae112802 piclsbl_fini(void)
412e2529962Sae112802 {
413e2529962Sae112802 	/* unregister the event handler */
414e2529962Sae112802 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
415e2529962Sae112802 	    piclsbl_handler, NULL);
416e2529962Sae112802 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
417e2529962Sae112802 	    piclsbl_handler, NULL);
418e2529962Sae112802 }
419e2529962Sae112802 
420e2529962Sae112802 static void
piclsbl_register(void)421e2529962Sae112802 piclsbl_register(void)
422e2529962Sae112802 {
423e2529962Sae112802 	picld_plugin_register(&piclsbl_reg);
424e2529962Sae112802 }
425