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

📄 sdata.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 5 页
字号:
		}		else if(dev){			outb(cmdport+Dh, dev);			dev = 0;		}		else if(ready == 0 || (as & ready)){			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);			return as;		}		if(micro-- <= 0){			atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);			break;		}		microdelay(1);	}	atadebug(cmdport, ctlport, "ataready: timeout");	return -1;}/*static intatacsf(Drive* drive, vlong csf, int supported){	ushort *info;	int cmdset, i, x;	if(supported)		info = &drive->info[Icsfs];	else		info = &drive->info[Icsfe];	for(i = 0; i < 3; i++){		x = (csf>>(16*i)) & 0xFFFF;		if(x == 0)			continue;		cmdset = info[i];		if(cmdset == 0 || cmdset == 0xFFFF)			return 0;		return cmdset & x;	}	return 0;}*/static intatadone(void* arg){	return ((Ctlr*)arg)->done;}static intatarwmmode(Drive* drive, int cmdport, int ctlport, int dev){	int as, maxrwm, rwm;	maxrwm = (drive->info[Imaxrwm] & 0xFF);	if(maxrwm == 0)		return 0;	/*	 * Sometimes drives come up with the current count set	 * to 0; if so, set a suitable value, otherwise believe	 * the value in Irwm if the 0x100 bit is set.	 */	if(drive->info[Irwm] & 0x100)		rwm = (drive->info[Irwm] & 0xFF);	else		rwm = 0;	if(rwm == 0)		rwm = maxrwm;	if(rwm > 16)		rwm = 16;	if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0)		return 0;	outb(cmdport+Count, rwm);	outb(cmdport+Cmd, Csm);	microdelay(1);	as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000);	inb(cmdport+Status);	if(as < 0 || (as & (Df|Err)))		return 0;	drive->rwm = rwm;	if (conf.idedma)		drive->rwmctl = drive->rwm;	/* FS special */	else		drive->rwm = 0;	return rwm;}static intatadmamode(Drive* drive){	int dma;	/*	 * Check if any DMA mode enabled.	 * Assumes the BIOS has picked and enabled the best.	 * This is completely passive at the moment, no attempt is	 * made to ensure the hardware is correctly set up.	 */	dma = drive->info[Imwdma] & 0x0707;	drive->dma = (dma>>8) & dma;	if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){		dma = drive->info[Iudma] & 0x7F7F;		drive->dma = (dma>>8) & dma;		if(drive->dma)			drive->dma |= 'U'<<16;	}	if (conf.idedma)		drive->dmactl = drive->dma;	/* FS special */	else		drive->dma = 0;	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+Cmd, 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;}/* * DEBUGGING only. * write, read and verify block 1 (never used in an fs otherwise) * to see if dma and rwm actually work. * if not, turn them off, though the kernel could be corrupt by then. */static voidataverify(Drive *dp){	int n, nb, dev = dp->driveno;	uchar *buf = dp->buf;	if (dp->ctlr == nil)		panic("ataverify: nil ctlr for drive");	atadriveprobe(dev);	print("ataverify h%d...", dev);	for (n = 0; n < RBUFSIZE; n++)		buf[n] = n;	if (ataseek(dev, RBUFSIZE) < 0)		panic("ataverify: seek 1");	nb = atawrite(dev, buf, RBUFSIZE);	if (nb != RBUFSIZE)		print("short write of %d bytes to block 1\n", nb);	else {		for (n = 0; n < RBUFSIZE; n++)			buf[n] = ~n;		if (ataseek(dev, RBUFSIZE) < 0)			panic("ataverify: seek 1");		nb = ataread(dev, buf, RBUFSIZE);		if (nb != RBUFSIZE)			print("short read of %d bytes to block 1\n", nb);		else {			for (n = 0; n < RBUFSIZE; n++)				if ((uchar)buf[n] != (uchar)n)					break;			if (n == RBUFSIZE) {				print("OK\n");				return;			/* verified OK */			}			print("byte comparison failed\n");		}	}	print("ataverify: disabling dma and rwm\n");	dp->dmactl = dp->rwmctl = 0;}static Drive*atagetdrive(int cmdport, int ctlport, int dev){	Drive *drive;	int as, i, pkt, driveno;	uchar buf[512], *p;	ushort iconfig, *sp;	driveno = (cmdport == Ctlr0cmd? 0:		cmdport == Ctlr1cmd? NCtlrdrv: 2*NCtlrdrv);	if (dev == Dev1)		driveno++;	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;	drive->driveno = -1;				/* unset */	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){		print("atagetdrive: port 0x%uX dev 0x%2.2uX: packet device\n",			cmdport, dev);		if(iconfig & 0x01)			drive->pkt = 16;		else			drive->pkt = 12;	}	else{		if (iconfig == 0x848A)			print("atagetdrive: port 0x%uX dev 0x%2.2uX: non-packet CF device\n",				cmdport, dev);		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)					| ((Devsize)drive->info[Ilba48+2]<<32);				drive->flags |= Lba48;			}else				drive->sectors = (drive->info[Ilba+1]<<16)					 |drive->info[Ilba];			drive->dev |= Lba;			drive->lba = 1;		}else			drive->sectors = drive->c * drive->h * drive->s;		atarwmmode(drive, cmdport, ctlport, dev);	}	atadmamode(drive);	if(DEBUG & DbgCONFIG){		print("ata h%d: dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",			driveno, 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\n", drive->dma, drive->rwm);		if(drive->flags&Lba48)			print("\tLLBA sectors %lld\n", (Wideoff)drive->sectors);	}	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 int drivenum = 0;	/* hope that we probe in order */static voidupdprobe(int cmdport){	if(cmdport == Ctlr0cmd)		drivenum = NCtlrdrv;	else if (cmdport == Ctlr1cmd)		drivenum = 2*NCtlrdrv;}static SDev*ataprobe(int cmdport, int ctlport, int irq){	Ctlr* ctlr;	SDev *sdev;	Drive *drive;	int i, dev, error, rhi, rlo;	if(cmdport == Ctlr0cmd)		drivenum = 0;	else if (cmdport == Ctlr1cmd)		drivenum = NCtlrdrv;	if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {		print("ataprobe: Cannot allocate %X\n", cmdport);		updprobe(cmdport);		return nil;	}	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){		print("ataprobe: Cannot allocate %X\n", ctlport + As);		iofree(cmdport);		updprobe(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);				updprobe(cmdport);				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+Cmd, 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 = atagetdrive(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;	atactlr[drivenum/NCtlrdrv] = ctlr;	atadrive[drivenum++] = drive;	sdevs[drivenum/NCtlrdrv] = sdev;	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 = atagetdrive(cmdport, ctlport, Dev1);			if(drive != nil){				drive->ctlr = ctlr;				ctlr->drive[1] = drive;			}			else{				outb(cmdport+Dh, Dev0);				microdelay(1);			}			atadrive[drivenum] = drive;		}	}	else		ctlr->drive[1] = drive;	drivenum++;	print("ata%d: cmd 0x%ux ctl 0x%ux irq %d\n",		(drivenum-1)/NCtlrdrv, cmdport, ctlport, irq);	ctlr->cmdport = cmdport;	ctlr->ctlport = ctlport;	ctlr->irq = irq;	ctlr->tbdf = BUSUNKNOWN;	ctlr->command = Cedd;		/* debugging */	sdev->ifc = &sdataifc;	sdev->ctlr = ctlr;	sdev->nunit = NCtlrdrv;	ctlr->sdev = sdev;	if (0)		for (i = drivenum - 2; i < drivenum; i++)			if (atadrive[i])				ataverify(atadrive[i]);	updprobe(cmdport);	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]);	/* TODO: clear entries in atadrive[] too */	if (sdev->name)		free(sdev->name);	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);}#ifndef FSstatic SDev*ataprobew(DevConf *cf){	if (cf->nports != 2)		error(Ebadarg);	return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum);}#endifstatic intatasetsense(Drive* drive, int status, int key, int asc, int ascq)

⌨️ 快捷键说明

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