| \ ***************************************************************************** |
| \ * Copyright (c) 2013 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 |
| \ ****************************************************************************/ |
| |
| \ ( usbdev -- ) |
| |
| new-device |
| |
| VALUE usbdev |
| |
| s" slofdev.fs" included |
| |
| false VALUE usb-disk-debug? |
| |
| usbdev slof-dev>port l@ dup set-unit encode-phys " reg" property |
| s" storage" device-name |
| |
| s" dev-parent-calls.fs" included |
| |
| 2 encode-int s" #address-cells" property |
| 0 encode-int s" #size-cells" property |
| |
| : decode-unit 2 hex64-decode-unit ; |
| : encode-unit 2 hex64-encode-unit ; |
| |
| 0 CONSTANT USB_PIPE_OUT |
| 1 CONSTANT USB_PIPE_IN |
| |
| \ ----------------------------------------------------------- |
| \ Specific properties |
| \ ----------------------------------------------------------- |
| |
| usbdev slof-dev>udev @ VALUE udev |
| usbdev slof-dev>port l@ VALUE port |
| usbdev slof-dev>hcitype l@ VALUE hcitype |
| |
| 0 INSTANCE VALUE lun |
| 10000 VALUE dev-max-transfer |
| 0 VALUE resp-buffer |
| 0 VALUE resp-size |
| 0f CONSTANT SCSI-COMMAND-OFFSET |
| |
| \ ------------------------------------------------------- |
| \ DMA-able buffers |
| \ ------------------------------------------------------- |
| |
| STRUCT |
| dev-max-transfer FIELD usb>data |
| 40 FIELD usb>cmd |
| 20 FIELD usb>csw |
| CONSTANT /dma-buf |
| |
| 0 VALUE dma-buf |
| 0 VALUE dma-buf-phys |
| 0 VALUE td-buf |
| 0 VALUE td-buf-phys |
| 1000 CONSTANT /td-buf |
| |
| : (dma-buf-init) ( -- ) |
| /dma-buf dma-alloc TO dma-buf |
| dma-buf /dma-buf 0 dma-map-in TO dma-buf-phys |
| /td-buf dma-alloc TO td-buf |
| td-buf /td-buf 0 dma-map-in TO td-buf-phys |
| ; |
| |
| : (dma-buf-free) ( -- ) |
| td-buf td-buf-phys /td-buf dma-map-out |
| td-buf /td-buf dma-free |
| 0 TO td-buf |
| 0 TO td-buf-phys |
| dma-buf dma-buf-phys /dma-buf dma-map-out |
| dma-buf /dma-buf dma-free |
| 0 TO dma-buf |
| 0 TO dma-buf-phys |
| ; |
| |
| |
| scsi-open |
| |
| \ ----------------------------------------------------------- |
| \ Perform SCSI commands |
| \ ----------------------------------------------------------- |
| |
| 0 INSTANCE VALUE current-target |
| |
| \ SCSI command. We do *NOT* implement the "standard" execute-command |
| \ because that doesn't have a way to return the sense buffer back, and |
| \ we do have auto-sense with some hosts. Instead we implement a made-up |
| \ do-scsi-command. |
| \ |
| \ Note: stat is -1 for "hw error" (ie, error queuing the command or |
| \ getting the response). |
| \ |
| \ A sense buffer is returned whenever the status is non-0 however |
| \ if sense-len is 0 then no sense data is actually present |
| \ |
| |
| : do-bulk-command ( resp-buffer resp-size -- TRUE | FALSE ) |
| TO resp-size |
| TO resp-buffer |
| udev USB_PIPE_OUT td-buf td-buf-phys dma-buf-phys usb>cmd 1F |
| usb-transfer-bulk IF \ transfer CBW |
| resp-size IF |
| d# 125 us |
| udev USB_PIPE_IN td-buf td-buf-phys resp-buffer resp-size |
| usb-transfer-bulk 1 = not IF \ transfer data |
| usb-disk-debug? IF ." Data phase failed " cr THEN |
| \ FALSE EXIT |
| \ in case of a stall/halted endpoint we clear the halt |
| \ Fall through and try reading the CSW |
| THEN |
| THEN |
| d# 125 us |
| udev USB_PIPE_IN td-buf td-buf-phys dma-buf-phys usb>csw 0D |
| usb-transfer-bulk \ transfer CSW |
| ELSE |
| FALSE EXIT |
| THEN |
| ; |
| |
| STRUCT \ cbw |
| /l FIELD cbw>sig |
| /l FIELD cbw>tag |
| /l FIELD cbw>len |
| /c FIELD cbw>flags |
| /c FIELD cbw>lun \ 0:3 bits |
| /c FIELD cbw>cblen \ 0:4 bits |
| CONSTANT cbw-length |
| |
| STRUCT \ csw |
| /l FIELD csw>sig |
| /l FIELD csw>tag |
| /l FIELD csw>data-residue |
| /c FIELD csw>status |
| CONSTANT cbw-length |
| |
| 0 VALUE cbw-addr |
| 0 VALUE csw-addr |
| |
| : build-cbw ( tag xfer-len dir lun cmd-len addr -- ) |
| TO cbw-addr ( tag xfer-len dir lun cmd-len ) |
| cbw-addr cbw-length erase ( tag xfer-len dir lun cmd-len ) |
| cbw-addr cbw>cblen c! ( tag xfer-len dir lun ) |
| cbw-addr cbw>lun c! ( tag xfer-len dir ) |
| \ dir is true or false |
| \ bmCBWFlags |
| \ BIT 7 Direction |
| \ 0 - OUT |
| \ 1 - IN |
| IF 80 ELSE 0 THEN |
| cbw-addr cbw>flags c! ( tag xfer-len ) |
| cbw-addr cbw>len l!-le ( tag ) |
| cbw-addr cbw>tag l!-le ( ) |
| 43425355 cbw-addr cbw>sig l!-le |
| ; |
| |
| 0 INSTANCE VALUE usb-buf-addr |
| 0 INSTANCE VALUE usb-buf-len |
| 0 INSTANCE VALUE usb-dir |
| 0 INSTANCE VALUE usb-cmd-addr |
| 0 INSTANCE VALUE usb-cmd-len |
| 1 VALUE tag |
| |
| : execute-scsi-command ( buf-addr buf-len dir cmd-addr cmd-len -- ... ) |
| ( ... [ sense-buf sense-len ] stat ) |
| \ Cleanup virtio request and response |
| to usb-cmd-len to usb-cmd-addr to usb-dir to usb-buf-len to usb-buf-addr |
| |
| dma-buf usb>cmd 40 0 fill |
| dma-buf usb>csw 20 0 fill |
| |
| tag usb-buf-len usb-dir lun usb-cmd-len dma-buf usb>cmd |
| ( tag transfer-len dir lun cmd-len addr ) |
| build-cbw |
| 1 tag + to tag |
| |
| usb-cmd-addr |
| dma-buf usb>cmd SCSI-COMMAND-OFFSET + |
| usb-cmd-len |
| move |
| |
| \ Send it |
| dma-buf-phys usb>data usb-buf-len |
| do-bulk-command IF |
| dma-buf usb>data usb-buf-addr usb-buf-len move |
| ELSE |
| ." USB-DISK: Bulk commad failed!" cr |
| 0 0 -1 EXIT |
| THEN |
| |
| dma-buf usb>csw to csw-addr |
| csw-addr csw>sig l@ 55534253 <> IF |
| ." USB-DISK: CSW signature invalid " cr |
| 0 0 -1 EXIT |
| THEN |
| |
| csw-addr csw>status c@ CASE |
| 0 OF ENDOF \ Good |
| 1 OF |
| usb-disk-debug? IF |
| ." USB-DISK: CSW Data residue: " |
| csw-addr csw>data-residue l@-le . cr |
| THEN |
| 0 0 8 EXIT ENDOF \ Command failed, Retry |
| dup OF 0 0 -1 EXIT ENDOF \ Anything else -> HW error |
| ENDCASE |
| |
| \ Other error status |
| csw-addr csw>status c@ dup 0<> IF |
| usb-disk-debug? IF |
| over scsi-get-sense-data |
| ." USB-DISK: Sense key [ " dup . ." ] " .sense-text |
| ." ASC,ASCQ: " . . cr |
| THEN |
| rot |
| THEN |
| ; |
| |
| \ -------------------------------- |
| \ Include the generic host helpers |
| \ -------------------------------- |
| |
| " scsi-host-helpers.fs" included |
| |
| 0 VALUE open-count |
| |
| : usb-storage-init ( -- TRUE ) |
| td-buf 0= IF |
| usb-disk-debug? IF ." USB-DISK: Allocating buffer " cr THEN |
| (dma-buf-init) |
| udev USB-MSC-INIT 0= IF |
| ." USB-DISK: Unable to initialize MSC " cr |
| FALSE |
| ELSE |
| TRUE |
| THEN |
| THEN |
| ; |
| |
| : usb-storage-cleanup |
| td-buf 0<> IF |
| usb-disk-debug? IF ." USB-DISK: Freeing buffer " cr THEN |
| (dma-buf-free) |
| udev USB-MSC-EXIT 0= IF ." USB-DISK: Unable to exit MSC " cr THEN |
| THEN |
| ; |
| |
| : open |
| usb-disk-debug? IF ." USB-DISK: Opening (count is " open-count . ." )" cr THEN |
| |
| open-count 0= IF |
| usb-storage-init IF |
| 1 to open-count true |
| ELSE ." USB-DISK initialization failed !" cr false THEN |
| ELSE |
| open-count 1 + to open-count |
| true |
| THEN |
| ; |
| |
| : close |
| usb-disk-debug? IF ." USB-DISK: Closing (count is " open-count . ." )" cr THEN |
| |
| open-count 0> IF |
| open-count 1 - dup to open-count |
| 0= IF |
| usb-storage-cleanup |
| THEN |
| THEN |
| ; |
| |
| \ ----------------------------------------------------------- |
| \ SCSI scan at boot and child device support |
| \ ----------------------------------------------------------- |
| |
| \ We use SRP luns of the form 01000000 | (target << 8) | lun |
| \ in the top 32 bits of the 64-bit LUN |
| : (set-target) |
| dup 20 >> FFFF and to lun |
| dup 30 >> FF and to port |
| to current-target |
| usb-disk-debug? IF ." USB-DISK: udev " udev . ." lun:" lun . ." port:" port . cr THEN |
| ; |
| |
| : dev-generate-srplun ( target lun-id -- srplun ) |
| swap drop port 0100 or 10 << or 20 << |
| ; |
| |
| \ FIXME: Check max transfer coming from virtio config |
| : max-transfer ( -- n ) |
| dev-max-transfer |
| ; |
| |
| \ We obtain here a unit address on the stack, since our #address-cells |
| \ is 2, the 64-bit srplun is split in two cells that we need to join |
| \ |
| \ Note: This diverges a bit from the original OF scsi spec as the two |
| \ cells are the 2 words of a 64-bit SRP LUN |
| : set-address ( srplun.lo srplun.hi -- ) |
| lxjoin (set-target) |
| usb-disk-debug? IF ." USB-DISK: udev " udev . ." lun:" lun . ." port:" port . cr THEN |
| ; |
| |
| 1 CONSTANT #target |
| : dev-max-target ( -- #target ) |
| #target |
| ; |
| |
| " scsi-probe-helpers.fs" included |
| |
| scsi-close \ no further scsi words required |
| |
| \ Set scsi alias if none is set yet |
| : setup-alias |
| s" scsi" find-alias 0= IF |
| s" scsi" get-node node>path set-alias |
| ELSE |
| drop |
| THEN |
| ; |
| |
| : usb-storage-init-and-scan ( -- ) |
| usb-disk-debug? IF ." Initializing usb-disk: udev " udev . cr THEN |
| |
| \ Create instance for scanning: |
| 0 0 get-node open-node ?dup 0= IF EXIT THEN |
| my-self >r |
| dup to my-self |
| |
| hcitype |
| CASE |
| 1 OF 4000 TO dev-max-transfer ENDOF \ OHCI |
| 2 OF 10000 TO dev-max-transfer ENDOF \ EHCI |
| 3 OF F000 TO dev-max-transfer ENDOF \ XHCI |
| ENDCASE |
| usb-storage-init |
| scsi-find-disks |
| setup-alias |
| usb-storage-cleanup |
| \ Close the temporary instance: |
| close-node |
| r> to my-self |
| ; |
| |
| ." USB Storage " cr |
| : usb-scsi-add-disk |
| " scsi-disk.fs" included |
| ; |
| |
| usb-scsi-add-disk |
| usb-storage-init-and-scan |
| |
| finish-device |