/*
* Copyright 1997,1998 by Apple Computer, Inc.
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appears in all copies and
* that both the copyright notice and this permission notice appear in
* supporting documentation.
*
* APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
* NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// for printf()
#include <stdio.h>
// for malloc() & free()
#include <stdlib.h>
#include <ATA.h>
// for SCSI command structures
#include "MacSCSICommand.h"
#include "ATA_media.h"
#include "util.h"
/*
* Global Variables
*/
static long ata_inited = 0;
static struct ATA_manager ata_mgr;
/*
* Forward declarations
*/
int ATAManagerPresent(void);
int ATAHardwarePresent(void);
pascal SInt16 ataManager(ataPB *pb);
void ata_init(void);
ATA_MEDIA new_ata_media(void);
long read_ata_media(MEDIA m, long long offset, uint32_t count, void *address);
long write_ata_media(MEDIA m, long long offset, uint32_t count, void *address);
long close_ata_media(MEDIA m);
long os_reload_ata_media(MEDIA m);
long compute_id(long bus, long device);
pascal SInt16 ataManager(ataPB *pb);
int ATA_ReadBlock(UInt32 deviceID, ATA_INFO info, UInt32 block_size, UInt32 block, UInt8 *address);
int ATA_WriteBlock(UInt32 deviceID, ATA_INFO info, UInt32 block_size, UInt32 block, UInt8 *address);
long get_info(long id, struct ATA_identify_drive_info *ip);
long get_pi_info(long id, struct ATAPI_identify_drive_info *ip);
long is_atapi(long id);
long read_atapi_media(MEDIA m, long long offset, uint32_t count, void *address);
long write_atapi_media(MEDIA m, long long offset, uint32_t count, void *address);
int ATAPI_ReadBlock(UInt32 deviceID, UInt32 block_size, UInt32 block, UInt8 *address);
int ATAPI_TestUnitReady(UInt32 deviceID);
int ATAPI_ReadCapacity(UInt32 deviceID, uint32_t *block_size, uint32_t *blocks);
ATA_MEDIA_ITERATOR new_ata_iterator(void);
void reset_ata_iterator(MEDIA_ITERATOR m);
char *step_ata_iterator(MEDIA_ITERATOR m);
void delete_ata_iterator(MEDIA_ITERATOR m);
int ata_bus_present(int num);
/*
* Routines
*/
#if GENERATINGPOWERPC
pascal SInt16
ataManager(ataPB *pb)
{
#ifdef applec
#if sizeof(SInt16) > 4
#error "Result types larger than 4 bytes are not supported."
#endif
#endif
long private_result;
long
compute_id(long bus, long device)
{
long id;
int i;
id = -1;
for (i = 0; i < ata_mgr.busCount; i++) {
if (bus == ata_mgr.bus_list[i]) {
break;
}
}
if (i >= ata_mgr.busCount) {
/* bad bus id */
} else if (ata_mgr.version.major < 3) {
if (device != 0) {
/* bad device id */
} else {
id = bus & 0xFF;
}
} else {
if (device < 0 || device > 1) {
/* bad device id */
} else {
id = ((device & 0xFF) << 8) | (bus & 0xFF);
}
}
return id;
}
static long
get_info(long id, struct ATA_identify_drive_info *ip)
{
ataIdentify pb;
ataDevConfiguration pb2;
OSErr status;
long rtn_value;
long atapi;
status = ataManager((ataPB*) &pb );
if (status != noErr) {
//printf("is atatpi status = %d\n", status);
} else if (pb.ataDeviceType == kDevATAPI) {
atapi = 1;
/* the drive can be asleep or something in which case this doesn't work */
/* how do we do reads */
}
}
return atapi;
}
MEDIA
open_ata_as_media(long bus, long device)
{
ATA_MEDIA a;
long id;
struct ATA_identify_drive_info info;
uint8_t *buf;
uint32_t total;
if (ata_inited == 0) {
ata_init();
}
if (ata_mgr.exists == 0) {
//printf("ATA manager does not exist\n");
return 0;
}
id = compute_id(bus, device);
if (id < 0) {
return 0;
} else if (is_atapi(id)) {
a = (ATA_MEDIA) open_atapi_as_media(bus, device);
} else {
a = 0;
if (get_info(id, &info) != 0) {
a = new_ata_media();
if (a != 0) {
a->m.kind = ata_mgr.kind;
if ((info.capabilities & LBA_CAPABLE) != 0) {
total = info.lba_sectors;
a->info.lba = 1;
a->info.heads = 0;
a->info.sectors = 0;
} else {
/* Only CHS - Cylinder Head Sector addressing */
total = info.total_sectors;
a->info.lba = 0;
a->info.heads = info.cur_heads;
a->info.sectors = info.cur_sec_per_track;
}
{ /* XXX this should be a loop in a subroutine */
buf = malloc(2048);
if (ATA_ReadBlock(id, &a->info, 512, 0, buf)) {
a->m.grain = 512;
} else if (ATA_ReadBlock(id, &a->info, 1024, 0, buf)) {
a->m.grain = 1024;
} else if (ATA_ReadBlock(id, &a->info, 2048, 0, buf)) {
a->m.grain = 2048;
} else {
a->m.grain = 512; /* XXX should really return failure here */
}
free(buf);
}
if (total == 0) {
a->m.size_in_bytes = ((long long)1000) * a->m.grain; /* XXX not right */
} else {
a->m.size_in_bytes = ((long long)total) * a->m.grain;
}
a->m.do_read = read_ata_media;
a->m.do_write = write_ata_media;
a->m.do_close = close_ata_media;
a->m.do_os_reload = os_reload_ata_media;
a->id = id;
}
} else {
printf("ATA - couldn't get info\n");
}
}
return (MEDIA) a;
}
long
read_ata_media(MEDIA m, long long offset, uint32_t count, void *address)
{
ATA_MEDIA a;
ataIOPB pb;
OSErr status;
long rtn_value;
long block;
long block_count;
long block_size;
uint8_t *buffer;
int i;
a = (ATA_MEDIA) m;
rtn_value = 0;
if (a == 0) {
/* no media */
} else if (a->m.kind != ata_mgr.kind) {
/* wrong kind - XXX need to error here - this is an internal problem */
} else if (count <= 0 || count % a->m.grain != 0) {
/* can't handle size */
} else if (offset < 0 || offset % a->m.grain != 0) {
/* can't handle offset */
} else if (offset + count > a->m.size_in_bytes) {
/* check for offset (and offset+count) too large */
} else {
/* do a read on the physical device */
block_size = a->m.grain;
block = offset / block_size;
block_count = count / block_size;
buffer = address;
rtn_value = 1;
for (i = 0; i < block_count; i++) {
if (ATA_ReadBlock(a->id, &a->info, block_size, block, buffer) == 0) {
rtn_value = 0;
break;
}
buffer += block_size;
block += 1;
}
}
return rtn_value;
}
long
write_ata_media(MEDIA m, long long offset, uint32_t count, void *address)
{
ATA_MEDIA a;
long rtn_value;
long block;
long block_count;
long block_size;
uint8_t *buffer;
int i;
a = (ATA_MEDIA) m;
rtn_value = 0;
if (a == 0) {
/* no media */
} else if (a->m.kind != ata_mgr.kind) {
/* XXX need to error here - this is an internal problem */
} else if (count <= 0 || count % a->m.grain != 0) {
/* can't handle size */
} else if (offset < 0 || offset % a->m.grain != 0) {
/* can't handle offset */
} else if (offset + count > a->m.size_in_bytes) {
/* check for offset (and offset+count) too large */
} else {
/* do a write on the physical device */
block_size = a->m.grain;
block = offset / block_size;
block_count = count / block_size;
buffer = address;
rtn_value = 1;
for (i = 0; i < block_count; i++) {
if (ATA_WriteBlock(a->id, &a->info, block_size, block, buffer) == 0) {
rtn_value = 0;
break;
}
buffer += block_size;
block += 1;
}
}
return rtn_value;
}
long
close_ata_media(MEDIA m)
{
ATA_MEDIA a;
a = (ATA_MEDIA) m;
if (a == 0) {
return 0;
} else if (a->m.kind != ata_mgr.kind) {
/* XXX need to error here - this is an internal problem */
return 0;
}
/* XXX nothing to do - I think? */
return 1;
}
long
os_reload_ata_media(MEDIA m)
{
printf("Reboot your system so the partition table will be reread.\n");
return 1;
}
int
ATA_ReadBlock(UInt32 deviceID, ATA_INFO info, UInt32 block_size, UInt32 block, UInt8 *address)
{
ataIOPB pb;
OSErr status;
long slave;
long lba, cyl, head, sector;
if (status != noErr) {
//printf("get pi info status = %d\n", status);
rtn_value = 0;
} else {
rtn_value = 1;
}
return rtn_value;
}
MEDIA
open_atapi_as_media(long bus, long device)
{
ATA_MEDIA a;
long id;
struct ATAPI_identify_drive_info info;
uint8_t *buf;
uint32_t block_size;
uint32_t blocks;
if (ata_inited == 0) {
ata_init();
}
if (ata_mgr.exists == 0) {
return 0;
}
id = compute_id(bus, device);
if (!is_atapi(id)) {
a = 0;
} else {
a = 0;
if (get_pi_info(id, &info) != 0
&& (info.capabilities & LBA_CAPABLE) != 0) {
if (ATAPI_TestUnitReady(id) != 0) {
a = new_ata_media();
if (a != 0) {
a->m.kind = ata_mgr.kind;
if (ATAPI_ReadCapacity(id, &block_size, &blocks) == 0) {
block_size = 2048;
blocks = 1000;
}
a->m.grain = block_size;
a->m.size_in_bytes = ((long long)blocks) * a->m.grain;
a->m.do_read = read_atapi_media;
a->m.do_write = write_atapi_media;
a->m.do_close = close_ata_media;
a->m.do_os_reload = os_reload_ata_media;
a->id = id;
}
} else {
printf("ATAPI - unit not ready\n");
}
} else {
printf("ATAPI - couldn't get info or not LBA capable\n");
}
}
return (MEDIA) a;
}
long
read_atapi_media(MEDIA m, long long offset, uint32_t count, void *address)
{
ATA_MEDIA a;
ataIOPB pb;
OSErr status;
long rtn_value;
long block;
long block_count;
long block_size;
uint8_t *buffer;
int i;
a = (ATA_MEDIA) m;
rtn_value = 0;
if (a == 0) {
/* no media */
} else if (a->m.kind != ata_mgr.kind) {
/* wrong kind - XXX need to error here - this is an internal problem */
} else if (count <= 0 || count % a->m.grain != 0) {
/* can't handle size */
} else if (offset < 0 || offset % a->m.grain != 0) {
/* can't handle offset */
} else if (offset + count > a->m.size_in_bytes) {
/* check for offset (and offset+count) too large */
} else {
/* XXX do a read on the physical device */
block_size = a->m.grain;
block = offset / block_size;
block_count = count / block_size;
buffer = address;
rtn_value = 1;
for (i = 0; i < block_count; i++) {
if (ATAPI_ReadBlock(a->id, block_size, block, buffer) == 0) {
rtn_value = 0;
break;
}
buffer += block_size;
block += 1;
}
}
return rtn_value;
}
long
write_atapi_media(MEDIA m, long long offset, uint32_t count, void *address)
{
return 0;
}
int
ATAPI_ReadBlock(UInt32 deviceID, UInt32 block_size, UInt32 block, UInt8 *address)
{
ataIOPB pb;
OSErr status;
long slave;
ATAPICmdPacket cmdPacket;
SCSI_10_Byte_Command *gRead;
long count;
void
reset_ata_iterator(MEDIA_ITERATOR m)
{
ATA_MEDIA_ITERATOR a;
a = (ATA_MEDIA_ITERATOR) m;
if (a == 0) {
/* no media */
} else if (a->m.kind != ata_mgr.kind) {
/* wrong kind - XXX need to error here - this is an internal problem */
} else if (a->m.state != kInit) {
a->m.state = kReset;
}
}
char *
step_ata_iterator(MEDIA_ITERATOR m)
{
ATA_MEDIA_ITERATOR a;
char *result;
a = (ATA_MEDIA_ITERATOR) m;
if (a == 0) {
/* no media */
} else if (a->m.kind != ata_mgr.kind) {
/* wrong kind - XXX need to error here - this is an internal problem */
} else {
switch (a->m.state) {
case kInit:
/* find # of buses (done in ata_init) */
a->m.state = kReset;
/* fall through to reset */
case kReset:
a->bus_index = 0 /* low bus id */;
a->bus = ata_mgr.bus_list[a->bus_index];
a->id = 0 /* low device id */;
a->m.state = kIterating;
/* fall through to iterate */
case kIterating:
while (1) {
if (a->bus_index >= ata_mgr.busCount/* max bus id */) {
break;
}
if (a->id > 1 /*max id for bus */) {
a->bus_index += 1;
a->bus = ata_mgr.bus_list[a->bus_index];
a->id = 0 /* low device id */;
continue; /* try again */
}
if (a->bus > 9) {
// insure that name creation works
break;
}
/* generate result */
result = (char *) malloc(20);
if (result != NULL) {
snprintf(result, 20, "/dev/ata%c.%c",
'0'+a->bus, '0'+a->id);
}
a->id += 1; /* next id */
return result;
}
a->m.state = kEnd;
/* fall through to end */
case kEnd:
default:
break;
}
}
return 0 /* no entry */;
}
void
delete_ata_iterator(MEDIA_ITERATOR m)
{
return;
}
#pragma mark -
#ifdef notdef
MEDIA
open_linux_ata_as_media(long index)
{
long bus;
long id;
long i;
i = index / 2;
if (i >= ata_mgr.busCount) {
// set bogus id
bus = 0;
id = 2;
} else {
bus = ata_mgr.bus_list[index / 2];
id = index % 2;
}
return open_ata_as_media(bus, id);
}
#else
MEDIA
open_linux_ata_as_media(long index)
{
long bus;
long id;
bus = index / 2;
id = index % 2;
return open_ata_as_media(bus, id);
}
#endif
char *
linux_ata_name(long bus, long id)
{
char *result;
if (bus >= 13) {
// a bus >= 13 would be a bogus character
return NULL;
}
result = (char *) malloc(20);
if (result != NULL) {
/* name is hda, hdb, hdc, hdd, ...
* in order (0,0) (0,1) (1,0) (1,1) ...
*/
snprintf(result, 20, "/dev/hd%c", 'a' + (bus*2 + id));
}
return result;
}