if_ed_hpp.c revision 294883
1/*- 2 * Copyright (c) 2005, M. Warner Losh 3 * All rights reserved. 4 * Copyright (c) 1995, David Greenman 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sys/dev/ed/if_ed_hpp.c 294883 2016-01-27 02:23:54Z jhibbits $"); 32 33#include "opt_ed.h" 34 35#ifdef ED_HPP 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/sockio.h> 40#include <sys/mbuf.h> 41#include <sys/kernel.h> 42#include <sys/socket.h> 43#include <sys/syslog.h> 44 45#include <sys/bus.h> 46 47#include <machine/bus.h> 48#include <sys/rman.h> 49#include <machine/resource.h> 50 51#include <net/ethernet.h> 52#include <net/if.h> 53#include <net/if_var.h> /* XXX: ed_hpp_set_physical_link() */ 54#include <net/if_arp.h> 55#include <net/if_dl.h> 56#include <net/if_mib.h> 57#include <net/if_media.h> 58 59#include <net/bpf.h> 60 61#include <dev/ed/if_edreg.h> 62#include <dev/ed/if_edvar.h> 63 64static void ed_hpp_readmem(struct ed_softc *, bus_size_t, uint8_t *, 65 uint16_t); 66static void ed_hpp_writemem(struct ed_softc *, uint8_t *, uint16_t, 67 uint16_t); 68static void ed_hpp_set_physical_link(struct ed_softc *sc); 69static u_short ed_hpp_write_mbufs(struct ed_softc *, struct mbuf *, 70 bus_size_t); 71 72/* 73 * Interrupt conversion table for the HP PC LAN+ 74 */ 75static uint16_t ed_hpp_intr_val[] = { 76 0, /* 0 */ 77 0, /* 1 */ 78 0, /* 2 */ 79 3, /* 3 */ 80 4, /* 4 */ 81 5, /* 5 */ 82 6, /* 6 */ 83 7, /* 7 */ 84 0, /* 8 */ 85 9, /* 9 */ 86 10, /* 10 */ 87 11, /* 11 */ 88 12, /* 12 */ 89 0, /* 13 */ 90 0, /* 14 */ 91 15 /* 15 */ 92}; 93 94#define ED_HPP_TEST_SIZE 16 95 96/* 97 * Probe and vendor specific initialization for the HP PC Lan+ Cards. 98 * (HP Part nos: 27247B and 27252A). 99 * 100 * The card has an asic wrapper around a DS8390 core. The asic handles 101 * host accesses and offers both standard register IO and memory mapped 102 * IO. Memory mapped I/O allows better performance at the expense of greater 103 * chance of an incompatibility with existing ISA cards. 104 * 105 * The card has a few caveats: it isn't tolerant of byte wide accesses, only 106 * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions 107 * don't allow 32 bit accesses; these are indicated by a bit in the software 108 * ID register (see if_edreg.h). 109 * 110 * Other caveats are: we should read the MAC address only when the card 111 * is inactive. 112 * 113 * For more information; please consult the CRYNWR packet driver. 114 * 115 * The AUI port is turned on using the "link2" option on the ifconfig 116 * command line. 117 */ 118int 119ed_probe_HP_pclanp(device_t dev, int port_rid, int flags) 120{ 121 struct ed_softc *sc = device_get_softc(dev); 122 int error; 123 int n; /* temp var */ 124 int memsize; /* mem on board */ 125 u_char checksum; /* checksum of board address */ 126 u_char irq; /* board configured IRQ */ 127 uint8_t test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */ 128 uint8_t test_buffer[ED_HPP_TEST_SIZE]; /* probing card */ 129 rman_res_t conf_maddr, conf_msize, conf_irq, junk; 130 131 error = ed_alloc_port(dev, 0, ED_HPP_IO_PORTS); 132 if (error) 133 return (error); 134 135 /* Fill in basic information */ 136 sc->asic_offset = ED_HPP_ASIC_OFFSET; 137 sc->nic_offset = ED_HPP_NIC_OFFSET; 138 139 sc->chip_type = ED_CHIP_TYPE_DP8390; 140 sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */ 141 142 /* 143 * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53" 144 */ 145 146 if ((ed_asic_inb(sc, ED_HPP_ID) != 0x50) || 147 (ed_asic_inb(sc, ED_HPP_ID + 1) != 0x48) || 148 ((ed_asic_inb(sc, ED_HPP_ID + 2) & 0xF0) != 0) || 149 (ed_asic_inb(sc, ED_HPP_ID + 3) != 0x53)) 150 return (ENXIO); 151 152 /* 153 * Read the MAC address and verify checksum on the address. 154 */ 155 156 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_MAC); 157 for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++) 158 checksum += (sc->enaddr[n] = 159 ed_asic_inb(sc, ED_HPP_MAC_ADDR + n)); 160 161 checksum += ed_asic_inb(sc, ED_HPP_MAC_ADDR + ETHER_ADDR_LEN); 162 163 if (checksum != 0xFF) 164 return (ENXIO); 165 166 /* 167 * Verify that the software model number is 0. 168 */ 169 170 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_ID); 171 if (((sc->hpp_id = ed_asic_inw(sc, ED_HPP_PAGE_4)) & 172 ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000) 173 return (ENXIO); 174 175 /* 176 * Read in and save the current options configured on card. 177 */ 178 179 sc->hpp_options = ed_asic_inw(sc, ED_HPP_OPTION); 180 181 sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET | 182 ED_HPP_OPTION_CHIP_RESET | ED_HPP_OPTION_ENABLE_IRQ); 183 184 /* 185 * Reset the chip. This requires writing to the option register 186 * so take care to preserve the other bits. 187 */ 188 189 ed_asic_outw(sc, ED_HPP_OPTION, 190 (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET | 191 ED_HPP_OPTION_CHIP_RESET))); 192 193 DELAY(5000); /* wait for chip reset to complete */ 194 195 ed_asic_outw(sc, ED_HPP_OPTION, 196 (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET | 197 ED_HPP_OPTION_CHIP_RESET | 198 ED_HPP_OPTION_ENABLE_IRQ))); 199 200 DELAY(5000); 201 202 if (!(ed_nic_inb(sc, ED_P0_ISR) & ED_ISR_RST)) 203 return (ENXIO); /* reset did not complete */ 204 205 /* 206 * Read out configuration information. 207 */ 208 209 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW); 210 211 irq = ed_asic_inb(sc, ED_HPP_HW_IRQ); 212 213 /* 214 * Check for impossible IRQ. 215 */ 216 217 if (irq >= (sizeof(ed_hpp_intr_val) / sizeof(ed_hpp_intr_val[0]))) 218 return (ENXIO); 219 220 /* 221 * If the kernel IRQ was specified with a '?' use the cards idea 222 * of the IRQ. If the kernel IRQ was explicitly specified, it 223 * should match that of the hardware. 224 */ 225 error = bus_get_resource(dev, SYS_RES_IRQ, 0, &conf_irq, &junk); 226 if (error) 227 bus_set_resource(dev, SYS_RES_IRQ, 0, ed_hpp_intr_val[irq], 1); 228 else { 229 if (conf_irq != ed_hpp_intr_val[irq]) 230 return (ENXIO); 231 } 232 233 /* 234 * Fill in softconfig info. 235 */ 236 237 sc->vendor = ED_VENDOR_HP; 238 sc->type = ED_TYPE_HP_PCLANPLUS; 239 sc->type_str = "HP-PCLAN+"; 240 241 sc->mem_shared = 0; /* we DON'T have dual ported RAM */ 242 sc->mem_start = 0; /* we use offsets inside the card RAM */ 243 244 sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */ 245 246 /* 247 * The board has 32KB of memory. Is there a way to determine 248 * this programmatically? 249 */ 250 251 memsize = 32768; 252 253 /* 254 * Check if memory mapping of the I/O registers possible. 255 */ 256 if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) { 257 u_long mem_addr; 258 259 /* 260 * determine the memory address from the board. 261 */ 262 263 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW); 264 mem_addr = (ed_asic_inw(sc, ED_HPP_HW_MEM_MAP) << 8); 265 266 /* 267 * Check that the kernel specified start of memory and 268 * hardware's idea of it match. 269 */ 270 error = bus_get_resource(dev, SYS_RES_MEMORY, 0, 271 &conf_maddr, &conf_msize); 272 if (error) 273 return (error); 274 275 if (mem_addr != conf_maddr) 276 return (ENXIO); 277 278 error = ed_alloc_memory(dev, 0, memsize); 279 if (error) 280 return (error); 281 282 sc->hpp_mem_start = rman_get_virtual(sc->mem_res); 283 } 284 285 /* 286 * Fill in the rest of the soft config structure. 287 */ 288 289 /* 290 * The transmit page index. 291 */ 292 293 sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET; 294 295 if (device_get_flags(dev) & ED_FLAGS_NO_MULTI_BUFFERING) 296 sc->txb_cnt = 1; 297 else 298 sc->txb_cnt = 2; 299 300 /* 301 * Memory description 302 */ 303 304 sc->mem_size = memsize; 305 sc->mem_ring = sc->mem_start + 306 (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE); 307 sc->mem_end = sc->mem_start + sc->mem_size; 308 309 /* 310 * Receive area starts after the transmit area and 311 * continues till the end of memory. 312 */ 313 314 sc->rec_page_start = sc->tx_page_start + 315 (sc->txb_cnt * ED_TXBUF_SIZE); 316 sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE); 317 318 319 sc->cr_proto = 0; /* value works */ 320 321 /* 322 * Set the wrap registers for string I/O reads. 323 */ 324 325 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_HW); 326 ed_asic_outw(sc, ED_HPP_HW_WRAP, 327 ((sc->rec_page_start / ED_PAGE_SIZE) | 328 (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8))); 329 330 /* 331 * Reset the register page to normal operation. 332 */ 333 334 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF); 335 336 /* 337 * Verify that we can read/write from adapter memory. 338 * Create test pattern. 339 */ 340 341 for (n = 0; n < ED_HPP_TEST_SIZE; n++) 342 test_pattern[n] = (n*n) ^ ~n; 343 344#undef ED_HPP_TEST_SIZE 345 346 /* 347 * Check that the memory is accessible thru the I/O ports. 348 * Write out the contents of "test_pattern", read back 349 * into "test_buffer" and compare the two for any 350 * mismatch. 351 */ 352 353 for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) { 354 ed_hpp_writemem(sc, test_pattern, (n * ED_PAGE_SIZE), 355 sizeof(test_pattern)); 356 ed_hpp_readmem(sc, (n * ED_PAGE_SIZE), 357 test_buffer, sizeof(test_pattern)); 358 359 if (bcmp(test_pattern, test_buffer, 360 sizeof(test_pattern))) 361 return (ENXIO); 362 } 363 364 sc->sc_mediachg = ed_hpp_set_physical_link; 365 sc->sc_write_mbufs = ed_hpp_write_mbufs; 366 sc->readmem = ed_hpp_readmem; 367 return (0); 368} 369 370/* 371 * HP PC Lan+ : Set the physical link to use AUI or TP/TL. 372 */ 373 374static void 375ed_hpp_set_physical_link(struct ed_softc *sc) 376{ 377 struct ifnet *ifp = sc->ifp; 378 int lan_page; 379 380 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN); 381 lan_page = ed_asic_inw(sc, ED_HPP_PAGE_0); 382 383 if (ifp->if_flags & IFF_LINK2) { 384 /* 385 * Use the AUI port. 386 */ 387 388 lan_page |= ED_HPP_LAN_AUI; 389 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN); 390 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page); 391 } else { 392 /* 393 * Use the ThinLan interface 394 */ 395 396 lan_page &= ~ED_HPP_LAN_AUI; 397 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_LAN); 398 ed_asic_outw(sc, ED_HPP_PAGE_0, lan_page); 399 } 400 401 /* 402 * Wait for the lan card to re-initialize itself 403 */ 404 DELAY(150000); /* wait 150 ms */ 405 406 /* 407 * Restore normal pages. 408 */ 409 ed_asic_outw(sc, ED_HPP_PAGING, ED_HPP_PAGE_PERF); 410} 411 412/* 413 * Support routines to handle the HP PC Lan+ card. 414 */ 415 416/* 417 * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped 418 * IO. 419 */ 420 421static void 422ed_hpp_readmem(struct ed_softc *sc, bus_size_t src, uint8_t *dst, 423 uint16_t amount) 424{ 425 int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); 426 427 /* Program the source address in RAM */ 428 ed_asic_outw(sc, ED_HPP_PAGE_2, src); 429 430 /* 431 * The HP PC Lan+ card supports word reads as well as 432 * a memory mapped i/o port that is aliased to every 433 * even address on the board. 434 */ 435 if (sc->hpp_mem_start) { 436 /* Enable memory mapped access. */ 437 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options & 438 ~(ED_HPP_OPTION_MEM_DISABLE | 439 ED_HPP_OPTION_BOOT_ROM_ENB)); 440 441 if (use_32bit_access && (amount > 3)) { 442 uint32_t *dl = (uint32_t *) dst; 443 volatile uint32_t *const sl = 444 (uint32_t *) sc->hpp_mem_start; 445 uint32_t *const fence = dl + (amount >> 2); 446 447 /* 448 * Copy out NIC data. We could probably write this 449 * as a `movsl'. The currently generated code is lousy. 450 */ 451 while (dl < fence) 452 *dl++ = *sl; 453 454 dst += (amount & ~3); 455 amount &= 3; 456 457 } 458 459 /* Finish off any words left, as a series of short reads */ 460 if (amount > 1) { 461 u_short *d = (u_short *) dst; 462 volatile u_short *const s = 463 (u_short *) sc->hpp_mem_start; 464 u_short *const fence = d + (amount >> 1); 465 466 /* Copy out NIC data. */ 467 while (d < fence) 468 *d++ = *s; 469 470 dst += (amount & ~1); 471 amount &= 1; 472 } 473 474 /* 475 * read in a byte; however we need to always read 16 bits 476 * at a time or the hardware gets into a funny state 477 */ 478 479 if (amount == 1) { 480 /* need to read in a short and copy LSB */ 481 volatile u_short *const s = 482 (volatile u_short *) sc->hpp_mem_start; 483 *dst = (*s) & 0xFF; 484 } 485 486 /* Restore Boot ROM access. */ 487 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options); 488 } else { 489 /* Read in data using the I/O port */ 490 if (use_32bit_access && (amount > 3)) { 491 ed_asic_insl(sc, ED_HPP_PAGE_4, dst, amount >> 2); 492 dst += (amount & ~3); 493 amount &= 3; 494 } 495 if (amount > 1) { 496 ed_asic_insw(sc, ED_HPP_PAGE_4, dst, amount >> 1); 497 dst += (amount & ~1); 498 amount &= 1; 499 } 500 if (amount == 1) { /* read in a short and keep the LSB */ 501 *dst = ed_asic_inw(sc, ED_HPP_PAGE_4) & 0xFF; 502 } 503 } 504} 505 506/* 507 * HP PC Lan+: Write to NIC memory, using either PIO or memory mapped 508 * IO. 509 * Only used in the probe routine to test the memory. 'len' must 510 * be even. 511 */ 512static void 513ed_hpp_writemem(struct ed_softc *sc, uint8_t *src, uint16_t dst, uint16_t len) 514{ 515 /* reset remote DMA complete flag */ 516 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC); 517 518 /* program the write address in RAM */ 519 ed_asic_outw(sc, ED_HPP_PAGE_0, dst); 520 521 if (sc->hpp_mem_start) { 522 u_short *s = (u_short *) src; 523 volatile u_short *d = (u_short *) sc->hpp_mem_start; 524 u_short *const fence = s + (len >> 1); 525 526 /* 527 * Enable memory mapped access. 528 */ 529 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options & 530 ~(ED_HPP_OPTION_MEM_DISABLE | 531 ED_HPP_OPTION_BOOT_ROM_ENB)); 532 533 /* 534 * Copy to NIC memory. 535 */ 536 while (s < fence) 537 *d = *s++; 538 539 /* 540 * Restore Boot ROM access. 541 */ 542 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options); 543 } else { 544 /* write data using I/O writes */ 545 ed_asic_outsw(sc, ED_HPP_PAGE_4, src, len / 2); 546 } 547} 548 549/* 550 * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using 551 * outsw() or via the memory mapped interface to the same register. 552 * Writes have to be in word units; byte accesses won't work and may cause 553 * the NIC to behave weirdly. Long word accesses are permitted if the ASIC 554 * allows it. 555 */ 556 557static u_short 558ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, bus_size_t dst) 559{ 560 int len, wantbyte; 561 unsigned short total_len; 562 unsigned char savebyte[2]; 563 volatile u_short * const d = 564 (volatile u_short *) sc->hpp_mem_start; 565 int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); 566 567 /* select page 0 registers */ 568 ed_nic_barrier(sc, ED_P0_CR, 1, 569 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 570 ed_nic_outb(sc, ED_P0_CR, sc->cr_proto | ED_CR_STA); 571 ed_nic_barrier(sc, ED_P0_CR, 1, 572 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 573 574 /* reset remote DMA complete flag */ 575 ed_nic_outb(sc, ED_P0_ISR, ED_ISR_RDC); 576 577 /* program the write address in RAM */ 578 ed_asic_outw(sc, ED_HPP_PAGE_0, dst); 579 580 if (sc->hpp_mem_start) /* enable memory mapped I/O */ 581 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options & 582 ~(ED_HPP_OPTION_MEM_DISABLE | 583 ED_HPP_OPTION_BOOT_ROM_ENB)); 584 585 wantbyte = 0; 586 total_len = 0; 587 588 if (sc->hpp_mem_start) { /* Memory mapped I/O port */ 589 while (m) { 590 total_len += (len = m->m_len); 591 if (len) { 592 caddr_t data = mtod(m, caddr_t); 593 /* finish the last word of the previous mbuf */ 594 if (wantbyte) { 595 savebyte[1] = *data; 596 *d = *((u_short *) savebyte); 597 data++; len--; wantbyte = 0; 598 } 599 /* output contiguous words */ 600 if ((len > 3) && (use_32bit_accesses)) { 601 volatile uint32_t *const dl = 602 (volatile uint32_t *) d; 603 uint32_t *sl = (uint32_t *) data; 604 uint32_t *fence = sl + (len >> 2); 605 606 while (sl < fence) 607 *dl = *sl++; 608 609 data += (len & ~3); 610 len &= 3; 611 } 612 /* finish off remain 16 bit writes */ 613 if (len > 1) { 614 u_short *s = (u_short *) data; 615 u_short *fence = s + (len >> 1); 616 617 while (s < fence) 618 *d = *s++; 619 620 data += (len & ~1); 621 len &= 1; 622 } 623 /* save last byte if needed */ 624 if ((wantbyte = (len == 1)) != 0) 625 savebyte[0] = *data; 626 } 627 m = m->m_next; /* to next mbuf */ 628 } 629 if (wantbyte) /* write last byte */ 630 *d = *((u_short *) savebyte); 631 } else { 632 /* use programmed I/O */ 633 while (m) { 634 total_len += (len = m->m_len); 635 if (len) { 636 caddr_t data = mtod(m, caddr_t); 637 /* finish the last word of the previous mbuf */ 638 if (wantbyte) { 639 savebyte[1] = *data; 640 ed_asic_outw(sc, ED_HPP_PAGE_4, 641 *((u_short *)savebyte)); 642 data++; 643 len--; 644 wantbyte = 0; 645 } 646 /* output contiguous words */ 647 if ((len > 3) && use_32bit_accesses) { 648 ed_asic_outsl(sc, ED_HPP_PAGE_4, 649 data, len >> 2); 650 data += (len & ~3); 651 len &= 3; 652 } 653 /* finish off remaining 16 bit accesses */ 654 if (len > 1) { 655 ed_asic_outsw(sc, ED_HPP_PAGE_4, 656 data, len >> 1); 657 data += (len & ~1); 658 len &= 1; 659 } 660 if ((wantbyte = (len == 1)) != 0) 661 savebyte[0] = *data; 662 663 } /* if len != 0 */ 664 m = m->m_next; 665 } 666 if (wantbyte) /* spit last byte */ 667 ed_asic_outw(sc, ED_HPP_PAGE_4, *(u_short *)savebyte); 668 669 } 670 671 if (sc->hpp_mem_start) /* turn off memory mapped i/o */ 672 ed_asic_outw(sc, ED_HPP_OPTION, sc->hpp_options); 673 674 return (total_len); 675} 676 677#endif /* ED_HPP */ 678