Skip to content
Snippets Groups Projects
cmd_ide.c 39.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    			c = ide_wait (device, IDE_TIME_OUT);	/* can't take over 500 ms */
    		}
    
    		if ((c&(ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR)) != ATA_STAT_DRQ) {
    			printf ("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
    				device, blknr, c);
    			break;
    		}
    
    		input_data (device, buffer, ATA_SECTORWORDS);
    
    		(void) ide_inb (device, ATA_STATUS);	/* clear IRQ */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		++n;
    		++blknr;
    		buffer += ATA_SECTORWORDS;
    	}
    IDE_READ_E:
    	ide_led (DEVICE_LED(device), 0);	/* LED off	*/
    	return (n);
    }
    
    /* ------------------------------------------------------------------------- */
    
    
    ulong ide_write (int device, ulong blknr, ulong blkcnt, ulong *buffer)
    {
    	ulong n = 0;
    	unsigned char c;
    
    	ide_led (DEVICE_LED(device), 1);	/* LED on	*/
    
    	/* Select device
    	 */
    
    	ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	while (blkcnt-- > 0) {
    
    		c = ide_wait (device, IDE_TIME_OUT);
    
    		if (c & ATA_STAT_BUSY) {
    			printf ("IDE read: device %d not ready\n", device);
    			goto WR_OUT;
    		}
    
    
    		ide_outb (device, ATA_SECT_CNT, 1);
    		ide_outb (device, ATA_LBA_LOW,  (blknr >>  0) & 0xFF);
    		ide_outb (device, ATA_LBA_MID,  (blknr >>  8) & 0xFF);
    		ide_outb (device, ATA_LBA_HIGH, (blknr >> 16) & 0xFF);
    		ide_outb (device, ATA_DEV_HD,   ATA_LBA		|
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					    ATA_DEVICE(device)	|
    					    ((blknr >> 24) & 0xF) );
    
    		ide_outb (device, ATA_COMMAND,  ATA_CMD_WRITE);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		udelay (50);
    
    		c = ide_wait (device, IDE_TIME_OUT);	/* can't take over 500 ms */
    
    		if ((c&(ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR)) != ATA_STAT_DRQ) {
    			printf ("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
    				device, blknr, c);
    			goto WR_OUT;
    		}
    
    		output_data (device, buffer, ATA_SECTORWORDS);
    
    		c = ide_inb (device, ATA_STATUS);	/* clear IRQ */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		++n;
    		++blknr;
    		buffer += ATA_SECTORWORDS;
    	}
    WR_OUT:
    	ide_led (DEVICE_LED(device), 0);	/* LED off	*/
    	return (n);
    }
    
    /* ------------------------------------------------------------------------- */
    
    /*
     * copy src to dest, skipping leading and trailing blanks and null
     * terminate the string
     */
    static void ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len)
    {
    	int start,end;
    
    	start=0;
    	while (start<len) {
    		if (src[start]!=' ')
    			break;
    		start++;
    	}
    	end=len-1;
    	while (end>start) {
    		if (src[end]!=' ')
    			break;
    		end--;
    	}
    	for ( ; start<=end; start++) {
    		*dest++=src[start];
    	}
    	*dest='\0';
    }
    
    /* ------------------------------------------------------------------------- */
    
    /*
     * Wait until Busy bit is off, or timeout (in ms)
     * Return last status
     */
    static uchar ide_wait (int dev, ulong t)
    {
    	ulong delay = 10 * t;		/* poll every 100 us */
    	uchar c;
    
    
    	while ((c = ide_inb(dev, ATA_STATUS)) & ATA_STAT_BUSY) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		udelay (100);
    		if (delay-- == 0) {
    			break;
    		}
    	}
    	return (c);
    }
    
    /* ------------------------------------------------------------------------- */
    
    #ifdef CONFIG_IDE_RESET
    extern void ide_set_reset(int idereset);
    
    static void ide_reset (void)
    {
    #if defined(CFG_PB_12V_ENABLE) || defined(CFG_PB_IDE_MOTOR)
    	volatile immap_t *immr = (immap_t *)CFG_IMMR;
    #endif
    	int i;
    
    	curr_device = -1;
    	for (i=0; i<CFG_IDE_MAXBUS; ++i)
    		ide_bus_ok[i] = 0;
    	for (i=0; i<CFG_IDE_MAXDEVICE; ++i)
    		ide_dev_desc[i].type = DEV_TYPE_UNKNOWN;
    
    	ide_set_reset (1); /* assert reset */
    
    	WATCHDOG_RESET();
    
    #ifdef CFG_PB_12V_ENABLE
    	immr->im_cpm.cp_pbdat &= ~(CFG_PB_12V_ENABLE);	/* 12V Enable output OFF */
    	immr->im_cpm.cp_pbpar &= ~(CFG_PB_12V_ENABLE);
    	immr->im_cpm.cp_pbodr &= ~(CFG_PB_12V_ENABLE);
    	immr->im_cpm.cp_pbdir |=   CFG_PB_12V_ENABLE;
    
    	/* wait 500 ms for the voltage to stabilize
    	 */
    	for (i=0; i<500; ++i) {
    		udelay (1000);
    	}
    
    	immr->im_cpm.cp_pbdat |=   CFG_PB_12V_ENABLE;	/* 12V Enable output ON */
    #endif	/* CFG_PB_12V_ENABLE */
    
    #ifdef CFG_PB_IDE_MOTOR
    	/* configure IDE Motor voltage monitor pin as input */
    	immr->im_cpm.cp_pbpar &= ~(CFG_PB_IDE_MOTOR);
    	immr->im_cpm.cp_pbodr &= ~(CFG_PB_IDE_MOTOR);
    	immr->im_cpm.cp_pbdir &= ~(CFG_PB_IDE_MOTOR);
    
    	/* wait up to 1 s for the motor voltage to stabilize
    	 */
    	for (i=0; i<1000; ++i) {
    		if ((immr->im_cpm.cp_pbdat & CFG_PB_IDE_MOTOR) != 0) {
    			break;
    		}
    		udelay (1000);
    	}
    
    	if (i == 1000) {	/* Timeout */
    		printf ("\nWarning: 5V for IDE Motor missing\n");
    # ifdef CONFIG_STATUS_LED
    #  ifdef STATUS_LED_YELLOW
    		status_led_set  (STATUS_LED_YELLOW, STATUS_LED_ON );
    #  endif
    #  ifdef STATUS_LED_GREEN
    		status_led_set  (STATUS_LED_GREEN,  STATUS_LED_OFF);
    #  endif
    # endif	/* CONFIG_STATUS_LED */
    	}
    #endif	/* CFG_PB_IDE_MOTOR */
    
    	WATCHDOG_RESET();
    
    	/* de-assert RESET signal */
    	ide_set_reset(0);
    
    	/* wait 250 ms */
    	for (i=0; i<250; ++i) {
    		udelay (1000);
    	}
    }
    
    #endif	/* CONFIG_IDE_RESET */
    
    /* ------------------------------------------------------------------------- */
    
    #ifdef CONFIG_IDE_LED
    
    static	uchar	led_buffer = 0;		/* Buffer for current LED status	*/
    
    static void ide_led (uchar led, uchar status)
    {
    	uchar *led_port = LED_PORT;
    
    	if (status)	{		/* switch LED on	*/
    		led_buffer |=  led;
    	} else {			/* switch LED off	*/
    		led_buffer &= ~led;
    	}
    
    	*led_port = led_buffer;
    }
    
    #endif	/* CONFIG_IDE_LED */
    
    /* ------------------------------------------------------------------------- */
    
    #ifdef CONFIG_ATAPI
    /****************************************************************************
     * ATAPI Support
     */
    
    
    
    #undef	ATAPI_DEBUG
    
    #ifdef	ATAPI_DEBUG
    #define	AT_PRINTF(fmt,args...)	printf (fmt ,##args)
    #else
    #define AT_PRINTF(fmt,args...)
    #endif
    
    
    #ifdef __PPC__
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /* since ATAPI may use commands with not 4 bytes alligned length
     * we have our own transfer functions, 2 bytes alligned */
    static void
    output_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    	ushort	*dbuf;
    	volatile ushort	*pbuf;
    
    	pbuf = (ushort *)(ATA_CURR_BASE(dev)+ATA_DATA_REG);
    	dbuf = (ushort *)sect_buf;
    	while (shorts--) {
    		__asm__ volatile ("eieio");
    		*pbuf = *dbuf++;
    	}
    }
    
    static void
    input_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    	ushort	*dbuf;
    	volatile ushort	*pbuf;
    
    	pbuf = (ushort *)(ATA_CURR_BASE(dev)+ATA_DATA_REG);
    	dbuf = (ushort *)sect_buf;
    	while (shorts--) {
    		__asm__ volatile ("eieio");
    		*dbuf++ = *pbuf;
    	}
    }
    
    
    #else	/* ! __PPC__ */
    static void
    output_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    	outsw(ATA_DATA_REG, sect_buf, shorts);
    }
    
    
    static void
    input_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    	insw(ATA_DATA_REG, sect_buf, shorts);
    }
    
    #endif	/* __PPC__ */
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*
     * Wait until (Status & mask) == res, or timeout (in ms)
     * Return last status
     * This is used since some ATAPI CD ROMs clears their Busy Bit first
     * and then they set their DRQ Bit
     */
    static uchar atapi_wait_mask (int dev, ulong t,uchar mask, uchar res)
    {
    	ulong delay = 10 * t;		/* poll every 100 us */
    	uchar c;
    
    
    	c = ide_inb(dev,ATA_DEV_CTL); /* prevents to read the status before valid */
    	while (((c = ide_inb(dev, ATA_STATUS)) & mask) != res) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* break if error occurs (doesn't make sense to wait more) */
    		if((c & ATA_STAT_ERR)==ATA_STAT_ERR)
    			break;
    		udelay (100);
    		if (delay-- == 0) {
    			break;
    		}
    	}
    	return (c);
    }
    
    /*
     * issue an atapi command
     */
    unsigned char atapi_issue(int device,unsigned char* ccb,int ccblen, unsigned char * buffer,int buflen)
    {
    	unsigned char c,err,mask,res;
    	int n;
    	ide_led (DEVICE_LED(device), 1);	/* LED on	*/
    
    	/* Select device
    	 */
    	mask = ATA_STAT_BUSY|ATA_STAT_DRQ;
    	res = 0;
    
    	ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    	if ((c & mask) != res) {
    		printf ("ATAPI_ISSUE: device %d not ready status %X\n", device,c);
    		err=0xFF;
    		goto AI_OUT;
    	}
    	/* write taskfile */
    
    	ide_outb (device, ATA_ERROR_REG, 0); /* no DMA, no overlaped */
    	ide_outb (device, ATA_CYL_LOW,  (unsigned char)(buflen & 0xFF));
    	ide_outb (device, ATA_CYL_HIGH, (unsigned char)((buflen<<8) & 0xFF));
    	ide_outb (device, ATA_DEV_HD,   ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	ide_outb (device, ATA_COMMAND,  ATAPI_CMD_PACKET);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	udelay (50);
    
    	mask = ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR;
    	res = ATA_STAT_DRQ;
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    
    	if ((c & mask) != res) { /* DRQ must be 1, BSY 0 */
    		printf ("ATTAPI_ISSUE: Error (no IRQ) before sending ccb dev %d status 0x%02x\n",device,c);
    		err=0xFF;
    		goto AI_OUT;
    	}
    
    	output_data_shorts (device, (unsigned short *)ccb,ccblen/2); /* write command block */
     	/* ATAPI Command written wait for completition */
    	udelay (5000); /* device must set bsy */
    
    	mask = ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR;
    	/* if no data wait for DRQ = 0 BSY = 0
    	 * if data wait for DRQ = 1 BSY = 0 */
    	res=0;
    	if(buflen)
    		res = ATA_STAT_DRQ;
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    	if ((c & mask) != res ) {
    		if (c & ATA_STAT_ERR) {
    
    			err=(ide_inb(device,ATA_ERROR_REG))>>4;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			AT_PRINTF("atapi_issue 1 returned sense key %X status %02X\n",err,c);
    		} else {
    			printf ("ATTAPI_ISSUE: (no DRQ) after sending ccb (%x)  status 0x%02x\n", ccb[0],c);
    			err=0xFF;
    		}
    		goto AI_OUT;
    	}
    
    	n=ide_inb(device, ATA_CYL_HIGH);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	n<<=8;
    
    	n+=ide_inb(device, ATA_CYL_LOW);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if(n>buflen) {
    		printf("ERROR, transfer bytes %d requested only %d\n",n,buflen);
    		err=0xff;
    		goto AI_OUT;
    	}
    	if((n==0)&&(buflen<0)) {
    		printf("ERROR, transfer bytes %d requested %d\n",n,buflen);
    		err=0xff;
    		goto AI_OUT;
    	}
    	if(n!=buflen) {
    		AT_PRINTF("WARNING, transfer bytes %d not equal with requested %d\n",n,buflen);
    	}
    	if(n!=0) { /* data transfer */
    		AT_PRINTF("ATAPI_ISSUE: %d Bytes to transfer\n",n);
    		 /* we transfer shorts */
    		n>>=1;
    		/* ok now decide if it is an in or output */
    
    		if ((ide_inb(device, ATA_SECT_CNT)&0x02)==0) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			AT_PRINTF("Write to device\n");
    			output_data_shorts(device,(unsigned short *)buffer,n);
    		} else {
    			AT_PRINTF("Read from device @ %p shorts %d\n",buffer,n);
    			input_data_shorts(device,(unsigned short *)buffer,n);
    		}
    	}
    	udelay(5000); /* seems that some CD ROMs need this... */
    	mask = ATA_STAT_BUSY|ATA_STAT_ERR;
    	res=0;
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    	if ((c & ATA_STAT_ERR) == ATA_STAT_ERR) {
    
    		err=(ide_inb(device,ATA_ERROR_REG) >> 4);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		AT_PRINTF("atapi_issue 2 returned sense key %X status %X\n",err,c);
    	} else {
    		err = 0;
    	}
    AI_OUT:
    	ide_led (DEVICE_LED(device), 0);	/* LED off	*/
    	return (err);
    }
    
    /*
     * sending the command to atapi_issue. If an status other than good
     * returns, an request_sense will be issued
     */
    
    #define ATAPI_DRIVE_NOT_READY 	100
    #define ATAPI_UNIT_ATTN		10
    
    unsigned char atapi_issue_autoreq (int device,
    				   unsigned char* ccb,
    				   int ccblen,
    				   unsigned char *buffer,
    				   int buflen)
    {
    	unsigned char sense_data[18],sense_ccb[12];
    	unsigned char res,key,asc,ascq;
    	int notready,unitattn;
    
    	unitattn=ATAPI_UNIT_ATTN;
    	notready=ATAPI_DRIVE_NOT_READY;
    
    retry:
    	res= atapi_issue(device,ccb,ccblen,buffer,buflen);
    	if (res==0)
    		return (0); /* Ok */
    
    	if (res==0xFF)
    		return (0xFF); /* error */
    
    	AT_PRINTF("(auto_req)atapi_issue returned sense key %X\n",res);
    
    	memset(sense_ccb,0,sizeof(sense_ccb));
    	memset(sense_data,0,sizeof(sense_data));
    	sense_ccb[0]=ATAPI_CMD_REQ_SENSE;
    	sense_ccb[4]=18; /* allocation Legnth */
    
    	res=atapi_issue(device,sense_ccb,12,sense_data,18);
    	key=(sense_data[2]&0xF);
    	asc=(sense_data[12]);
    	ascq=(sense_data[13]);
    
    	AT_PRINTF("ATAPI_CMD_REQ_SENSE returned %x\n",res);
    	AT_PRINTF(" Sense page: %02X key %02X ASC %02X ASCQ %02X\n",
    		sense_data[0],
    		key,
    		asc,
    		ascq);
    
    	if((key==0))
    		return 0; /* ok device ready */
    
    	if((key==6)|| (asc==0x29) || (asc==0x28)) { /* Unit Attention */
    		if(unitattn-->0) {
    			udelay(200*1000);
    			goto retry;
    		}
    		printf("Unit Attention, tried %d\n",ATAPI_UNIT_ATTN);
    		goto error;
    	}
    	if((asc==0x4) && (ascq==0x1)) { /* not ready, but will be ready soon */
    		if (notready-->0) {
    			udelay(200*1000);
    			goto retry;
    		}
    		printf("Drive not ready, tried %d times\n",ATAPI_DRIVE_NOT_READY);
    		goto error;
    	}
    	if(asc==0x3a) {
    		AT_PRINTF("Media not present\n");
    		goto error;
    	}
    	printf ("ERROR: Unknown Sense key %02X ASC %02X ASCQ %02X\n",key,asc,ascq);
    error:
    	AT_PRINTF ("ERROR Sense key %02X ASC %02X ASCQ %02X\n",key,asc,ascq);
    	return (0xFF);
    }
    
    
    
    static void	atapi_inquiry(block_dev_desc_t * dev_desc)
    {
    	unsigned char ccb[12]; /* Command descriptor block */
    	unsigned char iobuf[64]; /* temp buf */
    	unsigned char c;
    	int device;
    
    	device=dev_desc->dev;
    	dev_desc->type=DEV_TYPE_UNKNOWN; /* not yet valid */
    	dev_desc->block_read=atapi_read;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    
    	ccb[0]=ATAPI_CMD_INQUIRY;
    	ccb[4]=40; /* allocation Legnth */
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,40);
    
    	AT_PRINTF("ATAPI_CMD_INQUIRY returned %x\n",c);
    	if (c!=0)
    		return;
    
    	/* copy device ident strings */
    	ident_cpy(dev_desc->vendor,&iobuf[8],8);
    	ident_cpy(dev_desc->product,&iobuf[16],16);
    	ident_cpy(dev_desc->revision,&iobuf[32],5);
    
    	dev_desc->lun=0;
    	dev_desc->lba=0;
    	dev_desc->blksz=0;
    	dev_desc->type=iobuf[0] & 0x1f;
    
    	if ((iobuf[1]&0x80)==0x80)
    		dev_desc->removable = 1;
    	else
    		dev_desc->removable = 0;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    	ccb[0]=ATAPI_CMD_START_STOP;
    	ccb[4]=0x03; /* start */
    
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,0);
    
    	AT_PRINTF("ATAPI_CMD_START_STOP returned %x\n",c);
    	if (c!=0)
    		return;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,0);
    
    	AT_PRINTF("ATAPI_CMD_UNIT_TEST_READY returned %x\n",c);
    	if (c!=0)
    		return;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    	ccb[0]=ATAPI_CMD_READ_CAP;
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,8);
    	AT_PRINTF("ATAPI_CMD_READ_CAP returned %x\n",c);
    	if (c!=0)
    		return;
    
    	AT_PRINTF("Read Cap: LBA %02X%02X%02X%02X blksize %02X%02X%02X%02X\n",
    		iobuf[0],iobuf[1],iobuf[2],iobuf[3],
    		iobuf[4],iobuf[5],iobuf[6],iobuf[7]);
    
    	dev_desc->lba  =((unsigned long)iobuf[0]<<24) +
    			((unsigned long)iobuf[1]<<16) +
    			((unsigned long)iobuf[2]<< 8) +
    			((unsigned long)iobuf[3]);
    	dev_desc->blksz=((unsigned long)iobuf[4]<<24) +
    			((unsigned long)iobuf[5]<<16) +
    			((unsigned long)iobuf[6]<< 8) +
    			((unsigned long)iobuf[7]);
    	return;
    }
    
    
    /*
     * atapi_read:
     * we transfer only one block per command, since the multiple DRQ per
     * command is not yet implemented
     */
    #define ATAPI_READ_MAX_BYTES	2048	/* we read max 2kbytes */
    #define ATAPI_READ_BLOCK_SIZE	2048	/* assuming CD part */
    #define ATAPI_READ_MAX_BLOCK ATAPI_READ_MAX_BYTES/ATAPI_READ_BLOCK_SIZE	/* max blocks */
    
    ulong atapi_read (int device, ulong blknr, ulong blkcnt, ulong *buffer)
    {
    	ulong n = 0;
    	unsigned char ccb[12]; /* Command descriptor block */
    	ulong cnt;
    
    	AT_PRINTF("atapi_read dev %d start %lX, blocks %lX buffer at %lX\n",
    		device, blknr, blkcnt, (ulong)buffer);
    
    	do {
    		if (blkcnt>ATAPI_READ_MAX_BLOCK) {
    			cnt=ATAPI_READ_MAX_BLOCK;
    		} else {
    			cnt=blkcnt;
    		}
    		ccb[0]=ATAPI_CMD_READ_12;
    		ccb[1]=0; /* reserved */
    		ccb[2]=(unsigned char) (blknr>>24) & 0xFF; /* MSB Block */
    		ccb[3]=(unsigned char) (blknr>>16) & 0xFF; /*  */
    		ccb[4]=(unsigned char) (blknr>> 8) & 0xFF;
    		ccb[5]=(unsigned char)  blknr      & 0xFF; /* LSB Block */
    		ccb[6]=(unsigned char) (cnt  >>24) & 0xFF; /* MSB Block count */
    		ccb[7]=(unsigned char) (cnt  >>16) & 0xFF;
    		ccb[8]=(unsigned char) (cnt  >> 8) & 0xFF;
    		ccb[9]=(unsigned char)  cnt	   & 0xFF; /* LSB Block */
    		ccb[10]=0; /* reserved */
    		ccb[11]=0; /* reserved */
    
    		if (atapi_issue_autoreq(device,ccb,12,
    					(unsigned char *)buffer,
    					cnt*ATAPI_READ_BLOCK_SIZE) == 0xFF) {
    			return (n);
    		}
    		n+=cnt;
    		blkcnt-=cnt;
    		blknr+=cnt;
    		buffer+=cnt*(ATAPI_READ_BLOCK_SIZE/4); /* ulong blocksize in ulong */
    	} while (blkcnt > 0);
    	return (n);
    }
    
    /* ------------------------------------------------------------------------- */
    
    #endif /* CONFIG_ATAPI */
    
    #endif	/* CONFIG_COMMANDS & CFG_CMD_IDE */