Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1209874
  • 博文数量: 122
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4002
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-20 08:27
文章分类
文章存档

2016年(1)

2015年(21)

2014年(100)

分类: LINUX

2014-08-08 14:47:46

programming_ata_atapi  
An Introduction To Programming With ATA And ATAPI
If you're familiar with computer maintenance procedures,  
you're probably familiar with IDE (Integrated Drive Electronics),  
the typical standard for connecting hard drives and CD-ROM drives  
to a PC. ATA (AT Attachment) is essentially the same thing as IDE;  
just a different name. 
 
Most motherboards today have two IDE controllers built-in,  
designated as the primary and the secondary IDE controller.  
These two IDE controllers use the following standard I/O addresses: 
 
Primary IDE controller: 1F0h to 1F7h and 3F6h to 3F7h 
Secondary IDE controller: 170h to 177h and 376h to 377h 
 
Each I/O address corresponds to a register on the IDE controller.  
The following is a list of each I/O address used by ATA controllers  
and the corresponding regiuster. (I/O addys given are for the primary  
IDE controller, obviously, but they correspond to the same secondary  
IDE controller addresses. Thus, for example, the secondary IDE  
controller's data register is at 170h, the secondary controller's  
error and features register is at 171h, and so on): 
 
1F0 (Read and Write): Data Register 
1F1 (Read): Error Register 
1F1 (Write): Features Register 
1F2 (Read and Write): Sector Count Register 
1F3 (Read and Write): LBA Low Register 
1F4 (Read and Write): LBA Mid Register 
1F5 (Read and Write): LBA High Register 
1F6 (Read and Write): Drive/Head Register 
1F7 (Read): Status Register 
1F7 (Write): Command Register 
3F6 (Read): Alternate Status Register 
3F6 (Write): Device Control Register 
 
The status register is an 8-bit register which contains the following bits,  
listed in order from left to right: 
 
BSY (busy) 
DRDY (device ready) 
DF (Device Fault) 
DSC (seek complete) 
DRQ (Data Transfer Requested) 
CORR (data corrected) 
IDX (index mark) 
ERR (error) 
 
The error register is also an 8-bit register, and contains the following bits,  
again listed in order from left to right: 
 
BBK (Bad Block) 
UNC (Uncorrectable data error) 
MC (Media Changed) 
IDNF (ID mark Not Found) 
MCR (Media Change Requested) 
ABRT (command aborted) 
TK0NF (Track 0 Not Found) 
AMNF (Address Mark Not Found) 
 
ATA commands are issued by writing the commands to the command register.  
More specifically, ATA commands are issued using the following steps: 
 
1. Poll the status register until it indicates the device is not busy  
(BUSY will be set to 0) 
 
2. Disable interrupts (assembler "cli" command) 
 
3. Poll the status register until it indicates the device is ready  
(DRDY will be set to 1) 
 
4. Issue the command by outputting the command opcode to the command register 
 
5. Re-enable interrupts (assembler "sti" command) 
 
The following program is a relatively simple assembler program to run  
the ATA "IDENTIFY DRIVE" command, and print out the results of this  
command to the screen. 
 
MOV DX, 1F7h ;status register 
LOOP1: 
IN AL, DX ;sets AL to status register (which is 8 bits) 
 
;If the first bit of the status register (BUSY) isn't 0, the device is busy, 
;so keep looping until it isn't. 
 
AND AL, 10000000xB 
JNE LOOP1 
 
;---------------------------------------------------------------------------- 
 
;Clear interrupts so something doesn't interrupt the drive or controller 
;while this program is working. 
CLI 
 
;---------------------------------------------------------------------------- 
 
MOV DX, 1F7h ;status register again 
LOOP2: 
IN AL, DX ;sets AL to status register again 
 
;If the second bit of the status register (DRDY) isn't 1, the device isn't 
;ready, so keep looping until it is. 
 
AND AL, 01000000xB 
JE LOOP2 
 
;---------------------------------------------------------------------------- 
 
MOV DX, 1F6h ;device/head register 
MOV AL, 0 ;0 selects device 0 (master). 10h would select device 1 (slave). 
OUT DX, AL ;selects master device 
 
MOV DX, 1F7h ;command register 
MOV AL, 0ECh ;"IDENTIFY DRIVE" command 
OUT DX, AL ;sends the command! 
 
;---------------------------------------------------------------------------- 
 
MOV DX, 1F7h ;status register 
LOOP3: 
IN AL, DX 
 
AND AL, 00001000xB ;if DRQ is not high, the device doesn't have data for us 
JE LOOP3 ;yet, so keep looking until it does! 
 
;---------------------------------------------------------------------------- 
 
