From db05ce30603758d3d4e4d8f1cd16492c1cdd7b16 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Tue, 10 Oct 2017 01:43:33 -0400 Subject: [PATCH] Added usbds example. --- usb/usbds/Makefile | 138 ++++++++++++++++++++++++++++++++++++++++ usb/usbds/source/main.c | 133 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+) create mode 100644 usb/usbds/Makefile create mode 100644 usb/usbds/source/main.c diff --git a/usb/usbds/Makefile b/usb/usbds/Makefile new file mode 100644 index 0000000..f093e53 --- /dev/null +++ b/usb/usbds/Makefile @@ -0,0 +1,138 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITA64)),) +$(error "Please set DEVKITA64 in your environment. export DEVKITA64=DEVKITA64") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITA64)/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the input directory containing data copied into exefs, normally should only contain "main.npdm". +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include +EXEFS_SRC := exefs_src + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -fPIE + +CFLAGS := -g -Wall -O2 \ + -ffast-math \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).pfs0 : $(OUTPUT).nso + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/usb/usbds/source/main.c b/usb/usbds/source/main.c new file mode 100644 index 0000000..ea08a70 --- /dev/null +++ b/usb/usbds/source/main.c @@ -0,0 +1,133 @@ +#include +#include +#include + +#include + +//Example for usbds, see libnx usb.h. Switch-as-device<>host USB comms. +//Linux detects this as a serial device. +//Data transfer over USB works fine with this, however data isn't returned by "/dev/{device}" (depending on what is used for reading it) due to the header(?) from the device->host transfer not being valid. + +Result usbds_test(u8 *tmpbuf) +{ + Result ret=0; + s32 tmpindex=0; + Handle usb_state_event; + UsbDsInterface* interface = NULL; + UsbDsEndpoint *endpoint_in = NULL, *endpoint_out = NULL; + + struct usb_interface_descriptor interface_descriptor = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USBDS_DEFAULT_InterfaceNumber, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC, + }; + + struct usb_endpoint_descriptor endpoint_descriptor_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_IN, + .bmAttributes = USB_TRANSFER_TYPE_BULK, + .wMaxPacketSize = 0x40, + }; + + struct usb_endpoint_descriptor endpoint_descriptor_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_OUT, + .bmAttributes = USB_TRANSFER_TYPE_BULK, + .wMaxPacketSize = 0x40, + }; + + usb_state_event = usbDsGetStateChangeEvent(); + + //Setup interface. + ret = usbDsGetDsInterface(&interface, &interface_descriptor, "usb"); + if(R_FAILED(ret))return ret; + + //Setup endpoints. + ret = usbDsInterface_GetDsEndpoint(interface, &endpoint_in, &endpoint_descriptor_in);//device->host + if(R_FAILED(ret))return ret; + + ret = usbDsInterface_GetDsEndpoint(interface, &endpoint_out, &endpoint_descriptor_out);//host->device + if(R_FAILED(ret))return ret; + + ret = usbDsInterface_EnableInterface(interface); + if(R_FAILED(ret))return ret; + + //Wait for usb comms init to start, which includes waiting for the usb cable to be inserted if it's not already. + svcWaitSynchronization(&tmpindex, &usb_state_event, 1, U64_MAX); + svcClearEvent(usb_state_event); + + svcSleepThread(5000000000);//The above will be signalled before the endpoints are ready for use, so just delay 5s (there's currently no known way to properly wait for endpoints-ready). + + memset(tmpbuf, 0, 0x1000); + tmpbuf[0] = 0x11; + tmpbuf[1] = 0x1; + char *strptr = "Hello World!\n"; + strncpy((char*)&tmpbuf[2], strptr, 0x1000-2); + + //Start a device->host transfer. + ret = usbDsEndpoint_PostBufferAsync(endpoint_in, tmpbuf, 2+strlen(strptr), NULL); + if(R_FAILED(ret))return ret; + //Wait for the transfer to finish. + svcWaitSynchronization(&tmpindex, &endpoint_in->CompletionEvent, 1, U64_MAX); + svcClearEvent(endpoint_in->CompletionEvent); + + //Start a host->device transfer. + ret = usbDsEndpoint_PostBufferAsync(endpoint_out, tmpbuf, 0x200, NULL); + if(R_FAILED(ret))return ret; + + //Wait for the transfer to finish. + svcWaitSynchronization(&tmpindex, &endpoint_out->CompletionEvent, 1, U64_MAX); + svcClearEvent(endpoint_out->CompletionEvent); + + memcpy(&tmpbuf[0x400], tmpbuf, 0x200-2); + tmpbuf[0] = 0x11; + tmpbuf[1] = 0x1; + memcpy(&tmpbuf[2], &tmpbuf[0x400], 0x200-2); + + //Start a device->host transfer. + ret = usbDsEndpoint_PostBufferAsync(endpoint_in, tmpbuf, 0x200, NULL); + if(R_FAILED(ret))return ret; + + //Wait for the transfer to finish. + svcWaitSynchronization(&tmpindex, &endpoint_in->CompletionEvent, 1, U64_MAX); + svcClearEvent(endpoint_in->CompletionEvent); + + return 0; +} + +int main(int argc, char **argv) +{ + Result ret; + + usbDsDeviceInfo deviceinfo = { + .idVendor = 0x0403, // "Future Technology Devices International, Ltd" + .idProduct = 0x6001, // "FT232 USB-Serial (UART) IC" + .bcdDevice = 0x0200, + .Manufacturer = "libnx", + .Product = "usbds-example", + .SerialNumber = "1337", + }; + + ret = usbDsInitialize(USBCOMPLEXID_Default, &deviceinfo); + + if (R_SUCCEEDED(ret)) { + u8 *tmpbuf = memalign(0x1000, 0x1000);//The buffer for PostBufferAsync commands must be 0x1000-byte aligned. + if(tmpbuf==NULL)ret = -4; + + if (R_SUCCEEDED(ret)) ret = usbds_test(tmpbuf); + + usbDsExit(); + } + + if(R_FAILED(ret))fatalSimple(ret); + + svcSleepThread(5000000000);//Delay 5s + + return 0; +} +