xref: /netbsd-src/usr.sbin/eeprom/eehandlers.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: eehandlers.c,v 1.6 1997/10/18 08:40:40 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/types.h>
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <string.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <time.h>
49 #include <unistd.h>
50 
51 #include <machine/eeprom.h>
52 #ifdef __sparc__
53 #include <machine/openpromio.h>
54 #endif /* __sparc__ */
55 
56 #include "defs.h"
57 
58 extern	char *path_eeprom;
59 extern	int eval;
60 extern	int update_checksums;
61 extern	int ignore_checksum;
62 extern	int fix_checksum;
63 extern	int cksumfail;
64 extern	u_short writecount;
65 
66 struct	timeb;
67 extern	time_t get_date __P((char *, struct timeb *));
68 
69 static	char err_str[BUFSIZE];
70 
71 static	void badval __P((struct keytabent *, char *));
72 static	int doio __P((struct keytabent *, u_char *, ssize_t, int));
73 
74 #define BARF(kt) {							\
75 	badval((kt), arg);						\
76 	++eval;								\
77 	return;								\
78 }
79 
80 #define FAILEDREAD(kt) {						\
81 	warnx(err_str);							\
82 	warnx("failed to read field `%s'", (kt)->kt_keyword);		\
83 	++eval;								\
84 	return;								\
85 }
86 
87 #define FAILEDWRITE(kt) {						\
88 	warnx(err_str);							\
89 	warnx("failed to update field `%s'", (kt)->kt_keyword);		\
90 	++eval;								\
91 	return;								\
92 }
93 
94 void
95 ee_hwupdate(ktent, arg)
96 	struct keytabent *ktent;
97 	char *arg;
98 {
99 	time_t t;
100 	char *cp, *cp2;
101 
102 	if (arg) {
103 		if ((strcmp(arg, "now") == 0) ||
104 		    (strcmp(arg, "today") == 0)) {
105 			if ((t = time(NULL)) == (time_t)(-1)) {
106 				warnx("can't get current time");
107 				++eval;
108 				return;
109 			}
110 		} else
111 			if ((t = get_date(arg, NULL)) == (time_t)(-1))
112 				BARF(ktent);
113 
114 		if (doio(ktent, (u_char *)&t, sizeof(t), IO_WRITE))
115 			FAILEDWRITE(ktent);
116 	} else
117 		if (doio(ktent, (u_char *)&t, sizeof(t), IO_READ))
118 			FAILEDREAD(ktent);
119 
120 	cp = ctime(&t);
121 	if ((cp2 = strrchr(cp, '\n')) != NULL)
122 		*cp2 = '\0';
123 
124 	printf("%s=%ld (%s)\n", ktent->kt_keyword, (long)t, cp);
125 }
126 
127 void
128 ee_num8(ktent, arg)
129 	struct keytabent *ktent;
130 	char *arg;
131 {
132 	u_char num8 = 0;
133 	u_int num32;
134 	int i;
135 
136 	if (arg) {
137 		for (i = 0; i < (strlen(arg) - 1); ++i)
138 			if (!isdigit(arg[i]))
139 				BARF(ktent);
140 		num32 = atoi(arg);
141 		if (num32 > 0xff)
142 			BARF(ktent);
143 		num8 += num32;
144 		if (doio(ktent, &num8, sizeof(num8), IO_WRITE))
145 			FAILEDWRITE(ktent);
146 	} else
147 		if (doio(ktent, &num8, sizeof(num8), IO_READ))
148 			FAILEDREAD(ktent);
149 
150 	printf("%s=%d\n", ktent->kt_keyword, num8);
151 }
152 
153 void
154 ee_num16(ktent, arg)
155 	struct keytabent *ktent;
156 	char *arg;
157 {
158 	u_int16_t num16 = 0;
159 	u_int num32;
160 	int i;
161 
162 	if (arg) {
163 		for (i = 0; i < (strlen(arg) - 1); ++i)
164 			if (!isdigit(arg[i]))
165 				BARF(ktent);
166 		num32 = atoi(arg);
167 		if (num32 > 0xffff)
168 			BARF(ktent);
169 		num16 += num32;
170 		if (doio(ktent, (u_char *)&num16, sizeof(num16), IO_WRITE))
171 			FAILEDWRITE(ktent);
172 	} else
173 		if (doio(ktent, (u_char *)&num16, sizeof(num16), IO_READ))
174 			FAILEDREAD(ktent);
175 
176 	printf("%s=%d\n", ktent->kt_keyword, num16);
177 }
178 
179 static	struct strvaltabent scrsizetab[] = {
180 	{ "1152x900",		EE_SCR_1152X900 },
181 	{ "1024x1024",		EE_SCR_1024X1024 },
182 	{ "1600x1280",		EE_SCR_1600X1280 },
183 	{ "1440x1440",		EE_SCR_1440X1440 },
184 	{ NULL,			0 },
185 };
186 
187 void
188 ee_screensize(ktent, arg)
189 	struct keytabent *ktent;
190 	char *arg;
191 {
192 	struct strvaltabent *svp;
193 	u_char scsize;
194 
195 	if (arg) {
196 		for (svp = scrsizetab; svp->sv_str != NULL; ++svp)
197 			if (strcmp(svp->sv_str, arg) == 0)
198 				break;
199 		if (svp->sv_str == NULL)
200 			BARF(ktent);
201 
202 		scsize = svp->sv_val;
203 		if (doio(ktent, &scsize, sizeof(scsize), IO_WRITE))
204 			FAILEDWRITE(ktent);
205 	} else {
206 		if (doio(ktent, &scsize, sizeof(scsize), IO_READ))
207 			FAILEDREAD(ktent);
208 
209 		for (svp = scrsizetab; svp->sv_str != NULL; ++svp)
210 			if (svp->sv_val == scsize)
211 				break;
212 		if (svp->sv_str == NULL) {
213 			warnx("unknown %s value %d", ktent->kt_keyword,
214 			    scsize);
215 			return;
216 		}
217 	}
218 	printf("%s=%s\n", ktent->kt_keyword, svp->sv_str);
219 }
220 
221 static	struct strvaltabent truthtab[] = {
222 	{ "true",		EE_TRUE },
223 	{ "false",		EE_FALSE },
224 	{ NULL,			0 },
225 };
226 
227 void
228 ee_truefalse(ktent, arg)
229 	struct keytabent *ktent;
230 	char *arg;
231 {
232 	struct strvaltabent *svp;
233 	u_char truth;
234 
235 	if (arg) {
236 		for (svp = truthtab; svp->sv_str != NULL; ++svp)
237 			if (strcmp(svp->sv_str, arg) == 0)
238 				break;
239 		if (svp->sv_str == NULL)
240 			BARF(ktent);
241 
242 		truth = svp->sv_val;
243 		if (doio(ktent, &truth, sizeof(truth), IO_WRITE))
244 			FAILEDWRITE(ktent);
245 	} else {
246 		if (doio(ktent, &truth, sizeof(truth), IO_READ))
247 			FAILEDREAD(ktent);
248 
249 		for (svp = truthtab; svp->sv_str != NULL; ++svp)
250 			if (svp->sv_val == truth)
251 				break;
252 		if (svp->sv_str == NULL) {
253 			warnx("unknown truth value 0x%x for %s", truth,
254 			    ktent->kt_keyword);
255 			return;
256 		}
257 	}
258 	printf("%s=%s\n", ktent->kt_keyword, svp->sv_str);
259 }
260 
261 void
262 ee_bootdev(ktent, arg)
263 	struct keytabent *ktent;
264 	char *arg;
265 {
266 	u_char dev[5];
267 	int i;
268 	size_t arglen;
269 	char *cp;
270 
271 	if (arg) {
272 		/*
273 		 * The format of the string we accept is the following:
274 		 *	cc(n,n,n)
275 		 * where:
276 		 *	c -- an alphabetical character [a-z]
277 		 *	n -- a number in hexadecimal, between 0 and ff,
278 		 *	     with no leading `0x'.
279 		 */
280 		arglen = strlen(arg);
281 		if (arglen < 9 || arglen > 12 || arg[2] != '(' ||
282 		     arg[arglen - 1] != ')')
283 			BARF(ktent);
284 
285 		/* Handle the first 2 letters. */
286 		for (i = 0; i < 2; ++i) {
287 			if (arg[i] < 'a' || arg[i] > 'z')
288 				BARF(ktent);
289 			dev[i] = (u_char)arg[i];
290 		}
291 
292 		/* Handle the 3 `0x'-less hex values. */
293 		cp = &arg[3];
294 		for (i = 2; i < 5; ++i) {
295 			if (*cp == '\0')
296 				BARF(ktent);
297 
298 			if (*cp >= '0' && *cp <= '9')
299 				dev[i] = *cp++ - '0';
300 			else if (*cp >= 'a' && *cp <= 'f')
301 				dev[i] = 10 + (*cp++ - 'a');
302 			else
303 				BARF(ktent);
304 
305 			/* Deal with a second digit. */
306 			if (*cp >= '0' && *cp <= '9') {
307 				dev[i] <<= 4;
308 				dev[i] &= 0xf0;
309 				dev[i] += *cp++ - '0';
310 			} else if (*cp >= 'a' && *cp <= 'f') {
311 				dev[i] <<= 4;
312 				dev[i] &= 0xf0;
313 				dev[i] += 10 + (*cp++ - 'a');
314 			}
315 
316 			/* Ensure we have the correct delimiter. */
317 			if ((*cp == ',' && i < 4) || (*cp == ')' && i == 4)) {
318 				++cp;
319 				continue;
320 			} else
321 				BARF(ktent);
322 		}
323 		if (doio(ktent, (u_char *)&dev[0], sizeof(dev), IO_WRITE))
324 			FAILEDWRITE(ktent);
325 	} else
326 		if (doio(ktent, (u_char *)&dev[0], sizeof(dev), IO_READ))
327 			FAILEDREAD(ktent);
328 
329 	printf("%s=%c%c(%x,%x,%x)\n", ktent->kt_keyword, dev[0],
330 	     dev[1], dev[2], dev[3], dev[4]);
331 }
332 
333 void
334 ee_kbdtype(ktent, arg)
335 	struct keytabent *ktent;
336 	char *arg;
337 {
338 	u_char kbd = 0;
339 	u_int kbd2;
340 	int i;
341 
342 	if (arg) {
343 		for (i = 0; i < (strlen(arg) - 1); ++i)
344 			if (!isdigit(arg[i]))
345 				BARF(ktent);
346 		kbd2 = atoi(arg);
347 		if (kbd2 > 0xff)
348 			BARF(ktent);
349 		kbd += kbd2;
350 		if (doio(ktent, &kbd, sizeof(kbd), IO_WRITE))
351 			FAILEDWRITE(ktent);
352 	} else
353 		if (doio(ktent, &kbd, sizeof(kbd), IO_READ))
354 			FAILEDREAD(ktent);
355 
356 	printf("%s=%d (%s)\n", ktent->kt_keyword, kbd, kbd ? "other" : "Sun");
357 }
358 
359 static	struct strvaltabent constab[] = {
360 	{ "b&w",		EE_CONS_BW },
361 	{ "ttya",		EE_CONS_TTYA },
362 	{ "ttyb",		EE_CONS_TTYB },
363 	{ "color",		EE_CONS_COLOR },
364 	{ "p4opt",		EE_CONS_P4OPT },
365 	{ NULL,			0 },
366 };
367 
368 void
369 ee_constype(ktent, arg)
370 	struct keytabent *ktent;
371 	char *arg;
372 {
373 	struct strvaltabent *svp;
374 	u_char cons;
375 
376 	if (arg) {
377 		for (svp = constab; svp->sv_str != NULL; ++svp)
378 			if (strcmp(svp->sv_str, arg) == 0)
379 				break;
380 		if (svp->sv_str == NULL)
381 			BARF(ktent);
382 
383 		cons = svp->sv_val;
384 		if (doio(ktent, &cons, sizeof(cons), IO_WRITE))
385 			FAILEDWRITE(ktent);
386 	} else {
387 		if (doio(ktent, &cons, sizeof(cons), IO_READ))
388 			FAILEDREAD(ktent);
389 
390 		for (svp = constab; svp->sv_str != NULL; ++svp)
391 			if (svp->sv_val == cons)
392 				break;
393 		if (svp->sv_str == NULL) {
394 			warnx("unknown type 0x%x for %s", cons,
395 			    ktent->kt_keyword);
396 			return;
397 		}
398 	}
399 	printf("%s=%s\n", ktent->kt_keyword, svp->sv_str);
400 
401 }
402 
403 void
404 ee_diagpath(ktent, arg)
405 	struct keytabent *ktent;
406 	char *arg;
407 {
408 	char path[40];
409 
410 	memset(path, 0, sizeof(path));
411 	if (arg) {
412 		if (strlen(arg) > sizeof(path))
413 			BARF(ktent);
414 		memcpy(path, arg, sizeof path);
415 		if (doio(ktent, (u_char *)&path[0], sizeof(path), IO_WRITE))
416 			FAILEDWRITE(ktent);
417 	} else
418 		if (doio(ktent, (u_char *)&path[0], sizeof(path), IO_READ))
419 			FAILEDREAD(ktent);
420 
421 	printf("%s=%s\n", ktent->kt_keyword, path);
422 }
423 
424 void
425 ee_banner(ktent, arg)
426 	struct keytabent *ktent;
427 	char *arg;
428 {
429 	char string[80];
430 	u_char enable;
431 	struct keytabent kt;
432 
433 	kt.kt_keyword = "enable_banner";
434 	kt.kt_offset = EE_BANNER_ENABLE_LOC;
435 	kt.kt_handler = ee_notsupp;
436 
437 	memset(string, '\0', sizeof(string));
438 	if (arg) {
439 		if (strlen(arg) > sizeof(string))
440 			BARF(ktent);
441 		if (*arg != '\0') {
442 			enable = EE_TRUE;
443 			memcpy(string, arg, sizeof string);
444 			if (doio(ktent, (u_char *)string,
445 			    sizeof(string), IO_WRITE))
446 				FAILEDWRITE(ktent);
447 		} else {
448 			enable = EE_FALSE;
449 			if (doio(ktent, (u_char *)string,
450 			    sizeof(string), IO_READ))
451 				FAILEDREAD(ktent);
452 		}
453 
454 		if (doio(&kt, &enable, sizeof(enable), IO_WRITE))
455 			FAILEDWRITE(&kt);
456 	} else {
457 		if (doio(ktent, (u_char *)string, sizeof(string), IO_READ))
458 			FAILEDREAD(ktent);
459 		if (doio(&kt, &enable, sizeof(enable), IO_READ))
460 			FAILEDREAD(&kt);
461 	}
462 	printf("%s=%s (%s)\n", ktent->kt_keyword, string,
463 	    enable == EE_TRUE ? "enabled" : "disabled");
464 }
465 
466 /* ARGSUSED */
467 void
468 ee_notsupp(ktent, arg)
469 	struct keytabent *ktent;
470 	char *arg;
471 {
472 
473 	warnx("field `%s' not yet supported", ktent->kt_keyword);
474 }
475 
476 static void
477 badval(ktent, arg)
478 	struct keytabent *ktent;
479 	char *arg;
480 {
481 
482 	warnx("inappropriate value `%s' for field `%s'", arg,
483 	    ktent->kt_keyword);
484 }
485 
486 static int
487 doio(ktent, buf, len, wr)
488 	struct keytabent *ktent;
489 	u_char *buf;
490 	ssize_t len;
491 	int wr;
492 {
493 	int fd, rval = 0;
494 	u_char *buf2;
495 
496 	buf2 = (u_char *)calloc(1, len);
497 	if (buf2 == NULL) {
498 		memcpy(err_str, "memory allocation failed", sizeof err_str);
499 		return (1);
500 	}
501 
502 	fd = open(path_eeprom, wr == IO_WRITE ? O_RDWR : O_RDONLY, 0640);
503 	if (fd < 0) {
504 		(void)snprintf(err_str, sizeof err_str, "open: %s: %s", path_eeprom,
505 		    strerror(errno));
506 		free(buf2);
507 		return (1);
508 	}
509 
510 	if (lseek(fd, (off_t)ktent->kt_offset, SEEK_SET) < (off_t)0) {
511 		(void)snprintf(err_str, sizeof err_str, "lseek: %s: %s",
512 		    path_eeprom, strerror(errno));
513 		rval = 1;
514 		goto done;
515 	}
516 
517 	if (read(fd, buf2, len) != len) {
518 		(void)snprintf(err_str, sizeof err_str, "read: %s: %s",
519 		    path_eeprom, strerror(errno));
520 		return (1);
521 	}
522 
523 	if (wr == IO_WRITE) {
524 		if (memcmp(buf, buf2, len) == 0)
525 			goto done;
526 
527 		if (lseek(fd, (off_t)ktent->kt_offset, SEEK_SET) < (off_t)0) {
528 			(void)snprintf(err_str, sizeof err_str, "lseek: %s: %s",
529 			    path_eeprom, strerror(errno));
530 			rval = 1;
531 			goto done;
532 		}
533 
534 		++update_checksums;
535 		if (write(fd, buf, len) < 0) {
536 			(void)snprintf(err_str, sizeof err_str, "write: %s: %s",
537 			    path_eeprom, strerror(errno));
538 			rval = 1;
539 			goto done;
540 		}
541 	} else
542 		memmove(buf, buf2, len);
543 
544  done:
545 	free(buf2);
546 	(void)close(fd);
547 	return (rval);
548 }
549 
550 /*
551  * Read from eeLastHwUpdate to just before eeReserved.  Calculate
552  * a checksum, and deposit 3 copies of it sequentially starting at
553  * eeChecksum[0].  Increment the write count, and deposit 3 copies
554  * of it sequentially starting at eeWriteCount[0].
555  */
556 void
557 ee_updatechecksums()
558 {
559 	struct keytabent kt;
560 	u_char checkme[EE_SIZE - EE_HWUPDATE_LOC];
561 	u_char checksum;
562 	int i;
563 
564 	kt.kt_keyword = "eeprom contents";
565 	kt.kt_offset = EE_HWUPDATE_LOC;
566 	kt.kt_handler = ee_notsupp;
567 
568 	if (doio(&kt, checkme, sizeof(checkme), IO_READ)) {
569 		cksumfail = 1;
570 		FAILEDREAD(&kt);
571 	}
572 
573 	checksum = ee_checksum(checkme, sizeof(checkme));
574 
575 	kt.kt_keyword = "eeprom checksum";
576 	for (i = 0; i < 4; ++i) {
577 		kt.kt_offset = EE_CKSUM_LOC + (i * sizeof(checksum));
578 		if (doio(&kt, &checksum, sizeof(checksum), IO_WRITE)) {
579 			cksumfail = 1;
580 			FAILEDWRITE(&kt);
581 		}
582 	}
583 
584 	kt.kt_keyword = "eeprom writecount";
585 	for (i = 0; i < 4; ++i) {
586 		kt.kt_offset = EE_WC_LOC + (i * sizeof(writecount));
587 		if (doio(&kt, (u_char *)&writecount, sizeof(writecount),
588 		    IO_WRITE)) {
589 			cksumfail = 1;
590 			FAILEDWRITE(&kt);
591 		}
592 	}
593 }
594 
595 void
596 ee_verifychecksums()
597 {
598 	struct keytabent kt;
599 	u_char checkme[EE_SIZE - EE_HWUPDATE_LOC];
600 	u_char checksum, ochecksum[3];
601 	u_short owritecount[3];
602 
603 	/*
604 	 * Verify that the EEPROM's write counts match, and update the
605 	 * global copy for use later.
606 	 */
607 	kt.kt_keyword = "eeprom writecount";
608 	kt.kt_offset = EE_WC_LOC;
609 	kt.kt_handler = ee_notsupp;
610 
611 	if (doio(&kt, (u_char *)&owritecount, sizeof(owritecount), IO_READ)) {
612 		cksumfail = 1;
613 		FAILEDREAD(&kt);
614 	}
615 
616 	if (owritecount[0] != owritecount[1] ||
617 	    owritecount[0] != owritecount[2]) {
618 		warnx("eeprom writecount mismatch %s",
619 		    ignore_checksum ? "(ignoring)" :
620 		    (fix_checksum ? "(fixing)" : ""));
621 
622 		if (!ignore_checksum && !fix_checksum) {
623 			cksumfail = 1;
624 			return;
625 		}
626 
627 		writecount = MAXIMUM(owritecount[0], owritecount[1]);
628 		writecount = MAXIMUM(writecount, owritecount[2]);
629 	} else
630 		writecount = owritecount[0];
631 
632 	/*
633 	 * Verify that the EEPROM's checksums match and are correct.
634 	 */
635 	kt.kt_keyword = "eeprom checksum";
636 	kt.kt_offset = EE_CKSUM_LOC;
637 
638 	if (doio(&kt, ochecksum, sizeof(ochecksum), IO_READ)) {
639 		cksumfail = 1;
640 		FAILEDREAD(&kt);
641 	}
642 
643 	if (ochecksum[0] != ochecksum[1] ||
644 	    ochecksum[0] != ochecksum[2]) {
645 		warnx("eeprom checksum mismatch %s",
646 		    ignore_checksum ? "(ignoring)" :
647 		    (fix_checksum ? "(fixing)" : ""));
648 
649 		if (!ignore_checksum && !fix_checksum) {
650 			cksumfail = 1;
651 			return;
652 		}
653 	}
654 
655 	kt.kt_keyword = "eeprom contents";
656 	kt.kt_offset = EE_HWUPDATE_LOC;
657 
658 	if (doio(&kt, checkme, sizeof(checkme), IO_READ)) {
659 		cksumfail = 1;
660 		FAILEDREAD(&kt);
661 	}
662 
663 	checksum = ee_checksum(checkme, sizeof(checkme));
664 
665 	if (ochecksum[0] != checksum) {
666 		warnx("eeprom checksum incorrect %s",
667 		    ignore_checksum ? "(ignoring)" :
668 		    (fix_checksum ? "(fixing)" : ""));
669 
670 		if (!ignore_checksum && !fix_checksum) {
671 			cksumfail = 1;
672 			return;
673 		}
674 	}
675 
676 	if (fix_checksum)
677 		ee_updatechecksums();
678 }
679 
680 u_char
681 ee_checksum(area, len)
682 	u_char *area;
683 	size_t len;
684 {
685 	u_char sum = 0;
686 
687 	while (len--)
688 		sum += *area++;
689 
690 	return (0x100 - sum);
691 }
692