summaryrefslogtreecommitdiffstats
path: root/qemu/roms/SLOF/slof/fs/scsi-support.fs
blob: 3e65c8781e464ffb5c5a0db55fed6f4a3a782f31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
\ *****************************************************************************
\ * 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
\ ****************************************************************************/

\ ************************************************
\ create a new scsi word-list named 'scsi-words'
\ ************************************************
vocabulary scsi-words                  \ create new word list named 'scsi-words'
also scsi-words  definitions           \ place next definitions into new list

\ for some commands specific parameters are used, which normally
\ need not to be altered. These values are preset at include time
\ or explicit by a call of 'scsi-supp-init'
false  value   scsi-param-debug        \ common debugging flag
d# 0   value   scsi-param-size         \ length of CDB processed last
h# 0   value   scsi-param-control      \ control word for CDBs as defined in SAM-4
d# 0   value   scsi-param-errors       \ counter for detected errors

\ utility to increment error counter
: scsi-inc-errors
   scsi-param-errors 1 + to scsi-param-errors
;

\ ***************************************************************************
\ SCSI-Command: TEST UNIT READY
\         Type: Primary Command (SPC-3 clause 6.33)
\ ***************************************************************************
\ Forth Word:   scsi-build-test-unit-ready    ( cdb -- )
\ ***************************************************************************
\ checks if a device is ready to receive commands
\ ***************************************************************************
\ command code:
00 CONSTANT scsi-cmd-test-unit-ready
\ CDB structure:
STRUCT
	/c	FIELD test-unit-ready>operation-code     \ 00h
	4	FIELD test-unit-ready>reserved           \ unused
	/c	FIELD test-unit-ready>control            \ control byte as specified in SAM-4
CONSTANT scsi-length-test-unit-ready

\ cdb build:
\ all fields are zeroed
: scsi-build-test-unit-ready  ( cdb -- )
   dup scsi-length-test-unit-ready erase  ( cdb )
   scsi-param-control swap test-unit-ready>control c!  ( )
   scsi-length-test-unit-ready to scsi-param-size   \ update CDB length
;

\ ***************************************************************************
\ SCSI-Command: REPORT LUNS
\         Type: Primary Command
\ ***************************************************************************
\ Forth Word:   scsi-build-report-luns    ( cdb -- )
\ ***************************************************************************
\ report all LUNs supported by a device
\ ***************************************************************************
\ command code:
a0 CONSTANT scsi-cmd-report-luns
\ CDB structure:
STRUCT
	/c	FIELD report-luns>operation-code     \ a0h
	1	FIELD report-luns>reserved           \ unused
        /c      FIELD report-luns>select-report      \ report select byte
        3       FIELD report-luns>reserved2          \ unused
        /l      FIELD report-luns>alloc-length       \ report length
	1	FIELD report-luns>reserved3          \ unused
	/c	FIELD report-luns>control            \ control byte
CONSTANT scsi-length-report-luns

\ cdb build:
\ all fields are zeroed
: scsi-build-report-luns ( alloc-len cdb -- )
   dup scsi-length-report-luns erase              \ 12 bytes CDB
	scsi-cmd-report-luns over	          ( alloc-len cdb cmd cdb )
	report-luns>operation-code c!	          ( alloc-len cdb )
   scsi-param-control over report-luns>control c! ( alloc-len cdb )
	report-luns>alloc-length l!	  \ size of Data-In Buffer
   scsi-length-report-luns to scsi-param-size     \ update CDB length
;

\ ***************************************************************************
\ SCSI-Command: REQUEST SENSE
\         Type: Primary Command (SPC-3 clause 6.27)
\ ***************************************************************************
\ Forth Word:   scsi-build-request-sense    ( cdb -- )
\ ***************************************************************************
\ for return data a buffer of at least 252 bytes must be present!
\ see spec: SPC-3 (r23) / clauses 4.5 and 6.27
\ ***************************************************************************
\ command code:
03 CONSTANT scsi-cmd-request-sense
\ CDB structure:
STRUCT
	/c	FIELD request-sense>operation-code     \ 03h
	3	FIELD request-sense>reserved           \ unused
	/c	FIELD request-sense>allocation-length  \ buffer-length for data response
	/c	FIELD request-sense>control            \ control byte as specified in SAM-4
CONSTANT scsi-length-request-sense

