⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sdata.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 4 页
字号:
	return 0;}static voidatadmastart(Ctlr* ctlr, int write){	if(write)		outb(ctlr->bmiba+Bmicx, Ssbm);	else		outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm);}static intatadmastop(Ctlr* ctlr){	int bmiba;	bmiba = ctlr->bmiba;	outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm);	return inb(bmiba+Bmisx);}static voidatadmainterrupt(Drive* drive, int count){	Ctlr* ctlr;	int bmiba, bmisx;	ctlr = drive->ctlr;	bmiba = ctlr->bmiba;	bmisx = inb(bmiba+Bmisx);	switch(bmisx & (Ideints|Idedmae|Bmidea)){	case Bmidea:		/*		 * Data transfer still in progress, nothing to do		 * (this should never happen).		 */		return;	case Ideints:	case Ideints|Bmidea:		/*		 * Normal termination, tidy up.		 */		drive->data += count;		break;	default:		/*		 * What's left are error conditions (memory transfer		 * problem) and the device is not done but the PRD is		 * exhausted. For both cases must somehow tell the		 * drive to abort.		 */		ataabort(drive, 0);		break;	}	atadmastop(ctlr);	ctlr->done = 1;}static voidatapktinterrupt(Drive* drive){	Ctlr* ctlr;	int cmdport, len;	ctlr = drive->ctlr;	cmdport = ctlr->cmdport;	switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){	case Cd:		outss(cmdport+Data, drive->pktcmd, drive->pkt/2);		break;	case 0:		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);		if(drive->data+len > drive->limit){			atanop(drive, 0);			break;		}		outss(cmdport+Data, drive->data, len/2);		drive->data += len;		break;	case Io:		len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);		if(drive->data+len > drive->limit){			atanop(drive, 0);			break;		}		inss(cmdport+Data, drive->data, len/2);		drive->data += len;		break;	case Io|Cd:		if(drive->pktdma)			atadmainterrupt(drive, drive->dlen);		else			ctlr->done = 1;		break;	}}static intatapktio(Drive* drive, uchar* cmd, int clen){	Ctlr *ctlr;	int as, cmdport, ctlport, len, r, timeo;	if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)		return atamodesense(drive, cmd);	r = SDok;	drive->command = Cpkt;	memmove(drive->pktcmd, cmd, clen);	memset(drive->pktcmd+clen, 0, drive->pkt-clen);	drive->limit = drive->data+drive->dlen;	ctlr = drive->ctlr;	cmdport = ctlr->cmdport;	ctlport = ctlr->ctlport;	qlock(ctlr);	as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000);	/* used to test as&Chk as failure too, but some CD readers use that for media change */	if(as < 0){		qunlock(ctlr);		return -1;	}	ilock(ctlr);	if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))		drive->pktdma = Dma;	else		drive->pktdma = 0;	outb(cmdport+Features, drive->pktdma);	outb(cmdport+Count, 0);	outb(cmdport+Sector, 0);	len = 16*drive->secsize;	outb(cmdport+Bytelo, len);	outb(cmdport+Bytehi, len>>8);	outb(cmdport+Dh, drive->dev);	ctlr->done = 0;	ctlr->curdrive = drive;	ctlr->command = Cpkt;		/* debugging */	if(drive->pktdma)		atadmastart(ctlr, drive->write);	outb(cmdport+Command, Cpkt);	if((drive->info[Iconfig] & Mdrq) != 0x0020){		microdelay(1);		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);		if(as < 0 || (as & (Bsy|Chk))){			drive->status = as<0 ? 0 : as;			ctlr->curdrive = nil;			ctlr->done = 1;			r = SDtimeout;		}else			atapktinterrupt(drive);	}	iunlock(ctlr);	while(waserror())		;	if(!drive->pktdma)		sleep(ctlr, atadone, ctlr);	else for(timeo = 0; !ctlr->done; timeo++){		tsleep(ctlr, atadone, ctlr, 1000);		if(ctlr->done)			break;		ilock(ctlr);		atadmainterrupt(drive, 0);		if(!drive->error && timeo > 20){			ataabort(drive, 0);			atadmastop(ctlr);			drive->dmactl = 0;			drive->error |= Abrt;		}		if(drive->error){			drive->status |= Chk;			ctlr->curdrive = nil;		}		iunlock(ctlr);	}	poperror();	qunlock(ctlr);	if(drive->status & Chk)		r = SDcheck;	return r;}static uchar cmd48[256] = {	[Crs]	Crs48,	[Crd]	Crd48,	[Crdq]	Crdq48,	[Crsm]	Crsm48,	[Cws]	Cws48,	[Cwd]	Cwd48,	[Cwdq]	Cwdq48,	[Cwsm]	Cwsm48,};static intatageniostart(Drive* drive, vlong lba){	Ctlr *ctlr;	uchar cmd;	int as, c, cmdport, ctlport, h, len, s, use48;	use48 = 0;	if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){		if(!(drive->flags & Lba48))			return -1;		use48 = 1;		c = h = s = 0;	}	else if(drive->dev & Lba){		c = (lba>>8) & 0xFFFF;		h = (lba>>24) & 0x0F;		s = lba & 0xFF;	}	else{		c = lba/(drive->s*drive->h);		h = ((lba/drive->s) % drive->h);		s = (lba % drive->s) + 1;	}	ctlr = drive->ctlr;	cmdport = ctlr->cmdport;	ctlport = ctlr->ctlport;	if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0)		return -1;	ilock(ctlr);	if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){		if(drive->write)			drive->command = Cwd;		else			drive->command = Crd;	}	else if(drive->rwmctl){		drive->block = drive->rwm*drive->secsize;		if(drive->write)			drive->command = Cwsm;		else			drive->command = Crsm;	}	else{		drive->block = drive->secsize;		if(drive->write)			drive->command = Cws;		else			drive->command = Crs;	}	drive->limit = drive->data + drive->count*drive->secsize;	cmd = drive->command;	if(use48){		outb(cmdport+Count, (drive->count>>8) & 0xFF);		outb(cmdport+Count, drive->count & 0XFF);		outb(cmdport+Lbalo, (lba>>24) & 0xFF);		outb(cmdport+Lbalo, lba & 0xFF);		outb(cmdport+Lbamid, (lba>>32) & 0xFF);		outb(cmdport+Lbamid, (lba>>8) & 0xFF);		outb(cmdport+Lbahi, (lba>>40) & 0xFF);		outb(cmdport+Lbahi, (lba>>16) & 0xFF);		outb(cmdport+Dh, drive->dev|Lba);		cmd = cmd48[cmd];		if(DEBUG & Dbg48BIT)			print("using 48-bit commands\n");	}	else{		outb(cmdport+Count, drive->count);		outb(cmdport+Sector, s);		outb(cmdport+Cyllo, c);		outb(cmdport+Cylhi, c>>8);		outb(cmdport+Dh, drive->dev|h);	}	ctlr->done = 0;	ctlr->curdrive = drive;	ctlr->command = drive->command;	/* debugging */	outb(cmdport+Command, cmd);	switch(drive->command){	case Cws:	case Cwsm:		microdelay(1);		/* 10*1000 for flash ide drives - maybe detect them? */		as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*1000);		if(as < 0 || (as & Err)){			iunlock(ctlr);			return -1;		}		len = drive->block;		if(drive->data+len > drive->limit)			len = drive->limit-drive->data;		outss(cmdport+Data, drive->data, len/2);		break;	case Crd:	case Cwd:		atadmastart(ctlr, drive->write);		break;	}	iunlock(ctlr);	return 0;}static intatagenioretry(Drive* drive){	if(drive->dmactl){		drive->dmactl = 0;		print("atagenioretry: disabling dma\n");	}	else if(drive->rwmctl)		drive->rwmctl = 0;	else		return atasetsense(drive, SDcheck, 4, 8, drive->error);	return SDretry;}static intatagenio(Drive* drive, uchar* cmd, int){	uchar *p;	Ctlr *ctlr;	vlong lba, len;	int count, maxio;	/*	 * Map SCSI commands into ATA commands for discs.	 * Fail any command with a LUN except INQUIRY which	 * will return 'logical unit not supported'.	 */	if((cmd[1]>>5) && cmd[0] != 0x12)		return atasetsense(drive, SDcheck, 0x05, 0x25, 0);	switch(cmd[0]){	default:		return atasetsense(drive, SDcheck, 0x05, 0x20, 0);	case 0x00:			/* test unit ready */		return SDok;	case 0x03:			/* request sense */		if(cmd[4] < sizeof(drive->sense))			len = cmd[4];		else			len = sizeof(drive->sense);		if(drive->data && drive->dlen >= len){			memmove(drive->data, drive->sense, len);			drive->data += len;		}		return SDok;	case 0x12:			/* inquiry */		if(cmd[4] < sizeof(drive->inquiry))			len = cmd[4];		else			len = sizeof(drive->inquiry);		if(drive->data && drive->dlen >= len){			memmove(drive->data, drive->inquiry, len);			drive->data += len;		}		return SDok;	case 0x1B:			/* start/stop unit */		/*		 * NOP for now, can use the power management feature		 * set later.		 */		return SDok;	case 0x25:			/* read capacity */		if((cmd[1] & 0x01) || cmd[2] || cmd[3])			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);		if(drive->data == nil || drive->dlen < 8)			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);		/*		 * Read capacity returns the LBA of the last sector.		 */		len = drive->sectors-1;		p = drive->data;		*p++ = len>>24;		*p++ = len>>16;		*p++ = len>>8;		*p++ = len;		len = drive->secsize;		*p++ = len>>24;		*p++ = len>>16;		*p++ = len>>8;		*p = len;		drive->data += 8;		return SDok;	case 0x9E:			/* long read capacity */		if((cmd[1] & 0x01) || cmd[2] || cmd[3])			return atasetsense(drive, SDcheck, 0x05, 0x24, 0);		if(drive->data == nil || drive->dlen < 8)			return atasetsense(drive, SDcheck, 0x05, 0x20, 1);		/*		 * Read capacity returns the LBA of the last sector.		 */		len = drive->sectors-1;		p = drive->data;		*p++ = len>>56;		*p++ = len>>48;		*p++ = len>>40;		*p++ = len>>32;		*p++ = len>>24;		*p++ = len>>16;		*p++ = len>>8;		*p++ = len;		len = drive->secsize;		*p++ = len>>24;		*p++ = len>>16;		*p++ = len>>8;		*p = len;		drive->data += 12;		return SDok;	case 0x28:			/* read */	case 0x2A:			/* write */		break;	case 0x5A:		return atamodesense(drive, cmd);	}	ctlr = drive->ctlr;	lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];	count = (cmd[7]<<8)|cmd[8];	if(drive->data == nil)		return SDok;	if(drive->dlen < count*drive->secsize)		count = drive->dlen/drive->secsize;	qlock(ctlr);	if(ctlr->maxio)		maxio = ctlr->maxio;	else if(drive->flags & Lba48)		maxio = 65536;	else		maxio = 256;	while(count){		if(count > maxio)			drive->count = maxio;		else			drive->count = count;		if(atageniostart(drive, lba)){			ilock(ctlr);			atanop(drive, 0);			iunlock(ctlr);			qunlock(ctlr);			return atagenioretry(drive);		}		while(waserror())			;		tsleep(ctlr, atadone, ctlr, 60*1000);		poperror();		if(!ctlr->done){			/*			 * What should the above timeout be? In			 * standby and sleep modes it could take as			 * long as 30 seconds for a drive to respond.			 * Very hard to get out of this cleanly.			 */			atadumpstate(drive, cmd, lba, count);			ataabort(drive, 1);			qunlock(ctlr);			return atagenioretry(drive);		}		if(drive->status & Err){			qunlock(ctlr);			return atasetsense(drive, SDcheck, 4, 8, drive->error);		}		count -= drive->count;		lba += drive->count;	}	qunlock(ctlr);	return SDok;}static intatario(SDreq* r){	Ctlr *ctlr;	Drive *drive;	SDunit *unit;	uchar cmd10[10], *cmdp, *p;	int clen, reqstatus, status;	unit = r->unit;	if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){		r->status = SDtimeout;		return SDtimeout;	}	drive = ctlr->drive[unit->subno];	/*	 * Most SCSI commands can be passed unchanged except for	 * the padding on the end. The few which require munging	 * are not used internally. Mode select/sense(6) could be	 * converted to the 10-byte form but it's not worth the	 * effort. Read/write(6) are easy.	 */	switch(r->cmd[0]){	case 0x08:			/* read */	case 0x0A:			/* write */		cmdp = cmd10;		memset(cmdp, 0, sizeof(cmd10));		cmdp[0] = r->cmd[0]|0x20;		cmdp[1] = r->cmd[1] & 0xE0;		cmdp[5] = r->cmd[3];		cmdp[4] = r->cmd[2];		cmdp[3] = r->cmd[1] & 0x0F;		cmdp[8] = r->cmd[4];		clen = sizeof(cmd10);		break;	default:		cmdp = r->cmd;		clen = r->clen;		break;	}	qlock(drive);retry:	drive->write = r->write;	drive->data = r->data;	drive->dlen = r->dlen;	drive->status = 0;	drive->error = 0;	if(drive->pkt)		status = atapktio(drive, cmdp, clen);	else		status = atagenio(drive, cmdp, clen);	if(status == SDretry){		if(DbgDEBUG)			print("%s: retry: dma %8.8uX rwm %4.4uX\n",				unit->name, drive->dmactl, drive->rwmctl);		goto retry;	}	if(status == SDok){		atasetsense(drive, SDok, 0, 0, 0);		if(drive->data){			p = r->data;			r->rlen = drive->data - p;		}		else			r->rlen = 0;	}	else if(status == SDcheck && !(r->flags & SDnosense)){		drive->write = 0;		memset(cmd10, 0, sizeof(cmd10));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -