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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2000, 2002 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * PICL Daktari platform plug-in to create environment tree nodes.
31 */
32
33#include	<poll.h>
34#include	<picl.h>
35#include	<picltree.h>
36#include	<stdio.h>
37#include	<time.h>
38#include	<fcntl.h>
39#include	<unistd.h>
40#include	<stdlib.h>
41#include	<libintl.h>
42#include	<limits.h>
43#include 	<ctype.h>
44#include	<pthread.h>
45#include	<errno.h>
46#include	<syslog.h>
47#include	<sys/types.h>
48#include	<sys/systeminfo.h>
49#include	<psvc_objects.h>
50#include	<strings.h>
51
52/*LINTLIBRARY*/
53
54#define	BUFSZ	512
55
56static psvc_opaque_t hdlp;
57
58#define	PSVC_PLUGIN_VERSION	PICLD_PLUGIN_VERSION_1
59
60#pragma init(psvc_psr_plugin_register)	/* place in .init section */
61
62
63struct proj_prop {	/* projected property */
64	picl_prophdl_t	handle;
65	picl_nodehdl_t  dst_node;
66	char		name[32];
67};
68
69typedef struct {
70	char		name[32];
71	picl_nodehdl_t	node;
72} picl_psvc_t;
73
74extern struct handle {
75	uint32_t	obj_count;
76	picl_psvc_t *objects;
77	FILE *fp;
78} psvc_hdl;
79
80extern struct proj_prop *prop_list;
81extern uint32_t proj_prop_count;
82
83void psvc_psr_plugin_init(void);
84void psvc_psr_plugin_fini(void);
85
86picld_plugin_reg_t psvc_psr_reg = {
87	PSVC_PLUGIN_VERSION,
88	PICLD_PLUGIN_CRITICAL,
89	"PSVC_PSR",
90	psvc_psr_plugin_init,
91	psvc_psr_plugin_fini
92};
93
94
95#define	PSVC_INIT_MSG		gettext("%s: Error in psvc_init(): %s\n")
96#define	PTREE_DELETE_NODE_MSG	gettext("%s: ptree_delete_node() failed: %s\n")
97#define	PTREE_GET_NODE_MSG			\
98	gettext("%s: ptree_get_node_by_path() failed for %s: %s\n")
99#define	INVALID_FILE_FORMAT_MSG		gettext("%s: Invalid file format\n")
100#define	ID_NOT_FOUND_MSG	gettext("%s: Can't determine id of %s\n")
101#define	NODE_NOT_FOUND_MSG	gettext("%s: Can't determine node of %s\n")
102#define	SIZE_NOT_FOUND_MSG	gettext("%s: Couldn't determine size of %s\n")
103#define	PTREE_CREATE_PROP_FAILED_MSG		\
104	gettext("%s: ptree_create_prop failed, %s\n")
105#define	PTREE_ADD_PROP_FAILED_MSG gettext("%s: ptree_add_prop: %s\n")
106#define	FANSPEED_PROP_NOT_FOUND_MSG		\
107	gettext("%s: Can't find property fan-speed\n")
108#define	FANSPEED_PROP_DELETE_FAILED_MSG		\
109	gettext("%s: Can't delete property fan-speed\n")
110
111static int32_t count_records(FILE *fp, char *end, uint32_t *countp)
112{
113	long first_record;
114	char *ret;
115	char buf[BUFSZ];
116	uint32_t count = 0;
117
118	first_record = ftell(fp);
119
120	while ((ret = fgets(buf, BUFSZ, fp)) != NULL) {
121		if (strncmp(end, buf, strlen(end)) == 0)
122			break;
123		++count;
124	}
125
126	if (ret == NULL) {
127		errno = EINVAL;
128		return (-1);
129	}
130
131	fseek(fp, first_record, SEEK_SET);
132	*countp = count;
133	return (0);
134}
135
136/*
137 * Find start of a section within the config file,
138 * Returns number of records in the section.
139 * FILE *fd is set to first data record within section.
140 */
141static int32_t
142find_file_section(FILE *fd, char *start)
143{
144	char *ret;
145	char buf[BUFSZ];
146	char name[32];
147	int found;
148
149	fseek(fd, 0, SEEK_SET);
150	while ((ret = fgets(buf, BUFSZ, fd)) != NULL) {
151		if (strncmp(start, buf, strlen(start)) == 0)
152			break;
153	}
154
155	if (ret == NULL) {
156		errno = EINVAL;
157		return (-1);
158	}
159
160	found = sscanf(buf, "%s", name);
161	if (found != 1) {
162		errno = EINVAL;
163		return (-1);
164	} else {
165		return (0);
166	}
167
168}
169
170static int32_t name_compare_bsearch(char *s1, picl_psvc_t *s2)
171{
172	return (strcmp(s1, s2->name));
173}
174
175static void init_err(char *fmt, char *arg1, char *arg2)
176{
177	char msg[256];
178
179	sprintf(msg, fmt, arg1, arg2);
180	syslog(LOG_ERR, msg);
181}
182
183static int
184projected_lookup(picl_prophdl_t proph, struct proj_prop **dstp)
185{
186	int i;
187
188	for (i = 0; i < proj_prop_count; ++i) {
189		if (prop_list[i].handle == proph) {
190			*dstp = &prop_list[i];
191			return (PICL_SUCCESS);
192		}
193	}
194
195	return (PICL_INVALIDHANDLE);
196}
197
198int
199fan_speed_read(ptree_rarg_t *rarg, void *buf)
200{
201	struct proj_prop *dstinfo;
202	int err;
203	ptree_propinfo_t propinfo;
204	picl_prophdl_t assoctbl;
205
206	err = projected_lookup(rarg->proph, &dstinfo);
207	if (err != PSVC_SUCCESS) {
208		return (PICL_FAILURE);
209	}
210
211
212	/* see if there's a tach switch */
213	err = ptree_get_propval_by_name(rarg->nodeh,
214	    "PSVC_FAN_PRIM_SEC_SELECTOR", &assoctbl, sizeof (assoctbl));
215
216	if (err != PICL_SUCCESS) {
217		return (err);
218	} else {
219		char switch_state[32], temp_state[32];
220		uint64_t features;
221		picl_prophdl_t entry;
222		picl_nodehdl_t tach_switch;
223		char id[PICL_PROPNAMELEN_MAX];
224		char name[PICL_PROPNAMELEN_MAX];
225
226		err = ptree_get_next_by_row(assoctbl, &entry);
227		if (err != PICL_SUCCESS) {
228			return (err);
229		}
230		err = ptree_get_propval(entry, &tach_switch,
231			sizeof (tach_switch));
232		if (err != PICL_SUCCESS) {
233			return (err);
234		}
235
236		err = ptree_get_propval_by_name(rarg->nodeh, PICL_PROP_NAME,
237			&id, PICL_PROPNAMELEN_MAX);
238
239		err = psvc_get_attr(hdlp, id, PSVC_FEATURES_ATTR, &features);
240
241		if (err != PSVC_SUCCESS) {
242			return (err);
243		}
244		if (features & PSVC_DEV_PRIMARY) {
245			strlcpy(switch_state, PSVC_SWITCH_ON,
246			    sizeof (switch_state));
247		} else {
248			strlcpy(switch_state, PSVC_SWITCH_OFF,
249			    sizeof (switch_state));
250		}
251
252		pthread_mutex_lock(&fan_mutex);
253
254		err = ptree_get_propval_by_name(tach_switch, PICL_PROP_NAME,
255			&name, PICL_PROPNAMELEN_MAX);
256
257		err = ptree_get_propval_by_name(tach_switch, "State",
258			&temp_state, sizeof (temp_state));
259
260		err = psvc_set_attr(hdlp, name, PSVC_SWITCH_STATE_ATTR,
261			&switch_state);
262
263		if (err != PSVC_SUCCESS) {
264			pthread_mutex_unlock(&fan_mutex);
265			return (err);
266		}
267		(void) poll(NULL, 0, 250);
268	}
269
270
271	err = ptree_get_propinfo(rarg->proph, &propinfo);
272
273	if (err != PICL_SUCCESS) {
274		pthread_mutex_unlock(&fan_mutex);
275		return (err);
276	}
277
278	err = ptree_get_propval_by_name(dstinfo->dst_node,
279		dstinfo->name, buf, propinfo.piclinfo.size);
280	if (err != PICL_SUCCESS) {
281		pthread_mutex_unlock(&fan_mutex);
282		return (err);
283	}
284
285	pthread_mutex_unlock(&fan_mutex);
286
287	return (PICL_SUCCESS);
288}
289
290
291/* Load projected properties */
292/*
293 * This Routine Searches through the projected properties section of the conf
294 * file and replaces the currently set up values in the CPU and IO Fan Objects
295 * Fan-Speed property to Daktari specific values
296 */
297static void
298load_projected_properties(FILE *fp)
299{
300	int32_t found;
301	ptree_propinfo_t propinfo;
302	ptree_propinfo_t dstinfo;
303	picl_prophdl_t src_prophdl, dst_prophdl;
304	picl_nodehdl_t src_node, dst_node;
305	int err, i;
306	picl_psvc_t *srcobjp, *dstobjp;
307	char src[32], dst[256];
308	char src_prop[32], dst_prop[32];
309	char buf[BUFSZ];
310	char *funcname = "load_projected_properties";
311
312	if (find_file_section(fp, "PROJECTED_PROPERTIES") != 0)
313		return;
314
315	if (count_records(fp, "PROJECTED_PROPERTIES_END",
316		&proj_prop_count) != 0) {
317		init_err(INVALID_FILE_FORMAT_MSG, funcname, 0);
318		return;
319	}
320
321
322	for (i = 0; i < proj_prop_count; ++i) {
323		fgets(buf, BUFSZ, fp);
324		found = sscanf(buf, "%s %s %s %s", src, src_prop, dst,
325			dst_prop);
326		if (found != 4) {
327			init_err(INVALID_FILE_FORMAT_MSG, funcname, 0);
328			return;
329		}
330		if (strcmp(src_prop, "Fan-speed") != 0)
331			continue;
332
333		if ((strcmp(src, "IO_BRIDGE_PRIM_FAN") == 0) ||
334			(strcmp(src, "IO_BRIDGE_SEC_FAN") == 0))
335			continue;
336
337		/* find src node */
338		if (src[0] == '/') {
339			/* picl node name, outside psvc subtree */
340			err = ptree_get_node_by_path(src, &src_node);
341			if (err != 0) {
342				init_err(NODE_NOT_FOUND_MSG, funcname, src);
343				return;
344			}
345		} else {
346			srcobjp = (picl_psvc_t *)bsearch(src, psvc_hdl.objects,
347				psvc_hdl.obj_count, sizeof (picl_psvc_t),
348				(int (*)(const void *, const void *))
349				name_compare_bsearch);
350			if (srcobjp == NULL) {
351				init_err(ID_NOT_FOUND_MSG, funcname, src);
352				return;
353			}
354			src_node = srcobjp->node;
355		}
356
357		/*
358		 * Get the property Handle for the property names "Fan-Speed"
359		 * from the source node
360		 */
361		err = ptree_get_prop_by_name(src_node, "Fan-speed",
362		    &src_prophdl);
363		if (err != 0) {
364			init_err(FANSPEED_PROP_NOT_FOUND_MSG, funcname, 0);
365			return;
366		}
367
368		/*
369		 * Delete the current property Handle as we are going to replace
370		 * it's values
371		 */
372		err = ptree_delete_prop(src_prophdl);
373		if (err != 0) {
374			init_err(FANSPEED_PROP_DELETE_FAILED_MSG, funcname, 0);
375			return;
376		}
377
378		/* destroy property created by generic plugin */
379		ptree_delete_prop(prop_list[i].handle);
380		ptree_destroy_prop(prop_list[i].handle);
381
382		/* find dest node */
383		if (dst[0] == '/') {
384			/* picl node name, outside psvc subtree */
385			err = ptree_get_node_by_path(dst, &dst_node);
386			if (err != 0) {
387				init_err(NODE_NOT_FOUND_MSG, funcname, dst);
388				return;
389			}
390			prop_list[i].dst_node = dst_node;
391		} else {
392			dstobjp = (picl_psvc_t *)bsearch(dst, psvc_hdl.objects,
393				psvc_hdl.obj_count, sizeof (picl_psvc_t),
394				(int (*)(const void *, const void *))
395				name_compare_bsearch);
396			if (dstobjp == NULL) {
397				init_err(ID_NOT_FOUND_MSG, funcname, dst);
398				return;
399			}
400			prop_list[i].dst_node = dstobjp->node;
401			dst_node = dstobjp->node;
402		}
403
404		/* determine destination property size */
405		err = ptree_get_first_prop(dst_node, &dst_prophdl);
406		while (err == 0) {
407			err = ptree_get_propinfo(dst_prophdl, &dstinfo);
408			if (err != 0)
409				break;
410			if (strcmp(dst_prop, dstinfo.piclinfo.name) == 0)
411				break;
412			err = ptree_get_next_prop(dst_prophdl, &dst_prophdl);
413		}
414		if (err != 0) {
415			init_err(SIZE_NOT_FOUND_MSG, funcname, dst_prop);
416			return;
417		}
418
419		propinfo.version = PSVC_PLUGIN_VERSION;
420		propinfo.read = fan_speed_read;
421		propinfo.write = 0;
422		propinfo.piclinfo.type = dstinfo.piclinfo.type;
423		propinfo.piclinfo.accessmode = PICL_READ | PICL_VOLATILE;
424		propinfo.piclinfo.size = dstinfo.piclinfo.size;
425		strcpy(propinfo.piclinfo.name, src_prop);
426
427		err = ptree_create_prop(&propinfo, 0, &src_prophdl);
428		if (err != 0) {
429			init_err(PTREE_CREATE_PROP_FAILED_MSG, funcname,
430				picl_strerror(err));
431			return;
432		}
433
434		err = ptree_add_prop(src_node, src_prophdl);
435		if (err != 0) {
436			init_err(PTREE_ADD_PROP_FAILED_MSG, funcname,
437				picl_strerror(err));
438			return;
439		}
440
441		prop_list[i].handle = src_prophdl;
442		strcpy(prop_list[i].name, dst_prop);
443	}
444}
445
446
447void
448psvc_psr_plugin_init(void)
449{
450	char *funcname = "psvc_psr_plugin_init";
451	int32_t i;
452	int err;
453	boolean_t present;
454
455	/*
456	 * So the volatile read/write routines can retrieve data from
457	 * psvc or picl
458	 */
459	err = psvc_init(&hdlp);
460	if (err != 0) {
461		init_err(PSVC_INIT_MSG, funcname, strerror(errno));
462	}
463
464	load_projected_properties(psvc_hdl.fp);
465
466	/*
467	 * Remove nodes whose devices aren't present from the picl tree.
468	 */
469	for (i = 0; i < psvc_hdl.obj_count; ++i) {
470		picl_psvc_t *objp;
471		uint64_t features;
472
473		objp = &psvc_hdl.objects[i];
474
475		err = psvc_get_attr(hdlp, objp->name, PSVC_PRESENCE_ATTR,
476			&present);
477		if (err != PSVC_SUCCESS)
478			continue;
479		err = psvc_get_attr(hdlp, objp->name, PSVC_FEATURES_ATTR,
480			&features);
481		if (err != PSVC_SUCCESS)
482			continue;
483		if ((features & (PSVC_DEV_HOTPLUG | PSVC_DEV_OPTION)) &&
484			(present == PSVC_ABSENT)) {
485			err = ptree_delete_node(objp->node);
486			if (err != 0) {
487				init_err(PTREE_DELETE_NODE_MSG, funcname,
488					picl_strerror(err));
489				return;
490			}
491		}
492	}
493
494	free(psvc_hdl.objects);
495
496}
497
498void
499psvc_psr_plugin_fini(void)
500{
501	psvc_fini(hdlp);
502}
503
504void
505psvc_psr_plugin_register(void)
506{
507	picld_plugin_register(&psvc_psr_reg);
508}
509