Xavier NX device works as USB drive

I have a Xavier NX device, I want it to work like a USB drive when I connect it to a PC because I want to copy some files from one NX folder to my PC. How should I do to make it work?

Thanks

There is already an example in place. This does require using the micro-OTG connector with a micro-B USB cable. To see what is already there (which is a read-only example), on your host PC, monitor “dmesg --follow”, and then plug in the cable and watch the messages being logged. One “virtual” device the Jetson pretends to be is basically a network router, and this is how the host PC can use ssh to talk to the Jetson at address “192.168.55.1”. There is also serial UART content. The other log lines are that of mass storage.

Note that this can also be demonstrated by plugging the NX’s own micro-B to full-sized type-A connector…basically using USB from the Jetson directly to the Jetson to see the same thing (though it isn’t particularly useful). An example log:

[  132.483035] cdc_ncm 1-2.1:1.5: MAC-Address: 12:1e:0f:ea:dc:5a
[  132.483051] IPv6: ADDRCONF(NETDEV_CHANGE): usb0: link becomes ready
[  132.483992] cdc_ncm 1-2.1:1.5 usb1: register 'cdc_ncm' at usb-3610000.xhci-2.1, CDC NCM, 12:1e:0f:ea:dc:5a
[  132.550837] tegra-xudc-new 3550000.xudc: setup request failed: -95
[  132.553925] rndis_host 1-2.1:1.0 usb2: register 'rndis_host' at usb-3610000.xhci-2.1, RNDIS device, 56:d0:0b:d2:17:78
[  132.554937] usbcore: registered new interface driver rndis_host
[  132.555826] cdc_acm 1-2.1:1.2: ttyACM0: USB ACM device
[  132.561336] usbcore: registered new interface driver cdc_acm
[  132.561345] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[  132.604151] IPv6: ADDRCONF(NETDEV_UP): usb2: link is not ready
[  132.625725] IPv6: ADDRCONF(NETDEV_UP): usb1: link is not ready
[  132.626046] IPv6: ADDRCONF(NETDEV_UP): usb1: link is not ready
[  132.664344] cdc_ncm 1-2.1:1.5 usb1: 425 mbit/s downlink 425 mbit/s uplink
[  132.696338] cdc_ncm 1-2.1:1.5 usb1: 425 mbit/s downlink 425 mbit/s uplink
[  132.728355] cdc_ncm 1-2.1:1.5 usb1: network connection: connected
[  132.728422] IPv6: ADDRCONF(NETDEV_CHANGE): usb1: link becomes ready
[  133.467271] scsi 0:0:0:0: Direct-Access     Linux    File-Stor Gadget 0409 PQ: 0 ANSI: 2
[  133.469167] sd 0:0:0:0: [sda] 32768 512-byte logical blocks: (16.8 MB/16.0 MiB)
[  133.469597] sd 0:0:0:0: [sda] Write Protect is on
[  133.469699] sd 0:0:0:0: [sda] Mode Sense: 0f 00 80 00
[  133.469948] sd 0:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[  133.478217]  sda:
[  133.479909] sd 0:0:0:0: [sda] Attached SCSI removable disk
[  134.576969] l4tbr0: port 2(usb0) entered blocking state
[  134.576979] l4tbr0: port 2(usb0) entered forwarding state
[  134.577075] l4tbr0: port 1(rndis0) entered blocking state
[  134.577081] l4tbr0: port 1(rndis0) entered forwarding state
[  134.577265] IPv6: ADDRCONF(NETDEV_UP): l4tbr0: link is not ready
[  134.577290] IPv6: ADDRCONF(NETDEV_CHANGE): l4tbr0: link becomes ready

If I remove the log lines for everything but the mass storage and ignore network and serial UART device log lines:

[  133.467271] scsi 0:0:0:0: Direct-Access     Linux    File-Stor Gadget 0409 PQ: 0 ANSI: 2
[  133.469167] sd 0:0:0:0: [sda] 32768 512-byte logical blocks: (16.8 MB/16.0 MiB)
[  133.469597] sd 0:0:0:0: [sda] Write Protect is on
[  133.469699] sd 0:0:0:0: [sda] Mode Sense: 0f 00 80 00
[  133.469948] sd 0:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[  133.478217]  sda:
[  133.479909] sd 0:0:0:0: [sda] Attached SCSI removable disk

The content is not actually related to Jetsons, and is in fact part of the Linux “gadget” API which makes “standard” devices easier to emulate (the “gadget” API is a framework to make creating fake USB devices easier, but it is not a complete system without the user working on it). To see how the example devices were plugged in look here:
/opt/nvidia/l4t-usb-device-mode

