Search
lxdream.org :: lxdream/src/maple/maple.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/maple/maple.c
changeset 1172:43eef98ff265
prev1072:d82e04e6d497
next1237:377077d10d62
author Nathan Keynes <nkeynes@lxdream.org>
date Wed May 11 08:45:31 2011 +1000 (11 years ago)
permissions -rw-r--r--
last change Fix returned maple packet length for VMU block reads and get-mem-info
Fix returned block-id in block reads to match the input block-id.
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 "dream.h"
    23 #include "mem.h"
    24 #include "asic.h"
    25 #include "maple.h"
    27 void maple_init( void );
    29 struct dreamcast_module maple_module = { "Maple", maple_init, NULL, NULL, NULL,
    30         NULL, NULL, NULL };
    32 struct maple_device_class *maple_device_classes[] = { 
    33         &controller_class, &keyboard_class, &lightgun_class, &mouse_class, &vmu_class, NULL };
    35 void maple_init( void )
    36 {
    38 }
    40 maple_device_t maple_new_device( const gchar *name )
    41 {
    42     maple_device_class_t clz = maple_get_device_class(name);
    43     if( clz != NULL ) {
    44         return clz->new_device();
    45     } 
    46     return NULL;
    47 }
    49 maple_device_class_t maple_get_device_class( const gchar *name )
    50 {
    51     int i;
    52     for( i=0; maple_device_classes[i] != NULL; i++ ) {
    53         if( g_strcasecmp(maple_device_classes[i]->name, name ) == 0 )
    54             return maple_device_classes[i];
    55     }
    56     return NULL;
    57 }
    59 const struct maple_device_class **maple_get_device_classes()
    60 {
    61     return (const struct maple_device_class **)maple_device_classes;
    62 }
    64 lxdream_config_group_t maple_get_device_config( maple_device_t dev )
    65 {
    66     if( dev->get_config == NULL )
    67         return NULL;
    68     return dev->get_config(dev);
    69 }
    71 /**
    72  * Input data looks like this:
    73  *    0: transfer control word
    74  *      0: length of data in words (not including 3 word header)
    75  *      1: low bit = lightgun mode
    76  *      2: low 2 bits = port # (0..3)
    77  *      3: 0x80 = last packet, 0x00 = normal packet
    78  *    4: output buffer address
    79  *    8: Command word
    80  *      8: command code
    81  *      9: destination address
    82  *     10: source address
    83  *     11: length of data in words (not including 3 word header)
    84  *   12: command-specific data
    85  */
    87 /**
    88  * array is [port][subperipheral], so [0][0] is main peripheral on port A,
    89  * [1][2] is the second subperipheral on port B and so on.
    90  */
    91 maple_device_t maple_devices[4][6];
    92 int maple_periph_mask[4];
    93 #define GETBYTE(n) ((uint32_t)(buf[n]))
    94 #define GETWORD(n) (*((uint32_t *)(buf+(n))))
    95 #define PUTBYTE(n,x) (buf[n] = (char)x)
    96 #define PUTWORD(n,x) (*((uint32_t *)(return_buf+(n))) = (x))
    98 maple_device_t maple_get_device( unsigned int port, unsigned int periph ) {
    99     if( port >= 4 )
   100         return NULL;
   101     if( periph >= 6 )
   102         return NULL;
   103     return maple_devices[port][periph];
   104 }
   106 void maple_handle_buffer( uint32_t address ) {
   107     unsigned char *buf = (unsigned char *)mem_get_region(address);
   108     if( buf == NULL ) {
   109         ERROR( "Invalid or unmapped buffer passed to maple (0x%08X)", address );
   110     } else {
   111         unsigned int last = 0;
   112         int i = 0, count;
   113         for( count=0; !last; count++ ) {
   114             unsigned int port, length, mode, periph, periph_id, out_length;
   115             unsigned int cmd, recv_addr, send_addr;
   116             uint32_t return_addr;
   117             unsigned char *return_buf;
   118             maple_device_t dev;
   120             last = GETBYTE(3) & 0x80; /* indicates last packet */
   121             port = GETBYTE(2) & 0x03;
   122             mode = GETBYTE(1) & 0x07;
   123             length = GETBYTE(0) & 0xFF;
   124             return_addr = GETWORD(4);
   126             switch( mode ) {
   127             case 2: /* lightgun */
   128                 dev = maple_devices[port][0];
   129                 if( dev != NULL && dev->start_gun != NULL ) {
   130                     dev->start_gun(dev);
   131                     return; // Pending
   132                 } else {
   133                     asic_event( EVENT_MAPLE_DMA );
   134                     return;
   135                 }
   136             case 7: /* skip */
   137                 buf += 4;
   138                 address +=4; 
   139                 continue;
   140             }
   141             if( (return_addr & 0x1C000000) != 0x0C000000 ) {
   142                 ERROR( "Bad return address in maple packet: %08X", return_addr );
   143                 break;
   144             }
   145             return_buf = mem_get_region(return_addr);
   146             cmd = GETBYTE(8);
   147             recv_addr = GETBYTE(9);
   148             send_addr = GETBYTE(10);
   149             /* Sanity checks */
   150             if( GETBYTE(11) != length ||
   151                     send_addr >> 6 != port ||
   152                     recv_addr >> 6 != port ||
   153                     return_buf == NULL ) {
   154                 ERROR( "Received bad packet: %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X",
   155                         buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
   156                         buf[8], buf[9], buf[10], buf[11] );
   157                 break;
   158             }
   159             periph = 0;
   160             periph_id = recv_addr & 0x3F;
   161             if( periph_id != 0x20 ) {
   162                 for( i=0;i<5;i++ ) {
   163                     if( periph_id == (1<<i) ) {
   164                         periph = i+1;
   165                         break;
   166                     }
   167                 }
   168                 if( periph == 0 ) { /* Bad setting */
   169                     /* ERROR */
   170                 }
   171             }
   173             dev = maple_devices[port][periph];
   174             if( dev == NULL ) {
   175                 /* no device attached */
   176                 *((uint32_t *)return_buf) = -1;
   177             } else {
   178                 int status, func;
   179                 unsigned int pt, phase, block, blkid;
   180                 out_length = 0;
   181                 switch( cmd ) {
   182                 case MAPLE_CMD_INFO:
   183                     status = MAPLE_RESP_INFO;
   184                     memcpy( return_buf+4, dev->ident, 112 );
   185                     out_length = 0x1C;
   186                     if( periph == 0 ) {
   187                         /* Identify command on the primary device also sets the
   188                          * bits in the address in the response according to the
   189                          * sub-peripherals present.
   190                          */
   191                         recv_addr &= 0xE0;
   192                         for( i=0; i<5; i++ ) {
   193                             if( maple_devices[port][i+1] != NULL ) {
   194                                 recv_addr |= (1<<i);
   195                             }
   196                         }
   197                     }
   198                     break;
   199                 case MAPLE_CMD_EXT_INFO:
   200                     status = MAPLE_RESP_EXT_INFO;
   201                     memcpy( return_buf+4, dev->ident, 192 );
   202                     out_length = 0x30;
   203                     break;
   204                 case MAPLE_CMD_RESET:
   205                     if( dev->reset == NULL )
   206                         status = MAPLE_RESP_ACK;
   207                     else status = dev->reset(dev);
   208                     break;
   209                 case MAPLE_CMD_SHUTDOWN:
   210                     if( dev->shutdown == NULL )
   211                         status = MAPLE_RESP_ACK;
   212                     else status = dev->shutdown(dev);
   213                     break;
   214                 case MAPLE_CMD_GET_COND:
   215                     func = GETWORD(12);
   216                     if( dev->get_condition == NULL )
   217                         status = MAPLE_ERR_CMD_UNKNOWN;
   218                     else status = dev->get_condition(dev, func,
   219                             return_buf+8,
   220                             &out_length );
   221                     if( status == 0 ) {
   222                         out_length++;
   223                         status = MAPLE_RESP_DATA;
   224                         PUTWORD(4,func);
   225                     }
   226                     break;
   227                 case MAPLE_CMD_SET_COND:
   228                     func = GETWORD(12);
   229                     if( dev->set_condition == NULL )
   230                         status = MAPLE_ERR_CMD_UNKNOWN;
   231                     else status = dev->set_condition(dev, func,
   232                             buf+16,
   233                             length-1);
   234                     if( status == 0 )
   235                         status = MAPLE_RESP_ACK;
   236                     break;
   237                 case MAPLE_CMD_MEM_INFO:
   238                     func = GETWORD(12);
   239                     pt = GETWORD(16);
   240                     if( dev->get_memory_info == NULL ) 
   241                         status = MAPLE_ERR_CMD_UNKNOWN;
   242                     else status = dev->get_memory_info(dev,func, pt, return_buf+8, &out_length);
   243                     if( status == 0 ) {
   244                         out_length++;
   245                         status = MAPLE_RESP_DATA;
   246                         PUTWORD(4,func);
   247                     }
   248                     break;
   249                 case MAPLE_CMD_READ_BLOCK:
   250                     func = GETWORD(12);
   251                     pt = GETBYTE(16);
   252                     phase = GETBYTE(17);
   253                     block = (GETBYTE(18)<<8) | GETBYTE(19);
   254                     blkid = GETWORD(16);
   255                     if( dev->read_block == NULL )
   256                         status = MAPLE_ERR_CMD_UNKNOWN;
   257                     else status = dev->read_block(dev, func, pt, block, phase,
   258                             return_buf+12,
   259                             &out_length );
   260                     if( status == 0 ) {
   261                         status = MAPLE_RESP_DATA;
   262                         out_length += 2;
   263                         PUTWORD(4,func);
   264                         PUTWORD(8,blkid);
   265                     }
   266                     break;
   267                 case MAPLE_CMD_WRITE_BLOCK:
   268                     func = GETWORD(12);
   269                     pt = GETBYTE(16);
   270                     phase = GETBYTE(17);
   271                     block = (GETBYTE(18)<<8) | GETBYTE(19);
   272                     if( dev->write_block == NULL )
   273                         status = MAPLE_ERR_CMD_UNKNOWN;
   274                     else {
   275                         status = dev->write_block(dev, func, pt, block, phase, 
   276                                 buf+20, length-2);
   277                         if( status == 0 )
   278                             status = MAPLE_RESP_ACK;
   279                     }
   280                     break;
   281                 case MAPLE_CMD_SYNC_BLOCK:
   282                     func = GETWORD(12);
   283                     pt = GETBYTE(16);
   284                     phase = GETBYTE(17);
   285                     block = (GETBYTE(18)<<8) | GETBYTE(19);
   286                     /* TODO: something? */
   287                     status = MAPLE_RESP_ACK;
   288                     break;
   289                 default:
   290                     status = MAPLE_ERR_CMD_UNKNOWN;
   291                 }
   292                 return_buf[0] = status;
   293                 return_buf[1] = send_addr;
   294                 return_buf[2] = recv_addr;
   295                 if( periph == 0 )
   296                     return_buf[2] |= maple_periph_mask[port];
   297                 return_buf[3] = out_length;
   298             }
   299             buf += 12 + (length<<2);
   300             address += 12 + (length<<2);
   301         }
   302         asic_event( EVENT_MAPLE_DMA );
   303     }
   304 }
   306 void maple_attach_device( maple_device_t dev, unsigned int port,
   307                           unsigned int periph ) {
   308     assert( port < 4 );
   309     assert( periph < 6 );
   311     if( maple_devices[port][periph] != NULL ) {
   312         /* Detach existing peripheral first */
   313         maple_detach_device( port, periph );
   314     }
   316     maple_devices[port][periph] = dev;
   317     if( periph != 0 )
   318         maple_periph_mask[port] |= (1<<(periph-1));
   319     else maple_periph_mask[port] |= 0x20;
   320     if( dev->attach != NULL ) {
   321         dev->attach( dev );
   322     }
   323 }
   325 void maple_detach_device( unsigned int port, unsigned int periph ) {
   326     assert( port < 4 );
   327     assert( periph < 6 );
   329     maple_device_t dev = maple_devices[port][periph];
   330     if( dev == NULL ) /* already detached */
   331         return;
   332     maple_devices[port][periph] = NULL;
   333     if( dev->detach != NULL ) {
   334         dev->detach(dev);
   335     }
   336     if( dev->destroy != NULL ) {
   337         dev->destroy(dev);
   338     }
   339     if( periph == 0 ) {
   340         /* If we detach the main peripheral, we also have to detach all the
   341          * subperipherals, or the system could get quite confused
   342          */
   343         int i;
   344         maple_periph_mask[port] = 0;
   345         for( i=1; i<6; i++ ) {
   346             maple_detach_device(port,i);
   347         }
   348     } else {
   349         maple_periph_mask[port] &= (~(1<<(periph-1)));
   350     }
   352 }
   354 void maple_detach_all() {
   355     int i, j;
   356     for( i=0; i<4; i++ ) {
   357         for( j=0; j<6; j++ ) {
   358             if( maple_devices[i][j] != NULL ) {
   359                 maple_device_t dev = maple_devices[i][j];
   360                 if( dev->detach != NULL )
   361                     dev->detach(dev);
   362                 if( dev->destroy != NULL )
   363                     dev->destroy(dev);
   364             }
   365         }
   366         maple_periph_mask[i] = 0;
   367     }
   368 }
   370 void maple_reattach_all() 
   371 {
   372     int i, j;
   373     for( i=0; i<4; i++ ) {
   374         for( j=0; j<6; j++ ) {
   375             if( maple_devices[i][j] != NULL ) {
   376                 maple_device_t dev = maple_devices[i][j];
   377                 if( dev->detach != NULL ) 
   378                     dev->detach(dev);
   379                 if( dev->attach != NULL )
   380                     dev->attach(dev);
   381             }
   382         }
   383     }
   384 }
   386 gboolean maple_should_grab()
   387 {
   388     int mode = MAPLE_GRAB_DONTCARE;
   389     int i,j;
   390     for( i=0; i<4; i++ ) {
   391         for( j=0; j<6; j++ ) {
   392             if( maple_devices[i][j] != NULL ) {
   393                 maple_device_t dev = maple_devices[i][j];
   394                 if( (dev->device_class->flags&MAPLE_GRAB_MASK) > mode ) {
   395                     mode = dev->device_class->flags & MAPLE_GRAB_MASK;
   396                 }
   397             }
   398         }
   399     }
   400     return mode == MAPLE_GRAB_YES;
   401 }
   403 void maple_default_destroy( maple_device_t mdev )
   404 {
   405     free(mdev);
   406 }
.