Annotation of kernel/bsd/dev/ppc/EventSrcPCPointer.m, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
                      3:  *
                      4:  * @APPLE_LICENSE_HEADER_START@
                      5:  * 
                      6:  * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
                      7:  * Reserved.  This file contains Original Code and/or Modifications of
                      8:  * Original Code as defined in and that are subject to the Apple Public
                      9:  * Source License Version 1.1 (the "License").  You may not use this file
                     10:  * except in compliance with the License.  Please obtain a copy of the
                     11:  * License at http://www.apple.com/publicsource and read it before using
                     12:  * this file.
                     13:  * 
                     14:  * The Original Code and all software distributed under the License are
                     15:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
                     16:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
                     17:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
                     19:  * License for the specific language governing rights and limitations
                     20:  * under the License.
                     21:  * 
                     22:  * @APPLE_LICENSE_HEADER_END@
                     23:  */
                     24: 
                     25: /*     Copyright (c) 1992 NeXT Computer, Inc.  All rights reserved. 
                     26:  *
                     27:  * EventSrcPCPointer.m - PC Pointer EventSrc subclass implementation
                     28:  *
                     29:  * HISTORY
                     30:  * 28 Aug 1992    Joe Pasqua
                     31:  *      Created. 
                     32:  * 11 April 1997  Simon Douglas
                     33:  *      ADB version for PPC.
                     34:  */
                     35: 
                     36: // TO DO:
                     37: // * VBL sync'ing
                     38: // * Real Mac scaling & acceleration & move to device
                     39: // * Better FixDiv/Mul
                     40: //
                     41: // NOTES:
                     42: // * The EventSrcExported protocol is implemented completely by our superclass.
                     43: //   At this point there is no need for this subclass to override anything.
                     44: // * In the current system the EventDriver instance is always the owner
                     45: //   of this EventSrc.
                     46: // * To find things that need to be fixed, search for FIX, to find questions
                     47: //   to be resolved, search for ASK, to find stuff that still needs to be
                     48: //   done, search for TO DO.
                     49: //
                     50: 
                     51: #import <driverkit/generalFuncs.h>
                     52: #import <machkit/NXLock.h>
                     53: #import <bsd/dev/evsio.h>
                     54: #import <bsd/dev/ppc/EventSrcPCPointer.h> 
                     55: 
                     56: #define MACSCALE       1
                     57: 
                     58: #if MACSCALE
                     59: #define MAXMAG 128
                     60: static int scaleValues[MAXMAG];
                     61: static int fractX,fractY;
                     62: #endif
                     63: 
                     64: #define        PC_PTR_DFLT_DELTA_T     2
                     65: 
                     66: // Default pointer acceleration or scaling table
                     67: static const unsigned int dfltPointerScaling[] =
                     68: {
                     69:        2,      2,      // Threshold and scaling pairs
                     70:        3,      3,
                     71:        4,      5,
                     72:        5,      7,
                     73:        6,      10
                     74: };
                     75: 
                     76: static EventSrcPCPointer *instance = (EventSrcPCPointer *)0;
                     77: 
                     78: @implementation EventSrcPCPointer: IOEventSource
                     79: 
                     80: int
                     81: FixMul( int a, int b)
                     82: {
                     83:     union {
                     84:            long long   result;
                     85:            int         word[2];
                     86:     } big;
                     87: 
                     88:     big.result = ((long long) a) * ((long long) b) * 65536;
                     89:     return( big.word[0] );
                     90: }
                     91: 
                     92: int
                     93: FixDiv( int a, int b)
                     94: {
                     95:     return( (((long long) a) * 65536) / ((long long) b));
                     96: }
                     97: 
                     98: 
                     99: //
                    100: // BEGIN:      Implementation of Private EventSrcPCPointer methods
                    101: //
                    102: - scalePointerInX:(int *)dxp
                    103:     andY:(int *)dyp
                    104:     over:(unsigned)dt
                    105:     atRes:(unsigned)res
                    106: // Description:        Perform pointer acceleration computations here.
                    107: //             Given the resolution, dx, dy, and time, compute the velocity
                    108: //             of the pointer over a Manhatten distance in inches/second.
                    109: //             Using this velocity, do a lookup in the pointerScaling table
                    110: //             to select a scaling factor. Scale dx and dy up as appropriate.
                    111: // Preconditions:
                    112: // *   deviceLock should be held on entry
                    113: {
                    114:     int dx, dy;
                    115:     int absDx, absDy;
                    116:     unsigned delta, velocity;
                    117:     int index;
                    118: 
                    119:     dx = *dxp;
                    120:     dy = *dyp;
                    121:     absDx = (dx < 0) ? -dx : dx;
                    122:     absDy = (dy < 0) ? -dy : dy;
                    123: 
                    124: #if MACSCALE
                    125: 
                    126: #if 1
                    127:     if( absDx > absDy)
                    128:        delta = (absDx + (absDy / 2));
                    129:     else
                    130:        delta = (absDy + (absDx / 2));
                    131: #else
                    132:     delta = absDx + absDy;
                    133: #endif
                    134: 
                    135:     // scale
                    136:     if( delta > (MAXMAG - 1))
                    137:        delta = MAXMAG - 1;
                    138:     dx = FixMul( dx << 16, scaleValues[delta]);
                    139:     dy = FixMul( dy << 16, scaleValues[delta]);
                    140: 
                    141:     // if no direction changes add fract parts
                    142:     if( (dx ^ fractX) >= 0)
                    143:        dx += fractX;
                    144:     if( (dy ^ fractY) >= 0)
                    145:        dy += fractY;
                    146: 
                    147:     *dxp = dx / 65536;
                    148:     *dyp = dy / 65536;
                    149: 
                    150:     // get fractional part with sign extend
                    151:     if( dx >= 0)
                    152:        fractX = dx & 0xffff;
                    153:     else
                    154:        fractX = dx | 0xffff0000;
                    155:     if( dy >= 0)
                    156:        fractY = dy & 0xffff;
                    157:     else
                    158:        fractY = dy | 0xffff0000;
                    159: 
                    160: #else
                    161: 
                    162:     // Take the sum of the X and Y distances as the delta to compute
                    163:     // the velocity. (Manhatten distance)
                    164:     delta = absDx + absDy;
                    165:     if ( dt == 0 )
                    166:            dt = PC_PTR_DFLT_DELTA_T;
                    167:     //
                    168:     // Velocity in IPS = (delta in Dots * ticks/sec)/(res in DPI * dT)
                    169:     //
                    170:     // This is a bit heavyweight, and could probably be approximated
                    171:     // with considerable savings.
                    172:     //
                    173:     velocity =  (delta * EV_TICKS_PER_SEC) / (res * dt);
                    174: 
                    175:     // Look through the scalings for the first threshold greater than
                    176:     // or equal to our velocity, and back off by one.
                    177:     if ( velocity > pointerScaling.scaleThresholds[0] )
                    178:     {
                    179:        for ( index = 1; index < pointerScaling.numScaleLevels; ++index )
                    180:        {
                    181:            if ( velocity <= pointerScaling.scaleThresholds[index] )
                    182:                break;
                    183:        }
                    184:        --index;        // Back off to get index covering our velocity.
                    185:        *dxp = dx * pointerScaling.scaleFactors[index];
                    186:        *dyp = dy * pointerScaling.scaleFactors[index];
                    187:     }
                    188: 
                    189: #endif
                    190: 
                    191:     return self;
                    192: }
                    193: 
                    194: IOReturn ReadNVRAM( unsigned int offset, unsigned int length, unsigned char * buffer );
                    195: 
                    196: - setPointerScaling:(unsigned)numScalings data:(unsigned const *)scaleData
                    197: // Description:        Set the pointer scaling factors and thresholds from the int 
                    198: //             array and count passed in here. There should be 2*numScaling
                    199: //             ints in the scaleData array, packed with a threshold followed
                    200: //             by a scaling factor.
                    201: // Preconditions:
                    202: // *   deviceLock must not be held on entry
                    203: {
                    204:     int i;
                    205:     if ( numScalings > NX_MAXMOUSESCALINGS )
                    206:        numScalings = NX_MAXMOUSESCALINGS;      // Clamp range
                    207: 
                    208:     [deviceLock lock];
                    209: 
                    210: #if MACSCALE
                    211: // a pale imitation of CrsrDev.a
                    212:     {
                    213:        static int deviceSpeed[] = { 0x000000, 0x00713b, 0x010000, 0x044ec5, 0x0c0000, 0x16ec4f, 0x1d3b14, 0x227627, 0x7ffffff };
                    214:        static int cursorSpeed[] = { 0x000000, 0x006000, 0x010000, 0x108000, 0x5f0000, 0x8b0000, 0x948000, 0x960000, 0x0960000 };
                    215: 
                    216:        int     slope,j = 0;
                    217:        int     x;
                    218:        int     devSpeed, crsrSpeed;
                    219:        int     lastCrsrSpeed, nextCrsrSpeed;
                    220:        int     lastDeviceSpeed, nextDeviceSpeed;
                    221:        int     accl = 0x008000;
                    222: 
                    223: #if 0
                    224:        static int pram2Fixed[] =  { 0x000000, 0x002000, 0x005000, 0x008000, 0x00b000, 0x00e000, 0x010000, 0x010000 };
                    225:        unsigned char abyte;
                    226:        if( 0 == ReadNVRAM( 0x1300 + 8, 1, &abyte )) {
                    227:            abyte = 7 & (abyte >> 3);
                    228:            accl = pram2Fixed[ abyte ];
                    229:        }
                    230: #else
                    231:        int nxMax = (*(scaleData + (2 * numScalings) - 1)) - 1;
                    232:        if( nxMax > 18)
                    233:            nxMax = 18;
                    234:        accl = FixDiv( nxMax << 16, 18 << 16 );
                    235: #endif
                    236: kprintf( "Mouse acceleration = 0x%x\n", accl);
                    237: 
                    238:        // scale for device speed
                    239:        devSpeed = FixDiv( 90 << 16, resolution << 16 );                        // no vbl sync, so 90 autopolls /s
                    240:        // scale for cursor speed
                    241:        crsrSpeed = FixDiv( 72 << 16, resolution << 16 );                       // screen is 72 dpi
                    242: 
                    243:        nextCrsrSpeed = 0;
                    244:        nextDeviceSpeed = 0;
                    245: 
                    246:        // Precalculate fixed point scales. Not as accurate as MacOS, but no FixDiv() in handler
                    247:        for ( i = 1; i < MAXMAG; i++ )
                    248:        {
                    249:            x = FixMul( i << 16, devSpeed );
                    250:            if( x > deviceSpeed[j]) {
                    251:                lastCrsrSpeed = nextCrsrSpeed;
                    252:                lastDeviceSpeed = nextDeviceSpeed;
                    253:                j++;
                    254:                nextDeviceSpeed = deviceSpeed[j];
                    255:                // Interpolate by accl between y=x and y=acclTable(x) to get nextCrsrSpeed
                    256:                {
                    257:                    int factorCursor;
                    258:                    int factorDevice;
                    259:                    if( cursorSpeed[j] < nextDeviceSpeed) {
                    260:                        factorDevice = accl;
                    261:                        factorCursor = (0x10000 - accl);
                    262:                    } else {
                    263:                        factorCursor = accl;
                    264:                        factorDevice = (0x10000 - accl);
                    265:                    }
                    266:                    nextCrsrSpeed = FixMul( factorCursor, cursorSpeed[j] ) + FixMul( factorDevice, nextDeviceSpeed);
                    267:                }
                    268:                slope = FixDiv( nextCrsrSpeed - lastCrsrSpeed, nextDeviceSpeed - lastDeviceSpeed );
                    269:            }
                    270:            scaleValues[i] = FixDiv( FixMul( crsrSpeed, FixMul( slope, x - lastDeviceSpeed ) + lastCrsrSpeed), x);
                    271:        }
                    272:        scaleValues[0] = scaleValues[1];
                    273:        fractX = fractY = 0;
                    274:     }
                    275: #endif
                    276: 
                    277:     pointerScaling.numScaleLevels = numScalings;
                    278:     for ( i = 0; i < numScalings; ++i )
                    279:     {
                    280:        pointerScaling.scaleThresholds[i] = *scaleData++;
                    281:        pointerScaling.scaleFactors[i] = *scaleData++;
                    282:     }
                    283: 
                    284:     [deviceLock unlock];
                    285:     return self;
                    286: }
                    287: 
                    288: - pointerScaling:(unsigned *)numScalings data:(unsigned *)scaleData
                    289: // Description:        Return the pointer scaling factors and thresholds using the int 
                    290: //             array and count passed in here. On entry, *numScalings should
                    291: //             reflect the max number of scaling entries scaleData can hold.
                    292: {
                    293:     int i;
                    294: 
                    295:     [deviceLock lock];
                    296:     if ( *numScalings > pointerScaling.numScaleLevels )
                    297:        *numScalings = pointerScaling.numScaleLevels;   // Clamp range
                    298: 
                    299:     for ( i = 0; i < *numScalings; ++i )
                    300:     {
                    301:        *scaleData++ = pointerScaling.scaleThresholds[i];
                    302:        *scaleData++ = pointerScaling.scaleFactors[i];
                    303:     }
                    304:     [deviceLock unlock];
                    305:     return self;
                    306: }
                    307: 
                    308: - initPointer
                    309: // Description:        Perform setup work needed to find and configure our
                    310: //             PCPointer device. We have to tell it to send events to us.
                    311: // Preconditions:
                    312: // *   deviceLock must be held on entry
                    313: {
                    314:     if ((pointerDevice = [PCPointer activePointerDevice]) == nil) {
                    315:        IOLog("initPointer: Can't find active pointer device\n");
                    316:        return nil;
                    317:     }
                    318:     if ( [pointerDevice respondsTo:@selector(setEventTarget:)] )
                    319:        [pointerDevice setEventTarget: self];
                    320:     else {
                    321:        IOLog("initPointer: PCPointer0 does not respond to setEventTarget:\n");
                    322:        return nil;
                    323:     }
                    324:     [pointerDevice setEventTarget: self];
                    325:     return self;
                    326: }
                    327: 
                    328: - resetPointer
                    329: {
                    330:     [deviceLock lock];
                    331:     buttonMode = NX_OneButton;
                    332:     [deviceLock unlock];
                    333:     // setPointerScaling will try to acquire deviceLock, so release it first.
                    334:     [self setPointerScaling:
                    335:        ((sizeof dfltPointerScaling/sizeof dfltPointerScaling[0])/2)
                    336:        data:dfltPointerScaling];
                    337:     return self;
                    338: }
                    339: //
                    340: // END:                Implementation of Private EventSrcPCPointer methods
                    341: //
                    342: 
                    343: 
                    344: //
                    345: // BEGIN:      Implementation of PCPointerTarget protocol
                    346: //
                    347: - (void)dispatchPointerEvent:(PointerEvent *)event
                    348: // Description:        This method is the heart of event dispatching. The underlying
                    349: //             PCPointer object invokes this method with each event.
                    350: //             The event structure passed in by reference should not be freed.
                    351: {
                    352:     int buttons;
                    353:     int dx;
                    354:     int dy;
                    355:     unsigned tick;
                    356:     unsigned delta_t;
                    357:     int menuButton;
                    358: 
                    359:     [deviceLock lock];
                    360: 
                    361:     menuButton = (event->buttonCount > 1);
                    362:     buttons = 0;
                    363: 
                    364:     if (event->b0 == 0)
                    365:         buttons |= EV_LB;
                    366: 
                    367:     if( menuButton) {
                    368:        if ((event->b1 || event->b2 || event->b3) == 0)         // either down
                    369:            buttons |= EV_RB;
                    370:     }
                    371: 
                    372:     dx = event->dx;
                    373:     dy = event->dy;
                    374: 
                    375:     // Convert the nanosecond time into a tick time since boot.
                    376:     tick = EV_NS_TO_TICK(event->timeStamp);
                    377: 
                    378:     // Perform pointer acceleration computations
                    379:     if ( lastPointerEvent == 0 )
                    380:        delta_t = PC_PTR_DFLT_DELTA_T;
                    381:     else
                    382:        delta_t = tick - lastPointerEvent;
                    383: 
                    384:     [self scalePointerInX:&dx
                    385:        andY:&dy
                    386:        over:delta_t
                    387:        atRes:resolution];
                    388:     lastPointerEvent = tick;
                    389: 
                    390:     // Perform button tying and mapping.  This
                    391:     // stuff applies to relative posn devices (mice) only.
                    392:     if ( buttonMode == NX_OneButton )
                    393:     {
                    394:        if ( (buttons & (EV_LB|EV_RB)) != 0 )
                    395:            buttons = EV_LB;
                    396:     }
                    397:     else if ( menuButton && (buttonMode == NX_LeftButton) )    // Menus on left button. Swap!
                    398:     {
                    399:        int temp = 0;
                    400:        if ( buttons & EV_LB )
                    401:            temp = EV_RB;
                    402:        if ( buttons & EV_RB )
                    403:            temp |= EV_LB;
                    404:        buttons = temp;
                    405:     }
                    406:     [deviceLock unlock];
                    407: 
                    408:     [[self owner] relativePointerEvent:buttons
                    409:        deltaX:dx
                    410:        deltaY:dy
                    411:        atTime:event->timeStamp];
                    412: }
                    413: //
                    414: // END:                Implementation of PCPointerTarget protocol
                    415: //
                    416: 
                    417: 
                    418: //
                    419: // BEGIN:      Implementation of Exported EventSrcPCPointer methods
                    420: //
                    421: - init
                    422: // Description:        Basic initialization stuff.
                    423: // Preconditions:
                    424: // *   deviceLock must not be held on entry
                    425: {
                    426:     id rtn;
                    427: 
                    428:     if ( deviceLock == nil )
                    429:            deviceLock = [NXLock new];
                    430:     [deviceLock lock];
                    431:     [super init];
                    432:     pointerDevice = nil;
                    433:     buttonMode = NX_OneButton;
                    434: 
                    435:     rtn = [self initPointer];
                    436:     resolution = [pointerDevice getResolution];
                    437: 
                    438:     [deviceLock unlock];
                    439:     // setPointerScaling will try to acquire deviceLock, so release it first.
                    440:     [self setPointerScaling:
                    441:        ((sizeof dfltPointerScaling/sizeof dfltPointerScaling[0])/2)
                    442:        data:dfltPointerScaling];
                    443:     return rtn;
                    444: }
                    445: 
                    446: + probe
                    447: // Description:        This is our factory method. It is the IODevice probe
                    448: //             routine for psuedo drivers.
                    449: {
                    450:     if ( instance != nil )
                    451:        return instance;
                    452: 
                    453:     instance = [self alloc];
                    454: 
                    455:     [instance setName:"EventSrcPCPointer0"];
                    456:     [instance setDeviceKind:"EventSrcPCPointer"];
                    457:     
                    458:     if ( [instance init] == nil )
                    459:        [instance free];        // Zaps 'instance' on the way out
                    460: 
                    461:     return instance;
                    462: }
                    463: 
                    464: - free
                    465: // Description:        Go Away. Be careful when freeing the lock.
                    466: {
                    467:     id lock;
                    468: 
                    469:     [deviceLock lock];
                    470:     instance = nil;
                    471:     lock = deviceLock;
                    472:     deviceLock = nil;
                    473:     // Release pointer device, so we won't get any more events
                    474:     if ( pointerDevice != nil )
                    475:        [pointerDevice setEventTarget:nil];
                    476:     [lock unlock];
                    477:     [lock free];
                    478:     return [super free];
                    479: }
                    480: 
                    481: 
                    482: - (IOReturn)getIntValues:(unsigned *)parameterArray
                    483:        forParameter:(IOParameterName)parameterName
                    484:        count:(unsigned int *)count
                    485: {
                    486:     IOReturn r = IO_R_INVALID_ARG;
                    487:     NXEventSystemDevice *dp;
                    488:     unsigned maxCount = *count;
                    489:     unsigned *returnedCount = count;
                    490:     
                    491:     if ( strcmp( parameterName, EVSIOCMS ) == 0 )      // Pointer Scaling
                    492:     {
                    493:        parameterArray[EVSIOSMS_NSCALINGS] = (maxCount - 1)/2;
                    494:        [self pointerScaling:&parameterArray[EVSIOSMS_NSCALINGS]
                    495:            data:&parameterArray[EVSIOSMS_DATA]];
                    496:        *returnedCount = (parameterArray[EVSIOSMS_NSCALINGS] * 2) + 1;
                    497:        r = IO_R_SUCCESS;
                    498:     }
                    499:     else if ( strcmp( parameterName, EVSIOCMH ) == 0 ) // Pointer Handedness
                    500:     {
                    501:        if ( maxCount >= EVSIOCMH_SIZE )
                    502:        {
                    503:            *returnedCount = EVSIOCMH_SIZE;
                    504:            [deviceLock lock];
                    505:            parameterArray[0] = buttonMode;
                    506:            [deviceLock unlock];
                    507:            r = IO_R_SUCCESS;
                    508:        }
                    509:     }
                    510:     else if ( strcmp( parameterName, EVSIOINFO ) == 0 )        // Device info
                    511:     {
                    512:        dp = (NXEventSystemDevice *) &parameterArray[0];
                    513:        *returnedCount = 0;
                    514:        // No need to lock device since we're not even reading the structure.
                    515:        dp->interface = NX_EVS_DEVICE_INTERFACE_SERIAL_ACE;
                    516:        dp->dev_type = NX_EVS_DEVICE_TYPE_MOUSE;
                    517:        dp->interface_addr = 0;
                    518:        dp->id = 0;
                    519:        *returnedCount = sizeof (NXEventSystemDevice) / sizeof (int);
                    520:        r = IO_R_SUCCESS;
                    521:     }
                    522:     else
                    523:     {
                    524:        r = [super getIntValues:parameterArray
                    525:                   forParameter: parameterName
                    526:                   count : count];
                    527:        if (r == IO_R_UNSUPPORTED)
                    528:            r = IO_R_INVALID_ARG;
                    529:     }
                    530:     return r;
                    531: }
                    532: 
                    533: - (IOReturn)getCharValues:(unsigned char *)parameterArray
                    534:        forParameter:(IOParameterName)parameterName
                    535:        count:(unsigned int *)count
                    536: {
                    537:     return IO_R_INVALID_ARG;
                    538: }
                    539: 
                    540: - (IOReturn)setIntValues:(unsigned *)parameterArray
                    541:        forParameter:(IOParameterName)parameterName
                    542:        count:(unsigned int)count
                    543: {
                    544:     IOReturn r = IO_R_INVALID_ARG;
                    545:     int cnt;
                    546: 
                    547:     if ( strcmp( parameterName, EVSIOSMS ) == 0 )      // Pointer Scaling
                    548:     {
                    549:        cnt = (parameterArray[EVSIOSMS_NSCALINGS] * 2) + 1;
                    550:        if ( count <= EVSIOSMS_SIZE && cnt <= count )
                    551:        {
                    552:            [self setPointerScaling:parameterArray[EVSIOSMS_NSCALINGS]
                    553:                data:&parameterArray[EVSIOSMS_DATA]];
                    554:            r = IO_R_SUCCESS;
                    555:        }
                    556:     }
                    557:     else if ( strcmp( parameterName, EVSIOSMH ) == 0 ) // Pointer Handedness
                    558:     {
                    559:        if ( count == EVSIOSMH_SIZE )
                    560:        {
                    561:            [deviceLock lock];
                    562:            buttonMode = parameterArray[0];
                    563:            [deviceLock unlock];
                    564:            r = IO_R_SUCCESS;
                    565:        }
                    566:     }
                    567:     else if ( strcmp( parameterName, EVSIORMS ) == 0 )
                    568:     {
                    569:        r = IO_R_SUCCESS;
                    570:     }
                    571:     else
                    572:     {
                    573:        r = [super setIntValues:parameterArray
                    574:                   forParameter:parameterName
                    575:                   count : count];
                    576:        if (r == IO_R_UNSUPPORTED)
                    577:            r = IO_R_INVALID_ARG;
                    578:     }
                    579:     return r;
                    580: }
                    581: //
                    582: // END:                Implementation of Exported EventSrcPCPointer methods
                    583: //
                    584: @end

unix.superglobalmegacorp.com

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