	NOLIST
;	This file is part of picide, ATA(PI) interface to PIC18 family MCUs.
;	Copyright (C) 2004-5 Toby Thain, toby@telegraphics.com.au

;	This program is free software; you can redistribute it and/or modify
;	it under the terms of the GNU General Public License as published by  
;	the Free Software Foundation; either version 2 of the License, or
;	(at your option) any later version.

;	This program is distributed in the hope that it will be useful,
;	but WITHOUT ANY WARRANTY; without even the implied warranty of
;	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;	GNU General Public License for more details.

;	You should have received a copy of the GNU General Public License  
;	along with this program; if not, write to the Free Software
;	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	LIST

	TITLE picide
	SUBTITLE "ATAPI module"

;; Version history
;; 21-Oct-2004: 0.1 - started by Toby
;; 23-Feb-2005: 1.0 - public release under GPL

; contents of this module:
	global atapipkt			; data block for 12 byte ATAPI command packet
	global atapi_zeropkt		; initialise ATAPI command packet to zeroes

	; before calling the following routines,
	; set up parameters in atapipkt (LBA0/8/16/24 and LEN0/8)
	global atapi_read10		; ATAPI READ(10) command
	global atapi_readcd		; ATAPI READCD command
	global atapi_modesense		; ATAPI MODE SENSE command
	global atapi_reqsense		; ATAPI REQUEST SENSE command
	global atapi_readcapacity	; ATAPI READ CAPACITY command

	; check error in last command
	global atapi_checkerr		

#include "defs.inc"
#include "idecmd.inc"
#include "idedefs.inc"
#include "atapidefs.inc"

;#define ATAPI_DEBUG

	extern datasetup,ide_rdata,ide_wdata,wdcnt,ide_atapipacket
	extern longwaitbsy,seccnt
	extern rdreg,wrreg,ide_rblk,delay2ms,delay1500ms
	extern putdec16,dec16,putch,puthex,dumpsec

	udata_acs
wcnt	res 2
errsave	res 1
atapipkt res 12	; command packet

	code

	; handle read-data phase of ATAPI command
atapi_rdata:
	;call delay2ms	; FIXME
	call datasetup	; wait for data ready (!BSY & DRQ)
	_returniferr	; check timeout
	;call delay2ms	; FIXME
moredata:
	; device indicates how many bytes are to be transferred
	_rdreg ATAPI_REG_BYTECNTHI
	movwf wcnt+1
	_rdreg ATAPI_REG_BYTECNTLO
	movwf wcnt
#ifdef ATAPI_DEBUG
	_puts "bytecnt=\0"
	movff wcnt+1,dec16+1
	movff wcnt,dec16
	call putdec16
	_crlf
#endif
	bcf STATUS,C	; clear carry
	rrcf wcnt+1	; divide bytes by 2, leaving sector count in wcnt+1
	rrcf wcnt 	; and remaining # of words in wcnt
#ifdef ATAPI_DEBUG
	_puts " =\0"
	movf wcnt+1,w
	call puthex
	_puts "sec+\0"
	movf wcnt,w
	call puthex
	_puts "wds\r\n\0"
#endif
	; now do transfer, 256 words at a time
	lfsr 0,secbuf	; point indirection reg at sector buffer

	movf wcnt+1,w
	movwf seccnt
	bz part		; <1 sector
whole:	; do whole sectors
#ifdef ATAPI_DEBUG
	  _putchar 'W'
#endif
	  clrf wdcnt	; do 256 words
	  call ide_rblk	; PIO transfer
	  decfsz seccnt
	    bra whole
part:
#ifdef ATAPI_DEBUG
	_putchar 'P'
#endif
	movf wcnt,w	; check if there is a partial sector remaining
	bz done		; nothing left
	  movwf wdcnt	; loop counter used by ide_rdata
	  call ide_rblk
done:
	call longwaitbsy
	;call delay2ms	; FIXME why is this needed???
	_rdreg IDE_REG_STATUS
	movwf errsave
	andlw IDE_STATUS_DRQ
	bnz moredata
#ifdef ATAPI_DEBUG
	_puts " data done\r\n\0"
#endif
	movf errsave,w
	andlw ATAPI_STATUS_CHECK
	return

;---------------------------------------------------------
atapi_checkerr:
	; first see if STATUS indicates an error condition (CHECK)
	call longwaitbsy
	_rdreg IDE_REG_STATUS
#ifdef ATAPI_DEBUG
	movwf errsave
	_puts "ATAPI Status\0"
	movf errsave,w
	_putstrbits " BSY\000 DRDY\000 DF\000 DSC\000 DRQ\000 CORR\000 1\000 CHECK\000"
	_crlf
	movf errsave,w
#endif
	andlw ATAPI_STATUS_CHECK
	bnz err
	_puts "atapi_checkerr: No Error\r\n\0"
	retlw NO_ERR	; no error

err:	; error condition exists, examine Sense Key
	_rdreg IDE_REG_ERROR
	movwf errsave
#ifdef ATAPI_DEBUG
	_puts "      Error \0"
	movf errsave,w
	_putstrbits "\000\000\000\000 MCR\000 ABRT\000 EOM\000 ILI\000"
	_crlf
