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