Search
lxdream.org :: lxdream/src/maple/maple.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/maple/maple.c
changeset 1237:377077d10d62
prev1172:43eef98ff265
next1261:644f66135e94
author nkeynes
date Sat Feb 25 09:56:10 2012 +1000 (8 years ago)
permissions -rw-r--r--
last change Add a 200us delay time for Maple requests, to avoid problems with games that
continually poll maple.

Patch by guinux, thanks!
file annotate diff log raw
nkeynes@31
     1
/**
nkeynes@561
     2
 * $Id$
nkeynes@31
     3
 *
nkeynes@31
     4
 * Implements the core Maple bus, including DMA transfers to and from the bus.
nkeynes@31
     5
 *
nkeynes@31
     6
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@31
     7
 *
nkeynes@31
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@31
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@31
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@31
    11
 * (at your option) any later version.
nkeynes@31
    12
 *
nkeynes@31
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@31
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@31
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@31
    16
 * GNU General Public License for more details.
nkeynes@31
    17
 */
nkeynes@35
    18
#define MODULE maple_module
nkeynes@31
    19
nkeynes@1
    20
#include <assert.h>
nkeynes@480
    21
#include <glib/gstrfuncs.h>
nkeynes@1237
    22
#include "eventq.h"
nkeynes@1
    23
#include "dream.h"
nkeynes@1
    24
#include "mem.h"
nkeynes@1
    25
#include "asic.h"
nkeynes@1
    26
#include "maple.h"
nkeynes@1
    27
nkeynes@15
    28
void maple_init( void );
nkeynes@15
    29
nkeynes@15
    30
struct dreamcast_module maple_module = { "Maple", maple_init, NULL, NULL, NULL,
nkeynes@736
    31
        NULL, NULL, NULL };
nkeynes@15
    32
nkeynes@608
    33
struct maple_device_class *maple_device_classes[] = { 
nkeynes@1034
    34
        &controller_class, &keyboard_class, &lightgun_class, &mouse_class, &vmu_class, NULL };
nkeynes@144
    35
nkeynes@1237
    36
/**
nkeynes@1237
    37
 * Fire interrupt to notify the completion of DMA transfer
nkeynes@1237
    38
 */
nkeynes@1237
    39
static void maple_event_handler( int eventid )
nkeynes@1237
    40
{
nkeynes@1237
    41
    MMIO_WRITE( ASIC, MAPLE_STATE, 0 );
nkeynes@1237
    42
    asic_event( EVENT_MAPLE_DMA );
nkeynes@1237
    43
}
nkeynes@1237
    44
nkeynes@15
    45
void maple_init( void )
nkeynes@15
    46
{
nkeynes@1237
    47
    register_event_callback( EVENT_MAPLE_DMA, maple_event_handler );
nkeynes@15
    48
}
nkeynes@15
    49
nkeynes@144
    50
maple_device_t maple_new_device( const gchar *name )
nkeynes@144
    51
{
nkeynes@447
    52
    maple_device_class_t clz = maple_get_device_class(name);
nkeynes@447
    53
    if( clz != NULL ) {
nkeynes@736
    54
        return clz->new_device();
nkeynes@447
    55
    } 
nkeynes@447
    56
    return NULL;
nkeynes@447
    57
}
nkeynes@447
    58
nkeynes@480
    59
maple_device_class_t maple_get_device_class( const gchar *name )
nkeynes@447
    60
{
nkeynes@144
    61
    int i;
nkeynes@144
    62
    for( i=0; maple_device_classes[i] != NULL; i++ ) {
nkeynes@736
    63
        if( g_strcasecmp(maple_device_classes[i]->name, name ) == 0 )
nkeynes@736
    64
            return maple_device_classes[i];
nkeynes@144
    65
    }
nkeynes@144
    66
    return NULL;
nkeynes@144
    67
}
nkeynes@144
    68
nkeynes@447
    69
const struct maple_device_class **maple_get_device_classes()
nkeynes@447
    70
{
nkeynes@480
    71
    return (const struct maple_device_class **)maple_device_classes;
nkeynes@447
    72
}
nkeynes@447
    73
nkeynes@1072
    74