MOV DX, 1F0h ;data register 
MOV DI, OFFSET buff ;points DI to the buffer we're using 
MOV CX, 256 ;256 decimal. This controls the REP command. 
CLD ;clear the direction flag so INSW increments DI (not decrements it) 
REP INSW 
 
;---------------------------------------------------------------------------- 
 
;We now have the string data in buff, so let's re-enable interrupts. 
 
STI 
 
;---------------------------------------------------------------------------- 
 
;...And now we can display the contents of buff! 
 
MOV ES, SEG buff 
MOV BX, OFFSET buff 
MOV CX, 256 ;256 decimal 
MOV AH, 2 ;"display output" option for INT 21 
LOOP4: 
MOV DL, [BX] ;moves the contents of the byte from "buff" into DL 
INT 21h 
INC BX 
LOOPNZ LOOP4 ;does this 256 times, because CX was set to 256 
 
mov ah,004C  ;terminate program 
int 21h 
 
buff db 256 DUP(?) ;buffer to hold the drive identification info 
 
ATAPI (ATA Packet Interface) is an extension to ATA which  
essentially allows SCSI commands (commands used to control SCSI devices)  
to be sent to ATA devices. ATAPI is used specifically for CD-ROM drives, 
 which, when they first started appearing for computers, were almost  
universally SCSI. Because SCSI controllers were expensive and clunky,  
the SCSI command set was eventually adopted for IDE, and typical CD-ROM  
drives today use ATAPI. ATAPI basically uses "packets" (similar to the  
packet concept of computer networking as it applies to TCP/IP, for example)  
to send and receive data and commands. Properly speaking, ATAPI is part  
of the EIDE (Enhanced IDE) standard. 
 
A packet sent to an ATAPI device which contains a command is called a  
command packet. These command packets are written to the data register  
via the ATA interface, and that's how ATAPI devices receive their commands.  
The command packet has a 12-byte standard format, and the first byte of  
the command packet contains the actual operation code. (The remaining 11  
bytes supply parameter info for the command.) Note that although the  
command packet is 12 bytes long, the packet is sent to the ATAPI device  
through word writes, not byte writes. A "word" in PC assembly language  
is 2 bytes, so you'll actually send the 12-byte command packet in only  
6 write operations. 
 
The "operation code" value you place in the ATAPI command packet is  
actually a SCSI command code. You do not use ATA commands with ATAPI  
devices; ATAPI devices use SCSI commands. For example, the SCSI  
command to eject a CD-ROM drive tray is the "START/STOP UNIT"  
command, which is SCSI command 1Bh. Similarly, to get an ATAPI  
CD-ROM drive to eject, you'd send it a command packet with 1Bh  
for an operation code. 
 
ATAPI contains several commands, but the most fundamental of these  
is the PACKET command, which has an ATAPI opcode of A0h. The first  
step in sending a command to an ATAPI device is to send it the  
PACKET command over the regular ATA command register, just as the  
program above sends the IDENTIFY DEVICE command. Once this PACKET  
command is sent, the ATAPI interface goes into a condition called  
HPD0: Check_Status_A State, which means that the ATA controller is  
to wait for 400 nanoseconds, then poll the status register until  
the BSY bit is zero. If the BSY bit is one, the host is supposed  
to keep polling the status register until the BSY bit clears. 
 
Once the BSY bit does clear (and DRQ is set to one), the ATAPI  
interface changes to HPD1: Send_Packet State. In this state, the  
host is supposed to send the command packet to the ATA controller's  
data register, one byte at a time. When all the bytes of the command  
packet have been sent, the host is to either transition to HPD2:  
Check_Status_B State (if nIEN is set to one), or to HPD3: INTRQ_ 
Wait State (if nIEN is set to zero). Note that nIEN is the  
second-rightmost bit of the ATA Device Control Register.  
You will note that this register is write-only, so you should  
write to this register to set nIEN before you begin sending the  
ATAPI packet. 
 
If the host does transition to HPD3: INTRQ_Wait State, all it's  
supposed to do is wait for INTRQ to be asserted. When INTRQ is  
asserted, then the host shall transition to HPD2: Check_Status_B State. 
 
The HPD2: Check_Status_B State is where things get a little hairy.  
This is where you check the status register, but there are a lot of  
condition bits you're supposed to check. First of all, the ATAPI spec  
specifies that "When entering this state from the HP1 ... state,  
the host shall wait one PIO transfer cycle time before reading the  
Status register. The wait may be accomplished by reading the  
Alternate Status register and ignoring the result." 
 
Once that's done, start checking the status register.  
First of all, if BUSY is set to 1, the host is not to  
leave the HPD2 state. The host is supposed to remain in HPD2  
until BUSY clears to zero. 
 
