;;;
;;; han14xs.lisp
;;;
;;; experiments to figure out how to control a han14xs (buslink) mp3 player
;;;

(cl:defpackage :han14xs (:use :common-lisp #| :sb-alien |# :sb-usb))

(cl:in-package :han14xs)

;; Utility function
(defun hexdump (array index base &optional (n #x80) &aux (textbuf (make-string 18)))
  (unless (zerop (logand base 15))
    (format t "~(~8,'0X:~)" base))

  (setf (aref textbuf 0) #\|)
  
  (dotimes (i (logand base 15))
    (format t "   ")
    (setf (aref textbuf i) #\Space)
    (setf (aref textbuf (1+ i)) #\|))

  (setf (aref textbuf 17) #\|)
  
  (dotimes (j n)
    (when (zerop (logand 15 (+ base j)))
      (format t "~(~8,'0X:~)" (+ base j)))
    (format t " ~(~2,'0X~)" (aref array (+ index j)))
    (setf (aref textbuf (1+ (logand 15 (+ base j))))
	  (if (and (< (aref array (+ index j)) #x80)
		   (graphic-char-p (code-char (aref array (+ index j)))))
	      (code-char (aref array (+ index j)))
	    #\.))
    (when (= 15 (logand 15 (+ base j)))
      (format t " ~A~%" textbuf)
      (setf (aref textbuf 0) #\|)))
  
  (unless (zerop (logand 15 (+ base n)))
    (setf (aref textbuf (1+ (logand 15 (+ base n)))) #\|)
    (dotimes (i (- 16 (logand 15 (+ base n))))
      (format t "   ")
      (setf (aref textbuf (+ 2 i (logand 15 (+ base n)))) #\Space))
    (format t " ~A~%" textbuf)))


;;; DOS file date/time encoding.

(defun file-encode-date (time)
  "Convert the universal-time TIME to a DOS file date as an unsigned-byte 16."
  (multiple-value-bind (f1 f2 f3 day month year)
      (decode-universal-time time)
    (declare (ignore f1 f2 f3))
    (dpb (- year 1980)
	 (byte 7 9)
	 (dpb month
	      (byte 4 5)
	      day))))

(defun file-decode-date (date)
  "Convert the DOS file date (unsigned-byte 16) DATE to a string."
  (let ((year (+ 1980 (ldb (byte 7 9) date)))
	(month (ldb (byte 4 5) date))
	(day (ldb (byte 5 0) date)))
    (format nil "~4,'0D-~A-~2,'0D" year (elt '(nil "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec") month) day)))

(defun file-encode-time (time)
  "Convert the universal-time TIME to a DOS file time as an unsigned-byte 16."
  (multiple-value-bind (seconds minutes hours)
      (decode-universal-time time)
    (dpb hours
	 (byte 5 11)
	 (dpb minutes
	      (byte 6 5)
	      (ldb (byte 5 1)
		   seconds)))))

(defun file-decode-time (time)
  "Convert the (unsigned-byte 16) DOS file time TIME to a string."
  (let ((hours (ldb (byte 5 11) time))
	(minutes (ldb (byte 6 5) time))
	(seconds (ash (ldb (byte 5 0) time) 1)))
    (format nil "~2,'0D:~2,'0D:~2,'0D" hours minutes seconds)))


;; Actual device handling

(defun han14xs-start-command-frame (dev)
  (let ((result (make-array 1 :element-type '(unsigned-byte 8) :fill-pointer t)))
    (usb-control dev #xc0 #x0a #x0000 #x0000 result)
    (assert (= (aref result 0) #x40))))

(defun han14xs-start-upload-frame (dev)
  (let ((result (make-array 1 :element-type '(unsigned-byte 8) :fill-pointer t)))
    (usb-control dev #xc0 #x0b #x0000 #x0000 result)
    (assert (= (aref result 0) #x80))))

(defun han14xs-start-result-frame (dev)
  (let ((result (make-array 1 :element-type '(unsigned-byte 8) :fill-pointer t)))
    (usb-control dev #xc0 #x0c #x0000 #x0000 result)
    (assert (= (aref result 0) #x40))))

(defun han14xs-stop-frame (dev)
  (let ((result (make-array 1 :element-type '(unsigned-byte 8) :fill-pointer t)))
    (usb-control dev #xc0 #x0a #x0000 #xffff result)
    (assert (= (aref result 0) #x40))))

(defun han14xs-send-command (dev &rest bytes)
  (let ((command (make-array (logand #x-40 (+ #x3f 5 (length bytes)))
			     :element-type '(unsigned-byte 8)
			     :initial-element 0
			     :fill-pointer t)))
    (setf (aref command 0) #x23
	  (aref command 2) (+ 2 (length bytes))
	  (aref command 3) (char-code #\:)
	  (aref command (+ 4 (length bytes))) (char-code #\.))
    (loop for index from 4
	  for byte in bytes
	  do (setf (aref command index) byte))
    ;; FIXME: Should really send in #x40-byte chunks.
    (usb-bulk dev 2 command)))

(defun han14xs-recv-result (dev)
  (let ((result (make-array #x40
			    :element-type '(unsigned-byte 8)
			    :fill-pointer t)))
    (usb-bulk dev #x83 result)
    result))

(defmacro with-restart-on-timeout (&body body)
  `(handler-bind ((usb-timeout #'(lambda (c)
				   (declare (ignore c))
				   (invoke-restart 'retry))))
    ,@body))

(defun han14xs-read-bulk-data (dev length)
  (let* ((padded-length (logand #x-40 (+ #x3f length)))
	 (blocks (ash padded-length -6))
	 (result (make-array padded-length
			     :element-type '(unsigned-byte 8)
			     :fill-pointer t)))
  (han14xs-start-result-frame dev)
  (with-restart-on-timeout
    (loop for i from 0 below blocks
	  do
	  (setf (fill-pointer result) (ash (1+ i) 6))
	  (usb-bulk dev #x83 result (ash i 6))))
  (han14xs-stop-frame dev)
  (setf (fill-pointer result) length)
  result))

(defun han14xs-read-reply-data (dev)
  (let ((result (make-array #x40
			    :element-type '(unsigned-byte 8)
			    :fill-pointer t)))
    ;; Load the first result block.
    (with-restart-on-timeout
      (usb-bulk dev #x83 result))

    ;; Validate the first part of the wrapper.
    (assert (= #x23 (aref result 0)))
    (assert (zerop (aref result 1)))
    (assert (= #x3a (aref result 3)))

    ;; Find the reply length, number of additional blocks
    ;; required, and validate the length.
    (let* ((length (aref result 2))
	   (extra-blocks (floor (+ length 3) #x40)))
      (assert (>= length 2))
      
      ;; Load additional result blocks if required.
      (unless (zerop extra-blocks)
	;; Allocate more space for the result.
	(let ((new-result (make-array (* #x40 (1+ extra-blocks))
				      :element-type '(unsigned-byte 8)
				      :fill-pointer #x40)))
	  (replace new-result result)
	  (setf result new-result))
	;; Load the remaining data in #x40 byte blocks.
	(with-restart-on-timeout
	  (loop for i from 1 to extra-blocks
		do (incf (fill-pointer result) #x40)
		(usb-bulk dev #x83 result (* i #x40)))))

      ;; Validate the result end marker.
      (assert (= #x2e (aref result (+ 2 length))))

      ;; Pick out the actual result data.
      (subseq result 4 (+ length 2)))))

(defun han14xs-transact (dev &rest command)
  (han14xs-start-command-frame dev)
  (apply #'han14xs-send-command dev command)
  (han14xs-stop-frame dev)
  (han14xs-start-result-frame dev)
  (prog1
      (han14xs-read-reply-data dev)
    (han14xs-stop-frame dev)))

(defun han14xs-bulk-transact (dev result-length &rest command)
  (han14xs-start-command-frame dev)
  (apply #'han14xs-send-command dev command)
  (han14xs-stop-frame dev)
  (han14xs-start-result-frame dev)
  (prog1
      (han14xs-read-bulk-data dev result-length)
    (han14xs-stop-frame dev)))

(defun han14xs-read-directory (dev card)
  (let* ((card-byte (ecase card (:internal #x4d) (:external #x53)))
	 (result-1 (han14xs-transact dev card-byte #x47)))
    (assert (= 4 (length result-1)))

    (let* ((length (dpb (aref result-1 2)
			(byte 8 8)
			(aref result-1 3)))
	   (result-2 (han14xs-bulk-transact dev length #x46))
	   (card-size-blocks (dpb (aref result-2 0)
				  (byte 8 8)
				  (aref result-2 1)))
	   (card-remaining-blocks (dpb (aref result-2 4)
				       (byte 8 8)
				       (aref result-2 5))))

      (format t "~A card (~A/~A blocks free, ~A/~A kbytes free):~%" card
	      card-remaining-blocks card-size-blocks
	      (* #x40 card-remaining-blocks) (* #x40 card-size-blocks))

      ;; FIXME: Convert to more useful form?
      (hexdump result-2 8 0 (- length 8)))))

(defun han14xs-format-card (dev card)
  (let* ((card-byte (ecase card (:internal #x4d) (:external #x53)))
	 (result-1 (han14xs-transact dev card-byte #x46)))
    (assert (= 2 (length result-1)))
    (assert (= card-byte (aref result-1 0)))
    (assert (= #x46 (aref result-1 1)))

    (let ((result-2 (han14xs-transact dev #x46)))
      (assert (= 1 (length result-2)))
      (assert (zerop (aref result-2 0))))))

(defun han14xs-delete-file (dev short-name)
  "Delete a file from the player.  SHORT-NAME is a DOS 8.3 filename."
  ;; Internal memory only?
  (let* ((command-bytes (concatenate 'list '(#x4d #x45)
				     (map 'list #'char-code short-name)))
	 (result-1 (apply #'han14xs-transact dev command-bytes))
	 (result-2 (han14xs-transact dev #x46)))
    ;; FIXME: Probably should validate result-1 somehow.
    (declare (ignore result-1))

    (assert (= 1 (length result-2)))
    (assert (zerop (aref result-2 0)))))

(defun han14xs-upload-file (dev card filespec &key (shortname "TESTFI~1.MP3") progress-report)
  (with-open-file (infile filespec
			  :direction :input
			  :element-type '(unsigned-byte 8))
    (let* ((longname (file-namestring (pathname filespec)))
	   ;; FIXME: Real shortname?
	   (shortname shortname)
	   (file-time (file-write-date filespec))
	   (dos-file-time (file-encode-time file-time))
	   (dos-file-date (file-encode-date file-time))
	   (encoded-time (list (ldb (byte 8 0) dos-file-time)
			       (ldb (byte 8 8) dos-file-time)))
	   (encoded-date (list (ldb (byte 8 0) dos-file-date)
			       (ldb (byte 8 8) dos-file-date)))
	   (encoded-shortname (loop for char across shortname
				    collect (char-code char)))
	   (shortname-length (length shortname))
	   (encoded-shortname-length (list (ldb (byte 8 8) shortname-length)
					   (ldb (byte 8 0) shortname-length)))
	   (encoded-longname (sb-ext:string-to-octets longname
						      :external-format :ucs2le))
	   (longname-length (* 2 (length longname)))
	   (encoded-longname-length (list (ldb (byte 8 8) longname-length)
					  (ldb (byte 8 0) longname-length)))
	   (card-byte (ecase card (:internal #x4d) (:external #x53)))
	   (file-length (file-length infile))
	   (encoded-file-length (list (ldb (byte 8 24) file-length)
				      (ldb (byte 8 16) file-length)
				      (ldb (byte 8 8) file-length)
				      (ldb (byte 8 0) file-length)))
	   (command-bytes (concatenate 'list
				       (list card-byte)
				       '(#x90 #x17 #x02 #x20) ;; Magic?
				       encoded-date
				       encoded-time
				       encoded-file-length
				       '(#x00) ;; Padding?
				       encoded-shortname-length
				       encoded-shortname
				       encoded-longname-length
				       encoded-longname))
	   (result-1 (apply #'han14xs-transact dev command-bytes)))
      ;; FIXME: Do some validation on result-1.
      (declare (ignore result-1))

      (let* ((upload-data (make-array #x40
				     :element-type '(unsigned-byte 8)
				     :fill-pointer t))
	     (padded-length (logand #x-8000 (+ #x7fff file-length)))
	     (blocks (ash padded-length -6)))
	(han14xs-start-upload-frame dev)

	;; Upload the file data.
	(with-restart-on-timeout
	  (loop for i from 0 below blocks
		for index = (read-sequence upload-data infile)
		when (< index #x40)
		do (fill upload-data 0 :start index) end
	       ;; FIXME: Real progress report interface would be nice.
	        when (and progress-report (zerop (logand i #x1f)))
	        do (swank::background-message "~D/~D blocks transferred (~D%)"
					      (1+ i) blocks
					      (truncate (* 100
							   (/ blocks (1+ i))))) end
		do (usb-bulk dev 2 upload-data)))
	
	(han14xs-stop-frame dev)))))

(defun read-usb-devices ()
  "Returns the contents of /proc/bus/usb/devices as a string."
  (with-open-file (devlist "/proc/bus/usb/devices")
    ;; This "file" violates the usual contract of select(2).
    ;; Anally.  With a spiked dildo.  An input-ready condition on
    ;; the file indicates that a device has been connected or
    ;; disconnected, not that there happens to be more data to
    ;; read.  No select(2) means no serve-event.  No serve-event
    ;; means that we can't use the standard Lisp file I/O, and
    ;; have to do our own buffering...  And, while we're at it, we
    ;; won't bother doing our own error handling.
    (let* ((buffers
	    (loop for buf = (make-array #x200
					:element-type '(unsigned-byte 8)
					:fill-pointer t)
		  for (len error) = (multiple-value-list
				     (sb-unix:unix-read
				      (sb-sys:fd-stream-fd devlist)
				      (sb-sys:vector-sap
				       (sb-kernel:%array-data-vector buf))
				      #x200))
		  until (or (null len) (zerop len))
		  do (setf (fill-pointer buf) len)
		  collect buf))
	   (combined-buffer (apply #'concatenate
				   '(vector (unsigned-byte 8)) buffers))
	   (file-contents (sb-ext:octets-to-string combined-buffer)))
    
      file-contents)))

(defun open-usb-device (bus device)
  (let ((device-file (format nil "/proc/bus/usb/~3,'0D/~3,'0D" bus device)))
    (open device-file :direction :io :if-exists :overwrite)))


#|
(let ((directory *dir*))
  (loop with lfn
	for entry from 8 below (length directory) by #x20
	when (= #x0f (aref directory (+ entry #x0b)))
	do (let* ((length-bits (ldb (byte 5 0) (aref directory entry)))
		  (base (* 26 (1- length-bits))))
	     (when (logbitp 6 (aref directory entry))
	       (setf lfn (make-array (* length-bits 26)
				     :element-type '(unsigned-byte 8)
				     :fill-pointer t)))
	     (setf (subseq lfn base (+ base 10)) (subseq directory (+ entry 1))
		   (subseq lfn (+ base 10) (+ base 22)) (subseq directory (+ entry #x0e))
		   (subseq lfn (+ base 22) (+ base 26)) (subseq directory (+ entry #x1c))))
	else
	collect
	(let* ((8-length (or (position #x20 directory
				       :start entry :end (+ entry 8)) 8))
	       (3-length (or (position #x20 directory
				       :start (+ entry 8) :end (+ entry 11)) 3))
	       (8-part (subseq directory entry (+ entry 8-length)))
	       (3-part (subseq directory (+ entry 8) (+ entry 8 3-length)))
	       (filename (sb-ext:octets-to-string
			  (concatenate '(vector (unsigned-byte 8))
				       8-part '(#.(char-code #\.)) 3-part)))
	       (encoded-date (dpb (aref directory (+ entry #x19))
				  (byte 8 8)
				  (aref directory (+ entry #x18))))
	       (encoded-time (dpb (aref directory (+ entry #x16))
				  (byte 8 8)
				  (aref directory (+ entry #x17))))
	       (decoded-date (file-decode-date encoded-date))
	       (decoded-time (file-decode-time encoded-time))
	       (file-length (dpb (dpb (aref directory (+ entry #x1f))
				      (byte 8 8)
				      (aref directory (+ entry #x1e)))
				 (byte 16 16)
				 (dpb (aref directory (+ entry #x1d))
				      (byte 8 8)
				      (aref directory (+ entry #x1c))))))
	  ;; Crop LFN to length.
	  (loop for index from 0 below (length lfn) by 2
		when (and (zerop (aref lfn index))
			  (zerop (aref lfn (1+ index))))
		do (setf (fill-pointer lfn) index)
		(loop-finish))
	  (list filename (sb-ext:octets-to-string lfn :external-format :ucs2le)
		file-length decoded-date decoded-time))
	and do (setf lfn nil)))
|#


#|
(with-open-file (dev "/proc/bus/usb/001/022"
		     :direction :io
		     :if-exists :overwrite)
  ;(usb-disconnect dev) ;; "no data available"?
  (usb-claim-interface dev 0)
  ;; FIXME: Should probably check connectinfo here.
  #+(or)
  (let ((descriptor (make-array #x12
				:element-type '(unsigned-byte 8)
				:fill-pointer t)))
    (usb-control dev #x80 #x06 #x0001 #x0000 descriptor)
    descriptor)
  
  (format t "Sending identify request:~%")
  (han14xs-start-command-frame dev)
  (han14xs-send-command dev #x90 #x53) ;; What does this command mean?
  (han14xs-stop-frame dev)

  (han14xs-start-result-frame dev)
  (handler-bind ((usb-timeout #'(lambda (c) (declare (ignore c)) (invoke-restart 'retry))))
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40))
  (han14xs-stop-frame dev)

  #;|
  (format t "Sending mystery request:~%")
  (han14xs-start-command-frame dev)
  (han14xs-send-command dev #x4d #x47) ;; What does this command mean?
  (han14xs-stop-frame dev)

  (han14xs-start-result-frame dev)
  (handler-bind ((usb-timeout #'(lambda (c) (declare (ignore c)) (invoke-restart 'retry))))
    (hexdump (han14xs-recv-result dev) 0 0 #x40))
  (han14xs-stop-frame dev)

  (format t "Sending internal directory request:~%")
  (han14xs-start-command-frame dev)
  (han14xs-send-command dev #x46) ;; What does this command mean?
  (han14xs-stop-frame dev)

  (han14xs-start-result-frame dev)
  (handler-bind ((usb-timeout #'(lambda (c) (declare (ignore c)) (invoke-restart 'retry))))
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40))
  (han14xs-stop-frame dev)

  (format t "Sending mystery request:~%")
  (han14xs-start-command-frame dev)
  (han14xs-send-command dev #x53 #x47) ;; What does this command mean?
  (han14xs-stop-frame dev)

  (han14xs-start-result-frame dev)
  (handler-bind ((usb-timeout #'(lambda (c) (declare (ignore c)) (invoke-restart 'retry))))
    (hexdump (han14xs-recv-result dev) 0 0 #x40))
  (han14xs-stop-frame dev)

  (format t "Sending external directory request:~%")
  (han14xs-start-command-frame dev)
  (han14xs-send-command dev #x46) ;; What does this command mean?
  (han14xs-stop-frame dev)

  (han14xs-start-result-frame dev)
  (handler-bind ((usb-timeout #'(lambda (c) (declare (ignore c)) (invoke-restart 'retry))))
    (hexdump (han14xs-recv-result dev) 0 0 #x40)
    (hexdump (han14xs-recv-result dev) 0 0 #x40))
  (han14xs-stop-frame dev)
  |;#

  (format t "Sending internal directory request:~%")
  (let (result-1)
    (han14xs-start-command-frame dev)
    (han14xs-send-command dev #x4d #x47) ;; Prep for internal directory
    (han14xs-stop-frame dev)

    (han14xs-start-result-frame dev)
    (handler-bind ((usb-timeout #'(lambda (c)
				    (declare (ignore c))
				    (invoke-restart 'retry))))
      (setf result-1 (han14xs-recv-result dev)))
    (han14xs-stop-frame dev)

    (han14xs-start-command-frame dev)
    (han14xs-send-command dev #x46) ;; Signal ready for data
    (han14xs-stop-frame dev)

    (let* ((dir-length (dpb (aref result-1 6)
			   (byte 8 8)
			   (aref result-1 7)))
	   (directory-data (han14xs-read-bulk-data dev dir-length)))
      (hexdump directory-data 0 0 (length directory-data))))

  (format t "Sending external directory request:~%")
  (let (result-1)
    (han14xs-start-command-frame dev)
    (han14xs-send-command dev #x53 #x47) ;; Prep for external directory
    (han14xs-stop-frame dev)

    (han14xs-start-result-frame dev)
    (handler-bind ((usb-timeout #'(lambda (c)
				    (declare (ignore c))
				    (invoke-restart 'retry))))
      (setf result-1 (han14xs-recv-result dev)))
    (han14xs-stop-frame dev)

    (han14xs-start-command-frame dev)
    (han14xs-send-command dev #x46) ;; Signal ready for data
    (han14xs-stop-frame dev)

    (let* ((dir-length (dpb (aref result-1 6)
			   (byte 8 8)
			   (aref result-1 7)))
	   (directory-data (han14xs-read-bulk-data dev dir-length)))
      (hexdump directory-data 0 0 (length directory-data))))

  )
|#


;; Sending identify request:
;; 00000000: 23 00 8a 3a 90 53 00 00 00 09 00 30 32 33 34 35 |#..:.S.....02345|
;;                 ^^ reply length (including 3a and 2e bytes)
;; 00000010: 36 37 38 2e 41 42 43 00 00 00 00 00 00 00 00 00 |678.ABC.........|
;; 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 32 2e 32 |.............2.2|
;; 00000010: 2e 30 2e 30 2e 31 20 20 32 30 30 32 30 39 30 30 |.0.0.1  20020900|
;; 00000020: 48 61 6e 2d 31 34 78 73 6d 20 20 20 20 41 4d 41 |Han-14xsm    AMA|
;; 00000030: 43 2e 20 43 6f 2e 20 20 20 20 20 20 20 20 20 20 |C. Co.          |
;; 00000000: 20 00 00 00 00 00 00 00 00 00 00 00 2e 56 00 00 | ............V..|
;; 00000010: 8d e3 00 06 f6 90 00 00 39 92 00 5a 00 36 60 05 |........9..Z.6`.|
;; 00000020: 01 00 04 00 8f e3 00 1d 96 9f b2 00 39 92 00 00 |............9...|
;; 00000030: 8d e3 00 89 1d 01 8d e3 00 1d 89 d4 d7 00 39 92 |..............9.|
;; Select internal card:
;; 00000000: 23 00 06 3a 4d 47 03 08 2e 00 00 00 00 00 00 00 |#..:MG..........|
;;                 ^^ reply length (including 3a and 2e bytes)
;;                       ^^ ^^ command bytes (as originally sent)
;;                             ^^ ^^ directory data length
;; 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; Sending directory request:
;; 00000000: 07 d0 00 00 00 c1 80 00                         |........|

;; "isbw_special002_060327.mp3" ?
;; 00000000:                         42 30 00 32 00 5f 00 30         |B0.2._.0|
;; 00000010: 00 36 00 0f 00 9d 30 00 33 00 32 00 37 00 2e 00 |.6....0.3.2.7...|
;; 00000020: 6d 00 00 00 70 00 33 00 01 69 00 73 00 62 00 77 |m...p.3..i.s.b.w|
;; 00000030: 00 5f 00 0f 00 9d 73 00 70 00 65 00 63 00 69 00 |._....s.p.e.c.i.|
;; 00000000: 61 00 00 00 6c 00 30 00                         |a...l.0.|

;; "IS1C54~1MP3" Is this a FAT-style directory entry? Yes.
; Filename: "IS1C54~1"
; Ext: "MP3"
; Attrib: #x20 (Archive)
; Reserved: #x03 #xe7 #xd1 #x02 #x00 #x8e #xc9 #x8e #xe3 #x00
; Time: #xa77b
; Date: #x365a
; Cluster: 3
; Size: #x2d571e (2971422 bytes)
;; 00000000:                         49 53 31 43 35 34 7e 31         |IS1C54~1|
;; 00000010: 4d 50 33 20 03 e7 d1 02 00 8e c9 8e e3 00 7b a7 |MP3 ..........{.|
;; 00000020: 5a 36 03 00 1e 57 2d 00                         |Z6...W-.|

;; "isbw_special003_060328.mp3" ?
;; 00000020:                         42 30 00 33 00 5f 00 30         |B0.3._.0|
;; 00000030: 00 36 00 0f 00 e3 30 00 33 00 32 00 38 00 2e 00 |.6....0.3.2.8...|
;; 00000000: 6d 00 00 00 70 00 33 00 01 69 00 73 00 62 00 77 |m...p.3..i.s.b.w|
;; 00000010: 00 5f 00 0f 00 e3 73 00 70 00 65 00 63 00 69 00 |._....s.p.e.c.i.|
;; 00000020: 61 00 00 00 6c 00 30 00 49 53 35 44 45 33 7e 31 |a...l.0.IS5DE3~1|
;; 00000030: 4d 50 33 20 02 00 8e c9 8e 8e c9 8e e3 00 56 a8 |MP3 ..........V.|
;; 00000000: 5a 36 b9 00 6f d4 49 00                         |Z6..o.I.|

;; 00000000:                         42 30 00 34 00 5f 00 30         |B0.4._.0|
;; 00000010: 00 36 00 0f 00 21 30 00 33 00 32 00 39 00 2e 00 |.6...!0.3.2.9...|
;; 00000020: 6d 00 00 00 70 00 33 00 01 69 00 73 00 62 00 77 |m...p.3..i.s.b.w|
;; 00000030: 00 5f 00 0f 00 21 73 00 70 00 65 00 63 00 69 00 |._...!s.p.e.c.i.|
;; 00000000: 61 00 00 00 6c 00 30 00 49 53 37 32 41 41 7e 31 |a...l.0.IS72AA~1|
;; 00000010: 4d 50 33 20 c9 8e 8e c9 8e 8e c9 8e e3 00 73 a8 |MP3 ..........s.|
;; 00000020: 5a 36 e1 01 2c 41 4d 00 42 30 00 35 00 5f 00 30 |Z6..,AM.B0.5._.0|
;; 00000030: 00 36 00 0f 00 6c 30 00 33 00 33 00 30 00 2e 00 |.6...l0.3.3.0...|
;; 00000000: 6d 00 00 00 70 00 33 00 01 69 00 73 00 62 00 77 |m...p.3..i.s.b.w|
;; 00000010: 00 5f 00 0f 00 6c 73 00 70 00 65 00 63 00 69 00 |._...ls.p.e.c.i.|
;; 00000020: 61 00 00 00 6c 00 30 00 49 53 42 57 5f 53 7e 31 |a...l.0.ISBW_S~1|
;; 00000030: 4d 50 33 20 c9 8e 8e c9 8e 8e c9 8e e3 00 ba a6 |MP3 ............|
;; 00000000: 5a 36 17 03 6a e5 4b 00 42 30 00 36 00 5f 00 30 |Z6..j.K.B0.6._.0|
;; 00000010: 00 36 00 0f 00 6c 30 00 33 00 33 00 31 00 2e 00 |.6...l0.3.3.1...|
;; 00000020: 6d 00 00 00 70 00 33 00 01 69 00 73 00 62 00 77 |m...p.3..i.s.b.w|
;; 00000030: 00 5f 00 0f 00 6c 73 00 70 00 65 00 63 00 69 00 |._...ls.p.e.c.i.|
;; 00000000: 61 00 00 00 6c 00 30 00 49 53 42 57 5f 53 7e 31 |a...l.0.ISBW_S~1|
;; 00000010: 4d 50 33 20 c9 8e 8e c9 8e 8e c9 8e e3 00 f7 a6 |MP3 ............|
;; 00000020: 5a 36 47 04 d7 32 9e 00 42 30 00 36 00 30 00 34 |Z6G..2..B0.6.0.4|
;; 00000030: 00 30 00 0f 00 c8 35 00 2e 00 6d 00 70 00 33 00 |.0....5...m.p.3.|
;; 00000000: 00 00 00 00 ff ff ff ff 01 69 00 73 00 62 00 77 |.........i.s.b.w|
;; 00000010: 00 5f 00 0f 00 c8 73 00 68 00 6f 00 77 00 30 00 |._....s.h.o.w.0.|
;; 00000020: 33 00 00 00 30 00 5f 00 49 53 34 41 38 41 7e 31 |3...0._.IS4A8A~1|
;; 00000030: 4d 50 33 20 c9 8e 8e c9 8e 8e c9 8e e3 00 47 a8 |MP3 ..........G.|
;; 00000000: 5a 36 c0 06 0b 4b 60 01 42 30 00 36 00 30 00 34 |Z6...K`.B0.6.0.4|
;; 00000010: 00 31 00 0f 00 6c 30 00 2e 00 6d 00 70 00 33 00 |.1...l0...m.p.3.|
;; 00000020: 00 00 00 00 ff ff ff ff 01 69 00 73 00 62 00 77 |.........i.s.b.w|
;; 00000030: 00 5f 00 0f 00 6c 73 00 68 00 6f 00 77 00 30 00 |._...ls.h.o.w.0.|
;; 00000000: 33 00 00 00 31 00 5f 00 49 53 42 57 5f 53 7e 31 |3...1._.ISBW_S~1|
;; 00000010: 4d 50 33 20 c9 8e 8e c9 8e 8e c9 8e e3 00 dc a6 |MP3 ............|
;; 00000020: 5a 36 42 0c b5 aa 3b 01 42 30 00 36 00 30 00 34 |Z6B...;.B0.6.0.4|
;; 00000030: 00 31 00 0f 00 f8 36 00 2e 00 6d 00 70 00 33 00 |.1....6...m.p.3.|
;; 00000000: 00 00 00 00 ff ff ff ff 01 69 00 73 00 62 00 77 |.........i.s.b.w|
;; 00000010: 00 5f 00 0f 00 f8 73 00 68 00 6f 00 77 00 30 00 |._....s.h.o.w.0.|
;; 00000020: 33 00 00 00 32 00 5f 00 49 53 30 39 45 33 7e 31 |3...2._.IS09E3~1|
;; 00000030: 4d 50 33 20 c9 8e 8e c9 8e 8e c9 8e e3 00 7a a7 |MP3 ..........z.|
;; 00000000: 5a 36 31 11 13 db bf 02 2e 40 0a 24 02 63 06 0d |Z61......@.$.c..|
;; 00000010: e0 00 50 40 c6 00 00 07 30 08 00 0a 00 14 59 04 |..P@....0.....Y.|
;; 00000020: 40 88 10 00 a6 20 c0 31 0b 54 82 48 04 18 81 21 |@.... .1.T.H...!|
;; 00000030: 03 ac 00 80 11 0c 0b 10 82 21 00 88 41 22 01 00 |.........!..A"..|
;; Select external card:
;; 00000000: 23 00 06 3a 53 47 00 68 2e 00 00 00 00 00 00 00 |#..:SG.h........|
;; 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
;; Sending directory request:
;; 00000000: 07 d0 00 00 06 d8 40 00 42 30 00 36 00 30 00 34 |......@.B0.6.0.4|
;; 00000010: 00 32 00 0f 00 6c 33 00 2e 00 6d 00 70 00 33 00 |.2...l3...m.p.3.|
;; 00000020: 00 00 00 00 ff ff ff ff 01 69 00 73 00 62 00 77 |.........i.s.b.w|
;; 00000030: 00 5f 00 0f 00 6c 73 00 68 00 6f 00 77 00 30 00 |._...ls.h.o.w.0.|
;; 00000000: 33 00 00 00 33 00 5f 00 49 53 42 57 5f 53 7e 31 |3...3._.ISBW_S~1|
;; 00000010: 4d 50 33 20 03 e7 d1 02 00 8e c9 8e e3 00 f1 a6 |MP3 ............|
;; 00000020: 5a 36 02 00 74 de f4 00 2e 30 00 33 00 5f 00 30 |Z6..t....0.3._.0|
;; 00000030: 00 36 00 0f 00 e3 30 00 33 00 32 00 38 00 2e 00 |.6....0.3.2.8...|


#|

(defparameter *dev* (open "/proc/bus/usb/002/001" :direction :io :if-exists :overwrite))

(usb-claim-interface *dev* 0)

(han14xs-read-directory *dev* :internal)

|#

;;; EOF
