;;; -*- Mode:LISP; Package:TAPE; Readtable:CL; Base:10 -*- ;;; ;;; Nupi tape device for explorer. ;;; ;;; - wbg of Chaparral Dallas, 6/86 ;;; - dg 11/85 ;;; (DefFlavor NuPI-Device ((unit 6) (density :ignore) (command-block (si:get-nupi-command-block nil))) (Basic-Tape-Device) :gettable-instance-variables) (DefMethod (nupi-device :print-self) (stream ignore slashify) (If slashify (si:printing-random-object (self stream) (format stream "NuPI Unit: ~d" unit)) (format stream "NuPI Unit ~d" unit)) ) (DefMethod (nupi-device :set-options) (&rest ignore)) (DefMethod (nupi-device :deinitialize) () ) (DefMethod (nupi-device :lock-device) (&rest ignore)) (DefMethod (nupi-device :unlock-device) (&rest ignore)) (DefMethod (nupi-device :device-locked-p) (&rest ignore) t) (DefMethod (nupi-device :reset) (&rest ignore) (send self :initialize)) (defmethod (nupi-device :status) (&rest ignore)) (defmethod (nupi-device :optimal-chunk-size) (record-size) record-size ) (defmethod (nupi-device :rewind) (&optional (wait-p t)) (si:start-nupi-simple-command command-block (si:nupi-logical-to-physical-unit unit) #x20 0 0 nil nil nil) (When wait-p (si:wait-for-nupi-command command-block "Tape Rewind") (si:finish-nupi-command command-block nil)) ) (defmethod (nupi-device :unload) () (si:simple-nupi-command command-block #x21 unit 0 0 nil nil nil "Tape Unload") ) (defmethod (nupi-device :speed-threshold) (record-size) (declare (ignore record-size)) -1) (defmethod (nupi-device :space) (number-of-records &optional (speed :low)) (check-type number-of-records (integer 1)) (check-type speed (member :high :low)) ;; Convert from LMFL (4096 byte) blocks to NuPI (1024 byte) blocks. (Let ((number-of-blocks (/ (* number-of-records 4096.) 1024.))) (si:simple-nupi-command command-block #x23 unit 0 4 number-of-blocks 0 nil "Tape Space")) ) (defmethod (nupi-device :space-reverse) (number-of-records &optional (speed :low)) (check-type number-of-records (integer 1)) (check-type speed (member :high :low)) (signal 'tape:driver-error :device-type 'nupi-device :error-code #x23 :error-message "Space Reverse operation not available.") ) (defmethod (nupi-device :search-filemark) (number-of-filemarks &optional (speed :low)) (declare (ignore speed)) (si:simple-nupi-command command-block #x27 unit 0 4 number-of-filemarks 0 nil "Tape Search FM") ) (DefMethod (nupi-device :Search-Filemark-Reverse) (number-of-filemarks &optional (speed :low)) (declare (ignore number-of-filemarks speed)) ;; +++ this command cannot be supported by this device +++ (signal 'tape:driver-error :device-type 'nupi-device :error-code #x27 :error-message "Search Reverse Filemark operation not available.") ) (defmethod (nupi-device :read-block) (dma-buffer record-size) (setq record-size 4096) (si:nupi-read-from-tape command-block unit 0 record-size dma-buffer 0)) (defmethod (nupi-device :write-block) (dma-buffer record-size) (si:nupi-write-to-tape command-block unit 0 record-size dma-buffer 0)) (defmethod (nupi-device :read-array) (array number-of-records record-size) (check-arg array (memq (array-type array) `(art-8b art-string art-16b art-32b)) "an art-8, art-string, art-16b or art-32b array") (let* ((nbytes (* record-size number-of-records)) (npages (ceiling nbytes (* si:page-size 4)))) (Condition-Case (error) (using-resource (buffer si:dma-buffer npages) (with-buffer-wired (buffer npages) (si:nupi-read-from-tape command-block unit 0 nbytes buffer 0) (case (array-type array) ((art-8b art-string) (si:copy-array-portion (si:dma-buffer-string buffer) 0 nbytes array 0 nbytes)) (art-16b (check-arg record-size (zerop (remainder nbytes 2)) "a half-word even record-size for an art-16b array") (si:copy-array-portion (si:dma-buffer-16b buffer) 0 (/ nbytes 2) array 0 (/ nbytes 2))) (art-32b (if (eq (named-structure-p array) 'si:dma-buffer) (copy-array-portion (si:dma-buffer-string buffer) 0 nbytes (si:dma-buffer-string array) 0 nbytes) (ferror nil "Error... cannot copy art-32b array.")))))) (tape:physical-end-of-tape ;; Handle this error and generate a new with the right data transfer value. (signal 'tape:physical-end-of-tape :device-type 'nupi-device :unit unit :data-transferred (truncate (Send error :data-transferred) record-size))))) ) (defmethod (nupi-device :write-array) (array number-of-records record-size) (check-arg array (memq (array-type array) `(art-8b art-string art-16b art-32b)) "an art-8, art-string, art-16b or art-32b array") (let* ((nbytes (* record-size number-of-records)) (npages (ceiling nbytes (* si:page-size 4)))) (Condition-Case (error) (using-resource (buffer si:dma-buffer npages) (case (array-type array) ((art-8b art-string) (si:copy-array-portion array 0 nbytes (si:dma-buffer-string buffer) 0 nbytes)) (art-16b (check-arg record-size (zerop (remainder nbytes 2)) "a half-word even record-size for an art-16b array") (si:copy-array-portion array 0 (/ nbytes 2) (si:dma-buffer-16b buffer) 0 (/ nbytes 2))) (art-32b (if (eq (named-structure-p array) 'si:dma-buffer) (copy-array-portion (si:dma-buffer-string array) 0 nbytes (si:dma-buffer-string buffer) 0 nbytes) (ferror nil "Error... cannot copy art-32b array.")))) (with-buffer-wired (buffer npages) (si:nupi-write-to-tape command-block unit 0 nbytes buffer 0))) (tape:physical-end-of-tape ;; Handle this error and generate a new with the right data transfer value. (signal 'tape:physical-end-of-tape :device-type 'nupi-device :unit unit :data-transferred (truncate (Send error :data-transferred) record-size))))) ) (Defun Report-Progress (old-progress-count block-count granularity) (Let ((progress-count (truncate block-count granularity))) (When (> progress-count old-progress-count) (Format t " ~d" progress-count)) progress-count) ) (DefParameter *block-transfer-size* 20.) (DefParameter *streaming-priority* 20.) (Defun streamer-tape-block-request-complete (tape-command-block blocks-transferred command-name) (Condition-Case (error) (si:streamer-tape-request-complete tape-command-block command-name) (tape:physical-end-of-tape ;; Handle this error and generate a new with the right data transfer value. (signal 'tape:physical-end-of-tape :device-type 'nupi-device :unit (ldb (byte 8 0) (si:nupi-command-word tape-command-block)) :data-transferred (+ blocks-transferred (truncate (Send error :data-transferred) 1024.))))) ) (defmethod (nupi-device :write-from-disk) (disk-unit starting-block number-of-blocks record-size &key silent) (check-arg record-size (zerop (remainder record-size (* si:page-size 4))) "a page-even record-size") (let* ((chunk-size (min *block-transfer-size* number-of-blocks)) (old-priority (Send si:current-process :priority)) number-of-chunks last-chunk-size) (multiple-value-bind (a b) (floor number-of-blocks chunk-size) (setq number-of-chunks (if (zerop b) a (add1 a)) last-chunk-size (if (zerop b) chunk-size b))) (Unless silent (Format t "~&Writing ~d blocks to tape (starting at ~d.) Counting hundreds:" number-of-blocks starting-block)) (Unwind-Protect (using-resource (buffer-1 si:dma-buffer chunk-size) (using-resource (buffer-2 si:dma-buffer chunk-size) (using-resource (disk-command-block si:dma-buffer 1) (setf (si:dma-buffer-named-structure-symbol disk-command-block) 'si:nupi-command-block) (using-resource (tape-command-block si:dma-buffer 1) (setf (si:dma-buffer-named-structure-symbol tape-command-block) 'si:nupi-command-block) (Let ((transfer-size (* chunk-size si:page-size 4)) (address starting-block)) (Send si:current-process :Set-Priority *streaming-priority*) (si:nupi-read-from-disk disk-command-block disk-unit address transfer-size buffer-1 0) (si:nupi-write-to-tape-proceed tape-command-block unit 0 transfer-size buffer-1 0) (do ((count 1 (add1 count)) (progress-count 0) (last-transfer-size chunk-size) (buffer-1? t (not buffer-1?))) ((> count number-of-chunks) ;; Wait for last tape command to complete. (streamer-tape-block-request-complete tape-command-block (- address starting-block last-transfer-size) "Tape Write")) (incf address chunk-size) (When (= count number-of-chunks) (Setq chunk-size last-chunk-size transfer-size (* chunk-size si:page-size 4))) ;; Read into buffer not used by tape request. (si:nupi-read-from-disk disk-command-block disk-unit address transfer-size (If buffer-1? buffer-2 buffer-1) 0) ;; Wait for tape request to complete. (streamer-tape-block-request-complete tape-command-block (- address starting-block last-transfer-size) "Tape Write") ;; Start next tape request. (si:nupi-write-to-tape-proceed tape-command-block unit 0 transfer-size (If buffer-1? buffer-2 buffer-1) 0) (setq last-transfer-size chunk-size) (Unless silent (Setq progress-count (Report-Progress progress-count (- address starting-block) 100.))))))))) (Send si:current-process :Set-Priority old-priority))) ) (Defun Compare-Buffers (buffer-a buffer-b size) (let ((alphabetic-case-affects-string-comparison t)) (Unless (%string-equal buffer-a 0 buffer-b 0 size) (string-compare buffer-a buffer-b))) ) (Defun Buffer-Compare (buffer-a buffer-b size block-number address error-list) (Let ((compare (compare-buffers (si:dma-buffer-string buffer-a) (si:dma-buffer-string buffer-b) size))) (If (Null compare) error-list (format *error-output* "~& Error in record number ~d; Byte: ~d" block-number (abs compare)) (cons (cons block-number address) error-list))) ) (defmethod (nupi-device :compare-to-disk) (disk-unit starting-block number-of-blocks record-size &key silent) (check-unit disk-unit) (check-type starting-block (integer 0)) (check-type number-of-blocks (integer 0)) (check-type record-size (integer 1)) (let* ((chunk-size (min *block-transfer-size* number-of-blocks)) (old-priority (Send si:current-process :priority)) number-of-chunks last-chunk-size error-list) (multiple-value-bind (a b) (floor number-of-blocks chunk-size) (setq number-of-chunks (if (zerop b) a (add1 a)) last-chunk-size (if (zerop b) chunk-size b))) (Unless silent (Format t "~&Comparing ~d blocks to disk. Counting hundreds:" number-of-blocks)) (unwind-protect (using-resource (tape-buffer-1 si:dma-buffer chunk-size) (using-resource (tape-buffer-2 si:dma-buffer chunk-size) (using-resource (disk-buffer si:dma-buffer chunk-size) (using-resource (disk-command-block si:dma-buffer 1) (setf (si:dma-buffer-named-structure-symbol disk-command-block) 'si:nupi-command-block) (using-resource (tape-command-block si:dma-buffer 1) (setf (si:dma-buffer-named-structure-symbol tape-command-block) 'si:nupi-command-block) (Let ((transfer-size (* chunk-size si:page-size 4)) (last-transfer-size 0) (address starting-block)) (Send si:current-process :Set-Priority *streaming-priority*) (si:nupi-read-from-tape-proceed tape-command-block unit 0 transfer-size tape-buffer-1 0) (do ((count 1 (add1 count)) (progress-count 0) (buffer-1? t (not buffer-1?))) ((>= count number-of-chunks) (si:nupi-read-from-disk disk-command-block disk-unit address transfer-size disk-buffer 0) (streamer-tape-block-request-complete tape-command-block (- address starting-block) "Tape Read") (Setq error-list (buffer-compare disk-buffer (if buffer-1? tape-buffer-1 tape-buffer-2) transfer-size (* count chunk-size) address error-list))) (si:nupi-read-from-disk disk-command-block disk-unit address transfer-size disk-buffer 0) (Setq last-transfer-size transfer-size) ;; Wait for tape request to complete. (streamer-tape-block-request-complete tape-command-block (- address starting-block) "Tape Read") ;; Start next tape read request (into other buffer.) (When (= count number-of-chunks) (Setq chunk-size last-chunk-size transfer-size (* chunk-size si:page-size 4))) (si:nupi-read-from-tape-proceed tape-command-block unit 0 transfer-size (If buffer-1? tape-buffer-2 tape-buffer-1) 0) (Setq error-list (buffer-compare disk-buffer (if buffer-1? tape-buffer-1 tape-buffer-2) last-transfer-size (* count chunk-size) address error-list)) (Unless silent (Setq progress-count (Report-Progress progress-count (- address starting-block) 100.))) (incf address chunk-size)))))))) (Send si:current-process :Set-Priority old-priority)) (Unless (Null error-list) (nreverse error-list) t)) ) (defmethod (nupi-device :read-to-disk) (disk-unit starting-block number-of-blocks record-size &key silent) (check-arg record-size (zerop (remainder record-size (* si:page-size 4))) "a page-even record-size") (let* ((chunk-size (min *block-transfer-size* number-of-blocks)) (old-priority (Send si:current-process :priority)) number-of-chunks last-chunk-size) (multiple-value-bind (a b) (floor number-of-blocks chunk-size) (setq number-of-chunks (if (zerop b) a (add1 a)) last-chunk-size (if (zerop b) chunk-size b))) (Unless silent (Format t "~&Reading ~d blocks from tape. Counting hundreds:" number-of-blocks)) (Unwind-Protect (using-resource (buffer-1 si:dma-buffer chunk-size) (using-resource (buffer-2 si:dma-buffer chunk-size) (using-resource (disk-command-block si:dma-buffer 1) (setf (si:dma-buffer-named-structure-symbol disk-command-block) 'si:nupi-command-block) (using-resource (tape-command-block si:dma-buffer 1) (setf (si:dma-buffer-named-structure-symbol tape-command-block) 'si:nupi-command-block) (Let ((transfer-size (* chunk-size si:page-size 4)) (address starting-block)) (Send si:current-process :Set-Priority *streaming-priority*) (si:nupi-read-from-tape-proceed tape-command-block unit 0 transfer-size buffer-1 0) (do ((count 1 (add1 count)) (progress-count 0) (buffer-1? t (not buffer-1?))) ((> count number-of-chunks) ;; Wait for last tape request to complete. (streamer-tape-block-request-complete tape-command-block (- address starting-block) "Tape Read") (si:nupi-write-to-disk disk-command-block disk-unit address transfer-size (If buffer-1? buffer-1 buffer-2) 0)) ;; Wait for tape request to complete. (streamer-tape-block-request-complete tape-command-block (- address starting-block) "Tape Read") (When (= count number-of-chunks) (Setq chunk-size last-chunk-size transfer-size (* chunk-size si:page-size 4))) ;; Start read into other buffer. (si:nupi-read-from-tape-proceed tape-command-block unit 0 transfer-size (If buffer-1? buffer-2 buffer-1) 0) ;; Write completed buffer to disk. (si:nupi-write-to-disk disk-command-block disk-unit address transfer-size (If buffer-1? buffer-1 buffer-2) 0) (incf address chunk-size) (Unless silent (Setq progress-count (Report-Progress progress-count (- address starting-block) 100.))))))))) (Send si:current-process :Set-Priority old-priority))) ) (defmethod (nupi-device :write-filemark) (&optional (number-of-marks 1)) (dotimes (j number-of-marks) (si:simple-nupi-command command-block #x25 unit 0 0 nil nil nil "Tape Write FM"))) (defmethod (nupi-device :erase-tape) () (si:simple-nupi-command command-block #x22 unit 0 0 nil nil nil "Tape Erase") ) (defmethod (nupi-device :initialize) (&rest ignore) ;; BUFFERED - set mode to stream (tells formatter to use buffered vs. unbuffered mode). ;; SPEED - 1 = low, 0 & 2 = high, 3 = auto-adjust ;; DENSITY - 0 formatter defaults (Explorer default), 4 QIC-11 (Symbolics & NuMachine) ;; 6 1/2 inch 3200 bpi, 1-3 variations of 1/2 inch ;; BLOCK-SIZE - Size in bytes of blocks on tape, (24 bit field). ;; This command is send to the formatter (controller) rather than to the tape unit itself. (let ((buffered T) (speed 0) (density-code 0) (block-size #x200) (long-erase T) (unload-retension nil) (load-retension nil) (parms (* 2 (1+ si:%nupi-reserved-b))) (buffer (si:dma-buffer-16b command-block))) ;; This next word in the parameter block is RESERVED in SCSI spec but NUPI uses it!! (aset 12. buffer parms) ; scsi data length. (let ((half-word (dpb (if long-erase 0 1) #o1701 ; Info for NUPI only. Not part of (dpb (if unload-retension 1 0) #o1601 ; this SCSI command!!! (dpb (if load-retension 1 0) #o1501 0))))) (aset half-word buffer (+ parms 1))) ;; From here down we're stuffing params strictly according to SCSI ;; halfword - SCSI bytes in parameter list ;; 3 2,3 ;; halfword - SCSI bytes in descriptor block ;; 4 0,1 density-code, number-of-blocks MSB ;; 5 2,3 number-of-blocks (middle, LSB) ;; 6 4,5 reserved, Block Size MSB ;; 7 6,7 Block Size (middle, LSB) (aset #x0000 buffer (+ parms 2)) (let ((half (dpb (if buffered 1 0) #o0401 #x0800))) ; 8 is constant=bytes left (block descriptor) (aset (dpb speed #o0002 half) buffer (+ parms 3))) (aset (dpb density-code #o0004 0) buffer (+ parms 4)) (aset (dpb (ldb #o2010 block-size) #o1010 0) buffer (+ parms 6)) ; MSB (aset (dpb (ldb #o0010 block-size) #o1010 ; middle & LSB (dpb (ldb #o1010 block-size) #o0010 0)) buffer (+ parms 7)) ;; Send formatter setup command. +++ dma-buffer parameter is just a place holder +++ (si:simple-nupi-command command-block #x41 unit 0 16. command-block 0 nil "Tape Initialize")) ) ;;; +++ should do better than this +++ (defun nupi-device-present? () (select-processor (:explorer t) (:cadr) (:lambda))) (define-tape-device nupi-device "nt" nupi-device-present?)