123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824 |
- /* 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/slab.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;
- 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);
- 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 = mtd_device_register(mtd_cur, NULL, 0);
- 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 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
- * function_data element has details of the memory card
- */
- c = hweight_long(test_flash_data);
- basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
- card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
- if (!card) {
- error = -ENOMEM;
- 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 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;
- mtd_device_unregister(&((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 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 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 = 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");
|