#endif
	swapf errsave,f
	movlw 0x0f
	andwf errsave,f
;#ifdef ATAPI_DEBUG
	_puts "  *** Sense  \0"
	movf errsave,w
	call puthex
	_putchar '='
	movf errsave,w
	_putstrlist "No Sense\000Recov Err\000Not Ready\000Medium Err\000Hw Err\000Illegal Req\000Unit Attn\000Data Protect\000Blank Check\0009\000A\000Aborted Cmd\000C\000D\000Miscompare\000F\000"
	_crlf
;#endif
	movlw ATAPI_SENSE_UNITATTN
	cpfseq errsave	; is it UNIT ATTENTION?
	  bra notattn
	; UNIT ATTENTION - issue Request Sense to clear it
#ifdef ATAPI_DEBUG
	_puts "== Unit Attn; Req Sense...\r\n\0"
#endif
	rcall atapi_reqsense
	_returniferr
	retlw ATAPI_RETRY		; tell caller to retry

notattn:
	movlw ATAPI_SENSE_NOTREADY
	cpfseq errsave	
	  retlw ABORT_ERR	; give up.

#ifdef ATAPI_DEBUG
	_puts "== Not Ready; retry\r\n\0"
#endif
	; device reports NOT READY
	call delay1500ms	; wait a bit...
	retlw ATAPI_RETRY	; and suggest caller retries.

; interpretation of status bits if dma/overlapped
;	dw " BSY\000 DRDY\000 DMAREADY\000 SERVICE\000 DRQ\000 CORR\000 1\000 CHECK\000"

;---------------------------------------------------------
atapi_reqsense:
	rcall atapi_zeropkt
	setf ATAPI_LBA8	; allocation length = 255
	movlw ATAPI_CMD_REQSENSE
	rcall atapi_cmd
	_returniferr
	BANKSEL secbuf
	_puts "Sense Data: \0"
	btfss secbuf+0,7,1
	  bra notvalid
	_puts "Info(LBA)=\0"
	movf secbuf+3,w,1
	call puthex
	movf secbuf+4,w,1
	call puthex
	movf secbuf+5,w,1
	call puthex
	movf secbuf+6,w,1
	call puthex
notvalid:
	_puts " Key=\0"
	movlw 0x0f
	andwf secbuf+2,w,1
	call puthex
	_puts " ASC=\0"
	movf secbuf+12,w,1
	call puthex
	_puts " ASCQ=\0"
	movf secbuf+13,w,1
	call puthex
	_crlf
#ifdef ATAPI_DEBUG
	movlw 2
	call dumpsec
#endif
	retlw NO_ERR

;---------------------------------------------------------
atapi_read10:
	movlw ATAPI_CMD_READ10
atapi_cmd:movwf ATAPI_OPCODE
	; caller must have set up LBA0/8/16/24 and LEN0/8
	call ide_atapipacket	; issue command
	_returniferr		; only timeout?
	rcall atapi_rdata	; handle data phase
	tstfsz WREG
	  rcall atapi_checkerr
	return

;---------------------------------------------------------
atapi_modesense:
	movlw ATAPI_CMD_MODESENSE
	bra atapi_cmd

;---------------------------------------------------------
atapi_readcd:
	movlw ATAPI_CMD_READCD
	bra atapi_cmd

;---------------------------------------------------------
retryreadcap:
	_puts "Retry Read Capacity:  \0"
atapi_readcapacity:
	rcall atapi_zeropkt
	movlw ATAPI_CMD_READCAPACITY
	rcall atapi_cmd
	addlw 0	; set flags on W
	bz readcok
	xorlw ATAPI_RETRY
	bz retryreadcap
	xorlw ATAPI_RETRY	; restore error code
	return	; not retry, we must give up
readcok:
	;movlw 1
	;call dumpsec
	_puts "Capacity: LBA=0x\0"
	lfsr 1,secbuf
	movf POSTINC1,w
	call puthex
	movf POSTINC1,w
	movwf dec16+1
	call puthex
	movf POSTINC1,w
	movwf dec16
	call puthex
	movf POSTINC1,w
	call puthex
	; compute megabytes **assuming 2K blocks**
	; bytes = cap << 11
	; megabytes = (cap << 11) >> 20
	;           = cap >> 9, so eliminate the lsbyte, and shift 1 bit right
	_putchar '='
	bcf STATUS,C
	rrcf dec16+1
	rrcf dec16
	call putdec16	; this uses FSR0!!
	_puts "MB  Blk len=\0"
	movf POSTINC1,w
	;call puthex
	movf POSTINC1,w
	;call puthex
	movf POSTINC1,w
	movwf dec16+1
	;call puthex
	movf POSTINC1,w
	movwf dec16
	;call puthex
	call putdec16
	_crlf
	retlw NO_ERR

;---------------------------------------------------------
atapi_zeropkt:	; zero 12 bytes of command packet
	lfsr 0,atapipkt
	movlw 12
loop:	clrf POSTINC0
	decfsz WREG
	  bra loop
	return

;---------------------------------------------------------
	end