lxdream_config_group_t maple_get_device_config( maple_device_t dev )
nkeynes@144
    75
{
nkeynes@144
    76
    if( dev->get_config == NULL )
nkeynes@736
    77
        return NULL;
nkeynes@144
    78
    return dev->get_config(dev);
nkeynes@144
    79
}
nkeynes@144
    80
nkeynes@2
    81
/**
nkeynes@2
    82
 * Input data looks like this:
nkeynes@2
    83
 *    0: transfer control word
nkeynes@2
    84
 *      0: length of data in words (not including 3 word header)
nkeynes@2
    85
 *      1: low bit = lightgun mode
nkeynes@2
    86
 *      2: low 2 bits = port # (0..3)
nkeynes@2
    87
 *      3: 0x80 = last packet, 0x00 = normal packet
nkeynes@2
    88
 *    4: output buffer address
nkeynes@2
    89
 *    8: Command word
nkeynes@2
    90
 *      8: command code
nkeynes@2
    91
 *      9: destination address
nkeynes@2
    92
 *     10: source address
nkeynes@2
    93
 *     11: length of data in words (not including 3 word header)
nkeynes@2
    94
 *   12: command-specific data
nkeynes@2
    95
 */
nkeynes@2
    96
nkeynes@2
    97
/**
nkeynes@2
    98
 * array is [port][subperipheral], so [0][0] is main peripheral on port A,
nkeynes@2
    99
 * [1][2] is the second subperipheral on port B and so on.
nkeynes@2
   100
 */
nkeynes@2
   101
maple_device_t maple_devices[4][6];
nkeynes@2
   102
int maple_periph_mask[4];
nkeynes@2
   103
#define GETBYTE(n) ((uint32_t)(buf[n]))
nkeynes@2
   104
#define GETWORD(n) (*((uint32_t *)(buf+(n))))
nkeynes@2
   105
#define PUTBYTE(n,x) (buf[n] = (char)x)
nkeynes@2
   106
#define PUTWORD(n,x) (*((uint32_t *)(return_buf+(n))) = (x))
nkeynes@2
   107
nkeynes@144
   108
maple_device_t maple_get_device( unsigned int port, unsigned int periph ) {
nkeynes@144
   109
    if( port >= 4 )
nkeynes@736
   110
        return NULL;
nkeynes@144
   111
    if( periph >= 6 )
nkeynes@736
   112
        return NULL;
nkeynes@144
   113
    return maple_devices[port][periph];
nkeynes@144
   114
}
nkeynes@144
   115
nkeynes@1
   116
