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 2010 Emulex. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Source file containing the implementation of the driver entry points
29 * and related helper functions
30 */
31
32 #include <oce_impl.h>
33 #include <oce_stat.h>
34 #include <oce_ioctl.h>
35
36 #define ATTACH_DEV_INIT 0x1
37 #define ATTACH_FM_INIT 0x2
38 #define ATTACH_LOCK_INIT 0x4
39 #define ATTACH_PCI_INIT 0x8
40 #define ATTACH_HW_INIT 0x10
41 #define ATTACH_SETUP_TXRX 0x20
42 #define ATTACH_SETUP_ADAP 0x40
43 #define ATTACH_SETUP_INTR 0x80
44 #define ATTACH_STAT_INIT 0x100
45 #define ATTACH_MAC_REG 0x200
46
47 /* ---[ globals and externs ]-------------------------------------------- */
48 const char oce_ident_string[] = OCE_IDENT_STRING;
49 const char oce_mod_name[] = OCE_MOD_NAME;
50
51 /* driver properties */
52 static const char flow_control[] = "flow_control";
53 static const char mtu_prop_name[] = "oce_default_mtu";
54 static const char tx_ring_size_name[] = "tx_ring_size";
55 static const char tx_bcopy_limit_name[] = "tx_bcopy_limit";
56 static const char rx_bcopy_limit_name[] = "rx_bcopy_limit";
57 static const char rx_frag_size_name[] = "rx_frag_size";
58 static const char rx_max_bufs_name[] = "rx_max_bufs";
59 static const char fm_cap_name[] = "oce_fm_capability";
60 static const char log_level_name[] = "oce_log_level";
61 static const char lso_capable_name[] = "lso_capable";
62 static const char rx_pkt_per_intr_name[] = "rx_pkts_per_intr";
63 static const char tx_reclaim_threshold_name[] = "tx_reclaim_threshold";
64 static const char rx_rings_name[] = "max_rx_rings";
65 static const char tx_rings_name[] = "max_tx_rings";
66
67 /* --[ static function prototypes here ]------------------------------- */
68 static int oce_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd);
69 static int oce_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
70 static int oce_quiesce(dev_info_t *dip);
71 static int oce_suspend(dev_info_t *dip);
72 static int oce_resume(dev_info_t *dip);
73 static void oce_unconfigure(struct oce_dev *dev);
74 static void oce_init_locks(struct oce_dev *dev);
75 static void oce_destroy_locks(struct oce_dev *dev);
76 static void oce_get_params(struct oce_dev *dev);
77 static int oce_get_prop(struct oce_dev *dev, char *propname, int minval,
78 int maxval, int defval, uint32_t *values);
79
80 static struct cb_ops oce_cb_ops = {
81 nulldev, /* cb_open */
82 nulldev, /* cb_close */
83 nodev, /* cb_strategy */
84 nodev, /* cb_print */
85 nodev, /* cb_dump */
86 nodev, /* cb_read */
87 nodev, /* cb_write */
88 nodev, /* cb_ioctl */
89 nodev, /* cb_devmap */
90 nodev, /* cb_mmap */
91 nodev, /* cb_segmap */
92 nochpoll, /* cb_chpoll */
93 ddi_prop_op, /* cb_prop_op */
94 NULL, /* cb_stream */
95 D_MP, /* cb_flag */
96 CB_REV, /* cb_rev */
97 nodev, /* cb_aread */
98 nodev /* cb_awrite */
99 };
100
101 static struct dev_ops oce_dev_ops = {
102 DEVO_REV, /* devo_rev */
103 0, /* devo_refcnt */
104 NULL, /* devo_getinfo */
105 NULL, /* devo_identify */
106 nulldev, /* devo_probe */
107 oce_attach, /* devo_attach */
108 oce_detach, /* devo_detach */
109 nodev, /* devo_reset */
110 &oce_cb_ops, /* devo_cb_ops */
111 NULL, /* devo_bus_ops */
112 nodev, /* devo_power */
113 oce_quiesce /* devo_quiesce */
114 };
115
116 static struct modldrv oce_drv = {
117 &mod_driverops, /* Type of module. This one is a driver */
118 (char *)oce_ident_string, /* Description string */
119 &oce_dev_ops, /* driver ops */
120 };
121
122 static struct modlinkage oce_mod_linkage = {
123 MODREV_1, &oce_drv, NULL
124 };
125
126 #define OCE_M_CB_FLAGS (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | \
127 MC_PROPINFO)
128 static mac_callbacks_t oce_mac_cb = {
129 OCE_M_CB_FLAGS, /* mc_callbacks */
130 oce_m_stat, /* mc_getstat */
131 oce_m_start, /* mc_start */
132 oce_m_stop, /* mc_stop */
133 oce_m_promiscuous, /* mc_setpromisc */
134 oce_m_multicast, /* mc_multicast */
135 oce_m_unicast, /* mc_unicast */
136 oce_m_send, /* mc_tx */
137 NULL, /* mc_reserve */
138 oce_m_ioctl, /* mc_ioctl */
139 oce_m_getcap, /* mc_getcapab */
140 NULL, /* open */
141 NULL, /* close */
142 oce_m_setprop, /* set properties */
143 oce_m_getprop, /* get properties */
144 oce_m_propinfo /* properties info */
145 };
146
147 extern char *oce_priv_props[];
148
149 /* Module Init */
150 int
_info(struct modinfo * modinfop)151 _info(struct modinfo *modinfop)
152 {
153 return (mod_info(&oce_mod_linkage, modinfop));
154 } /* _info */
155
156 int
_init(void)157 _init(void)
158 {
159 int ret = 0;
160
161 /* install the module */
162 mac_init_ops(&oce_dev_ops, "oce");
163
164 ret = mod_install(&oce_mod_linkage);
165 if (ret) {
166 cmn_err(CE_WARN, "mod_install failed rval=%x", ret);
167 }
168
169 return (ret);
170 } /* _init */
171
172
173 int
_fini(void)174 _fini(void)
175 {
176 int ret = 0;
177 /* remove the module */
178 ret = mod_remove(&oce_mod_linkage);
179 if (ret != 0) {
180 return (ret);
181 }
182
183 mac_fini_ops(&oce_dev_ops);
184
185 return (ret);
186 } /* _fini */
187
188
189 static int
oce_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)190 oce_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
191 {
192 int ret = 0;
193 struct oce_dev *dev = NULL;
194 mac_register_t *mac;
195
196 switch (cmd) {
197 case DDI_RESUME:
198 return (oce_resume(dip));
199 default:
200 return (DDI_FAILURE);
201
202 case DDI_ATTACH:
203 break;
204 }
205
206 /* allocate dev */
207 dev = kmem_zalloc(sizeof (struct oce_dev), KM_SLEEP);
208
209 /* populate the dev structure */
210 dev->dip = dip;
211 dev->dev_id = ddi_get_instance(dip);
212 dev->suspended = B_FALSE;
213
214 /* get the parameters */
215 oce_get_params(dev);
216
217 /*
218 * set the ddi driver private data pointer. This is
219 * sent to all mac callback entry points
220 */
221 ddi_set_driver_private(dip, dev);
222
223 dev->attach_state |= ATTACH_DEV_INIT;
224
225 oce_fm_init(dev);
226 dev->attach_state |= ATTACH_FM_INIT;
227
228 /* setup PCI bars */
229 ret = oce_pci_init(dev);
230 if (ret != DDI_SUCCESS) {
231 oce_log(dev, CE_WARN, MOD_CONFIG,
232 "PCI initialization failed with %d", ret);
233 goto attach_fail;
234 }
235 dev->attach_state |= ATTACH_PCI_INIT;
236
237 ret = oce_setup_intr(dev);
238 if (ret != DDI_SUCCESS) {
239 oce_log(dev, CE_WARN, MOD_CONFIG,
240 "Interrupt setup failed with %d", ret);
241 goto attach_fail;
242
243 }
244 dev->attach_state |= ATTACH_SETUP_INTR;
245
246 /* initialize locks */
247 oce_init_locks(dev);
248 dev->attach_state |= ATTACH_LOCK_INIT;
249
250
251 /* HW init */
252 ret = oce_hw_init(dev);
253 if (ret != DDI_SUCCESS) {
254 oce_log(dev, CE_WARN, MOD_CONFIG,
255 "HW initialization failed with %d", ret);
256 goto attach_fail;
257 }
258 dev->attach_state |= ATTACH_HW_INIT;
259
260 ret = oce_init_txrx(dev);
261 if (ret != DDI_SUCCESS) {
262 oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
263 "Failed to init rings");
264 goto attach_fail;
265 }
266 dev->attach_state |= ATTACH_SETUP_TXRX;
267
268 ret = oce_setup_adapter(dev);
269 if (ret != DDI_SUCCESS) {
270 oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
271 "Failed to setup adapter");
272 goto attach_fail;
273 }
274 dev->attach_state |= ATTACH_SETUP_ADAP;
275
276
277 ret = oce_stat_init(dev);
278 if (ret != DDI_SUCCESS) {
279 oce_log(dev, CE_WARN, MOD_CONFIG,
280 "kstat setup Failed with %d", ret);
281 goto attach_fail;
282 }
283 dev->attach_state |= ATTACH_STAT_INIT;
284
285 /* mac_register_t */
286 oce_log(dev, CE_NOTE, MOD_CONFIG,
287 "MAC_VERSION = 0x%x", MAC_VERSION);
288 mac = mac_alloc(MAC_VERSION);
289 if (mac == NULL) {
290 oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
291 "MAC allocation Failed");
292 goto attach_fail;
293 }
294 /*
295 * fill the mac structure before calling mac_register
296 */
297 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
298 mac->m_driver = dev;
299 mac->m_dip = dip;
300 mac->m_src_addr = dev->mac_addr;
301 mac->m_callbacks = &oce_mac_cb;
302 mac->m_min_sdu = 0;
303 mac->m_max_sdu = dev->mtu;
304 mac->m_margin = VLAN_TAGSZ;
305 mac->m_priv_props = oce_priv_props;
306
307 oce_log(dev, CE_NOTE, MOD_CONFIG,
308 "Driver Private structure = 0x%p", (void *)dev);
309
310 /* now register with GLDv3 */
311 ret = mac_register(mac, (mac_handle_t *)&dev->mac_handle);
312 /* regardless of the status, free mac_register */
313 mac_free(mac);
314 mac = NULL;
315 if (ret != DDI_SUCCESS) {
316 oce_log(dev, CE_WARN, MOD_CONFIG,
317 "MAC registration failed :0x%x", ret);
318 goto attach_fail;
319
320 }
321
322 /* correct link status only after start */
323 dev->link_status = LINK_STATE_UNKNOWN;
324 mac_link_update(dev->mac_handle, dev->link_status);
325
326 dev->attach_state |= ATTACH_MAC_REG;
327 dev->state |= STATE_INIT;
328
329 oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
330 "ATTACH SUCCESS");
331
332 return (DDI_SUCCESS);
333
334 attach_fail:
335 oce_unconfigure(dev);
336 return (DDI_FAILURE);
337 } /* oce_attach */
338
339 static int
oce_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)340 oce_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
341 {
342 struct oce_dev *dev;
343 int pcnt = 0;
344 int qid;
345
346 dev = ddi_get_driver_private(dip);
347 if (dev == NULL) {
348 return (DDI_FAILURE);
349 }
350 oce_log(dev, CE_NOTE, MOD_CONFIG,
351 "Detaching driver: cmd = 0x%x", cmd);
352
353 switch (cmd) {
354 default:
355 return (DDI_FAILURE);
356 case DDI_SUSPEND:
357 return (oce_suspend(dip));
358 case DDI_DETACH:
359 break;
360 } /* switch cmd */
361
362 /* Fail detach if MAC unregister is unsuccessfule */
363 if (mac_unregister(dev->mac_handle) != 0) {
364 oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
365 "Failed to unregister MAC ");
366 }
367 dev->attach_state &= ~ATTACH_MAC_REG;
368
369 /* check if the detach is called with out stopping */
370 DEV_LOCK(dev);
371 if (dev->state & STATE_MAC_STARTED) {
372 dev->state &= ~STATE_MAC_STARTED;
373 oce_stop(dev);
374 DEV_UNLOCK(dev);
375 } else
376 DEV_UNLOCK(dev);
377
378 /*
379 * Wait for Packets sent up to be freed
380 */
381 for (qid = 0; qid < dev->rx_rings; qid++) {
382 pcnt = oce_rx_pending(dev, dev->rq[qid], DEFAULT_DRAIN_TIME);
383 if (pcnt != 0) {
384 oce_log(dev, CE_WARN, MOD_CONFIG,
385 "%d Pending Buffers Detach failed", pcnt);
386 return (DDI_FAILURE);
387 }
388 }
389 oce_unconfigure(dev);
390
391 return (DDI_SUCCESS);
392 } /* oce_detach */
393
394 static int
oce_quiesce(dev_info_t * dip)395 oce_quiesce(dev_info_t *dip)
396 {
397 int ret = DDI_SUCCESS;
398 struct oce_dev *dev = ddi_get_driver_private(dip);
399
400 if (dev == NULL) {
401 return (DDI_FAILURE);
402 }
403 if (dev->suspended) {
404 return (DDI_SUCCESS);
405 }
406
407 oce_chip_di(dev);
408
409 ret = oce_reset_fun(dev);
410
411 return (ret);
412 }
413
414 static int
oce_suspend(dev_info_t * dip)415 oce_suspend(dev_info_t *dip)
416 {
417 struct oce_dev *dev = ddi_get_driver_private(dip);
418
419 mutex_enter(&dev->dev_lock);
420 /* Suspend the card */
421 dev->suspended = B_TRUE;
422 /* stop the adapter */
423 if (dev->state & STATE_MAC_STARTED) {
424 oce_stop(dev);
425 oce_unsetup_adapter(dev);
426 }
427 dev->state &= ~STATE_MAC_STARTED;
428 mutex_exit(&dev->dev_lock);
429 return (DDI_SUCCESS);
430 } /* oce_suspend */
431
432 static int
oce_resume(dev_info_t * dip)433 oce_resume(dev_info_t *dip)
434 {
435 struct oce_dev *dev;
436 int ret;
437
438 /* get the dev pointer from dip */
439 dev = ddi_get_driver_private(dip);
440 mutex_enter(&dev->dev_lock);
441 if (!dev->suspended) {
442 mutex_exit(&dev->dev_lock);
443 return (DDI_SUCCESS);
444 }
445 if (!(dev->state & STATE_MAC_STARTED)) {
446 ret = oce_setup_adapter(dev);
447 if (ret != DDI_SUCCESS) {
448 mutex_exit(&dev->dev_lock);
449 return (DDI_FAILURE);
450 }
451 ret = oce_start(dev);
452 if (ret != DDI_SUCCESS) {
453 mutex_exit(&dev->dev_lock);
454 return (DDI_FAILURE);
455 }
456 }
457 dev->suspended = B_FALSE;
458 dev->state |= STATE_MAC_STARTED;
459 mutex_exit(&dev->dev_lock);
460 return (ret);
461 } /* oce_resume */
462
463 static void
oce_init_locks(struct oce_dev * dev)464 oce_init_locks(struct oce_dev *dev)
465 {
466 /* initialize locks */
467 mutex_init(&dev->dev_lock, NULL, MUTEX_DRIVER,
468 DDI_INTR_PRI(dev->intr_pri));
469 mutex_init(&dev->bmbx_lock, NULL, MUTEX_DRIVER,
470 DDI_INTR_PRI(dev->intr_pri));
471 } /* oce_init_locks */
472
473 static void
oce_destroy_locks(struct oce_dev * dev)474 oce_destroy_locks(struct oce_dev *dev)
475 {
476 mutex_destroy(&dev->dev_lock);
477 mutex_destroy(&dev->bmbx_lock);
478 } /* oce_destroy_locks */
479
480 static void
oce_unconfigure(struct oce_dev * dev)481 oce_unconfigure(struct oce_dev *dev)
482 {
483 uint32_t state = dev->attach_state;
484
485 if (state & ATTACH_MAC_REG) {
486 (void) mac_unregister(dev->mac_handle);
487 }
488 if (state & ATTACH_STAT_INIT) {
489 oce_stat_fini(dev);
490 }
491 if (state & ATTACH_SETUP_ADAP) {
492 oce_unsetup_adapter(dev);
493 }
494
495 if (state & ATTACH_SETUP_TXRX) {
496 oce_fini_txrx(dev);
497 }
498
499 if (state & ATTACH_HW_INIT) {
500 oce_hw_fini(dev);
501 }
502 if (state & ATTACH_LOCK_INIT) {
503 oce_destroy_locks(dev);
504 }
505
506 if (state & ATTACH_SETUP_INTR) {
507 (void) oce_teardown_intr(dev);
508 }
509 if (state & ATTACH_PCI_INIT) {
510 oce_pci_fini(dev);
511 }
512 if (state & ATTACH_FM_INIT) {
513 oce_fm_fini(dev);
514 }
515 if (state & ATTACH_DEV_INIT) {
516 ddi_set_driver_private(dev->dip, NULL);
517 kmem_free(dev, sizeof (struct oce_dev));
518 }
519 } /* oce_unconfigure */
520
521 static void
oce_get_params(struct oce_dev * dev)522 oce_get_params(struct oce_dev *dev)
523 {
524 uint32_t log_level;
525 uint16_t mod_mask;
526 uint16_t severity;
527 /*
528 * Allowed values for the driver parameters. If all values in a range
529 * is allowed, the the array has only one value.
530 */
531 uint32_t fc_values[] = {OCE_FC_NONE, OCE_FC_TX, OCE_FC_RX,
532 OCE_DEFAULT_FLOW_CONTROL, END};
533 uint32_t mtu_values[] = {OCE_MIN_MTU, OCE_MAX_MTU, END};
534 uint32_t tx_rs_values[] = {SIZE_256, SIZE_512, SIZE_1K, SIZE_2K, END};
535 uint32_t tx_bcl_values[] = {SIZE_128, SIZE_256, SIZE_512, SIZE_1K,
536 SIZE_2K, END};
537 uint32_t rx_bcl_values[] = {SIZE_128, SIZE_256, SIZE_512, SIZE_1K,
538 SIZE_2K, END};
539 uint32_t rq_fs_values[] = {SIZE_2K, SIZE_4K, SIZE_8K, END};
540 uint32_t rq_mb_values[] = {SIZE_2K, SIZE_4K, SIZE_8K, END};
541 uint32_t lso_capable_values[] = {0, 1, END};
542 uint32_t fm_caps_values[] = {DDI_FM_NOT_CAPABLE, OCE_FM_CAPABILITY,
543 END};
544 uint32_t tx_rt_values[] = {END};
545 uint32_t rx_ppi_values[] = {END};
546 uint32_t rx_rings_values[] = {END};
547 uint32_t tx_rings_values[] = {END};
548 uint32_t log_level_values[] = {END};
549
550 /* non tunables */
551 dev->rx_ring_size = OCE_DEFAULT_RX_RING_SIZE;
552
553 /* configurable parameters */
554 dev->flow_control = oce_get_prop(dev, (char *)flow_control, OCE_FC_NONE,
555 OCE_DEFAULT_FLOW_CONTROL, OCE_DEFAULT_FLOW_CONTROL, fc_values);
556
557 dev->mtu = oce_get_prop(dev, (char *)mtu_prop_name, OCE_MIN_MTU,
558 OCE_MAX_MTU, OCE_MIN_MTU, mtu_values);
559
560 dev->tx_ring_size = oce_get_prop(dev, (char *)tx_ring_size_name,
561 SIZE_256, SIZE_2K, OCE_DEFAULT_TX_RING_SIZE, tx_rs_values);
562
563 dev->tx_bcopy_limit = oce_get_prop(dev, (char *)tx_bcopy_limit_name,
564 SIZE_128, SIZE_2K, OCE_DEFAULT_TX_BCOPY_LIMIT, tx_bcl_values);
565
566 dev->rx_bcopy_limit = oce_get_prop(dev, (char *)rx_bcopy_limit_name,
567 SIZE_128, SIZE_2K, OCE_DEFAULT_RX_BCOPY_LIMIT, rx_bcl_values);
568
569 dev->rq_frag_size = oce_get_prop(dev, (char *)rx_frag_size_name,
570 SIZE_2K, SIZE_8K, OCE_RQ_BUF_SIZE, rq_fs_values);
571
572 dev->rq_max_bufs = oce_get_prop(dev, (char *)rx_max_bufs_name, SIZE_2K,
573 SIZE_8K, OCE_RQ_NUM_BUFFERS, rq_mb_values);
574
575 dev->lso_capable = oce_get_prop(dev, (char *)lso_capable_name, 0,
576 1, 1, lso_capable_values);
577
578 dev->fm_caps = oce_get_prop(dev, (char *)fm_cap_name,
579 DDI_FM_NOT_CAPABLE, OCE_FM_CAPABILITY, OCE_FM_CAPABILITY,
580 fm_caps_values);
581
582 dev->tx_reclaim_threshold = oce_get_prop(dev,
583 (char *)tx_reclaim_threshold_name, 0, dev->tx_ring_size/2,
584 OCE_DEFAULT_TX_RECLAIM_THRESHOLD, tx_rt_values);
585
586 dev->rx_pkt_per_intr = oce_get_prop(dev, (char *)rx_pkt_per_intr_name,
587 0, dev->rx_ring_size/2, OCE_DEFAULT_RX_PKT_PER_INTR, rx_ppi_values);
588
589 dev->rx_rings = oce_get_prop(dev, (char *)rx_rings_name,
590 OCE_DEFAULT_RQS, OCE_MAX_RQS, OCE_DEFAULT_RQS, rx_rings_values);
591
592 dev->tx_rings = oce_get_prop(dev, (char *)tx_rings_name,
593 OCE_DEFAULT_WQS, OCE_DEFAULT_WQS, OCE_DEFAULT_WQS, tx_rings_values);
594
595 log_level = oce_get_prop(dev, (char *)log_level_name, 0,
596 OCE_MAX_LOG_SETTINGS, OCE_DEFAULT_LOG_SETTINGS, log_level_values);
597
598 severity = (uint16_t)(log_level & 0xffff);
599 mod_mask = (uint16_t)(log_level >> 16);
600 if (mod_mask > MOD_ISR) {
601 mod_mask = 0;
602 }
603 if (severity > CE_IGNORE) {
604 severity = 0;
605 }
606
607 dev->mod_mask = mod_mask;
608 dev->severity = severity;
609 } /* oce_get_params */
610
611 static int
oce_get_prop(struct oce_dev * dev,char * propname,int minval,int maxval,int defval,uint32_t * values)612 oce_get_prop(struct oce_dev *dev, char *propname, int minval, int maxval,
613 int defval, uint32_t *values)
614 {
615 int value = 0;
616 int i = 0;
617
618 value = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
619 DDI_PROP_DONTPASS, propname, defval);
620
621 if (value > maxval)
622 value = maxval;
623
624 if (value < minval)
625 value = minval;
626
627 while (values[i] != 0xdeadface) {
628 if (values[i] == value) {
629 break;
630 }
631 i++;
632 }
633
634 if ((i != 0) && (values[i] == 0xdeadface)) {
635 value = defval;
636 }
637
638 return (value);
639 }
640