/* Disk functions Copyright (C) 1996 Pete A. Zaitcev 1996,1997 Jakub Jelinek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "silo.h" typedef int (*Read0) (int dev, int num_blks, int blk_st, char *buf); typedef int (*Seek2) (int, int, int); typedef int (*Read2) (int, char *, int); typedef struct { int fd_; int net_; int seekp_; int floppy_; Read0 read0_; Seek2 seek2_; Read2 read2_; const struct linux_romvec *promvec; } Disk; static Disk disk0; char bootdevice[4096]; static char currentdevice[4096]; int open (char *device) { register Disk *t = &disk0; strcpy (currentdevice, device); t->net_ = 0; t->floppy_ = 0; if (t->promvec->pv_romvers < 2) { char buffer[20], *p; if (strlen (device) < 20) { /* v0 prom likes to paint in devopen parameter sometimes... */ strcpy (buffer, device); p = buffer; } else p = device; t->fd_ = (*t->promvec->pv_v0devops.v0_devopen) (p); if (device[0] == 'f' && device[1] == 'd') t->floppy_ = 1; else if ((device[0] == 'l' || device[0] == 'i') && device[1] == 'e') t->net_ = 1; } else { int node; char buffer[20]; t->fd_ = (*t->promvec->pv_v2devops.v2_dev_open) (device); if ((unsigned)(t->fd_ + 1) > 1) { node = (*t->promvec->pv_v2devops.v2_inst2pkg) (t->fd_); prom_getstring (node, "device_type", buffer, 20); if (!strcmp (buffer, "network")) t->net_ = 1; } } if (t->fd_ == 0 || t->fd_ == -1) { printf ("\nFatal error: Couldn't open device %s\n", device); return -1; } return 0; } extern char boot_part; int diskinit (const struct linux_romvec *promvec) { register Disk *t = &disk0; t->promvec = promvec; t->fd_ = 0; t->seekp_ = -1; t->read0_ = 0; t->seek2_ = 0; t->read2_ = 0; if (promvec->pv_romvers < 2) { struct linux_arguments_v0 *ap = *promvec->pv_v0bootargs; char *s = bootdevice; int unit; *s++ = ap->boot_dev[0]; *s++ = ap->boot_dev[1]; *s++ = '('; *s++ = (ap->boot_dev_ctrl & 07) + '0'; *s++ = ','; if ((*s = ap->boot_dev_unit / 10 + '0') != '0') s++; *s++ = ap->boot_dev_unit % 10 + '0'; *s++ = ','; *s++ = boot_part + '0'; *s++ = ')'; *s = 0; t->read0_ = promvec->pv_v0devops.v0_rdblkdev; } else { char *p; t->seek2_ = (Seek2) promvec->pv_v2devops.v2_dev_seek; t->read2_ = promvec->pv_v2devops.v2_dev_read; strcpy (bootdevice, *promvec->pv_v2bootargs.bootpath); p = strchr (bootdevice, 0); if (*(p - 2) == ':' && *(p - 1) >= 'a' && *(p - 1) <= 'h') *(p - 1) = boot_part + 'a'; else *p++ = ':'; *p++ = boot_part + 'a'; *p = 0; } return open (bootdevice); } void reopen(void) { char c; c = *currentdevice; close (); *currentdevice = c; open (currentdevice); } int read (char *buff, int size, int offset) { register Disk *t = &disk0; if (!size) return 0; if (t->read0_) { if (t->net_) return (*t->promvec->pv_v0devops.v0_rdnetdev) (t->fd_, size, buff); else { char buffer[512]; int i = 0, j, rc, ret = 0; if (offset & 0x1ff) { if (size > 512 - (offset & 0x1ff)) i = 512 - (offset & 0x1ff); else i = size; for (j = 0; j < 5; j++) { rc = (*t->read0_) (t->fd_, 1, offset >> 9, buffer); if (rc) break; reopen(); } if (rc != 1) return -1; memcpy (buff, buffer + (offset & 0x1ff), i); buff += i; size -= i; offset = ((offset + 512) & 0x1ff); ret = i; } if (size >> 9) { for (j = 0; j < 5; j++) { rc = (*t->read0_) (t->fd_, size >> 9, offset >> 9, buff); if (rc) break; reopen(); } if (rc != size >> 9) return -1; i = (size & (~0x1ff)); ret += i; buff += i; offset += i; } size &= 0x1ff; if (size) { for (j = 0; j < 5; j++) { rc = (*t->read0_) (t->fd_, 1, offset >> 9, buffer); if (rc) break; reopen(); } if (rc != 1) return -1; memcpy (buff, buffer, size); ret += size; } return ret; } } else { int rc; if (!t->net_) { if (((romvec->pv_printrev >> 16) < 2 || ((romvec->pv_printrev >> 16) == 2 && (romvec->pv_printrev && 0xffff) < 6)) && offset >= 0x40000000) { printf ("Buggy old PROMs don't allow reading past 1GB from start of the disk. Send complains to SMCC\n"); return -1; } if (t->seekp_ != offset) { if ((*t->seek2_) (t->fd_, 0, offset) == -1) return -1; t->seekp_ = offset; } } rc = (*t->read2_) (t->fd_, buff, size); if (!t->net_) { t->seekp_ += size; if (rc == size) return size; } else return rc; } return -1; } int xmit (char *buff, int size) { register Disk *t = &disk0; if (!t->net_) return -1; if (t->read0_) return (*t->promvec->pv_v0devops.v0_wrnetdev) (t->fd_, size, buff); else return (*t->promvec->pv_v2devops.v2_dev_write) (t->fd_, buff, size); } void close () { if (*currentdevice) { if (disk0.promvec->pv_romvers < 2) (*disk0.promvec->pv_v0devops.v0_devclose) (disk0.fd_); else (*disk0.promvec->pv_v2devops.v2_dev_close) (disk0.fd_); } *currentdevice = 0; } int setdisk (char *device) { if (!strcmp (currentdevice, device)) { return 0; } close (); return open (device); }