\ cdb build:
: scsi-build-request-sense    ( alloc-len cdb -- )
   >r                         ( alloc-len )  ( R: -- cdb )
   r@ scsi-length-request-sense erase  ( alloc-len )
   scsi-cmd-request-sense r@           ( alloc-len cmd cdb )
   request-sense>operation-code c!     ( alloc-len )
   dup d# 252 >                        \ buffer length too big ?
   IF
      scsi-inc-errors
      drop d# 252                      \ replace with 252
   ELSE
      dup d# 18 <                      \ allocated buffer too small ?
      IF
         scsi-inc-errors
         drop 0                        \ reject return data
      THEN
   THEN                                      ( alloclen )
   r@ request-sense>allocation-length c!     (  )
   scsi-param-control r> request-sense>control c!  ( alloc-len cdb )  ( R: cdb -- )
   scsi-length-request-sense to scsi-param-size  \ update CDB length
;

\ ----------------------------------------
\ SCSI-Response: SENSE_DATA
\ ----------------------------------------
70 CONSTANT scsi-response(request-sense-0)
71 CONSTANT scsi-response(request-sense-1)

STRUCT
   /c FIELD sense-data>response-code   \ 70h (current errors) or 71h (deferred errors)
   /c FIELD sense-data>obsolete
   /c FIELD sense-data>sense-key       \ D3..D0 = sense key, D7 = EndOfMedium
   /l FIELD sense-data>info
   /c FIELD sense-data>alloc-length    \ <= 244 (for max size)
   /l FIELD sense-data>command-info
   /c FIELD sense-data>asc             \ additional sense key
   /c FIELD sense-data>ascq            \ additional sense key qualifier
   /c FIELD sense-data>unit-code
   3  FIELD sense-data>key-specific
   /c FIELD sense-data>add-sense-bytes \ start of appended extra bytes
CONSTANT scsi-length-sense-data

\ ----------------------------------------
\ get from SCSI response block:
\  - Additional Sense Code Qualifier
\  - Additional Sense Code
\  - sense-key
\ ----------------------------------------
\ Forth Word:   scsi-get-sense-data  ( addr -- ascq asc sense-key )
\ ----------------------------------------
: scsi-get-sense-data                  ( addr -- ascq asc sense-key )   
   >r                                  ( R: -- addr )
   r@ sense-data>response-code c@ 7f and 72 >= IF
     r@ 3 + c@                           ( ascq )
     r@ 2 + c@                           ( ascq asc ) 
     r> 1 + c@ 0f and                    ( ascq asc sense-key )
   ELSE
     r@ sense-data>ASCQ c@               ( ascq )
     r@ sense-data>ASC c@                ( ascq asc )
     r> sense-data>sense-key c@ 0f and   ( ascq asc sense-key ) ( R: addr -- )
   THEN
;

\ --------------------------------------------------------------------------
\ Forth Word:   scsi-get-sense-data?  ( addr -- false | ascq asc sense-key true )
\ --------------------------------------------------------------------------
: scsi-get-sense-data?                 ( addr -- false | ascq asc sense-key true )
   dup
   sense-data>response-code c@
   7e AND dup 70 = swap 72 = or         \ Response code (some devices have MSB set)
   IF
      scsi-get-sense-data TRUE
   ELSE
      drop FALSE        \ drop addr
   THEN

;

\ --------------------------------------------------------------------------
\ Forth Word:   scsi-get-sense-ID?  ( addr -- false | sense-ID true )
\ same as scsi-get-sense-data? but returns
\ a single word composed of: sense-key<<16 | asc<<8 | ascq
\ --------------------------------------------------------------------------
: scsi-get-sense-ID?                 ( addr -- false | ascq asc sense-key true )
   dup
   sense-data>response-code c@
   7e AND 70 =          \ Response code (some devices have MSB set)
   IF
      scsi-get-sense-data        ( ascq asc sense-key )
      10 lshift                  ( ascq asc sense-key16 )
      swap 8 lshift or           ( ascq sense-key+asc )
      swap or                    \ 24-bit sense-ID ( sense-key+asc+ascq )
      TRUE
   ELSE
      drop FALSE        \ drop addr
   THEN
;

