Team Ai
Datasetpublic

hschumann2/TempleOS-Source-Code

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes847downloads
DskATA.txt613 linesDownload Raw Back to BlkDev
1 2U0 ATABlkSel(CBlkDev *bd,I64 blk,I64 cnt)3{4  if (bd->type!=BDT_ATAPI && bd->base1)5    OutU8(bd->base1+ATAR1_CTRL,0x8);6  if (bd->flags & BDF_EXT_SIZE) { //48 Bit LBA?7    OutU8(bd->base0+ATAR0_NSECT,cnt.u8[1]);8    OutU8(bd->base0+ATAR0_SECT,blk.u8[3]);9    OutU8(bd->base0+ATAR0_LCYL,blk.u8[4]);10    OutU8(bd->base0+ATAR0_HCYL,blk.u8[5]);11    OutU8(bd->base0+ATAR0_NSECT,cnt);12    OutU8(bd->base0+ATAR0_SECT,blk);13    OutU8(bd->base0+ATAR0_LCYL,blk.u8[1]);14    OutU8(bd->base0+ATAR0_HCYL,blk.u8[2]);15    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);16  } else { //28 Bit LBA17    OutU8(bd->base0+ATAR0_NSECT,cnt);18    OutU8(bd->base0+ATAR0_SECT,blk);19    OutU8(bd->base0+ATAR0_LCYL,blk.u8[1]);20    OutU8(bd->base0+ATAR0_HCYL,blk.u8[2]);21    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4|blk.u8[3]);22  }23}24 25Bool ATAWaitNotBUSY(CBlkDev *bd,F64 timeout)26{27  I64 i;28  do {29    for (i=0;i<3;i++)30      if (!(InU8(bd->base0+ATAR0_STAT)&ATAS_BSY))31        return TRUE;32    Yield;33  } while (!(0<timeout<tS));34  return FALSE;35}36 37Bool ATAWaitDRQ(CBlkDev *bd,F64 timeout)38{39  I64 i;40  do {41    for (i=0;i<3;i++)42      if (InU8(bd->base0+ATAR0_STAT)&ATAS_DRQ)43        return TRUE;44    Yield;45  } while (!(0<timeout<tS));46  return FALSE;47}48 49Bool ATANop(CBlkDev *bd,F64 timeout)50{51  if (bd->flags & BDF_EXT_SIZE)52    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);53  else54    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);55  OutU8(bd->base0+ATAR0_FEAT,0);56  OutU8(bd->base0+ATAR0_CMD,ATA_NOP);57  return ATAWaitNotBUSY(bd,timeout);58}59 60U0 ATACmd(CBlkDev *bd,U8 cmd)61{62  OutU8(bd->base0+ATAR0_FEAT,0);63  OutU8(bd->base0+ATAR0_CMD,cmd);64  bd->last_time=tS;65  PortNop;66}67 68Bool ATAGetRes(CBlkDev *bd,F64 timeout,U8 *buf,I64 cnt,69                I64 _avail,Bool one_read)70{71  I64 avail,overflow;72  bd->flags&=~BDF_LAST_WAS_WRITE;73  MemSet(buf,0,cnt);74  while (cnt>0) {75    if (!ATAWaitDRQ(bd,timeout))76      return FALSE;77    if (_avail)78      avail=_avail;79    else80      avail=InU8(bd->base0+ATAR0_HCYL)<<8+InU8(bd->base0+ATAR0_LCYL);81    if (avail) {82      if (avail>cnt) {83        overflow=avail-cnt;84        avail=cnt;85      } else86        overflow=0;87      if (avail&2)88        RepInU16(buf,avail>>1,bd->base0+ATAR0_DATA);89      else90        RepInU32(buf,avail>>2,bd->base0+ATAR0_DATA);91      cnt-=avail;92      buf+=avail;93      while (overflow>0) {94        InU16(bd->base0+ATAR0_DATA);95        overflow-=2;96        if (0<timeout<tS)97          return FALSE;98      }99      if (one_read)100        break;101    } else102      Yield;103  }104  return ATAWaitNotBUSY(bd,timeout);105}106 107Bool ATAPIWritePktWord(CBlkDev *bd,F64 timeout,...)108{109  I64 i;110  for (i=0;i<argc;i++) {111    if (!ATAWaitDRQ(bd,timeout))112      return FALSE;113    OutU16(bd->base0+ATAR0_DATA,EndianU16(argv[i]));114    bd->last_time=tS;115  }116  return TRUE;117}118 119Bool ATAPISetMaxSpeed(CBlkDev *bd)120{121  if (bd->flags & BDF_EXT_SIZE)122    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);123  else124    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);125  OutU8(bd->base0+ATAR0_LCYL,0);126  OutU8(bd->base0+ATAR0_HCYL,0);127  ATACmd(bd,ATA_PACKET);128  ATAPIWritePktWord(bd,0,0xBB00,0xFFFF,0xFFFF,0,0,0);129  return ATAWaitNotBUSY(bd,0);130}131 132Bool ATAPISeek(CBlkDev *bd,I64 native_blk)133{134  if (bd->flags & BDF_EXT_SIZE)135    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);136  else137    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);138  OutU8(bd->base0+ATAR0_LCYL,0);139  OutU8(bd->base0+ATAR0_HCYL,0);140  ATACmd(bd,ATA_PACKET);141  ATAPIWritePktWord(bd,0,0x2B00,native_blk>>16,native_blk,0,0,0);142  return ATAWaitNotBUSY(bd,0);143}144 145Bool ATAPIStartStop(CBlkDev *bd,F64 timeout,Bool start)146{147  I64 i;148  if (start)149    i=0x100;150  else151    i=0;152  if (bd->flags & BDF_EXT_SIZE)153    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);154  else155    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);156  ATACmd(bd,ATA_PACKET);157//Start/Stop158  if (ATAPIWritePktWord(bd,timeout,0x1B00,0,i,0,0,0))159    return ATAWaitNotBUSY(bd,timeout);160  else161    return FALSE;162}163 164I64 ATAGetDevId(CBlkDev *bd,F64 timeout,Bool keep_id_record)165{166  I64 res=BDT_NULL;167  U16 *id_record=NULL;168  if (bd->type!=BDT_ATAPI && bd->base1)169    OutU8(bd->base1+ATAR1_CTRL,0x8);170  if (bd->flags & BDF_EXT_SIZE)171    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);172  else173    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);174  ATACmd(bd,ATA_ID_DEV);175  if (ATAWaitNotBUSY(bd,timeout)) {176    if (InU8(bd->base0+ATAR0_STAT)&ATAS_ERR)177      res=BDT_ATAPI;178    else {179      id_record=ACAlloc(512);180      if (ATAGetRes(bd,timeout,id_record,512,512,FALSE))181        res=BDT_ATA;182      else {183        Free(id_record);184        id_record=NULL;185      }186    }187  }188  if (keep_id_record) {189     Free(bd->dev_id_record);190     bd->dev_id_record=id_record;191  }192  return res;193}194 195I64 ATAReadNativeMax(CBlkDev *bd,F64 timeout)196{//Returns zero on err197  I64 res=0;198  Bool okay=TRUE;199  if (bd->type==BDT_ATAPI) {200    if (bd->flags & BDF_EXT_SIZE)201      OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);202    else203      OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);204    ATACmd(bd,ATA_DEV_RST);205    if (!ATAWaitNotBUSY(bd,0))206      okay=FALSE;207  } else {208    while (InU8(bd->base0+ATAR0_STAT) & ATAS_BSY) {209      if (bd->flags&BDF_LAST_WAS_WRITE)210        OutU16(bd->base0+ATAR0_DATA,0);211      else212        InU16(bd->base0+ATAR0_DATA);213      Yield;214      if (0<timeout<tS)215        return FALSE;216    }217    if (ATAGetDevId(bd,timeout,TRUE)==BDT_NULL)218      okay=FALSE;219    else220      BEqu(&bd->flags,BDf_EXT_SIZE,Bt(&bd->dev_id_record[86],10));221  }222  if (okay) {223    if (bd->flags & BDF_EXT_SIZE && bd->base1) {224      OutU8(bd->base1+ATAR1_CTRL,0x8);225      OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);226      ATACmd(bd,ATA_READ_NATIVE_MAX_EXT);227      if (ATAWaitNotBUSY(bd,timeout)) {228        res.u8[0]=InU8(bd->base0+ATAR0_SECT);229        res.u8[1]=InU8(bd->base0+ATAR0_LCYL);230        res.u8[2]=InU8(bd->base0+ATAR0_HCYL);231 232        OutU8(bd->base1+ATAR1_CTRL,0x80);233        res.u8[3]=InU8(bd->base0+ATAR0_SECT);234        res.u8[4]=InU8(bd->base0+ATAR0_LCYL);235        res.u8[5]=InU8(bd->base0+ATAR0_HCYL);236 237        if (res>>24==res&0xFFFFFF) {//Kludge to make QEMU work238          bd->flags&=~BDF_EXT_SIZE;239          res&=0xFFFFFF;240        }241      }242    } else {243      if (bd->type!=BDT_ATAPI && bd->base1)244        OutU8(bd->base1+ATAR1_CTRL,0x8);245      OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);246      ATACmd(bd,ATA_READ_NATIVE_MAX);247      if (ATAWaitNotBUSY(bd,timeout)) {248        res.u8[0]=InU8(bd->base0+ATAR0_SECT);249        res.u8[1]=InU8(bd->base0+ATAR0_LCYL);250        res.u8[2]=InU8(bd->base0+ATAR0_HCYL);251        res.u8[3]=InU8(bd->base0+ATAR0_SEL) & 0xF;252      }253    }254  }255  return bd->max_blk=res;256}257 258I64 ATAPIReadCapacity(CBlkDev *bd,I64 *_blk_size=NULL)259{//Supposedly this can return a res +/- 75 sects.260//Error might just be for music.261  Bool unlock=BlkDevLock(bd);262  U32 buf[2];263  if (ATAWaitNotBUSY(bd,0)) {264    if (bd->flags & BDF_EXT_SIZE)265      OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);266    else267      OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);268    OutU8(bd->base0+ATAR0_LCYL,8);269    OutU8(bd->base0+ATAR0_HCYL,0);270    ATACmd(bd,ATA_PACKET);271    ATAPIWritePktWord(bd,0,0x2500,0,0,0,0,0);272    if (!ATAGetRes(bd,0,buf,8,0,TRUE))273      buf[0]=buf[1]=0;274  } else275    buf[0]=buf[1]=0;276 277  if (unlock) BlkDevUnlock(bd);278  if (_blk_size) *_blk_size=EndianU32(buf[1]);279  return EndianU32(buf[0]);280}281 282CATAPITrack *ATAPIReadTrackInfo(CBlkDev *bd,I64 blk)283{284  CATAPITrack *res=CAlloc(sizeof(CATAPITrack));285  Bool unlock=BlkDevLock(bd);286  if (ATAWaitNotBUSY(bd,0)) {287    if (bd->flags & BDF_EXT_SIZE)288      OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);289    else290      OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);291    OutU8(bd->base0+ATAR0_LCYL,sizeof(CATAPITrack)&0xFF);292    OutU8(bd->base0+ATAR0_HCYL,sizeof(CATAPITrack)>>8);293    ATACmd(bd,ATA_PACKET);294    ATAPIWritePktWord(bd,0,0x5200,blk.u16[1],blk.u16[0],295          (sizeof(CATAPITrack)&0xFF00)>>8,(sizeof(CATAPITrack)&0x00FF)<<8,0);296    if (!ATAGetRes(bd,0,res,sizeof(CATAPITrack),0,TRUE)) {297      Free(res);298      res=NULL;299    }300  } else {301    Free(res);302    res=NULL;303  }304  if (unlock) BlkDevUnlock(bd);305  return res;306}307 308Bool ATAInit(CBlkDev *bd)309{310  Bool unlock=BlkDevLock(bd),okay=FALSE;311 312  if (bd->type==BDT_ATAPI)313    bd->flags&=~BDF_EXT_SIZE;314  else315    bd->flags|=BDF_EXT_SIZE;316 317  if (ATAReadNativeMax(bd,tS+0.1)) {318    ATABlkSel(bd,bd->max_blk,0);319    if (bd->flags&BDF_EXT_SIZE)320      ATACmd(bd,ATA_SET_MAX_EXT);321    else322      ATACmd(bd,ATA_SET_MAX);323    if (ATAWaitNotBUSY(bd,0)) {324      okay=TRUE;325      if (bd->type==BDT_ATAPI) {326        if (ATAPIStartStop(bd,0,TRUE)) {327          if(!ATAPISetMaxSpeed(bd))328            okay=FALSE;329        } else330          okay=FALSE;331      }332    }333  }334  if (unlock) BlkDevUnlock(bd);335  return okay;336}337 338Bool ATAPIWaitReady(CBlkDev *bd,F64 timeout)339{340  do {341    if (!ATAWaitNotBUSY(bd,timeout) ||342          !ATANop(bd,timeout) ||343          !ATAPIStartStop(bd,timeout,TRUE))344      return FALSE;345    if (InU8(bd->base0+ATAR0_STAT) & ATAS_DRDY &&346          !InU8(bd->base0+ATAR0_FEAT));347    return TRUE;348    ATAInit(bd);349    Yield;350  } while (!(0<timeout<tS));351  return FALSE;352}353 354U0 ATAReadBlks(CBlkDev *bd,U8 *buf, I64 blk, I64 cnt)355{356  I64 retries=3;357  Bool unlock=BlkDevLock(bd);358 359  retry:360  ATABlkSel(bd,blk,cnt);361  if (bd->flags & BDF_EXT_SIZE)362    ATACmd(bd,ATA_READ_MULTI_EXT);363  else364    ATACmd(bd,ATA_READ_MULTI);365  if (!ATAGetRes(bd,tS+1.0,buf,cnt*bd->blk_size,BLK_SIZE,FALSE)) {366    if (retries--) {367      ATAWaitNotBUSY(bd,0);368      goto retry;369    } else370      throw('BlkDev');371  }372 373  blkdev.read_cnt+=(cnt*bd->blk_size)>>BLK_SIZE_BITS;374  if (unlock) BlkDevUnlock(bd);375}376 377I64 ATAProbe(I64 base0,I64 base1,I64 unit)378{379  CBlkDev bd;380  MemSet(&bd,0,sizeof(CBlkDev));381  bd.type=BDT_ATAPI;382  bd.base0=base0;383  bd.base1=base1;384  bd.unit=unit;385  bd.blk_size=DVD_BLK_SIZE;386  return ATAGetDevId(&bd,tS+0.1,FALSE);387}388 389Bool ATAPIReadBlks2(CBlkDev *bd,F64 timeout,U8 *buf,390                I64 native_blk, I64 cnt,Bool lock)391{392  Bool res=FALSE,unlock;393  if (cnt<=0)394    return FALSE;395  if (lock)396    unlock=BlkDevLock(bd);397  if (ATAPIWaitReady(bd,timeout)) {398    if (bd->flags & BDF_EXT_SIZE)399      OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);400    else401      OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);402    OutU8(bd->base0+ATAR0_LCYL,bd->blk_size);403    OutU8(bd->base0+ATAR0_HCYL,bd->blk_size.u8[1]);404    ATACmd(bd,ATA_PACKET);405    if (ATAPIWritePktWord(bd,timeout,0xA800,406          native_blk.u16[1],native_blk,cnt.u16[1],cnt,0) &&407          ATAGetRes(bd,timeout,buf,cnt*bd->blk_size,0,FALSE)) {408      blkdev.read_cnt+=(cnt*bd->blk_size)>>BLK_SIZE_BITS;409      res=TRUE;410    }411  }412//  ATAPIStartStop(bd,0,FALSE);413  if (lock && unlock) BlkDevUnlock(bd);414  return res;415}416 417U0 ATAPIReadBlks(CBlkDev *bd,U8 *buf, I64 blk, I64 cnt)418{419  CDrv *dv=Let2Drv(bd->first_drv_let);420  I64 retry,spc=bd->blk_size>>BLK_SIZE_BITS,n,blk2,421        l2=bd->max_reads<<1+spc<<1;422  U8 *dvd_buf=MAlloc(l2<<BLK_SIZE_BITS);423  if (cnt>0) {424    if (blk<=bd->max_reads)425      blk2=0;426    else427      blk2=FloorU64(blk-bd->max_reads,spc);428    if (blk2+l2>dv->size+dv->drv_offset)429      l2=dv->size+dv->drv_offset-blk2;430    n=(l2+spc-1)/spc;431 432    retry=4;433    while (--retry)434      if (ATAPIReadBlks2(bd,tS+7.0+0.004*n,dvd_buf,blk2/spc,n,TRUE))435//n is 0x800 if max_reads.  Up to 8 additional seconds436        break;437    if (!retry)438      ATAPIReadBlks2(bd,0,dvd_buf,blk2/spc,n,TRUE);439    if (bd->flags & BDF_READ_CACHE)440      DskCacheAdd(dv,dvd_buf,blk2,n*spc);441    MemCpy(buf,dvd_buf+(blk-blk2)<<BLK_SIZE_BITS,cnt<<BLK_SIZE_BITS);442  }443  Free(dvd_buf);444}445 446Bool ATARBlks(CDrv *dv,U8 *buf, I64 blk, I64 cnt)447{448  I64 n;449  CBlkDev *bd=dv->bd;450  while (cnt>0) {451    n=cnt;452    if (n>bd->max_reads)453      n=bd->max_reads;454    if (bd->type==BDT_ATAPI)455      ATAPIReadBlks(bd,buf,blk,n);456    else457      ATAReadBlks(bd,buf,blk,n);458    buf+=n<<BLK_SIZE_BITS;459    blk+=n;460    cnt-=n;461  }462  return TRUE;463}464 465U0 ATAWriteBlks(CBlkDev *bd,U8 *buf, I64 blk, I64 cnt)466{//For low level disk access.467//Use BlkWrite() instead.468  I64 i,U32s_avail,sects_avail,retries=3;469  F64 timeout;470  Bool unlock=BlkDevLock(bd);471retry:472  ATABlkSel(bd,blk,cnt);473  if (bd->flags&BDF_EXT_SIZE)474    ATACmd(bd,ATA_WRITE_MULTI_EXT);475  else476    ATACmd(bd,ATA_WRITE_MULTI);477  bd->flags|=BDF_LAST_WAS_WRITE;478  while (cnt>0) {479    timeout=tS+1.0;480    while (TRUE) {481      i=InU8(bd->base0+ATAR0_STAT);482      if (!(i & ATAS_DRDY)||!(i & ATAS_DRQ)) {483        Yield;484      } else485        break;486      if (/* i&ATAS_ERR||*/ tS>timeout) {487        if (retries--) {488          ATAWaitNotBUSY(bd,0);489          goto retry;490        } else491          throw('BlkDev');492      }493    }494    sects_avail=1;495    U32s_avail=sects_avail<<BLK_SIZE_BITS>>2;496    RepOutU32(buf,U32s_avail,bd->base0+ATAR0_DATA);497    buf+=U32s_avail<<2;498    cnt-=sects_avail;499    retries=3;500  }501  ATAWaitNotBUSY(bd,0);502  if (unlock) BlkDevUnlock(bd);503}504 505Bool ATAPISync(CBlkDev *bd)506{507  Bool okay=TRUE;508  if (!ATAWaitNotBUSY(bd,0))509    okay=FALSE;510  else {511    if (bd->flags & BDF_EXT_SIZE)512      OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);513    else514      OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);515    OutU8(bd->base0+ATAR0_LCYL,0);516    OutU8(bd->base0+ATAR0_HCYL,0);517    ATACmd(bd,ATA_PACKET);518    ATAPIWritePktWord(bd,0,0x3500,0,0,0,0,0);519    if (!ATAWaitNotBUSY(bd,0))520      okay=FALSE;521  }522  return okay;523}524 525U0 ATAPIClose(CBlkDev *bd,I64 close_field=0x200,I64 track=0)526{//0x200 CD/DVD part 1527// 0x300    DVD part 2528  if (bd->flags & BDF_EXT_SIZE)529    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);530  else531    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);532  OutU8(bd->base0+ATAR0_LCYL,0);533  OutU8(bd->base0+ATAR0_HCYL,0);534  ATACmd(bd,ATA_PACKET);535  ATAPIWritePktWord(bd,0,0x5B00,close_field,track,0,0,0);536  ATAWaitNotBUSY(bd,0);537}538 539U0 ATAPIWriteBlks(CBlkDev *bd,U8 *buf, I64 native_blk, I64 cnt)540{541  I64 U32s_avail;542  U8 *buf2;543  ATAWaitNotBUSY(bd,0);544  ATAPISeek(bd,native_blk);545 546  OutU8(bd->base0+ATAR0_FEAT,0);547  OutU8(bd->base0+ATAR0_LCYL,bd->blk_size);548  OutU8(bd->base0+ATAR0_HCYL,bd->blk_size.u8[1]);549  if (bd->flags & BDF_EXT_SIZE)550    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);551  else552    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);553  OutU8(bd->base0+ATAR0_CMD,ATA_PACKET);554  ATAPIWritePktWord(bd,0,0x0400,native_blk.u16[1],native_blk,cnt.u16[1],cnt,0);555  bd->flags|=BDF_LAST_WAS_WRITE;556  ATAWaitNotBUSY(bd,0);557 558  ATAPISeek(bd,native_blk);559 560  if (bd->flags & BDF_EXT_SIZE)561    OutU8(bd->base0+ATAR0_SEL,0xEF|bd->unit<<4);562  else563    OutU8(bd->base0+ATAR0_SEL,0xE0|bd->unit<<4);564  OutU8(bd->base0+ATAR0_LCYL,bd->blk_size);565  OutU8(bd->base0+ATAR0_HCYL,bd->blk_size.u8[1]);566  ATACmd(bd,ATA_PACKET);567  ATAPIWritePktWord(bd,0,0xAA00,native_blk.u16[1],native_blk,cnt.u16[1],cnt,0);568  buf2=buf+bd->blk_size*cnt;569  while (buf<buf2) {570    ATAWaitDRQ(bd,0);571    U32s_avail=(InU8(bd->base0+ATAR0_HCYL)<<8+InU8(bd->base0+ATAR0_LCYL))>>2;572    if (buf+U32s_avail<<2>buf2)573      U32s_avail=(buf2-buf)>>2;574    if (U32s_avail) {575      RepOutU32(buf,U32s_avail,bd->base0+ATAR0_DATA);576      buf+=U32s_avail<<2;577      blkdev.write_cnt+=U32s_avail>>(BLK_SIZE_BITS-2);578    }579  }580  ATAWaitNotBUSY(bd,0);581}582 583Bool ATAWBlks(CDrv *dv,U8 *buf, I64 blk, I64 cnt)584{585  I64 n,spc;586  CBlkDev *bd=dv->bd;587  Bool unlock;588  spc=bd->blk_size>>BLK_SIZE_BITS;589  if (bd->type==BDT_ATAPI) {590    unlock=BlkDevLock(bd);591    ATAPIWaitReady(bd,0);592  }593  while (cnt>0) {594    n=cnt;595    if (n>bd->max_writes)596      n=bd->max_writes;597    if (bd->type==BDT_ATAPI)598      ATAPIWriteBlks(bd,buf,blk/spc,(n+spc-1)/spc);599    else600      ATAWriteBlks(bd,buf,blk,n);601    buf+=n<<BLK_SIZE_BITS;602    blk+=n;603    cnt-=n;604    blkdev.write_cnt+=n;605  }606  if (bd->type==BDT_ATAPI) {607    ATAPISync(bd);608//    ATAPIStartStop(bd,0,FALSE);609    if (unlock) BlkDevUnlock(bd);610  }611  return TRUE;612}613