|
|
1.1 ! root 1: #include "u.h" ! 2: #include "../port/lib.h" ! 3: #include "mem.h" ! 4: #include "dat.h" ! 5: #include "fns.h" ! 6: #include "../port/error.h" ! 7: #include "io.h" ! 8: #include "devtab.h" ! 9: ! 10: #include "ether.h" ! 11: ! 12: enum { ! 13: IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ ! 14: ! 15: /* Commands */ ! 16: SelectWindow = 0x01, /* SelectWindow command */ ! 17: StartCoax = 0x02, /* Start Coaxial Transceiver */ ! 18: RxDisable = 0x03, /* RX Disable */ ! 19: RxEnable = 0x04, /* RX Enable */ ! 20: RxReset = 0x05, /* RX Reset */ ! 21: RxDiscard = 0x08, /* RX Discard Top Packet */ ! 22: TxEnable = 0x09, /* TX Enable */ ! 23: TxDisable = 0x0A, /* TX Disable */ ! 24: TxReset = 0x0B, /* TX Reset */ ! 25: AckIntr = 0x0D, /* Acknowledge Interrupt */ ! 26: SetIntrMask = 0x0E, /* Set Interrupt Mask */ ! 27: SetReadZeroMask = 0x0F, /* Set Read Zero Mask */ ! 28: SetRxFilter = 0x10, /* Set RX Filter */ ! 29: SetTxAvailable = 0x12, /* Set TX Available Threshold */ ! 30: ! 31: /* RX Filter Command Bits */ ! 32: MyEtherAddr = 0x01, /* Individual address */ ! 33: Multicast = 0x02, /* Group (multicast) addresses */ ! 34: Broadcast = 0x04, /* Broadcast address */ ! 35: Promiscuous = 0x08, /* All addresses (promiscuous mode) */ ! 36: ! 37: /* Window Register Offsets */ ! 38: Command = 0x0E, /* all windows */ ! 39: Status = 0x0E, ! 40: ! 41: ManufacturerID = 0x00, /* window 0 */ ! 42: ProductID = 0x02, ! 43: ConfigControl = 0x04, ! 44: AddressConfig = 0x06, ! 45: ResourceConfig = 0x08, ! 46: EEPROMcmd = 0x0A, ! 47: EEPROMdata = 0x0C, ! 48: ! 49: /* AddressConfig Bits */ ! 50: XcvrTypeMask = 0xC000, /* Transceiver Type Select */ ! 51: Xcvr10BaseT = 0x0000, ! 52: XcvrAUI = 0x4000, ! 53: XcvrBNC = 0xC000, ! 54: ! 55: /* ConfigControl */ ! 56: Rst = 0x04, /* Reset Adapter */ ! 57: Ena = 0x01, /* Enable Adapter */ ! 58: ! 59: Fifo = 0x00, /* window 1 */ ! 60: RxStatus = 0x08, ! 61: Timer = 0x0A, ! 62: TxStatus = 0x0B, ! 63: TxFreeBytes = 0x0C, ! 64: ! 65: /* Status/Interrupt Bits */ ! 66: Latch = 0x0001, /* Interrupt Latch */ ! 67: Failure = 0x0002, /* Adapter Failure */ ! 68: TxComplete = 0x0004, /* TX Complete */ ! 69: TxAvailable = 0x0008, /* TX Available */ ! 70: RxComplete = 0x0010, /* RX Complete */ ! 71: RxEarly = 0x0020, /* RX partial packet available */ ! 72: AllIntr = 0x00FE, /* All Interrupt Bits */ ! 73: CmdInProgress = 0x1000, /* Command In Progress */ ! 74: ! 75: /* TxStatus Bits */ ! 76: TxJabber = 0x20, /* Jabber Error */ ! 77: TxUnderrun = 0x10, /* Underrun */ ! 78: TxMaxColl = 0x08, /* Maximum Collisions */ ! 79: ! 80: /* RxStatus Bits */ ! 81: RxByteMask = 0x07FF, /* RX Bytes (0-1514) */ ! 82: RxErrMask = 0x3800, /* Type of Error: */ ! 83: RxErrOverrun = 0x0000, /* Overrrun */ ! 84: RxErrOversize = 0x0800, /* Oversize Packet (>1514) */ ! 85: RxErrDribble = 0x1000, /* Dribble Bit(s) */ ! 86: RxErrRunt = 0x1800, /* Runt Packet */ ! 87: RxErrFraming = 0x2000, /* Alignment (Framing) */ ! 88: RxErrCRC = 0x2800, /* CRC */ ! 89: RxError = 0x4000, /* Error */ ! 90: RxEmpty = 0x8000, /* Incomplete or FIFO empty */ ! 91: ! 92: FIFOdiag = 0x04, /* window 4 */ ! 93: MediaStatus = 0x0A, ! 94: ! 95: /* FIFOdiag bits */ ! 96: TxOverrun = 0x0400, /* TX Overrrun */ ! 97: RxOverrun = 0x0800, /* RX Overrun */ ! 98: RxStatusOverrun = 0x1000, /* RX Status Overrun */ ! 99: RxUnderrun = 0x2000, /* RX Underrun */ ! 100: RxReceiving = 0x8000, /* RX Receiving */ ! 101: ! 102: /* MediaStatus bits */ ! 103: JabberEna = 0x0040, /* Jabber Enabled (writeable) */ ! 104: LinkBeatEna = 0x0080, /* Link Beat Enabled (writeable) */ ! 105: }; ! 106: ! 107: #define COMMAND(port, cmd, a) outs(port+Command, ((cmd)<<11)|(a)) ! 108: ! 109: static void ! 110: attach(Ctlr *ctlr) ! 111: { ! 112: ulong port; ! 113: ! 114: port = ctlr->card.port; ! 115: /* ! 116: * Set the receiver packet filter for our own and ! 117: * and broadcast addresses, set the interrupt masks ! 118: * for all interrupts, and enable the receiver and transmitter. ! 119: * The only interrupt we should see under normal conditions ! 120: * is the receiver interrupt. If the transmit FIFO fills up, ! 121: * we will also see TxAvailable interrupts. ! 122: */ ! 123: COMMAND(port, SetRxFilter, Broadcast|MyEtherAddr); ! 124: COMMAND(port, SetReadZeroMask, AllIntr|Latch); ! 125: COMMAND(port, SetIntrMask, AllIntr|Latch); ! 126: COMMAND(port, RxEnable, 0); ! 127: COMMAND(port, TxEnable, 0); ! 128: } ! 129: ! 130: static void ! 131: mode(Ctlr *ctlr, int on) ! 132: { ! 133: ulong port; ! 134: ! 135: port = ctlr->card.port; ! 136: if(on) ! 137: COMMAND(port, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr); ! 138: else ! 139: COMMAND(port, SetRxFilter, Broadcast|MyEtherAddr); ! 140: } ! 141: ! 142: static void ! 143: receive(Ctlr *ctlr) ! 144: { ! 145: ushort status; ! 146: RingBuf *rb; ! 147: int len; ! 148: ulong port; ! 149: ! 150: port = ctlr->card.port; ! 151: while(((status = ins(port+RxStatus)) & RxEmpty) == 0){ ! 152: /* ! 153: * If we had an error, log it and continue ! 154: * without updating the ring. ! 155: */ ! 156: if(status & RxError){ ! 157: switch(status & RxErrMask){ ! 158: ! 159: case RxErrOverrun: /* Overrrun */ ! 160: ctlr->overflows++; ! 161: break; ! 162: ! 163: case RxErrOversize: /* Oversize Packet (>1514) */ ! 164: case RxErrRunt: /* Runt Packet */ ! 165: ctlr->buffs++; ! 166: break; ! 167: case RxErrFraming: /* Alignment (Framing) */ ! 168: ctlr->frames++; ! 169: break; ! 170: ! 171: case RxErrCRC: /* CRC */ ! 172: ctlr->crcs++; ! 173: break; ! 174: } ! 175: } ! 176: else { ! 177: /* ! 178: * We have a packet. Read it into the next ! 179: * free ring buffer, if any. ! 180: * The CRC is already stripped off. ! 181: */ ! 182: rb = &ctlr->rb[ctlr->ri]; ! 183: if(rb->owner == Interface){ ! 184: len = (status & RxByteMask); ! 185: rb->len = len; ! 186: ! 187: /* ! 188: * Must read len bytes padded to a ! 189: * doubleword. We can pick them out 32-bits ! 190: * at a time . ! 191: */ ! 192: insl(port+Fifo, rb->pkt, HOWMANY(len, 4)); ! 193: ! 194: /* ! 195: * Update the ring. ! 196: */ ! 197: rb->owner = Host; ! 198: ctlr->ri = NEXT(ctlr->ri, ctlr->nrb); ! 199: } ! 200: } ! 201: ! 202: /* ! 203: * Discard the packet as we're done with it. ! 204: * Wait for discard to complete. ! 205: */ ! 206: COMMAND(port, RxDiscard, 0); ! 207: while(ins(port+Status) & CmdInProgress) ! 208: ; ! 209: } ! 210: } ! 211: ! 212: static void ! 213: transmit(Ctlr *ctlr) ! 214: { ! 215: RingBuf *tb; ! 216: ushort len; ! 217: ulong port; ! 218: ! 219: port = ctlr->card.port; ! 220: for(tb = &ctlr->tb[ctlr->ti]; tb->owner == Interface; tb = &ctlr->tb[ctlr->ti]){ ! 221: /* ! 222: * If there's no room in the FIFO for this packet, ! 223: * set up an interrupt for when space becomes available. ! 224: * Output packet must be a multiple of 4 in length and ! 225: * we need 4 bytes for the preamble. ! 226: */ ! 227: len = ROUNDUP(tb->len, 4); ! 228: if(len+4 > ins(port+TxFreeBytes)){ ! 229: COMMAND(port, SetTxAvailable, len); ! 230: break; ! 231: } ! 232: ! 233: /* ! 234: * There's room, copy the packet to the FIFO and free ! 235: * the buffer back to the host. ! 236: */ ! 237: outs(port+Fifo, tb->len); ! 238: outs(port+Fifo, 0); ! 239: outsl(port+Fifo, tb->pkt, len/4); ! 240: tb->owner = Host; ! 241: ctlr->ti = NEXT(ctlr->ti, ctlr->ntb); ! 242: } ! 243: } ! 244: ! 245: static ushort ! 246: getdiag(Ctlr *ctlr) ! 247: { ! 248: ushort bytes; ! 249: ulong port; ! 250: ! 251: port = ctlr->card.port; ! 252: COMMAND(port, SelectWindow, 4); ! 253: bytes = ins(port+FIFOdiag); ! 254: COMMAND(port, SelectWindow, 1); ! 255: return bytes & 0xFFFF; ! 256: } ! 257: ! 258: static void ! 259: interrupt(Ctlr *ctlr) ! 260: { ! 261: ushort status, diag; ! 262: uchar txstatus, x; ! 263: ulong port; ! 264: ! 265: port = ctlr->card.port; ! 266: status = ins(port+Status); ! 267: ! 268: if(status & Failure){ ! 269: /* ! 270: * Adapter failure, try to find out why. ! 271: * Reset if necessary. ! 272: * What happens if Tx is active and we reset, ! 273: * need to retransmit? ! 274: * This probably isn't right. ! 275: */ ! 276: diag = getdiag(ctlr); ! 277: print("ether509: status #%ux, diag #%ux\n", status, diag); ! 278: ! 279: if(diag & TxOverrun){ ! 280: COMMAND(port, TxReset, 0); ! 281: COMMAND(port, TxEnable, 0); ! 282: } ! 283: ! 284: if(diag & RxUnderrun){ ! 285: COMMAND(port, RxReset, 0); ! 286: attach(ctlr); ! 287: } ! 288: ! 289: if(diag & TxOverrun) ! 290: transmit(ctlr); ! 291: ! 292: return; ! 293: } ! 294: ! 295: if(status & RxComplete){ ! 296: receive(ctlr); ! 297: wakeup(&ctlr->rr); ! 298: status &= ~RxComplete; ! 299: } ! 300: ! 301: if(status & TxComplete){ ! 302: /* ! 303: * Pop the TX Status stack, accumulating errors. ! 304: * If there was a Jabber or Underrun error, reset ! 305: * the transmitter. For all conditions enable ! 306: * the transmitter. ! 307: */ ! 308: txstatus = 0; ! 309: do{ ! 310: if(x = inb(port+TxStatus)) ! 311: outb(port+TxStatus, 0); ! 312: txstatus |= x; ! 313: }while(ins(port+Status) & TxComplete); ! 314: ! 315: if(txstatus & (TxJabber|TxUnderrun)) ! 316: COMMAND(port, TxReset, 0); ! 317: COMMAND(port, TxEnable, 0); ! 318: ctlr->oerrs++; ! 319: } ! 320: ! 321: if(status & (TxAvailable|TxComplete)){ ! 322: /* ! 323: * Reset the Tx FIFO threshold. ! 324: */ ! 325: if(status & TxAvailable) ! 326: COMMAND(port, AckIntr, TxAvailable); ! 327: transmit(ctlr); ! 328: wakeup(&ctlr->tr); ! 329: status &= ~(TxAvailable|TxComplete); ! 330: } ! 331: ! 332: /* ! 333: * Panic if there are any interrupt bits on we haven't ! 334: * dealt with other than Latch. ! 335: * Otherwise, acknowledge the interrupt. ! 336: */ ! 337: if(status & AllIntr) ! 338: panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(ctlr)); ! 339: ! 340: COMMAND(port, AckIntr, Latch); ! 341: } ! 342: ! 343: typedef struct Adapter Adapter; ! 344: struct Adapter { ! 345: Adapter *next; ! 346: ulong port; ! 347: }; ! 348: static Adapter *adapter; ! 349: ! 350: /* ! 351: * Write two 0 bytes to identify the IDport and then reset the ! 352: * ID sequence. Then send the ID sequence to the card to get ! 353: * the card into command state. ! 354: */ ! 355: static void ! 356: idseq(void) ! 357: { ! 358: int i; ! 359: uchar al; ! 360: ! 361: outb(IDport, 0); ! 362: outb(IDport, 0); ! 363: for(al = 0xFF, i = 0; i < 255; i++){ ! 364: outb(IDport, al); ! 365: if(al & 0x80){ ! 366: al <<= 1; ! 367: al ^= 0xCF; ! 368: } ! 369: else ! 370: al <<= 1; ! 371: } ! 372: } ! 373: ! 374: static ulong ! 375: activate(int tag) ! 376: { ! 377: int i; ! 378: ushort x, acr; ! 379: ulong port; ! 380: ! 381: /* ! 382: * Do the little configuration dance: ! 383: * ! 384: * 2. write the ID sequence to get to command state. ! 385: */ ! 386: idseq(); ! 387: ! 388: /* ! 389: * 3. Read the Manufacturer ID from the EEPROM. ! 390: * This is done by writing the IDPort with 0x87 (0x80 ! 391: * is the 'read EEPROM' command, 0x07 is the offset of ! 392: * the Manufacturer ID field in the EEPROM). ! 393: * The data comes back 1 bit at a time. ! 394: * We seem to need a delay here between reading the bits. ! 395: * ! 396: * If the ID doesn't match, there are no more adapters. ! 397: */ ! 398: outb(IDport, 0x87); ! 399: for(x = 0, i = 0; i < 16; i++){ ! 400: delay(5); ! 401: x <<= 1; ! 402: x |= inb(IDport) & 0x01; ! 403: } ! 404: if(x != 0x6D50) ! 405: return 0; ! 406: ! 407: /* ! 408: * 3. Read the Address Configuration from the EEPROM. ! 409: * The Address Configuration field is at offset 0x08 in the EEPROM). ! 410: */ ! 411: outb(IDport, 0x88); ! 412: for(acr = 0, i = 0; i < 16; i++){ ! 413: delay(20); ! 414: acr <<= 1; ! 415: acr |= inb(IDport) & 0x01; ! 416: } ! 417: port = (acr & 0x1F)*0x10 + 0x200; ! 418: ! 419: if(tag){ ! 420: /* ! 421: * 6. Tag the adapter so it won't respond in future. ! 422: * 6. Activate the adapter by writing the Activate command ! 423: * (0xFF). ! 424: */ ! 425: outb(IDport, 0xD1); ! 426: outb(IDport, 0xFF); ! 427: ! 428: /* ! 429: * 8. Now we can talk to the adapter's I/O base addresses. ! 430: * We get the I/O base address from the acr just read. ! 431: * ! 432: * Enable the adapter. ! 433: */ ! 434: outs(port+ConfigControl, Ena); ! 435: } ! 436: ! 437: return port; ! 438: } ! 439: ! 440: static ulong ! 441: tcm509(ISAConf *isa) ! 442: { ! 443: static int untag; ! 444: ulong port; ! 445: Adapter *ap; ! 446: ! 447: /* ! 448: * One time only: ! 449: * write ID sequence to get the attention of all adapters; ! 450: * untag all adapters. ! 451: * If we do a global reset here on all adapters we'll confuse any ! 452: * ISA cards configured for EISA mode. ! 453: */ ! 454: if(untag == 0){ ! 455: idseq(); ! 456: outb(IDport, 0xD0); ! 457: untag = 1; ! 458: } ! 459: ! 460: /* ! 461: * Attempt to activate adapters until one matches our ! 462: * address criteria. If adapter is set for EISA mode, ! 463: * tag it and ignore. Otherwise, reset the adapter and ! 464: * activate it fully. ! 465: */ ! 466: while(port = activate(0)){ ! 467: if(port == 0x3F0){ ! 468: outb(IDport, 0xD1); ! 469: continue; ! 470: } ! 471: outb(IDport, 0xC0); ! 472: delay(2); ! 473: if(activate(1) != port) ! 474: print("tcm509: activate\n"); ! 475: ! 476: if(isa->port == 0 || isa->port == port) ! 477: return port; ! 478: ! 479: ap = malloc(sizeof(Adapter)); ! 480: ap->port = port; ! 481: ap->next = adapter; ! 482: adapter = ap; ! 483: } ! 484: ! 485: return 0; ! 486: } ! 487: ! 488: static ulong ! 489: tcm579(ISAConf *isa) ! 490: { ! 491: static int slot = 1; ! 492: ushort x; ! 493: ulong port; ! 494: Adapter *ap; ! 495: ! 496: /* ! 497: * First time through, check if this is an EISA machine. ! 498: * If not, nothing to do. ! 499: */ ! 500: if(slot == 1 && strncmp((char*)(KZERO|0xFFFD9), "EISA", 4)) ! 501: return 0; ! 502: ! 503: /* ! 504: * Continue through the EISA slots looking for a match on both ! 505: * 3COM as the manufacturer and 3C579 or 3C579-TP as the product. ! 506: * If we find an adapter, select window 0, enable it and clear ! 507: * out any lingering status and interrupts. ! 508: * Trying to do a GlobalReset here to re-init the card (as in the ! 509: * 509 code) doesn't seem to work. ! 510: */ ! 511: while(slot < 16){ ! 512: port = slot++*0x1000; ! 513: if(ins(port+0xC80+ManufacturerID) != 0x6D50) ! 514: continue; ! 515: x = ins(port+0xC80+ProductID); ! 516: if((x & 0xF0FF) != 0x9050/* || (x != 0x9350 && x != 0x9250)*/) ! 517: continue; ! 518: ! 519: COMMAND(port, SelectWindow, 0); ! 520: outs(port+ConfigControl, Ena); ! 521: ! 522: COMMAND(port, TxReset, 0); ! 523: COMMAND(port, RxReset, 0); ! 524: COMMAND(port, AckIntr, 0xFF); ! 525: ! 526: if(isa->port == 0 || isa->port == port) ! 527: return port; ! 528: ! 529: ap = malloc(sizeof(Adapter)); ! 530: ap->port = port; ! 531: ap->next = adapter; ! 532: adapter = ap; ! 533: } ! 534: ! 535: return 0; ! 536: } ! 537: ! 538: static ulong ! 539: tcm589(ISAConf *isa) ! 540: { ! 541: USED(isa); ! 542: return 0; ! 543: } ! 544: ! 545: /* ! 546: * Get configuration parameters. ! 547: */ ! 548: int ! 549: ether509reset(Ctlr *ctlr) ! 550: { ! 551: int i, eax; ! 552: uchar ea[Eaddrlen]; ! 553: ushort x, acr; ! 554: ulong port; ! 555: Adapter *ap, **app; ! 556: ! 557: /* ! 558: * Any adapter matches if no port is supplied, ! 559: * otherwise the ports must match. ! 560: * See if we've already found an adapter that fits ! 561: * the bill. ! 562: * If no match then try for an EISA card, an ISA card ! 563: * and finally for a PCMCIA card. ! 564: */ ! 565: port = 0; ! 566: for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){ ! 567: if(ctlr->card.port == 0 || ctlr->card.port == ap->port){ ! 568: port = ap->port; ! 569: *app = ap->next; ! 570: /*free(ap);lost*/ ! 571: break; ! 572: } ! 573: } ! 574: if(strcmp(ctlr->card.type, "3C589") == 0) ! 575: port = ctlr->card.port; ! 576: if(port == 0) ! 577: port = tcm579(&ctlr->card); ! 578: if(port == 0) ! 579: port = tcm509(&ctlr->card); ! 580: if(port == 0) ! 581: return -1; ! 582: ! 583: /* ! 584: * Read the IRQ from the Resource Configuration Register, ! 585: * the ethernet address from the EEPROM, and the address configuration. ! 586: * The EEPROM command is 8 bits, the lower 6 bits being ! 587: * the address offset. ! 588: */ ! 589: if(strcmp(ctlr->card.type, "3C589") != 0) ! 590: ctlr->card.irq = (ins(port+ResourceConfig)>>12) & 0x0F; ! 591: for(eax = 0, i = 0; i < 3; i++, eax += 2){ ! 592: while(ins(port+EEPROMcmd) & 0x8000) ! 593: ; ! 594: outs(port+EEPROMcmd, (2<<6)|i); ! 595: while(ins(port+EEPROMcmd) & 0x8000) ! 596: ; ! 597: x = ins(port+EEPROMdata); ! 598: ea[eax] = (x>>8) & 0xFF; ! 599: ea[eax+1] = x & 0xFF; ! 600: } ! 601: acr = ins(port+AddressConfig); ! 602: ! 603: /* ! 604: * Finished with window 0. Now set the ethernet address ! 605: * in window 2. ! 606: * CommandRs have the format 'CCCCCAAAAAAAAAAA' where C ! 607: * is a bit in the command and A is a bit in the argument. ! 608: */ ! 609: if((ctlr->ea[0]|ctlr->ea[1]|ctlr->ea[2]|ctlr->ea[3]|ctlr->ea[4]|ctlr->ea[5]) == 0) ! 610: memmove(ctlr->ea, ea, Eaddrlen); ! 611: COMMAND(port, SelectWindow, 2); ! 612: for(i = 0; i < Eaddrlen; i++) ! 613: outb(port+i, ctlr->ea[i]); ! 614: ! 615: /* ! 616: * Enable the transceiver if necessary. ! 617: */ ! 618: switch(acr & XcvrTypeMask){ ! 619: ! 620: case Xcvr10BaseT: ! 621: /* ! 622: * Enable Link Beat and Jabber to start the ! 623: * transceiver. ! 624: */ ! 625: COMMAND(port, SelectWindow, 4); ! 626: outb(port+MediaStatus, LinkBeatEna|JabberEna); ! 627: break; ! 628: ! 629: case XcvrBNC: ! 630: /* ! 631: * Start the DC-DC converter. ! 632: * Wait > 800 microseconds. ! 633: */ ! 634: COMMAND(port, StartCoax, 0); ! 635: delay(1); ! 636: break; ! 637: } ! 638: ! 639: /* ! 640: * Set window 1 for normal operation. ! 641: * Clear out any lingering Tx status. ! 642: */ ! 643: COMMAND(port, SelectWindow, 1); ! 644: while(inb(port+TxStatus)) ! 645: outb(port+TxStatus, 0); ! 646: ! 647: ctlr->card.port = port; ! 648: ! 649: /* ! 650: * Set up the software configuration. ! 651: */ ! 652: ctlr->card.reset = ether509reset; ! 653: ctlr->card.attach = attach; ! 654: ctlr->card.mode = mode; ! 655: ctlr->card.transmit = transmit; ! 656: ctlr->card.intr = interrupt; ! 657: ctlr->card.bit16 = 1; ! 658: ! 659: return 0; ! 660: } ! 661: ! 662: void ! 663: ether509link(void) ! 664: { ! 665: addethercard("3C509", ether509reset); ! 666: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.