Annotation of XNU/iokit/Families/IOUSBBus/IOUSBDevice.cpp, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
        !             3:  *
        !             4:  * @APPLE_LICENSE_HEADER_START@
        !             5:  * 
        !             6:  * The contents of this file constitute Original Code as defined in and
        !             7:  * are subject to the Apple Public Source License Version 1.1 (the
        !             8:  * "License").  You may not use this file except in compliance with the
        !             9:  * License.  Please obtain a copy of the License at
        !            10:  * http://www.apple.com/publicsource and read it before using this file.
        !            11:  * 
        !            12:  * This Original Code and all software distributed under the License are
        !            13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
        !            14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
        !            15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
        !            16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
        !            17:  * License for the specific language governing rights and limitations
        !            18:  * under the License.
        !            19:  * 
        !            20:  * @APPLE_LICENSE_HEADER_END@
        !            21:  */
        !            22: /*
        !            23:  * Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.
        !            24:  *
        !            25:  * HISTORY
        !            26:  * 08 Dec 98 ehewitt created.
        !            27:  *
        !            28:  */
        !            29: 
        !            30: #include <libkern/OSByteOrder.h>
        !            31: 
        !            32: #include <libkern/c++/OSDictionary.h>
        !            33: #include <IOKit/IOMemoryDescriptor.h>
        !            34: #include <libkern/c++/OSData.h>
        !            35: 
        !            36: #include <IOKit/usb/USB.h>
        !            37: #include <IOKit/usb/IOUSBController.h>
        !            38: #include <IOKit/usb/IOUSBDevice.h>
        !            39: #include <IOKit/usb/IOUSBInterface.h>
        !            40: #include <IOKit/usb/IOUSBPipe.h>
        !            41: 
        !            42: #include "IOUSBUserClient.h"
        !            43: #include "IOUSBHub.h"
        !            44: 
        !            45: #define super IOUSBNub
        !            46: 
        !            47: static int devdebug = 0;
        !            48: #define DEBUGLOG if (devdebug) kprintf
        !            49: 
        !            50: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
        !            51: 
        !            52: OSDefineMetaClassAndStructors(IOUSBDevice, IOUSBNub)
        !            53: 
        !            54: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
        !            55: 
        !            56: void IOUSBDevice::setPort(IOUSBHubPort *port)
        !            57: {
        !            58:     _port = port;
        !            59: }
        !            60: 
        !            61: bool IOUSBDevice::init(OSDictionary * propTable)
        !            62: {
        !            63:     if(!IOUSBNub::init(propTable))
        !            64:        return false;
        !            65:     if(_descriptor.numConf) {
        !            66:         _configList = (UInt8 **)IOMalloc(sizeof(UInt8 *) * _descriptor.numConf);
        !            67:         if(!_configList)
        !            68:             return false;
        !            69:         bzero(_configList, sizeof(UInt8 *) * _descriptor.numConf);
        !            70:     }
        !            71:     return true;
        !            72: }
        !            73: 
        !            74: void IOUSBDevice::free()
        !            75: {
        !            76:     if(_configList) {
        !            77:        int i;
        !            78:         for(i=0; i<_descriptor.numConf; i++) {
        !            79:             if(_configList[i]) {
        !            80:                 int len = OSReadLittleInt16(&((IOUSBConfigurationDescriptor *)_configList[i])->totalLength, 0);
        !            81:                 IOFree(_configList[i], len+2);
        !            82:                _configList[i] = NULL;
        !            83:             }
        !            84:        }
        !            85:         IOFree(_configList, sizeof(UInt8 *) * _descriptor.numConf);
        !            86:     }
        !            87:     IOUSBNub::free();
        !            88: }
        !            89: 
        !            90: bool IOUSBDevice::attach(IOService *provider)
        !            91: {
        !            92:     IOReturn err;
        !            93:     char name[128];
        !            94: 
        !            95:     if( !IOUSBNub::attach(provider))
        !            96:         return (false);
        !            97:     // Set controller if we weren't given one earlier.
        !            98:     if(_controller == NULL)
        !            99:        _controller = OSDynamicCast(IOUSBController, provider);
        !           100:     if(_controller == NULL)
        !           101:        return false;
        !           102: 
        !           103:     // Don't do this until we have a controller
        !           104:     _endpointZero.length = sizeof(_endpointZero);
        !           105:     _endpointZero.descriptorType = kUSBEndpointDesc;
        !           106:     _endpointZero.endpointAddress = 0;
        !           107:     _endpointZero.attributes = kUSBControl;
        !           108:     OSWriteLittleInt16(&_endpointZero.maxPacketSizeLo, 0, _descriptor.maxPacketSize);  
        !           109:     _endpointZero.interval = 0;
        !           110: 
        !           111:     _pipeZero = IOUSBPipe::toEndpoint(&_endpointZero, _speed, _address, _controller);
        !           112: 
        !           113:     if(_descriptor.prodIdx) {
        !           114:         err = GetStringDescriptor(_descriptor.prodIdx, name, sizeof(name));
        !           115:        if(err == kIOReturnSuccess) {
        !           116:             setName(name);
        !           117:             setProperty("USB Product Name", name);
        !           118:        }
        !           119:     }
        !           120:     if(_descriptor.manuIdx) {
        !           121:         err = GetStringDescriptor(_descriptor.manuIdx, name, sizeof(name));
        !           122:         if(err == kIOReturnSuccess) {
        !           123:             setProperty("USB Vendor Name", name);
        !           124:        }
        !           125:     }
        !           126:     if(_descriptor.serialIdx) {
        !           127:         err = GetStringDescriptor(_descriptor.serialIdx, name, sizeof(name));
        !           128:         if(err == kIOReturnSuccess) {
        !           129:             setProperty("USB Serial Number", name);
        !           130:         }
        !           131:     }
        !           132:     return true;
        !           133: }
        !           134: 
        !           135: // Stop all activity, reset device, restart.
        !           136: IOReturn IOUSBDevice::resetDevice()
        !           137: {
        !           138:     IOReturn err;
        !           139: 
        !           140:     if(_pipeZero) {
        !           141:         _pipeZero->release();
        !           142:         _pipeZero = NULL;
        !           143:     }
        !           144:     err = _port->resetDevice();
        !           145: 
        !           146:     // Recreate pipe 0 object
        !           147:     if(err == kIOReturnSuccess) {
        !           148:         _pipeZero = IOUSBPipe::toEndpoint(&_endpointZero, _speed, _address, _controller);
        !           149:         if(!_pipeZero)
        !           150:             err = kIOReturnNoMemory;
        !           151:     }
        !           152: 
        !           153:     return err;
        !           154: }
        !           155: 
        !           156: /******************************************************
        !           157:  * Helper Methods
        !           158:  ******************************************************/
        !           159: 
        !           160: const IOUSBConfigurationDescriptor *
        !           161: IOUSBDevice::findConfig(UInt8 configValue, int *configIndex)
        !           162: {
        !           163:     int i;
        !           164:     const IOUSBConfigurationDescriptor *cd = NULL;
        !           165: 
        !           166:     for(i = 0; i < _descriptor.numConf; i++) {
        !           167:         cd = getFullConfigurationDescriptor(i);
        !           168:         if(!cd)
        !           169:             continue;
        !           170:         if(cd->configValue == configValue)
        !           171:             break;
        !           172:     }
        !           173:     if(cd && configIndex)
        !           174:        *configIndex = i;
        !           175: 
        !           176:     return cd;
        !           177: }
        !           178: 
        !           179: // Finding the correct interface
        !           180: /*
        !           181:  * findNextInterface
        !           182:  * This method should really be rewritten to use iterators or
        !           183:  * be broken out into more functions without a request structure.
        !           184:  * Or even better, make the interfaces and endpoint objects that
        !           185:  * have their own methods for this stuff.
        !           186:  *
        !           187:  * returns:
        !           188:  *   next interface matching criteria.  0 if no matches
        !           189:  */
        !           190: IOUSBInterface *
        !           191: IOUSBDevice::findNextInterface(IOUSBInterface *current,
        !           192:                                FindInterfaceRequest *request)
        !           193: {
        !           194:     const IOUSBConfigurationDescriptor *cd;
        !           195:     const IOUSBInterfaceDescriptor *   id;
        !           196:     const IOUSBDescriptorHeader *      pos = NULL;
        !           197:     bool                               found;
        !           198:     int                                        configIndex;
        !           199:     if(current == NULL) {
        !           200:        configIndex = 0;
        !           201:         cd = getFullConfigurationDescriptor(configIndex);
        !           202:         if(!cd)
        !           203:             return NULL;
        !           204:         pos = (IOUSBDescriptorHeader *)cd;
        !           205:     }
        !           206:     else {
        !           207:         cd = findConfig(current->getConfigValue(), &configIndex);
        !           208:         if(!cd)
        !           209:             return NULL;
        !           210:         pos = (IOUSBDescriptorHeader *)cd;
        !           211:         while((pos = findNextDescriptor(pos, kUSBInterfaceDesc))) {
        !           212:             if(bcmp(pos, current->interfaceDescriptor(),
        !           213:                           sizeof(IOUSBInterfaceDescriptor)) == 0) {
        !           214:                 break;
        !           215:             }
        !           216:         }
        !           217:     }
        !           218:     while (configIndex < _descriptor.numConf)
        !           219:     {
        !           220:         pos = findNextDescriptor(pos, kUSBInterfaceDesc);
        !           221:         if(pos == NULL) {
        !           222:             // End of that configuration
        !           223:             configIndex++;
        !           224:             if(configIndex >= _descriptor.numConf)
        !           225:                 break;
        !           226: 
        !           227:             cd = getFullConfigurationDescriptor(configIndex);
        !           228:             continue;
        !           229:        }
        !           230:         id = (const IOUSBInterfaceDescriptor *)pos;
        !           231: 
        !           232:         // check the request parameters
        !           233:        found = true;
        !           234:         if (request->theClass != 0 && request->theClass != id->interfaceClass)
        !           235:             found = false;             // this is not it
        !           236:         if (request->subClass != 0 && request->subClass != id->interfaceSubClass)
        !           237:             found = false;             // this is not it
        !           238:         if (request->protocol != 0 && request->protocol != id->interfaceProtocol)
        !           239:             found = false;             // this is not it
        !           240:         if (request->maxPower != 0 && request->maxPower < cd->maxPower)
        !           241:             found = false;             // draws too much power
        !           242:         if (id->alternateSetting != 0)
        !           243:             found = false;             // Only want main setting
        !           244: 
        !           245:         if (found)
        !           246:         {
        !           247:             request->theClass = id->interfaceClass;
        !           248:             request->subClass = id->interfaceSubClass;
        !           249:             request->protocol = id->interfaceProtocol;
        !           250:             request->maxPower = cd->maxPower;
        !           251:             request->busPowered = cd->attributes & kUSBAtrBusPowered ? 2 : 1;
        !           252:             request->selfPowered = cd->attributes & kUSBAtrSelfPowered ? 2 : 1;
        !           253:             request->remoteWakeup = cd->attributes & kUSBAtrRemoteWakeup ? 2 : 1;
        !           254:              return(GetInterface(id, cd));
        !           255:         }
        !           256:     }
        !           257:     return(0);
        !           258: }
        !           259: 
        !           260: const IOUSBConfigurationDescriptor *IOUSBDevice::getFullConfigurationDescriptor(UInt8 index)
        !           261: {
        !           262:     IOReturn err;
        !           263: 
        !           264:     if(_configList[index] == NULL) {
        !           265:         int len;
        !           266:         IOUSBConfigurationDescriptor temp;
        !           267:         err = GetConfigDescriptor(index, &temp, sizeof(temp));
        !           268:         if (err) {
        !           269:             DEBUGLOG("getFullConfigurationDescriptor: Error 0x%x getting configuration descriptor\n", err);
        !           270:             return(0);
        !           271:         }
        !           272:         len = OSReadLittleInt16(&temp.totalLength, 0);
        !           273:         _configList[index] = (UInt8 *)IOMalloc(len + 2);
        !           274:         if(_configList[index] == NULL)
        !           275:             return 0;
        !           276:         err = GetConfigDescriptor(index, _configList[index], len);
        !           277:         if (err) {
        !           278:             DEBUGLOG("getFullConfigurationDescriptor: Error %d getting full configuration\n", err);
        !           279:             return(0);
        !           280:         }
        !           281:         *(_configList[index]+len) = 0; // A Known ~Elephant.
        !           282:         *(_configList[index]+len+1) = 0;// A Known ~Elephant.
        !           283:     }
        !           284:     return (IOUSBConfigurationDescriptor *)_configList[index];
        !           285: }
        !           286: 
        !           287: /*
        !           288:  * GetConfigDescriptor:
        !           289:  *     In: pointer to buffer for config descriptor, length to get
        !           290:  * Assumes: desc is has enough space to put it all
        !           291:  */
        !           292: IOReturn IOUSBDevice::GetConfigDescriptor(UInt8 configIndex,
        !           293:                                           void *desc, int len)
        !           294: {
        !           295:     IOReturn           err = kIOReturnSuccess;
        !           296:     IOUSBDevRequest    request;
        !           297: 
        !           298:     DEBUGLOG("********** GET CONFIG DESCRIPTOR (%d)**********\n", len);
        !           299: 
        !           300:     /*
        !           301:      * with config descriptors, the device will send back all descriptors,
        !           302:      * if the request is big enough.
        !           303:      */
        !           304: 
        !           305:     request.rqDirection = kUSBIn;
        !           306:     request.rqType = kUSBStandard;
        !           307:     request.rqRecipient = kUSBDevice;
        !           308:     request.bRequest = kUSBRqGetDescriptor;
        !           309:     OSWriteLittleInt16(&request.wValue, 0, (kUSBConfDesc << 8) + configIndex);
        !           310:     OSWriteLittleInt16(&request.wIndex, 0, 0);
        !           311:     OSWriteLittleInt16(&request.wLength, 0, len);
        !           312:     request.pData = desc;
        !           313: 
        !           314:     err = deviceRequest(&request);
        !           315: 
        !           316:     if (err)
        !           317:     {
        !           318:         DEBUGLOG("%s: error getting device config descriptor. err=0x%x\n", getName(), err);
        !           319:     }
        !           320: 
        !           321:     return(err);
        !           322: }
        !           323: 
        !           324: IOReturn IOUSBDevice::SetConfiguration(UInt8 configNumber)
        !           325: {
        !           326:     IOReturn           err = kIOReturnSuccess;
        !           327:     IOUSBDevRequest    request;
        !           328: 
        !           329:     DEBUGLOG("********** SET CONFIGURATION TO %d **********\n", configNumber);
        !           330: 
        !           331:     request.rqDirection = kUSBOut;
        !           332:     request.rqType = kUSBStandard;
        !           333:     request.rqRecipient = kUSBDevice;
        !           334:     request.bRequest = kUSBRqSetConfig;
        !           335:     OSWriteLittleInt16(&request.wValue, 0, configNumber);
        !           336:     OSWriteLittleInt16(&request.wIndex, 0, 0);
        !           337:     OSWriteLittleInt16(&request.wLength, 0, 0);
        !           338:     request.pData = 0;
        !           339: 
        !           340:     err = deviceRequest(&request);
        !           341: 
        !           342:     if (err)
        !           343:     {
        !           344:         DEBUGLOG("%s: error setting config. err=%d\n", getName(), err);
        !           345:     }
        !           346: 
        !           347:     return(err);
        !           348: }
        !           349: 
        !           350: IOUSBInterface * IOUSBDevice::GetInterface(const IOUSBInterfaceDescriptor *interface,
        !           351:                                                     const IOUSBConfigurationDescriptor *config)
        !           352: {
        !           353:     OSDictionary *             propTable = 0;
        !           354:     OSArray *                  epArray = 0;
        !           355:     IOUSBInterface *           newDevice = 0;
        !           356:     OSData *                   data;
        !           357:     OSNumber *                 offset;
        !           358:     int                                i;
        !           359:     char                       location[8];
        !           360:     OSIterator *               clients;
        !           361:     /*
        !           362:      * First see if the interface has already been created,
        !           363:      * ie. there's already one with the given interface descriptor and config descriptor
        !           364:      */
        !           365:     clients = getClientIterator();
        !           366:     if(clients) {
        !           367:         OSObject *next;
        !           368:         while((next = clients->getNextObject())) {
        !           369:             IOUSBInterface *testIt = OSDynamicCast(IOUSBInterface, next);
        !           370:             if(testIt) {
        !           371:                 if(config->configValue == testIt->getConfigValue() &&
        !           372:                    bcmp(interface, testIt->interfaceDescriptor(), sizeof(IOUSBInterfaceDescriptor)) == 0) {
        !           373:                     newDevice = testIt;
        !           374:                     break;
        !           375:                }
        !           376:             }
        !           377:        }
        !           378:        clients->release();
        !           379:     }
        !           380:     if(newDevice) {
        !           381:        newDevice->retain();
        !           382:        return newDevice;
        !           383:     }
        !           384:     do {
        !           385:         propTable = OSDictionary::withCapacity(13);
        !           386:         if (!propTable)
        !           387:             continue;
        !           388: 
        !           389:         data = OSData::withBytes( &_descriptor, sizeof( _descriptor ));
        !           390:         if (data) {
        !           391:             propTable->setObject("device descriptor", data);
        !           392:             data->release();
        !           393:         }
        !           394:         offset = OSNumber::withNumber(interface->interfaceClass, 8);
        !           395:         if(offset) {
        !           396:             propTable->setObject("class", offset);
        !           397:             offset->release();
        !           398:         }
        !           399:         offset = OSNumber::withNumber(interface->interfaceSubClass, 8);
        !           400:         if(offset) {
        !           401:             propTable->setObject("subClass", offset);
        !           402:             offset->release();
        !           403:         }
        !           404:         offset = OSNumber::withNumber(interface->interfaceProtocol, 8);
        !           405:         if(offset) {
        !           406:             propTable->setObject("protocol", offset);
        !           407:             offset->release();
        !           408:         }
        !           409:         offset = OSNumber::withNumber(OSReadLittleInt16(&_descriptor.vendor, 0), 16);
        !           410:         if(offset) {
        !           411:             propTable->setObject("vendor", offset);
        !           412:             offset->release();
        !           413:         }
        !           414:         offset = OSNumber::withNumber(OSReadLittleInt16(&_descriptor.product, 0), 16);
        !           415:         if(offset) {
        !           416:             propTable->setObject("product", offset);
        !           417:             offset->release();
        !           418:         }
        !           419:         offset = OSNumber::withNumber(OSReadLittleInt16(&_descriptor.devRel, 0), 16);
        !           420:         if(offset) {
        !           421:             propTable->setObject("version", offset);
        !           422:             offset->release();
        !           423:         }
        !           424:         data = OSData::withBytes(&_address, sizeof( _address ));
        !           425:         if (data) {
        !           426:             propTable->setObject("device address", data);
        !           427:             data->release();
        !           428:         }
        !           429:         data = OSData::withBytes( config,
        !           430:                                   sizeof( *config ));
        !           431:         if (data) {
        !           432:             propTable->setObject("configuration descriptor", data);
        !           433:             data->release();
        !           434:         }
        !           435:         data = OSData::withBytes( interface,
        !           436:                                    sizeof(*interface));
        !           437:         if (data) {
        !           438:             propTable->setObject("interface descriptor", data);
        !           439:             data->release();
        !           440:         }
        !           441:         data = OSData::withBytes(&_busPowerAvailable, sizeof(_busPowerAvailable));
        !           442:         if (data) {
        !           443:             propTable->setObject("bus power available", data);
        !           444:             data->release();
        !           445:         }
        !           446:         data = OSData::withBytes(&_speed, sizeof(_speed));
        !           447:         if (data) {
        !           448:             propTable->setObject("low speed device", data);
        !           449:             data->release();
        !           450:         }
        !           451: 
        !           452:         epArray = OSArray::withCapacity(interface->numEndpoints);
        !           453:         if (!epArray)
        !           454:             continue;
        !           455:         IOUSBEndpointDescriptor *ep = (IOUSBEndpointDescriptor *)interface;
        !           456:         for(i=0; i<interface->numEndpoints; i++) {
        !           457:             ep = (IOUSBEndpointDescriptor *)findNextDescriptor(ep, kUSBEndpointDesc);
        !           458:             if(!ep)
        !           459:                break;
        !           460:             data = OSData::withBytes(ep, sizeof(IOUSBEndpointDescriptor));
        !           461:             if (data) {
        !           462:                 epArray->setObject(i, data);
        !           463:                 data->release();
        !           464:             }
        !           465:        }
        !           466:         propTable->setObject("Endpoints", epArray);
        !           467:         epArray->release();
        !           468: 
        !           469:         newDevice = createInterface(propTable, config, interface);
        !           470: 
        !           471:         if (!newDevice )
        !           472:             continue;
        !           473: 
        !           474:         propTable->release();  // done with it after init
        !           475:         propTable = 0;
        !           476: 
        !           477:         sprintf( location, "%x", _address );
        !           478:         newDevice->setLocation(location);
        !           479:         if (!newDevice->attach(this))
        !           480:             break;
        !           481: 
        !           482:         return(newDevice);
        !           483: 
        !           484:     } while (false);
        !           485: 
        !           486:     if (propTable)
        !           487:         propTable->release();
        !           488:     if (epArray)
        !           489:        epArray->release();
        !           490:     if (newDevice)
        !           491:         newDevice->release();
        !           492: 
        !           493:     return(NULL);
        !           494: }
        !           495: 
        !           496: IOUSBInterface *
        !           497: IOUSBDevice::createInterface(OSDictionary *table,
        !           498:                    const IOUSBConfigurationDescriptor *config,
        !           499:                                         const IOUSBInterfaceDescriptor *interface)
        !           500: {
        !           501:     IOUSBInterface *newIface;
        !           502: 
        !           503:     newIface = new IOUSBInterface;
        !           504:     if(!newIface)
        !           505:        return NULL;
        !           506:     if(!newIface->init(table, config, interface)) {
        !           507:         newIface->release();
        !           508:         newIface = NULL;
        !           509:     }
        !           510:     return newIface;
        !           511: }
        !           512: 
        !           513: // Copy data into supplied buffer, up to 'len' bytes.
        !           514: IOReturn IOUSBDevice::getConfigurationDescriptor(UInt8 configValue, void *data, UInt32 len)
        !           515: {
        !           516:     unsigned int toCopy;
        !           517:     const IOUSBConfigurationDescriptor *cd;
        !           518:     cd = findConfig(configValue);
        !           519:     if(!cd)
        !           520:         return kIOUSBConfigNotFound;
        !           521: 
        !           522:     toCopy = OSReadLittleInt16(&cd->totalLength, 0);
        !           523:     if(len < toCopy)
        !           524:        toCopy = len;
        !           525:     bcopy(cd, data, toCopy);
        !           526:     return kIOReturnSuccess;
        !           527: }
        !           528: 
        !           529: 
        !           530: /**
        !           531:  ** IOUserClient methods
        !           532:  **/
        !           533: 
        !           534: IOReturn IOUSBDevice::newUserClient(  task_t           owningTask,
        !           535:                                       void *           /* security_id */,
        !           536:                                       UInt32           type,
        !           537:                                       IOUserClient **  handler )
        !           538: 
        !           539: {
        !           540:     IOReturn                   err = kIOReturnSuccess;
        !           541:     IOUSBDeviceUserClient *    client;
        !           542: 
        !           543:     if( type != kIOUSBDeviceConnectType)
        !           544:         return( kIOReturnBadArgument);
        !           545: 
        !           546:     client = IOUSBDeviceUserClient::withTask(owningTask);
        !           547: 
        !           548:     if( !client || (false == client->attach( this )) ||
        !           549:         (false == client->start( this )) ) {
        !           550:         if(client) {
        !           551:             client->detach( this );
        !           552:             client->release();
        !           553:         }
        !           554:         err = kIOReturnNoMemory;
        !           555:     }
        !           556: 
        !           557:     *handler = client;
        !           558:     return( err );
        !           559: }
        !           560: 
        !           561: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.