Rev 2 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Project: S.Ha.R.K.
*
* Coordinators:
* Giorgio Buttazzo <giorgio@sssup.it>
* Paolo Gai <pj@gandalf.sssup.it>
*
* Authors :
* Paolo Gai <pj@gandalf.sssup.it>
* Massimiliano Giorgi <massy@gandalf.sssup.it>
* Luca Abeni <luca@gandalf.sssup.it>
* (see the web pages for full authors list)
*
* ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
*
* http://www.sssup.it
* http://retis.sssup.it
* http://shark.sssup.it
*/
/***************************************
CVS : $Id: idelow.c,v 1.1.1.1 2002-03-29 14:12:49 pj Exp $
Revision: $Revision: 1.1.1.1 $
Last update: $Date: 2002-03-29 14:12:49 $
This module is responsable of the protocol between the IDE device driver
interface and the host (the computer).
***************************************/
/*
* Copyright (C) 1999,2000 Massimiliano Giorgi
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "glue.h"
#include <fs/bdevinit.h>
#include <fs/magic.h>
#include <fs/assert.h>
#include <fs/util.h>
#include <fs/maccess.h>
#include "bdev.h"
#include "phdsk.h"
#include "lodsk.h"
#include "ide.h"
#include "idereq.h"
#include "bqueue.h"
/*+ some kernel depend features implemented +*/
#include "ideglue.h"
/*
* TRACER FLAGS
*/
/* if defined use tracer user events 1 and 2 before and after blocking
* for synchronization (so we can compute the waiting time for a
* request)
*/
#define TRACEWAIT
#undef TRACEWAIT
#ifdef TRACEWAIT
#include <trace/types.h>
#include <kernel/trace.h>
#endif
/*
* FLAGS
*/
/* if defined:
* enable "set features" commands (so we can set features on ide drives).
*/
#define IDE_ENABLESETFEATURES 1
//#undef IDE_ENABLESETFEATURES
/* if defined:
* add a delay into the IRQ handler
* (my old cdrom seems to clear the IRQ at the end of the data in/out
* not when reading the STATUS register)
* PS: if the DEBUG_SERVER is defined this delay isn't required.
*/
#define IDE_OLDATAPIDELAY 1
#undef IDE_OLDATAPIDELAY
/* if defined:
* after the soft reset timeout expired do a soft reset
* also if the device is busy
* (some old cdrom seems not to clear the busy bits ?!?)
*/
#define IDE_FORCERESET 1
//#undef IDE_FORCERESET
/*
* if defined:
* after issuing a command does not test for command in progress
* (does not read the status register!)
* DANGER: need for all TX motherboard
*/
#define IDE_SKIPSTATUSTEST 1
//#undef IDE_SKIPSTATUSTEST
/*
* if defined:
* skip all test when issuing a command!
* (it imply IDE_SKIPSTATUSTEST)
* DANGER: need on some TX motherboard
* on few TX motherboards PIO mode is not safe (DMA must be used)
*/
#define IDE_SKIPALLTEST 1
#undef IDE_SKIPALLTEST
#ifdef IDE_SKIPALLTEST
#define IDE_SKIPSTATUSTEST 1
#endif
/*
* DEBUG
*/
/* show server activity */
#define DEBUG_SERVER KERN_DEBUG
#undef DEBUG_SERVER
/* show request and result */
#define DEBUG_SHOWREQUEST KERN_DEBUG
#undef DEBUG_SHOWREQUEST
/* trace soft reset function */
#define DEBUG_SOFTRESET KERN_DEBUG
#undef DEBUG_SOFTRESET
/* trace REQ_EPILOG (the epilog of every device request) */
#define DEBUG_REQEPILOG KERN_DEBUG
#undef DEBUG_REQEPILOG
/* trace do_io_request() (WARNIG can cause a deadline miss)*/
#define DEBUG_IOREQUEST KERN_DEBUG
#undef DEBUG_IOREQUEST
/**/
#ifdef DEBUG_SERVER
#define printk0(ideif,fmt,args...) \
printk(DEBUG_SERVER "ide%i server:" fmt,ideif,##args)
#else
#define printk0(fmt,args...)
#endif
#ifdef DEBUG_SHOWREQUEST
#define printk1(ideif,fmt,args...) \
printk(DEBUG_SHOWREQUEST "ide%i request:" fmt,ideif,##args)
#else
#define printk1(fmt,args...)
#endif
#ifdef DEBUG_SOFTRESET
#define printk2(ideif,fmt,args...) \
printk(DEBUG_SOFTRESET "ide%i soft reset:" fmt,ideif,##args)
#else
#define printk2(fmt,args...)
#endif
#ifdef DEBUG_REQEPILOG
#define printk3(ideif,fmt,args...) \
printk(DEBUG_REQEPILOG "ide%i req_epilog:" fmt,ideif,##args)
#else
#define printk3(fmt,args...)
#endif
#ifdef DEBUG_IOREQUEST
#define printk4(ideif,fmt,args...) \
printk(DEBUG_IOREQUEST fmt)
#else
#define printk4(fmt,args...)
#endif
/*
*
* REGISTERS
*
*/
/*
* from ATA/ATAPI 4 (T13/1153D revision 18)
* paragraph 7.x
*/
/* io_port registers (usually on 0x1f0 for first IDE interface) */
#define REG_DATA 0x00
#define REG_ERROR 0x01
#define REG_FEATURES 0x01
#define REG_SECCOU 0x02
#define REG_SECNUM 0x03
#define REG_CYLLOW 0x04
#define REG_CYLHIG 0x05
#define REG_DEVHEAD 0x06
#define REG_STATUS 0x07
#define REG_COMMAND 0x07
/* to register the i/o space */
#define IOPORT_OFFSET 0
#define IOPORT_LEN 8
/* io_port2 registers (usually on 0x3f0 for first IDE interface) */
#define REG_ALTSTATUS 0x06
#define REG_DEVCTRL 0x06
/* to register the i/o space */
#define IOPORT2_OFFSET 6
#define IOPORT2_LEN 1
/* to use with VM_in/VM_out */
/* for read/write */
#define IDE_REG_DATA (ide[ideif].io_port+REG_DATA)
#define IDE_REG_SECCOU (ide[ideif].io_port+REG_SECCOU)
#define IDE_REG_SECNUM (ide[ideif].io_port+REG_SECNUM)
#define IDE_REG_CYLLOW (ide[ideif].io_port+REG_CYLLOW)
#define IDE_REG_CYLHIG (ide[ideif].io_port+REG_CYLHIG)
#define IDE_REG_DEVHEAD (ide[ideif].io_port+REG_DEVHEAD)
/* for read */
#define IDE_REG_STATUS (ide[ideif].io_port+REG_STATUS)
#define IDE_REG_ERROR (ide[ideif].io_port+REG_ERROR)
#define IDE_REG_ALTSTATUS (ide[ideif].io_port2+REG_ALTSTATUS)
/* for write */
#define IDE_REG_COMMAND (ide[ideif].io_port+REG_COMMAND)
#define IDE_REG_FEATURES (ide[ideif].io_port+REG_ERROR)
#define IDE_REG_DEVCTRL (ide[ideif].io_port2+REG_DEVCTRL)
/* for status & alt_status register */
#define STATUS_BUSY 0x80
#define STATUS_DRDY 0x40
#define STATUS_R_FAULT 0x20
#define STATUS_R_SEEKCOM 0x10
#define STATUS_DRQ 0x08
#define STATUS_R_DWC 0x04
#define STATUS_R_CMDPROG 0x02
#define STATUS_ERR 0x01
/* for error register */
#define ERROR_OBS_BADBLOCK 0x80
#define ERROR_OBS_CRCECC 0x40
#define ERROR_OBS_IDNOTFOUND 0x10
#define ERROR_ABORTCMD 0x04
#define ERROR_OBS_TRACK00 0x02
#define ERROR_OBS_DAMNOTFOUND 0x01
/* for devhead register */
#define DEVHEAD_DEV 0x10
/* for devctrl (device control) register */
#define DEVCTRL_SRST 0x04
#define DEVCTRL_NIEN 0x02
#define DEVCTRL_SOFTRESET (DEVCTRL_SRST)
#define DEVCTRL_ENABLEINT (0)
#define DEVCTRL_DISABLEINT (DEVCTRL_NIEN)
#define DEVCTRL_NORMALOP (DEVCTRL_ENABLEINT)
/*
* these are not from ATA specification
*/
/* bus master registers */
#define REG_BMICX 0
#define REG_BMISX 2
#define REG_BMIDTPX 4
/* to use with VM_in/VM_out */
#define BM_REG_COMMAND (ide[ideif].io_bmdma+REG_BMICX)
#define BM_REG_STATUS (ide[ideif].io_bmdma+REG_BMISX)
#define BM_REG_PRDADDR (ide[ideif].io_bmdma+REG_BMIDTPX)
/* to register the i/o space */
#define BMDMAPORT_OFFSET 0
#define BMDMAPORT_LEN 8
/*
*
*
*
*/
static int decode_error_result(int ideif)
{
BYTE error;
error=inp(IDE_REG_ERROR);
/*
* obsoleted by ATA 4
*
if (error&ERR_BADBLOCK) return IDE_ERR_BADBLOCK;
else if (error&ERR_CRCECC) return IDE_ERR_CRCECC;
else if (error&ERR_IDNOTFOUND) return IDE_ERR_IDNOTFOUND;
else if (error&ERR_ABORTCMD) return IDE_ERR_ABORTCMD;
else if (error&ERR_TRACK00) return IDE_ERR_TRACK00;
else if (error&ERR_DAMNOTFOUND) return IDE_ERR_DAMNOTFOUND;
*/
if (error&ERROR_ABORTCMD) return IDE_ERR_ABORTCMD;
return IDE_ERR_UNKNOWN;
}
static void debug_status(char *s,BYTE status,BYTE mask)
{
printk(IDEDEBUGLOG "%s: %s\n",s,status&mask?"yes":"no");
}
void ide_dump_interface_status(int ideif)
{
BYTE status;
printk(IDEDEBUGLOG "ide%i status\n",ideif);
status=inp(IDE_REG_ALTSTATUS);
debug_status("busy ",status,STATUS_BUSY);
if (status&STATUS_BUSY) return;
debug_status("ready ",status,STATUS_DRDY);
//debug_status("write fault ",status,ST_WRFAULT);
//debug_status("seek complete ",status,ST_SEEKCOM);
debug_status("data request ",status,STATUS_DRQ);
//debug_status("data was corrected ",status,ST_DWC);
//debug_status("command in progress",status,ST_CMDPROG);
debug_status("error ",status,STATUS_ERR);
}
//#ifndef NDEBUG
/*+ for the IDE_ERR_??? constants +*/
char *ide_error_msg[]={
"no error",
"there are too much request pending",
"there was an interrupt but the interface is busy or can not do a command",
"IDE error: bad block",
"IDE error: crc/ecc error",
"IDE error: sector id not found",
"IDE error: abort command",
"IDE error: track00 seek error",
"IDE error: DAM not found",
"generic error",
"there was an interrupt, we aspect data but there is not data",
"there was an interrupt, we do not aspect data but there is data",
"interface not ready for a command",
"a timeout waiting for a reply",
"the interface must be busy but it is not",
"device fault"
};
//#endif
/*
*
*
*
*/
/* on i386 machine */
/* (todo better) */
/* done better! */
//#define ntohs(x) ((((x)&0xff00)>>8)|(((x)&0x00ff)<<8))
/* Well... the strings reported are swapped so if we have
* a "Maxtor" hard disk the following string is reported: "aMtxro"
*/
/* from Linux kernel (modified!) */
static void ide_fixstring (BYTE *s, const int bytecount, const int byteswap)
{
BYTE *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
if (byteswap) {
/* convert from big-endian to host byte order */
for (p = end ; p != s;) {
WORD *pp = (WORD *) (p -= 2);
*pp = ntohs(*pp);
}
}
/* strip leading blanks */
while (s != end && *s == ' ')
++s;
/* compress internal blanks and strip trailing blanks */
while (s != end && *s) {
if (*s++ != ' ' || (s != end && *s && *s != ' '))
*p++ = *(s-1);
}
/* wipe out trailing garbage */
while (p != end)
*p++ = '\0';
}
static void ide_fixdiskid(struct ata_diskid *ptr)
{
ide_fixstring(ptr->firmware,sizeof(ptr->firmware),1);
ptr->firmware[sizeof(ptr->firmware)-1]='\0';
ide_fixstring(ptr->model,sizeof(ptr->model),1);
ptr->model[sizeof(ptr->model)-1]='\0';
ide_fixstring(ptr->serial,sizeof(ptr->serial),1);
ptr->serial[sizeof(ptr->serial)-1]='\0';
}
static void ide_fixatapidiskid(struct atapi_diskid *ptr)
{
ide_fixstring(ptr->firmware,sizeof(ptr->firmware),1);
ptr->firmware[sizeof(ptr->firmware)-1]='\0';
ide_fixstring(ptr->model,sizeof(ptr->model),1);
ptr->model[sizeof(ptr->model)-1]='\0';
ide_fixstring(ptr->serial,sizeof(ptr->serial),1);
ptr->serial[sizeof(ptr->serial)-1]='\0';
}
static void lba2chs(int ideif, int id, DWORD lsector, int req)
{
if (ide[ideif].info[id].use_lba) {
idereq[req].secnum=lsector&0xff;
lsector>>=8;
idereq[req].cyllow=lsector&0xff;
lsector>>=8;
idereq[req].cylhig=lsector&0xff;
lsector>>=8;
idereq[req].devhead=0xe0|(lsector&0x0f)|((id&0x01)<<4);
} else {
/* for old hard-disk; is this correct? */
int sect,head,cyl,track;
struct dskgeometry *drive=&(ide[ideif].pdisk[id]->pd_phgeom);
track = lsector / drive->sectors;
sect = lsector % drive->sectors + 1;
idereq[req].secnum=sect;
head = track % drive->heads;
cyl = track / drive->heads;
idereq[req].cyllow=cyl&0xff;
idereq[req].cylhig=(cyl>>8)&0xff;
idereq[req].devhead=0xa0|(head&0x0f)|((id&0x01)<<4);
}
}
int ide_register(__uint16_t io_port, __uint16_t io_port2,
__uint8_t irq,
__uint8_t dma, __uint16_t io_bmdma)
{
//struct phdskinfo disk;
//MODEL m=BASE_MODEL;
//int ind;
int ideif;
int res;
//int req;
//int ret;
/* find an IDE interface sequential number */
ideif=nextideif();
if (ideif==MAXIDEINTERFACES) {
if (ide_showinfo_flag)
printk(IDELOG "ide: can't register (unavailable space)");
return -1;
}
/* request to use resources... */
if (!__request_irq(irq)) return -1;
if (!__request_io_space(io_port+IOPORT_OFFSET,IOPORT_LEN)) {
mark_ide_free(ideif);
__release_irq(irq);
return -1;
}
if (!__request_io_space(io_port2+IOPORT2_OFFSET,IOPORT2_LEN)) {
mark_ide_free(ideif);
__release_irq(irq);
__release_io_space(io_port+IOPORT_OFFSET,IOPORT_LEN);
return -1;
}
if (io_bmdma!=0)
if (!__request_io_space(io_bmdma+BMDMAPORT_OFFSET,BMDMAPORT_LEN)) {
mark_ide_free(ideif);
__release_irq(irq);
__release_io_space(io_port+IOPORT_OFFSET,IOPORT_LEN);
__release_io_space(io_port2+IOPORT2_OFFSET,IOPORT2_LEN);
return -1;
}
#ifdef _PARANOIA
ide[ideif].magic=IDE_MAGIC;
#endif
ide[ideif].io_port=io_port;
ide[ideif].io_port2=io_port2;
ide[ideif].irq=irq;
ide[ideif].dma=dma;
ide[ideif].io_bmdma=io_bmdma;
ide[ideif].pdisk[IDE_MASTER]=NULL;
ide[ideif].info[IDE_MASTER].use_lba=0;
ide[ideif].pdisk[IDE_SLAVE]=NULL;
ide[ideif].info[IDE_SLAVE].use_lba=0;
ide[ideif].actreq=-1;
ide[ideif].actdrv=-1;
bqueue_init(&ide[ideif].queue[0]);
bqueue_init(&ide[ideif].queue[1]);
/*
* glue initialization
*/
res=ide_glue_activate_interface(ideif);
if (res==-1) {
mark_ide_free(ideif);
__release_irq(irq);
__release_io_space(io_port+IOPORT_OFFSET,IOPORT_LEN);
__release_io_space(io_port2+IOPORT2_OFFSET,IOPORT2_LEN);
if (io_bmdma!=0)
__release_io_space(io_bmdma+BMDMAPORT_OFFSET,BMDMAPORT_LEN);
return -1;
}
return ideif;
}
void ide_unregister(int ideif)
{
ide_glue_unactivate_interface(ideif);
__release_irq(ide[ideif].irq);
__release_io_space(ide[ideif].io_port+IOPORT_OFFSET,IOPORT_LEN);
__release_io_space(ide[ideif].io_port2+IOPORT2_OFFSET,IOPORT2_LEN);
mark_ide_free(ideif);
//if (ideif!=ideiftail-1)
// memcpy(ide+ideif,ide+ideiftail-1,sizeof(ideinfo_t));
}
/*
*
*
*
*/
/* commands used */
#define ATA_READ 0x20
#define ATA_WRITE 0x30
#define ATA_SEEK 0x70
#define ATA_DMAREAD 0xc8
#define ATA_DMAWRITE 0xca
#define ATA_IDENTIFY 0xec
#define ATA_PIDENTIFY 0xa1
#define ATA_SETFEATURE 0xef
/* for 'set feature' command */
#define ATA_FEATURE_ENABLELOOKAHEAD 0xaa
#define ATA_FEATURE_DISABLELOOKAHEAD 0x55
#define ATA_FEATURE_SETTRANSFERTMODE 0x03
/* commands type */
#define ATA_TYPE_PIOIN 0
#define ATA_TYPE_PIOOUT 1
#define ATA_TYPE_DMA 2
#define ATA_TYPE_NODATA 3
static inline int cmd2type(int cmd)
{
switch(cmd) {
case ATA_READ:
case ATA_IDENTIFY:
case ATA_PIDENTIFY:
return ATA_TYPE_PIOIN;
case ATA_WRITE:
return ATA_TYPE_PIOOUT;
case ATA_DMAREAD:
case ATA_DMAWRITE:
return ATA_TYPE_DMA;
case ATA_SEEK:
case ATA_SETFEATURE:
return ATA_TYPE_NODATA;
}
return ATA_TYPE_NODATA;
}
#if defined(DEBUG_SERVER)||defined(DEBUG_SHOWREQUEST)
static char *cmd2str(int cmd)
{
switch(cmd) {
case ATA_READ: return "'read'";
case ATA_WRITE: return "'write'";
case ATA_SEEK: return "'seek'";
case ATA_DMAREAD: return "'dma read'";
case ATA_DMAWRITE: return "'dma write'";
case ATA_IDENTIFY: return "'identify'";
case ATA_PIDENTIFY: return "'packet identify'";
case ATA_SETFEATURE: return "'set feature'";
}
return "'unknown'";
}
#endif
/*
* from ATA/ATAPI 4 (T13/1153D revision 18)
* paragraph 9.7 (figure 12)
*/
#define RETRY 3
/*from Linux 2.2.13*/
/*
static int do_io_request(int ideif)
{
int req=ide[ideif].reqhead;
VM_out(IDE_REG_DEVCTRL,DEVCTRL_NORMALOP);
outp(IDE_REG_SECCOU,idereq[req].seccou);
outp(IDE_REG_SECNUM,idereq[req].secnum);
outp(IDE_REG_CYLLOW,idereq[req].cyllow);
outp(IDE_REG_CYLHIG,idereq[req].cylhig);
outp(IDE_REG_DEVHEAD,idereq[req].devhead);
outp(IDE_REG_COMMAND,idereq[req].cmd);
return IDE_OK;
}
*/
static int do_io_request(int ideif)
{
int req;
BYTE status;
int n;
WORD *ptr;
#ifndef IDE_SKIPALLTEST
int retries;
#endif
printk4(ideif,"A");
//outp(IDE_REG_DEVCTRL,DEVCTRL_NORMALOP);
//outp(IDE_REG_SECCOU,idereq[req].seccou);
req=first_idereq(ideif);
assertk(req!=NIL);
/* requirement for some commands */
switch(idereq[req].cmd) {
/* DRDY must be one */
case ATA_READ:
case ATA_DMAREAD:
case ATA_WRITE:
case ATA_DMAWRITE:
case ATA_SEEK:
case ATA_IDENTIFY:
case ATA_SETFEATURE:
#ifndef IDE_SKIPALLTEST
retries=RETRY;
while (--retries>=0) {
status=inp(IDE_REG_STATUS);
if ((status&STATUS_DRDY)!=0) break;
}
if (retries<0) return idereq[req].result=IDE_ERR_NOTDRDY;
break;
#endif
/* no requirement */
case ATA_PIDENTIFY:
break;
}
printk4(ideif,"B");
/* a command can be issued when BUSY==0 && DRQ==0 */
#ifndef IDE_SKIPALLTEST
retries=RETRY;
while (--retries>=0) {
status=inp(IDE_REG_STATUS);
if ((status&STATUS_BUSY)==0&&(status&STATUS_DRQ)==0) break;
}
if (retries<0) {
/* if the ide device is in stand-by the request fail! */
if (status&STATUS_BUSY) return idereq[req].result=IDE_ERR_BUSY;
else return idereq[req].result=IDE_ERR_NOTREADY;
}
#endif
printk4(ideif,"C");
/* write "change device" register */
outp(IDE_REG_DEVHEAD,idereq[req].devhead);
/* to wait for 400ns (I hope) */
#ifndef IDE_SKIPALLTEST
inp(IDE_REG_ALTSTATUS);
#endif
/* wait for "change device" to take effect */
#ifndef IDE_SKIPALLTEST
retries=RETRY;
while (--retries>=0) {
status=inp(IDE_REG_STATUS);
if ((status&STATUS_BUSY)==0&&(status&STATUS_DRQ)==0) break;
}
if (retries<0) {
/* Well, if I do a command to a disk that does not exist an
* interrupt is generated... so I MUST report no error
* (the error is manage into the ide server)
*/
return IDE_OK;
if (status&STATUS_BUSY) return idereq[req].result=IDE_ERR_BUSY;
else return idereq[req].result=IDE_ERR_NOTREADY;
}
#endif
printk4(ideif,"D");
/* write all the registers */
outp(IDE_REG_FEATURES,idereq[req].features);
outp(IDE_REG_SECCOU,idereq[req].seccou);
outp(IDE_REG_SECNUM,idereq[req].secnum);
outp(IDE_REG_CYLLOW,idereq[req].cyllow);
outp(IDE_REG_CYLHIG,idereq[req].cylhig);
//outp(IDE_REG_DEVHEAD,idereq[req].devhead);
if (cmd2type(idereq[req].cmd)==ATA_TYPE_DMA) {
/*
*
* Bus Master DMA commands
*
*/
/* these code come from Linux 2.2.12 (modified!) */
__uint32_t addr,*ptr;
unsigned int size;
int count;
/* make PRD table */
addr=__lin_to_phy(idereq[req].buffer);
if (addr&3) {
/* misaligned buffer */
printk(KERN_ERR "ide do_io_request: misaligned DMA buffer (0x%08lx)",
(long)addr);
return IDE_ERR_UNKNOWN;
}
size=IDE_SECTOR_SIZE; /* for now only 1 sector */
ptr=ide[ideif].prd;
count=0;
while (size) {
if (++count>=MAXPRDENTRIES) {
/* table to small for the request */
printk(KERN_ERR "ide do_io_request: PRD table too small");
return IDE_ERR_UNKNOWN;
} else {
unsigned int xcount, bcount = 0x10000 - (addr & 0xffff);
if (bcount > size)
bcount = size;
*ptr++ = addr;
xcount = bcount & 0xffff;
//if (is_trm290_chipset)
// xcount = ((xcount >> 2) - 1) << 16;
*ptr++ = xcount;
addr += bcount;
size -= bcount;
}
}
printk4(ideif,"E3");
/* PRD table */
outpd(BM_REG_PRDADDR,__lin_to_phy(ide[ideif].prd));
/* specify r/w */
if (idereq[req].cmd==ATA_DMAREAD) outp(BM_REG_COMMAND,1<<3);
else {
/* for default now... read */
/*outp(BM_REG_COMMAND,1<<3);*/
/* for write */
outp(BM_REG_COMMAND,0);
}
/* clear INTR & ERROR flags */
outp(BM_REG_STATUS,inp(BM_REG_STATUS)|6);
/* write command*/
outp(IDE_REG_COMMAND,idereq[req].cmd);
/* start DMA */
outp(BM_REG_COMMAND,inp(BM_REG_COMMAND)|1);
printk4(ideif,"F3");
return IDE_OK;
}
/*
*
* PIO IN/OUT and NO_DATA commands
*
*/
/* write command*/
outp(IDE_REG_COMMAND,idereq[req].cmd);
/* to wait for 400ns; I hope */
#ifndef IDE_SKIPSTATUSTEST
inp(IDE_REG_STATUS);
#endif
switch (cmd2type(idereq[req].cmd)) {
/* for PIO data in commands and NODATA commands */
case ATA_TYPE_PIOIN:
case ATA_TYPE_NODATA:
/* Well, the host should set the BUSY flag*/
#ifndef IDE_SKIPSTATUSTEST
retries=RETRY;
while (--retries>=0) {
status=inp(IDE_REG_ALTSTATUS);
if ((status&STATUS_BUSY)!=0) break;
}
if (retries<0) return IDE_ERR_NOTBUSY;
#endif
printk4(ideif,"E1");
return IDE_OK;
/* for PIO data out commands */
case ATA_TYPE_PIOOUT:
for (;;) {
/* Well, perhaps a timeout is necessary! */
status=inp(IDE_REG_ALTSTATUS);
#ifndef IDE_SKIPALLTEST
if ((status&STATUS_BUSY)!=0) break;
#else
break;
#endif
}
printk4(ideif,"E2");
if ((status&STATUS_ERR)||(status&STATUS_R_FAULT)) {
/* an error is detected! */
idereq[req].result=((status&STATUS_ERR)?
decode_error_result(ideif):IDE_ERR_FAULT
);
return idereq[req].result;
}
if (!(status&STATUS_DRQ)) {
/* error, */
return IDE_ERR_NODATAREQ;
}
/* Data I/O */
ptr=(WORD*)idereq[req].buffer;
for (n=0;n<IDE_SECTOR_SIZE>>1;n++)
*ptr++=inpw(IDE_REG_DATA);
/* to wait for 400ns; I hope ;-) */
#ifndef IDE_SKIPALLTEST
inp(IDE_REG_ALTSTATUS);
#endif
#ifndef IDE_SKIPALLTEST
status=inp(IDE_REG_ALTSTATUS);
if ((status&STATUS_BUSY)==0) return IDE_ERR_NOTBUSY;
#endif
printk4(ideif,"F2");
return IDE_OK;
}
return IDE_ERR_UNKNOWN;
}
/* (must be 6sec for ATA specs) */
#define WAITENDRESET_ELAPSE 200000l
int do_ide_softreset(int ideif)
{
unsigned long t;
int flag;
int status;
printk2(ideif,"START");
printk2(ideif,"waiting for not busy...");
/* 1 msec */
flag=1;
t=__gettimer()+1000;
while (__gettimer()<t) {
status=inp(IDE_REG_ALTSTATUS);
if (!(status&STATUS_BUSY)) { flag=0; break; }
}
if (flag) {
printk2(ideif,"device is busy!");
#ifndef IDE_FORCERESET
printk2(ideif,"END");
return IDE_ERR_TIMEOUT;
#endif
printk2(ideif,"with FORCERESET");
} else
printk2(ideif,"not busy");
printk2(ideif,"soft resetting");
outp(IDE_REG_DEVCTRL,DEVCTRL_SOFTRESET);
__delayk(5);
outp(IDE_REG_DEVCTRL,DEVCTRL_NORMALOP);
__delayk(2000);
printk2(ideif,"waiting for soft resetting end");
/* 6sec */
flag=1;
t=__gettimer()+WAITENDRESET_ELAPSE;
while (__gettimer()<t) {
status=inp(IDE_REG_ALTSTATUS);
if (!(status&STATUS_BUSY||!(status&STATUS_DRDY))) { flag=0; break; }
}
if (flag) {
printk2(ideif,"not ending!!");
printk2(ideif,"END");
return IDE_ERR_TIMEOUT;
}
printk2(ideif,"resetting end");
printk2(ideif,"END");
return IDE_OK;
}
/*
*
*
*
*/
/* abort per i packet */
static int REQ_PROLOG(void)
{
int req;
req=get_idereq();
if (req==NIL)
return IDE_ERR_TOOMUCHREQ;
/* ide[].resetonerror:
*
* used by calling thread
* 0 -> no soft reset on error
* 1 -> request a soft reset on error
* used by called thread (the server)
* 2 -> the calling thread MUST do a soft reset
*/
/* for safety */
idereq[req].resetonerror=1;
return req;
}
/* elapse for a timeouted request in usec (must be 2sec for ATA spec) */
#define TIMED_ELAPSE 80000l
static int __REQ_EPILOG(int ideif, int drv, int req, int timed)
{
int res,ret;
//long num;
printk3(ideif,"START");
ret=insert_idereq(ideif,drv,req);
if (ret) {
printk3(ideif,"activating server task");
ide_glue_send_request(ideif);
} else
printk3(ideif,"server task already running");
if (timed) {
unsigned long timer,timer2,timer3;
printk3(ideif,"waiting timed server reply");
res=1;
timer=__gettimer();
printk3(ideif,"AA");
assertk(timer!=0);
timer+=TIMED_ELAPSE;
printk3(ideif,"BB");
timer3=0;
/*
{
SYS_FLAGS f;
f=kern_fsave();
kern_frestore(f);
cprintf("[f=0x%x]",(int)f);
}
*/
while ((timer2=__gettimer())<timer) {
//cprintf("<%li>",timer2);
//if (timer2<timer3) break;
//timer3=timer2;
res=__b_sem_trywait(&idereq[req].wait);
if (!res) break;
}
printk3(ideif,"CC");
if (res) {
/* DANGER: if this is sent and an'interrupt occur
* the reqpending assertion of the server fail
*/
printk3(ideif,"timer expired.. try to remove request");
ide_glue_send_request(ideif);
__b_sem_wait(&idereq[req].wait);
res=IDE_ERR_TIMEOUT;
} else {
res=idereq[req].result;
printk3(ideif,"server reply ok");
}
} else {
printk3(ideif,"waiting server reply");
#ifdef TRACEWAIT
num=exec_shadow;
trc_logevent(TRC_USER1,&num);
#endif
__b_sem_wait(&idereq[req].wait);
#ifdef TRACEWAIT
trc_logevent(TRC_USER2,&num);
#endif
printk3(ideif,"server reply ok");
res=idereq[req].result;
}
free_idereq(req);
if (idereq[req].resetonerror==2) {
printk3(ideif,"SOFT RESET");
do_ide_softreset(ideif);
ret=releasequeue_idereq(ideif);
if (ret) {
/* there are request pending... */
ide_glue_send_request(ideif);
}
}
printk3(ideif,"END");
return res;
}
#define REQ_EPILOG(ideif,drv,req) __REQ_EPILOG(ideif,drv,req,0)
#define TIMED_REQ_EPILOG(ideif,drv,req) __REQ_EPILOG(ideif,drv,req,1)
/**/
static void fill_prologue(int req,
int cmd,
unsigned lsector,
struct phdskinfo *pdisk)
{
unsigned track;
if (cmd==REQ_DUMMY) {
idereq[req].info.sector=0;
idereq[req].info.head=0;
idereq[req].info.cylinder=0;
idereq[req].info.nsectors=0;
idereq[req].info.operation=cmd;
return;
}
track=lsector/pdisk->pd_phgeom.sectors;
idereq[req].info.sector=lsector%pdisk->pd_phgeom.sectors+1;
idereq[req].info.head=track%pdisk->pd_phgeom.heads;
idereq[req].info.cylinder=track/pdisk->pd_phgeom.heads;
idereq[req].info.nsectors=1;
idereq[req].info.operation=cmd;
}
int ide_identify(int ideif, int id, struct ata_diskid *buffer)
{
int req,ret;
printk1(ideif,"%s start",cmd2str(ATA_IDENTIFY));
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end=%i",cmd2str(ATA_IDENTIFY),req);
return req;
}
idereq[req].cmd=ATA_IDENTIFY;
idereq[req].features=0;
idereq[req].cyllow=0;
idereq[req].cylhig=0;
idereq[req].seccou=0;
idereq[req].secnum=0;
idereq[req].devhead=0xa0|((id&0x01)<<4);
idereq[req].buffer=(BYTE*)buffer;
idereq[req].resetonerror=0;
fill_prologue(req,REQ_DUMMY,0,ide[ideif].pdisk[id]);
ret=TIMED_REQ_EPILOG(ideif,id,req);
if (ret==IDE_OK) ide_fixdiskid(buffer);
printk1(ideif,"%s end=%i",cmd2str(ATA_IDENTIFY),ret);
return ret;
}
int ide_pidentify(int ideif, int id, struct atapi_diskid *buffer)
{
int req,ret;
printk1(ideif,"%s start",cmd2str(ATA_PIDENTIFY));
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end=%i",cmd2str(ATA_PIDENTIFY),req);
return req;
}
idereq[req].cmd=ATA_PIDENTIFY;
idereq[req].features=0;
idereq[req].cyllow=0;
idereq[req].cylhig=0;
idereq[req].seccou=0;
idereq[req].secnum=0;
idereq[req].devhead=0xa0|((id&0x01)<<4);
idereq[req].buffer=(BYTE*)buffer;
idereq[req].resetonerror=0;
fill_prologue(req,REQ_DUMMY,0,ide[ideif].pdisk[id]);
ret=TIMED_REQ_EPILOG(ideif,id,req);
if (ret==IDE_OK) ide_fixatapidiskid(buffer);
printk1(ideif,"%s end=%i",cmd2str(ATA_PIDENTIFY),ret);
return ret;
}
#define IDE_READ_RETRIES 1
#define IDE_SEEK_RETRIES 1
#define IDE_WRITE_RETRIES 1
int ide_read(int ideif, int id, __blkcnt_t lsector, BYTE *buffer)
{
int req,ret;
int i;
printk1(ideif,"%s start",cmd2str(ATA_READ));
for (i=0;i<IDE_READ_RETRIES;i++) {
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end(1)=%i",cmd2str(ATA_READ),req);
return req;
}
idereq[req].cmd=ATA_READ;
assertk(ide[ideif].info[id].use_dma==0);
if (ide[ideif].info[id].use_bm_dma) idereq[req].cmd=ATA_DMAREAD;
lba2chs(ideif,id,lsector,req);
idereq[req].features=0;
idereq[req].seccou=1;
idereq[req].buffer=buffer;
idereq[req].resetonerror=1;
fill_prologue(req,REQ_READ,lsector,ide[ideif].pdisk[id]);
ret=REQ_EPILOG(ideif,id,req);
if (ret==IDE_OK) break;
if (i!=0) printk1(ideif,"%s retry",cmd2str(ATA_READ));
}
printk1(ideif,"%s end(2)=%i",cmd2str(ATA_READ),ret);
return ret;
}
int ide_seek(int ideif, int id, __blkcnt_t lsector)
{
int req,ret;
int i;
printk1(ideif,"%s start",cmd2str(ATA_SEEK));
for (i=0;i<IDE_SEEK_RETRIES;i++) {
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end=%i",cmd2str(ATA_SEEK),req);
return req;
}
idereq[req].cmd=ATA_SEEK;
lba2chs(ideif,id,lsector,req);
idereq[req].features=0;
idereq[req].seccou=0;
idereq[req].buffer=NULL;
idereq[req].resetonerror=1;
fill_prologue(req,REQ_SEEK,lsector,ide[ideif].pdisk[id]);
ret=REQ_EPILOG(ideif,id,req);
if (ret==IDE_OK) break;
if (i!=0) printk1(ideif,"%s retry",cmd2str(ATA_SEEK));
}
printk1(ideif,"%s end=%i",cmd2str(ATA_SEEK),ret);
return ret;
}
int ide_enablelookahead(int ideif, int id)
{
int req,ret;
#ifndef IDE_ENABLESETFEATURES
printk(KERN_NOTICE "ide command 'enable look-ahead' not yet implementated");
return IDE_ERR_UNKNOWN;
#endif
printk1(ideif,"%s start (enable look a head)",cmd2str(ATA_SETFEATURE));
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end=%i",cmd2str(ATA_SETFEATURE),req);
return req;
}
idereq[req].cmd=ATA_SETFEATURE;
idereq[req].features=ATA_FEATURE_ENABLELOOKAHEAD;
idereq[req].cyllow=0;
idereq[req].cylhig=0;
idereq[req].seccou=0;
idereq[req].secnum=0;
idereq[req].devhead=((id&0x01)<<4);
idereq[req].buffer=NULL;
idereq[req].resetonerror=1;
fill_prologue(req,REQ_DUMMY,0,ide[ideif].pdisk[id]);
ret=REQ_EPILOG(ideif,id,req);
//ide_dump_interface_status(ideif);
__delayk(5);
outp(IDE_REG_DEVCTRL,DEVCTRL_NORMALOP);
printk1(ideif,"%s end=%i",cmd2str(ATA_SETFEATURE),ret);
return ret;
}
int ide_disablelookahead(int ideif, int id)
{
int req,ret;
#ifndef IDE_ENABLESETFEATURES
printk(KERN_NOTICE "ide command 'disable look-ahead' not yet implementated");
return IDE_ERR_UNKNOWN;
#endif
printk1(ideif,"%s start (enable look a head)",cmd2str(ATA_SETFEATURE));
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end=%i",cmd2str(ATA_SETFEATURE),req);
return req;
}
idereq[req].cmd=ATA_SETFEATURE;
idereq[req].features=ATA_FEATURE_DISABLELOOKAHEAD;
idereq[req].cyllow=0;
idereq[req].cylhig=0;
idereq[req].seccou=0;
idereq[req].secnum=0;
idereq[req].devhead=((id&0x01)<<4);
idereq[req].buffer=NULL;
idereq[req].resetonerror=1;
fill_prologue(req,REQ_DUMMY,0,ide[ideif].pdisk[id]);
ret=REQ_EPILOG(ideif,id,req);
printk1(ideif,"%s end=%i",cmd2str(ATA_SETFEATURE),ret);
return ret;
}
int ide_settransfertmode(int ideif, int id, int mode)
{
int req,ret;
#ifndef IDE_ENABLESETFEATURES
printk(KERN_NOTICE "ide command 'set transfert mode' not yet implementated");
return IDE_ERR_UNKNOWN;
#endif
printk1(ideif,"%s start (set transfert mode)",cmd2str(ATA_SETFEATURE));
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end=%i",cmd2str(ATA_SETFEATURE),req);
return req;
}
idereq[req].cmd=ATA_SETFEATURE;
idereq[req].features=ATA_FEATURE_SETTRANSFERTMODE;
idereq[req].cyllow=0;
idereq[req].cylhig=0;
idereq[req].seccou=mode;
idereq[req].secnum=0;
idereq[req].devhead=((id&0x01)<<4);
idereq[req].buffer=NULL;
idereq[req].resetonerror=1;
fill_prologue(req,REQ_DUMMY,0,ide[ideif].pdisk[id]);
ret=REQ_EPILOG(ideif,id,req);
ide_dump_interface_status(ideif);
printk1(ideif,"%s end=%i",cmd2str(ATA_SETFEATURE),ret);
return ret;
}
int ide_write(int ideif, int id, __blkcnt_t lsector, BYTE *buffer)
{
int req,ret;
int i;
printk1(ideif,"%s start",cmd2str(ATA_WRITE));
for (i=0;i<IDE_WRITE_RETRIES;i++) {
req=REQ_PROLOG();
if (req<0) {
printk1(ideif,"%s end=%i",cmd2str(ATA_WRITE),req);
return req;
}
idereq[req].cmd=ATA_WRITE;
assertk(ide[ideif].info[id].use_dma==0);
if (ide[ideif].info[id].use_bm_dma) idereq[req].cmd=ATA_DMAWRITE;
else {
panic("there is no support for polled write (only DMA allowed)!");
}
lba2chs(ideif,id,lsector,req);
idereq[req].features=0;
idereq[req].seccou=1;
idereq[req].buffer=buffer;
idereq[req].resetonerror=1;
fill_prologue(req,REQ_WRITE,lsector,ide[ideif].pdisk[id]);
ret=REQ_EPILOG(ideif,id,req);
if (ret==IDE_OK) break;
if (i!=0) printk1(ideif,"%s retry",cmd2str(ATA_WRITE));
}
printk1(ideif,"%s end=%i",cmd2str(ATA_WRITE),ret);
return ret;
}
/**/
void ide_service_request(int ideif)
{
static int reqpending=0;
BYTE status,dma_status;
int res=0,req=0;
int n;
printk0(ideif,"ACTIVATED");
//assertk(ide[ideif].reqhead!=NIL);
if (!reqpending) {
printk0(ideif,"doing a new request");
reqpending=1;
goto DO_REQUEST;
}
printk0(ideif,"start to serve %s request",cmd2str(idereq[req].cmd));
status=inp(IDE_REG_STATUS);
//req=ide[ideif].reqhead;
req=actual_idereq(ideif);
/*
if (req==NIL) {
printk(KERN_INFO "unaspceted INTR catch");
continue;
}
*/
if (status&STATUS_BUSY||status&STATUS_ERR) {
idereq[req].cmd=0;
/* to FIX! */
}
switch (idereq[req].cmd) {
/*
* DMA COMMANDS
*
* DMAREAD
*/
case ATA_DMAREAD:
case ATA_DMAWRITE:
/* from Linux 2.2.12 */
/* stop DMA */
outp(BM_REG_COMMAND,inp(BM_REG_COMMAND)&~1);
/* get DMA status */
dma_status = inp(BM_REG_STATUS);
/* clear the INTR & ERROR bits */
outp(BM_REG_STATUS,dma_status|6);
/* verify good DMA status (0 -> OK)*/
dma_status=(dma_status&7)!=4;
if (dma_status||status&STATUS_DRQ)
idereq[req].result=((status&STATUS_ERR)?
decode_error_result(ideif):IDE_ERR_DMA
);
else
idereq[req].result=IDE_OK;
break;
/*
* NO DATA COMMANDS
*
* SEEK and SET FEATURE
*/
case ATA_SEEK:
case ATA_SETFEATURE:
if (status&STATUS_DRQ) {
idereq[req].result=((status&STATUS_ERR)?
decode_error_result(ideif):IDE_ERR_DATA
);
} else
idereq[req].result=IDE_OK;
break;
case ATA_PIDENTIFY:
case ATA_IDENTIFY:
case ATA_READ:
/*
* PIO IN COMMANDS
*
* PIDENTIFY, IDENTIFY and READ commands
*/
if (status&STATUS_DRQ) {
WORD *ptr=(WORD*)idereq[req].buffer;
for (n=0;n<IDE_SECTOR_SIZE>>1;n++)
*ptr++=inpw(IDE_REG_DATA);
status=inp(IDE_REG_ALTSTATUS);
} else {
idereq[req].result=((status&STATUS_ERR)?
decode_error_result(ideif):IDE_ERR_NODATA
);
}
status=inp(IDE_REG_STATUS);
if (status&STATUS_DRQ) idereq[req].result=IDE_ERR_NODATA; /*fix*/
else idereq[req].result=IDE_OK;
break;
/*
* PIO OUT COMMANDS
*
* WRITE command
*/
case ATA_WRITE:
/* all work is done into do_io_request() */
idereq[req].result=IDE_OK;
break;
/*
* BOH ?!?
*/
default:
/* an abort is more appropiate! */
inp(IDE_REG_ALTSTATUS);
idereq[req].result=IDE_ERR_UNKNOWN;
break;
}
#ifdef IDE_OLDATAPIDELAY
if (idereq[req].cmd==ATA_PIDENTIFY) {
/* delay for old ATAPI device */
/* on my old cdrom a n>700 is ok */
for (n=0;n<750;n++) {
status=inp(IDE_REG_ALTSTATUS);
if (status&0x100) break;
}
}
#endif
reqpending=remove_idereq(ideif);
printk0(ideif,"end to serve request (result=%i)",idereq[req].result);
__b_sem_signal(&idereq[req].wait);
if (reqpending) printk0(ideif,"another request into the queue");
DO_REQUEST:
/* if there are requests pending... */
if (reqpending)
/* loop until no request error! */
for (;;) {
/* made request! */
printk0(ideif,"made new request");
res=do_io_request(ideif);
if (res!=IDE_OK) {
/* if request fail... */
/* update result */
printk0(ideif,"new request fail (code: %i)",res);
//req=ide[ideif].reqhead;
req=actual_idereq(ideif);
idereq[req].result=res;
/* if "request on error" ... */
if (idereq[req].resetonerror==1) {
/* request a soft error */
printk0(ideif,"ERROR: soft reset in progress..");
idereq[req].resetonerror=2;
ide[ideif].errors++;
/* remove request (blocking new ones) and wake up blocked thread */
reqpending=remove_idereq_blocking(ideif);
__b_sem_signal(&idereq[req].wait);
break;
}
/* remove request and wake up waiting thread */
reqpending=remove_idereq(ideif);
__b_sem_signal(&idereq[req].wait);
if (reqpending) printk0(ideif,"redoing a new request");
/* if no more request... go out of the loop! */
if (!reqpending) break;
} else
/* if request does not fail... */
/* go out of the loop */
printk0(ideif,"new request in progress");
break;
}
}