2009-03-04 00:31:04 +00:00
/* vmu-flash.c
* Driver for SEGA Dreamcast Visual Memory Unit
*
* Copyright ( c ) Adrian McMenamin 2002 - 2009
* Copyright ( c ) Paul Mundt 2001
*
* Licensed under version 2 of the
* GNU General Public Licence
*/
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/delay.h>
# include <linux/maple.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
struct vmu_cache {
unsigned char * buffer ; /* Cache */
unsigned int block ; /* Which block was cached */
unsigned long jiffies_atc ; /* When was it cached? */
int valid ;
} ;
struct mdev_part {
struct maple_device * mdev ;
int partition ;
} ;
struct vmupart {
u16 user_blocks ;
u16 root_block ;
u16 numblocks ;
char * name ;
struct vmu_cache * pcache ;
} ;
struct memcard {
u16 tempA ;
u16 tempB ;
u32 partitions ;
u32 blocklen ;
u32 writecnt ;
u32 readcnt ;
u32 removeable ;
int partition ;
int read ;
unsigned char * blockread ;
struct vmupart * parts ;
struct mtd_info * mtd ;
} ;
struct vmu_block {
unsigned int num ; /* block number */
unsigned int ofs ; /* block offset */
} ;
static struct vmu_block * ofs_to_block ( unsigned long src_ofs ,
struct mtd_info * mtd , int partition )
{
struct vmu_block * vblock ;
struct maple_device * mdev ;
struct memcard * card ;
struct mdev_part * mpart ;
int num ;
mpart = mtd - > priv ;
mdev = mpart - > mdev ;
card = maple_get_drvdata ( mdev ) ;
if ( src_ofs > = card - > parts [ partition ] . numblocks * card - > blocklen )
goto failed ;
num = src_ofs / card - > blocklen ;
if ( num > card - > parts [ partition ] . numblocks )
goto failed ;
vblock = kmalloc ( sizeof ( struct vmu_block ) , GFP_KERNEL ) ;
if ( ! vblock )
goto failed ;
vblock - > num = num ;
vblock - > ofs = src_ofs % card - > blocklen ;
return vblock ;
failed :
return NULL ;
}
/* Maple bus callback function for reads */
static void vmu_blockread ( struct mapleq * mq )
{
struct maple_device * mdev ;
struct memcard * card ;
mdev = mq - > dev ;
card = maple_get_drvdata ( mdev ) ;
/* copy the read in data */
if ( unlikely ( ! card - > blockread ) )
return ;
memcpy ( card - > blockread , mq - > recvbuf - > buf + 12 ,
card - > blocklen / card - > readcnt ) ;
}
/* Interface with maple bus to read blocks
* caching the results so that other parts
* of the driver can access block reads */
static int maple_vmu_read_block ( unsigned int num , unsigned char * buf ,
struct mtd_info * mtd )
{
struct memcard * card ;
struct mdev_part * mpart ;
struct maple_device * mdev ;
int partition , error = 0 , x , wait ;
unsigned char * blockread = NULL ;
struct vmu_cache * pcache ;
__be32 sendbuf ;
mpart = mtd - > priv ;
mdev = mpart - > mdev ;
partition = mpart - > partition ;
card = maple_get_drvdata ( mdev ) ;
pcache = card - > parts [ partition ] . pcache ;
pcache - > valid = 0 ;
/* prepare the cache for this block */
if ( ! pcache - > buffer ) {
pcache - > buffer = kmalloc ( card - > blocklen , GFP_KERNEL ) ;
if ( ! pcache - > buffer ) {
dev_err ( & mdev - > dev , " VMU at (%d, %d) - read fails due "
" to lack of memory \n " , mdev - > port ,
mdev - > unit ) ;
error = - ENOMEM ;
goto outB ;
}
}
/*
* Reads may be phased - again the hardware spec
* supports this - though may not be any devices in
* the wild that implement it , but we will here
*/
for ( x = 0 ; x < card - > readcnt ; x + + ) {
sendbuf = cpu_to_be32 ( partition < < 24 | x < < 16 | num ) ;
if ( atomic_read ( & mdev - > busy ) = = 1 ) {
wait_event_interruptible_timeout ( mdev - > maple_wait ,
atomic_read ( & mdev - > busy ) = = 0 , HZ ) ;
if ( atomic_read ( & mdev - > busy ) = = 1 ) {
dev_notice ( & mdev - > dev , " VMU at (%d, %d) "
" is busy \n " , mdev - > port , mdev - > unit ) ;
error = - EAGAIN ;
goto outB ;
}
}
atomic_set ( & mdev - > busy , 1 ) ;
blockread = kmalloc ( card - > blocklen / card - > readcnt , GFP_KERNEL ) ;
if ( ! blockread ) {
error = - ENOMEM ;
atomic_set ( & mdev - > busy , 0 ) ;
goto outB ;
}
card - > blockread = blockread ;
maple_getcond_callback ( mdev , vmu_blockread , 0 ,
MAPLE_FUNC_MEMCARD ) ;
error = maple_add_packet ( mdev , MAPLE_FUNC_MEMCARD ,
MAPLE_COMMAND_BREAD , 2 , & sendbuf ) ;
/* Very long timeouts seem to be needed when box is stressed */
wait = wait_event_interruptible_timeout ( mdev - > maple_wait ,
( atomic_read ( & mdev - > busy ) = = 0 | |
atomic_read ( & mdev - > busy ) = = 2 ) , HZ * 3 ) ;
/*
* MTD layer does not handle hotplugging well
* so have to return errors when VMU is unplugged
* in the middle of a read ( busy = = 2 )
*/
if ( error | | atomic_read ( & mdev - > busy ) = = 2 ) {
if ( atomic_read ( & mdev - > busy ) = = 2 )
error = - ENXIO ;
atomic_set ( & mdev - > busy , 0 ) ;
card - > blockread = NULL ;
goto outA ;
}
if ( wait = = 0 | | wait = = - ERESTARTSYS ) {
card - > blockread = NULL ;
atomic_set ( & mdev - > busy , 0 ) ;
error = - EIO ;
list_del_init ( & ( mdev - > mq - > list ) ) ;
kfree ( mdev - > mq - > sendbuf ) ;
mdev - > mq - > sendbuf = NULL ;
if ( wait = = - ERESTARTSYS ) {
dev_warn ( & mdev - > dev , " VMU read on (%d, %d) "
" interrupted on block 0x%X \n " ,
mdev - > port , mdev - > unit , num ) ;
} else
dev_notice ( & mdev - > dev , " VMU read on (%d, %d) "
" timed out on block 0x%X \n " ,
mdev - > port , mdev - > unit , num ) ;
goto outA ;
}
memcpy ( buf + ( card - > blocklen / card - > readcnt ) * x , blockread ,
card - > blocklen / card - > readcnt ) ;
memcpy ( pcache - > buffer + ( card - > blocklen / card - > readcnt ) * x ,
card - > blockread , card - > blocklen / card - > readcnt ) ;
card - > blockread = NULL ;
pcache - > block = num ;
pcache - > jiffies_atc = jiffies ;
pcache - > valid = 1 ;
kfree ( blockread ) ;
}
return error ;
outA :
kfree ( blockread ) ;
outB :
return error ;
}
/* communicate with maple bus for phased writing */
static int maple_vmu_write_block ( unsigned int num , const unsigned char * buf ,
struct mtd_info * mtd )
{
struct memcard * card ;
struct mdev_part * mpart ;
struct maple_device * mdev ;
int partition , error , locking , x , phaselen , wait ;
__be32 * sendbuf ;
mpart = mtd - > priv ;
mdev = mpart - > mdev ;
partition = mpart - > partition ;
card = maple_get_drvdata ( mdev ) ;
phaselen = card - > blocklen / card - > writecnt ;
sendbuf = kmalloc ( phaselen + 4 , GFP_KERNEL ) ;
if ( ! sendbuf ) {
error = - ENOMEM ;
goto fail_nosendbuf ;
}
for ( x = 0 ; x < card - > writecnt ; x + + ) {
sendbuf [ 0 ] = cpu_to_be32 ( partition < < 24 | x < < 16 | num ) ;
memcpy ( & sendbuf [ 1 ] , buf + phaselen * x , phaselen ) ;
/* wait until the device is not busy doing something else
* or 1 second - which ever is longer */
if ( atomic_read ( & mdev - > busy ) = = 1 ) {
wait_event_interruptible_timeout ( mdev - > maple_wait ,
atomic_read ( & mdev - > busy ) = = 0 , HZ ) ;
if ( atomic_read ( & mdev - > busy ) = = 1 ) {
error = - EBUSY ;
dev_notice ( & mdev - > dev , " VMU write at (%d, %d) "
" failed - device is busy \n " ,
mdev - > port , mdev - > unit ) ;
goto fail_nolock ;
}
}
atomic_set ( & mdev - > busy , 1 ) ;
locking = maple_add_packet ( mdev , MAPLE_FUNC_MEMCARD ,
MAPLE_COMMAND_BWRITE , phaselen / 4 + 2 , sendbuf ) ;
wait = wait_event_interruptible_timeout ( mdev - > maple_wait ,
atomic_read ( & mdev - > busy ) = = 0 , HZ / 10 ) ;
if ( locking ) {
error = - EIO ;
atomic_set ( & mdev - > busy , 0 ) ;
goto fail_nolock ;
}
if ( atomic_read ( & mdev - > busy ) = = 2 ) {
atomic_set ( & mdev - > busy , 0 ) ;
} else if ( wait = = 0 | | wait = = - ERESTARTSYS ) {
error = - EIO ;
dev_warn ( & mdev - > dev , " Write at (%d, %d) of block "
" 0x%X at phase %d failed: could not "
" communicate with VMU " , mdev - > port ,
mdev - > unit , num , x ) ;
atomic_set ( & mdev - > busy , 0 ) ;
kfree ( mdev - > mq - > sendbuf ) ;
mdev - > mq - > sendbuf = NULL ;
list_del_init ( & ( mdev - > mq - > list ) ) ;
goto fail_nolock ;
}
}
kfree ( sendbuf ) ;
return card - > blocklen ;
fail_nolock :
kfree ( sendbuf ) ;
fail_nosendbuf :
dev_err ( & mdev - > dev , " VMU (%d, %d): write failed \n " , mdev - > port ,
mdev - > unit ) ;
return error ;
}
/* mtd function to simulate reading byte by byte */
static unsigned char vmu_flash_read_char ( unsigned long ofs , int * retval ,
struct mtd_info * mtd )
{
struct vmu_block * vblock ;
struct memcard * card ;
struct mdev_part * mpart ;
struct maple_device * mdev ;
unsigned char * buf , ret ;
int partition , error ;
mpart = mtd - > priv ;
mdev = mpart - > mdev ;
partition = mpart - > partition ;
card = maple_get_drvdata ( mdev ) ;
* retval = 0 ;
buf = kmalloc ( card - > blocklen , GFP_KERNEL ) ;
if ( ! buf ) {
* retval = 1 ;
ret = - ENOMEM ;
goto finish ;
}
vblock = ofs_to_block ( ofs , mtd , partition ) ;
if ( ! vblock ) {
* retval = 3 ;
ret = - ENOMEM ;
goto out_buf ;
}
error = maple_vmu_read_block ( vblock - > num , buf , mtd ) ;
if ( error ) {
ret = error ;
* retval = 2 ;
goto out_vblock ;
}
ret = buf [ vblock - > ofs ] ;
out_vblock :
kfree ( vblock ) ;
out_buf :
kfree ( buf ) ;
finish :
return ret ;
}
/* mtd higher order function to read flash */
static int vmu_flash_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , u_char * buf )
{
struct maple_device * mdev ;
struct memcard * card ;
struct mdev_part * mpart ;
struct vmu_cache * pcache ;
struct vmu_block * vblock ;
int index = 0 , retval , partition , leftover , numblocks ;
unsigned char cx ;
if ( len < 1 )
return - EIO ;
mpart = mtd - > priv ;
mdev = mpart - > mdev ;
partition = mpart - > partition ;
card = maple_get_drvdata ( mdev ) ;
numblocks = card - > parts [ partition ] . numblocks ;
if ( from + len > numblocks * card - > blocklen )
len = numblocks * card - > blocklen - from ;
if ( len = = 0 )
return - EIO ;
/* Have we cached this bit already? */
pcache = card - > parts [ partition ] . pcache ;
do {
vblock = ofs_to_block ( from + index , mtd , partition ) ;
if ( ! vblock )
return - ENOMEM ;
/* Have we cached this and is the cache valid and timely? */
if ( pcache - > valid & &
time_before ( jiffies , pcache - > jiffies_atc + HZ ) & &
( pcache - > block = = vblock - > num ) ) {
/* we have cached it, so do necessary copying */
leftover = card - > blocklen - vblock - > ofs ;
if ( vblock - > ofs + len - index < card - > blocklen ) {
/* only a bit of this block to copy */
memcpy ( buf + index ,
pcache - > buffer + vblock - > ofs ,
len - index ) ;
index = len ;
} else {
/* otherwise copy remainder of whole block */
memcpy ( buf + index , pcache - > buffer +
vblock - > ofs , leftover ) ;
index + = leftover ;
}
} else {
/*
* Not cached so read one byte -
* but cache the rest of the block
*/
cx = vmu_flash_read_char ( from + index , & retval , mtd ) ;
if ( retval ) {
* retlen = index ;
kfree ( vblock ) ;
return cx ;
}
memset ( buf + index , cx , 1 ) ;
index + + ;
}
kfree ( vblock ) ;
} while ( len > index ) ;
* retlen = index ;
return 0 ;
}
static int vmu_flash_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
{
struct maple_device * mdev ;
struct memcard * card ;
struct mdev_part * mpart ;
int index = 0 , partition , error = 0 , numblocks ;
struct vmu_cache * pcache ;
struct vmu_block * vblock ;
unsigned char * buffer ;
mpart = mtd - > priv ;
mdev = mpart - > mdev ;
partition = mpart - > partition ;
card = maple_get_drvdata ( mdev ) ;
/* simple sanity checks */
if ( len < 1 ) {
error = - EIO ;
goto failed ;
}
numblocks = card - > parts [ partition ] . numblocks ;
if ( to + len > numblocks * card - > blocklen )
len = numblocks * card - > blocklen - to ;
if ( len = = 0 ) {
error = - EIO ;
goto failed ;
}
vblock = ofs_to_block ( to , mtd , partition ) ;
if ( ! vblock ) {
error = - ENOMEM ;
goto failed ;
}
buffer = kmalloc ( card - > blocklen , GFP_KERNEL ) ;
if ( ! buffer ) {
error = - ENOMEM ;
goto fail_buffer ;
}
do {
/* Read in the block we are to write to */
error = maple_vmu_read_block ( vblock - > num , buffer , mtd ) ;
if ( error )
goto fail_io ;
do {
buffer [ vblock - > ofs ] = buf [ index ] ;
vblock - > ofs + + ;
index + + ;
if ( index > = len )
break ;
} while ( vblock - > ofs < card - > blocklen ) ;
/* write out new buffer */
error = maple_vmu_write_block ( vblock - > num , buffer , mtd ) ;
/* invalidate the cache */
pcache = card - > parts [ partition ] . pcache ;
pcache - > valid = 0 ;
if ( error ! = card - > blocklen )
goto fail_io ;
vblock - > num + + ;
vblock - > ofs = 0 ;
} while ( len > index ) ;
kfree ( buffer ) ;
* retlen = index ;
kfree ( vblock ) ;
return 0 ;
fail_io :
kfree ( buffer ) ;
fail_buffer :
kfree ( vblock ) ;
failed :
dev_err ( & mdev - > dev , " VMU write failing with error %d \n " , error ) ;
return error ;
}
static void vmu_flash_sync ( struct mtd_info * mtd )
{
/* Do nothing here */
}
/* Maple bus callback function to recursively query hardware details */
static void vmu_queryblocks ( struct mapleq * mq )
{
struct maple_device * mdev ;
unsigned short * res ;
struct memcard * card ;
__be32 partnum ;
struct vmu_cache * pcache ;
struct mdev_part * mpart ;
struct mtd_info * mtd_cur ;
struct vmupart * part_cur ;
int error ;
mdev = mq - > dev ;
card = maple_get_drvdata ( mdev ) ;
res = ( unsigned short * ) ( mq - > recvbuf - > buf ) ;
card - > tempA = res [ 12 ] ;
card - > tempB = res [ 6 ] ;
dev_info ( & mdev - > dev , " VMU device at partition %d has %d user "
" blocks with a root block at %d \n " , card - > partition ,
card - > tempA , card - > tempB ) ;
part_cur = & card - > parts [ card - > partition ] ;
part_cur - > user_blocks = card - > tempA ;
part_cur - > root_block = card - > tempB ;
part_cur - > numblocks = card - > tempB + 1 ;
part_cur - > name = kmalloc ( 12 , GFP_KERNEL ) ;
if ( ! part_cur - > name )
goto fail_name ;
sprintf ( part_cur - > name , " vmu%d.%d.%d " ,
mdev - > port , mdev - > unit , card - > partition ) ;
mtd_cur = & card - > mtd [ card - > partition ] ;
mtd_cur - > name = part_cur - > name ;
mtd_cur - > type = 8 ;
mtd_cur - > flags = MTD_WRITEABLE | MTD_NO_ERASE ;
mtd_cur - > size = part_cur - > numblocks * card - > blocklen ;
mtd_cur - > erasesize = card - > blocklen ;
mtd_cur - > write = vmu_flash_write ;
mtd_cur - > read = vmu_flash_read ;
mtd_cur - > sync = vmu_flash_sync ;
mtd_cur - > writesize = card - > blocklen ;
mpart = kmalloc ( sizeof ( struct mdev_part ) , GFP_KERNEL ) ;
if ( ! mpart )
goto fail_mpart ;
mpart - > mdev = mdev ;
mpart - > partition = card - > partition ;
mtd_cur - > priv = mpart ;
mtd_cur - > owner = THIS_MODULE ;
pcache = kzalloc ( sizeof ( struct vmu_cache ) , GFP_KERNEL ) ;
if ( ! pcache )
goto fail_cache_create ;
part_cur - > pcache = pcache ;
error = add_mtd_device ( mtd_cur ) ;
if ( error )
goto fail_mtd_register ;
maple_getcond_callback ( mdev , NULL , 0 ,
MAPLE_FUNC_MEMCARD ) ;
/*
* Set up a recursive call to the ( probably theoretical )
* second or more partition
*/
if ( + + card - > partition < card - > partitions ) {
partnum = cpu_to_be32 ( card - > partition < < 24 ) ;
maple_getcond_callback ( mdev , vmu_queryblocks , 0 ,
MAPLE_FUNC_MEMCARD ) ;
maple_add_packet ( mdev , MAPLE_FUNC_MEMCARD ,
MAPLE_COMMAND_GETMINFO , 2 , & partnum ) ;
}
return ;
fail_mtd_register :
dev_err ( & mdev - > dev , " Could not register maple device at (%d, %d) "
" error is 0x%X \n " , mdev - > port , mdev - > unit , error ) ;
for ( error = 0 ; error < = card - > partition ; error + + ) {
kfree ( ( ( card - > parts ) [ error ] ) . pcache ) ;
( ( card - > parts ) [ error ] ) . pcache = NULL ;
}
fail_cache_create :
fail_mpart :
for ( error = 0 ; error < = card - > partition ; error + + ) {
kfree ( ( ( card - > mtd ) [ error ] ) . priv ) ;
( ( card - > mtd ) [ error ] ) . priv = NULL ;
}
maple_getcond_callback ( mdev , NULL , 0 ,
MAPLE_FUNC_MEMCARD ) ;
kfree ( part_cur - > name ) ;
fail_name :
return ;
}
/* Handles very basic info about the flash, queries for details */
static int __devinit vmu_connect ( struct maple_device * mdev )
{
unsigned long test_flash_data , basic_flash_data ;
int c , error ;
struct memcard * card ;
u32 partnum = 0 ;
test_flash_data = be32_to_cpu ( mdev - > devinfo . function ) ;
/* Need to count how many bits are set - to find out which
2009-11-20 14:56:07 +09:00
* function_data element has details of the memory card
*/
c = hweight_long ( test_flash_data ) ;
2009-03-04 00:31:04 +00:00
basic_flash_data = be32_to_cpu ( mdev - > devinfo . function_data [ c - 1 ] ) ;
card = kmalloc ( sizeof ( struct memcard ) , GFP_KERNEL ) ;
if ( ! card ) {
2009-11-11 21:47:06 +01:00
error = - ENOMEM ;
2009-03-04 00:31:04 +00:00
goto fail_nomem ;
}
card - > partitions = ( basic_flash_data > > 24 & 0xFF ) + 1 ;
card - > blocklen = ( ( basic_flash_data > > 16 & 0xFF ) + 1 ) < < 5 ;
card - > writecnt = basic_flash_data > > 12 & 0xF ;
card - > readcnt = basic_flash_data > > 8 & 0xF ;
card - > removeable = basic_flash_data > > 7 & 1 ;
card - > partition = 0 ;
/*
* Not sure there are actually any multi - partition devices in the
* real world , but the hardware supports them , so , so will we
*/
card - > parts = kmalloc ( sizeof ( struct vmupart ) * card - > partitions ,
GFP_KERNEL ) ;
if ( ! card - > parts ) {
error = - ENOMEM ;
goto fail_partitions ;
}
card - > mtd = kmalloc ( sizeof ( struct mtd_info ) * card - > partitions ,
GFP_KERNEL ) ;
if ( ! card - > mtd ) {
error = - ENOMEM ;
goto fail_mtd_info ;
}
maple_set_drvdata ( mdev , card ) ;
/*
* We want to trap meminfo not get cond
* so set interval to zero , but rely on maple bus
* driver to pass back the results of the meminfo
*/
maple_getcond_callback ( mdev , vmu_queryblocks , 0 ,
MAPLE_FUNC_MEMCARD ) ;
/* Make sure we are clear to go */
if ( atomic_read ( & mdev - > busy ) = = 1 ) {
wait_event_interruptible_timeout ( mdev - > maple_wait ,
atomic_read ( & mdev - > busy ) = = 0 , HZ ) ;
if ( atomic_read ( & mdev - > busy ) = = 1 ) {
dev_notice ( & mdev - > dev , " VMU at (%d, %d) is busy \n " ,
mdev - > port , mdev - > unit ) ;
error = - EAGAIN ;
goto fail_device_busy ;
}
}
atomic_set ( & mdev - > busy , 1 ) ;
/*
* Set up the minfo call : vmu_queryblocks will handle
* the information passed back
*/
error = maple_add_packet ( mdev , MAPLE_FUNC_MEMCARD ,
MAPLE_COMMAND_GETMINFO , 2 , & partnum ) ;
if ( error ) {
dev_err ( & mdev - > dev , " Could not lock VMU at (%d, %d) "
" error is 0x%X \n " , mdev - > port , mdev - > unit , error ) ;
goto fail_mtd_info ;
}
return 0 ;
fail_device_busy :
kfree ( card - > mtd ) ;
fail_mtd_info :
kfree ( card - > parts ) ;
fail_partitions :
kfree ( card ) ;
fail_nomem :
return error ;
}
static void __devexit vmu_disconnect ( struct maple_device * mdev )
{
struct memcard * card ;
struct mdev_part * mpart ;
int x ;
mdev - > callback = NULL ;
card = maple_get_drvdata ( mdev ) ;
for ( x = 0 ; x < card - > partitions ; x + + ) {
mpart = ( ( card - > mtd ) [ x ] ) . priv ;
mpart - > mdev = NULL ;
del_mtd_device ( & ( ( card - > mtd ) [ x ] ) ) ;
kfree ( ( ( card - > parts ) [ x ] ) . name ) ;
}
kfree ( card - > parts ) ;
kfree ( card - > mtd ) ;
kfree ( card ) ;
}
/* Callback to handle eccentricities of both mtd subsystem
* and general flakyness of Dreamcast VMUs
*/
static int vmu_can_unload ( struct maple_device * mdev )
{
struct memcard * card ;
int x ;
struct mtd_info * mtd ;
card = maple_get_drvdata ( mdev ) ;
for ( x = 0 ; x < card - > partitions ; x + + ) {
mtd = & ( ( card - > mtd ) [ x ] ) ;
if ( mtd - > usecount > 0 )
return 0 ;
}
return 1 ;
}
# define ERRSTR "VMU at (%d, %d) file error -"
static void vmu_file_error ( struct maple_device * mdev , void * recvbuf )
{
enum maple_file_errors error = ( ( int * ) recvbuf ) [ 1 ] ;
switch ( error ) {
case MAPLE_FILEERR_INVALID_PARTITION :
dev_notice ( & mdev - > dev , ERRSTR " invalid partition number \n " ,
mdev - > port , mdev - > unit ) ;
break ;
case MAPLE_FILEERR_PHASE_ERROR :
dev_notice ( & mdev - > dev , ERRSTR " phase error \n " ,
mdev - > port , mdev - > unit ) ;
break ;
case MAPLE_FILEERR_INVALID_BLOCK :
dev_notice ( & mdev - > dev , ERRSTR " invalid block number \n " ,
mdev - > port , mdev - > unit ) ;
break ;
case MAPLE_FILEERR_WRITE_ERROR :
dev_notice ( & mdev - > dev , ERRSTR " write error \n " ,
mdev - > port , mdev - > unit ) ;
break ;
case MAPLE_FILEERR_INVALID_WRITE_LENGTH :
dev_notice ( & mdev - > dev , ERRSTR " invalid write length \n " ,
mdev - > port , mdev - > unit ) ;
break ;
case MAPLE_FILEERR_BAD_CRC :
dev_notice ( & mdev - > dev , ERRSTR " bad CRC \n " ,
mdev - > port , mdev - > unit ) ;
break ;
default :
dev_notice ( & mdev - > dev , ERRSTR " 0x%X \n " ,
mdev - > port , mdev - > unit , error ) ;
}
}
static int __devinit probe_maple_vmu ( struct device * dev )
{
int error ;
struct maple_device * mdev = to_maple_dev ( dev ) ;
struct maple_driver * mdrv = to_maple_driver ( dev - > driver ) ;
mdev - > can_unload = vmu_can_unload ;
mdev - > fileerr_handler = vmu_file_error ;
mdev - > driver = mdrv ;
error = vmu_connect ( mdev ) ;
if ( error )
return error ;
return 0 ;
}
static int __devexit remove_maple_vmu ( struct device * dev )
{
struct maple_device * mdev = to_maple_dev ( dev ) ;
vmu_disconnect ( mdev ) ;
return 0 ;
}
static struct maple_driver vmu_flash_driver = {
. function = MAPLE_FUNC_MEMCARD ,
. drv = {
. name = " Dreamcast_visual_memory " ,
. probe = probe_maple_vmu ,
. remove = __devexit_p ( remove_maple_vmu ) ,
} ,
} ;
static int __init vmu_flash_map_init ( void )
{
return maple_driver_register ( & vmu_flash_driver ) ;
}
static void __exit vmu_flash_map_exit ( void )
{
maple_driver_unregister ( & vmu_flash_driver ) ;
}
module_init ( vmu_flash_map_init ) ;
module_exit ( vmu_flash_map_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Adrian McMenamin " ) ;
MODULE_DESCRIPTION ( " Flash mapping for Sega Dreamcast visual memory " ) ;