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

📄 sdata.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 4 页
字号:
	}	return dma;}static intataidentify(int cmdport, int ctlport, int dev, int pkt, void* info){	int as, command, drdy;	if(pkt){		command = Cidpkt;		drdy = 0;	}	else{		command = Cid;		drdy = Drdy;	}	as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);	if(as < 0)		return as;	outb(cmdport+Command, command);	microdelay(1);	as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);	if(as < 0)		return -1;	if(as & Err)		return as;	memset(info, 0, 512);	inss(cmdport+Data, info, 256);	inb(cmdport+Status);	if(DEBUG & DbgIDENTIFY){		int i;		ushort *sp;		sp = (ushort*)info;		for(i = 0; i < 256; i++){			if(i && (i%16) == 0)				print("\n");			print(" %4.4uX", *sp);			sp++;		}		print("\n");	}	return 0;}static Drive*atadrive(int cmdport, int ctlport, int dev){	Drive *drive;	int as, i, pkt;	uchar buf[512], *p;	ushort iconfig, *sp;	atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);	pkt = 1;retry:	as = ataidentify(cmdport, ctlport, dev, pkt, buf);	if(as < 0)		return nil;	if(as & Err){		if(pkt == 0)			return nil;		pkt = 0;		goto retry;	}	if((drive = malloc(sizeof(Drive))) == nil)		return nil;	drive->dev = dev;	memmove(drive->info, buf, sizeof(drive->info));	drive->sense[0] = 0x70;	drive->sense[7] = sizeof(drive->sense)-7;	drive->inquiry[2] = 2;	drive->inquiry[3] = 2;	drive->inquiry[4] = sizeof(drive->inquiry)-4;	p = &drive->inquiry[8];	sp = &drive->info[Imodel];	for(i = 0; i < 20; i++){		*p++ = *sp>>8;		*p++ = *sp++;	}	drive->secsize = 512;	/*	 * Beware the CompactFlash Association feature set.	 * Now, why this value in Iconfig just walks all over the bit	 * definitions used in the other parts of the ATA/ATAPI standards	 * is a mystery and a sign of true stupidity on someone's part.	 * Anyway, the standard says if this value is 0x848A then it's	 * CompactFlash and it's NOT a packet device.	 */	iconfig = drive->info[Iconfig];	if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){		if(iconfig & 0x01)			drive->pkt = 16;		else			drive->pkt = 12;	}	else{		if(drive->info[Ivalid] & 0x0001){			drive->c = drive->info[Iccyl];			drive->h = drive->info[Ichead];			drive->s = drive->info[Icsec];		}		else{			drive->c = drive->info[Ilcyl];			drive->h = drive->info[Ilhead];			drive->s = drive->info[Ilsec];		}		if(drive->info[Icapabilities] & Mlba){			if(drive->info[Icsfs+1] & Maddr48){				drive->sectors = drive->info[Ilba48]					| (drive->info[Ilba48+1]<<16)					| ((vlong)drive->info[Ilba48+2]<<32);				drive->flags |= Lba48;			}			else{				drive->sectors = (drive->info[Ilba+1]<<16)					 |drive->info[Ilba];			}			drive->dev |= Lba;		}		else			drive->sectors = drive->c*drive->h*drive->s;		atarwmmode(drive, cmdport, ctlport, dev);	}	atadmamode(drive);		if(DEBUG & DbgCONFIG){		print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",			dev, cmdport, iconfig, drive->info[Icapabilities]);		print(" mwdma %4.4uX", drive->info[Imwdma]);		if(drive->info[Ivalid] & 0x04)			print(" udma %4.4uX", drive->info[Iudma]);		print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm);		if(drive->flags&Lba48)			print("\tLLBA sectors %lld", drive->sectors);		print("\n");	}	return drive;}static voidatasrst(int ctlport){	/*	 * Srst is a big stick and may cause problems if further	 * commands are tried before the drives become ready again.	 * Also, there will be problems here if overlapped commands	 * are ever supported.	 */	microdelay(5);	outb(ctlport+Dc, Srst);	microdelay(5);	outb(ctlport+Dc, 0);	microdelay(2*1000);}static SDev*ataprobe(int cmdport, int ctlport, int irq){	Ctlr* ctlr;	SDev *sdev;	Drive *drive;	int dev, error, rhi, rlo;	static int nonlegacy = 'C';		if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {		print("ataprobe: Cannot allocate %X\n", cmdport);		return nil;	}	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){		print("ataprobe: Cannot allocate %X\n", ctlport + As);		iofree(cmdport);		return nil;	}	/*	 * Try to detect a floating bus.	 * Bsy should be cleared. If not, see if the cylinder registers	 * are read/write capable.	 * If the master fails, try the slave to catch slave-only	 * configurations.	 * There's no need to restore the tested registers as they will	 * be reset on any detected drives by the Cedd command.	 * All this indicates is that there is at least one drive on the	 * controller; when the non-existent drive is selected in a	 * single-drive configuration the registers of the existing drive	 * are often seen, only command execution fails.	 */	dev = Dev0;	if(inb(ctlport+As) & Bsy){		outb(cmdport+Dh, dev);		microdelay(1);trydev1:		atadebug(cmdport, ctlport, "ataprobe bsy");		outb(cmdport+Cyllo, 0xAA);		outb(cmdport+Cylhi, 0x55);		outb(cmdport+Sector, 0xFF);		rlo = inb(cmdport+Cyllo);		rhi = inb(cmdport+Cylhi);		if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){			if(dev == Dev1){release:				iofree(cmdport);				iofree(ctlport+As);				return nil;			}			dev = Dev1;			if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)				goto trydev1;		}	}	/*	 * Disable interrupts on any detected controllers.	 */	outb(ctlport+Dc, Nien);tryedd1:	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){		/*		 * There's something there, but it didn't come up clean,		 * so try hitting it with a big stick. The timing here is		 * wrong but this is a last-ditch effort and it sometimes		 * gets some marginal hardware back online.		 */		atasrst(ctlport);		if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0)			goto release;	}	/*	 * Can only get here if controller is not busy.	 * If there are drives Bsy will be set within 400nS,	 * must wait 2mS before testing Status.	 * Wait for the command to complete (6 seconds max).	 */	outb(cmdport+Command, Cedd);	delay(2);	if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)		goto release;	/*	 * If bit 0 of the error register is set then the selected drive	 * exists. This is enough to detect single-drive configurations.	 * However, if the master exists there is no way short of executing	 * a command to determine if a slave is present.	 * It appears possible to get here testing Dev0 although it doesn't	 * exist and the EDD won't take, so try again with Dev1.	 */	error = inb(cmdport+Error);	atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);	if((error & ~0x80) != 0x01){		if(dev == Dev1)			goto release;		dev = Dev1;		goto tryedd1;	}	/*	 * At least one drive is known to exist, try to	 * identify it. If that fails, don't bother checking	 * any further.	 * If the one drive found is Dev0 and the EDD command	 * didn't indicate Dev1 doesn't exist, check for it.	 */	if((drive = atadrive(cmdport, ctlport, dev)) == nil)		goto release;	if((ctlr = malloc(sizeof(Ctlr))) == nil){		free(drive);		goto release;	}	memset(ctlr, 0, sizeof(Ctlr));	if((sdev = malloc(sizeof(SDev))) == nil){		free(ctlr);		free(drive);		goto release;	}	memset(sdev, 0, sizeof(SDev));	drive->ctlr = ctlr;	if(dev == Dev0){		ctlr->drive[0] = drive;		if(!(error & 0x80)){			/*			 * Always leave Dh pointing to a valid drive,			 * otherwise a subsequent call to ataready on			 * this controller may try to test a bogus Status.			 * Ataprobe is the only place possibly invalid			 * drives should be selected.			 */			drive = atadrive(cmdport, ctlport, Dev1);			if(drive != nil){				drive->ctlr = ctlr;				ctlr->drive[1] = drive;			}			else{				outb(cmdport+Dh, Dev0);				microdelay(1);			}		}	}	else		ctlr->drive[1] = drive;	ctlr->cmdport = cmdport;	ctlr->ctlport = ctlport;	ctlr->irq = irq;	ctlr->tbdf = BUSUNKNOWN;	ctlr->command = Cedd;		/* debugging */		switch(cmdport){	default:		sdev->idno = nonlegacy;		break;	case 0x1F0:		sdev->idno = 'C';		nonlegacy = 'E';		break;	case 0x170:		sdev->idno = 'D';		nonlegacy = 'E';		break;	}	sdev->ifc = &sdataifc;	sdev->ctlr = ctlr;	sdev->nunit = 2;	ctlr->sdev = sdev;	return sdev;}static voidataclear(SDev *sdev){	Ctlr* ctlr;	ctlr = sdev->ctlr;	iofree(ctlr->cmdport);	iofree(ctlr->ctlport + As);	if (ctlr->drive[0])		free(ctlr->drive[0]);	if (ctlr->drive[1])		free(ctlr->drive[1]);	if (sdev->name)		free(sdev->name);	if (sdev->unitflg)		free(sdev->unitflg);	if (sdev->unit)		free(sdev->unit);	free(ctlr);	free(sdev);}static char *atastat(SDev *sdev, char *p, char *e){	Ctlr *ctlr = sdev->ctlr;	return seprint(p, e, "%s ata port %X ctl %X irq %d\n", 		    	       sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);}static SDev*ataprobew(DevConf *cf){	char *p;	ISAConf isa;		if (cf->nports != 2)		error(Ebadarg);	memset(&isa, 0, sizeof isa);	isa.port = cf->ports[0].port;	isa.irq = cf->intnum;	if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0)		error("cannot find controller");	return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);}/* * These are duplicated with sdsetsense, etc., in devsd.c, but * those assume that the disk is not SCSI while in fact here * ata drives are not SCSI but ATAPI ones kind of are. */static intatasetsense(Drive* drive, int status, int key, int asc, int ascq){	drive->sense[2] = key;	drive->sense[12] = asc;	drive->sense[13] = ascq;	return status;}static intatamodesense(Drive* drive, uchar* cmd){	int len;	/*	 * Fake a vendor-specific request with page code 0,	 * return the drive info.	 */	if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)		return atasetsense(drive, SDcheck, 0x05, 0x24, 0);	len = (cmd[7]<<8)|cmd[8];	if(len == 0)		return SDok;	if(len < 8+sizeof(drive->info))		return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);	if(drive->data == nil || drive->dlen < len)		return atasetsense(drive, SDcheck, 0x05, 0x20, 1);	memset(drive->data, 0, 8);	drive->data[0] = sizeof(drive->info)>>8;	drive->data[1] = sizeof(drive->info);	memmove(drive->data+8, drive->info, sizeof(drive->info));	drive->data += 8+sizeof(drive->info);	return SDok;}static intatastandby(Drive* drive, int period){	Ctlr* ctlr;	int cmdport, done;	ctlr = drive->ctlr;	drive->command = Cstandby;	qlock(ctlr);	cmdport = ctlr->cmdport;	ilock(ctlr);	outb(cmdport+Count, period);	outb(cmdport+Dh, drive->dev);	ctlr->done = 0;	ctlr->curdrive = drive;	ctlr->command = Cstandby;	/* debugging */	outb(cmdport+Command, Cstandby);	iunlock(ctlr);	while(waserror())		;	tsleep(ctlr, atadone, ctlr, 60*1000);	poperror();	done = ctlr->done;	qunlock(ctlr);	if(!done || (drive->status & Err))		return atasetsense(drive, SDcheck, 4, 8, drive->error);	return SDok;}static voidatanop(Drive* drive, int subcommand){	Ctlr* ctlr;	int as, cmdport, ctlport, timeo;	/*	 * Attempt to abort a command by using NOP.	 * In response, the drive is supposed to set Abrt	 * in the Error register, set (Drdy|Err) in Status	 * and clear Bsy when done. However, some drives	 * (e.g. ATAPI Zip) just go Bsy then clear Status	 * when done, hence the timeout loop only on Bsy	 * and the forced setting of drive->error.	 */	ctlr = drive->ctlr;	cmdport = ctlr->cmdport;	outb(cmdport+Features, subcommand);	outb(cmdport+Dh, drive->dev);	ctlr->command = Cnop;		/* debugging */	outb(cmdport+Command, Cnop);	microdelay(1);	ctlport = ctlr->ctlport;	for(timeo = 0; timeo < 1000; timeo++){		as = inb(ctlport+As);		if(!(as & Bsy))			break;		microdelay(1);	}	drive->error |= Abrt;}static voidataabort(Drive* drive, int dolock){	/*	 * If NOP is available (packet commands) use it otherwise	 * must try a software reset.	 */	if(dolock)		ilock(drive->ctlr);	if(drive->info[Icsfs] & Mnop)		atanop(drive, 0);	else{		atasrst(drive->ctlr->ctlport);		drive->error |= Abrt;	}	if(dolock)		iunlock(drive->ctlr);}static intatadmasetup(Drive* drive, int len){	Prd *prd;	ulong pa;	Ctlr *ctlr;	int bmiba, bmisx, count, i, span;	ctlr = drive->ctlr;	pa = PCIWADDR(drive->data);	if(pa & 0x03)		return -1;	/*	 * Sometimes drives identify themselves as being DMA capable	 * although they are not on a busmastering controller.	 */	prd = ctlr->prdt;	if(prd == nil){		drive->dmactl = 0;		print("disabling dma: not on a busmastering controller\n");		return -1;	}	for(i = 0; len && i < Nprd; i++){		prd->pa = pa;		span = ROUNDUP(pa, ctlr->span);		if(span == pa)			span += ctlr->span;		count = span - pa;		if(count >= len){			prd->count = PrdEOT|len;			break;		}		prd->count = count;		len -= count;		pa += count;		prd++;	}	if(i == Nprd)		(prd-1)->count |= PrdEOT;	bmiba = ctlr->bmiba;	outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt));	if(drive->write)		outb(ctlr->bmiba+Bmicx, 0);	else		outb(ctlr->bmiba+Bmicx, Rwcon);	bmisx = inb(bmiba+Bmisx);	outb(bmiba+Bmisx, bmisx|Ideints|Idedmae);

⌨️ 快捷键说明

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