| \ ***************************************************************************** |
| \ * Copyright (c) 2004, 2008 IBM Corporation |
| \ * All rights reserved. |
| \ * This program and the accompanying materials |
| \ * are made available under the terms of the BSD License |
| \ * which accompanies this distribution, and is available at |
| \ * http://www.opensource.org/licenses/bsd-license.php |
| \ * |
| \ * Contributors: |
| \ * IBM Corporation - initial implementation |
| \ ****************************************************************************/ |
| \ |
| \ 26.06.2007 added: two devices (Master/Slave) per channel |
| |
| 1 encode-int s" #address-cells" property |
| 0 encode-int s" #size-cells" property |
| |
| : decode-unit 1 hex-decode-unit ; |
| : encode-unit 1 hex-encode-unit ; |
| |
| 0 VALUE >ata \ base address for command-block |
| 0 VALUE >ata1 \ base address for control block |
| |
| true VALUE no-timeout \ flag that no timeout occurred |
| |
| 0c CONSTANT #cdb-bytes \ command descriptor block (12 bytes) |
| 800 CONSTANT atapi-size |
| 200 CONSTANT ata-size |
| |
| \ ***************************** |
| \ Some register access helpers. |
| \ ***************************** |
| : ata-ctrl! 2 >ata1 + io-c! ; \ device control reg |
| : ata-astat@ 2 >ata1 + io-c@ ; \ read alternate status |
| |
| : ata-data@ 0 >ata + io-w@ ; \ data reg |
| : ata-data! 0 >ata + io-w! ; \ data reg |
| : ata-err@ 1 >ata + io-c@ ; \ error reg |
| : ata-feat! 1 >ata + io-c! ; \ feature reg |
| : ata-cnt@ 2 >ata + io-c@ ; \ sector count reg |
| : ata-cnt! 2 >ata + io-c! ; \ sector count reg |
| : ata-lbal! 3 >ata + io-c! ; \ lba low reg |
| : ata-lbal@ 3 >ata + io-c@ ; \ lba low reg |
| : ata-lbam! 4 >ata + io-c! ; \ lba mid reg |
| : ata-lbam@ 4 >ata + io-c@ ; \ lba mid reg |
| : ata-lbah! 5 >ata + io-c! ; \ lba high reg |
| : ata-lbah@ 5 >ata + io-c@ ; \ lba high reg |
| : ata-dev! 6 >ata + io-c! ; \ device reg |
| : ata-dev@ 6 >ata + io-c@ ; \ device reg |
| : ata-cmd! 7 >ata + io-c! ; \ command reg |
| : ata-stat@ 7 >ata + io-c@ ; \ status reg |
| |
| \ ********************************************************************** |
| \ ATA / ATAPI Commands specifications: |
| \ - AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) |
| \ - ATA Packet Interface for CD-ROMs SFF-8020i |
| \ - ATA/ATAPI Host Adapters Standard (T13/1510D) |
| \ ********************************************************************** |
| 00 CONSTANT cmd#nop \ ATA and ATAPI |
| 08 CONSTANT cmd#device-reset \ ATAPI only (mandatory) |
| 20 CONSTANT cmd#read-sector \ ATA and ATAPI |
| 90 CONSTANT cmd#execute-device-diagnostic \ ATA and ATAPI |
| a0 CONSTANT cmd#packet \ ATAPI only (mandatory) |
| a1 CONSTANT cmd#identify-packet-device \ ATAPI only (mandatory) |
| ec CONSTANT cmd#identify-device \ ATA and ATAPI |
| |
| \ ***************************** |
| \ Setup Regs for ATA: |
| \ BAR 0 & 1 : Device 0 |
| \ BAR 2 & 3 : Device 1 |
| \ ***************************** |
| : set-regs ( n -- ) |
| dup |
| 01 and \ only Chan 0 or Chan 1 allowed |
| 3 lshift dup 10 + config-l@ -4 and to >ata |
| 14 + config-l@ -4 and to >ata1 |
| 02 ata-ctrl! \ disable interrupts |
| 02 and |
| IF |
| 10 |
| ELSE |
| 00 |
| THEN |
| ata-dev! |
| ; |
| |
| ata-size VALUE block-size |
| 80000 VALUE max-transfer \ Arbitrary, really |
| |
| CREATE sector d# 512 allot |
| CREATE packet-cdb #cdb-bytes allot |
| CREATE return-buffer atapi-size allot |
| |
| scsi-open \ add scsi functions |
| |
| \ ******************************** |
| \ show all ATAPI-registers |
| \ data-register not read in order |
| \ to not influence PIO mode |
| \ ******************************** |
| : show-regs |
| cr |
| cr ." alt. Status: " ata-astat@ . |
| cr ." Status : " ata-stat@ . |
| cr ." Device : " ata-dev@ . |
| cr ." Error-Reg : " ata-err@ . |
| cr ." Sect-Count : " ata-cnt@ . |
| cr ." LBA-Low : " ata-lbal@ . |
| cr ." LBA-Med : " ata-lbam@ . |
| cr ." LBA-High : " ata-lbah@ . |
| ; |
| |
| \ *************************************************** |
| \ reads ATAPI-Status and displays it if check-bit set |
| \ *************************************************** |
| : status-check ( -- ) |
| ata-stat@ |
| dup |
| 01 and \ is 'check' flag set ? |
| IF |
| cr |
| ." - ATAPI-Status: " . |
| ata-err@ \ retrieve sense code |
| dup |
| 60 = \ sense code = 6 ? |
| IF |
| ." ( media changed or reset )" \ 'unit attention' |
| drop \ drop err-reg content |
| ELSE |
| dup |
| ." (Err : " . \ show err-reg content |
| space |
| rshift 4 .sense-text \ show text string |
| 29 emit |
| THEN |
| cr |
| ELSE |
| drop \ remove unused status |
| THEN |
| ; |
| |
| \ ************************************* |
| \ Wait for interface ready condition |
| \ Bit 7 of Status-Register is busy flag |
| \ new version with abort after 5 sec. |
| \ ************************************* |
| : wait-for-ready |
| get-msecs \ start timer |
| BEGIN |
| ata-stat@ 80 and 0<> \ busy flag still set ? |
| no-timeout and |
| WHILE \ yes |
| dup get-msecs swap |
| - \ calculate timer difference |
| FFFF AND \ reduce to 65.5 seconds |
| d# 5000 > \ difference > 5 seconds ? |
| IF |
| false to no-timeout |
| THEN |
| REPEAT |
| drop |
| ; |
| |
| \ ************************************* |
| \ wait for specific status bits |
| \ new version with abort after 5 sec. |
| \ ************************************* |
| : wait-for-status ( val mask -- ) |
| get-msecs \ initial timer value (start) |
| >r |
| BEGIN |
| 2dup \ val mask |
| ata-stat@ and <> \ expected status ? |
| no-timeout and \ and no timeout ? |
| WHILE |
| get-msecs r@ - \ calculate timer difference |
| FFFF AND \ mask-off overflow bits |
| d# 5000 > \ 5 seconds exceeded ? |
| IF |
| false to no-timeout \ set global flag |
| THEN |
| REPEAT |
| r> \ clean return stack |
| 3drop |
| ; |
| |
| \ ********************************* |
| \ remove extra spaces from string end |
| \ ********************************* |
| : cut-string ( saddr nul -- ) |
| swap |
| over + |
| swap |
| 1 rshift \ bytecount -> wordcount |
| 0 do |
| /w - |
| dup ( addr -- addr addr ) |
| w@ ( addr addr -- addr nuw ) |
| dup ( addr nuw -- addr nuw nuw ) |
| 2020 = |
| IF |
| drop |
| 0 |
| ELSE |
| LEAVE |
| THEN |
| over |
| w! |
| LOOP |
| drop |
| drop |
| ; |
| |
| \ **************************************************** |
| \ prints model-string received by identify device |
| \ **************************************************** |
| : show-model ( dev# chan# -- ) |
| 2dup |
| ." CH " . \ channel 0 / 1 |
| 0= IF ." / MA" \ Master / Slave |
| ELSE ." / SL" |
| THEN |
| swap |
| 2 * + ." (@" . ." ) : " \ device number |
| sector 1 + |
| c@ |
| 80 AND 0= |
| IF |
| ." ATA-Drive " |
| ELSE |
| ." ATAPI-Drive " |
| THEN |
| |
| 22 emit \ start string display with " |
| sector d# 54 + \ string starts 54 bytes from buffer start |
| dup |
| d# 40 \ and is 40 chars long |
| cut-string \ remove all trailing spaces |
| |
| BEGIN |
| dup |
| w@ |
| wbflip |
| wbsplit |
| dup 0<> \ first char |
| IF |
| emit |
| dup 0<> \ second char |
| IF |
| emit |
| wa1+ \ increment address for next |
| false |
| ELSE \ second char = EndOfString |
| drop |
| true |
| THEN |
| ELSE \ first char = EndOfString |
| drop |
| drop |
| true |
| THEN |
| UNTIL \ end of string detected |
| drop |
| 22 emit \ end string display |
| |
| sector c@ \ get lower byte of first doublet |
| 80 AND \ check bit 7 |
| IF |
| ." (removable media)" |
| THEN |
| |
| sector 1 + |
| c@ |
| 80 AND 0= IF \ is this an ATA drive ? |
| sector d# 120 + \ get word 60 + 61 |
| rl@-le \ read 32-bit as little endian value |
| d# 512 \ standard ATA block-size |
| swap |
| .capacity-text ( block-size #blocks -- ) |
| THEN |
| |
| sector d# 98 + \ goto word 49 |
| w@ |
| wbflip |
| 200 and 0= IF cr ." ** LBA is not supported " THEN |
| |
| sector c@ \ get lower byte of first doublet |
| 03 AND 01 = \ we use 12-byte packet commands (=00b) |
| IF |
| cr ." packet size = 16 ** not supported ! **" |
| THEN |
| no-timeout not \ any timeout occurred so far ? |
| IF |
| cr ." ** timeout **" |
| THEN |
| ; |
| |
| \ **************************** |
| \ ATA functions |
| \ **************************** |
| : pio-sector ( addr -- ) 100 0 DO ata-data@ |
| over w! wa1+ LOOP drop ; |
| : pio-sector ( addr -- ) |
| wait-for-ready pio-sector ; |
| : pio-sectors ( n addr -- ) swap 0 ?DO dup pio-sector 200 + LOOP drop ; |
| |
| : lba! lbsplit |
| 0f and 40 or \ always set LBA-mode + LBA (27..24) |
| ata-dev@ 10 and or \ add current device-bit (DEV) |
| ata-dev! \ set LBA (27..24) |
| ata-lbah! \ set LBA (23..16) |
| ata-lbam! \ set LBA (15..8) |
| ata-lbal! \ set LBA (7..0) |
| ; |
| |
| : read-sectors ( lba count addr -- ) |
| >r dup >r ata-cnt! lba! 20 ata-cmd! r> r> pio-sectors ; |
| |
| : read-sectors ( lba count addr dev-nr -- ) |
| set-regs ( lba count addr ) \ Set ata regs |
| BEGIN >r dup 100 > WHILE |
| over 100 r@ read-sectors |
| >r 100 + r> 100 - r> 20000 + REPEAT |
| r> read-sectors |
| ; |
| |
| : ata-read-blocks ( addr block# #blocks dev# -- #read ) |
| swap dup >r swap >r rot r> ( addr block# #blocks dev # R: #blocks ) |
| read-sectors r> ( R: #read ) |
| ; |
| |
| \ ******************************* |
| \ ATAPI functions |
| \ preset LBA register with maximum |
| \ allowed block-size (16-bits) |
| \ ******************************* |
| : set-lba ( block-length -- ) |
| lbsplit ( quad -- b1.lo b2 b3 b4.hi ) |
| drop \ skip upper two bytes |
| drop |
| ata-lbah! |
| ata-lbam! |
| ; |
| |
| \ ******************************************* |
| \ gets byte-count and reads a block of words |
| \ from data-register to a buffer |
| \ ******************************************* |
| : read-pio-block ( buff-addr -- buff-addr-new ) |
| ata-lbah@ 8 lshift \ get block length High |
| ata-lbam@ or \ get block length Low |
| 1 rshift \ bcount -> wcount |
| dup |
| 0> IF \ any data to transfer? |
| 0 DO \ words to read |
| dup \ buffer-address |
| ata-data@ swap w! \ write 16-bits |
| wa1+ \ address of next entry |
| LOOP |
| ELSE |
| drop ( buff-addr wcount -- buff-addr ) |
| THEN |
| wait-for-ready |
| ; |
| |
| \ ******************************************** |
| \ ATAPI support |
| \ Send a command block (12 bytes) in PIO mode |
| \ read data if requested |
| \ ******************************************** |
| : send-atapi-packet ( req-buffer -- ) |
| >r ( R: req-buffer ) |
| atapi-size set-lba \ set regs to length limit |
| 00 ata-feat! |
| cmd#packet ata-cmd! \ A0 = ATAPI packet command |
| 48 C8 wait-for-status ( val mask -- ) \ BSY:0 DRDY:1 DRQ:1 |
| 6 0 do |
| packet-cdb i 2 * + \ transfer command block (12 bytes) |
| w@ |
| ata-data! \ 6 doublets PIO transfer to device |
| loop \ copy packet to data-reg |
| status-check ( -- ) \ status err bit set ? -> display |
| wait-for-ready ( -- ) \ busy released ? |
| BEGIN |
| ata-stat@ 08 and 08 = WHILE \ Data-Request-Bit set ? |
| r> \ get last target buffer address |
| read-pio-block \ only if from device requested |
| >r \ start of next block |
| REPEAT |
| r> \ original value |
| drop \ return clean |
| ; |
| |
| : atapi-packet-io ( -- ) |
| return-buffer atapi-size erase \ clear return buffer |
| return-buffer send-atapi-packet \ send 'packet-cdb' , get 'return-buffer' |
| ; |
| |
| |
| |
| \ ******************************** |
| \ ATAPI packet commands |
| \ ******************************** |
| |
| \ Methods to access atapi disk |
| |
| : atapi-test ( -- true|false ) |
| packet-cdb scsi-build-test-unit-ready \ command-code: 00 |
| atapi-packet-io ( ) \ send CDB, get return-buffer |
| ata-stat@ 1 and IF false ELSE true THEN |
| ; |
| |
| : atapi-sense ( -- ascq asc sense-key ) |
| d# 252 packet-cdb scsi-build-request-sense ( alloc-len cdb -- ) |
| atapi-packet-io ( ) \ send CDB, get return-buffer |
| return-buffer scsi-get-sense-data ( cdb-addr -- ascq asc sense-key ) |
| ; |
| |
| : atapi-read-blocks ( address block# #blocks dev# -- #read-blocks ) |
| set-regs ( address block# #blocks ) |
| dup >r ( address block# #blocks ) |
| packet-cdb scsi-build-read-10 ( address block# #blocks cdb -- ) |
| send-atapi-packet ( address -- ) |
| r> \ return requested number of blocks |
| ; |
| |
| \ *************************************** |
| \ read capacity of drive medium |
| \ use SCSI-Support Package |
| \ *************************************** |
| : atapi-read-capacity ( -- ) |
| packet-cdb scsi-build-read-cap-10 \ fill block with command |
| atapi-packet-io ( ) \ send CDB, get return-buffer |
| return-buffer scsi-get-capacity-10 ( cdb -- block-size #blocks ) |
| .capacity-text ( block-size #blocks -- ) |
| status-check ( -- ) |
| ; |
| |
| \ *************************************** |
| \ read capacity of drive medium |
| \ use SCSI-Support Package |
| \ *************************************** |
| : atapi-read-capacity-ext ( -- ) |
| packet-cdb scsi-build-read-cap-16 \ fill block with command |
| atapi-packet-io ( ) \ send CDB, get return-buffer |
| return-buffer scsi-get-capacity-16 ( cdb -- block-size #blocks ) |
| .capacity-text ( block-size #blocks -- ) |
| status-check ( -- ) |
| ; |
| |
| |
| \ *********************************************** |
| \ wait until media in drive is ready ( max 5 sec) |
| \ *********************************************** |
| : wait-for-media-ready ( -- true|false ) |
| get-msecs \ initial timer value (start) |
| >r |
| BEGIN |
| atapi-test \ unit ready? false if not |
| not |
| no-timeout and |
| WHILE |
| atapi-sense ( -- ascq asc sense-key ) |
| 02 = \ sense key 2 = media error |
| IF \ check add. sense code |
| 3A = \ asc: device not ready ? |
| IF |
| false to no-timeout |
| ." empty (" . 29 emit \ show asc qualifier |
| ELSE |
| drop \ discard asc qualifier |
| THEN \ medium not present, abort waiting |
| ELSE |
| drop \ discard asc |
| drop \ discard ascq |
| THEN |
| get-msecs r@ - \ calculate timer difference |
| FFFF AND \ mask-off overflow bits |
| d# 5000 > \ 5 seconds exceeded ? |
| IF |
| false to no-timeout \ set global flag |
| THEN |
| REPEAT |
| r> |
| drop |
| no-timeout |
| ; |
| |
| \ ****************************************************** |
| \ Method pointer for read-blocks methods |
| \ controller implements 2 channels (primary / secondary) |
| \ for 2 devices each (master / slasve) |
| \ ****************************************************** |
| \ 2 channels (primary/secondary) per controller |
| 2 CONSTANT #chan |
| |
| \ 2 devices (master/slave) per channel |
| 2 CONSTANT #dev |
| |
| \ results in a total of devices |
| \ connected to a controller with |
| \ two separate channels (4) |
| : #totaldev #dev #chan * ; |
| |
| CREATE read-blocks-xt #totaldev cells allot read-blocks-xt #totaldev cells erase |
| |
| \ Execute read-blocks of device |
| : dev-read-blocks ( address block# #blocks dev# -- #read-blocks ) |
| dup cells read-blocks-xt + @ execute |
| ; |
| |
| \ ********************************************************** |
| \ Read device type |
| \ Signature ATAPI ATA |
| \ --------------------------------------------- |
| \ Sector Count 01h 01h |
| \ Sector Number 01h 01h |
| \ Cylinder Low 14h 00h |
| \ Cylinder High EBh 00h |
| \ Device/Head 00h or 10h 00h or 01h |
| \ see also ATA/ATAPI errata at: |
| \ http://suif.stanford.edu/~csapuntz/blackmagic.html |
| \ ********************************************************** |
| : read-ident ( -- true|false ) |
| false |
| 00 ata-lbal! \ clear previous signature |
| 00 ata-lbam! |
| 00 ata-lbah! |
| cmd#identify-device ata-cmd! wait-for-ready \ first try ATA, ATAPI aborts command |
| ata-stat@ CF and 48 = |
| IF |
| drop true \ cmd accepted, this is a ATA |
| d# 512 set-lba \ set LBA to sector-length |
| ELSE \ ATAPI sends signature instead |
| ata-lbam@ 14 = IF \ cylinder low = 14 ? |
| ata-lbah@ EB = IF \ cylinder high = EB ? |
| cmd#device-reset ata-cmd! wait-for-ready \ only supported by ATAPI |
| cmd#identify-packet-device ata-cmd! wait-for-ready \ first try ata |
| ata-stat@ CF and 48 = IF |
| drop true \ replace flag |
| THEN |
| THEN |
| THEN |
| THEN |
| dup IF |
| ata-stat@ 8 AND IF \ data requested (as expected) ? |
| sector read-pio-block |
| drop \ discard address end |
| ELSE |
| drop false |
| THEN |
| THEN |
| |
| no-timeout not IF \ check without any timeout ? |
| drop |
| false \ no, detection discarded |
| THEN |
| ; |
| |
| scsi-close \ remove scsi commands from word list |
| |
| |
| \ ************************************************* |
| \ Init controller ( chan 0 and 1 ) |
| \ device 0 (= master) and device 1 ( = slave) |
| \ #dev #chan Dev-ID |
| \ ---------------------- |
| \ 0 0 0 Master of Channel 0 |
| \ 0 1 1 Master of Channel 1 |
| \ 1 0 2 Slave of Channel 0 |
| \ 1 1 3 Slave of Channel 1 |
| \ ************************************************* |
| : find-disks ( -- ) |
| #chan 0 DO \ check 2 channels (primary & secondary) |
| #dev 0 DO \ check 2 devices per channel (master / slave) |
| i 2 * j + |
| set-regs \ set base address and dev-register for register access |
| ata-stat@ 7f and 7f <> \ Check, if device is connected |
| IF |
| true to no-timeout \ preset timeout-flag |
| read-ident ( -- true|false ) |
| IF |
| i j show-model \ print manufacturer + device string |
| sector 1+ c@ C0 and 80 = \ Check for ata or atapi |
| IF |
| wait-for-media-ready \ wait up to 5 sec if not ready |
| no-timeout and |
| IF |
| atapi-read-capacity |
| atapi-size to block-size \ ATAPI: 2048 bytes |
| 80000 to max-transfer |
| ['] atapi-read-blocks i 2 * j + cells read-blocks-xt + ! |
| s" cdrom" strdup i 2 * j + s" generic-disk.fs" included |
| ELSE |
| ." -" \ show hint for not registered |
| THEN |
| ELSE |
| ata-size to block-size \ ATA: 512 bytes |
| 80000 to max-transfer |
| ['] ata-read-blocks i 2 * j + cells read-blocks-xt + ! |
| s" disk" strdup i 2 * j + s" generic-disk.fs" included |
| THEN |
| cr |
| THEN |
| THEN |
| i 2 * j + 200 + cp |
| LOOP |
| LOOP |
| ; |
| |
| find-disks |
| |