xref: /openbsd-src/sys/scsi/safte.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: safte.c,v 1.48 2011/06/15 01:10:05 dlg Exp $ */
2 
3 /*
4  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "bio.h"
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/device.h>
24 #include <sys/scsiio.h>
25 #include <sys/malloc.h>
26 #include <sys/pool.h>
27 #include <sys/proc.h>
28 #include <sys/rwlock.h>
29 #include <sys/queue.h>
30 #include <sys/sensors.h>
31 
32 #if NBIO > 0
33 #include <dev/biovar.h>
34 #endif
35 
36 #include <scsi/scsi_all.h>
37 #include <scsi/scsiconf.h>
38 
39 #include <scsi/safte.h>
40 
41 #ifdef SAFTE_DEBUG
42 #define DPRINTF(x)	do { if (safte_debug) printf x ; } while (0)
43 int	safte_debug = 1;
44 #else
45 #define DPRINTF(x)	/* x */
46 #endif
47 
48 
49 int	safte_match(struct device *, void *, void *);
50 void	safte_attach(struct device *, struct device *, void *);
51 int	safte_detach(struct device *, int);
52 
53 struct safte_sensor {
54 	struct ksensor		se_sensor;
55 	enum {
56 		SAFTE_T_FAN,
57 		SAFTE_T_PWRSUP,
58 		SAFTE_T_DOORLOCK,
59 		SAFTE_T_ALARM,
60 		SAFTE_T_TEMP
61 	}			se_type;
62 	u_int8_t		*se_field;
63 };
64 
65 struct safte_softc {
66 	struct device		sc_dev;
67 	struct scsi_link	 *sc_link;
68 	struct rwlock		sc_lock;
69 
70 	u_int			sc_encbuflen;
71 	u_char			*sc_encbuf;
72 
73 	int			sc_nsensors;
74 	struct safte_sensor	*sc_sensors;
75 	struct ksensordev	sc_sensordev;
76 	struct sensor_task	*sc_sensortask;
77 
78 	int			sc_celsius;
79 	int			sc_ntemps;
80 	struct safte_sensor	*sc_temps;
81 	u_int8_t		*sc_temperrs;
82 
83 #if NBIO > 0
84 	int			sc_nslots;
85 	u_int8_t		*sc_slots;
86 #endif
87 };
88 
89 struct cfattach safte_ca = {
90 	sizeof(struct safte_softc), safte_match, safte_attach, safte_detach
91 };
92 
93 struct cfdriver safte_cd = {
94 	NULL, "safte", DV_DULL
95 };
96 
97 #define DEVNAME(s)	((s)->sc_dev.dv_xname)
98 
99 int	safte_read_config(struct safte_softc *);
100 void	safte_read_encstat(void *);
101 
102 #if NBIO > 0
103 int	safte_ioctl(struct device *, u_long, caddr_t);
104 int	safte_bio_blink(struct safte_softc *, struct bioc_blink *);
105 #endif
106 
107 int64_t	safte_temp2uK(u_int8_t, int);
108 
109 int
110 safte_match(struct device *parent, void *match, void *aux)
111 {
112 	struct scsi_inquiry_data *inqbuf;
113 	struct scsi_attach_args	*sa = aux;
114 	struct scsi_inquiry_data *inq = sa->sa_inqbuf;
115 	struct scsi_xfer *xs;
116 	struct safte_inq *si;
117 	int error, flags = 0, length;
118 
119 	if (inq == NULL)
120 		return (0);
121 
122 	/* match on dell enclosures */
123 	if ((inq->device & SID_TYPE) == T_PROCESSOR &&
124 	    SCSISPC(inq->version) == 3)
125 		return (2);
126 
127  	if ((inq->device & SID_TYPE) != T_PROCESSOR ||
128 	    SCSISPC(inq->version) != 2 ||
129 	    (inq->response_format & SID_ANSII) != 2)
130 		return (0);
131 
132 	length = inq->additional_length + SAFTE_EXTRA_OFFSET;
133 	if (length < SAFTE_INQ_LEN)
134 		return (0);
135 	if (length > sizeof(*inqbuf))
136 		length = sizeof(*inqbuf);
137 
138 	inqbuf = dma_alloc(sizeof(*inqbuf), PR_NOWAIT | PR_ZERO);
139 	if (inqbuf == NULL)
140 		return (0);
141 
142 	memset(inqbuf->extra, ' ', sizeof(inqbuf->extra));
143 
144 	if (cold)
145 		flags |= SCSI_AUTOCONF;
146 	xs = scsi_xs_get(sa->sa_sc_link, flags | SCSI_DATA_IN);
147 	if (xs == NULL)
148 		goto fail;
149 
150 	xs->retries = 2;
151 	xs->timeout = 10000;
152 
153 	scsi_init_inquiry(xs, 0, 0, inqbuf, length);
154 
155 	error = scsi_xs_sync(xs);
156 	scsi_xs_put(xs);
157 
158 	if (error)
159 		goto fail;
160 
161 	si = (struct safte_inq *)&inqbuf->extra;
162 	if (memcmp(si->ident, SAFTE_IDENT, sizeof(si->ident)) == 0) {
163 		dma_free(inqbuf, sizeof(*inqbuf));
164 		return (2);
165 	}
166 
167 fail:
168 	dma_free(inqbuf, sizeof(*inqbuf));
169 	return (0);
170 }
171 
172 void
173 safte_attach(struct device *parent, struct device *self, void *aux)
174 {
175 	struct safte_softc		*sc = (struct safte_softc *)self;
176 	struct scsi_attach_args		*sa = aux;
177 	int				i = 0;
178 
179 	sc->sc_link = sa->sa_sc_link;
180 	sa->sa_sc_link->device_softc = sc;
181 	rw_init(&sc->sc_lock, DEVNAME(sc));
182 
183 	printf("\n");
184 
185 	sc->sc_encbuf = NULL;
186 	sc->sc_nsensors = 0;
187 #if NBIO > 0
188 	sc->sc_nslots = 0;
189 #endif
190 
191 	if (safte_read_config(sc) != 0) {
192 		printf("%s: unable to read enclosure configuration\n",
193 		    DEVNAME(sc));
194 		return;
195 	}
196 
197 	if (sc->sc_nsensors > 0) {
198 		sc->sc_sensortask = sensor_task_register(sc,
199 		    safte_read_encstat, 10);
200 		if (sc->sc_sensortask == NULL) {
201 			printf("%s: unable to register update task\n",
202 			    DEVNAME(sc));
203 			sc->sc_nsensors = sc->sc_ntemps = 0;
204 			free(sc->sc_sensors, M_DEVBUF);
205 		} else {
206 			for (i = 0; i < sc->sc_nsensors; i++)
207 				sensor_attach(&sc->sc_sensordev,
208 				    &sc->sc_sensors[i].se_sensor);
209 			sensordev_install(&sc->sc_sensordev);
210 		}
211 	}
212 
213 #if NBIO > 0
214 	if (sc->sc_nslots > 0 &&
215 	    bio_register(self, safte_ioctl) != 0) {
216 		printf("%s: unable to register ioctl with bio\n", DEVNAME(sc));
217 		sc->sc_nslots = 0;
218 	} else
219 		i++;
220 #endif
221 
222 	if (i) /* if we're doing something, then preinit encbuf and sensors */
223 		safte_read_encstat(sc);
224 	else {
225 		dma_free(sc->sc_encbuf, sc->sc_encbuflen);
226 		sc->sc_encbuf = NULL;
227 	}
228 }
229 
230 int
231 safte_detach(struct device *self, int flags)
232 {
233 	struct safte_softc		*sc = (struct safte_softc *)self;
234 	int				i;
235 
236 	rw_enter_write(&sc->sc_lock);
237 
238 #if NBIO > 0
239 	if (sc->sc_nslots > 0)
240 		bio_unregister(self);
241 #endif
242 
243 	if (sc->sc_nsensors > 0) {
244 		sensordev_deinstall(&sc->sc_sensordev);
245 		sensor_task_unregister(sc->sc_sensortask);
246 
247 		for (i = 0; i < sc->sc_nsensors; i++)
248 			sensor_detach(&sc->sc_sensordev,
249 			    &sc->sc_sensors[i].se_sensor);
250 		free(sc->sc_sensors, M_DEVBUF);
251 	}
252 
253 	if (sc->sc_encbuf != NULL)
254 		dma_free(sc->sc_encbuf, sc->sc_encbuflen);
255 
256 	rw_exit_write(&sc->sc_lock);
257 
258 	return (0);
259 }
260 
261 int
262 safte_read_config(struct safte_softc *sc)
263 {
264 	struct safte_config *config = NULL;
265 	struct safte_readbuf_cmd *cmd;
266 	struct safte_sensor *s;
267 	struct scsi_xfer *xs;
268 	int error = 0, flags = 0, i, j;
269 
270 	config = dma_alloc(sizeof(*config), PR_NOWAIT);
271 	if (config == NULL)
272 		return (1);
273 
274 	if (cold)
275 		flags |= SCSI_AUTOCONF;
276 	xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
277 	if (xs == NULL) {
278 		error = 1;
279 		goto done;
280 	}
281 	xs->cmdlen = sizeof(*cmd);
282 	xs->data = (void *)config;
283 	xs->datalen = sizeof(*config);
284 	xs->retries = 2;
285 	xs->timeout = 30000;
286 
287 	cmd = (struct safte_readbuf_cmd *)xs->cmd;
288 	cmd->opcode = READ_BUFFER;
289 	cmd->flags |= SAFTE_RD_MODE;
290 	cmd->bufferid = SAFTE_RD_CONFIG;
291 	cmd->length = htobe16(sizeof(*config));
292 
293 	error = scsi_xs_sync(xs);
294 	scsi_xs_put(xs);
295 
296 	if (error != 0) {
297 		error = 1;
298 		goto done;
299 	}
300 
301 	DPRINTF(("%s: nfans: %d npwrsup: %d nslots: %d doorlock: %d ntemps: %d"
302 	    " alarm: %d celsius: %d ntherm: %d\n", DEVNAME(sc), config->nfans,
303 	    config->npwrsup, config->nslots, config->doorlock, config->ntemps,
304 	    config->alarm, SAFTE_CFG_CELSIUS(config->therm),
305 	    SAFTE_CFG_NTHERM(config->therm)));
306 
307 	sc->sc_encbuflen = config->nfans * sizeof(u_int8_t) + /* fan status */
308 	    config->npwrsup * sizeof(u_int8_t) + /* power supply status */
309 	    config->nslots * sizeof(u_int8_t) + /* device scsi id (lun) */
310 	    sizeof(u_int8_t) + /* door lock status */
311 	    sizeof(u_int8_t) + /* speaker status */
312 	    config->ntemps * sizeof(u_int8_t) + /* temp sensors */
313 	    sizeof(u_int16_t); /* temp out of range sensors */
314 
315 	sc->sc_encbuf = dma_alloc(sc->sc_encbuflen, PR_NOWAIT);
316 	if (sc->sc_encbuf == NULL) {
317 		error = 1;
318 		goto done;
319 	}
320 
321 	sc->sc_nsensors = config->nfans + config->npwrsup + config->ntemps +
322 		(config->doorlock ? 1 : 0) + (config->alarm ? 1 : 0);
323 
324 	sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct safte_sensor),
325 	    M_DEVBUF, M_NOWAIT | M_ZERO);
326 	if (sc->sc_sensors == NULL) {
327 		dma_free(sc->sc_encbuf, sc->sc_encbuflen);
328 		sc->sc_encbuf = NULL;
329 		sc->sc_nsensors = 0;
330 		error = 1;
331 		goto done;
332 	}
333 
334 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
335 	    sizeof(sc->sc_sensordev.xname));
336 
337 	s = sc->sc_sensors;
338 
339 	for (i = 0; i < config->nfans; i++) {
340 		s->se_type = SAFTE_T_FAN;
341 		s->se_field = (u_int8_t *)(sc->sc_encbuf + i);
342 		s->se_sensor.type = SENSOR_INDICATOR;
343 		snprintf(s->se_sensor.desc, sizeof(s->se_sensor.desc),
344 		    "Fan%d", i);
345 
346 		s++;
347 	}
348 	j = config->nfans;
349 
350 	for (i = 0; i < config->npwrsup; i++) {
351 		s->se_type = SAFTE_T_PWRSUP;
352 		s->se_field = (u_int8_t *)(sc->sc_encbuf + j + i);
353 		s->se_sensor.type = SENSOR_INDICATOR;
354 		snprintf(s->se_sensor.desc, sizeof(s->se_sensor.desc),
355 		    "PSU%d", i);
356 
357 		s++;
358 	}
359 	j += config->npwrsup;
360 
361 #if NBIO > 0
362 	sc->sc_nslots = config->nslots;
363 	sc->sc_slots = (u_int8_t *)(sc->sc_encbuf + j);
364 #endif
365 	j += config->nslots;
366 
367 	if (config->doorlock) {
368 		s->se_type = SAFTE_T_DOORLOCK;
369 		s->se_field = (u_int8_t *)(sc->sc_encbuf + j);
370 		s->se_sensor.type = SENSOR_INDICATOR;
371 		strlcpy(s->se_sensor.desc, "doorlock",
372 		    sizeof(s->se_sensor.desc));
373 
374 		s++;
375 	}
376 	j++;
377 
378 	if (config->alarm) {
379 		s->se_type = SAFTE_T_ALARM;
380 		s->se_field = (u_int8_t *)(sc->sc_encbuf + j);
381 		s->se_sensor.type = SENSOR_INDICATOR;
382 		strlcpy(s->se_sensor.desc, "alarm", sizeof(s->se_sensor.desc));
383 
384 		s++;
385 	}
386 	j++;
387 
388 	/*
389 	 * stash the temp info so we can get out of range status. limit the
390 	 * number so the out of temp checks cant go into memory it doesnt own
391 	 */
392 	sc->sc_ntemps = (config->ntemps > 15) ? 15 : config->ntemps;
393 	sc->sc_temps = s;
394 	sc->sc_celsius = SAFTE_CFG_CELSIUS(config->therm);
395 	for (i = 0; i < config->ntemps; i++) {
396 		s->se_type = SAFTE_T_TEMP;
397 		s->se_field = (u_int8_t *)(sc->sc_encbuf + j + i);
398 		s->se_sensor.type = SENSOR_TEMP;
399 
400 		s++;
401 	}
402 	j += config->ntemps;
403 
404 	sc->sc_temperrs = (u_int8_t *)(sc->sc_encbuf + j);
405 done:
406 	dma_free(config, sizeof(*config));
407 	return (error);
408 }
409 
410 void
411 safte_read_encstat(void *arg)
412 {
413 	struct safte_readbuf_cmd *cmd;
414 	struct safte_sensor *s;
415 	struct safte_softc *sc = (struct safte_softc *)arg;
416 	struct scsi_xfer *xs;
417 	int error, i, flags = 0;
418 	u_int16_t oot;
419 
420 	rw_enter_write(&sc->sc_lock);
421 
422 	if (cold)
423 		flags |= SCSI_AUTOCONF;
424 	xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
425 	if (xs == NULL) {
426 		rw_exit_write(&sc->sc_lock);
427 		return;
428 	}
429 	xs->cmdlen = sizeof(*cmd);
430 	xs->data = sc->sc_encbuf;
431 	xs->datalen = sc->sc_encbuflen;
432 	xs->retries = 2;
433 	xs->timeout = 30000;
434 
435 	cmd = (struct safte_readbuf_cmd *)xs->cmd;
436 	cmd->opcode = READ_BUFFER;
437 	cmd->flags |= SAFTE_RD_MODE;
438 	cmd->bufferid = SAFTE_RD_ENCSTAT;
439 	cmd->length = htobe16(sc->sc_encbuflen);
440 
441 	error = scsi_xs_sync(xs);
442 	scsi_xs_put(xs);
443 
444 	if (error != 0) {
445 		rw_exit_write(&sc->sc_lock);
446 		return;
447 	}
448 
449 	for (i = 0; i < sc->sc_nsensors; i++) {
450 		s = &sc->sc_sensors[i];
451 		s->se_sensor.flags &= ~SENSOR_FUNKNOWN;
452 
453 		DPRINTF(("%s: %d type: %d field: 0x%02x\n", DEVNAME(sc), i,
454 		    s->se_type, *s->se_field));
455 
456 		switch (s->se_type) {
457 		case SAFTE_T_FAN:
458 			switch (*s->se_field) {
459 			case SAFTE_FAN_OP:
460 				s->se_sensor.value = 1;
461 				s->se_sensor.status = SENSOR_S_OK;
462 				break;
463 			case SAFTE_FAN_MF:
464 				s->se_sensor.value = 0;
465 				s->se_sensor.status = SENSOR_S_CRIT;
466 				break;
467 			case SAFTE_FAN_NOTINST:
468 			case SAFTE_FAN_UNKNOWN:
469 			default:
470 				s->se_sensor.value = 0;
471 				s->se_sensor.status = SENSOR_S_UNKNOWN;
472 				s->se_sensor.flags |= SENSOR_FUNKNOWN;
473 				break;
474 			}
475 			break;
476 
477 		case SAFTE_T_PWRSUP:
478 			switch (*s->se_field) {
479 			case SAFTE_PWR_OP_ON:
480 				s->se_sensor.value = 1;
481 				s->se_sensor.status = SENSOR_S_OK;
482 				break;
483 			case SAFTE_PWR_OP_OFF:
484 				s->se_sensor.value = 0;
485 				s->se_sensor.status = SENSOR_S_OK;
486 				break;
487 			case SAFTE_PWR_MF_ON:
488 				s->se_sensor.value = 1;
489 				s->se_sensor.status = SENSOR_S_CRIT;
490 				break;
491 			case SAFTE_PWR_MF_OFF:
492 				s->se_sensor.value = 0;
493 				s->se_sensor.status = SENSOR_S_CRIT;
494 				break;
495 			case SAFTE_PWR_NOTINST:
496 			case SAFTE_PWR_PRESENT:
497 			case SAFTE_PWR_UNKNOWN:
498 				s->se_sensor.value = 0;
499 				s->se_sensor.status = SENSOR_S_UNKNOWN;
500 				s->se_sensor.flags |= SENSOR_FUNKNOWN;
501 				break;
502 			}
503 			break;
504 
505 		case SAFTE_T_DOORLOCK:
506 			switch (*s->se_field) {
507 			case SAFTE_DOOR_LOCKED:
508 				s->se_sensor.value = 1;
509 				s->se_sensor.status = SENSOR_S_OK;
510 				break;
511 			case SAFTE_DOOR_UNLOCKED:
512 				s->se_sensor.value = 0;
513 				s->se_sensor.status = SENSOR_S_CRIT;
514 				break;
515 			case SAFTE_DOOR_UNKNOWN:
516 				s->se_sensor.value = 0;
517 				s->se_sensor.status = SENSOR_S_CRIT;
518 				s->se_sensor.flags |= SENSOR_FUNKNOWN;
519 				break;
520 			}
521 			break;
522 
523 		case SAFTE_T_ALARM:
524 			switch (*s->se_field) {
525 			case SAFTE_SPKR_OFF:
526 				s->se_sensor.value = 0;
527 				s->se_sensor.status = SENSOR_S_OK;
528 				break;
529 			case SAFTE_SPKR_ON:
530 				s->se_sensor.value = 1;
531 				s->se_sensor.status = SENSOR_S_CRIT;
532 				break;
533 			}
534 			break;
535 
536 		case SAFTE_T_TEMP:
537 			s->se_sensor.value = safte_temp2uK(*s->se_field,
538 			    sc->sc_celsius);
539 			break;
540 		}
541 	}
542 
543 	oot = _2btol(sc->sc_temperrs);
544 	for (i = 0; i < sc->sc_ntemps; i++)
545 		sc->sc_temps[i].se_sensor.status =
546 		    (oot & (1 << i)) ? SENSOR_S_CRIT : SENSOR_S_OK;
547 
548 	rw_exit_write(&sc->sc_lock);
549 }
550 
551 #if NBIO > 0
552 int
553 safte_ioctl(struct device *dev, u_long cmd, caddr_t addr)
554 {
555 	struct safte_softc		*sc = (struct safte_softc *)dev;
556 	int				error = 0;
557 
558 	switch (cmd) {
559 	case BIOCBLINK:
560 		error = safte_bio_blink(sc, (struct bioc_blink *)addr);
561 		break;
562 
563 	default:
564 		error = EINVAL;
565 		break;
566 	}
567 
568 	return (error);
569 }
570 
571 int
572 safte_bio_blink(struct safte_softc *sc, struct bioc_blink *blink)
573 {
574 	struct safte_writebuf_cmd *cmd;
575 	struct safte_slotop *op;
576 	struct scsi_xfer *xs;
577 	int error, slot, flags = 0, wantblink;
578 
579 	switch (blink->bb_status) {
580 	case BIOC_SBBLINK:
581 		wantblink = 1;
582 		break;
583 	case BIOC_SBUNBLINK:
584 		wantblink = 0;
585 		break;
586 	default:
587 		return (EINVAL);
588 	}
589 
590 	rw_enter_read(&sc->sc_lock);
591 	for (slot = 0; slot < sc->sc_nslots; slot++) {
592 		if (sc->sc_slots[slot] == blink->bb_target)
593 			break;
594 	}
595 	rw_exit_read(&sc->sc_lock);
596 
597 	if (slot >= sc->sc_nslots)
598 		return (ENODEV);
599 
600 	op = dma_alloc(sizeof(*op), PR_WAITOK | PR_ZERO);
601 
602 	op->opcode = SAFTE_WRITE_SLOTOP;
603 	op->slot = slot;
604 	op->flags |= wantblink ? SAFTE_SLOTOP_IDENTIFY : 0;
605 
606 	if (cold)
607 		flags |= SCSI_AUTOCONF;
608 	xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_OUT | SCSI_SILENT);
609 	if (xs == NULL) {
610 		dma_free(op, sizeof(*op));
611 		return (ENOMEM);
612 	}
613 	xs->cmdlen = sizeof(*cmd);
614 	xs->data = (void *)op;
615 	xs->datalen = sizeof(*op);
616 	xs->retries = 2;
617 	xs->timeout = 30000;
618 
619 	cmd = (struct safte_writebuf_cmd *)xs->cmd;
620 	cmd->opcode = WRITE_BUFFER;
621 	cmd->flags |= SAFTE_WR_MODE;
622 	cmd->length = htobe16(sizeof(struct safte_slotop));
623 
624 	error = scsi_xs_sync(xs);
625 	scsi_xs_put(xs);
626 
627 	if (error != 0) {
628 		error = EIO;
629 	}
630 	dma_free(op, sizeof(*op));
631 
632 	return (error);
633 }
634 #endif /* NBIO > 0 */
635 
636 int64_t
637 safte_temp2uK(u_int8_t measured, int celsius)
638 {
639 	int64_t				temp;
640 
641 	temp = (int64_t)measured;
642 	temp += SAFTE_TEMP_OFFSET;
643 	temp *= 1000000; /* convert to micro (mu) degrees */
644 	if (!celsius)
645 		temp = ((temp - 32000000) * 5) / 9; /* convert to Celsius */
646 
647 	temp += 273150000; /* convert to kelvin */
648 
649 	return (temp);
650 }
651