void maple_handle_buffer( uint32_t address ) {
nkeynes@2
   117
    unsigned char *buf = (unsigned char *)mem_get_region(address);
nkeynes@1
   118
    if( buf == NULL ) {
nkeynes@1
   119
        ERROR( "Invalid or unmapped buffer passed to maple (0x%08X)", address );
nkeynes@1
   120
    } else {
nkeynes@2
   121
        unsigned int last = 0;
nkeynes@2
   122
        int i = 0, count;
nkeynes@2
   123
        for( count=0; !last; count++ ) {
nkeynes@494
   124
            unsigned int port, length, mode, periph, periph_id, out_length;
nkeynes@2
   125
            unsigned int cmd, recv_addr, send_addr;
nkeynes@2
   126
            uint32_t return_addr;
nkeynes@2
   127
            unsigned char *return_buf;
nkeynes@850
   128
            maple_device_t dev;
nkeynes@736
   129
nkeynes@2
   130
            last = GETBYTE(3) & 0x80; /* indicates last packet */
nkeynes@2
   131
            port = GETBYTE(2) & 0x03;
nkeynes@494
   132
            mode = GETBYTE(1) & 0x07;
nkeynes@2
   133
            length = GETBYTE(0) & 0xFF;
nkeynes@2
   134
            return_addr = GETWORD(4);
nkeynes@447
   135
nkeynes@850
   136
            switch( mode ) {
nkeynes@850
   137
            case 2: /* lightgun */
nkeynes@850
   138
                dev = maple_devices[port][0];
nkeynes@850
   139
                if( dev != NULL && dev->start_gun != NULL ) {
nkeynes@850
   140
                    dev->start_gun(dev);
nkeynes@850
   141
                    return; // Pending
nkeynes@850
   142
                } else {
nkeynes@1237
   143
                    /* FIXME: Determine how long maple IO really takes to process, 
nkeynes@1237
   144
                     * which is probably a function of the number of requests.
nkeynes@1237
   145
                     * For now, just use 0.2ms as a reasonable value.
nkeynes@1237
   146
                     */
nkeynes@1237
   147
                    event_schedule( EVENT_MAPLE_DMA, 200000 );
nkeynes@850
   148
                    return;
nkeynes@850
   149
                }
nkeynes@850
   150
            case 7: /* skip */
nkeynes@736
   151
                buf += 4;
nkeynes@850
   152
                address +=4; 
nkeynes@736
   153
                continue;
nkeynes@736
   154
            }
nkeynes@447
   155
            if( (return_addr & 0x1C000000) != 0x0C000000 ) {
nkeynes@736
   156
                ERROR( "Bad return address in maple packet: %08X", return_addr );
nkeynes@736
   157
                break;
nkeynes@2
   158
            }
nkeynes@2
   159
            return_buf = mem_get_region(return_addr);
nkeynes@2
   160
            cmd = GETBYTE(8);
nkeynes@2
   161
            recv_addr = GETBYTE(9);
nkeynes@2
   162
            send_addr = GETBYTE(10);
nkeynes@2
   163
            /* Sanity checks */
nkeynes@2
   164
            if( GETBYTE(11) != length ||
nkeynes@736
   165
                    send_addr >> 6 != port ||
nkeynes@736
   166
                    recv_addr >> 6 != port ||
nkeynes@736
   167
                    return_buf == NULL ) {
nkeynes@736
   168
                ERROR( "Received bad packet: %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X",
nkeynes@736
   169
                        buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
nkeynes@736
   170
                        buf[8], buf[9], buf[10], buf[11] );
nkeynes@736
   171
                break;
nkeynes@2
   172
            }
nkeynes@2
   173
            periph = 0;
nkeynes@2
   174
            periph_id = recv_addr & 0x3F;
nkeynes@2
   175
            if( periph_id != 0x20 ) {
nkeynes@2
   176
                for( i=0;i<5;i++ ) {
nkeynes@2
   177
                    if( periph_id == (1<<i) ) {
nkeynes@2
   178
                        periph = i+1;
nkeynes@2
   179
                        break;
nkeynes@2
   180
                    }
nkeynes@2
   181
                }
nkeynes@2
   182
                if( periph == 0 ) { /* Bad setting */
nkeynes@2
   183
                    /* ERROR */
nkeynes@2
   184
                }
nkeynes@2
   185
            }
nkeynes@2
   186
nkeynes@850
   187
            dev = maple_devices[port][periph];
nkeynes@2
   188
            if( dev == NULL ) {
nkeynes@2
   189
                /* no device attached */
nkeynes@2
   190
                *((uint32_t *)return_buf) = -1;
nkeynes@2
   191
            } else {
nkeynes@1034
   192
                int status, func;
nkeynes@1172
   193
                unsigned int pt, phase, block, blkid;
nkeynes@2
   194
                out_length = 0;
nkeynes@2
   195
                switch( cmd ) {
nkeynes@736
   196
                case MAPLE_CMD_INFO:
nkeynes@736
   197
                    status = MAPLE_RESP_INFO;
nkeynes@736
   198
                    memcpy( return_buf+4, dev->ident, 112 );
nkeynes@736
   199
                    out_length = 0x1C;
nkeynes@1034
   200
                    if( periph == 0 ) {
nkeynes@1034
   201
                        /* Identify command on the primary device also sets the
nkeynes@1034
   202
                         * bits in the address in the response according to the
nkeynes@1034
   203
                         * sub-peripherals present.
nkeynes@1034
   204
                         */
nkeynes@1034
   205
                        recv_addr &= 0xE0;
nkeynes@1034
   206
                        for( i=0; i<5; i++ ) {
nkeynes@1034
   207
                            if( maple_devices[port][i+1] != NULL ) {
nkeynes@1034
   208
                                recv_addr |= (1<<i);
nkeynes@1034
   209
                            }
nkeynes@1034
   210
                        }
nkeynes@1034
   211
                    }
nkeynes@736
   212
                    break;
nkeynes@736
   213
                case MAPLE_CMD_EXT_INFO:
nkeynes@736
   214
                    status = MAPLE_RESP_EXT_INFO;
nkeynes@736
   215
                    memcpy( return_buf+4, dev->ident, 192 );
nkeynes@736
   216
                    out_length = 0x30;
nkeynes@736
   217
                    break;
nkeynes@736
   218
                case MAPLE_CMD_RESET:
nkeynes@736
   219
                    if( dev->reset == NULL )
nkeynes@736
   220
                        status = MAPLE_RESP_ACK;
nkeynes@736
   221
                    else status = dev->reset(dev);
nkeynes@736
   222
                    break;
nkeynes@736
   223
                case MAPLE_CMD_SHUTDOWN:
nkeynes@736
   224
                    if( dev->shutdown == NULL )
nkeynes@736
   225
                        status = MAPLE_RESP_ACK;
nkeynes@736
   226
                    else status = dev->shutdown(dev);
nkeynes@736
   227
                    break;
nkeynes@736
   228
                case MAPLE_CMD_GET_COND:
nkeynes@736
   229
                    func = GETWORD(12);
nkeynes@736
   230
                    if( dev->get_condition == NULL )
nkeynes@736
   231
                        status = MAPLE_ERR_CMD_UNKNOWN;
nkeynes@736
   232
                    else status = dev->get_condition(dev, func,
nkeynes@736
   233
                            return_buf+8,
nkeynes@736
   234
                            &out_length );
nkeynes@736
   235
                    if( status == 0 ) {
nkeynes@736
   236
                        out_length++;
nkeynes@736
   237
                        status = MAPLE_RESP_DATA;
nkeynes@736
   238
                        PUTWORD(4,func);
nkeynes@736
   239
                    }
nkeynes@736
   240
                    break;
nkeynes@736
   241
                case MAPLE_CMD_SET_COND:
nkeynes@736
   242
                    func = GETWORD(12);
nkeynes@736
   243
                    if( dev->set_condition == NULL )
nkeynes@736
   244
                        status = MAPLE_ERR_CMD_UNKNOWN;
nkeynes@736
   245
                    else status = dev->set_condition(dev, func,
nkeynes@736
   246
                            buf+16,
nkeynes@1034
   247
                            length-1);
nkeynes@736
   248
                    if( status == 0 )
nkeynes@736
   249
                        status = MAPLE_RESP_ACK;
nkeynes@736
   250
                    break;
nkeynes@1034
   251
                case MAPLE_CMD_MEM_INFO:
nkeynes@1034
   252
                    func = GETWORD(12);
nkeynes@1034
   253
                    pt = GETWORD(16);
nkeynes@1034
   254
                    if( dev->get_memory_info == NULL ) 
nkeynes@1034
   255
                        status = MAPLE_ERR_CMD_UNKNOWN;
nkeynes@1034
   256
                    else status = dev->get_memory_info(dev,func, pt, return_buf+8, &out_length);
nkeynes@1034
   257
                    if( status == 0 ) {
nkeynes@1172
   258
                        out_length++;
nkeynes@1034
   259
                        status = MAPLE_RESP_DATA;
nkeynes@1034
   260
                        PUTWORD(4,func);
nkeynes@1034
   261
                    }
nkeynes@1034
   262
                    break;
nkeynes@736
   263
                case MAPLE_CMD_READ_BLOCK:
nkeynes@736
   264
                    func = GETWORD(12);
nkeynes@1034
   265
                    pt = GETBYTE(16);
nkeynes@1034
   266
                    phase = GETBYTE(17);
nkeynes@1034
   267
                    block = (GETBYTE(18)<<8) | GETBYTE(19);
nkeynes@1172
   268
                    blkid = GETWORD(16);
nkeynes@736
   269
                    if( dev->read_block == NULL )
nkeynes@736
   270
                        status = MAPLE_ERR_CMD_UNKNOWN;
nkeynes@1034
   271
                    else status = dev->read_block(dev, func, pt, block, phase,
nkeynes@736
   272
                            return_buf+12,
nkeynes@736
   273
                            &out_length );
nkeynes@736
   274
                    if( status == 0 ) {
nkeynes@736
   275
                        status = MAPLE_RESP_DATA;
nkeynes@1172
   276
                        out_length += 2;
nkeynes@736
   277
                        PUTWORD(4,func);
nkeynes@1172
   278
                        PUTWORD(8,blkid);
nkeynes@736
   279
                    }
nkeynes@736
   280
                    break;
nkeynes@736
   281
                case MAPLE_CMD_WRITE_BLOCK:
nkeynes@736
   282
                    func = GETWORD(12);
nkeynes@1034
   283
                    pt = GETBYTE(16);
nkeynes@1034
   284
                    phase = GETBYTE(17);
nkeynes@1034
   285
                    block = (GETBYTE(18)<<8) | GETBYTE(19);
nkeynes@736
   286
                    if( dev->write_block == NULL )
nkeynes@736
   287
                        status = MAPLE_ERR_CMD_UNKNOWN;
nkeynes@736
   288
                    else {
nkeynes@1034
   289
                        status = dev->write_block(dev, func, pt, block, phase, 
nkeynes@1034
   290
                                buf+20, length-2);
nkeynes@2
   291
                        if( status == 0 )
nkeynes@2
   292
                            status = MAPLE_RESP_ACK;
nkeynes@736
   293
                    }
nkeynes@736
   294
                    break;
nkeynes@1034
   295
                case MAPLE_CMD_SYNC_BLOCK:
nkeynes@1034
   296
                    func = GETWORD(12);
nkeynes@1034
   297
                    pt = GETBYTE(16);
nkeynes@1034
   298
                    phase = GETBYTE(17);
nkeynes@1034
   299
                    block = (GETBYTE(18)<<8) | GETBYTE(19);
nkeynes@1034
   300
                    /* TODO: something? */
nkeynes@1034
   301
                    status = MAPLE_RESP_ACK;
nkeynes@1034
   302
                    break;
nkeynes@736
   303
                default:
nkeynes@736
   304
                    status = MAPLE_ERR_CMD_UNKNOWN;
nkeynes@2
   305
                }
nkeynes@2
   306
                return_buf[0] = status;
nkeynes@2
   307
                return_buf[1] = send_addr;
nkeynes@2
   308
                return_buf[2] = recv_addr;
nkeynes@2
   309
                if( periph == 0 )
nkeynes@2
   310
                    return_buf[2] |= maple_periph_mask[port];
nkeynes@2
   311
                return_buf[3] = out_length;
nkeynes@2
   312
            }
nkeynes@2
   313
            buf += 12 + (length<<2);
nkeynes@736
   314
            address += 12 + (length<<2);
nkeynes@2
   315
        }
nkeynes@1237
   316
        event_schedule( EVENT_MAPLE_DMA, 200000 );
nkeynes@1
   317
    }
nkeynes@1
   318
}
nkeynes@2
   319