\ ***************************************************************************
\ SCSI-Command: INQUIRY
\         Type: Primary Command (SPC-3 clause 6.4)
\ ***************************************************************************
\ Forth Word:   scsi-build-inquiry    ( alloc-len cdb -- )
\ ***************************************************************************
\ command code:
12 CONSTANT scsi-cmd-inquiry

\ CDB structure
STRUCT
	/c	FIELD inquiry>operation-code     \ 0x12
	/c	FIELD inquiry>reserved           \ + EVPD-Bit (vital product data)
	/c	FIELD inquiry>page-code          \ page code for vital product data (if used)
	/w	FIELD inquiry>allocation-length  \ length of Data-In-Buffer
	/c	FIELD inquiry>control            \ control byte as specified in SAM-4
CONSTANT scsi-length-inquiry

\ Setup command INQUIRY
: scsi-build-inquiry                   ( alloc-len cdb -- )
   dup scsi-length-inquiry erase       \ 6 bytes CDB
	scsi-cmd-inquiry over				   ( alloc-len cdb cmd cdb )
	inquiry>operation-code c!	         ( alloc-len cdb )
   scsi-param-control over inquiry>control c! ( alloc-len cdb )
	inquiry>allocation-length w!	      \ size of Data-In Buffer
   scsi-length-inquiry to scsi-param-size    \ update CDB length
;

\ ----------------------------------------
\ block structure of inquiry return data:
\ ----------------------------------------
STRUCT
	/c	   FIELD inquiry-data>peripheral       \ qualifier and device type
	/c	   FIELD inquiry-data>reserved1
	/c	   FIELD inquiry-data>version          \ supported SCSI version (1,2,3)
	/c	   FIELD inquiry-data>data-format
	/c	   FIELD inquiry-data>add-length       \ total block length - 4
	/c	   FIELD inquiry-data>flags1
	/c	   FIELD inquiry-data>flags2
	/c	   FIELD inquiry-data>flags3
	d# 8	FIELD inquiry-data>vendor-ident     \ vendor string
	d# 16	FIELD inquiry-data>product-ident    \ device string
	/l 	FIELD inquiry-data>product-revision \ revision string
	d# 20	FIELD inquiry-data>vendor-specific  \ optional params
\ can be increased by vendor specific fields
CONSTANT scsi-length-inquiry-data

\ ***************************************************************************
\ SCSI-Command: READ CAPACITY (10)
\         Type: Block Command (SBC-3 clause 5.12)
\ ***************************************************************************
\ Forth Word:   scsi-build-read-capacity-10    ( cdb -- )
\ ***************************************************************************
25 CONSTANT scsi-cmd-read-capacity-10  \ command code

STRUCT                                 \ SCSI 10-byte CDB structure
	/c	FIELD read-cap-10>operation-code
	/c	FIELD read-cap-10>reserved1
	/l	FIELD read-cap-10>lba
	/w	FIELD read-cap-10>reserved2
	/c	FIELD read-cap-10>reserved3
	/c	FIELD read-cap-10>control
CONSTANT scsi-length-read-cap-10

\ Setup READ CAPACITY (10) command
: scsi-build-read-cap-10                     ( cdb -- )
   dup scsi-length-read-cap-10 erase         ( cdb )
	scsi-cmd-read-capacity-10 over            ( cdb cmd cdb )
	read-cap-10>operation-code c!             ( cdb )
   scsi-param-control swap read-cap-10>control c! ( )
   scsi-length-read-cap-10 to scsi-param-size    \ update CDB length
;