Once BUSY is zero, check DRQ. If DRQ is set to one, then the host  
shall transition to yet another state, called the HPD4: Transfer_Data State.  
However, the only time you'd need to enter HPD4 is if DRQ is 1 during the  
HPD2 State. If DRQ is zero now, you can skip HPD4 altogether. 
 
If both BUSY and DRQ are zero, then, the command is probably complete.  
Technically, there are a few other things you're supposed to check, but  
we won't worry about those now. 
 
The following is an ugly program in assembler to eject a CD-ROM drive.  
I haven't cleaned up the code as nicely as I should, but it does work  
as long as you run it in real mode (what Win9x users would call "DOS mode"). 
 
MOV DX, 177h ;status register 
LOOP1: 
IN AL, DX ;sets AL to status register (which is 8 bits) 
 
;If the first bit of the status register (BUSY) isn't 0, the device is busy, 
;so keep looping until it isn't. 
 
AND AL, 10000000xB 
JNE LOOP1 
 
;---------------------------------------------------------------------------- 
 
;Clear interrupts so something doesn't interrupt the drive or controller 
;while this program is working. 
CLI 
 
;---------------------------------------------------------------------------- 
 
MOV DX, 177h ;status register again 
LOOP2: 
IN AL, DX ;sets AL to status register again 
 
;If the second bit of the status register (DRDY) isn't 1, the device isn';ready, so keep looping until it is. 
 
AND AL, 01000000xB 
JE LOOP2 
 
;---------------------------------------------------------------------------- 
 
MOV DX, 176h ;device/head register 
MOV AL, 0 ;0 selects device 0 (master). 10h would select device 1 (slave). 
OUT DX, AL ;selects master device 
 
;IMPORTANT: Set nIEN before you send the PACKET command! 
;Let's set nIEN to 1 so we can skip the INTRQ_Wait state. 
 
MOV DX, 3F6h ;Device Control register 
MOV AL, 00001010xB ;nIEN is the second bit from the right here 
OUT DX, AL ;nIEN is now one! 
 
MOV DX, 177h ;command register 
MOV AL, 0A0h ;PACKET command 
OUT DX, AL ;sends the command! 
 
;After sending the PACKET command, the host is to wait 400 nanoseconds before 
;doing anything else. 
MOV CX,0FFFFh 
WAITLOOP: 
LOOPNZ WAITLOOP 
 
;---------------------------------------------------------------------------- 
 
MOV DX, 177h ;status register again 
LOOP3: 
IN AL, DX ;sets AL to status register again 
 
;Poll until BUSY bit is clear. 
 
AND AL, 10000000xB 
JNE LOOP3 
 
;Also, poll until DRQ is one. 
MOV DX, 177h ;status register again 
LOOP4: 
IN AL, DX 
AND AL, 00001000xB 
JE LOOP4 
 
;---------------------------------------------------------------------------- 
;NOW WE START SENDING THE COMMAND PACKET!!! 
 
MOV CX, 6 ;do this 6 times because it's 6 word writes (a word is 2 bytes) 
MOV DS, SEG buff 
MOV SI, OFFSET buff 
;DS:SI now points to the buffer which contains our ATAPI command packet 
CLD ;clear direction flag so SI gets incremented, not decremented 
 
COMPACKLOOP: ;command packet sending loop 
MOV DX, 170h ;data register 
 
;Because we're going to need to write a word (2 bytes), we can't just use an 
;8-bit register like AL. For this operation, we'll need to use the full width 
;of the 16-bit accumulator AX. We'll use the LODSW opcode, which loads AX 
;with whatever DS:SI points to. Not only this, but if the direction flag is 
;cleared (which we did a few lines above with the CLD instruction), LODSW 
;also auto-increments SI. 
LODSW 
OUT DX, AX ;send the current word of the command packet!!! 
 
MOV DX, 3F6h ;Alternate Status Register 
IN AL, DX ;wait one I/O cycle 
 
LOOPNZ COMPACKLOOP 
 
;---------------------------------------------------------------------------- 
 
;Once again, let's read the Alternate Status Register and ignore the result, 
;since the spec says so. 
 
MOV DX, 3F6h 
IN AL, DX 
 
;Okay... That's done. 
;Time to poll the status register until BUSY is 0 again. 
 
MOV DX, 177h ;status register 
LOOP5: 
IN AL, DX 
 
AND AL, 10000000xB 
JNE LOOP5 
 
;BUSY is zero here. 
;We're also supposed to check DRQ, but hey, screw it. 
 
STI 
 
;---------------------------------------------------------------------------- 
 
mov ah,004C  ;terminate program 
int 21h 
 
buff db 1Bh, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0
 
 
  
 
  
 
  
 
  
 
  

阅读(2183) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~