nkeynes@2
   320
void maple_attach_device( maple_device_t dev, unsigned int port,
nkeynes@2
   321
                          unsigned int periph ) {
nkeynes@2
   322
    assert( port < 4 );
nkeynes@2
   323
    assert( periph < 6 );
nkeynes@2
   324
nkeynes@2
   325
    if( maple_devices[port][periph] != NULL ) {
nkeynes@2
   326
        /* Detach existing peripheral first */
nkeynes@2
   327
        maple_detach_device( port, periph );
nkeynes@2
   328
    }
nkeynes@736
   329
nkeynes@2
   330
    maple_devices[port][periph] = dev;
nkeynes@2
   331
    if( periph != 0 )
nkeynes@2
   332
        maple_periph_mask[port] |= (1<<(periph-1));
nkeynes@2
   333
    else maple_periph_mask[port] |= 0x20;
nkeynes@2
   334
    if( dev->attach != NULL ) {
nkeynes@2
   335
        dev->attach( dev );
nkeynes@2
   336
    }
nkeynes@2
   337
}
nkeynes@2
   338
nkeynes@2
   339
void maple_detach_device( unsigned int port, unsigned int periph ) {
nkeynes@2
   340
    assert( port < 4 );
nkeynes@2
   341
    assert( periph < 6 );
nkeynes@2
   342
nkeynes@2
   343
    maple_device_t dev = maple_devices[port][periph];
nkeynes@2
   344
    if( dev == NULL ) /* already detached */
nkeynes@2
   345
        return;
nkeynes@2
   346
    maple_devices[port][periph] = NULL;
nkeynes@2
   347
    if( dev->detach != NULL ) {
nkeynes@2
   348
        dev->detach(dev);
nkeynes@2
   349
    }
nkeynes@447
   350
    if( dev->destroy != NULL ) {
nkeynes@736
   351
        dev->destroy(dev);
nkeynes@447
   352
    }
nkeynes@2
   353
    if( periph == 0 ) {
nkeynes@2
   354
        /* If we detach the main peripheral, we also have to detach all the
nkeynes@2
   355
         * subperipherals, or the system could get quite confused
nkeynes@2
   356
         */
nkeynes@2
   357
        int i;
nkeynes@2
   358
        maple_periph_mask[port] = 0;
nkeynes@2
   359
        for( i=1; i<6; i++ ) {
nkeynes@2
   360
            maple_detach_device(port,i);
nkeynes@2
   361
        }
nkeynes@2
   362
    } else {
nkeynes@2
   363
        maple_periph_mask[port] &= (~(1<<(periph-1)));
nkeynes@2
   364
    }
nkeynes@736
   365
nkeynes@2
   366
}
nkeynes@144
   367