\ ----------------------------------------
\ get from SCSI response block:
\  - Additional Sense Code Qualifier
\  - Additional Sense Code
\  - sense-key
\ ----------------------------------------
\ Forth Word:   scsi-get-capacity-10  ( addr -- block-size #blocks )
\ ----------------------------------------
\ Block structure
STRUCT
	/l	FIELD read-cap-10-data>max-lba
	/l	FIELD read-cap-10-data>block-size
CONSTANT scsi-length-read-cap-10-data

\ get data-block
: scsi-get-capacity-10                 ( addr -- block-size #blocks )
   >r                                  ( addr -- ) ( R: -- addr )
   r@ read-cap-10-data>block-size l@   ( block-size )
   r> read-cap-10-data>max-lba l@      ( block-size #blocks ) ( R: addr -- )
;

\ ***************************************************************************
\ SCSI-Command: READ CAPACITY (16)
\         Type: Block Command (SBC-3 clause 5.13)
\ ***************************************************************************
\ Forth Word:   scsi-build-read-capacity-16    ( cdb -- )
\ ***************************************************************************
9e CONSTANT scsi-cmd-read-capacity-16        \ command code

STRUCT                                       \ SCSI 16-byte CDB structure
	/c	FIELD read-cap-16>operation-code
	/c	FIELD read-cap-16>service-action
	/l	FIELD read-cap-16>lba-high
	/l	FIELD read-cap-16>lba-low
	/l	FIELD read-cap-16>allocation-length    \ should be 32
	/c	FIELD read-cap-16>reserved
	/c	FIELD read-cap-16>control
CONSTANT scsi-length-read-cap-16

\ Setup READ CAPACITY (16) command
: scsi-build-read-cap-16  ( cdb -- )
   >r r@                                     ( R: -- cdb )
   scsi-length-read-cap-16 erase             (  )
	scsi-cmd-read-capacity-16                 ( code )
	r@ read-cap-16>operation-code c!          (  )
   10 r@ read-cap-16>service-action c!
   d# 32                                     \ response size 32 bytes
   r@ read-cap-16>allocation-length l!       (  )
   scsi-param-control r> read-cap-16>control c! ( R: cdb -- )
   scsi-length-read-cap-16 to scsi-param-size \ update CDB length
;

\ ----------------------------------------
\ get from SCSI response block:
\  - Block Size (in Bytes)
\  - Number of Blocks
\ ----------------------------------------
\ Forth Word:   scsi-get-capacity-16  ( addr -- block-size #blocks )
\ ----------------------------------------
\ Block structure for return data
STRUCT
	/l	FIELD read-cap-16-data>max-lba-high    \ upper quadlet of Max-LBA
	/l	FIELD read-cap-16-data>max-lba-low     \ lower quadlet of Max-LBA
	/l	FIELD read-cap-16-data>block-size      \ logical block length in bytes
   /c	FIELD read-cap-16-data>protect         \ type of protection (4 bits)
   /c	FIELD read-cap-16-data>exponent        \ logical blocks per physical blocks
   /w	FIELD read-cap-16-data>lowest-aligned  \ first LBA of a phsy. block
   10 FIELD read-cap-16-data>reserved        \ 16 reserved bytes
CONSTANT scsi-length-read-cap-16-data        \ results in 32

\ get data-block
: scsi-get-capacity-16                       ( addr -- block-size #blocks )
   >r                                        ( R: -- addr )
   r@ read-cap-16-data>block-size l@         ( block-size )
   r@ read-cap-16-data>max-lba-high l@       ( block-size #blocks-high )
   d# 32 lshift                              ( block-size #blocks-upper )
   r> read-cap-16-data>max-lba-low l@ +      ( block-size #blocks ) ( R: addr -- )
;

\ ***************************************************************************
\ SCSI-Command: MODE SENSE (10)
\         Type: Primary Command (SPC-3 clause 6.10)
\ ***************************************************************************
\ Forth Word:   scsi-build-mode-sense-10  ( alloc-len subpage page cdb -- )
\ ***************************************************************************
5a CONSTANT scsi-cmd-mode-sense-10

\ CDB structure
STRUCT
	/c	FIELD mode-sense-10>operation-code
	/c	FIELD mode-sense-10>res-llbaa-dbd-res
	/c	FIELD mode-sense-10>pc-page-code       \ page code + page control
	/c	FIELD mode-sense-10>sub-page-code
	3	FIELD mode-sense-10>reserved2
	/w	FIELD mode-sense-10>allocation-length
	/c	FIELD mode-sense-10>control
CONSTANT scsi-length-mode-sense-10

: scsi-build-mode-sense-10                   ( alloc-len subpage page cdb -- )
   >r                                        ( alloc-len subpage page ) ( R: -- cdb )
   r@ scsi-length-mode-sense-10 erase        \ 10 bytes CDB
	scsi-cmd-mode-sense-10                    ( alloc-len subpage page cmd )
   r@  mode-sense-10>operation-code c!		   ( alloc-len subpage page )
   10 r@ mode-sense-10>res-llbaa-dbd-res c!  \ long LBAs accepted
	r@ mode-sense-10>pc-page-code c!	         ( alloc-len subpage )
	r@ mode-sense-10>sub-page-code c!	      ( alloc-len )
	r@ mode-sense-10>allocation-length w!     ( )

   scsi-param-control r> mode-sense-10>control c!  ( R: cdb -- )
   scsi-length-mode-sense-10 to scsi-param-size  \ update CDB length
;

\ return data processing
\ (see spec: SPC-3 clause 7.4.3)

STRUCT
	/w	FIELD mode-sense-10-data>head-length
	/c	FIELD mode-sense-10-data>head-medium
	/c	FIELD mode-sense-10-data>head-param
	/c	FIELD mode-sense-10-data>head-longlba
	/c	FIELD mode-sense-10-data>head-reserved
	/w	FIELD mode-sense-10-data>head-descr-len
CONSTANT scsi-length-mode-sense-10-data

\ ****************************************
\ This function shows the mode page header
\ helpful for further analysis
\ ****************************************
: .mode-sense-data   ( addr -- )
   cr
   dup mode-sense-10-data>head-length
   w@ ." Mode Length: " .d space
   dup mode-sense-10-data>head-medium
   c@ ." / Medium Type: " .d space
   dup mode-sense-10-data>head-longlba
   c@ ." / Long LBA: " .d space
   mode-sense-10-data>head-descr-len
   w@ ." / Descr. Length: " .d
;

\ ***************************************************************************
\ SCSI-Command: READ (6)
\         Type: Block Command (SBC-3 clause 5.7)
\ ***************************************************************************
\ Forth Word:   scsi-build-read-6  ( block# #blocks cdb -- )
\ ***************************************************************************
\ this SCSI command uses 21 bits to represent start LBA
\ and 8 bits to specify the numbers of blocks to read
\ The value of 0 blocks is interpreted as 256 blocks
\
\ command code
08 CONSTANT scsi-cmd-read-6

\ CDB structure
STRUCT
   /c FIELD read-6>operation-code      \ 08h
   /c FIELD read-6>block-address-msb   \ upper 5 bits
   /w FIELD read-6>block-address       \ lower 16 bits
   /c FIELD read-6>length              \ number of blocks to read
   /c FIELD read-6>control             \ CDB control
CONSTANT scsi-length-read-6

: scsi-build-read-6                    ( block# #blocks cdb -- )
   >r                                  ( block# #blocks ) ( R: -- cdb )
   r@ scsi-length-read-6 erase         \ 6 bytes CDB
	scsi-cmd-read-6 r@ read-6>operation-code c! ( block# #blocks )

   \ check block count to read (#blocks)
   dup d# 255 >                        \ #blocks exceeded limit ?
   IF
      scsi-inc-errors
      drop 1                           \ replace with any valid number
   THEN
   r@ read-6>length c!                 \ set #blocks to read

   \ check starting block number (block#)
   dup 1fffff >                        \ check address upper limit
   IF
      scsi-inc-errors
      drop                             \ remove original block#
      1fffff                           \ replace with any valid address
   THEN
   dup d# 16 rshift
   r@ read-6>block-address-msb c!      \ set upper 5 bits
   ffff and
   r@ read-6>block-address w!                \ set lower 16 bits
   scsi-param-control r> read-6>control c!   ( R: cdb -- )
   scsi-length-read-6 to scsi-param-size     \ update CDB length
;

\ ***************************************************************************
\ SCSI-Command: READ (10)
\         Type: Block Command (SBC-3 clause 5.8)
\ ***************************************************************************
\ Forth Word:   scsi-build-read-10  ( block# #blocks cdb -- )
\ ***************************************************************************
\ command code
28 CONSTANT scsi-cmd-read-10

\ CDB structure
STRUCT
   /c FIELD read-10>operation-code
   /c FIELD read-10>protect
   /l FIELD read-10>block-address      \ logical block address (32bits)
   /c FIELD read-10>group
   /w FIELD read-10>length             \ transfer length (16-bits)
   /c FIELD read-10>control
CONSTANT scsi-length-read-10

: scsi-build-read-10                         ( block# #blocks cdb -- )
   >r                                        ( block# #blocks )  ( R: -- cdb )
   r@ scsi-length-read-10 erase             \ 10 bytes CDB
	scsi-cmd-read-10 r@ read-10>operation-code c! ( block# #blocks )
   r@ read-10>length w!                      ( block# )
   r@ read-10>block-address l!               (  )
   scsi-param-control r> read-10>control c!  ( R: cdb -- )
   scsi-length-read-10 to scsi-param-size    \ update CDB length
;

\ ***************************************************************************
\ SCSI-Command: READ (12)
\         Type: Block Command (SBC-3 clause 5.9)
\ ***************************************************************************
\ Forth Word:   scsi-build-read-12  ( block# #blocks cdb -- )
\ ***************************************************************************
\ command code
a8 CONSTANT scsi-cmd-read-12

\ CDB structure
STRUCT
   /c FIELD read-12>operation-code     \ code: a8
   /c FIELD read-12>protect            \ RDPROTECT, DPO, FUA, FUA_NV
   /l FIELD read-12>block-address      \ lba
   /l FIELD read-12>length             \ transfer length (32bits)
   /c FIELD read-12>group              \ group number
   /c FIELD read-12>control
CONSTANT scsi-length-read-12

: scsi-build-read-12                         ( block# #blocks cdb -- )
   >r                                        ( block# #blocks )  ( R: -- cdb )
   r@ scsi-length-read-12 erase             \ 12 bytes CDB
	scsi-cmd-read-12 r@ read-12>operation-code c! ( block# #blocks )
   r@ read-12>length l!                      ( block# )
   r@ read-12>block-address l!               (  )
   scsi-param-control r> read-12>control c!  ( R: cdb -- )
   scsi-length-read-12 to scsi-param-size    \ update CDB length
;

\ ***************************************************************************
\ SCSI-Command: READ with autodetection of required command
\               read(10) or read(12) depending on parameter size
\               (read(6) removed because obsolete in some cases (USB))
\         Type: Block Command
\ ***************************************************************************
\ Forth Word:   scsi-build-read?    ( block# #blocks cdb -- )
\
\                         +----------------+---------------------------|
\                         |  block# (lba)  |  #block (transfer-length) |
\             +-----------+----------------+---------------------------|
\             | read-6    |  16-Bits       |  8  Bits                  |
\             | read-10   |  32-Bits       |  16 Bits                  |
\             | read-12   |  32-Bits       |  32 Bits                  |
\ ***************************************************************************
: scsi-build-read?   ( block# #blocks cdb -- length )
   over              ( block# #blocks cdb #blocks )
   fffe >            \ tx-length (#blocks) exceeds 16-bit limit ?
   IF
      scsi-build-read-12   ( block# #blocks cdb -- )
      scsi-length-read-12  ( length )
   ELSE                    ( block# #blocks cdb )
      scsi-build-read-10   ( block# #blocks cdb -- )
      scsi-length-read-10  ( length )
   THEN
;

\ ***************************************************************************
\ SCSI-Command: START STOP UNIT
\         Type: Block Command (SBC-3 clause 5.19)
\ ***************************************************************************
\ Forth Word:   scsi-build-start-stop-unit  ( state# cdb -- )
\ ***************************************************************************
\ command code
1b CONSTANT scsi-cmd-start-stop-unit

\ CDB structure
STRUCT
   /c FIELD start-stop-unit>operation-code
   /c FIELD start-stop-unit>immed
   /w FIELD start-stop-unit>reserved
   /c FIELD start-stop-unit>pow-condition
   /c FIELD start-stop-unit>control
CONSTANT scsi-length-start-stop-unit

\ START/STOP constants
\ (see spec: SBC-3 clause 5.19)
f1 CONSTANT scsi-const-active-power    \ param used for start-stop-unit
f2 CONSTANT scsi-const-idle-power      \ param used for start-stop-unit
f3 CONSTANT scsi-const-standby-power   \ param used for start-stop-unit
3  CONSTANT scsi-const-load            \ param used for start-stop-unit
2  CONSTANT scsi-const-eject           \ param used for start-stop-unit
1  CONSTANT scsi-const-start
0  CONSTANT scsi-const-stop

: scsi-build-start-stop-unit                 ( state# cdb -- )
   >r                                        ( state# )  ( R: -- cdb )
   r@ scsi-length-start-stop-unit erase      \ 6 bytes CDB
	scsi-cmd-start-stop-unit r@ start-stop-unit>operation-code c!
   dup 3 >
   IF
      4 lshift                         \ shift to upper nibble
   THEN                                ( state )
   r@ start-stop-unit>pow-condition c!       (  )
   scsi-param-control r> start-stop-unit>control c!  ( R: cdb -- )
   scsi-length-start-stop-unit to scsi-param-size  \ update CDB length
;

\ ***************************************************************************
\ SCSI-Command: SEEK(10)
\         Type: Block Command (obsolete)
\ ***************************************************************************
\ Forth Word:   scsi-build-seek  ( state# cdb -- )
\ Obsolete function (last listed in spec SBC / Nov. 1997)
\ implemented only for the sake of completeness
\ ***************************************************************************
\ command code
2b CONSTANT scsi-cmd-seek

\ CDB structure
STRUCT
   /c FIELD seek>operation-code
   /c FIELD seek>reserved1
   /l FIELD seek>lba
   3  FIELD seek>reserved2
   /c FIELD seek>control
CONSTANT scsi-length-seek

: scsi-build-seek  ( lba cdb -- )
   >r              ( lba )  ( R: -- cdb )
   r@ scsi-length-seek erase           \ 10 bytes CDB
	scsi-cmd-seek r@ seek>operation-code c!
   r> seek>lba l!  (  )  ( R: cdb -- )
   scsi-length-seek to scsi-param-size \ update CDB length
;

\ ****************************************************************************
\ CDROM media event stuff
\ ****************************************************************************

STRUCT
    /w FIELD media-event-data-len
    /c FIELD media-event-nea-class
    /c FIELD media-event-supp-class
    /l FIELD media-event-data
CONSTANT scsi-length-media-event

: scsi-build-get-media-event                     ( cdb -- )
   dup c erase				         ( cdb )
   4a over c!				         ( cdb )
   01 over 1 + c!
   10 over 4 + c!
   08 over 8 + c!
   drop
;



\ ***************************************************************************
\ SCSI-Utility: .sense-code
\ ***************************************************************************
\ this utility prints a string associated to the sense code
\ see specs: SPC-3/r23 clause 4.5.6
\ ***************************************************************************
: .sense-text ( scode -- )
   case
      0    OF s" OK"               ENDOF
      1    OF s" RECOVERED ERR"    ENDOF
      2    OF s" NOT READY"        ENDOF
      3    OF s" MEDIUM ERROR"     ENDOF
      4    OF s" HARDWARE ERR"     ENDOF
      5    OF s" ILLEGAL REQUEST"  ENDOF
      6    OF s" UNIT ATTENTION"   ENDOF
      7    OF s" DATA PROTECT"     ENDOF
      8    OF s" BLANK CHECK"      ENDOF
      9    OF s" VENDOR SPECIFIC"  ENDOF
      a    OF s" COPY ABORTED"     ENDOF
      b    OF s" ABORTED COMMAND"  ENDOF
      d    OF s" VOLUME OVERFLOW"  ENDOF
      e    OF s" MISCOMPARE"       ENDOF
      dup  OF s" UNKNOWN"          ENDOF
   endcase
   5b emit type 5d emit
;

\ ***************************************************************************
\ SCSI-Utility: .status-code
\ ***************************************************************************
\ this utility prints a string associated to the status code
\ see specs: SAM-3/r14 clause 5.3
\ ***************************************************************************
: .status-text  ( stat -- )
   case
      00  OF s" GOOD"                  ENDOF
      02  OF s" CHECK CONDITION"       ENDOF
      04  OF s" CONDITION MET"         ENDOF
      08  OF s" BUSY"                  ENDOF
      18  OF s" RESERVATION CONFLICT"  ENDOF
      28  OF s" TASK SET FULL"         ENDOF
      30  OF s" ACA ACTIVE"            ENDOF
      40  OF s" TASK ABORTED"          ENDOF
      dup OF s" UNKNOWN"               ENDOF
   endcase
   5b emit type 5d emit
;

\ ***************************************************************************
\ SCSI-Utility: .capacity-text
\ ***************************************************************************
\ utility that shows total capacity on screen by use of the return data
\ from read-capacity calculation is SI conform (base 10)
\ ***************************************************************************
\ sub function to print a 3 digit decimal
\ number with 2 post decimal positions xxx.yy
: .dec3-2 ( prenum postnum -- )
   swap
   base @ >r                           \ save actual base setting
   decimal                             \ show decimal values
   4 .r 2e emit
   dup 9 <= IF 30 emit THEN .d         \ 3 pre-decimal, right aligned
   r> base !                           \ restore base
;

: .capacity-text  ( block-size #blocks -- )
   scsi-param-debug                    \ debugging flag set ?
   IF                                  \ show additional info
      2dup
      cr
      ." LBAs: " .d                    \ highest logical block number
      ." / Block-Size: " .d
      ." / Total Capacity: "
   THEN
   *                                   \ calculate total capacity
   dup d# 1000000000000 >=             \ check terabyte limit
   IF
      d# 1000000000000 /mod
      swap
      d# 10000000000 /                 \ limit remainder to two digits
      .dec3-2 ." TB"                   \ show terabytes as xxx.yy
   ELSE
      dup d# 1000000000 >=             \ check gigabyte limit
      IF
         d# 1000000000 /mod
         swap
         d# 10000000 /
         .dec3-2 ." GB"                \ show gigabytes as xxx.yy
      ELSE
         dup d# 1000000 >=
         IF
            d# 1000000 /mod            \ check mega byte limit
            swap
            d# 10000 /
            .dec3-2 ." MB"             \ show megabytes as xxx.yy
         ELSE
            dup d# 1000 >=             \ check kilo byte limit
            IF
               d# 1000 /mod
               swap
               d# 10 /
               .dec3-2 ." kB"
            ELSE
               .d ."  Bytes"
            THEN
         THEN
      THEN
   THEN
;

\ ***************************************************************************
\ SCSI-Utility: .inquiry-text  ( addr -- )
\ ***************************************************************************
\ utility that shows:
\     vendor-ident product-ident and revision
\ from an inquiry return data block (addr)
\ ***************************************************************************
: .inquiry-text  ( addr -- )
   22 emit     \ enclose text with "
   dup inquiry-data>vendor-ident      8 type space
   dup inquiry-data>product-ident    10 type space
       inquiry-data>product-revision  4 type
   22 emit
;

\ ***************************************************************************
\ SCSI-Utility: scsi-supp-init  ( -- )
\ ***************************************************************************
\ utility that helps to ensure that parameters are set to valid values
: scsi-supp-init  ( -- )
   false   to scsi-param-debug         \ no debug strings
   h# 0   to scsi-param-size
   h# 0   to scsi-param-control        \ common CDB control byte
   d# 0   to scsi-param-errors         \ local errors (param limits)
;

\ ***************************************************************************
\ Constants used by SCSI controller's execute-scsi-command
\ ***************************************************************************
true  CONSTANT scsi-dir-read
false CONSTANT scsi-dir-write


\ ***************************************************************************
\ scsi loader
\ ***************************************************************************
0 VALUE scsi-context                   \ addr of word list on top


\ ****************************************************************************
\ open scsi-support by adding a new word list on top of search path
\   precondition: scsi-support.fs must have been included
\ ****************************************************************************
: scsi-init  ( -- )
   also scsi-words                     \ append scsi word-list
   context  to scsi-context            \ save for close process
   scsi-supp-init                      \ preset all scsi-param-xxx values
   scsi-param-debug
   IF
      space ." SCSI-SUPPORT OPENED" cr
      .wordlists
   THEN
;

\ ****************************************************************************
\ close scsi-session and remove scsi word list (if exists)
\ ****************************************************************************
\ if 'previous' is used without a preceding 'also' all forth words are lost !
\ ****************************************************************************
: scsi-close  ( -- )
\ FIXME This only works if scsi-words is the last vocabulary on the stack
\       Instead we could use get-order to find us on the "wordlist stack",
\       remove us and write the wordlist stack back with set-order.
\       BUT: Is this worth the effort?

   scsi-param-debug
   IF
      space ." Closing SCSI-SUPPORT .. " cr
   THEN
   context scsi-context =              \ scsi word list still active ?
   IF
      scsi-param-errors 0<>          \ any errors occurred ?
      IF
         cr ." ** WARNING: " scsi-param-errors .d
         ." SCSI Errors occurred ** " cr
      THEN
      previous                         \ remove scsi word list on top
      0 to scsi-context                \ prevent from being misinterpreted
   ELSE
      cr ." ** WARNING: Trying to close non-open SCSI-SUPPORT (1) ** " cr
   THEN
   scsi-param-debug
   IF
     .wordlists
   THEN
;


s" scsi-init" $find drop               \ return execution pointer, when included

previous                               \ remove scsi word list from search path
definitions                            \ place next definitions into previous list