xref: /illumos-gate/usr/src/cmd/sf880drd/sf880drd.c (revision 012e6ce759c490003aed29439cc47d3d73a99ad3)
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, 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #include <sys/types.h>
27 #include <sys/sunddi.h>
28 #include <sys/stat.h>
29 #include <sys/i2c/clients/i2c_client.h>
30 #include <sys/hpc3130_events.h>
31 #include <values.h>		/* for BITSPERBYTE */
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <fcntl.h>
36 #include <strings.h>
37 #include <poll.h>
38 #include <libintl.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <config_admin.h>
42 #include <sys/daktari.h>
43 #include "sf880drd.h"
44 
45 /*
46  * Hotplug Controller addresses.
47  */
48 static const unsigned char controller_names[NUM_CONTROLLERS] =
49 	{ 0xe2, 0xe6, 0xe8, 0xec };
50 
51 #define	INDEX2SLOT(INDEX)	((INDEX)%4) /* cf init_poll_events() */
52 #define	INDEX2CONTROLLER(INDEX)	((INDEX)/4) /* cf init_poll_events() */
53 
54 /*
55  *  Local variables.
56  */
57 static struct pollfd fds[NUM_FDS];
58 static unsigned int fault_leds[2];
59 static unsigned int ok2rem_leds[2];
60 
61 /*
62  *  Local prototypes.
63  */
64 static void init_poll_events(void);
65 static void process_event(int);
66 static void report_cfgadm_error(int, char *);
67 static void set_front_panel_led(uint8_t, boolean_t);
68 
69 static int i2c_set_bit(int, int, uint8_t);
70 static void report_syscall_error(char *);
71 
72 static void pushbutton_event(char *);
73 static void fault_led_event(hpc3130_event_type_t, int);
74 static void removable_led_event(hpc3130_event_type_t, int);
75 
76 /*
77  * main(): loops forever looking for events.
78  */
79 int
80 main()
81 {
82 	int	i;
83 	int	rv;
84 
85 	init_poll_events();
86 	for (;;) {
87 		/* sleep in poll() waiting an event */
88 		rv = poll(fds, NUM_FDS, -1);
89 		if (rv < 0) {
90 			report_syscall_error("poll");
91 			continue;
92 		}
93 
94 		/* woken up from poll() process the event */
95 		for (i = 0; i < NUM_FDS; ++i) {
96 			if (fds[i].revents == POLLIN)
97 				process_event(i);
98 		}
99 	}
100 	/* NOTREACHED */
101 	return (0);
102 }
103 
104 /*
105  * Set up poll fds.
106  */
107 static void
108 init_poll_events()
109 {
110 	int c;
111 	int p;
112 	int i = 0;
113 	char buf[sizeof (HPC3130_DEV_FMT)+8];
114 
115 	for (c = 0; c < NUM_CONTROLLERS; ++c) {
116 		for (p = 0; p < SLOTS_PER_CONTROLLER; ++p) {
117 			(void) sprintf(buf, HPC3130_DEV_FMT,
118 			    controller_names[c], p);
119 			fds[i].events = POLLIN;
120 			fds[i].fd = open(buf, O_RDWR);
121 			if (fds[i].fd == -1) {
122 				report_syscall_error(buf);
123 				exit(-1);
124 			}
125 			i++;
126 		}
127 	}
128 }
129 
130 /*
131  * Process poll events.
132  */
133 static void
134 process_event(int fdi)
135 {
136 	struct hpc3130_event e;
137 	int rv;
138 	int slot = INDEX2SLOT(fdi);
139 	int cntr = INDEX2CONTROLLER(fdi);
140 
141 	rv = ioctl(fds[fdi].fd, HPC3130_GET_EVENT, &e);
142 	if (rv == -1) {
143 		report_syscall_error("HPC3130_GET_EVENT");
144 		return;
145 	}
146 
147 	switch (e.id) {
148 	case HPC3130_EVENT_INSERTION:
149 	case HPC3130_EVENT_REMOVAL:
150 	case HPC3130_EVENT_POWERON:
151 	case HPC3130_EVENT_POWEROFF:
152 		break;
153 	case HPC3130_EVENT_BUTTON:
154 		DPRINTF(("\nBUTTON EVENT slot (%s)\n", e.name));
155 		pushbutton_event(e.name);
156 		break;
157 	case HPC3130_LED_FAULT_ON:
158 		DPRINTF(("\nFAULT LED ON EVENT\n"));
159 		fault_led_event(e.id, fdi);
160 		break;
161 	case HPC3130_LED_FAULT_OFF:
162 		DPRINTF(("\nFAULT LED OFF EVENT\n"));
163 		fault_led_event(e.id, fdi);
164 		break;
165 	case HPC3130_LED_REMOVABLE_ON:
166 		DPRINTF(("\nREMOVABLE LED ON EVENT\n"));
167 		removable_led_event(e.id, fdi);
168 		break;
169 	case HPC3130_LED_REMOVABLE_OFF:
170 		DPRINTF(("\nREMOVABLE LED OFF EVENT\n"));
171 		removable_led_event(e.id, fdi);
172 		break;
173 	default:
174 		(void) fprintf(stderr, "WARNING: bogus event: %x\n", e.id);
175 	}
176 }
177 
178 /*
179  * Button Event handler.
180  */
181 static void
182 pushbutton_event(char *ap_id)
183 {
184 	char			*errstr = NULL;
185 	struct cfga_list_data	*stat = NULL;
186 	int			nlist;
187 	cfga_cmd_t		cmd;
188 	cfga_err_t		rv;
189 
190 	rv = config_list_ext(1, &ap_id, &stat, &nlist,
191 	    NULL, NULL, &errstr, 0);
192 	if (rv != CFGA_OK) {
193 		report_cfgadm_error(rv, errstr);
194 		goto out;
195 	}
196 	assert(nlist == 1);
197 
198 	/* The only types of hotplug with buttons */
199 	assert(!(strcmp(stat->ap_class, "pci")));
200 
201 	switch (stat->ap_o_state) {
202 	case CFGA_STAT_UNCONFIGURED:
203 		cmd = CFGA_CMD_CONFIGURE;
204 		break;
205 	case CFGA_STAT_CONFIGURED:
206 		cmd = CFGA_CMD_DISCONNECT;
207 		break;
208 	default:
209 		/* Should never get here */
210 		assert(0);
211 	}
212 
213 	/*
214 	 * confp & msgp are NULL: when using the pushbutton,
215 	 * simply fail if prompting is required.
216 	 */
217 	rv = config_change_state(cmd, 1, &ap_id, NULL, NULL, NULL, &errstr, 0);
218 	if (rv != CFGA_OK) {
219 		report_cfgadm_error(rv, errstr);
220 		/* FALLTHRU to "out" */
221 	}
222 
223 out:
224 	if (errstr)
225 		free(errstr);
226 	if (stat)
227 		free(stat);
228 }
229 
230 static void
231 fault_led_event(hpc3130_event_type_t event, int fdi)
232 {
233 	int		side = 0;
234 	unsigned int	old;
235 
236 	if (INDEX2CONTROLLER(fdi) != GPTWO_CONTROLLER) {
237 		/* It's a PCI slot; left side of chassis */
238 		side = 1;
239 	}
240 
241 	old = fault_leds[side];
242 
243 	assert(fdi <= sizeof (fault_leds[side]) * BITSPERBYTE);
244 
245 	switch (event) {
246 	case HPC3130_LED_FAULT_ON:
247 		fault_leds[side] |= (1<<fdi);
248 		DPRINTF(("fault_led_event: HPC3130_LED_FAULT_ON\n"));
249 		break;
250 	case HPC3130_LED_FAULT_OFF:
251 		fault_leds[side] &= ~(1<<fdi);
252 		DPRINTF(("fault_led_event: HPC3130_LED_FAULT_OFF\n"));
253 		break;
254 	}
255 
256 	DPRINTF(("fault_led_event: old(0x%x), fault_leds[%d](0x%x)\n",
257 	    old, side, fault_leds[side]));
258 
259 	if ((old == 0) != (fault_leds[side] == 0) && ok2rem_leds[side] == 0) {
260 		/*
261 		 * The first FAULT LED has come on, or the last one has gone
262 		 * off on this side, and all the OK2REMOVE LEDS are off on this
263 		 * side.  So we have to update the front panel ARROW LED.
264 		 */
265 		set_front_panel_led(side ? LEFT_DOOR_ATTEN_LED :
266 		    RIGHT_DOOR_ATTEN_LED,
267 		    fault_leds[side] ? LED_ON : LED_OFF);
268 	}
269 }
270 
271 static void
272 removable_led_event(hpc3130_event_type_t event, int fdi)
273 {
274 	int		side = 0;
275 	unsigned int	old;
276 
277 	if (INDEX2CONTROLLER(fdi) != GPTWO_CONTROLLER) {
278 		/* It's a PCI slot; left side of chassis */
279 		side = 1;
280 	}
281 
282 	old = ok2rem_leds[side];
283 
284 	assert(fdi <= sizeof (ok2rem_leds[side]) * BITSPERBYTE);
285 
286 	switch (event) {
287 	case HPC3130_LED_REMOVABLE_ON:
288 		ok2rem_leds[side] |= (1<<fdi);
289 		DPRINTF(("removable_led_event: HPC3130_LED_REMOVABLE_ON\n"));
290 		break;
291 	case HPC3130_LED_REMOVABLE_OFF:
292 		ok2rem_leds[side] &= ~(1<<fdi);
293 		DPRINTF(("removable_led_event: HPC3130_LED_REMOVABLE_OFF\n"));
294 		break;
295 	}
296 
297 	DPRINTF(("removable_led_event: old(0x%x), ok2rem_leds[%d](0x%x)\n",
298 	    old, side, ok2rem_leds[side]));
299 
300 	if ((old == 0) != (ok2rem_leds[side] == 0)) {
301 		/*
302 		 * The first OKAY2REMOVE LED has come on, or the last
303 		 * one has gone off (on this side).  We may have to update
304 		 * the front panel LEDs.
305 		 */
306 		if (ok2rem_leds[!side] == 0) {
307 			/*
308 			 * The OK2REMOVE LEDs are all off on the other
309 			 * side of the chassis, so this side determines
310 			 * whether the front OK2REMOVE is on or off.
311 			 */
312 			set_front_panel_led(SYS_OK2REMOVE_LED,
313 			    ok2rem_leds[side] ? LED_ON : LED_OFF);
314 		}
315 		if (fault_leds[side] == 0) {
316 			/*
317 			 * All the FAULT LEDs are off on this side.  So the
318 			 * OK2REMOVE LEDs determine whether the ARROW LED is on.
319 			 */
320 		set_front_panel_led(side ? LEFT_DOOR_ATTEN_LED :
321 		    RIGHT_DOOR_ATTEN_LED,
322 		    ok2rem_leds[side] ? LED_ON : LED_OFF);
323 		}
324 	}
325 }
326 
327 /*
328  * Set front panel system leds either on or off.
329  */
330 static void
331 set_front_panel_led(uint8_t bit_num, boolean_t on_off)
332 {
333 	int		fd;
334 	int		rv;
335 	i2c_bit_t	arg;
336 
337 	fd = open(SSC050_LED_PORT, O_RDWR);
338 	if (fd == -1) {
339 		report_syscall_error("ssc050");
340 		return;
341 	}
342 
343 	arg.bit_num = bit_num;
344 	arg.bit_value = on_off;
345 
346 	rv = ioctl(fd, I2C_SET_BIT, &arg);
347 	if (rv == -1)
348 		report_syscall_error("LED I2C_SET_BIT");
349 
350 	(void) close(fd);
351 }
352 
353 static int
354 i2c_set_bit(int fp, int bit, uint8_t value)
355 {
356 	int		rv;
357 	i2c_bit_t	passin;
358 
359 	passin.bit_num = (uchar_t)bit;
360 	passin.bit_value = value;
361 	rv = ioctl(fp, I2C_SET_BIT, &passin);
362 	return (rv);
363 }
364 
365 static void
366 report_cfgadm_error(int cfgerrnum, char *errstr)
367 {
368 	const char	*ep;
369 
370 	ep = config_strerror(cfgerrnum);
371 
372 	if (ep == NULL)
373 		ep = gettext("configuration administration unknown error");
374 
375 	if (errstr != NULL && *errstr != '\0') {
376 		(void) fprintf(stderr, "%s: %s\n", ep, errstr);
377 	} else {
378 		(void) fprintf(stderr, "%s\n", ep);
379 	}
380 }
381 
382 static void
383 report_syscall_error(char *msg)
384 {
385 	if (errno != EINTR)
386 		perror(msg);
387 }
388