nkeynes@144
   368
void maple_detach_all() {
nkeynes@144
   369
    int i, j;
nkeynes@144
   370
    for( i=0; i<4; i++ ) {
nkeynes@736
   371
        for( j=0; j<6; j++ ) {
nkeynes@736
   372
            if( maple_devices[i][j] != NULL ) {
nkeynes@736
   373
                maple_device_t dev = maple_devices[i][j];
nkeynes@736
   374
                if( dev->detach != NULL )
nkeynes@736
   375
                    dev->detach(dev);
nkeynes@736
   376
                if( dev->destroy != NULL )
nkeynes@736
   377
                    dev->destroy(dev);
nkeynes@736
   378
            }
nkeynes@736
   379
        }
nkeynes@736
   380
        maple_periph_mask[i] = 0;
nkeynes@144
   381
    }
nkeynes@144
   382
}
nkeynes@144
   383
nkeynes@838
   384
void maple_reattach_all() 
nkeynes@838
   385
{
nkeynes@144
   386
    int i, j;
nkeynes@144
   387
    for( i=0; i<4; i++ ) {
nkeynes@736
   388
        for( j=0; j<6; j++ ) {
nkeynes@736
   389
            if( maple_devices[i][j] != NULL ) {
nkeynes@736
   390
                maple_device_t dev = maple_devices[i][j];
nkeynes@736
   391
                if( dev->detach != NULL ) 
nkeynes@736
   392
                    dev->detach(dev);
nkeynes@736
   393
                if( dev->attach != NULL )
nkeynes@736
   394
                    dev->attach(dev);
nkeynes@736
   395
            }
nkeynes@736
   396
        }
nkeynes@144
   397
    }
nkeynes@144
   398
}
nkeynes@608
   399
nkeynes@838
   400
gboolean maple_should_grab()
nkeynes@838
   401
{
nkeynes@838
   402
    int mode = MAPLE_GRAB_DONTCARE;
nkeynes@838
   403
    int i,j;
nkeynes@838
   404
    for( i=0; i<4; i++ ) {
nkeynes@838
   405
        for( j=0; j<6; j++ ) {
nkeynes@838
   406
            if( maple_devices[i][j] != NULL ) {
nkeynes@838
   407
                maple_device_t dev = maple_devices[i][j];
nkeynes@1034
   408
                if( (dev->device_class->flags&MAPLE_GRAB_MASK) > mode ) {
nkeynes@1034
   409
                    mode = dev->device_class->flags & MAPLE_GRAB_MASK;
nkeynes@838
   410
                }
nkeynes@838
   411
            }
nkeynes@838
   412
        }
nkeynes@838
   413
    }
nkeynes@838
   414
    return mode == MAPLE_GRAB_YES;
nkeynes@838
   415
}
nkeynes@838
   416
nkeynes@608
   417
void maple_default_destroy( maple_device_t mdev )
nkeynes@608
   418
{
nkeynes@608
   419
    free(mdev);
nkeynes@608
   420
}
.