In that content note that the file “filesystem.img” is actually a loopback mounted and formatted “pretend” partition. It contains a README file. If you run the command “lsblk -f”, then you will see something like this for the loopback mount:

NAME         FSTYPE LABEL      UUID                                 MOUNTPOINT
loop0        vfat   L4T-README 9C1C-F190                            
sda          vfat   L4T-README 9C1C-F190     

loop0” makes the file pretend to be a partition, and since it was formatted as VFAT, the filesystem on that loop device shows as “/dev/sda” (in this example). That partition can be replaced with a real partition, or else mounted somewhere (there is currently no mount point) and written to or read from.

Note that in the directory I listed the script “nv-l4t-usb-device-mode-config.sh” sets this up (there is also a script for stopping that device). You should examine that script and use it as a starting point.

I have to emphasize that the Linux “gadget” API is only a framework. It is much easier to present a read-only loopback mounted filesystem than it is to provide access to a real running partition which is part of the system. Once you go that far you must deal with permissions and filesystem type. Note that most anything can deal with the VFAT partition type, but the real Linux partitions of the system are type ext4, and to deal with that you need Linux. Thus, if you tried to use an existing partition which is part of the operating system for your data, then you have two new issues:

  • User permission translation.
  • Whether or not the device using the system must be Linux (if ext4), versus being able to use almost anything else (if type VFAT) at the cost of needing a completely different filesystem which is not the main operating system’s ext4 partition.

For more information on the Linux gadget API:
https://www.kernel.org/doc/html/v4.19/driver-api/usb/gadget.html

Thanks your information.

Another question is, if I have had a partition (EXT4), can I export this partition as USB storage to PC?

You can, but keep in mind the only operating system which can understand this is Linux. For example, Windows would see it, but would have no way of understanding it and would end up suggesting to reformat the partition to VFAT or NTFS.

ok. If I format the partition with NTFS, windows can see it and use it.
But how do I export NTFS partition as USB storage? /opt/nvidia/l4t-usb-device-mode looks to use the filesystem.img.

This would be via a customized use of the Gadget API. There is an example already in place, but I’ll explain more about that example.

When fully booted the default for Jetsons is that the micro-OTG USB port, when having a micro-B plug inserted, changes to device mode (versus normal port function as host mode). There are certain generic USB drivers which manufacturers can use, and then the manufacturer does not need to provide a custom driver. An example is that there are many USB mass storage devices which will “just work” when plugged in, and don’t need a special driver. This is because the manufacturer chose to follow the standards for the generic driver.

In “gadget” terms you can fill in various parameters for the gadget configuration, and then the Jetson will pretend to be that USB device which uses a generic driver at the other end (the host won’t need a custom driver). The location where the files are when a Jetson sets up its demonstration of this is at:
/opt/nvidia/l4t-usb-device-mode

When you are able to go to your host PC and access the Jetson at ethernet address 192.168.55.1 you’ve found one gadget…the Jetson pretending to be a router. You’ll notice that if you monitor (on the host) the command “dmesg --follow” as you plug in the USB cable, that there is also a storage device being emulated. That example is in the same above directory, and basically names a fake partition as the data source. That fake partition is just a loopback mounted file, “filesystem.img”. One can trade that loopback mounted image for a real partition. There might be some edits to make, but the basics are already there. Use that as the example.

Note that you could keep the original fake mass storage, and add a new one, or else edit the original.

@linuxdev ,

That example is in the same above directory, and basically names a fake partition as the data source.
→ I think tihs is what I want to find.

I looked at the scripts under /opt/nvidia/l4t-usb-device-mode, found below in nv-l4t-usb-device-mode-start.sh.

if [ ${enable_ums} -eq 1 ]; then
cfg_str="${cfg_str}+UMS"
func=functions/mass_storage.0
mkdir -p “${func}”
ln -sf “${func}” “${cfg}”
# Prevent users from corrupting the disk image; make it read-only
echo 1 > “${func}/lun.0/ro”
echo “${fs_img}” > “${func}/lun.0/file”
fi

fs_img is the filesystem.img. I think “${func}/lun.0/file” can only be filled with a file name instead of partition. I did not find I can set data source with a partition somewhere.
At the last of nv-l4t-usb-device-mode-start.sh, it is /sbin/losetup -f -r “${fs_img}” to bind fs_img with loop# device. There should be some place for USB to export the loop$ device as USB storage, do you konw where it is?

@harry_xiaye
you may consider
mounting as sshfs or fuse

