Commit 120aa298 authored by Ben Avison's avatar Ben Avison
Browse files

Initial import of ADFS 4

Detail:
  Version 4 of ADFS is a complete rewrite, but backward compatible with
  previous versions of ADFS. The hardware driver components have been
  extracted out into separate modules, along the lines of the
  SCSIFS/SCSIDriver and SDFS/SDIODriver interfaces. Initially, the only
  supported driver backend is for AHCI SATA controllers, but it is anticipated
  that PATA controller support and maybe even floppy drive support may be
  added at a later date to cater for legacy hardware. Background transfers are
  only partially implemented at present.
Admin:
  This version is what appeared in initial Titanium ROM releases.

Tagged as 'ADFS-4_00'
parents
hdr/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
s/** gitlab-language=armasm linguist-language=armasm linguist-detectable=true
c/** gitlab-language=c linguist-language=c linguist-detectable=true
h/** gitlab-language=c linguist-language=c linguist-detectable=true
cmhg/** gitlab-language=cmhg linguist-language=cmhg linguist-detectable=true
All files in this distribution are released under the
Common Development and Distribution License (CDDL).
--------------------------------------------------------------------
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0
1. Definitions.
1.1. "Contributor" means each individual or entity that creates
or contributes to the creation of Modifications.
1.2. "Contributor Version" means the combination of the Original
Software, prior Modifications used by a Contributor (if any),
and the Modifications made by that particular Contributor.
1.3. "Covered Software" means (a) the Original Software, or (b)
Modifications, or (c) the combination of files containing
Original Software with files containing Modifications, in
each case including portions thereof.
1.4. "Executable" means the Covered Software in any form other
than Source Code.
1.5. "Initial Developer" means the individual or entity that first
makes Original Software available under this License.
1.6. "Larger Work" means a work which combines Covered Software or
portions thereof with code not governed by the terms of this
License.
1.7. "License" means this document.
1.8. "Licensable" means having the right to grant, to the maximum
extent possible, whether at the time of the initial grant or
subsequently acquired, any and all of the rights conveyed
herein.
1.9. "Modifications" means the Source Code and Executable form of
any of the following:
A. Any file that results from an addition to, deletion from or
modification of the contents of a file containing Original
Software or previous Modifications;
B. Any new file that contains any part of the Original
Software or previous Modifications; or
C. Any new file that is contributed or otherwise made
available under the terms of this License.
1.10. "Original Software" means the Source Code and Executable
form of computer software code that is originally released
under this License.
1.11. "Patent Claims" means any patent claim(s), now owned or
hereafter acquired, including without limitation, method,
process, and apparatus claims, in any patent Licensable by
grantor.
1.12. "Source Code" means (a) the common form of computer software
code in which modifications are made and (b) associated
documentation included in or with such code.
1.13. "You" (or "Your") means an individual or a legal entity
exercising rights under, and complying with all of the terms
of, this License. For legal entities, "You" includes any
entity which controls, is controlled by, or is under common
control with You. For purposes of this definition,
"control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by
contract or otherwise, or (b) ownership of more than fifty
percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants.
2.1. The Initial Developer Grant.
Conditioned upon Your compliance with Section 3.1 below and
subject to third party intellectual property claims, the Initial
Developer hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Initial Developer, to use,
reproduce, modify, display, perform, sublicense and
distribute the Original Software (or portions thereof),
with or without Modifications, and/or as part of a Larger
Work; and
(b) under Patent Claims infringed by the making, using or
selling of Original Software, to make, have made, use,
practice, sell, and offer for sale, and/or otherwise
dispose of the Original Software (or portions thereof).
(c) The licenses granted in Sections 2.1(a) and (b) are
effective on the date Initial Developer first distributes
or otherwise makes the Original Software available to a
third party under the terms of this License.
(d) Notwithstanding Section 2.1(b) above, no patent license is
granted: (1) for code that You delete from the Original
Software, or (2) for infringements caused by: (i) the
modification of the Original Software, or (ii) the
combination of the Original Software with other software
or devices.
2.2. Contributor Grant.
Conditioned upon Your compliance with Section 3.1 below and
subject to third party intellectual property claims, each
Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Contributor to use, reproduce,
modify, display, perform, sublicense and distribute the
Modifications created by such Contributor (or portions
thereof), either on an unmodified basis, with other
Modifications, as Covered Software and/or as part of a
Larger Work; and
(b) under Patent Claims infringed by the making, using, or
selling of Modifications made by that Contributor either
alone and/or in combination with its Contributor Version
(or portions of such combination), to make, use, sell,
offer for sale, have made, and/or otherwise dispose of:
(1) Modifications made by that Contributor (or portions
thereof); and (2) the combination of Modifications made by
that Contributor with its Contributor Version (or portions
of such combination).
(c) The licenses granted in Sections 2.2(a) and 2.2(b) are
effective on the date Contributor first distributes or
otherwise makes the Modifications available to a third
party.
(d) Notwithstanding Section 2.2(b) above, no patent license is
granted: (1) for any code that Contributor has deleted
from the Contributor Version; (2) for infringements caused
by: (i) third party modifications of Contributor Version,
or (ii) the combination of Modifications made by that
Contributor with other software (except as part of the
Contributor Version) or other devices; or (3) under Patent
Claims infringed by Covered Software in the absence of
Modifications made by that Contributor.
3. Distribution Obligations.
3.1. Availability of Source Code.
Any Covered Software that You distribute or otherwise make
available in Executable form must also be made available in Source
Code form and that Source Code form must be distributed only under
the terms of this License. You must include a copy of this
License with every copy of the Source Code form of the Covered
Software You distribute or otherwise make available. You must
inform recipients of any such Covered Software in Executable form
as to how they can obtain such Covered Software in Source Code
form in a reasonable manner on or through a medium customarily
used for software exchange.
3.2. Modifications.
The Modifications that You create or to which You contribute are
governed by the terms of this License. You represent that You
believe Your Modifications are Your original creation(s) and/or
You have sufficient rights to grant the rights conveyed by this
License.
3.3. Required Notices.
You must include a notice in each of Your Modifications that
identifies You as the Contributor of the Modification. You may
not remove or alter any copyright, patent or trademark notices
contained within the Covered Software, or any notices of licensing
or any descriptive text giving attribution to any Contributor or
the Initial Developer.
3.4. Application of Additional Terms.
You may not offer or impose any terms on any Covered Software in
Source Code form that alters or restricts the applicable version
of this License or the recipients' rights hereunder. You may
choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of
Covered Software. However, you may do so only on Your own behalf,
and not on behalf of the Initial Developer or any Contributor.
You must make it absolutely clear that any such warranty, support,
indemnity or liability obligation is offered by You alone, and You
hereby agree to indemnify the Initial Developer and every
Contributor for any liability incurred by the Initial Developer or
such Contributor as a result of warranty, support, indemnity or
liability terms You offer.
3.5. Distribution of Executable Versions.
You may distribute the Executable form of the Covered Software
under the terms of this License or under the terms of a license of
Your choice, which may contain terms different from this License,
provided that You are in compliance with the terms of this License
and that the license for the Executable form does not attempt to
limit or alter the recipient's rights in the Source Code form from
the rights set forth in this License. If You distribute the
Covered Software in Executable form under a different license, You
must make it absolutely clear that any terms which differ from
this License are offered by You alone, not by the Initial
Developer or Contributor. You hereby agree to indemnify the
Initial Developer and every Contributor for any liability incurred
by the Initial Developer or such Contributor as a result of any
such terms You offer.
3.6. Larger Works.
You may create a Larger Work by combining Covered Software with
other code not governed by the terms of this License and
distribute the Larger Work as a single product. In such a case,
You must make sure the requirements of this License are fulfilled
for the Covered Software.
4. Versions of the License.
4.1. New Versions.
Sun Microsystems, Inc. is the initial license steward and may
publish revised and/or new versions of this License from time to
time. Each version will be given a distinguishing version number.
Except as provided in Section 4.3, no one other than the license
steward has the right to modify this License.
4.2. Effect of New Versions.
You may always continue to use, distribute or otherwise make the
Covered Software available under the terms of the version of the
License under which You originally received the Covered Software.
If the Initial Developer includes a notice in the Original
Software prohibiting it from being distributed or otherwise made
available under any subsequent version of the License, You must
distribute and make the Covered Software available under the terms
of the version of the License under which You originally received
the Covered Software. Otherwise, You may also choose to use,
distribute or otherwise make the Covered Software available under
the terms of any subsequent version of the License published by
the license steward.
4.3. Modified Versions.
When You are an Initial Developer and You want to create a new
license for Your Original Software, You may create and use a
modified version of this License if You: (a) rename the license
and remove any references to the name of the license steward
(except to note that the license differs from this License); and
(b) otherwise make it clear that the license contains terms which
differ from this License.
5. DISCLAIMER OF WARRANTY.
COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY
COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
DISCLAIMER.
6. TERMINATION.
6.1. This License and the rights granted hereunder will terminate
automatically if You fail to comply with terms herein and fail to
cure such breach within 30 days of becoming aware of the breach.
Provisions which, by their nature, must remain in effect beyond
the termination of this License shall survive.
6.2. If You assert a patent infringement claim (excluding
declaratory judgment actions) against Initial Developer or a
Contributor (the Initial Developer or Contributor against whom You
assert such claim is referred to as "Participant") alleging that
the Participant Software (meaning the Contributor Version where
the Participant is a Contributor or the Original Software where
the Participant is the Initial Developer) directly or indirectly
infringes any patent, then any and all rights granted directly or
indirectly to You by such Participant, the Initial Developer (if
the Initial Developer is not the Participant) and all Contributors
under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
notice from Participant terminate prospectively and automatically
at the expiration of such 60 day notice period, unless if within
such 60 day period You withdraw Your claim with respect to the
Participant Software against such Participant either unilaterally
or pursuant to a written agreement with Participant.
6.3. In the event of termination under Sections 6.1 or 6.2 above,
all end user licenses that have been validly granted by You or any
distributor hereunder prior to termination (excluding licenses
granted to You by any distributor) shall survive termination.
7. LIMITATION OF LIABILITY.
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO
NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
APPLY TO YOU.
8. U.S. GOVERNMENT END USERS.
The Covered Software is a "commercial item," as that term is
defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
computer software" (as that term is defined at 48
C.F.R. 252.227-7014(a)(1)) and "commercial computer software
documentation" as such terms are used in 48 C.F.R. 12.212
(Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48
C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
U.S. Government End Users acquire Covered Software with only those
rights set forth herein. This U.S. Government Rights clause is in
lieu of, and supersedes, any other FAR, DFAR, or other clause or
provision that addresses Government rights in computer software
under this License.
9. MISCELLANEOUS.
This License represents the complete agreement concerning subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. This License shall be governed
by the law of the jurisdiction specified in a notice contained
within the Original Software (except to the extent applicable law,
if any, provides otherwise), excluding such jurisdiction's
conflict-of-law provisions. Any litigation relating to this
License shall be subject to the jurisdiction of the courts located
in the jurisdiction and venue specified in a notice contained
within the Original Software, with the losing party responsible
for costs, including, without limitation, court costs and
reasonable attorneys' fees and expenses. The application of the
United Nations Convention on Contracts for the International Sale
of Goods is expressly excluded. Any law or regulation which
provides that the language of a contract shall be construed
against the drafter shall not apply to this License. You agree
that You alone are responsible for compliance with the United
States export administration regulations (and the export control
laws and regulation of any other countries) when You use,
distribute or otherwise make available any Covered Software.
10. RESPONSIBILITY FOR CLAIMS.
As between Initial Developer and the Contributors, each party is
responsible for claims and damages arising, directly or
indirectly, out of its utilization of rights under this License
and You agree to work with Initial Developer and Contributors to
distribute such responsibility on an equitable basis. Nothing
herein is intended or shall be deemed to constitute any admission
of liability.
--------------------------------------------------------------------
NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND
DISTRIBUTION LICENSE (CDDL)
For Covered Software in this distribution, this License shall
be governed by the laws of England and Wales (excluding
conflict-of-law provisions). Any disputes arising here from
shall be exclusively subject to the juristiction of the courts
of England and Wales.
# Makefile for ADFS
COMPONENT = ADFS
ASMHDRS = ADFS ADFSErr
ASMCHDRS = ADFS ADFSErr
HDRS =
CMHGFILE = ADFSHdr
CMHGDEPENDS = command module
OBJS = ata base command discop drive globals message miscop module swi
LIBS = ${SYNCLIB}
ROMCDEFINES = -DROM_MODULE
CLEAN_DEPEND = extra_clean
#CFLAGS += -DDEBUG_ENABLED
#CFLAGS += -DDEBUG_ENABLED -DDEBUGLIB -DDEBUGLIB_NOBRACKETS
#LIBS += ${DEBUGLIBS} ${NET5LIBS}
include CModule
expasmc.ADFSErr: hdr.ADFSErr
${HDR2H} hdr.ADFSErr ${C_EXP_HDR}.ADFSErr
expasm.ADFSErr: hdr.ADFSErr
${CP} hdr.ADFSErr ${EXP_HDR}.ADFSErr ${CPFLAGS}
hdr.ADFSErr: o.ADFSErr
${LD} -bin -o $@ o.ADFSErr
SetType $@ Text
ifeq (,${MAKE_VERSION})
# RISC OS / amu case
clean::
@IfThere hdr.ADFSErr Then ${ECHO} ${RM} hdr.ADFSErr
@IfThere hdr.ADFSErr Then ${RM} hdr.ADFSErr
else
# Posix / gmake case
clean::
${NOP}
endif
# Dynamic dependencies:
Dir <Obey$Dir>
amu clean
stripdepnd
Dir <Obey$Dir>
amu debug THROWBACK=-throwback
Dir <Obey$Dir>
amu export_hdrs
Dir <Obey$Dir>
amu gpa_debug
Dir <Obey$Dir>
amu standalone THROWBACK=-throwback
Dir <Obey$Dir>
amu rom
File added
Disc:disc
ExtEscape:Escape
BadDrive:Disc drive not known
/* (4.00)
*
* This file is automatically maintained by srccommit, do not edit manually.
* Last processed by srccommit version: 1.1.
*
*/
#define Module_MajorVersion_CMHG 4.00
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 12 Oct 2015
#define Module_MajorVersion "4.00"
#define Module_Version 400
#define Module_MinorVersion ""
#define Module_Date "12 Oct 2015"
#define Module_ApplicationDate "12-Oct-15"
#define Module_ComponentName "ADFS"
#define Module_ComponentPath "cddl/RiscOS/Sources/FileSys/ADFS/ADFS"
#define Module_FullVersion "4.00"
#define Module_HelpVersion "4.00 (12 Oct 2015)"
#define Module_LibraryVersionInfo "4:0"
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
/** \file command.c
* Implementation of star commands.
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include "swis.h"
#include "Global/CMOS.h"
#include "Global/FSNumbers.h"
#include "Global/OsBytes.h"
#include "Global/RISCOS.h"
#include "Interface/HighFSI.h"
#include "ADFSHdr.h"
#include "command.h"
#include "globals.h"
#include "message.h"
_kernel_oserror *command_adfs(void)
{
return _swix(OS_FSControl, _INR(0,1), FSControl_SelectFS, fsnumber_adfs);
}
_kernel_oserror *command_configure_syntax_adfsdircache(void)
{
printf("%s\n", message_lookup_direct("CADFDIR"));
return NULL;
}
_kernel_oserror *command_status_adfsdircache(void)
{
/* Use OS_ReadMemMapInfo to discover the RAM size, rather than relying on an
* undocumented return register from OS_SetEnv like old ADFS did! */
uint32_t page_size, num_pages, ram_size;
_swix(OS_ReadMemMapInfo, _OUTR(0,1), &page_size, &num_pages);
ram_size = page_size * num_pages;
/* Read dir cache from CMOS byte 199 */
uint32_t dir_cache = 0;
_swix(OS_Byte, _INR(0,1)|_OUT(2), OsByte_ReadCMOS, DIR_CACHE_BYTE, &dir_cache);
if (dir_cache == 0)
{
/* For consistency with old ADFS, we print the actual cache size even if the
* CMOS is set to zero.
* There's a chance we might be back-ported to tiny machines one day, so
* honour the rules for value 0 as with old ADFS. */
#ifdef ENABLE_BACKGROUND_TRANSFERS
if (ram_size <= 1024*1024)
dir_cache = ram_size / 512 / 1024;
else
dir_cache = ram_size / 256 / 1024;
#else
dir_cache = ram_size / 128 / 1024;
#endif
dir_cache = MIN(dir_cache, 255);
}
printf("ADFSDirCache %uK\n", dir_cache);
return NULL;
}
_kernel_oserror *command_syntax_adfsdircache(void)
{
printf("%s\n", message_lookup_direct("SADFDIR"));
return NULL;
}
_kernel_oserror *command_configure_adfsdircache(const char *dircache)
{
uint32_t dircache_num;
_kernel_oserror *e = _swix(OS_ReadUnsigned, _INR(0,1)|_OUT(2), 1<<30, dircache, &dircache_num);
if (e)
return e;
return _swix(OS_Byte, _INR(0,2), OsByte_WriteCMOS, DIR_CACHE_BYTE, dircache_num);
}
_kernel_oserror *command_configure_syntax_drive(void)
{
printf("%s\n", message_lookup_direct("CADFDRI"));
return NULL;
}
_kernel_oserror *command_status_drive(void)
{
uint32_t default_drive;
_swix(OS_Byte, _INR(0,1)|_OUT(2), OsByte_ReadCMOS, DEFAULT_DRIVE_BYTE, &default_drive);
default_drive = ((default_drive & DEFAULT_DRIVE_MASK) >> DEFAULT_DRIVE_SHIFT);
printf("Drive %u\n", default_drive);
return NULL;
}
_kernel_oserror *command_syntax_drive(void)
{
printf("%s\n", message_lookup_direct("SADFDRI"));
return NULL;
}
_kernel_oserror *command_configure_drive(const char *drive)
{
uint32_t default_drive;
uint32_t cmos;
char *after;
errno = 0;
default_drive = (uint32_t) strtoul(drive, &after, 10);
if (errno == ERANGE || after == drive)
return configure_NUMBER_NEEDED;
if (default_drive > DEFAULT_DRIVE_MASK >> DEFAULT_DRIVE_SHIFT)
return configure_TOO_LARGE;
_swix(OS_Byte, _INR(0,1)|_OUT(2), OsByte_ReadCMOS, DEFAULT_DRIVE_BYTE, &cmos);
cmos = cmos &~ DEFAULT_DRIVE_MASK;
cmos |= default_drive << DEFAULT_DRIVE_SHIFT;
_swix(OS_Byte, _INR(0,2), OsByte_WriteCMOS, DEFAULT_DRIVE_BYTE, cmos);
return NULL;
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
/** \file discop.c
* Implementation of low-level disc operation handlers.
*/
#include <string.h>
#include "swis.h"
#include "Global/NewErrors.h"
#include "Interface/FileCore.h"
#include "Interface/FileCoreErr.h"
#include "Interface/ADFSErr.h"
#include "Interface/ATA.h"
#include "ata.h"
#include "discop.h"
#include "globals.h"
#include "message.h"
/** Timeout to set on ATA commands (cs) */
#define OP_TIMEOUT (1000)
/** FileCore scatter list threshold */
#define SCATTER_THRESHOLD (0xFFFF0000u)
/** Move to the next scatter entry in an array; cope with FileCore-style looped lists */
#define ADVANCE_SCATTER_POINTER(p) \
do \
{ \
(p)++; \
if ((p)->length == 0 && (uintptr_t) (p)->address >= SCATTER_THRESHOLD) \
(p) = (scatter_t *) ((uintptr_t) (p) + (uintptr_t) (p)->address); \
} \
while (0)
/** Round a 64-bit number down to a power-of-2 */
#define ROUND_DOWN_64(number, bits) ((number) &~ ((1ull << (bits)) - 1))
/** Round a 64-bit number up to a power-of-2 */
#define ROUND_UP_64(number, bits) ROUND_DOWN_64(((number) + (1ull << (bits)) - 1), (bits))
uint8_t discop_decode_disc_error(_kernel_oserror *e)
{
/** Table mapping from offsets into SATADriver's error block to vaguely ST506-compatible disc error numbers */
static const uint8_t disc_error_lut[][2] = {
{ ErrorNumber_ATA_CmdBusy & 0xFF, 0x21 }, /* Drive busy when commanded -> Drive busy when commanded */
{ ErrorNumber_ATA_CmdNotRdy & 0xFF, 0x08 }, /* Drive not ready -> NRY Ready signal has been negated */
{ ErrorNumber_ATA_Busy & 0xFF, 0x22 }, /* Drive busy -> Drive busy on command completion */
{ ErrorNumber_ATA_WFT & 0xFF, 0x07 }, /* Drive write fault -> WFL Write fault */
{ ErrorNumber_ATA_ICRC & 0xFF, 0x26 }, /* Drive interface CRC error */
{ ErrorNumber_ATA_Packet & 0xFF, 0x25 }, /* Packet drive error */
{ ErrorNumber_ATA_Unknown & 0xFF, 0x24 }, /* Unknown code in error reg */
{ ErrorNumber_ATA_NDAM & 0xFF, 0x18 }, /* No data address mark -> NDA Missing address mark */
{ ErrorNumber_ATA_NTK0 & 0xFF, 0x09 }, /* No track 0 -> NSC No seek complete */
{ ErrorNumber_ATA_ABRT & 0xFF, 0x02 }, /* Drive abort -> IVC Invalid command */
{ ErrorNumber_ATA_IDNF & 0xFF, 0x16 }, /* Sector ID not found -> TOV ID not found within timeout */
{ ErrorNumber_ATA_UNC & 0xFF, 0x13 }, /* Uncorrectable error -> DFE Fatal ECC error in data area */
{ ErrorNumber_ATA_BBK & 0xFF, 0x17 }, /* Bad block -> NIA ID area started with an improper address mark */
};
if ((e->errnum &~ 0xFF) == ErrorBase_ATA)
for (size_t i = 0; i < sizeof disc_error_lut / sizeof *disc_error_lut; ++i)
if ((e->errnum & 0xFF) == disc_error_lut[i][0])
return disc_error_lut[i][1];
return 0;
}
static low_level_error_t transfer_with_retries(uint32_t reason,
uint32_t flags,
drive_t * restrict dr,
uint64_t * restrict disc_address,
block_or_scatter_t * restrict ptr,
size_t * restrict transfer_length)
{
low_level_error_t e;
/* Old ADFS only used retries for floppies and ST506 hard discs. Let's try
* reintroducing them. */
int32_t retries = reason == DiscOp_Verify ? 0 : g_retries.type.winnie;
do
{
size_t transferred, not_transferred;
e.oserror = NULL;
static const uint8_t command[3][2][2] = {
{ { ATACOMMAND_READ_VERIFY_SECTORS, ATACOMMAND_READ_VERIFY_SECTORS },
{ ATACOMMAND_READ_VERIFY_SECTORS_EXT, ATACOMMAND_READ_VERIFY_SECTORS_EXT }, },
{ { ATACOMMAND_READ_SECTORS, ATACOMMAND_READ_DMA, },
{ ATACOMMAND_READ_SECTORS_EXT, ATACOMMAND_READ_DMA_EXT, }, },
{ { ATACOMMAND_WRITE_SECTORS, ATACOMMAND_WRITE_DMA, },
{ ATACOMMAND_WRITE_SECTORS_EXT, ATACOMMAND_WRITE_DMA_EXT, }, },
};
ataop_param_lba48_t p = {
.sector_count0 = (uint8_t) (*transfer_length >> LOG2_SECTOR_SIZE),
.sector_count1 = (uint8_t) (*transfer_length >> (LOG2_SECTOR_SIZE + 8)),
.lba0 = (uint8_t) (*disc_address >> LOG2_SECTOR_SIZE),
.lba1 = (uint8_t) (*disc_address >> (LOG2_SECTOR_SIZE + 8)),
.lba2 = (uint8_t) (*disc_address >> (LOG2_SECTOR_SIZE + 16)),
.lba3 = (uint8_t) (*disc_address >> (LOG2_SECTOR_SIZE + 24)),
.lba4 = (uint8_t) (*disc_address >> (LOG2_SECTOR_SIZE + 32)),
.lba5 = (uint8_t) (*disc_address >> (LOG2_SECTOR_SIZE + 36)),
.device = DEVICE_MAGIC | DEVICE_LBA,
.command = command[reason][1][1]
};
flags |= ATAOp_DMA |
(dr->deviceid << ATAOp_DeviceIDShift) |
(dr->cpid << ATAOp_CPIDShift) |
(reason << ATAOp_TransShift);
dprintf("ATAOp: flags %08X, command %02X, ptr %p, len %u -> ",
flags, p.command, ptr->block, *transfer_length);
e.oserror = ata_op_fg_data(flags, 11, &p, ptr->block, *transfer_length, OP_TIMEOUT, &not_transferred);
if (e.oserror && e.oserror->errnum == ErrorNumber_TooComplex)
{
/* Special case: hit capability limit in controller, so recalculate */
dprintf("TooComplex: limit = %u\n", not_transferred);
*transfer_length = not_transferred;
break; /* No retries for this one */
}
dprintf("%s: not_transferred = %u\n", e.oserror ? e.oserror->errmess : "OK", not_transferred);
/* Advance buffer pointer or scatter list (as appropriate) according to
* however many bytes we successfully transferred */
transferred = *transfer_length - not_transferred;
*disc_address += transferred;
if (flags & ATAOp_Scatter)
while (transferred > 0)
{
size_t this_time = MIN(ptr->scatter->length, transferred);
ptr->scatter->address += this_time;
if ((ptr->scatter->length -= this_time) == 0)
ADVANCE_SCATTER_POINTER(ptr->scatter);
transferred -= this_time;
}
else
ptr->block += transferred;
*transfer_length = not_transferred;
if (e.oserror && e.oserror->errnum == ErrorNumber_Escape)
{
e.oserror = MESSAGE_ERRORLOOKUP(true, ExtEscape, 0);
break; /* No retries for this one */
}
if (e.oserror && transferred)
++retries; /* At least some data transferred, so it's not fair to decrement the retry count when we loop */
}
while (e.oserror != NULL && --retries >= 0);
return e;
}
static low_level_error_t buffered_transfer(uint32_t reason,
uint32_t flags,
drive_t *dr,
uint64_t * restrict disc_address,
block_or_scatter_t * restrict ptr,
size_t * restrict total_length)
{
/* The buffer will in practice always be small enough that we don't have to
* worry about "Transfer too complex" errors from the driver. */
low_level_error_t e;
e.number = 0;
if (reason == DiscOp_WriteSecs)
{
/* First read the existing contents of the disc */
uint64_t buffer_disc_address = *disc_address;
uint8_t *buffer = dr->block_buffer;
size_t remaining = SECTOR_SIZE;
e = transfer_with_retries(DiscOp_ReadSecs,
flags &~ ATAOp_Scatter,
dr,
&buffer_disc_address,
(block_or_scatter_t *) &buffer,
&remaining);
}
if (e.number == 0)
{
if (reason == DiscOp_WriteSecs)
{
/* Partially overwrite the buffer with the new data (leave the scatter list entries untouched at this point) */
block_or_scatter_t from = *ptr;
uint8_t *to = dr->block_buffer;
int32_t count = *total_length;
if (flags & ATAOp_Scatter)
while (count > 0)
{
size_t this_time = MIN(from.scatter->length, count);
memcpy(to, from.scatter->address, this_time);
ADVANCE_SCATTER_POINTER(from.scatter); /* doesn't matter if we advance on last iteration */
to += this_time;
count -= this_time;
}
else
memcpy(to, from.block, count);
}
/* Do the main transfer */
uint64_t buffer_disc_address = *disc_address;
uint8_t *buffer = dr->block_buffer;
size_t remaining = SECTOR_SIZE;
e = transfer_with_retries(reason,
flags &~ ATAOp_Scatter,
dr,
&buffer_disc_address,
(block_or_scatter_t *) &buffer,
&remaining);
/* Irrespective of whether there was an error or not, update the scatter list etc
* according to how many bytes were successfully read/written. Note that we have
* to be able to cope with an error after the "interesting" region. */
uint8_t *from = dr->block_buffer;
int32_t count = buffer - from;
count = MIN(count, *total_length);
*disc_address += count;
*total_length -= count;
if (flags & ATAOp_Scatter)
while (count > 0)
{
size_t this_time = MIN(ptr->scatter->length, count);
if (reason == DiscOp_ReadSecs)
{
memcpy(ptr->scatter->address, from, count);
from += count;
}
ptr->scatter->address += this_time;
if ((ptr->scatter->length -= this_time) == 0)
ADVANCE_SCATTER_POINTER(ptr->scatter);
count -= this_time;
}
else
{
if (reason == DiscOp_ReadSecs)
memcpy(ptr->block, from, count);
ptr->block += count;
}
}
return e;
}
low_level_error_t discop(uint32_t reason,
uint32_t flags,
uint32_t drive,
uint64_t * restrict disc_address,
block_or_scatter_t * restrict ptr,
size_t * restrict fg_length)
{
low_level_error_t e;
e.number = 0;
spinrw_read_lock(&g_drive_lock);
drive_t *dr = g_drive[drive - 4 + NUM_FLOPPIES];
if (drive < 4 || dr == NULL)
{
dprintf("Bad drive number: %u\n", drive);
e.number = BadDriveErr;
goto exit;
}
if (*disc_address + *fg_length > dr->capacity || !IS_SECTOR_ALIGNED(*disc_address))
{
dprintf("Disc address range :%u/%016llX-%016llX, limit %016llX, %ssector aligned\n",
drive, *disc_address, *disc_address + *fg_length, dr->capacity, IS_SECTOR_ALIGNED(*disc_address) ? "is " : "not ");
e.number = BadParmsErr;
goto exit;
}
#ifndef ENABLE_BACKGROUND_TRANSFERS
if (flags & ATAOp_Background)
{
dprintf("Background transfer not supported\n");
e.number = BadParmsErr;
goto exit;
}
#endif
// if ((flags & ATAOp_Scatter) == 0)
// {
// uint32_t flags;
// _swix(OS_ValidateAddress, _INR(0,1)|_OUT(_FLAGS), ptr->block, ptr->block + *fg_length, &flags);
// dprintf("Validate flags 0x%X\n", flags);
// if (flags & _C)
// while (1);
// if (flags & _N) // should be C but I want to trap with JTAG
// {
// dprintf("No memory at address\n");
// e.oserror = (_kernel_oserror *) "\0\0\0\0No memory at address";
// goto exit;
// }
// }
if (*fg_length == 0)
{
dprintf("Foreground length 0\n");
goto exit;
}
e.oserror = _swix(ATA_Control, _INR(0,1), ATAControl_Lock, (dr->cpid << ATAControl_CPIDShift) | (dr->deviceid << ATAControl_DeviceIDShift));
if (e.oserror)
{
dprintf("Slot lock returned %s\n", e.oserror->errmess);
goto exit;
}
/* Now we need to subdivide the operation into chunks that are acceptable to
* the ATA command set. Some additional restrictions imposed by the controller
* may be present; most of these are passed back to us by ATA_Op returning the
* error "Transfer too complex". Constraints are:
* - must start on sector boundaries (DiscOps are allowed to fault any that
* aren't though, so no additional action is needed here)
* - must end on word boundaries (may require read-modify-write)
* - must not exceed 256 sectors (LBA28 or CHS) or 65536 sectors (LBA48)
*/
{ /* remove warnings about jump past initialisation of variables */
uint64_t end_down = ROUND_DOWN_64(*disc_address + *fg_length, LOG2_SECTOR_SIZE);
uint64_t end_up = ROUND_UP_64(*disc_address + *fg_length, LOG2_SECTOR_SIZE);
uint32_t chunk_size = 65536 << LOG2_SECTOR_SIZE; // TODO 256 sectors for non-LBA48
dprintf("%016llX/%016llX/%016llX, chunk=%08X\n", *disc_address, end_down, end_up, chunk_size);
block_or_scatter_t ptr_for_verify_ops;
if (reason == DiscOp_Verify)
{
ptr = &ptr_for_verify_ops; /* so we don't corrupt R3 on exit */
flags &= ~ATAOp_Scatter; /* silly if it's set, but PRM allows it */
}
while (e.number == 0 && *disc_address < end_down)
{
size_t transfer_length = (size_t) MIN(chunk_size, end_down - *disc_address);
size_t remain = transfer_length;
e = transfer_with_retries(reason, flags, dr, disc_address, ptr, &remain);
while (e.oserror && e.oserror->errnum == ErrorNumber_TooComplex)
{
transfer_length = remain;
e = transfer_with_retries(reason, flags, dr, disc_address, ptr, &remain);
}
*fg_length -= transfer_length - remain;
}
if (e.number == 0 && *disc_address < end_up && *fg_length != 0)
e = buffered_transfer(reason, flags, dr, disc_address, ptr, fg_length);
}
if (e.oserror != 0)
{
/* Convert into a disc error if possible */
if ((e.oserror->errnum &~ 0xFF) == ErrorBase_ATA)
{
uint8_t disc_error = discop_decode_disc_error(e.oserror);
if (disc_error != 0)
{
dr->disc_error.drive = dr->drive;
dr->disc_error.disc_error = disc_error;
dr->disc_error.unused_MBZ = 0;
dr->disc_error.byte = *disc_address;
e.new_disc_error = &dr->disc_error;
e.flag.new_disc_error = true;
}
}
}
_swix(ATA_Control, _INR(0,1), ATAControl_Unlock, (dr->cpid << ATAControl_CPIDShift) | (dr->deviceid << ATAControl_DeviceIDShift));
exit:
spinrw_read_unlock(&g_drive_lock);
return e;
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
#include <stdlib.h>
#include <string.h>
#include "Global/CMOS.h"
#include "Global/OsBytes.h"
#include "Interface/ADFS.h"
#include "drive.h"
#include "globals.h"
#include "swi.h"
void drive_attached(uint32_t cpid, uint32_t deviceid, bool packet_device)
{
dprintf("%sdevice attached: %u:%u -> ", packet_device ? "packet " : "", cpid, deviceid);
_kernel_oserror *e;
drive_t *dr = malloc(sizeof *dr);
if (dr == NULL)
return;
memset(dr, 0, sizeof dr);
dr->drive = -1u;
dr->packet_device = packet_device;
dr->cpid = cpid;
dr->deviceid = deviceid;
uint32_t status, remain;
ataop_param_lba28_t params = {
.device = DEVICE_MAGIC,
.command = packet_device ? ATACOMMAND_IDENTIFY_PACKET_DEVICE : ATACOMMAND_IDENTIFY_DEVICE
};
e = swi_ide_user_op(ADFSIDEUserOp_NoDRDY |
(deviceid << ADFSIDEUserOp_DeviceIDShift) |
(cpid << ADFSIDEUserOp_CPIDShift) |
ADFSIDEUserOp_TransRead,
(uint8_t *) &params,
dr->identify_info,
sizeof dr->identify_info,
0, &status, &remain);
if (e != NULL || status != 0)
{
dprintf("%s\n", e->errmess);
free(dr);
return;
}
spinrw_write_lock(&g_drive_lock);
if (!packet_device)
{
uint32_t i;
for (i = NUM_FLOPPIES; i < NUM_FLOPPIES + NUM_WINNIES; i++)
{
/* Find a spare drive slot */
if (g_drive[i] == NULL)
break;
}
if (i < NUM_FLOPPIES + NUM_WINNIES)
{
dr->block_buffer = malloc(SECTOR_SIZE);
if (dr->block_buffer == NULL)
{
dprintf("fail to malloc block buffer\n");
free(dr);
spinrw_write_unlock(&g_drive_lock);
return;
}
g_drive[i] = dr;
dr->drive = i - NUM_FLOPPIES + 4;
uint32_t standby_timer = 0;
_swix(OS_Byte, _INR(0,1)|_OUT(2), OsByte_ReadCMOS, ADFSSpinDownCMOS, &standby_timer);
if (standby_timer)
{
ataop_param_lba28_t params = {
.device = DEVICE_MAGIC,
.sector_count = (uint8_t) standby_timer,
.command = ATACOMMAND_IDLE
};
/* Set the value in the drive, this could fail if the drive doesn't support
* power management. If it does, a default value of 0 remains. */
e = swi_ide_user_op(ADFSIDEUserOp_NoDRDY |
(dr->cpid << ADFSIDEUserOp_CPIDShift) | (dr->deviceid << ADFSIDEUserOp_DeviceIDShift) |
ADFSIDEUserOp_TransNone,
(uint8_t *) &params,
NULL, 0, 0, /* No data, default timeout */
&status, &remain);
if (!e && (status == 0))
{
dr->standby_timer = (uint8_t) standby_timer;
}
}
}
/* Assume 48-bit LBA support for now. Fortunately, value is little-endian */
dr->capacity = SECTOR_SIZE * *(uint64_t *)&dr->identify_info[OFFSET_MAX_LBA48];
dprintf("assigned to drive %u\n", dr->drive);
}
else
dprintf("not assigned a drive\n");
if (g_drive_list_tail)
g_drive_list_tail->next = dr;
else
g_drive_list_head = dr;
g_drive_list_tail = dr;
spinrw_write_unlock(&g_drive_lock);
}
void drive_detached(uint32_t cpid, uint32_t deviceid, bool packet_device)
{
IGNORE(packet_device);
spinrw_write_lock(&g_drive_lock);
drive_t *dr, *prev;
for (prev = NULL, dr = g_drive_list_head; dr != NULL; prev = dr, dr = dr->next)
{
if (dr->cpid == cpid && dr->deviceid == deviceid)
break;
}
if (dr != NULL)
{
if (prev == NULL)
g_drive_list_head = dr->next;
else
prev->next = dr->next;
if (dr->drive != -1u)
g_drive[dr->drive - 4 + NUM_FLOPPIES] = NULL;
free(dr->block_buffer);
free(dr);
}
spinrw_write_unlock(&g_drive_lock);
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
/** \file globals.c
* Definitions of global variables.
*/
#include "globals.h"
uint32_t g_message_fd[4];
void *g_module_pw;
void *g_filecore_pw;
void (*g_filecore_callback_floppy_op_completed)(void);
void (*g_filecore_callback_winnie_op_completed)(void);
void (*g_filecore_callback_release_fiq)(void);
char g_media_type_name[MEDIA_TYPE_NAME_LEN];
drive_t *g_drive_list_head;
drive_t *g_drive_list_tail;
drive_t *g_drive[NUM_FLOPPIES + NUM_WINNIES];
spinrwlock_t g_drive_lock = SPINRW_INITIALISER;
retries_t g_retries = { .type.winnie = 16, .type.floppy = 8, .type.floppy_mount = 3, .type.floppy_defect = 1 };
spinlock_t g_retries_lock = SPIN_INITIALISER;
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2012 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
/** \file message.c
* Internationalisation.
*/
#include <string.h>
#include <stdarg.h>
#include "kernel.h"
#include "swis.h"
#include "globals.h"
#include "message.h"
static _kernel_oserror m_ErrorBuf;
_kernel_oserror *message_error_lookup(bool local, uint32_t number, const char *name, ...)
{
const char *arg[4];
va_list ap;
va_start(ap, name);
/* Yes, this technically reads off the end of the stack frame in most cases,
but there will always be at least 4 words on the stack so this is safe */
for (int i = 0; i < 4; i++)
arg[i] = va_arg(ap, const char *);
va_end(ap);
/* Can't use MessageTrans_ErrorLookup because of the difficulty of allocating
an input error block in C (at least without wasting most of 256 bytes) */
m_ErrorBuf.errnum = number;
_kernel_oserror *e = _swix(MessageTrans_Lookup, _INR(0,7), local ? &g_message_fd : 0, name, m_ErrorBuf.errmess, sizeof m_ErrorBuf.errmess, arg[0], arg[1], arg[2], arg[3]);
if (e != NULL)
strncpy(m_ErrorBuf.errmess, strchr(name, ':') + 1, sizeof m_ErrorBuf.errmess);
return &m_ErrorBuf;
}
const char *message_lookup_direct(const char *token)
{
const char *result;
_kernel_oserror *e = _swix(MessageTrans_Lookup, _INR(0,2)|_OUT(2), &g_message_fd, token, 0, &result);
if (!e)
return result;
else
return e->errmess;
}
const char *message_lookup_buffer(const char *token, char *buffer, size_t buflen)
{
const char *result;
_kernel_oserror *e = _swix(MessageTrans_Lookup, _INR(0,3)|_OUT(2), &g_message_fd, token, buffer, buflen, &result);
/* Note that MessageTrans always null-terminates the result even if it's
* truncated, and the buffer length includes the terminator character. */
if (!e)
return result;
else
return e->errmess;
}
_kernel_oserror *message_copy(const _kernel_oserror *error)
{
m_ErrorBuf.errnum = error->errnum;
strcpy(m_ErrorBuf.errmess, error->errmess);
return &m_ErrorBuf;
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
/** \file miscop.c
* Implementation of low-level miscellaneous functions.
*/
#include "Interface/ATA.h"
#include "Interface/FileCore.h"
#include "Interface/FileCoreErr.h"
#include "discop.h"
#include "miscop.h"
/** Time between polls, in cs. This is the same as old ADFS uses with 82710 floppy controllers. */
#define POLL_PERIOD (100)
low_level_error_t miscop_mount(uint32_t drive, uint32_t byte_address, uint8_t * restrict buffer, size_t length, disc_record_t * restrict disc_record)
{
low_level_error_t e;
uint64_t disc_address = (uint64_t) byte_address & LegacyDiscAddress_ByteOffset_Mask;
IGNORE(disc_record);
dprintf("Mount - from :%u/%08X to %p, length %u, disc record %p\n",
drive, byte_address, buffer, length, (void *) disc_record);
e = discop(DiscOp_ReadSecs, ATAOp_DisableEscape, drive, &disc_address, (block_or_scatter_t *) &buffer, &length);
/* Unlike DiscOps, we don't update the pointers on exit */
/* Unlike old ADFS, we no longer use the hardware specific parameters from
* the disc record, so no need to parse the data that has been read. */
return e;
}
void miscop_poll_changed(uint32_t drive, uint32_t * restrict sequence_number, uint32_t * restrict result_flags)
{
/* For now, we only handle the hard disc case */
IGNORE(drive);
*sequence_number = 0;
*result_flags = MiscOp_PollChanged_EmptyWorks_Flag | MiscOp_PollChanged_ChangedWorks_Flag | MiscOp_PollChanged_NotChanged_Flag;
dprintf("PollChanged(drive %u) - return flags %03X, sequence_number %u\n", drive, *result_flags, *sequence_number);
return;
}
void miscop_lock_drive(uint32_t drive)
{
IGNORE(drive);
/* Could perhaps turn on the LED as the PRM suggests */
}
void miscop_unlock_drive(uint32_t drive)
{
IGNORE(drive);
/* Could perhaps turn off the LED as the PRM suggests */
}
void miscop_poll_period(uint32_t drive, uint32_t * restrict poll_period, const char ** restrict media_type_name)
{
IGNORE(drive);
*poll_period = POLL_PERIOD;
*media_type_name = g_media_type_name;
}
void miscop_eject(uint32_t drive)
{
/* Can't do this */
IGNORE(drive);
}
void miscop_drive_status(uint32_t drive, uint32_t *status_flags)
{
/* No need to hide drives unless/until we get hotplug support */
IGNORE(drive);
*status_flags = 0;
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
/** \file module.c
* Module entry point dispatch.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "swis.h"
#include "Global/CMOS.h"
#include "Global/FSNumbers.h"
#include "Global/OsBytes.h"
#include "Global/ModHand.h"
#include "Global/RISCOS.h"
#include "Global/SWIs.h"
#include "Interface/FileCore.h"
#include "Interface/FileCoreErr.h"
#include "Interface/ADFSErr.h"
#include "Interface/ATA.h"
#include "SyncLib/synclib.h"
#undef Module_Title /* unfortunate clash between CMHG and Hdr:ModHand */
#include "ADFSHdr.h"
#include "command.h"
#include "discop.h"
#include "drive.h"
#include "globals.h"
#include "message.h"
#include "miscop.h"
#include "swi.h"
/* This function is autogenerated by ResGen, and returns a pointer to a
* ResourceFS resource file data block */
extern void *Resources(void);
_kernel_oserror *module_initialise(const char *cmd_tail, int podule_base, void *pw)
{
_kernel_oserror *e;
uint32_t drive_handle = 0;
uint32_t drive_spec;
uint32_t num_winnies = 0;
uint32_t default_drive = 0;
uint32_t dir_cache = 0;
uint32_t file_cache = 0;
IGNORE(cmd_tail);
IGNORE(podule_base);
g_module_pw = pw;
#ifndef ROM_MODULE
e = _swix(ResourceFS_RegisterFiles, _IN(0), Resources());
if (e != NULL)
return e;
#else
/* In ROM builds, all resource files live in the Messages module instead */
#endif
e = _swix(MessageTrans_OpenFile, _INR(0,2), &g_message_fd, "Resources:$.Resources.ADFS.Messages", 0);
if (e != NULL)
{
#ifndef ROM_MODULE
_swix(ResourceFS_DeregisterFiles, _IN(0), Resources());
#endif
return e;
}
synclib_init();
#ifdef DEBUGLIB
/* Set up debugging */
debug_initialise(Module_Title, "", "");
debug_set_device(DADEBUG_OUTPUT);
debug_set_unbuffered_files(TRUE);
#endif
/* Initialise global variables */
message_lookup_buffer("Disc", g_media_type_name, MEDIA_TYPE_NAME_LEN);
/* Query SATADriver to see how many drives are attached. For now, we don't
* support hot-plugging, so we can save FileCore from allocating more
* map dynamic areas than we need by doing this before we call FileCore_Create */
while (!_swix(ATA_Enumerate, _INR(0,1)|_OUTR(1,2),
ATAEnumerate_Drives, drive_handle,
&drive_handle, &drive_spec) &&
drive_handle != 0)
{
drive_attached((drive_spec & ATAEnumerate_CPIDMask) >> ATAEnumerate_CPIDShift,
(drive_spec & ATAEnumerate_DeviceIDMask) >> ATAEnumerate_DeviceIDShift,
drive_spec & ATAEnumerate_PacketDevice);
if ((drive_spec & ATAEnumerate_PacketDevice) == 0 &&
num_winnies < NUM_WINNIES)
num_winnies++;
}
/* Read default drive from CMOS byte 11, bits 0-2 */
_swix(OS_Byte, _INR(0,1)|_OUT(2), OsByte_ReadCMOS, DEFAULT_DRIVE_BYTE, &default_drive);
default_drive = ((default_drive & DEFAULT_DRIVE_MASK) >> DEFAULT_DRIVE_SHIFT);
/* Use OS_ReadMemMapInfo to discover the RAM size, rather than relying on an
* undocumented return register from OS_SetEnv like old ADFS did! */
uint32_t page_size, num_pages, ram_size;
_swix(OS_ReadMemMapInfo, _OUTR(0,1), &page_size, &num_pages);
ram_size = page_size * num_pages;
/* Read dir cache from CMOS byte 199 */
_swix(OS_Byte, _INR(0,1)|_OUT(2), OsByte_ReadCMOS, DIR_CACHE_BYTE, &dir_cache);
if (dir_cache == 0)
{
/* There's a chance we might be back-ported to tiny machines one day, so
* honour the rules for value 0 as with old ADFS. */
#ifdef ENABLE_BACKGROUND_TRANSFERS
if (ram_size <= 1024*1024)
dir_cache = ram_size / 512;
else
dir_cache = ram_size / 256;
#else
dir_cache = ram_size / 128;
#endif
dir_cache = MIN(dir_cache, 255*1024);
}
else
dir_cache *= 1024;
#ifdef ENABLE_BACKGROUND_TRANSFERS
/* Read file cache from CMOS byte 137 */
_swix(OS_Byte, _INR(0,1)|_OUT(2), OsByte_ReadCMOS, FILE_CACHE_BYTE, &file_cache);
if (file_cache == 1)
{
/* There's a chance we might be back-ported to tiny machines one day, so
* honour the rules for value 1 as with old ADFS. */
if (ram_size <= 1024*1024)
file_cache = ram_size / 512 / 1024;
else
file_cache = ram_size / 256 / 1024;
file_cache = MIN(file_cache, 255*1024);
}
#endif
/* Register with FileCore */
e = _swix(FileCore_Create, _INR(0,6)|_OUTR(0,3),
(uint32_t []) {
/* not CreateFlag_FixedDiscNeedsFIQ */
/* TODO: CreateFlag_FloppyNeedsFIQ depends on whether floppies use DMA */
/* not CreateFlag_NoBigBuf - only currently used by RAMFS, probably
* most useful there because you don't get a speed benefit by
* doing additional RAM buffering of a RAM filing system */
/* not CreateFlag_FixedDiscsMountLikeFloppy */
/* not CreateFlag_FixedDiscPollChangeSupport */
CreateFlag_FloppyEjects |
/* not CreateFlag_FixedDiscEjects */
CreateFlag_DriveStatusWorks |
CreateFlag_BigDiscSupport |
CreateFlag_NewErrorSupport |
/* not CreateFlag_FloppyDiscsMountLikeFixed */
((uint32_t)fsnumber_adfs << (Create_Id * 8)),
FILING_SYSTEM_TITLE - MODULE_BASE_ADDRESS,
FILING_SYSTEM_AUTHOR " " FILING_SYSTEM_TITLE - MODULE_BASE_ADDRESS,
(const char *)module_discop_veneer - MODULE_BASE_ADDRESS,
(const char *)module_miscop_veneer - MODULE_BASE_ADDRESS
},
MODULE_BASE_ADDRESS,
g_module_pw,
NUM_FLOPPIES | (num_winnies << 8) | (default_drive << 16),
dir_cache,
file_cache,
-1, /* obsolete parameter - pass -1 for consistency with ADFS etc */
&g_filecore_pw,
&g_filecore_callback_floppy_op_completed,
&g_filecore_callback_winnie_op_completed,
&g_filecore_callback_release_fiq);
if (e) goto failed_init;
return NULL;
failed_init:
_swix(OS_Module, _INR(0,1), ModHandReason_Delete, "FileCore%" FILING_SYSTEM_TITLE);
_swix(MessageTrans_CloseFile, _IN(0), &g_message_fd);
#ifndef ROM_MODULE
_swix(ResourceFS_DeregisterFiles, _IN(0), Resources());
#endif
return e;
}
_kernel_oserror *module_finalise(int fatal, int podule, void *pw)
{
IGNORE(fatal);
IGNORE(podule);
IGNORE(pw);
/* Kill our supporting FileCore instantiation */
_swix(OS_Module, _INR(0,1), ModHandReason_Delete, "FileCore%" FILING_SYSTEM_TITLE);
/* Free dynamic memory allocations */
while (g_drive_list_head)
drive_detached(g_drive_list_head->cpid, g_drive_list_head->deviceid, g_drive_list_head->packet_device);
_swix(MessageTrans_CloseFile, _IN(0), &g_message_fd);
#ifndef ROM_MODULE
_swix(ResourceFS_DeregisterFiles, _IN(0), Resources());
#endif
return 0;
}
_kernel_oserror *module_command(const char *arg_string, int argc, int cmd_no, void *pw)
{
IGNORE(pw);
size_t max_args = cmd_no != CMD_ADFS ? 8 : argc; /* argc not valid for *Configure commands */
char *argv[MAX(max_args,1)];
char arg_buffer[1024]; /* command line length limit - can't need more than that */
/* Parse arguments */
size_t args = 0;
if (arg_string != arg_CONFIGURE_SYNTAX && arg_string != arg_STATUS)
{
const char *in = arg_string;
char *out = arg_buffer;
while (args < max_args) /* should always be true, except for *Configure commands with lots of arguments */
{
/* Skip leading / separating / trailing spaces */
while (*in == ' ') ++in;
if (*in == 0 || *in == 10 || *in == 13)
break; /* end of command line */
argv[args++] = out;
bool arg_is_quoted = *in == '"'; /* quote rules only apply if first char is a quote */
bool quote_active = false;
while (true)
{
if (*in == 0 || *in == 10 || *in == 13 || (*in == ' ' && !quote_active))
break; /* end of argument */
if (arg_is_quoted && *in == '"')
{
if (quote_active && in[1] == '"')
*out++ = '"'; /* two quote characters is an escaped quote */
--quote_active; /* toggle quote state */
}
else
*out++ = *in;
++in;
}
*out++ = '\0'; /* terminate our copy of the argument */
}
}
switch (cmd_no)
{
case CMD_ADFS:
return command_adfs();
#ifdef ENABLE_BACKGROUND_TRANSFERS
case CMD_ADFSbuffers:
if (arg_string == arg_CONFIGURE_SYNTAX)
return command_configure_syntax_adfsbuffers();
if (arg_string == arg_STATUS)
return command_status_adfsbuffers();
if (args != 1)
return command_syntax_adfsbuffers();
else
return command_configure_adfsbuffers(argv[0]);
#endif
case CMD_ADFSDirCache:
if (arg_string == arg_CONFIGURE_SYNTAX)
return command_configure_syntax_adfsdircache();
if (arg_string == arg_STATUS)
return command_status_adfsdircache();
if (args != 1)
return command_syntax_adfsdircache();
else
return command_configure_adfsdircache(argv[0]);
case CMD_Drive:
if (arg_string == arg_CONFIGURE_SYNTAX)
return command_configure_syntax_drive();
if (arg_string == arg_STATUS)
return command_status_drive();
if (args != 1)
return command_syntax_drive();
else
return command_configure_drive(argv[0]);
}
/* Shouldn't get here */
return NULL;
}
_kernel_oserror *module_swi(int swi_offset, _kernel_swi_regs *r, void *pw)
{
IGNORE(r);
IGNORE(pw);
switch (swi_offset)
{
#define PASS_TO_FILECORE(entry) \
case ADFS_##entry - ADFS_00: \
{ \
int orig_r8 = r->r[8]; \
r->r[8] = (int) g_filecore_pw; \
_kernel_oserror *e = _kernel_swi(FileCore_##entry, r, r); \
r->r[8] = orig_r8; \
return e; \
}
PASS_TO_FILECORE(DiscOp)
PASS_TO_FILECORE(Drives)
PASS_TO_FILECORE(FreeSpace)
PASS_TO_FILECORE(DescribeDisc)
PASS_TO_FILECORE(MiscOp)
PASS_TO_FILECORE(SectorDiscOp)
PASS_TO_FILECORE(FreeSpace64)
PASS_TO_FILECORE(DiscOp64)
//case ADFS_HDC - ADFS_00: obsolete (ST506 only)
case ADFS_Retries - ADFS_00:
return swi_retries(r->r[0], (uint32_t *)&r->r[1], (uint32_t *)&r->r[2], (uint32_t *)&r->r[3]);
//case ADFS_VetFormat - ADFS_00: TODO: floppy support
//case ADFS_FlpProcessDCB - ADFS_00: TODO: floppy support
case ADFS_ControllerType - ADFS_00:
return swi_controller_type(r->r[0], (uint32_t *)&r->r[0]);
case ADFS_PowerControl - ADFS_00:
switch (r->r[0])
{
case 0:
return swi_power_control_read_spin_state(r->r[1], (uint32_t *)&r->r[2]);
case 1:
return swi_power_control_set_autospindown(r->r[1], r->r[2], (uint32_t *)&r->r[3]);
case 2:
return swi_power_control_control_spin(r->r[1], r->r[2] == 0);
default:
return MESSAGE_ERRORLOOKUP(true, BadParms, 0);
}
//case ADFS_SetIDEController - ADFS_00: TODO: PATA support
case ADFS_IDEUserOp - ADFS_00:
return swi_ide_user_op(r->r[0], (uint8_t *)r->r[2], (uint8_t *)r->r[3], r->r[4], r->r[5], (uint32_t *)&r->r[0], (size_t *)&r->r[4]);
//case ADFS_ECCSAndRetries - ADFS_00: not supported; was only used for Acorn's production test of hard drives
case ADFS_LockIDE - ADFS_00:
return swi_lock_ide(r->r[0]);
case ADFS_IDEDeviceInfo - ADFS_00:
switch (r->r[0])
{
case 0:
return swi_ide_device_info_by_id((r->r[1] & 1) * 8, r->r[1] / 2, (uint32_t *)&r->r[1], (uint32_t *)&r->r[2], (void **)&r->r[3]);
case 1:
return swi_ide_device_info_by_id(r->r[1] & 15, r->r[1] / 16, (uint32_t *)&r->r[1], (uint32_t *)&r->r[2], (void **)&r->r[3]);
case 2:
return swi_ide_device_info_by_drive(r->r[1], (uint32_t *)&r->r[1], (uint32_t *)&r->r[2], (void **)&r->r[3]);
default:
return MESSAGE_ERRORLOOKUP(true, BadParms, 0);
}
case ADFS_ATAPIOp - ADFS_00:
return swi_atapi_op(r->r[0], r->r[1], (uint8_t *)r->r[2], (uint8_t *)r->r[3], r->r[4], r->r[5], (uint32_t *)&r->r[0], (uint8_t **)&r->r[3], (size_t *)&r->r[4]);
default:
return MESSAGE_ERRORLOOKUP(false, BadSWI, "ADFS");
}
return NULL;
}
_kernel_oserror *module_miscop_handler(_kernel_swi_regs *r, void *pw)
{
IGNORE(pw);
dprintf("MiscOp %u\n", r->r[0]);
switch (r->r[0])
{
case MiscOp_Mount:
return miscop_mount(r->r[1], r->r[2], (uint8_t *) r->r[3], r->r[4], (disc_record_t *) r->r[5]).oserror;
case MiscOp_PollChanged:
miscop_poll_changed(r->r[1], (uint32_t *) &r->r[2], (uint32_t *) &r->r[3]);
return NULL;
case MiscOp_LockDrive:
miscop_lock_drive(r->r[1]);
return NULL;
case MiscOp_UnlockDrive:
miscop_unlock_drive(r->r[1]);
return NULL;
case MiscOp_PollPeriod:
miscop_poll_period(r->r[1], (uint32_t *) &r->r[5], (const char **) &r->r[6]);
return NULL;
case MiscOp_Eject:
miscop_eject(r->r[1]);
return NULL;
case MiscOp_DriveStatus:
miscop_drive_status(r->r[1], (uint32_t *) &r->r[2]);
return NULL;
case MiscOp_ReadInfo: /* shouldn't be used for low-level MiscOp */
default:
return (_kernel_oserror *) BadParmsErr;
}
}
_kernel_oserror *module_discop_handler(_kernel_swi_regs *r, void *pw)
{
_kernel_oserror *e;
uint32_t reason;
uint32_t flags;
uint64_t disc_address;
IGNORE(pw);
dprintf("DiscOp %u, flags %03X, disc address %u/%08X, buffer %08X, length %u\n",
r->r[1] & DiscOp_Op_Mask,
r->r[1] &~ DiscOp_Op_Mask,
(uint32_t) r->r[2] >> 29,
r->r[2] &~ 0xE0000000,
r->r[3],
r->r[4]);
reason = r->r[1] & DiscOp_Op_Mask;
switch (reason)
{
/* Ops that we support */
case DiscOp_Verify:
if (r->r[1] & DiscOp_Op_BackgroundOp_Flag)
return (_kernel_oserror *) BadParmsErr;
/* else drop through */
case DiscOp_ReadSecs:
case DiscOp_WriteSecs:
flags = r->r[1] & (DiscOp_Op_ScatterList_Flag | DiscOp_Op_IgnoreEscape_Flag | DiscOp_Op_IgnoreTimeout_Flag | DiscOp_Op_BackgroundOp_Flag);
flags *= ATAOp_Scatter / DiscOp_Op_ScatterList_Flag;
disc_address = ((uint64_t) r->r[2] & LegacyDiscAddress_SectorOffset_Mask) << LOG2_SECTOR_SIZE;
if (disc_address + r->r[4] >= 1ull << (LegacyDiscAddress_DriveNumber_Shift + LOG2_SECTOR_SIZE))
/* We won't be able to update the disc address properly on exit, at
* least in the SectorDiscOp world */
return (_kernel_oserror *) BadParmsErr;
e = discop(reason, flags,
(uint32_t) r->r[2] >> LegacyDiscAddress_DriveNumber_Shift, &disc_address,
(block_or_scatter_t *) &r->r[3], (size_t *) &r->r[4]
).oserror;
r->r[2] = (r->r[2] &~ LegacyDiscAddress_SectorOffset_Mask) | (uint32_t) (disc_address >> LOG2_SECTOR_SIZE);
return e;
/* Ops that we don't support, but lack of support is harmless */
case DiscOp_Seek:
case DiscOp_Restore:
case DiscOp_StepIn:
case DiscOp_StepOut:
case DiscOp_Specify:
case DiscOp_WriteTrk: /* bodge for now to make HForm work */
return NULL;
/* Ops that we don't support, but caller would expect to do something */
case DiscOp_ReadTrk:
// case DiscOp_WriteTrk: bodge for now to make HForm work
/* drop through */
/* Anything else suggests a caller error, including DiscOp_CachedReadSecs which shouldn't get here */
default:
return (_kernel_oserror *) BadParmsErr;
}
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "Licence").
* You may not use this file except in compliance with the Licence.
*
* You can obtain a copy of the licence at
* cddl/RiscOS/Sources/FileSys/ADFS/ADFS/LICENCE.
* See the Licence for the specific language governing permissions
* and limitations under the Licence.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the Licence file. If applicable, add the
* following below this CDDL HEADER, with the fields enclosed by
* brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Ben Avison. All rights reserved.
* Use is subject to license terms.
*/
/** \file swi.c
* Implementation of SWI handlers for FS-specific SWIs.
*/
#include <stddef.h>
#include "kernel.h"
#include "Global/NewErrors.h"
#include "Interface/ADFS.h"
#include "Interface/ADFSErr.h"
#include "Interface/ATA.h"
#include "SyncLib/spinrw.h"
#include "ata.h"
#include "discop.h"
#include "globals.h"
#include "message.h"
#include "swi.h"
#define DEFAULT_OP_TIMEOUT (1000)
#define DEVICE_CONTROL_OP_TIMEOUT (50)
#define SOFTWARE_RESET_ASSERT_TIME (1) /* really only needs 50us (PATA, or 5us SATA) */
#define SOFTWARE_RESET_RECOVERY_TIME (50)
_kernel_oserror *swi_retries(uint32_t bic, uint32_t *eor, uint32_t *old, uint32_t *new)
{
uint32_t retries;
*eor &= bic;
spin_lock(&g_retries_lock);
*old = retries = g_retries.word;
*new = g_retries.word = (retries &~ bic) ^ *eor;
spin_unlock(&g_retries_lock);
return NULL;
}
_kernel_oserror *swi_controller_type(uint32_t drive, uint32_t *type)
{
if (drive > 7)
return MESSAGE_ERRORLOOKUP(true, BadDrive, 0);
if (drive < 4 || g_drive[drive - 4 + NUM_FLOPPIES] == NULL)
*type = ADFSControllerType_None;
else
*type = ADFSControllerType_WinnieIDE;
return NULL;
}
_kernel_oserror *swi_power_control_read_spin_state(uint32_t drive, uint32_t *state)
{
_kernel_oserror *e = NULL;
spinrw_read_lock(&g_drive_lock);
drive_t *dr = g_drive[drive - 4 + NUM_FLOPPIES];
if (drive < 4 || dr == NULL)
{
e = MESSAGE_ERRORLOOKUP(true, BadDrive, 0);
}
if (!e)
{
uint32_t status, remain;
ataop_param_lba28_t params = {
.device = DEVICE_MAGIC,
.command = ATACOMMAND_CHECK_POWER_MODE
};
e = swi_ide_user_op(ADFSIDEUserOp_NoDRDY |
(dr->cpid << ADFSIDEUserOp_CPIDShift) | (dr->deviceid << ADFSIDEUserOp_DeviceIDShift) |
ADFSIDEUserOp_TransNone,
(uint8_t *) &params,
NULL, 0, 0, /* No data, default timeout */
&status, &remain);
if (!e && (status == 0))
{
/* There was no attempt to interpret this value is old ADFS, so don't try here
* either, even though there are some non zero values that mean 'spun down'. */
*state = params.sector_count;
}
}
spinrw_read_unlock(&g_drive_lock);
return e;
}
_kernel_oserror *swi_power_control_set_autospindown(uint32_t drive, uint32_t new, uint32_t *old)
{
_kernel_oserror *e = NULL;
spinrw_write_lock(&g_drive_lock);
drive_t *dr = g_drive[drive - 4 + NUM_FLOPPIES];
if (drive < 4 || dr == NULL)
{
e = MESSAGE_ERRORLOOKUP(true, BadDrive, 0);
}
if (!e)
{
uint32_t status, remain;
ataop_param_lba28_t params = {
.device = DEVICE_MAGIC,
.sector_count = (uint8_t) new,
.command = ATACOMMAND_IDLE
};
e = swi_ide_user_op(ADFSIDEUserOp_NoDRDY |
(dr->cpid << ADFSIDEUserOp_CPIDShift) | (dr->deviceid << ADFSIDEUserOp_DeviceIDShift) |
ADFSIDEUserOp_TransNone,
(uint8_t *) &params,
NULL, 0, 0, /* No data, default timeout */
&status, &remain);
if (!e)
{
/* Update soft copy of the autospindown time */
*old = dr->standby_timer;
dr->standby_timer = (uint8_t) new;
}
}
spinrw_write_unlock(&g_drive_lock);
return e;
}
_kernel_oserror *swi_power_control_control_spin(uint32_t drive, bool spindown)
{
_kernel_oserror *e = NULL;
spinrw_read_lock(&g_drive_lock);
drive_t *dr = g_drive[drive - 4 + NUM_FLOPPIES];
if (drive < 4 || dr == NULL)
{
e = MESSAGE_ERRORLOOKUP(true, BadDrive, 0);
}
if (!e)
{
uint32_t status, remain;
ataop_param_lba28_t params = {
.device = DEVICE_MAGIC,
.sector_count = dr->standby_timer,
.command = spindown ? ATACOMMAND_STANDBY : ATACOMMAND_IDLE
};
e = swi_ide_user_op(ADFSIDEUserOp_NoDRDY |
(dr->cpid << ADFSIDEUserOp_CPIDShift) | (dr->deviceid << ADFSIDEUserOp_DeviceIDShift) |
ADFSIDEUserOp_TransNone,
(uint8_t *) &params,
NULL, 0, 0, /* No data, default timeout */
&status, &remain);
}
spinrw_read_unlock(&g_drive_lock);
return e;
}
static void delay(uint32_t howlong)
{
int32_t now, timeout;
_swix(OS_ReadMonotonicTime, _OUT(0), &now);
timeout = now + howlong + 1;
do
{
_swix(OS_ReadMonotonicTime, _OUT(0), &now);
}
while (timeout - now >= 0);
}
static _kernel_oserror *validate_address(void *base, size_t size, bool read, bool write)
{
uint8_t *low = base;
uint8_t *high = low + size;
uint32_t flags;
_kernel_oserror *e = _swix(OS_Memory, _INR(0,2)|_OUT(1), 24, low, high, &flags);
if (e == NULL)
{
if ((read && (flags & 4) == 0) ||
(write && (flags & 8) == 0))
return message_error_lookup(false, ErrorNumber_BadAddress, "BadAddress");
else
return NULL;
}
else
{
/* Fallback method for older kernels */
if (!write && (uintptr_t)low > 0xFC000000ul)
return NULL; /* Assume read from ROM is OK.
* ROM was already at a high address by the time of the first
* version of CDFSSoftATAPI that used ADFS_ATAPIOp. */
_swix(OS_ValidateAddress, _INR(0,1)|_OUT(_FLAGS), low, high, &flags);
if (flags & _C)
return message_error_lookup(false, ErrorNumber_BadAddress, "BadAddress");
else
return NULL;
}
}
_kernel_oserror *swi_ide_user_op(uint32_t flags, uint8_t *param, uint8_t *buffer, size_t length, uint32_t timeout, uint32_t *status, size_t *remain)
{
_kernel_oserror *e;
*status = 0;
e = _swix(ATA_Control, _INR(0,1), ATAControl_TryLock,
(flags & (ADFSIDEUserOp_DeviceIDMask | ADFSIDEUserOp_CPIDMask)) >> (ADFSIDEUserOp_DeviceIDShift - ATAControl_DeviceIDShift));
if (e)
{
dprintf("Lock failed (%X)\n", (flags & (ADFSIDEUserOp_DeviceIDMask | ADFSIDEUserOp_CPIDMask)) >> (ADFSIDEUserOp_DeviceIDShift - ATAControl_DeviceIDShift));
return e;
}
if (flags & ADFSIDEUserOp_Reset)
{
/* Reset - achieved by issuing a software reset */
ataop_param_device_control_t params = CONTROL_SRST;
e = ata_op_fg_nodata(ATAOp_NoDRDY | (flags & (ADFSIDEUserOp_DeviceIDMask | ADFSIDEUserOp_CPIDMask)),
1, &params, DEVICE_CONTROL_OP_TIMEOUT);
if (e)
goto exit;
delay(SOFTWARE_RESET_ASSERT_TIME);
params = 0;
e = ata_op_fg_nodata(ATAOp_NoDRDY | (flags & (ADFSIDEUserOp_DeviceIDMask | ADFSIDEUserOp_CPIDMask)),
1, &params, DEVICE_CONTROL_OP_TIMEOUT);
if (e)
goto exit;
delay(SOFTWARE_RESET_RECOVERY_TIME);
}
else
{
/* Issue a command */
uint8_t *device_param = param + (flags & ADFSIDEUserOp_LBA48 ? 9 : 5);
bool slave = *device_param & DEVICE_DEV;
uint32_t plength = flags & ADFSIDEUserOp_LBA48 ? 11 : 7;
e = validate_address(param, plength, true, true);
if (!e && (flags & (ADFSIDEUserOp_TransRead | ADFSIDEUserOp_TransWrite)) != 0)
e = validate_address(buffer, length, flags & ADFSIDEUserOp_TransWrite, flags & ADFSIDEUserOp_TransRead);
if (e)
goto exit;
if (timeout == 0)
timeout = DEFAULT_OP_TIMEOUT;
flags = (flags & (ADFSIDEUserOp_NoDRDY | ADFSIDEUserOp_DeviceIDMask | ADFSIDEUserOp_CPIDMask | ADFSIDEUserOp_TransMask))
| ((flags & ADFSIDEUserOp_DMA) / (ADFSIDEUserOp_DMA / ATAOp_DMA)) |
ATAOp_DisableEscape;
if (slave)
{
/* Backwards compatibility fudge */
*device_param &= ~DEVICE_DEV;
flags |= 8 << ATAOp_DeviceIDShift;
}
void *data = buffer;
scatter_t scatter[2];
uint32_t space;
if ((length & 3) && (flags & ATAOp_TransRead))
{
/* Data block is not a whole number of words long. There's not a whole lot
* we can do for writes (we're not in control of the parameter block so
* can't convert into a read-modify-write) so let ATA_Op generate the
* error for us in that case. But for reads, round up the transfer to a
* whole number of words and throw away the excess bytes. */
scatter[0].address = buffer;
scatter[0].length = length;
scatter[1].address = (uint8_t *) &space;
scatter[1].length = 4 - (length & 3);
data = &scatter;
length += scatter[1].length;
flags |= ATAOp_Scatter;
}
e = ata_op_fg_data(flags, plength, param, data, length, timeout, remain);
if (slave)
*device_param |= DEVICE_DEV;
if (e)
{
dprintf("ATA_Op(%X) failed\n", flags);
*status = discop_decode_disc_error(e);
if (*status != 0)
e = NULL;
}
}
exit:
_swix(ATA_Control, _INR(0,1), ATAControl_Unlock,
(flags & (ADFSIDEUserOp_DeviceIDMask | ADFSIDEUserOp_CPIDMask)) >> (ADFSIDEUserOp_DeviceIDShift - ATAControl_DeviceIDShift));
return e;
}
_kernel_oserror *swi_lock_ide(bool lock)
{
_kernel_oserror *e = NULL;
drive_t *lock_dr, *unlock_dr;
spinrw_read_lock(&g_drive_lock);
if (lock)
{
for (lock_dr = g_drive_list_head; lock_dr != NULL; lock_dr = lock_dr->next)
{
e = _swix(ATA_Control, _INR(0,1), ATAControl_TryLock,
(lock_dr->deviceid << ATAControl_DeviceIDShift) | (lock_dr->cpid << ATAControl_CPIDShift));
if (e)
break;
}
if (e)
{
for (unlock_dr = g_drive_list_head; unlock_dr != lock_dr; unlock_dr = unlock_dr->next)
{
_swix(ATA_Control, _INR(0,1), ATAControl_Unlock,
(unlock_dr->deviceid << ATAControl_DeviceIDShift) | (unlock_dr->cpid << ATAControl_CPIDShift));
}
e = MESSAGE_ERRORLOOKUP(true, DriverInUse, 0);
}
}
else
{
for (unlock_dr = g_drive_list_head; unlock_dr != NULL; unlock_dr = unlock_dr->next)
{
_swix(ATA_Control, _INR(0,1), ATAControl_Unlock,
(unlock_dr->deviceid << ATAControl_DeviceIDShift) | (unlock_dr->cpid << ATAControl_CPIDShift));
}
}
spinrw_read_unlock(&g_drive_lock);
return e;
}
_kernel_oserror *swi_ide_device_info_by_id(uint32_t deviceid, uint32_t cpid, uint32_t *type, uint32_t *drive, void **identify_block)
{
*type = ADFSIDEDeviceInfo_None;
*drive = -1u;
*identify_block = NULL;
spinrw_read_lock(&g_drive_lock);
for (drive_t *dr = g_drive_list_head; dr != NULL; dr = dr->next)
{
if (dr->deviceid == deviceid && dr->cpid == cpid)
{
*type = dr->packet_device ? ADFSIDEDeviceInfo_Packet : ADFSIDEDeviceInfo_NonPacket;
*drive = dr->drive;
*identify_block = &dr->identify_info;
break;
}
}
spinrw_read_unlock(&g_drive_lock);
return NULL;
}
_kernel_oserror *swi_ide_device_info_by_drive(uint32_t drive, uint32_t *type, uint32_t *id, void **identify_block)
{
if (drive > 7)
return MESSAGE_ERRORLOOKUP(true, BadDrive, 0);
spinrw_read_lock(&g_drive_lock);
if (drive < 4 || g_drive[drive - 4 + NUM_FLOPPIES] == NULL)
{
*type = ADFSIDEDeviceInfo_None;
*id = -1u;
*identify_block = NULL;
}
else
{
*type = ADFSIDEDeviceInfo_NonPacket;
*id = (g_drive[drive - 4 + NUM_FLOPPIES]->deviceid << ADFSIDEDeviceInfo_DeviceIDShift) |
(g_drive[drive - 4 + NUM_FLOPPIES]->cpid << ADFSIDEDeviceInfo_CPIDShift);
*identify_block = g_drive[drive - 4 + NUM_FLOPPIES]->identify_info;
}
spinrw_read_unlock(&g_drive_lock);
return NULL;
}
_kernel_oserror *swi_atapi_op(uint32_t flags, size_t control_len, uint8_t *control, uint8_t *buffer, size_t length, uint32_t timeout, uint32_t *status, uint8_t **buffer_end, size_t *remain)
{
_kernel_oserror *e;
*status = 0;
e = _swix(ATA_Control, _INR(0,1), ATAControl_TryLock,
(flags & (ADFSATAPIOp_DeviceIDMask | ADFSATAPIOp_CPIDMask)) >> (ADFSATAPIOp_DeviceIDShift - ATAControl_DeviceIDShift));
if (e)
return e;
if (timeout == 0)
timeout = DEFAULT_OP_TIMEOUT;
if (flags & ADFSIDEUserOp_Reset)
{
ataop_param_lba28_t params = {
.device = DEVICE_MAGIC,
.command = ATACOMMAND_RESET_DEVICE
};
e = ata_op_fg_nodata(ATAOp_NoDRDY | (flags & (ADFSATAPIOp_DeviceIDMask | ADFSATAPIOp_CPIDMask)) | ATAOp_DisableEscape,
7, &params, timeout);
}
else
{
/* Issue a command */
e = validate_address(control, control_len, true, false);
if (!e && (flags & (ADFSATAPIOp_TransRead | ADFSATAPIOp_TransWrite)) != 0)
e = validate_address(buffer, length, flags & ADFSATAPIOp_TransWrite, flags & ADFSATAPIOp_TransRead);
if (e)
goto exit;
flags = (flags & (ADFSATAPIOp_DeviceIDMask | ADFSATAPIOp_CPIDMask | ADFSATAPIOp_TransMask))
| ((flags & (ADFSATAPIOp_DMA | ADFSATAPIOp_DMADIR)) / (ADFSATAPIOp_DMA / ATAPacketOp_DMA)) |
ATAPacketOp_DisableEscape;
void *data = buffer;
scatter_t scatter[2];
uint32_t space;
if ((length & 3) && (flags & ATAPacketOp_TransRead))
{
/* Data block is not a whole number of words long. There's not a whole lot
* we can do for writes (we're not in control of the control block so
* can't convert into a read-modify-write) so let ATA_PacketOp generate the
* error for us in that case. But for reads, round up the transfer to a
* whole number of words and throw away the excess bytes. */
scatter[0].address = buffer;
scatter[0].length = length;
scatter[1].address = (uint8_t *) &space;
scatter[1].length = 4 - (length & 3);
data = &scatter;
length += scatter[1].length;
flags |= ATAPacketOp_Scatter;
}
e = ata_packet_op_fg(flags, control_len, control, data, length, timeout, remain);
*buffer_end = buffer + length - *remain;
}
exit:
if (e)
{
*status = discop_decode_disc_error(e);
if (*status != 0)
e = NULL;
}
_swix(ATA_Control, _INR(0,1), ATAControl_Unlock,
(flags & (ADFSATAPIOp_DeviceIDMask | ADFSATAPIOp_CPIDMask)) >> (ADFSATAPIOp_DeviceIDShift - ATAControl_DeviceIDShift));
return e;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment