1/*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * This plugin creates PICL nodes and properties for objects handled through
28 * the blade support chip (BSC). The BSC Solaris land device driver exposes
29 * information to the plugin and other clients through an existing LOM
30 * (Lights Out Management) ioctl interface. The plugin only exercises
31 * a subset of the interface which is known to be supported by the bsc.
32 *
33 * All the nodes which may be accessible through the BSC are included below
34 * the SUNW,bscv node (class system-controller) in the /platform tree.
35 * This plugin interrogates the BSC to determine which of
36 * those nodes are actually available. Properties are added to such nodes and
37 * in the case of volatile properties like temperature, a call-back function
38 * is established for on-demand access to the current value.
39 *
40 * NOTE:
41 * Depends on PICL devtree plugin.
42 */
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <unistd.h>
47#include <fcntl.h>
48#include <alloca.h>
49#include <syslog.h>
50#include <string.h>
51#include <libintl.h>
52#include <picl.h>
53#include <picltree.h>
54#include <errno.h>
55#include <limits.h>
56#include <ctype.h>
57#include <sys/types.h>
58#include <sys/obpdefs.h>
59#include <sys/lom_io.h>
60#include <sys/systeminfo.h>
61#include <time.h>
62#include <picldefs.h>
63#include <picld_pluginutil.h>
64#include "picllom.h"
65
66static	void		picllom_register(void);
67static	void		picllom_init(void);
68static	void		picllom_fini(void);
69static	node_el_t	*create_node_el(picl_nodehdl_t nodeh);
70static	void		delete_node_el(node_el_t *pel);
71static	node_list_t	*create_node_list();
72static	void		delete_node_list_contents(node_list_t *pnl);
73static	void		delete_node_list(node_list_t *pnl);
74static	void		add_node_to_list(picl_nodehdl_t nodeh,
75    node_list_t *listp);
76static	void		get_node_list_by_class(picl_nodehdl_t nodeh,
77    const char *classname, node_list_t *listp);
78static	int		get_lom_node(picl_nodehdl_t *lominfh);
79static	int		get_lom_device_path(picl_nodehdl_t *lominfh);
80static int		get_node_by_name_and_class(picl_nodehdl_t srchnodeh,
81    const char *nodename, const char *classname, picl_nodehdl_t *chdh);
82static	int		add_regular_prop(picl_nodehdl_t nodeh, const char *name,
83    int type, int access, int size, const void *valbuf, picl_prophdl_t *prophp);
84static	int		add_volatile_prop(picl_nodehdl_t nodeh, char *name,
85    int type, int access, int size, ptree_vol_rdfunc_t rdfunc,
86    ptree_vol_wrfunc_t wrfunc, picl_prophdl_t *prophp);
87static	int		open_lom_rd(int *lom_fd);
88static	int		get_lom_temp(int index, tempr_t *temp_p);
89static	int		update_voltage_stats();
90static	int		get_lom_volts_status(int index, int *voltsStatus_p);
91static	int		get_lom_volts_shutdown(int index, int *voltsShutdown_p);
92static	int		update_fan_stats();
93static	int		get_lom_fan_speed(int index, int *fan_speed);
94static	int		read_vol_temp(ptree_rarg_t *parg, void *buf);
95static	int		read_vol_volts_status(ptree_rarg_t *parg, void *buf);
96static	int		read_vol_volts_shutdown(ptree_rarg_t *parg, void *buf);
97static	int		read_fan_speed(ptree_rarg_t *parg, void *buf);
98static	int		read_fan_status(ptree_rarg_t *parg, void *buf);
99static	int		lookup_led_status(int8_t state, const char **string);
100static	int		read_led_status(ptree_rarg_t *parg, void *buf);
101static	void		convert_node_name(char *ptr);
102static	int		add_temp_sensors(int lom_fd, picl_nodehdl_t lominfh);
103static	int		add_voltage_monitors(int lom_fd,
104    picl_nodehdl_t lominfh);
105static	int		add_fan_nodes(int lom_fd, picl_nodehdl_t lominfh);
106static	int		get_config_file(char *outfilename);
107
108#pragma	init(picllom_register)
109
110static picld_plugin_reg_t  my_reg_info = {
111	PICLD_PLUGIN_VERSION_1,
112	PICLD_PLUGIN_NON_CRITICAL,
113	"SUNW_picllom",
114	picllom_init,
115	picllom_fini
116};
117
118static const char str_OK[] = "OK";
119static const char str_FAIL[] = "FAIL";
120static const char str_On[] = "on";
121static const char str_Off[] = "off";
122static const char str_Enabled[] = "Enabled";
123static const char str_Disabled[] = "Disabled";
124static char lom_device_path[PATH_MAX];
125static tempr_t high_warnings[MAX_TEMPS];
126static tempr_t high_shutdowns[MAX_TEMPS];
127static picl_prophdl_t temp_handles[MAX_TEMPS];
128static	lom_fandata_t fandata;
129static	picl_prophdl_t	fan_speed_handles[MAX_FANS];
130static	picl_prophdl_t	fan_status_handles[MAX_FANS];
131static	lom_volts_t	voltsdata;
132static	picl_prophdl_t	volts_status_handles[MAX_VOLTS];
133static	picl_prophdl_t	volts_shutdown_handles[MAX_VOLTS];
134static	int		n_leds = 0;
135static	int		max_state_size = 0;
136static	picl_prophdl_t	*led_handles = NULL;
137static	char		**led_labels = NULL;
138static	lom2_info_t	info2data;
139static	struct {
140	int		size;
141	char		*str_colour;
142} colour_lkup[1 + LOM_LED_COLOUR_AMBER];
143
144static	struct {
145	int8_t		state;
146	char		*str_ledstate;
147} ledstate_lkup[] = {
148	{	LOM_LED_OFF			},
149	{	LOM_LED_ON			},
150	{	LOM_LED_BLINKING		},
151};
152
153static node_el_t *
154create_node_el(picl_nodehdl_t nodeh)
155{
156	node_el_t *ptr = malloc(sizeof (node_el_t));
157
158	if (ptr != NULL) {
159		ptr->nodeh = nodeh;
160		ptr->next = NULL;
161	}
162
163	return (ptr);
164}
165
166static void
167delete_node_el(node_el_t *pel)
168{
169	free(pel);
170}
171
172static node_list_t *
173create_node_list()
174{
175	node_list_t *ptr = malloc(sizeof (node_list_t));
176
177	if (ptr != NULL) {
178		ptr->head = NULL;
179		ptr->tail = NULL;
180	}
181
182	return (ptr);
183}
184
185static void
186delete_node_list_contents(node_list_t *pnl)
187{
188	node_el_t	*pel;
189
190	if (pnl == NULL)
191		return;
192
193	while ((pel = pnl->head) != NULL) {
194		pnl->head = pel->next;
195		delete_node_el(pel);
196	}
197
198	pnl->tail = NULL;
199}
200
201static void
202delete_node_list(node_list_t *pnl)
203{
204	delete_node_list_contents(pnl);
205	free(pnl);
206}
207
208/*
209 * Get a linking element and add handle to end of chain
210 */
211static void
212add_node_to_list(picl_nodehdl_t nodeh, node_list_t *listp)
213{
214	node_el_t	*pel = create_node_el(nodeh);
215
216	if (pel != NULL) {
217		if (listp->tail == NULL)
218			listp->head = pel;
219		else
220			listp->tail->next = pel;
221
222		listp->tail = pel;
223	}
224}
225
226/*
227 * Get a list of nodes of the specified classname under nodeh.
228 * Once a node of the specified class is found, its children are not
229 * searched.
230 */
231static void
232get_node_list_by_class(picl_nodehdl_t nodeh, const char *classname,
233	    node_list_t *listp)
234{
235	int		err;
236	char		clname[PICL_CLASSNAMELEN_MAX+1];
237	picl_nodehdl_t	chdh;
238
239	/*
240	 * go through the children
241	 */
242	err = ptree_get_propval_by_name(nodeh, PICL_PROP_CHILD, &chdh,
243	    sizeof (picl_nodehdl_t));
244
245	while (err == PICL_SUCCESS) {
246		err = ptree_get_propval_by_name(chdh, PICL_PROP_CLASSNAME,
247		    clname, strlen(classname) + 1);
248
249		if ((err == PICL_SUCCESS) && (strcmp(clname, classname) == 0))
250			add_node_to_list(chdh, listp);
251		else
252			get_node_list_by_class(chdh, classname, listp);
253
254		err = ptree_get_propval_by_name(chdh, PICL_PROP_PEER, &chdh,
255		    sizeof (picl_nodehdl_t));
256	}
257}
258
259static int
260get_lom_node(picl_nodehdl_t *lominfh)
261{
262	int			err = PICL_SUCCESS;
263	node_list_t		*listp;
264
265	listp = create_node_list();
266
267	if ((err = ptree_get_node_by_path(PICL_NODE_ROOT PICL_NODE_PLATFORM,
268	    lominfh)) != PICL_SUCCESS) {
269		syslog(LOG_ERR, EM_MISSING_NODE,
270		    PICL_NODE_ROOT PICL_NODE_PLATFORM);
271		return (err);	/* no /platform ! */
272	}
273
274	get_node_list_by_class(*lominfh, PICL_CLASS_SERVICE_PROCESSOR, listp);
275
276	if (listp->head == NULL) {
277		*lominfh = 0;
278		syslog(LOG_ERR, EM_MISSING_NODE, PICL_CLASS_SERVICE_PROCESSOR);
279		err = PICL_NODENOTFOUND;
280	} else {
281		*lominfh = listp->head->nodeh;
282
283		if (listp->head != listp->tail)
284			syslog(LOG_ERR, EM_LOM_DUPLICATE);
285	}
286
287	delete_node_list(listp);
288	return (err);
289}
290
291static int
292get_lom_device_path(picl_nodehdl_t *lominfh)
293{
294	int err = PICL_SUCCESS;
295	char devfs_path[PATH_MAX];
296	char devices_path[PATH_MAX];
297
298	err = ptree_get_propval_by_name(*lominfh, PICL_PROP_DEVFS_PATH,
299	    devfs_path, sizeof (devfs_path));
300
301	/* Build up the full device path and set the global */
302	strcpy(devices_path, "/devices");
303	strcat(devices_path, devfs_path);
304	strcat(devices_path, LOM_DEV_MINOR_NAME);
305	strcpy(lom_device_path, devices_path);
306
307	return (err);
308
309}
310
311
312
313
314/*
315 * Look for a node of specified name and class
316 * Confine search to nodes one level below that of supplied handle
317 */
318static int
319get_node_by_name_and_class(picl_nodehdl_t srchnodeh, const char *nodename,
320    const char *classname, picl_nodehdl_t *chdh)
321{
322	int			err;
323	char			namebuf[PATH_MAX];
324
325	err = ptree_get_propval_by_name(srchnodeh, PICL_PROP_CHILD, chdh,
326	    sizeof (picl_nodehdl_t));
327
328	while (err == PICL_SUCCESS) {
329		err = ptree_get_propval_by_name(*chdh, PICL_PROP_NAME, namebuf,
330		    sizeof (namebuf));
331		if (err != PICL_SUCCESS)
332			break;
333		if (strcmp(namebuf, nodename) == 0) {
334			err = ptree_get_propval_by_name(*chdh,
335			    PICL_PROP_CLASSNAME, namebuf, sizeof (namebuf));
336			if ((err == PICL_SUCCESS) &&
337			    (strcmp(namebuf, classname) == 0))
338				return (PICL_SUCCESS);
339		}
340		err = ptree_get_propval_by_name(*chdh, PICL_PROP_PEER, chdh,
341		    sizeof (picl_nodehdl_t));
342	}
343
344	return (err);
345}
346
347/*
348 * Create and add the specified regular property
349 */
350
351static int
352add_regular_prop(picl_nodehdl_t nodeh, const char *name, int type, int access,
353    int size, const void *valbuf, picl_prophdl_t *prophp)
354{
355	int			err;
356	ptree_propinfo_t	propinfo;
357	picl_prophdl_t		proph;
358
359	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
360	    type, access, size, (char *)name, NULL, NULL);
361	if (err != PICL_SUCCESS)
362		return (err);
363
364	err = ptree_create_and_add_prop(nodeh, &propinfo, (void *)valbuf,
365	    &proph);
366	if (err == PICL_SUCCESS && prophp)
367		*prophp = proph;
368	return (err);
369}
370
371
372/*
373 * Create and add the specified volatile property
374 */
375static int
376add_volatile_prop(picl_nodehdl_t nodeh, char *name, int type, int access,
377    int size, ptree_vol_rdfunc_t rdfunc, ptree_vol_wrfunc_t wrfunc,
378    picl_prophdl_t *prophp)
379{
380	int			err;
381	ptree_propinfo_t	propinfo;
382	picl_prophdl_t		proph;
383
384	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
385	    type, (access|PICL_VOLATILE), size, name, rdfunc, wrfunc);
386	if (err != PICL_SUCCESS)
387		return (err);
388
389	err = ptree_create_and_add_prop(nodeh, &propinfo, NULL, &proph);
390	if (err == PICL_SUCCESS && prophp)
391		*prophp = proph;
392	return (err);
393}
394
395/*
396 * open LOM device to read
397 */
398static int
399open_lom_rd(int *lom_fd)
400{
401	*lom_fd = open(lom_device_path, O_RDONLY);
402
403	if (*lom_fd < 0)
404		return (PICL_FAILURE);
405
406	return (PICL_SUCCESS);
407}
408
409/*
410 * Function to open LOM and read temperature sensor values.
411 * The index to a specific sensor is supplied and that value returned.
412 */
413static int
414get_lom_temp(int index, tempr_t *temp_p)
415{
416	lom_temp_t	lom_temp;
417	int		lom_fd;
418	int		err;
419	int		res;
420
421	err = open_lom_rd(&lom_fd);
422
423	if (err == PICL_SUCCESS) {
424		res = ioctl(lom_fd, LOMIOCTEMP, &lom_temp);
425		(void) close(lom_fd);
426
427		if (res == 0) {
428			*temp_p = lom_temp.temp[index];
429		} else {
430			err = PICL_FAILURE;
431		}
432	}
433
434	return (err);
435}
436
437/*
438 * Function to open LOM and read voltage monitor values.
439 * Called for each property, so only perform update if time has changed
440 */
441static int
442update_voltage_stats()
443{
444	static time_t	then = 0;
445	int		lom_fd;
446	int		err;
447	int		res;
448	time_t		now = time(NULL);
449
450	if (now == then)
451		return (PICL_SUCCESS);
452
453	then = now;
454	err = open_lom_rd(&lom_fd);
455
456	if (err == PICL_SUCCESS) {
457		res = ioctl(lom_fd, LOMIOCVOLTS, &voltsdata);
458		(void) close(lom_fd);
459		if (res < 0) {
460			err = PICL_FAILURE;
461		}
462	}
463
464	return (err);
465}
466
467/*
468 * Function to open LOM and read voltage monitor values.
469 * The index to a specific voltage status is supplied and that value returned.
470 */
471static int
472get_lom_volts_status(int index, int *voltsStatus_p)
473{
474	int res;
475
476	if ((res = update_voltage_stats()) != PICL_SUCCESS)
477		return (res);
478
479	*voltsStatus_p = voltsdata.status[index];
480	return (PICL_SUCCESS);
481}
482
483/*
484 * Function to open LOM and read voltage monitor values.
485 * The index to a specific shutdown flag is supplied and that value returned.
486 */
487static int
488get_lom_volts_shutdown(int index, int *voltsShutdown_p)
489{
490	int res;
491
492	if ((res = update_voltage_stats()) != PICL_SUCCESS)
493		return (res);
494
495	*voltsShutdown_p = voltsdata.shutdown_enabled[index];
496	return (PICL_SUCCESS);
497}
498
499
500
501/*
502 * Function to open LOM and read fan values.
503 * Called for each property, so only perform update if time has changed
504 */
505static int
506update_fan_stats()
507{
508	static time_t	then = 0;
509	int		lom_fd;
510	int		err;
511	int		res;
512	time_t		now = time(NULL);
513
514	if (now == then)
515		return (PICL_SUCCESS);
516
517	then = now;
518	err = open_lom_rd(&lom_fd);
519	if (err == PICL_SUCCESS) {
520		res = ioctl(lom_fd, LOMIOCFANSTATE, &fandata);
521		(void) close(lom_fd);
522		if (res < 0) {
523			err = PICL_FAILURE;
524		}
525	}
526
527	return (err);
528}
529
530
531
532/*
533 * The index to a specific fan is supplied and its speed value returned.
534 */
535static int
536get_lom_fan_speed(int index, int *fan_speed)
537{
538	int res;
539
540	if ((res = update_fan_stats()) != PICL_SUCCESS)
541		return (res);
542
543	*fan_speed = fandata.speed[index];
544	return (PICL_SUCCESS);
545}
546
547
548/*
549 * Read function for volatile "Temperature" property via LOM
550 */
551static int
552read_vol_temp(ptree_rarg_t *parg, void *buf)
553{
554	tempr_t 	temp;
555	picl_prophdl_t	proph;
556	int		index;
557
558	/*
559	 * get the sensor index from the displacement of the
560	 * property handle and get its temperature.
561	 */
562	proph = parg->proph;
563	for (index = 0; index < MAX_TEMPS; index++) {
564		if (temp_handles[index] == proph)
565			break;
566	}
567
568	if (index == MAX_TEMPS) {
569		/*
570		 * Handle not found. As this is a plugin, stale handles
571		 * cannot occur, so just fail.
572		 */
573		return (PICL_FAILURE);
574	}
575
576	if (get_lom_temp(index, &temp) != PICL_SUCCESS)
577			return (PICL_FAILURE);
578	(void) memcpy(buf, (caddr_t)&temp, sizeof (tempr_t));
579	return (PICL_SUCCESS);
580}
581
582/*
583 * Read function for volatile "VoltageStatus" property via LOM
584 */
585static int
586read_vol_volts_status(ptree_rarg_t *parg, void *buf)
587{
588	int		voltsStatus;
589	picl_prophdl_t	proph;
590	int		index;
591
592	/*
593	 * get the voltage monitor index from the displacement of the
594	 * status property handle and get its status.
595	 */
596	proph = parg->proph;
597
598	for (index = 0; index < MAX_VOLTS; index++) {
599		if (volts_status_handles[index] == proph)
600			break;
601	}
602
603	if (index == MAX_VOLTS)
604		return (PICL_FAILURE);
605
606	if (get_lom_volts_status(index, &voltsStatus) != PICL_SUCCESS)
607		return (PICL_FAILURE);
608
609	(void) strlcpy(buf, (voltsStatus == 0) ? str_OK : str_FAIL,
610	    sizeof (str_FAIL));
611	return (PICL_SUCCESS);
612}
613
614/*
615 * Read function for volatile "VoltageShutdown" property via LOM
616 */
617static int
618read_vol_volts_shutdown(ptree_rarg_t *parg, void *buf)
619{
620	int		voltsShutdown;
621	picl_prophdl_t	proph;
622	int		index;
623
624	/*
625	 * get the voltage monitor index from the displacement of the
626	 * shutdown property handle and get its value.
627	 */
628	proph = parg->proph;
629
630	for (index = 0; index < MAX_VOLTS; index++) {
631		if (volts_shutdown_handles[index] == proph)
632			break;
633	}
634
635	if (index == MAX_VOLTS)
636		return (PICL_FAILURE);
637
638	if (get_lom_volts_shutdown(index, &voltsShutdown) != PICL_SUCCESS)
639		return (PICL_FAILURE);
640
641	(void) strlcpy(buf, (voltsShutdown == 0) ? str_Disabled : str_Enabled,
642	    sizeof (str_Disabled));
643	return (PICL_SUCCESS);
644}
645
646
647/*
648 * Read function for volatile fan speed property via LOM
649 */
650static int
651read_fan_speed(ptree_rarg_t *parg, void *buf)
652{
653	int		fan_speed;
654	picl_prophdl_t	proph;
655	int		index;
656
657	/*
658	 * get the relevant fan from the displacement of its property handle
659	 */
660	proph = parg->proph;
661
662	for (index = 0; index < MAX_FANS; index++) {
663		if (fan_speed_handles[index] == proph)
664			break;
665	}
666
667	if (index == MAX_FANS)
668		return (PICL_FAILURE);
669
670	if (get_lom_fan_speed(index, &fan_speed) != PICL_SUCCESS)
671		return (PICL_FAILURE);
672
673	(void) memcpy(buf, (caddr_t)&fan_speed, sizeof (fan_speed));
674	return (PICL_SUCCESS);
675}
676
677/*
678 * look up function to convert led status into string
679 */
680static int
681lookup_led_status(int8_t state, const char **string)
682{
683	int	i;
684	int	lim = sizeof (ledstate_lkup) / sizeof (ledstate_lkup[0]);
685
686	for (i = 0; i < lim; i++) {
687		if (ledstate_lkup[i].state == state) {
688			*string = ledstate_lkup[i].str_ledstate;
689			return (PICL_SUCCESS);
690		}
691	}
692
693	*string = "";
694	switch (state) {
695	case LOM_LED_ACCESS_ERROR:
696		return (PICL_PROPVALUNAVAILABLE);
697	case LOM_LED_NOT_IMPLEMENTED:
698	/*FALLTHROUGH*/
699	case LOM_LED_OUTOFRANGE:
700	/*FALLTHROUGH*/
701	default:
702		return (PICL_FAILURE);
703	}
704}
705
706/*
707 * Read function for volatile led status property.
708 */
709static int
710read_led_status(ptree_rarg_t *parg, void *buf)
711{
712	lom_led_state_t	led_data;
713	picl_prophdl_t	proph;
714	int		index;
715	int		lom_fd;
716	int		res;
717	const char	*string;
718
719	/*
720	 * get the relevant led from the displacement of its property handle
721	 */
722	proph = parg->proph;
723
724	for (index = 0; index < n_leds; index++) {
725		if (led_handles[index] == proph)
726			break;
727	}
728
729	if (index == n_leds)
730		return (PICL_FAILURE);
731
732	res = open_lom_rd(&lom_fd);
733	if (res != PICL_SUCCESS)
734		return (res);
735	/*
736	 * The interface for reading LED status doesn't promise to maintain
737	 * a constant mapping between LED index number and LED identity
738	 * (as defined by its label). On the other hand, PICL does promise
739	 * that whilst a handle remains valid the object it represents will
740	 * remain constant. To reconcile these positions, we maintain
741	 * tables of labels and handles linked by index value. We search
742	 * for the handle with which we are presented and then locate its
743	 * label. Then we request LED entries from the LOM and compare their
744	 * labels with the one we seek. As an optimisation, we try the original
745	 * index value first and then revert to a full search.
746	 */
747	(void) memset(&led_data, 0, sizeof (led_data));
748	led_data.index = index;
749	res = ioctl(lom_fd, LOMIOCLEDSTATE, &led_data);
750
751	if (res != 0 || led_data.state == LOM_LED_NOT_IMPLEMENTED ||
752	    strcmp(led_data.label, led_labels[index]) != 0) {
753		/*
754		 * full scan required (bet it doesn't work!)
755		 * first re-establish the range to scan
756		 */
757		int	i;
758		int	n;
759
760		(void) memset(&led_data, 0, sizeof (led_data));
761		led_data.index = -1;
762		res = ioctl(lom_fd, LOMIOCLEDSTATE, &led_data);
763
764		if (res != 0) {
765			(void) close(lom_fd);
766			return (PICL_PROPVALUNAVAILABLE);
767		}
768
769		if (led_data.state == LOM_LED_NOT_IMPLEMENTED ||
770		    strcmp(led_data.label, led_labels[index]) != 0) {
771			n = led_data.index;
772			for (i = 0; i < n; i++) {
773				(void) memset(&led_data, 0, sizeof (led_data));
774				led_data.index = i;
775				res = ioctl(lom_fd, LOMIOCLEDSTATE, &led_data);
776
777				if (res == 0 &&
778				    led_data.state != LOM_LED_NOT_IMPLEMENTED ||
779				    strcmp(led_data.label, led_labels[index]) ==
780				    0) {
781					break;
782				}
783			}
784
785			if (i == n) {
786				(void) close(lom_fd);
787				return (PICL_PROPVALUNAVAILABLE);
788			}
789		}
790	}
791
792	/*
793	 * if we get here, then we found the right LED.
794	 */
795	(void) close(lom_fd);
796	res = lookup_led_status(led_data.state, &string);
797	(void) strlcpy(buf, string, max_state_size);
798	return (res);
799}
800
801/*
802 * Read function for volatile fan status property.
803 * This is a synthesized property using speed and min speed properties
804 */
805static int
806read_fan_status(ptree_rarg_t *parg, void *buf)
807{
808	int		fan_speed;
809	picl_prophdl_t	proph;
810	int		index;
811
812	/*
813	 * get the relevant fan from the displacement of its property handle
814	 */
815	proph = parg->proph;
816
817	for (index = 0; index < MAX_FANS; index++) {
818		if (fan_status_handles[index] == proph)
819			break;
820	}
821
822	if (index == MAX_FANS)
823		return (PICL_FAILURE);
824
825	if (get_lom_fan_speed(index, &fan_speed) != PICL_SUCCESS)
826		return (PICL_FAILURE);
827
828	(void) strlcpy(buf,
829	    fan_speed < fandata.minspeed[index] ? str_FAIL : str_OK,
830	    sizeof (str_FAIL));
831	return (PICL_SUCCESS);
832}
833
834
835
836/*
837 * change to lower case and convert any spaces into hyphens
838 */
839static void
840convert_node_name(char *ptr)
841{
842	char ch;
843
844	for (ch = *ptr; ch != '\0'; ch = *++ptr) {
845		if (isupper(ch)) {
846			*ptr = tolower(ch);
847		} else if (isspace(ch)) {
848			*ptr = '-';
849		}
850	}
851}
852
853static int
854add_temp_sensors(int lom_fd, picl_nodehdl_t lominfh)
855{
856	lom_temp_t	lom_temp;
857	int		res;
858	int		i;
859	int		err = PICL_SUCCESS;
860	const char	*cptr;
861
862	res = ioctl(lom_fd, LOMIOCTEMP, &lom_temp);
863
864	if ((res == 0) && (lom_temp.num > 0)) {
865		/*
866		 * for each temperature location add a sensor node
867		 */
868		for (i = 0; i < lom_temp.num; i++) {
869			picl_nodehdl_t	tempsensh;
870			picl_prophdl_t proph;
871
872			high_warnings[i] = lom_temp.warning[i];
873			high_shutdowns[i] = lom_temp.shutdown[i];
874
875			convert_node_name(lom_temp.name[i]);
876
877			err = ptree_create_node(lom_temp.name[i],
878			    PICL_CLASS_TEMPERATURE_SENSOR, &tempsensh);
879			if (err != PICL_SUCCESS)
880				break;
881
882			err = add_volatile_prop(tempsensh,
883			    PICL_PROP_TEMPERATURE, PICL_PTYPE_INT, PICL_READ,
884			    sizeof (tempr_t), read_vol_temp, NULL,
885			    &temp_handles[i]);
886			if (err != PICL_SUCCESS)
887				break;
888
889			if (high_warnings[i] != 0) {
890				err = add_regular_prop(
891				    tempsensh, PICL_PROP_HIGH_WARNING,
892				    PICL_PTYPE_INT, PICL_READ,
893				    sizeof (tempr_t), &high_warnings[i],
894				    &proph);
895				if (err != PICL_SUCCESS)
896					break;
897			}
898
899			if (high_shutdowns[i] != 0) {
900				err = add_regular_prop(
901				    tempsensh, PICL_PROP_HIGH_SHUTDOWN,
902				    PICL_PTYPE_INT, PICL_READ,
903				    sizeof (tempr_t), &high_shutdowns[i],
904				    &proph);
905				if (err != PICL_SUCCESS)
906					break;
907			}
908
909			/*
910			 * for the benefit of prtdiag, add a label of
911			 * either enclosure or die where appropriate
912			 */
913			if ((strcasestr(lom_temp.name[i], CPU_ENCLOSURE) !=
914			    NULL) ||
915			    (strcasestr(lom_temp.name[i], CPU_AMBIENT) !=
916			    NULL)) {
917				cptr = CPU_AMBIENT;
918			} else if ((cptr = strcasestr(lom_temp.name[i],
919			    CPU_DIE)) != NULL) {
920				cptr = CPU_DIE;
921			}
922
923			if (cptr != NULL) {
924				err = add_regular_prop(
925				    tempsensh, PICL_PROP_LABEL,
926				    PICL_PTYPE_CHARSTRING, PICL_READ,
927				    strlen(cptr) + 1, cptr, &proph);
928
929				if (err != PICL_SUCCESS) {
930					break;
931				}
932			}
933
934			err = ptree_add_node(lominfh, tempsensh);
935
936			if (err != PICL_SUCCESS)
937				break;
938		}
939
940		if (err != PICL_SUCCESS) {
941			syslog(LOG_ERR, EM_LOMINFO_TREE_FAILED);
942		}
943	}
944
945	return (err);
946}
947
948static int
949add_voltage_monitors(int lom_fd, picl_nodehdl_t lominfh)
950{
951	int		res;
952	int		i;
953	int		err = PICL_SUCCESS;
954	picl_prophdl_t	proph;
955
956	res = ioctl(lom_fd, LOMIOCVOLTS, &voltsdata);
957
958	if ((res == 0) && (voltsdata.num > 0)) {
959		/*
960		 * for each voltage monitor add a monitor node
961		 */
962		for (i = 0; i < voltsdata.num; i++) {
963			picl_nodehdl_t	voltsmonh;
964
965			convert_node_name(voltsdata.name[i]);
966
967			err = ptree_create_node(voltsdata.name[i],
968			    PICL_CLASS_VOLTAGE_INDICATOR, &voltsmonh);
969			if (err != PICL_SUCCESS)
970				break;
971
972			err = add_regular_prop(voltsmonh, PICL_PROP_LABEL,
973			    PICL_PTYPE_CHARSTRING, PICL_READ,
974			    strlen(voltsdata.name[i]) + 1,
975			    voltsdata.name[i], &proph);
976			if (err != PICL_SUCCESS)
977				break;
978
979			err = add_volatile_prop(voltsmonh, PICL_VOLTS_SHUTDOWN,
980			    PICL_PTYPE_CHARSTRING, PICL_READ,
981			    sizeof (str_Disabled), read_vol_volts_shutdown,
982			    NULL, &volts_shutdown_handles[i]);
983			if (err != PICL_SUCCESS)
984				break;
985
986			err = add_volatile_prop(voltsmonh, PICL_PROP_CONDITION,
987			    PICL_PTYPE_CHARSTRING, PICL_READ,
988			    sizeof (str_FAIL), read_vol_volts_status, NULL,
989			    &volts_status_handles[i]);
990			if (err != PICL_SUCCESS)
991				break;
992
993			err = ptree_add_node(lominfh, voltsmonh);
994
995			if (err != PICL_SUCCESS)
996				break;
997		}
998
999		if (err != PICL_SUCCESS) {
1000			syslog(LOG_ERR, EM_LOMINFO_TREE_FAILED);
1001		}
1002	}
1003
1004	return (err);
1005}
1006
1007static void
1008add_led(const lom_led_state_t *led_state, picl_nodehdl_t lominfh)
1009{
1010	int		err;
1011	picl_nodehdl_t	ledh;
1012	picl_nodehdl_t	proph;
1013
1014	if (((unsigned char)led_state->state == LOM_LED_STATE_NOT_PRESENT) ||
1015	    (led_state->label[0] == '\0')) {
1016		return;
1017	}
1018
1019	err = ptree_create_node(led_state->label, PICL_CLASS_LED, &ledh);
1020	/*
1021	 * the led may exist already, e.g. Fault
1022	 */
1023	if (err != PICL_SUCCESS)
1024		return;
1025
1026	/*
1027	 * Unlike LEDs derived from other interfaces, these are not
1028	 * writable. Establish a read-only volatile property.
1029	 */
1030	err = add_volatile_prop(ledh, PICL_PROP_STATE, PICL_PTYPE_CHARSTRING,
1031	    PICL_READ, max_state_size, read_led_status, NULL,
1032	    &led_handles[led_state->index]);
1033	if (err != PICL_SUCCESS)
1034		return;
1035
1036	/*
1037	 * if colour was defined for this LED, add a colour property
1038	 */
1039	if ((led_state->colour != LOM_LED_COLOUR_NONE) &&
1040	    (led_state->colour != LOM_LED_COLOUR_ANY)) {
1041		err = add_regular_prop(ledh, PICL_PROP_COLOR,
1042		    PICL_PTYPE_CHARSTRING, PICL_READ,
1043		    colour_lkup[led_state->index].size,
1044		    colour_lkup[led_state->index].str_colour, &proph);
1045	}
1046	if (err != PICL_SUCCESS)
1047		return;
1048
1049	err = add_regular_prop(ledh, PICL_PROP_LABEL,
1050	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(led_state->label) + 1,
1051	    led_state->label, &proph);
1052	if (err != PICL_SUCCESS)
1053		return;
1054
1055	err = ptree_add_node(lominfh, ledh);
1056
1057	if (err != PICL_SUCCESS) {
1058		syslog(LOG_ERR, EM_LOMINFO_TREE_FAILED);
1059	}
1060}
1061
1062static void
1063fixstate(uint8_t state, const char *string, int *max_len)
1064{
1065	int		i;
1066	int		len;
1067
1068	for (i = 0; i < (sizeof (ledstate_lkup) / sizeof (ledstate_lkup[0]));
1069	    i++) {
1070		if (ledstate_lkup[i].state == state) {
1071			if (ledstate_lkup[i].str_ledstate != NULL)
1072				free(ledstate_lkup[i].str_ledstate);
1073			ledstate_lkup[i].str_ledstate = strdup(string);
1074			len = strlen(string);
1075			if (len >= *max_len)
1076				*max_len = len + 1;
1077			break;
1078		}
1079	}
1080}
1081
1082static void
1083add_led_nodes(int lom_fd, picl_nodehdl_t lominfh)
1084{
1085	lom_led_state_t	led_data;
1086	picl_nodehdl_t	ledh;
1087	int		res;
1088	int		i;
1089
1090	/*
1091	 * If the led state enquiry ioctl is supported, an enquiry on
1092	 * index -1 will return the state of the highest supported index
1093	 * value.
1094	 */
1095	(void) memset(&led_data, 0, sizeof (led_data));
1096	led_data.index = -1;
1097	res = ioctl(lom_fd, LOMIOCLEDSTATE, &led_data);
1098
1099	if (res != 0)
1100		return;
1101
1102	if (led_labels != NULL) {
1103		for (i = 0; i < n_leds; i++) {
1104			if (led_labels[i] != NULL) {
1105				free(led_labels[i]);
1106			}
1107		}
1108
1109		free(led_labels);
1110		led_labels = NULL;
1111	}
1112
1113	if (led_handles != NULL) {
1114		free(led_handles);
1115	}
1116
1117	n_leds = 0;
1118	led_handles = calloc(led_data.index + 1, sizeof (picl_nodehdl_t));
1119	led_labels = calloc(led_data.index + 1, sizeof (char *));
1120
1121	if ((led_labels == NULL) || (led_handles == NULL)) {
1122		if (led_labels != NULL)
1123			free(led_labels);
1124		if (led_handles != NULL)
1125			free(led_handles);
1126		led_labels = NULL;
1127		led_handles = NULL;
1128		syslog(LOG_ERR, EM_NO_LED_MEM);
1129		return;
1130	}
1131
1132	n_leds = led_data.index + 1;
1133
1134	/*
1135	 * For each LED with a valid state, add a node
1136	 * and because of the ludicrous API, stache a copy of its label too
1137	 */
1138	for (i = 0; i < n_leds; i++) {
1139		(void) memset(&led_data, 0, sizeof (led_data));
1140		led_data.index = i;
1141		res = ioctl(lom_fd, LOMIOCLEDSTATE, &led_data);
1142
1143		if (res != 0)
1144			continue;
1145
1146		if (led_data.state == LOM_LED_OUTOFRANGE ||
1147		    led_data.state == LOM_LED_NOT_IMPLEMENTED)
1148			continue;
1149
1150
1151		led_labels[i] = strdup(led_data.label);
1152		convert_node_name(led_data.label);
1153
1154		if (get_node_by_name_and_class(lominfh, led_data.label,
1155		    "led", &ledh) != PICL_SUCCESS) {
1156			/*
1157			 * only add a new led node,
1158			 * if it's not already in place
1159			 */
1160			add_led(&led_data, lominfh);
1161		}
1162	}
1163}
1164
1165static int
1166add_fan_nodes(int lom_fd, picl_nodehdl_t lominfh)
1167{
1168	int		res;
1169	int		i;
1170	int		err = PICL_SUCCESS;
1171
1172	res = ioctl(lom_fd, LOMIOCFANSTATE, &fandata);
1173
1174	if (res == 0) {
1175		/*
1176		 * fan data available through lom, remove any placeholder
1177		 * fan-unit nodes, they will be superseded via lom.conf
1178		 */
1179		char	path[80];
1180		int	slot;
1181		picl_nodehdl_t	fan_unit_h;
1182
1183		for (slot = 0; slot < MAX_FANS; slot++) {
1184			(void) snprintf(path, sizeof (path),
1185			    "/frutree/chassis/fan-slot?Slot=%d/fan-unit", slot);
1186			if (ptree_get_node_by_path(path, &fan_unit_h) !=
1187			    PICL_SUCCESS)
1188				continue;
1189			if (ptree_delete_node(fan_unit_h) != PICL_SUCCESS)
1190				continue;
1191			(void) ptree_destroy_node(fan_unit_h);
1192		}
1193		/*
1194		 * see if fan names can be obtained
1195		 */
1196		(void) memset(&info2data, 0, sizeof (info2data));
1197		/*
1198		 * if LOMIOCINFO2 not supported, names area
1199		 * will remain empty
1200		 */
1201		(void) ioctl(lom_fd, LOMIOCINFO2, &info2data);
1202
1203		/*
1204		 * for each fan which is present, add a fan node
1205		 */
1206		for (i = 0; i < MAX_FANS; i++) {
1207			char fanname[80];
1208			picl_nodehdl_t	fanh;
1209			picl_nodehdl_t	proph;
1210
1211			if (fandata.fitted[i] == 0)
1212				continue;
1213
1214			if (info2data.fan_names[i][0] == '\0') {
1215				(void) snprintf(fanname, sizeof (fanname),
1216				    "fan%d", i + 1);
1217			} else {
1218				(void) strlcpy(fanname, info2data.fan_names[i],
1219				    sizeof (fanname));
1220			}
1221			convert_node_name(fanname);
1222			err = ptree_create_node(fanname, PICL_CLASS_FAN, &fanh);
1223			if (err != PICL_SUCCESS)
1224				break;
1225
1226			err = add_volatile_prop(fanh, PICL_PROP_FAN_SPEED,
1227			    PICL_PTYPE_INT, PICL_READ, sizeof (int),
1228			    read_fan_speed, NULL, &fan_speed_handles[i]);
1229			if (err != PICL_SUCCESS)
1230				break;
1231
1232			err = add_regular_prop(fanh, PICL_PROP_LOW_WARNING,
1233			    PICL_PTYPE_INT, PICL_READ, sizeof (int),
1234			    &fandata.minspeed[i], &proph);
1235			if (err != PICL_SUCCESS)
1236				break;
1237
1238			err = add_regular_prop(fanh, PICL_PROP_FAN_SPEED_UNIT,
1239			    PICL_PTYPE_CHARSTRING, PICL_READ, sizeof ("%"),
1240			    "%", &proph);
1241			if (err != PICL_SUCCESS)
1242				break;
1243
1244			err = add_volatile_prop(fanh, PICL_PROP_CONDITION,
1245			    PICL_PTYPE_CHARSTRING, PICL_READ,
1246			    sizeof (str_FAIL), read_fan_status, NULL,
1247			    &fan_status_handles[i]);
1248			if (err != PICL_SUCCESS)
1249				break;
1250
1251			/*
1252			 * add a label for prtdiag
1253			 */
1254			err = add_regular_prop(fanh, PICL_PROP_LABEL,
1255			    PICL_PTYPE_CHARSTRING, PICL_READ,
1256			    strlen(fanname) + 1, fanname, &proph);
1257			if (err != PICL_SUCCESS)
1258				break;
1259
1260			err = ptree_add_node(lominfh, fanh);
1261			if (err != PICL_SUCCESS)
1262				break;
1263		}
1264
1265		if (err != PICL_SUCCESS) {
1266			syslog(LOG_ERR, EM_LOMINFO_TREE_FAILED);
1267		}
1268	}
1269
1270	return (err);
1271}
1272
1273static void
1274setup_strings()
1275{
1276	/*
1277	 * initialise led colours lookup
1278	 */
1279	int i;
1280	int lim = sizeof (colour_lkup) / sizeof (colour_lkup[0]);
1281
1282	for (i = 0; i < lim; i++) {
1283		if (colour_lkup[i].str_colour != NULL)
1284			free(colour_lkup[i].str_colour);
1285	}
1286
1287	colour_lkup[LOM_LED_COLOUR_ANY].str_colour = strdup(gettext("any"));
1288	colour_lkup[LOM_LED_COLOUR_WHITE].str_colour = strdup(gettext("white"));
1289	colour_lkup[LOM_LED_COLOUR_BLUE].str_colour = strdup(gettext("blue"));
1290	colour_lkup[LOM_LED_COLOUR_GREEN].str_colour = strdup(gettext("green"));
1291	colour_lkup[LOM_LED_COLOUR_AMBER].str_colour = strdup(gettext("amber"));
1292
1293	for (i = 0; i < lim; i++) {
1294		if (colour_lkup[i].str_colour != NULL)
1295			colour_lkup[i].size =
1296			    1 + strlen(colour_lkup[i].str_colour);
1297	}
1298
1299	/*
1300	 * initialise led state lookup strings
1301	 */
1302	fixstate(LOM_LED_OFF, gettext("off"), &max_state_size);
1303	fixstate(LOM_LED_ON, gettext("on"), &max_state_size);
1304	fixstate(LOM_LED_BLINKING, gettext("blinking"), &max_state_size);
1305}
1306
1307/*
1308 * The size of outfilename must be PATH_MAX
1309 */
1310static int
1311get_config_file(char *outfilename)
1312{
1313	char	nmbuf[SYS_NMLN];
1314	char	pname[PATH_MAX];
1315
1316	if (sysinfo(SI_PLATFORM, nmbuf, sizeof (nmbuf)) != -1) {
1317		(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
1318		(void) strlcat(pname, LOM_CONFFILE_NAME, PATH_MAX);
1319		if (access(pname, R_OK) == 0) {
1320			(void) strlcpy(outfilename, pname, PATH_MAX);
1321			return (0);
1322		}
1323	}
1324
1325	if (sysinfo(SI_MACHINE, nmbuf, sizeof (nmbuf)) != -1) {
1326		(void) snprintf(pname, PATH_MAX, PICLD_PLAT_PLUGIN_DIRF, nmbuf);
1327		(void) strlcat(pname, LOM_CONFFILE_NAME, PATH_MAX);
1328		if (access(pname, R_OK) == 0) {
1329			(void) strlcpy(outfilename, pname, PATH_MAX);
1330			return (0);
1331		}
1332	}
1333
1334	(void) snprintf(pname, PATH_MAX, "%s/%s", PICLD_COMMON_PLUGIN_DIR,
1335	    LOM_CONFFILE_NAME);
1336
1337	if (access(pname, R_OK) == 0) {
1338		(void) strlcpy(outfilename, pname, PATH_MAX);
1339		return (0);
1340	}
1341
1342	return (-1);
1343}
1344
1345
1346
1347/*
1348 * executed as part of .init when the plugin is dlopen()ed
1349 */
1350static void
1351picllom_register(void)
1352{
1353	(void) picld_plugin_register(&my_reg_info);
1354}
1355
1356/*
1357 * Init entry point of the plugin
1358 * Creates the PICL nodes and properties in the physical and logical aspects.
1359 */
1360static void
1361picllom_init(void)
1362{
1363	picl_nodehdl_t		rooth;
1364	picl_nodehdl_t		plfh;
1365	picl_nodehdl_t		lominfh;
1366	int			lom_fd;
1367	char			fullfilename[PATH_MAX];
1368
1369	/*
1370	 * Get platform node
1371	 */
1372	if (ptree_get_node_by_path(PICL_NODE_ROOT PICL_NODE_PLATFORM, &plfh)
1373	    != PICL_SUCCESS) {
1374		syslog(LOG_ERR, EM_MISSING_NODE, PICL_NODE_PLATFORM);
1375		syslog(LOG_ERR, EM_INIT_FAILED);
1376		return;
1377	}
1378
1379	/*
1380	 * Get lom node
1381	 */
1382	if (get_lom_node(&lominfh) != PICL_SUCCESS) {
1383		syslog(LOG_ERR, EM_LOM_NODE_MISSING);
1384		syslog(LOG_ERR, EM_INIT_FAILED);
1385		return;
1386	}
1387
1388	/*
1389	 * Retrive the device path to open
1390	 */
1391	if (get_lom_device_path(&lominfh) < 0) {
1392		syslog(LOG_ERR, EM_INIT_FAILED);
1393		return;
1394	}
1395
1396	/*
1397	 * Open LOM device and interrogate for devices it monitors
1398	 */
1399	if ((lom_fd = open(lom_device_path, O_RDONLY)) < 0) {
1400		syslog(LOG_ERR, EM_SYS_ERR, lom_device_path, strerror(errno));
1401		return;
1402	}
1403
1404	setup_strings();
1405	(void) add_temp_sensors(lom_fd, lominfh);
1406	(void) add_voltage_monitors(lom_fd, lominfh);
1407	(void) add_fan_nodes(lom_fd, lominfh);
1408	add_led_nodes(lom_fd, lominfh);
1409
1410
1411	if (get_config_file(fullfilename) < 0) {
1412		(void) close(lom_fd);
1413		syslog(LOG_ERR, EM_NO_CONFIG);
1414		return;
1415	}
1416
1417	if (ptree_get_root(&rooth) != PICL_SUCCESS) {
1418		(void) close(lom_fd);
1419		return;
1420	}
1421
1422	if (picld_pluginutil_parse_config_file(rooth, fullfilename) !=
1423	    PICL_SUCCESS)
1424		syslog(LOG_ERR, EM_INIT_FAILED);
1425
1426	(void) close(lom_fd);
1427}
1428
1429/*
1430 * fini entry point of the plugin
1431 */
1432static void
1433picllom_fini(void)
1434{
1435}
1436