A partition will have a device special file, and this can be used directly. An example being “/dev/sda1”. The file in the example is not directly mounted, but the loopback device created by losetup is what is actually used. So for example, if the losetup results in the creation of “/dev/loop0”, then it is “loop0” which is equivalent to “sda1”. When losetup runs it is typical for the creation of the device to also pass the device name (e.g., “/dev/loop0”) to the script. You won’t see “loop0” directly mentioned because it is extracted during the run of “losetup”.

On a TX2 I ran this command from “/opt/nvidia/l4t-usb-device-mode”:
egrep 'losetup' *

This was in the result:

nv-l4t-usb-device-mode.sh:/sbin/losetup -f -r "${fs_img}"
nv-l4t-usb-device-mode-stop.sh:loop_devs=$(losetup | grep "${fs_img}" | awk '{print $1}')
nv-l4t-usb-device-mode-stop.sh:        /sbin/losetup -d "${loop_dev}"
sample_nv-l4t-usb-device-mode.sh:#/sbin/losetup -f -r "${fs_img}"
sample_nv-l4t-usb-device-mode.sh:/sbin/losetup -f "${fs_img}"
start.sh:#/sbin/losetup -f -r "${fs_img}"
start.sh:/sbin/losetup -f "${fs_img}"
stop.sh:loop_devs=$(losetup | grep "${fs_img}" | awk '{print $1}')
stop.sh:        /sbin/losetup -d "${loop_dev}"

If you look for cases where a return value is stored (e.g., “loop_dev”), then this is the device actually passed to the gadget as the partition. I have not altered mine as a test, but if you were to stop the losetup, and directly replace “loop_dev” with “/dev/sda1”, then this would probably work.

Note that there is both a list of all loop devices, “loop_devs”, and the device which is actually chosen, a subset of all devices, “loop_dev”, and it is this latter which is basically a “synthetic partition”, and which could instead be changed as an actual partition.

You could in fact create an equivalent script which does not search for loop devices and picking of one loop device in particular, and change it to search for USB SATA drives, and pick the one dedicated to this purpose. You could go so far as to mount it only if the particular UUID of that partition is found.

I searched “losetup” and got below result.
nx:/opt/nvidia/l4t-usb-device-mode$ grep losetup *
nv-l4t-usb-device-mode-start.sh:/sbin/losetup -f -r “${fs_img}”
nv-l4t-usb-device-mode-stop.sh:loop_devs=$(losetup | grep “${fs_img}” | awk ‘{print $1}’)
nv-l4t-usb-device-mode-stop.sh: /sbin/losetup -d “${loop_dev}”

The loop_dev is only used when losetup -d to remove the device when stop. When the service is started, it does not use the ${loop_dev}.
Check below script, it will use file image but ${loop_dev} or any partition.

if [ ${enable_ums} -eq 1 ]; then
cfg_str="${cfg_str}+UMS"
func=functions/mass_storage.0
mkdir -p “${func}”
ln -sf “${func}” “${cfg}”
# Prevent users from corrupting the disk image; make it read-only
echo 1 > “${func}/lun.0/ro”
echo “${fs_img}” > “${func}/lun.0/file”
fi

Basically the losetup command can tell the invoking process which device special file was created (e.g., “/dev/loop0”). You don’t need losetup unless you are making a file emulate a partition. If you have an actual partition, then you name that device special file and skip all losetup use. The example in “/opt/nvidia/l4t-usb-device-mode” is quite useful, but you have to get rid of the “faking” of a file looking like a partition since you have a real partition in any other case.

If you are trying to make a file appear as a partition, then you are back to using losetup.

So I can remove losetup and set “${func}/lun.0/file” with actual partition device name like /dev/sda1, right?

I have not looked at the specific code (I’m not editing my Jetson), but basically, if you find a case where losetup covers a device, then eliminate that. Then look where the loop device is actually used, and replace that with your actual partition (e.g., “/dev/sda1”).

Note that there are places where the image is told to be read-only. It is possible you might wish to not do this, and it is also possible that even if you do want this, then doing so on an actual partition would differ in comparison to doing so on a loop device. I have not studied it close enough to be able to answer, but the simple starting case would be to comment out that code for testing (not sure if this is what the “${func}/lun.0/file” is for or not, I did not look close enough).

It does work.

I set “${func}/lun.0/file” with partition “/dev/nvme0n1p2”, remove the losetup command in the script. I can see USB drive pop up when I connect USB cable on win10, also can access it from win10.

The only issue is if I mounted the “/dev/nvme0n1p2” on NX system, when I connect the USB cable, win10 will pop up a warning windows like “The drive has problems, please scan it to fix the issues.”. But I still can access the USB drive without any problems.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.