/* * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #define CPLUSPLUS typedef long unsigned int uint_t; #include "floppy.h" #include "portdef.h" // kIOMessageMediaStateHasChanged // call message(kIOMessageMediaStateHasChanged); when state has changed. extern "C" { #include //This is for debugging purposes ONLY io_return_t fdsetstat(dev_t dev, dev_flavor_t flavor, dev_status_t data, natural_t count); void ScanForDisketteChange(void); BSIOStatus FloppyPluginIO(int unit, BSStorePtr ioStore, BSBlockListDescriptorRef blocks, MemListDescriptorRef memory, BSIORequestBlockPtr parentRequest, OptionBits options, struct BSErrorList **errors); int non_bsd_sleep(void *event, unsigned long usec); void donone(char *,...); char *onepage; } #undef MACH_DEBUG #define MACH_DEBUG 0 #if (!MACH_DEBUG) #define IOLog donone #else #if (SERIAL_DEBUG) #define IOLog kprintf #endif #endif int org_mklinux_iokit_swim3_busyflag = 0; // in floppy.cpp // virtual IOReturn doAsyncReadWrite(IOMemoryDescriptor *buffer, // UInt32 block,UInt32 nblks, // IOStorageCompletion completion) = 0; IOReturn org_mklinux_iokit_swim3_driver::doEjectMedia(void) { dev_t dev; if(locked) return kIOReturnBusy; dev=(this->busdev->unit << 6); if (fdsetstat(dev, (dev_flavor_t)V_EJECT, NULL, 0) == D_SUCCESS) return kIOReturnSuccess; else return kIOReturnError; } IOReturn org_mklinux_iokit_swim3_driver::doFormatMedia(UInt64 byteCapacity) { /* @@@ WRITE ME! @@@*/ return kIOReturnUnsupported; } UInt32 org_mklinux_iokit_swim3_driver::doGetFormatCapacities(UInt64 * capacities, UInt32 capacitiesMaxCount) { /* @@@ WRITE ME! @@@*/ return kIOReturnUnsupported; } IOReturn org_mklinux_iokit_swim3_driver::doLockUnlockMedia(bool doLock) { locked = doLock; return kIOReturnSuccess; } /* We're running into a panic when IOMalloc/IOFree are used repeatedly a few hundred (thousand) times. Thus far, the upper layers of the OS have yet to request any data more than a single block in length, despite the fact that we say that we can read the entire disk in a single transaction. (This is, in fact, a lie. We can read an entire track in a single transaction, though, and can do a whole disk read a heck of a lot faster inside the driver than the I/O Kit can do it by calling in once per track....) The IOMalloc and IOFree functions are basically thin shims to kalloc and kfree for requests under a page. After that, they use kmem_alloc and kmem_free. So the new version of the code uses kmem_free for requests above a page, and uses a static buffer for requests up to a page in length. */ IOReturn org_mklinux_iokit_swim3_driver::doSyncReadWrite(IOMemoryDescriptor *buffer, UInt32 block,UInt32 nblks) { #define NOTYETDAG #ifdef NOTYETDAG //#error UNFINISHED uint_t Result; BSIOStatus retval = kBSIOCompleted; OptionBits direction; #ifdef IOMALLOC_WORKS char *localbuf; #else vm_offset_t localbuf; #endif IOReturn myIOReturn; int retrycount; unsigned int blocks_per_page = PAGE_SIZE / 512; /* I assume the higher levels of the I/O Kit handle checking write protection */ #ifndef IOMALLOC_WORKS long int size = ((nblks * 512) + PAGE_SIZE - 1) & ~PAGE_SIZE; #endif // IOLog("swim3: Entered doSyncReadWrite.\n"); if (!buffer) return kIOReturnNoMemory; direction = (buffer->getDirection() == kIODirectionIn ? kBSRead : kBSWrite); // IOLog("swim3: direction is %s\n", ((direction == kBSRead) ? "kBSRead" : // "kBSWrite")); // IOLog("swim3: allocating buffer.\n"); if (nblks <= blocks_per_page) { #ifdef IOMALLOC_WORKS localbuf = onepage; #else localbuf = (vm_offset_t) onepage; #endif } else { #ifdef IOMALLOC_WORKS localbuf = (char *)IOMalloc(nblks * 512); if (!localbuf) #else if (kmem_alloc_pageable(kernel_map, &localbuf, size) != KERN_SUCCESS) #endif { IOLog("swim3: Out of MEMORY!\n"); return kIOReturnNoMemory; } } // IOLog("swim3: possibly waiting for busy flag....\n"); while (org_mklinux_iokit_swim3_busyflag == 1) { // IOLog("swim3: yup. We're waiting.\n"); non_bsd_sleep((char *) &org_mklinux_iokit_swim3_busyflag, 10); } // IOLog("swim3: done waiting.\n"); org_mklinux_iokit_swim3_busyflag = 1; if (direction == kBSWrite) { IOMemoryMap *map; IOLog("Mapping Data in 15\n"); IOLog("Mapping Data in 14\n"); IOLog("Mapping Data in 13\n"); IOLog("Mapping Data in 12\n"); IOLog("Mapping Data in 11\n"); IOLog("Mapping Data in 10\n"); IOLog("Mapping Data in 9\n"); IOLog("Mapping Data in 8\n"); IOLog("Mapping Data in 7\n"); IOLog("Mapping Data in 6\n"); IOLog("Mapping Data in 5\n"); IOLog("Mapping Data in 4\n"); IOLog("Mapping Data in 3\n"); IOLog("Mapping Data in 2\n"); IOLog("Mapping Data in 1\n"); IOLog("Mapping Data!\n"); /* Copy the data to kernel memory for writing */ map = buffer->map(); if (!map) { IOLog("Map in failed. Returning kIOReturnVMError\n"); if (nblks > blocks_per_page) { #ifdef IOMALLOC_WORKS IOFree(localbuf, nblks * 512); #else kmem_free(kernel_map, localbuf, size); #endif } org_mklinux_iokit_swim3_busyflag = 0; return kIOReturnVMError; } IOLog("Copying Data in 15\n"); IOLog("Copying Data in 14\n"); IOLog("Copying Data in 13\n"); IOLog("Copying Data in 12\n"); IOLog("Copying Data in 11\n"); IOLog("Copying Data in 10\n"); IOLog("Copying Data in 9\n"); IOLog("Copying Data in 8\n"); IOLog("Copying Data in 7\n"); IOLog("Copying Data in 6\n"); IOLog("Copying Data in 5\n"); IOLog("Copying Data in 4\n"); IOLog("Copying Data in 3\n"); IOLog("Copying Data in 2\n"); IOLog("Copying Data in 1\n"); IOLog("Copying Data!\n"); if (buffer->readBytes(0, (void *)localbuf, nblks * 512) != (nblks * 512)) { IOLog("Write copy failed. Returning kIOReturnVMError\n"); if (nblks > blocks_per_page) { #ifdef IOMALLOC_WORKS IOFree(localbuf, nblks * 512); #else kmem_free(kernel_map, localbuf, size); #endif } org_mklinux_iokit_swim3_busyflag = 0; return kIOReturnVMError; } IOLog("Unmapping Data in 15\n"); IOLog("Unmapping Data in 14\n"); IOLog("Unmapping Data in 13\n"); IOLog("Unmapping Data in 12\n"); IOLog("Unmapping Data in 11\n"); IOLog("Unmapping Data in 10\n"); IOLog("Unmapping Data in 9\n"); IOLog("Unmapping Data in 8\n"); IOLog("Unmapping Data in 7\n"); IOLog("Unmapping Data in 6\n"); IOLog("Unmapping Data in 5\n"); IOLog("Unmapping Data in 4\n"); IOLog("Unmapping Data in 3\n"); IOLog("Unmapping Data in 2\n"); IOLog("Unmapping Data in 1\n"); IOLog("Unmapping Data!\n"); map->unmap(); IOLog("Doing write.\n"); } // IOLog("swim3: calling FloppyPluginIO.\n"); for (retrycount = 0; retrycount < 5; retrycount++) { retval = FloppyPluginIO(this->busdev->unit, &Result, (nblks * 512), (MemListDescriptorRef)localbuf, block * 512, direction, 0); if (retval == kBSIOCompleted) break; IOLog("Retrying request.\n"); } // IOLog("swim3: checking return values and freeing.\n"); if (retval == kBSIOCompleted) { if (Result == (nblks * 512)) { if (direction == kBSWrite) { myIOReturn=kIOReturnSuccess; } else if (buffer->writeBytes(0, (const void *)localbuf, Result) == (Result)) { myIOReturn=kIOReturnSuccess; } else { /* copyout to user buffer failed */ myIOReturn=kIOReturnVMError; } } else { /* Partial read or write */ myIOReturn=kIOReturnUnderrun; } } else { /* Some error in the actual read/write operation */ myIOReturn=kIOReturnIOError; } if (nblks > blocks_per_page) { #ifdef IOMALLOC_WORKS IOFree(localbuf, nblks * 512); #else kmem_free(kernel_map, localbuf, size); #endif } org_mklinux_iokit_swim3_busyflag = 0; IOLog("swim3: for block %d-%d, returning %s (FloppyPluginIO returned %d/%x).\n", block, block+nblks-1, myIOReturn == kIOReturnSuccess ? "kIOReturnSuccess" : myIOReturn == kIOReturnVMError? "kIOReturnVMError" : myIOReturn == kIOReturnUnderrun ? "kIOReturnUnderrun" : "kIOReturnIOError", retval, retval); return myIOReturn; #else return kIOReturnIOError; return kIOReturnUnsupported; #endif // NOTYETDAG } // virtual IOReturn doSynchronizeCache(void) = 0; // virtual char * getAdditionalDeviceInfoString(void) = 0; // virtual char * getProductString(void) = 0; // virtual char * getRevisionString(void) = 0; // virtual char * getVendorString(void) = 0; // virtual bool init(OSDictionary * properties); extern "C" { OSStatus FloppyPluginGetInfo(BSStorePtr infoStore, struct BSStoreMPIInfo *info); } IOReturn org_mklinux_iokit_swim3_driver::reportBlockSize(UInt64 *blockSize) { struct BSStoreMPIInfo info; #if 0 BSStorePtr infoStore; #endif OSStatus ret; ret=FloppyPluginGetInfo(NULL, &info); *blockSize=info.writeBlockSize; if (ret == E_BSSuccess) return kIOReturnSuccess; else return kIOReturnError; } IOReturn org_mklinux_iokit_swim3_driver::reportEjectability(bool *isEjectable) { *isEjectable=1; return kIOReturnSuccess; } IOReturn org_mklinux_iokit_swim3_driver::reportLockability(bool *isLockable) { *isLockable=1; return kIOReturnSuccess; } IOReturn org_mklinux_iokit_swim3_driver::reportMaxReadTransfer(UInt64 blockSize,UInt64 *max) { /* It's the same for floppies, so why screw with two functions? */ return reportMaxWriteTransfer(blockSize, max); } IOReturn org_mklinux_iokit_swim3_driver::reportMaxValidBlock(UInt64 *maxBlock) { struct BSStoreMPIInfo info; #if 0 BSStorePtr infoStore; #endif OSStatus ret; ret=FloppyPluginGetInfo(NULL, &info); *maxBlock=info.storeSize / info.writeBlockSize; if (ret == E_BSSuccess) return kIOReturnSuccess; else return kIOReturnError; } IOReturn org_mklinux_iokit_swim3_driver::reportMaxWriteTransfer(UInt64 blockSize,UInt64 *max) { *max=MAX_PHYS / blockSize; return kIOReturnSuccess; } IOReturn org_mklinux_iokit_swim3_driver::reportMediaState(bool *mediaPresent,bool *changedState) { struct BSStoreMPIInfo info; #if 0 BSStorePtr infoStore; #endif OSStatus ret; // IOLog("Polling swim3\n"); ScanForDisketteChange(); ret=FloppyPluginGetInfo(NULL, &info); if (info.curState == kBSOnline) *mediaPresent = true; else *mediaPresent = false; if (media_present != *mediaPresent) *changedState = true; else *changedState = false; media_present = *mediaPresent; // IOLog("mediaPresent = %s, changedState = %s\n", // (*mediaPresent == true) ? "true" : "false", // (*changedState == true) ? "true" : "false"); if (ret == E_BSSuccess) return kIOReturnSuccess; else return kIOReturnError; } IOReturn org_mklinux_iokit_swim3_driver::reportPollRequirements(bool *pollRequired, bool *pollIsExpensive) { /* @@@ Should this really be true? I mean, we already have a polling thread. Shouldn't we just notify upstream somehow? Seems obvious, but it isn't obvious how to do it outside of the usual families (SCSI, ATA, etc.) @@@ */ *pollRequired=true; *pollIsExpensive=false; return kIOReturnSuccess; } IOReturn org_mklinux_iokit_swim3_driver::reportRemovability(bool *isRemovable) { *isRemovable=true; return kIOReturnSuccess; } IOReturn org_mklinux_iokit_swim3_driver::reportWriteProtection(bool *isWriteProtected) { struct BSStoreMPIInfo info; #if 0 BSStorePtr infoStore; #endif OSStatus ret; ret=FloppyPluginGetInfo(NULL, &info); if (info.curState == kBSOffline) { return kIOReturnOffline; // or kIOReturnNotReady } if (!info.isWriteable) *isWriteProtected = true; else *isWriteProtected = false; if (ret == E_BSSuccess) { IOLog("swim3: reportWriteProtection succeeded with status %d\n", *isWriteProtected); return kIOReturnSuccess; } else { IOLog("swim3: reportWriteProtection failed with status %d (error %d)\n", *isWriteProtected, ret); return kIOReturnError; } }