diff --git a/Documentation/NuttShell.html b/Documentation/NuttShell.html index 987f8a71779..af8eeba20b0 100644 --- a/Documentation/NuttShell.html +++ b/Documentation/NuttShell.html @@ -8,7 +8,7 @@

NuttShell (NSH)

-

Last Updated: February 5, 2017

+

Last Updated: April 5, 2017

@@ -2708,11 +2708,13 @@ nsh>

Command Syntax:

Synopsis. - Set the environment variable <name> to the string <value>. + Set the environment variable <name> to the string <value> and or set NSH + parser control options. For example, + For example,

+

+ Set the 'exit on error control' and/or 'print a trace' of commands when parsing + scripts in NSH. The settinngs are in effect from the point of exection, until + they are changed again, or in the case of the init script, the settings are + returned to the default settings when it exits. Included child scripts will run + with the parents settings and changes made in the child script will effect the + parent on return. +

+

+ Use 'set -e' to enable and 'set +e' to disable (ignore) the exit condition on commands. + The default is -e. Errors cause script to exit. +

+

+ Use 'set -x' to enable and 'set +x' to disable (silence) printing a trace of the script + commands as they are ececuted. + The default is +x. No printing of a trace of script commands as they are executed. + +

+ + Example 1 - no exit on command not found + + + Example 2 - will exit on command not found + + + Example 3 - will exit on command not found, and print a trace of the script commmands + + + Example 4 - will exit on command not found, and print a trace of the script commmands + and set foobar to foovalue. + + +
diff --git a/Makefile.unix b/Makefile.unix index 220740c5bd3..d816947a38e 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -498,7 +498,7 @@ do_qconfig: dirlinks apps_preconfig qconfig: do_qconfig clean_context -gconfig: dirlinks apps_preconfig +do_gconfig: dirlinks apps_preconfig $(Q) APPSDIR=${CONFIG_APPS_DIR} kconfig-gconf Kconfig gconfig: do_gconfig clean_context diff --git a/README.txt b/README.txt index 59648d01f4c..5d3721f4c50 100644 --- a/README.txt +++ b/README.txt @@ -464,9 +464,9 @@ Notes about Header Files Certain header files, such as setjmp.h, stdarg.h, and math.h, may still be needed from your toolchain and your compiler may not, however, be able - to find these if you compile NuttX without using standard header file. - If that is the case, one solution is to copy those header file from - your toolchain into the NuttX include directory. + to find these if you compile NuttX without using standard header files + (ie., with -nostdinc). If that is the case, one solution is to copy + those header file from your toolchain into the NuttX include directory. Duplicated Header Files. diff --git a/arch/arm/include/stm32/chip.h b/arch/arm/include/stm32/chip.h index 0844c5a09fb..df921df4b89 100644 --- a/arch/arm/include/stm32/chip.h +++ b/arch/arm/include/stm32/chip.h @@ -64,12 +64,16 @@ * STM32L15XCX -- 48-pins * STM32L15XRX -- 64-pins * STM32L15XVX -- 100-pins + * STM32L15XZX -- 144-pins * * STM32L15XX6 -- 32KB FLASH, 10KB SRAM, 4KB EEPROM * STM32L15XX8 -- 64KB FLASH, 10KB SRAM, 4KB EEPROM * STM32L15XXB -- 128KB FLASH, 16KB SRAM, 4KB EEPROM * * STM32L15XXC -- 256KB FLASH, 32KB SRAM, 8KB EEPROM (medium+ density) + * + * STM32L16XXD -- 384KB FLASH, 48KB SRAM, 12KB EEPROM (high density) + * STM32L16XXE -- 512KB FLASH, 80KB SRAM, 16KB EEPROM (high density) */ #if defined(CONFIG_ARCH_CHIP_STM32L151C6) || defined(CONFIG_ARCH_CHIP_STM32L151C8) || \ @@ -320,7 +324,7 @@ #elif defined(CONFIG_ARCH_CHIP_STM32L152RC) # define CONFIG_STM32_STM32L15XX 1 /* STM32L151xx and STM32L152xx family */ -# define CONFIG_STM32_ENERGYLITE 1 /* STM32L EnergyLite vamily */ +# define CONFIG_STM32_ENERGYLITE 1 /* STM32L EnergyLite family */ # undef CONFIG_STM32_STM32F10XX /* STM32F10xxx family */ # undef CONFIG_STM32_LOWDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 16/32 Kbytes * and STM32L15xxx */ @@ -331,6 +335,7 @@ # undef CONFIG_STM32_CONNECTIVITYLINE /* STM32F105x and STM32F107x */ # undef CONFIG_STM32_STM32F20XX /* STM32F205x and STM32F207x */ # undef CONFIG_STM32_STM32F30XX /* STM32F30xxx family */ +# undef CONFIG_STM32_STM32F33XX /* STM32F33xxx family */ # undef CONFIG_STM32_STM32F40XX /* STM32F405xx and STM32407xx families */ # define STM32_NFSMC 0 /* No FSMC */ # define STM32_NATIM 0 /* No advanced timers */ @@ -358,17 +363,18 @@ #elif defined(CONFIG_ARCH_CHIP_STM32L162ZD) # define CONFIG_STM32_STM32L15XX 1 /* STM32L151xx and STM32L152xx family */ -# define CONFIG_STM32_ENERGYLITE 1 /* STM32L EnergyLite vamily */ +# define CONFIG_STM32_ENERGYLITE 1 /* STM32L EnergyLite family */ # undef CONFIG_STM32_STM32F10XX /* STM32F10xxx family */ # undef CONFIG_STM32_LOWDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 16/32 Kbytes * and STM32L15xxx */ # undef CONFIG_STM32_MEDIUMDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 64/128 Kbytes */ # undef CONFIG_STM32_MEDIUMPLUSDENSITY /* STM32L15xxC w/ 32/256 Kbytes */ -# define CONFIG_STM32_HIGHDENSITY 1 /* STM32F100x, STM32F101x, and STM32F103x w/ 256/512 Kbytes, STM32L16x w/ 48/384 Kbytes. */ +# define CONFIG_STM32_HIGHDENSITY 1 /* STM32L16xD w/ 48/384 Kbytes. */ # undef CONFIG_STM32_VALUELINE /* STM32F100x */ # undef CONFIG_STM32_CONNECTIVITYLINE /* STM32F105x and STM32F107x */ # undef CONFIG_STM32_STM32F20XX /* STM32F205x and STM32F207x */ # undef CONFIG_STM32_STM32F30XX /* STM32F30xxx family */ +# undef CONFIG_STM32_STM32F33XX /* STM32F33xxx family */ # undef CONFIG_STM32_STM32F40XX /* STM32F405xx and STM32407xx families */ # define STM32_NFSMC 1 /* FSMC */ # define STM32_NATIM 0 /* No advanced timers */ @@ -395,6 +401,48 @@ # define STM32_NRNG 0 /* No random number generator (RNG) */ # define STM32_NDCMI 0 /* No digital camera interface (DCMI) */ +#elif defined(CONFIG_ARCH_CHIP_STM32L162VE) +# define CONFIG_STM32_STM32L15XX 1 /* STM32L151xx and STM32L152xx family */ +# define CONFIG_STM32_ENERGYLITE 1 /* STM32L EnergyLite family */ +# undef CONFIG_STM32_STM32F10XX /* STM32F10xxx family */ +# undef CONFIG_STM32_LOWDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 16/32 Kbytes + * and STM32L15xxx */ +# undef CONFIG_STM32_MEDIUMDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 64/128 Kbytes */ +# undef CONFIG_STM32_MEDIUMPLUSDENSITY /* STM32L15xxC w/ 32/256 Kbytes */ +# define CONFIG_STM32_HIGHDENSITY 1 /* STM32L16xE w/ 80/512 Kbytes. */ +# undef CONFIG_STM32_VALUELINE /* STM32F100x */ +# undef CONFIG_STM32_CONNECTIVITYLINE /* STM32F105x and STM32F107x */ +# undef CONFIG_STM32_STM32F20XX /* STM32F205x and STM32F207x */ +# undef CONFIG_STM32_STM32F30XX /* STM32F30xxx family */ +# undef CONFIG_STM32_STM32F33XX /* STM32F33xxx family */ +# undef CONFIG_STM32_STM32F40XX /* STM32F405xx and STM32407xx families */ +# define STM32_NFSMC 0 /* No FSMC */ +# define STM32_NATIM 0 /* No advanced timers */ +# define STM32_NGTIM 4 /* 16-bit general timers TIM2-4 with DMA + * 32-bit general timer TIM5 with DMA */ +# define STM32_NGTIMNDMA 3 /* 16-bit general timers TIM9-11 without DMA */ +# define STM32_NBTIM 2 /* 2 basic timers: TIM6, TIM7 with DMA */ +# define STM32_NDMA 2 /* DMA1, 12-channels */ +# define STM32_NSPI 3 /* SPI1-3 */ +# define STM32_NI2S 2 /* I2S1-2, overlapping with SPI2-3 */ +# define STM32_NUSART 5 /* USART1-3, UART4-5 */ +# define STM32_NI2C 2 /* I2C1-2 */ +# define STM32_NCAN 0 /* No CAN */ +# define STM32_NSDIO 0 /* No SDIO */ +# define STM32_NLCD 1 /* LCD 4x44, 8x40*/ +# define STM32_NUSBOTG 1 /* USB OTG FS/HS (only USB 2.0 device) */ +# define STM32_NGPIO 83 /* GPIOA-G,H */ + +# define STM32_NADC 1 /* ADC1, up to 40-channels (medium+ and high density). See for more information RM0038 Reference manual */ +# define STM32_NDAC 1 /* DAC 1, 2 channels. See for more information RM0038 Reference manual */ + /* (2) Comparators */ +# define STM32_NCAPSENSE 23 /* Capacitive sensing channels */ +# define STM32_NCRC 1 /* CRC */ +# define STM32_NETHERNET 0 /* No ethernet */ +# define STM32_NRNG 0 /* No random number generator (RNG) */ +# define STM32_NDCMI 0 /* No digital camera interface (DCMI) */ + + /* STM32 F100 Value Line ************************************************************/ #elif defined(CONFIG_ARCH_CHIP_STM32F100C8) || defined(CONFIG_ARCH_CHIP_STM32F100CB) \ @@ -562,7 +610,7 @@ #elif defined(CONFIG_ARCH_CHIP_STM32F102CB) # undef CONFIG_STM32_STM32L15XX /* STM32L151xx and STM32L152xx family */ -# undef CONFIG_STM32_ENERGYLITE /* STM32L EnergyLite vamily */ +# undef CONFIG_STM32_ENERGYLITE /* STM32L EnergyLite family */ # define CONFIG_STM32_STM32F10XX 1 /* STM32F10xx family */ # undef CONFIG_STM32_LOWDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 16/32 Kbytes */ # define CONFIG_STM32_MEDIUMDENSITY 1 /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 64/128 Kbytes */ @@ -1129,7 +1177,7 @@ #elif defined(CONFIG_ARCH_CHIP_STM32F302K6) || defined(CONFIG_ARCH_CHIP_STM32F302K8) # undef CONFIG_STM32_STM32L15XX /* STM32L151xx and STM32L152xx family */ -# undef CONFIG_STM32_ENERGYLITE /* STM32L EnergyLite vamily */ +# undef CONFIG_STM32_ENERGYLITE /* STM32L EnergyLite family */ # undef CONFIG_STM32_STM32F10XX /* STM32F10xxx family */ # undef CONFIG_STM32_LOWDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 16/32 Kbytes */ # undef CONFIG_STM32_MEDIUMDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 64/128 Kbytes */ @@ -1668,7 +1716,7 @@ #elif defined(CONFIG_ARCH_CHIP_STM32F373C8) || defined(CONFIG_ARCH_CHIP_STM32F373CB) || defined(CONFIG_ARCH_CHIP_STM32F373CC) # undef CONFIG_STM32_STM32L15XX /* STM32L151xx and STM32L152xx family */ -# undef CONFIG_STM32_ENERGYLITE /* STM32L EnergyLite vamily */ +# undef CONFIG_STM32_ENERGYLITE /* STM32L EnergyLite family */ # undef CONFIG_STM32_STM32F10XX /* STM32F10xxx family */ # undef CONFIG_STM32_LOWDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 16/32 Kbytes */ # undef CONFIG_STM32_MEDIUMDENSITY /* STM32F100x, STM32F101x, STM32F102x and STM32F103x w/ 64/128 Kbytes */ diff --git a/arch/arm/src/stm32/chip/stm32_flash.h b/arch/arm/src/stm32/chip/stm32_flash.h index 6bc1085bd18..13c966c05c2 100644 --- a/arch/arm/src/stm32/chip/stm32_flash.h +++ b/arch/arm/src/stm32/chip/stm32_flash.h @@ -59,16 +59,33 @@ #if defined(CONFIG_STM32_FLASH_CONFIG_DEFAULT) # if defined(CONFIG_STM32_STM32L15XX) +# if defined(CONFIG_STM32_HIGHDENSITY) -/* The STM32 L15xx/L16xx can support up to 384KB of FLASH. (In reality, supported - * L15xx parts have no more than 128KB). The program memory block is divided into - * 96 sectors of 4 Kbytes each, and each sector is further split up into 16 pages of - * 256 bytes each. The sector is the write protection granularity. In total, the +/* Different STM32L1xxx MCU version are now called by different 'categories' instead + * of 'densities'. Cat.5 MCU can have up to 512KB of FLASH. STM32L1xxx also have + * data EEPROM, up to 16KB. + */ + +# define STM32_FLASH_NPAGES 2048 +# define STM32_FLASH_PAGESIZE 256 +# else + +/* The STM32 (< Cat.5) L15xx/L16xx can support up to 384KB of FLASH. (In reality, most + * supported L15xx parts have no more than 128KB). The program memory block is divided + * into 96 sectors of 4 Kbytes each, and each sector is further split up into 16 pages + * of 256 bytes each. The sector is the write protection granularity. In total, the * program memory block contains 1536 pages. */ -# define STM32_FLASH_NPAGES 1536 -# define STM32_FLASH_PAGESIZE 256 +# define STM32_FLASH_NPAGES 1536 +# define STM32_FLASH_PAGESIZE 256 +# endif + + /* Maximum EEPROM size on Cat.5 MCU. TODO: this should be in chip config. */ + +# ifndef STM32_EEPROM_SIZE +# define STM32_EEPROM_SIZE (16 * 1024) +# endif # elif defined(CONFIG_STM32_LOWDENSITY) # define STM32_FLASH_NPAGES 32 @@ -201,27 +218,40 @@ # elif defined(CONFIG_STM32_FLASH_CONFIG_I) # endif # endif -#endif +#endif /* !defined(CONFIG_STM32_FLASH_CONFIG_DEFAULT) */ #ifdef STM32_FLASH_PAGESIZE # define STM32_FLASH_SIZE (STM32_FLASH_NPAGES * STM32_FLASH_PAGESIZE) -#endif /* def STM32_FLASH_PAGESIZE */ +#endif /* Register Offsets *****************************************************************/ -#define STM32_FLASH_ACR_OFFSET 0x0000 -#define STM32_FLASH_KEYR_OFFSET 0x0004 -#define STM32_FLASH_OPTKEYR_OFFSET 0x0008 -#define STM32_FLASH_SR_OFFSET 0x000c -#define STM32_FLASH_CR_OFFSET 0x0010 - -#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined(CONFIG_STM32_STM32F33XX) || defined(CONFIG_STM32_STM32F37XX) -# define STM32_FLASH_AR_OFFSET 0x0014 -# define STM32_FLASH_OBR_OFFSET 0x001c -# define STM32_FLASH_WRPR_OFFSET 0x0020 -#elif defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) -# define STM32_FLASH_OPTCR_OFFSET 0x0014 +#define STM32_FLASH_ACR_OFFSET 0x0000 +#if defined(CONFIG_STM32_STM32L15XX) +# define STM32_FLASH_PECR_OFFSET 0x0004 +# define STM32_FLASH_PDKEYR_OFFSET 0x0008 +# define STM32_FLASH_PEKEYR_OFFSET 0x000c +# define STM32_FLASH_PRGKEYR_OFFSET 0x0010 +# define STM32_FLASH_OPTKEYR_OFFSET 0x0014 +# define STM32_FLASH_SR_OFFSET 0x0018 +# define STM32_FLASH_OBR_OFFSET 0x001c +# define STM32_FLASH_WRPR1_OFFSET 0x0020 +# define STM32_FLASH_WRPR2_OFFSET 0x0080 +# define STM32_FLASH_WRPR3_OFFSET 0x0084 +# define STM32_FLASH_WRPR4_OFFSET 0x0088 +#else +# define STM32_FLASH_KEYR_OFFSET 0x0004 +# define STM32_FLASH_OPTKEYR_OFFSET 0x0008 +# define STM32_FLASH_SR_OFFSET 0x000c +# define STM32_FLASH_CR_OFFSET 0x0010 +# if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ + defined(CONFIG_STM32_STM32F33XX) || defined(CONFIG_STM32_STM32F37XX) +# define STM32_FLASH_AR_OFFSET 0x0014 +# define STM32_FLASH_OBR_OFFSET 0x001c +# define STM32_FLASH_WRPR_OFFSET 0x0020 +# elif defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +# define STM32_FLASH_OPTCR_OFFSET 0x0014 +# endif #endif #if defined(CONFIG_STM32_STM32F427) || defined(CONFIG_STM32_STM32F429) @@ -230,22 +260,36 @@ /* Register Addresses ***************************************************************/ -#define STM32_FLASH_ACR (STM32_FLASHIF_BASE+STM32_FLASH_ACR_OFFSET) -#define STM32_FLASH_KEYR (STM32_FLASHIF_BASE+STM32_FLASH_KEYR_OFFSET) -#define STM32_FLASH_OPTKEYR (STM32_FLASHIF_BASE+STM32_FLASH_OPTKEYR_OFFSET) -#define STM32_FLASH_SR (STM32_FLASHIF_BASE+STM32_FLASH_SR_OFFSET) -#define STM32_FLASH_CR (STM32_FLASHIF_BASE+STM32_FLASH_CR_OFFSET) +#define STM32_FLASH_ACR (STM32_FLASHIF_BASE+STM32_FLASH_ACR_OFFSET) +#if defined(CONFIG_STM32_STM32L15XX) +# define STM32_FLASH_PECR (STM32_FLASHIF_BASE+STM32_FLASH_PECR_OFFSET) +# define STM32_FLASH_PDKEYR (STM32_FLASHIF_BASE+STM32_FLASH_PDKEYR_OFFSET) +# define STM32_FLASH_PEKEYR (STM32_FLASHIF_BASE+STM32_FLASH_PEKEYR_OFFSET) +# define STM32_FLASH_PRGKEYR (STM32_FLASHIF_BASE+STM32_FLASH_PRGKEYR_OFFSET) +# define STM32_FLASH_OPTKEYR (STM32_FLASHIF_BASE+STM32_FLASH_OPTKEYR_OFFSET) +# define STM32_FLASH_SR (STM32_FLASHIF_BASE+STM32_FLASH_SR_OFFSET) +# define STM32_FLASH_OBR (STM32_FLASHIF_BASE+STM32_FLASH_OBR_OFFSET) +# define STM32_FLASH_WRPR1 (STM32_FLASHIF_BASE+STM32_FLASH_WRPR1_OFFSET) +# define STM32_FLASH_WRPR2 (STM32_FLASHIF_BASE+STM32_FLASH_WRPR2_OFFSET) +# define STM32_FLASH_WRPR3 (STM32_FLASHIF_BASE+STM32_FLASH_WRPR3_OFFSET) +# define STM32_FLASH_WRPR4 (STM32_FLASHIF_BASE+STM32_FLASH_WRPR4_OFFSET) +#else +# define STM32_FLASH_KEYR (STM32_FLASHIF_BASE+STM32_FLASH_KEYR_OFFSET) +# define STM32_FLASH_OPTKEYR (STM32_FLASHIF_BASE+STM32_FLASH_OPTKEYR_OFFSET) +# define STM32_FLASH_SR (STM32_FLASHIF_BASE+STM32_FLASH_SR_OFFSET) +# define STM32_FLASH_CR (STM32_FLASHIF_BASE+STM32_FLASH_CR_OFFSET) -#if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined(CONFIG_STM32_STM32F33XX) || defined(CONFIG_STM32_STM32F37XX) -# define STM32_FLASH_AR (STM32_FLASHIF_BASE+STM32_FLASH_AR_OFFSET) -# define STM32_FLASH_OBR (STM32_FLASHIF_BASE+STM32_FLASH_OBR_OFFSET) -# define STM32_FLASH_WRPR (STM32_FLASHIF_BASE+STM32_FLASH_WRPR_OFFSET) -#elif defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) -# define STM32_FLASH_OPTCR (STM32_FLASHIF_BASE+STM32_FLASH_OPTCR_OFFSET) -#endif -#if defined(CONFIG_STM32_STM32F427) || defined(CONFIG_STM32_STM32F429) -# define STM32_FLASH_OPTCR1 (STM32_FLASHIF_BASE+STM32_FLASH_OPTCR1_OFFSET) +# if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ + defined(CONFIG_STM32_STM32F33XX) || defined(CONFIG_STM32_STM32F37XX) +# define STM32_FLASH_AR (STM32_FLASHIF_BASE+STM32_FLASH_AR_OFFSET) +# define STM32_FLASH_OBR (STM32_FLASHIF_BASE+STM32_FLASH_OBR_OFFSET) +# define STM32_FLASH_WRPR (STM32_FLASHIF_BASE+STM32_FLASH_WRPR_OFFSET) +# elif defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +# define STM32_FLASH_OPTCR (STM32_FLASHIF_BASE+STM32_FLASH_OPTCR_OFFSET) +# endif +# if defined(CONFIG_STM32_STM32F427) || defined(CONFIG_STM32_STM32F429) +# define STM32_FLASH_OPTCR1 (STM32_FLASHIF_BASE+STM32_FLASH_OPTCR1_OFFSET) +# endif #endif /* Register Bitfield Definitions ****************************************************/ @@ -303,6 +347,34 @@ # define FLASH_SR_PGPERR (1 << 6) /* Bit 6: Programming parallelism error */ # define FLASH_SR_PGSERR (1 << 7) /* Bit 7: Programming sequence error */ # define FLASH_SR_BSY (1 << 16) /* Bit 16: Busy */ +#elif defined(CONFIG_STM32_STM32L15XX) +# define FLASH_SR_BSY (1 << 0) /* Bit 0: Busy */ +# define FLASH_SR_EOP (1 << 1) /* Bit 1: End of operation */ +# define FLASH_SR_ENDHV (1 << 2) /* Bit 2: End of high voltage */ +# define FLASH_SR_READY (1 << 3) /* Bit 3: Flash memory module ready after low power mode */ +# define FLASH_SR_WRPERR (1 << 8) /* Bit 8: Write protection error */ +# define FLASH_SR_PGAERR (1 << 9) /* Bit 9: Programming alignment error */ +# define FLASH_SR_SIZERR (1 << 10) /* Bit 10: Size error */ +# define FLASH_SR_OPTVERR (1 << 11) /* Bit 11: Option validity error */ +# define FLASH_SR_OPTVERRUSR (1 << 12) /* Bit 12: Option UserValidity Error */ +# define FLASH_SR_RDERR (1 << 13) /* Bit 13: Read protected error */ +#endif + +/* Program/Erase Control Register (PECR) */ + +#if defined(CONFIG_STM32_STM32L15XX) +# define FLASH_PECR_PELOCK (1 << 0) /* Bit 0: PECR and data EEPROM lock */ +# define FLASH_PECR_PRGLOCK (1 << 1) /* Bit 1: Program memory lock */ +# define FLASH_PECR_OPTLOCK (1 << 2) /* Bit 2: Option bytes block lock */ +# define FLASH_PECR_PROG (1 << 3) /* Bit 3: Program memory selection */ +# define FLASH_PECR_DATA (1 << 4) /* Bit 4: Data EEPROM selection */ +# define FLASH_PECR_FTDW (1 << 8) /* Bit 8: Fixed time data write for Byte, Half Word and Word programming */ +# define FLASH_PECR_ERASE (1 << 9) /* Bit 9: Page or Double Word erase mode */ +# define FLASH_PECR_FPRG (1 << 10) /* Bit 10: Half Page/Double Word programming mode */ +# define FLASH_PECR_PARALLBANK (1 << 15) /* Bit 15: Parallel bank mode */ +# define FLASH_PECR_EOPIE (1 << 16) /* Bit 16: End of programming interrupt enable */ +# define FLASH_PECR_ERRIE (1 << 17) /* Bit 17: Error interrupt enable */ +# define FLASH_PECR_OBL_LAUNCH (1 << 18) /* Bit 18: Launch the option byte loading */ #endif /* Flash Control Register (CR) */ @@ -380,7 +452,6 @@ # define FLASH_OPTCR1_BFB2_SHIFT (4) /* Bits 4: Dual-bank Boot option byte */ # define FLASH_OPTCR1_BFB2_MASK (1 << FLASH_OPTCR_NWRP_SHIFT) - #endif #if defined(CONFIG_STM32_STM32F446) diff --git a/arch/arm/src/stm32/chip/stm32f40xxx_pinmap.h b/arch/arm/src/stm32/chip/stm32f40xxx_pinmap.h index 82fb74a4481..d44c664edbf 100644 --- a/arch/arm/src/stm32/chip/stm32f40xxx_pinmap.h +++ b/arch/arm/src/stm32/chip/stm32f40xxx_pinmap.h @@ -424,6 +424,10 @@ #define GPIO_I2C3_SCL_2 (GPIO_ALT|GPIO_AF4|GPIO_SPEED_50MHz|GPIO_OPENDRAIN|GPIO_PORTH|GPIO_PIN7) #define GPIO_I2C3_SDA_1 (GPIO_ALT|GPIO_AF4|GPIO_SPEED_50MHz|GPIO_OPENDRAIN|GPIO_PORTC|GPIO_PIN9) #define GPIO_I2C3_SDA_2 (GPIO_ALT|GPIO_AF4|GPIO_SPEED_50MHz|GPIO_OPENDRAIN|GPIO_PORTH|GPIO_PIN8) +#if defined(CONFIG_STM32_STM32F411) +# define GPIO_I2C3_SDA_3 (GPIO_ALT|GPIO_AF9|GPIO_SPEED_50MHz|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN4) +# define GPIO_I2C3_SDA_4 (GPIO_ALT|GPIO_AF9|GPIO_SPEED_50MHz|GPIO_OPENDRAIN|GPIO_PORTB|GPIO_PIN8) +#endif #define GPIO_I2C3_SMBA_1 (GPIO_ALT|GPIO_AF4|GPIO_SPEED_50MHz|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN9) #define GPIO_I2C3_SMBA_2 (GPIO_ALT|GPIO_AF4|GPIO_SPEED_50MHz|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN9) #if defined(CONFIG_STM32_STM32F446) diff --git a/arch/arm/src/stm32/stm32_eth.c b/arch/arm/src/stm32/stm32_eth.c index 7703a9a745e..b17077fd54d 100644 --- a/arch/arm/src/stm32/stm32_eth.c +++ b/arch/arm/src/stm32/stm32_eth.c @@ -90,9 +90,7 @@ # error "Logic to support multiple Ethernet interfaces is incomplete" #endif -/* If processing is not done at the interrupt level, then work queue support - * is required. - */ +/* Work queue support is required. */ #if !defined(CONFIG_SCHED_WORKQUEUE) # error Work queue support is required diff --git a/arch/arm/src/stm32/stm32_flash.c b/arch/arm/src/stm32/stm32_flash.c index fb1e1cac220..1dae18724c5 100644 --- a/arch/arm/src/stm32/stm32_flash.c +++ b/arch/arm/src/stm32/stm32_flash.c @@ -59,10 +59,10 @@ #include "up_arch.h" -/* Only for the STM32F[1|3|4]0xx family for now */ +/* Only for the STM32F[1|3|4]0xx family and STM32L15xx (EEPROM only) for now */ #if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined (CONFIG_STM32_STM32F40XX) + defined (CONFIG_STM32_STM32F40XX) || defined(CONFIG_STM32_STM32L15XX) #if defined(CONFIG_STM32_FLASH_CONFIG_DEFAULT) && \ (defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX)) @@ -73,15 +73,25 @@ * Pre-processor Definitions ************************************************************************************/ -#define FLASH_KEY1 0x45670123 -#define FLASH_KEY2 0xCDEF89AB +#if defined(CONFIG_STM32_STM32L15XX) +# define FLASH_KEY1 0x8C9DAEBF +# define FLASH_KEY2 0x13141516 +#else +# define FLASH_KEY1 0x45670123 +# define FLASH_KEY2 0xCDEF89AB +#endif #if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) -#define FLASH_CR_PAGE_ERASE FLASH_CR_PER -#define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPRT_ERR +# define FLASH_CR_PAGE_ERASE FLASH_CR_PER +# define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPRT_ERR #elif defined(CONFIG_STM32_STM32F40XX) -#define FLASH_CR_PAGE_ERASE FLASH_CR_SER -#define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPERR +# define FLASH_CR_PAGE_ERASE FLASH_CR_SER +# define FLASH_SR_WRITE_PROTECTION_ERROR FLASH_SR_WRPERR +#endif + +#if defined(CONFIG_STM32_STM32L15XX) +# define EEPROM_KEY1 0x89ABCDEF +# define EEPROM_KEY2 0x02030405 #endif /************************************************************************************ @@ -107,6 +117,8 @@ static inline void sem_unlock(void) sem_post(&g_sem); } +#if !defined(CONFIG_STM32_STM32L15XX) + static void flash_unlock(void) { while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) @@ -128,6 +140,8 @@ static void flash_lock(void) modifyreg32(STM32_FLASH_CR, 0, FLASH_CR_LOCK); } +#endif /* !defined(CONFIG_STM32_STM32L15XX) */ + #if defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) static void data_cache_disable(void) { @@ -146,6 +160,183 @@ static void data_cache_enable(void) } #endif /* defined(CONFIG_STM32_FLASH_WORKAROUND_DATA_CACHE_CORRUPTION_ON_RWW) */ +#if defined(CONFIG_STM32_STM32L15XX) + +static void stm32_eeprom_unlock(void) +{ + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + if (getreg32(STM32_FLASH_PECR) & FLASH_PECR_PELOCK) + { + /* Unlock sequence */ + + putreg32(EEPROM_KEY1, STM32_FLASH_PEKEYR); + putreg32(EEPROM_KEY2, STM32_FLASH_PEKEYR); + } +} + +static void stm32_eeprom_lock(void) +{ + modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PELOCK); +} + +static void flash_unlock(void) +{ + if (getreg32(STM32_FLASH_PECR) & FLASH_PECR_PRGLOCK) + { + stm32_eeprom_unlock(); + + /* Unlock sequence */ + + putreg32(FLASH_KEY1, STM32_FLASH_PRGKEYR); + putreg32(FLASH_KEY2, STM32_FLASH_PRGKEYR); + } +} + +static void flash_lock(void) +{ + modifyreg32(STM32_FLASH_PECR, 0, FLASH_PECR_PRGLOCK); + stm32_eeprom_lock(); +} + +static ssize_t stm32_eeprom_erase_write(size_t addr, const void *buf, + size_t buflen) +{ + const char *cbuf = buf; + size_t i; + + if (buflen == 0) + { + return 0; + } + + /* Check for valid address range */ + + if (addr >= STM32_EEPROM_BASE) + { + addr -= STM32_EEPROM_BASE; + } + + if (addr >= STM32_EEPROM_SIZE) + { + return -EINVAL; + } + + /* TODO: Voltage range must be range 1 or 2. Erase/program not allowed in + * range 3. + */ + + stm32_eeprom_unlock(); + + /* Clear pending status flags. */ + + putreg32(FLASH_SR_WRPERR | FLASH_SR_PGAERR | + FLASH_SR_SIZERR | FLASH_SR_OPTVERR | + FLASH_SR_OPTVERRUSR | FLASH_SR_RDERR, STM32_FLASH_SR); + + /* Enable automatic erasing (by disabling 'fixed time' programming). */ + + modifyreg32(STM32_FLASH_PECR, FLASH_PECR_FTDW, 0); + + /* Write buffer to EEPROM data memory. */ + + addr += STM32_EEPROM_BASE; + i = 0; + while (i < buflen) + { + uint32_t writeval; + size_t left = buflen - i; + + if ((addr & 0x03) == 0x00 && left >= 4) + { + /* Read/erase/write word */ + + writeval = cbuf ? *(uint32_t *)cbuf : 0; + putreg32(writeval, addr); + } + else if ((addr & 0x01) == 0x00 && left >= 2) + { + /* Read/erase/write half-word */ + + writeval = cbuf ? *(uint16_t *)cbuf : 0; + putreg16(writeval, addr); + } + else + { + /* Read/erase/write byte */ + + writeval = cbuf ? *(uint8_t *)cbuf : 0; + putreg8(writeval, addr); + } + + /* ... and wait to complete. */ + + while (getreg32(STM32_FLASH_SR) & FLASH_SR_BSY) + { + up_waste(); + } + + /* Verify */ + + /* We do not check Options Byte invalid flags FLASH_SR_OPTVERR + * and FLASH_SR_OPTVERRUSR for EEPROM erase/write. They are unrelated + * and STM32L standard library does not check for these either. + */ + + if (getreg32(STM32_FLASH_SR) & (FLASH_SR_WRPERR | FLASH_SR_PGAERR | + FLASH_SR_SIZERR | FLASH_SR_RDERR)) + { + stm32_eeprom_lock(); + return -EROFS; + } + + if ((addr & 0x03) == 0x00 && left >= 4) + { + if (getreg32(addr) != writeval) + { + stm32_eeprom_lock(); + return -EIO; + } + + addr += 4; + i += 4; + cbuf += !!(cbuf) * 4; + } + else if ((addr & 0x01) == 0x00 && left >= 2) + { + if (getreg16(addr) != writeval) + { + stm32_eeprom_lock(); + return -EIO; + } + + addr += 2; + i += 2; + cbuf += !!(cbuf) * 2; + } + else + { + if (getreg8(addr) != writeval) + { + stm32_eeprom_lock(); + return -EIO; + } + + addr += 1; + i += 1; + cbuf += !!(cbuf) * 1; + } + } + + stm32_eeprom_lock(); + return buflen; +} + +#endif /* defined(CONFIG_STM32_STM32L15XX) */ + /************************************************************************************ * Public Functions ************************************************************************************/ @@ -164,6 +355,35 @@ void stm32_flash_lock(void) sem_unlock(); } +#if defined(CONFIG_STM32_STM32L15XX) + +size_t stm32_eeprom_size(void) +{ + return STM32_EEPROM_SIZE; +} + +size_t stm32_eeprom_getaddress(void) +{ + return STM32_EEPROM_BASE; +} + +ssize_t stm32_eeprom_write(size_t addr, const void *buf, size_t buflen) +{ + if (!buf) + { + return -EINVAL; + } + + return stm32_eeprom_erase_write(addr, buf, buflen); +} + +ssize_t stm32_eeprom_erase(size_t addr, size_t eraselen) +{ + return stm32_eeprom_erase_write(addr, NULL, eraselen); +} + +#endif /* defined(CONFIG_STM32_STM32L15XX) */ + #if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) size_t up_progmem_pagesize(size_t page) { @@ -260,6 +480,8 @@ size_t up_progmem_getaddress(size_t page) #endif /* def CONFIG_STM32_STM32F40XX */ +#if !defined(CONFIG_STM32_STM32L15XX) + size_t up_progmem_npages(void) { return STM32_FLASH_NPAGES; @@ -271,14 +493,14 @@ bool up_progmem_isuniform(void) return true; #else return false; -#endif /* def STM32_FLASH_PAGESIZE */ +#endif } ssize_t up_progmem_erasepage(size_t page) { #if defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) size_t page_address; -#endif /* defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) */ +#endif if (page >= STM32_FLASH_NPAGES) { @@ -438,5 +660,7 @@ ssize_t up_progmem_write(size_t addr, const void *buf, size_t count) return written; } +#endif /* !defined(CONFIG_STM32_STM32L15XX) */ + #endif /* defined(CONFIG_STM32_STM32F10XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined (CONFIG_STM32_STM32F40XX) */ + defined(CONFIG_STM32_STM32F40XX) || defined(CONFIG_STM32_STM32L15XX) */ diff --git a/arch/arm/src/stm32/stm32_flash.h b/arch/arm/src/stm32/stm32_flash.h index 10c5cc189ed..6da1b0b7034 100644 --- a/arch/arm/src/stm32/stm32_flash.h +++ b/arch/arm/src/stm32/stm32_flash.h @@ -46,4 +46,60 @@ #include "chip.h" #include "chip/stm32_flash.h" +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_eeprom_size + * + * Description: + * Get EEPROM data memory size + * + * Returns: + * Length of EEPROM memory region + * + ************************************************************************************/ + +size_t stm32_eeprom_size(void); + +/************************************************************************************ + * Name: stm32_eeprom_getaddress + * + * Description: + * Get EEPROM data memory address + * + * Returns: + * Address of EEPROM memory region + * + ************************************************************************************/ + +size_t stm32_eeprom_getaddress(void); + +/************************************************************************************ + * Name: stm32_eeprom_write + * + * Description: + * Write buffer to EEPROM data memory address + * + * Returns: + * Number of written bytes or error code. + * + ************************************************************************************/ + +ssize_t stm32_eeprom_write(size_t addr, const void *buf, size_t buflen); + +/************************************************************************************ + * Name: stm32_eeprom_erase + * + * Description: + * Erase memory on EEPROM data memory address + * + * Returns: + * Number of erased bytes or error code. + * + ************************************************************************************/ + +ssize_t stm32_eeprom_erase(size_t addr, size_t eraselen); + #endif /* __ARCH_ARM_SRC_STM32_STM32_FLASH_H */ diff --git a/arch/arm/src/stm32/stm32_iwdg.c b/arch/arm/src/stm32/stm32_iwdg.c index 1cdf8e47bc0..64495b71ab9 100644 --- a/arch/arm/src/stm32/stm32_iwdg.c +++ b/arch/arm/src/stm32/stm32_iwdg.c @@ -634,8 +634,8 @@ static int stm32_settimeout(FAR struct watchdog_lowerhalf_s *lower, * Name: stm32_iwdginitialize * * Description: - * Initialize the IWDG watchdog time. The watchdog timer is initialized and - * registers as 'devpath. The initial state of the watchdog time is + * Initialize the IWDG watchdog timer. The watchdog timer is initialized and + * registers as 'devpath'. The initial state of the watchdog timer is * disabled. * * Input Parameters: @@ -665,7 +665,7 @@ void stm32_iwdginitialize(FAR const char *devpath, uint32_t lsifreq) priv->started = false; /* Make sure that the LSI oscillator is enabled. NOTE: The LSI oscillator - * is enabled here but is not disabled by this file (because this file does + * is enabled here but is not disabled by this file, because this file does * not know the global usage of the oscillator. Any clock management * logic (say, as part of a power management scheme) needs handle other * LSI controls outside of this file. @@ -685,9 +685,9 @@ void stm32_iwdginitialize(FAR const char *devpath, uint32_t lsifreq) (void)watchdog_register(devpath, (FAR struct watchdog_lowerhalf_s *)priv); - /* When the microcontroller enters debug mode (Cortex™-M4F core halted), + /* When the microcontroller enters debug mode (Cortex-M4F core halted), * the IWDG counter either continues to work normally or stops, depending - * on DBG_WIDG_STOP configuration bit in DBG module. + * on DBG_IWDG_STOP configuration bit in DBG module. */ #if defined(CONFIG_STM32_JTAG_FULL_ENABLE) || \ @@ -695,7 +695,7 @@ void stm32_iwdginitialize(FAR const char *devpath, uint32_t lsifreq) defined(CONFIG_STM32_JTAG_SW_ENABLE) { #if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined(CONFIG_STM32_STM32F40XX) + defined(CONFIG_STM32_STM32F40XX) || defined(CONFIG_STM32_STM32L15XX) uint32_t cr = getreg32(STM32_DBGMCU_APB1_FZ); cr |= DBGMCU_APB1_IWDGSTOP; putreg32(cr, STM32_DBGMCU_APB1_FZ); diff --git a/arch/arm/src/stm32/stm32_wwdg.c b/arch/arm/src/stm32/stm32_wwdg.c index a299de8c991..c0cb34f1f71 100644 --- a/arch/arm/src/stm32/stm32_wwdg.c +++ b/arch/arm/src/stm32/stm32_wwdg.c @@ -734,8 +734,8 @@ static int stm32_ioctl(FAR struct watchdog_lowerhalf_s *lower, int cmd, * Name: stm32_wwdginitialize * * Description: - * Initialize the WWDG watchdog time. The watchdog timer is initialized and - * registers as 'devpath. The initial state of the watchdog time is + * Initialize the WWDG watchdog timer. The watchdog timer is initialized and + * registers as 'devpath'. The initial state of the watchdog timer is * disabled. * * Input Parameters: @@ -753,7 +753,7 @@ void stm32_wwdginitialize(FAR const char *devpath) wdinfo("Entry: devpath=%s\n", devpath); - /* NOTE we assume that clocking to the IWDG has already been provided by + /* NOTE we assume that clocking to the WWDG has already been provided by * the RCC initialization logic. */ @@ -780,7 +780,7 @@ void stm32_wwdginitialize(FAR const char *devpath) (void)watchdog_register(devpath, (FAR struct watchdog_lowerhalf_s *)priv); - /* When the microcontroller enters debug mode (Cortex�-M4F core halted), + /* When the microcontroller enters debug mode (Cortex-M core halted), * the WWDG counter either continues to work normally or stops, depending * on DBG_WWDG_STOP configuration bit in DBG module. */ @@ -790,7 +790,7 @@ void stm32_wwdginitialize(FAR const char *devpath) defined(CONFIG_STM32_JTAG_SW_ENABLE) { #if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F30XX) || \ - defined(CONFIG_STM32_STM32F40XX) + defined(CONFIG_STM32_STM32F40XX) || defined(CONFIG_STM32_STM32L15XX) uint32_t cr = getreg32(STM32_DBGMCU_APB1_FZ); cr |= DBGMCU_APB1_WWDGSTOP; putreg32(cr, STM32_DBGMCU_APB1_FZ); diff --git a/arch/arm/src/stm32/stm32l15xxx_rcc.c b/arch/arm/src/stm32/stm32l15xxx_rcc.c index 51a163d99a1..2624fade10d 100644 --- a/arch/arm/src/stm32/stm32l15xxx_rcc.c +++ b/arch/arm/src/stm32/stm32l15xxx_rcc.c @@ -524,6 +524,8 @@ static void stm32_stdclockconfig(void) #if defined(CONFIG_RTC_HSECLOCK) || defined(CONFIG_LCD_HSECLOCK) uint16_t pwrcr; #endif + uint32_t pwr_vos; + bool flash_1ws; /* Enable PWR clock from APB1 to give access to PWR_CR register */ @@ -537,12 +539,39 @@ static void stm32_stdclockconfig(void) * Range 1: PLLVCO up to 96MHz in range 1 (1.8V) * Range 2: PLLVCO up to 48MHz in range 2 (1.5V) (default) * Range 3: PLLVCO up to 24MHz in range 3 (1.2V) + * + * Range 1: SYSCLK up to 32Mhz + * Range 2: SYSCLK up to 16Mhz + * Range 3: SYSCLK up to 4.2Mhz + * + * Range 1: Flash 1WS if SYSCLK > 16Mhz + * Range 2: Flash 1WS if SYSCLK > 8Mhz + * Range 3: Flash 1WS if SYSCLK > 2.1Mhz */ -#if STM32_PLL_FREQUENCY > 48000000 - stm32_pwr_setvos(PWR_CR_VOS_SCALE_1); + pwr_vos = PWR_CR_VOS_SCALE_2; + flash_1ws = false; + +#ifdef STM32_PLL_FREQUENCY + if (STM32_PLL_FREQUENCY > 48000000) + { + pwr_vos = PWR_CR_VOS_SCALE_1; + } #endif + if (STM32_SYSCLK_FREQUENCY > 16000000) + { + pwr_vos = PWR_CR_VOS_SCALE_1; + } + + if ((pwr_vos == PWR_CR_VOS_SCALE_1 && STM32_SYSCLK_FREQUENCY > 16000000) || + (pwr_vos == PWR_CR_VOS_SCALE_2 && STM32_SYSCLK_FREQUENCY > 8000000)) + { + flash_1ws = true; + } + + stm32_pwr_setvos(pwr_vos); + #if defined(CONFIG_RTC_HSECLOCK) || defined(CONFIG_LCD_HSECLOCK) /* If RTC / LCD selects HSE as clock source, the RTC prescaler * needs to be set before HSEON bit is set. @@ -579,12 +608,11 @@ static void stm32_stdclockconfig(void) #endif - /* Enable the source clock for the PLL (via HSE or HSI), HSE, and HSI. - * NOTE that only PLL, HSE, or HSI are supported for the system clock - * in this implementation - */ + /* Enable the source clock for the PLL (via HSE or HSI), HSE, and HSI. */ + +#if (STM32_SYSCLK_SW == RCC_CFGR_SW_HSE) || \ + ((STM32_SYSCLK_SW == RCC_CFGR_SW_PLL) && (STM32_CFGR_PLLSRC == RCC_CFGR_PLLSRC)) -#if (STM32_CFGR_PLLSRC == RCC_CFGR_PLLSRC) || (STM32_SYSCLK_SW == RCC_CFGR_SW_HSE) /* The PLL is using the HSE, or the HSE is the system clock. In either * case, we need to enable HSE clocking. */ @@ -599,7 +627,9 @@ static void stm32_stdclockconfig(void) return; } -#elif (STM32_CFGR_PLLSRC == 0) || (STM32_SYSCLK_SW == RCC_CFGR_SW_HSI) +#elif (STM32_SYSCLK_SW == RCC_CFGR_SW_HSI) || \ + ((STM32_SYSCLK_SW == RCC_CFGR_SW_PLL) && STM32_CFGR_PLLSRC == 0) + /* The PLL is using the HSI, or the HSI is the system clock. In either * case, we need to enable HSI clocking. */ @@ -616,6 +646,8 @@ static void stm32_stdclockconfig(void) #endif +#if (STM32_SYSCLK_SW != RCC_CFGR_SW_MSI) + /* Increasing the CPU frequency (in the same voltage range): * * After reset, the used clock is the MSI (2 MHz) with 0 WS configured in the @@ -643,7 +675,15 @@ static void stm32_stdclockconfig(void) regval |= FLASH_ACR_ACC64; /* 64-bit access mode */ putreg32(regval, STM32_FLASH_ACR); - regval |= FLASH_ACR_LATENCY; /* One wait state */ + if (flash_1ws) + { + regval |= FLASH_ACR_LATENCY; /* One wait state */ + } + else + { + regval &= ~FLASH_ACR_LATENCY; /* Zero wait state */ + } + putreg32(regval, STM32_FLASH_ACR); /* Enable FLASH prefetch */ @@ -651,6 +691,8 @@ static void stm32_stdclockconfig(void) regval |= FLASH_ACR_PRFTEN; putreg32(regval, STM32_FLASH_ACR); +#endif /* STM32_SYSCLK_SW != RCC_CFGR_SW_MSI */ + /* Set the HCLK source/divider */ regval = getreg32(STM32_RCC_CFGR); diff --git a/arch/arm/src/stm32f7/Kconfig b/arch/arm/src/stm32f7/Kconfig index ff7dfe80cfd..e727aef24aa 100644 --- a/arch/arm/src/stm32f7/Kconfig +++ b/arch/arm/src/stm32f7/Kconfig @@ -1823,7 +1823,7 @@ config STM32F7_HAVE_RTC_COUNTER config STM32F7_HAVE_RTC_SUBSECONDS bool - default n + default y config RTC_MAGIC_REG int "The BKP register used to store/check the Magic value to determine if RTC is set already" diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index 8baa4fbf135..06a190f463f 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -149,6 +149,10 @@ ifeq ($(filter y,$(CONFIG_STM32F7_IWDG) $(CONFIG_STM32F7_RTC_LSICLOCK)),y) CHIP_CSRCS += stm32_lsi.c endif +ifeq ($(CONFIG_STM32F7_RTC_LSECLOCK),y) +CHIP_CSRCS += stm32_lse.c +endif + ifeq ($(CONFIG_STM32F7_I2C),y) CHIP_CSRCS += stm32_i2c.c endif diff --git a/arch/arm/src/stm32f7/stm32_exti_pwr.c b/arch/arm/src/stm32f7/stm32_exti_pwr.c index 09d064e930e..3c4f2d07a3b 100644 --- a/arch/arm/src/stm32f7/stm32_exti_pwr.c +++ b/arch/arm/src/stm32f7/stm32_exti_pwr.c @@ -129,7 +129,7 @@ static int stm32_exti_pvd_isr(int irq, void *context, void *arg) ****************************************************************************/ int stm32_exti_pvd(bool risingedge, bool fallingedge, bool event, - xcpt_t func, void *arg); + xcpt_t func, void *arg) { /* Get the previous GPIO IRQ handler; Save the new IRQ handler. */ diff --git a/net/sixlowpan/sixlowpan_sniffer.c b/arch/arm/src/stm32f7/stm32_lse.c similarity index 69% rename from net/sixlowpan/sixlowpan_sniffer.c rename to arch/arm/src/stm32f7/stm32_lse.c index 32a2e3121e1..162630060b9 100644 --- a/net/sixlowpan/sixlowpan_sniffer.c +++ b/arch/arm/src/stm32f7/stm32_lse.c @@ -1,8 +1,9 @@ /**************************************************************************** - * net/sixlowpan/sixlowpan_sniffer.c + * arch/arm/src/stm32f7/stm32_lse.c * * Copyright (C) 2017 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Authors: Gregory Nutt + * David Sidrane * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,43 +40,47 @@ #include -#include "nuttx/net/net.h" -#include "nuttx/net/sixlowpan.h" +#include "up_arch.h" -#include "sixlowpan/sixlowpan_internal.h" - -#ifdef CONFIG_NET_6LOWPAN_SNIFFER +#include "stm32_rcc.h" +#include "stm32_pwr.h" /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Function: sixlowpan_set_sniffer + * Name: stm32_rcc_enablelse * * Description: - * Configure to use an architecture-specific sniffer to enable tracing of - * IP. - * - * Input parameters: - * sniffer - A reference to the new sniffer to be used. This may - * be a NULL value to disable the sniffer. - * - * Returned Value: - * None + * Enable the External Low-Speed (LSE) oscillator. * ****************************************************************************/ -void sixlowpan_set_sniffer(FAR struct sixlowpan_rime_sniffer_s *sniffer) +void stm32_rcc_enablelse(void) { - /* Make sure that the sniffer is not in use */ + uint32_t regval; - net_lock(); + /* The LSE is in the RTC domain and write access is denied to this domain + * after reset, you have to enable write access using DBP bit in the PWR CR + * register before to configuring the LSE. + */ - /* Then instantiate the new sniffer */ + stm32_pwr_enablebkp(true); - g_sixlowpan_sniffer = sniffer; - net_unlock(); + /* Enable the External Low-Speed (LSE) oscillator by setting the LSEON bit + * the RCC BDCR register. + */ + + regval = getreg32(STM32_RCC_BDCR); + regval |= RCC_BDCR_LSEON; + putreg32(regval,STM32_RCC_BDCR); + + /* Wait for the LSE clock to be ready */ + + while (((regval = getreg32(STM32_RCC_BDCR)) & RCC_BDCR_LSERDY) == 0); + + /* Disable backup domain access if it was disabled on entry */ + + stm32_pwr_enablebkp(false); } - -#endif /* CONFIG_NET_6LOWPAN_SNIFFER */ diff --git a/arch/arm/src/stm32f7/stm32_lsi.c b/arch/arm/src/stm32f7/stm32_lsi.c index 46e96163727..636662721b5 100644 --- a/arch/arm/src/stm32f7/stm32_lsi.c +++ b/arch/arm/src/stm32f7/stm32_lsi.c @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm/src/stm32f/stm32_lsi.c + * arch/arm/src/stm32f7/stm32_lsi.c * * Copyright (C) 2012, 2015-2016 Gregory Nutt. All rights reserved. * Authors: Gregory Nutt diff --git a/arch/arm/src/stm32f7/stm32_rtc.c b/arch/arm/src/stm32f7/stm32_rtc.c index 8a21ec41fea..2840fac8a5d 100644 --- a/arch/arm/src/stm32f7/stm32_rtc.c +++ b/arch/arm/src/stm32f7/stm32_rtc.c @@ -196,7 +196,7 @@ static void rtc_dumpregs(FAR const char *msg) rtcinfo(" TSDR: %08x\n", getreg32(STM32_RTC_TSDR)); rtcinfo(" TSSSR: %08x\n", getreg32(STM32_RTC_TSSSR)); rtcinfo(" CALR: %08x\n", getreg32(STM32_RTC_CALR)); - rtcinfo(" TAFCR: %08x\n", getreg32(STM32_RTC_TAFCR)); + rtcinfo(" TAMPCR: %08x\n", getreg32(STM32_RTC_TAMPCR)); rtcinfo("ALRMASSR: %08x\n", getreg32(STM32_RTC_ALRMASSR)); rtcinfo("ALRMBSSR: %08x\n", getreg32(STM32_RTC_ALRMBSSR)); rtcinfo("MAGICREG: %08x\n", getreg32(RTC_MAGIC_REG)); @@ -227,7 +227,8 @@ static void rtc_dumpregs(FAR const char *msg) ****************************************************************************/ #ifdef CONFIG_DEBUG_RTC_INFO -static void rtc_dumptime(FAR const struct tm *tp, FAR const char *msg) +static void rtc_dumptime(FAR const struct tm *tp, FAR const uint32_t *usecs, + FAR const char *msg) { rtcinfo("%s:\n", msg); rtcinfo(" tm_sec: %08x\n", tp->tm_sec); @@ -236,9 +237,14 @@ static void rtc_dumptime(FAR const struct tm *tp, FAR const char *msg) rtcinfo(" tm_mday: %08x\n", tp->tm_mday); rtcinfo(" tm_mon: %08x\n", tp->tm_mon); rtcinfo(" tm_year: %08x\n", tp->tm_year); + + if (usecs != NULL) + { + rtcinfo(" usecs: %08x\n", (unsigned int)*usecs); + } } #else -# define rtc_dumptime(tp, msg) +# define rtc_dumptime(tp, usecs, msg) #endif /**************************************************************************** @@ -1069,34 +1075,48 @@ int up_rtc_initialize(void) * ****************************************************************************/ -#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS +#ifdef CONFIG_STM32F7_HAVE_RTC_SUBSECONDS int stm32_rtc_getdatetime_with_subseconds(FAR struct tm *tp, FAR long *nsec) #else int up_rtc_getdatetime(FAR struct tm *tp) #endif { -#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS - uint32_t ssr; -#endif uint32_t dr; uint32_t tr; uint32_t tmp; +#ifdef CONFIG_STM32F7_HAVE_RTC_SUBSECONDS + uint32_t ssr; + uint32_t prediv_s; + uint32_t usecs; +#endif /* Sample the data time registers. There is a race condition here... If * we sample the time just before midnight on December 31, the date could - * be wrong because the day rolled over while were sampling. + * be wrong because the day rolled over while were sampling. Thus loop for + * checking overflow here is needed. There is a race condition with + * subseconds too. If we sample TR register just before second rolling + * and subseconds are read at wrong second, we get wrong time. */ do { dr = getreg32(STM32_RTC_DR); tr = getreg32(STM32_RTC_TR); -#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS +#ifdef CONFIG_STM32F7_HAVE_RTC_SUBSECONDS ssr = getreg32(STM32_RTC_SSR); + tmp = getreg32(STM32_RTC_TR); + if (tmp != tr) + { + continue; + } #endif tmp = getreg32(STM32_RTC_DR); + if (tmp == dr) + { + break; + } } - while (tmp != dr); + while (1); rtc_dumpregs("Reading Time"); @@ -1141,31 +1161,31 @@ int up_rtc_getdatetime(FAR struct tm *tp) tp->tm_isdst = 0 #endif -#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS +#ifdef CONFIG_STM32F7_HAVE_RTC_SUBSECONDS /* Return RTC sub-seconds if no configured and if a non-NULL value * of nsec has been provided to receive the sub-second value. */ - if (nsec) + prediv_s = getreg32(STM32_RTC_PRER) & RTC_PRER_PREDIV_S_MASK; + prediv_s >>= RTC_PRER_PREDIV_S_SHIFT; + + ssr &= RTC_SSR_MASK; + + /* Maximum prediv_s is 0x7fff, thus we can multiply by 100000 and + * still fit 32-bit unsigned integer. + */ + + usecs = (((prediv_s - ssr) * 100000) / (prediv_s + 1)) * 10; + if (nsec != NULL) { - uint32_t prediv_s; - uint32_t usecs; - - prediv_s = getreg32(STM32_RTC_PRER) & RTC_PRER_PREDIV_S_MASK; - prediv_s >>= RTC_PRER_PREDIV_S_SHIFT; - - ssr &= RTC_SSR_MASK; - - /* Maximum prediv_s is 0x7fff, thus we can multiply by 100000 and - * still fit 32-bit unsigned integer. - */ - - usecs = (((prediv_s - ssr) * 100000) / (prediv_s + 1)) * 10; *nsec = usecs * 1000; } -#endif /* CONFIG_STM32_HAVE_RTC_SUBSECONDS */ - rtc_dumptime((FAR const struct tm *)tp, "Returning"); + rtc_dumptime((FAR const struct tm *)tp, &usecs, "Returning"); +#else /* CONFIG_STM32_HAVE_RTC_SUBSECONDS */ + rtc_dumptime((FAR const struct tm *)tp, NULL, "Returning"); +#endif + return OK; } @@ -1192,7 +1212,7 @@ int up_rtc_getdatetime(FAR struct tm *tp) * ****************************************************************************/ -#ifdef CONFIG_STM32_HAVE_RTC_SUBSECONDS +#ifdef CONFIG_STM32F7_HAVE_RTC_SUBSECONDS int up_rtc_getdatetime(FAR struct tm *tp) { return stm32_rtc_getdatetime_with_subseconds(tp, NULL); @@ -1221,7 +1241,7 @@ int stm32_rtc_setdatetime(FAR const struct tm *tp) uint32_t dr; int ret; - rtc_dumptime(tp, "Setting time"); + rtc_dumptime(tp, NULL, "Setting time"); /* Then write the broken out values to the RTC */ @@ -1337,7 +1357,7 @@ int stm32_rtc_setalarm(FAR struct alm_setalarm_s *alminfo) /* REVISIT: Should test that the time is in the future */ - rtc_dumptime(&alminfo->as_time, "New alarm time"); + rtc_dumptime(&alminfo->as_time, NULL, "New alarm time"); /* Break out the values to the HW alarm register format. The values in * all STM32 fields match the fields of struct tm in this case. Notice diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c index 2249dc87c3b..9225312bd1b 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.c +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -1510,7 +1510,7 @@ static int stm32_sdmmc_rdyinterrupt(int irq, void *context, void *arg) * ****************************************************************************/ -static int stm32_sdmmc_interrupt(int irq, void *context, void *arg); +static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) { struct stm32_dev_s *priv =(struct stm32_dev_s *)arg; uint32_t enabled; diff --git a/arch/arm/src/stm32f7/stm32_serial.c b/arch/arm/src/stm32f7/stm32_serial.c index 77138526dd6..b839aad8f44 100644 --- a/arch/arm/src/stm32f7/stm32_serial.c +++ b/arch/arm/src/stm32f7/stm32_serial.c @@ -1,8 +1,9 @@ /**************************************************************************** * arch/arm/src/stm32f7/stm32_serial.c * - * Copyright (C) 2015-2016 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2015-2017 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -189,16 +190,6 @@ CONFIG_USART_DMAPRIO | \ DMA_SCR_PBURST_SINGLE | \ DMA_SCR_MBURST_SINGLE) -# ifdef CONFIG_SERIAL_IFLOWCONTROL -# define SERIAL_DMA_IFLOW_CONTROL_WORD \ - (DMA_SCR_DIR_P2M | \ - DMA_SCR_MINC | \ - DMA_SCR_PSIZE_8BITS | \ - DMA_SCR_MSIZE_8BITS | \ - CONFIG_USART_DMAPRIO | \ - DMA_SCR_PBURST_SINGLE | \ - DMA_SCR_MBURST_SINGLE) -# endif #endif /* SERIAL_HAVE_DMA */ /* Power management definitions */ @@ -286,8 +277,7 @@ struct up_dev_s #ifdef SERIAL_HAVE_DMA DMA_HANDLE rxdma; /* currently-open receive DMA stream */ bool rxenable; /* DMA-based reception en/disable */ - uint16_t rxdmain; /* Next byte in the DMA where hardware will write */ - uint16_t rxdmaout; /* Next byte in the DMA buffer to be read */ + uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */ char *const rxfifo; /* Receive DMA buffer */ #endif @@ -1139,7 +1129,22 @@ static void up_set_format(struct uart_dev_s *dev) uint32_t regval; uint32_t usartdiv8; uint32_t cr1; + uint32_t cr1_ue; uint32_t brr; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Get the original state of UE */ + + cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET); + cr1_ue = cr1 & USART_CR1_UE; + cr1 &= ~USART_CR1_UE; + + /* Disable UE as the format bits and baud rate registers can not be + * updated while UE = 1 */ + + up_serialout(priv, STM32_USART_CR1_OFFSET, cr1); /* In case of oversampling by 8, the equation is: * @@ -1159,7 +1164,6 @@ static void up_set_format(struct uart_dev_s *dev) /* Use oversamply by 8 only if the divisor is small. But what is small? */ - cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET); if (usartdiv8 > 100) { /* Use usartdiv16 */ @@ -1188,30 +1192,44 @@ static void up_set_format(struct uart_dev_s *dev) /* Configure parity mode */ - regval = up_serialin(priv, STM32_USART_CR1_OFFSET); - regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0); + cr1 &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1); if (priv->parity == 1) /* Odd parity */ { - regval |= (USART_CR1_PCE | USART_CR1_PS); + cr1 |= (USART_CR1_PCE | USART_CR1_PS); } else if (priv->parity == 2) /* Even parity */ { - regval |= USART_CR1_PCE; + cr1 |= USART_CR1_PCE; } - /* Configure word length (Default: 8-bits) */ + /* Configure word length (parity uses one of configured bits) + * + * Default: 1 start, 8 data (no parity), n stop, OR + * 1 start, 7 data + parity, n stop + */ - if (priv->bits == 7) + if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0)) { - regval |= USART_CR1_M1; + /* Select: 1 start, 8 data + parity, n stop, OR + * 1 start, 9 data (no parity), n stop. + */ + + cr1 |= USART_CR1_M0; } - else if (priv->bits == 9) + else if (priv->bits == 7 && priv->parity == 0) { - regval |= USART_CR1_M0; + /* Select: 1 start, 7 data (no parity), n stop, OR + */ + + cr1 |= USART_CR1_M1; } - up_serialout(priv, STM32_USART_CR1_OFFSET, regval); + /* Else Select: 1 start, 7 data + parity, n stop, OR + * 1 start, 8 data (no parity), n stop. + */ + + up_serialout(priv, STM32_USART_CR1_OFFSET, cr1); /* Configure STOP bits */ @@ -1230,7 +1248,8 @@ static void up_set_format(struct uart_dev_s *dev) regval = up_serialin(priv, STM32_USART_CR3_OFFSET); regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE); -#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32F7_FLOWCONTROL_BROKEN) +#if defined(CONFIG_SERIAL_IFLOWCONTROL) && \ + !defined(CONFIG_STM32F7_FLOWCONTROL_BROKEN) if (priv->iflow && (priv->rts_gpio != 0)) { regval |= USART_CR3_RTSE; @@ -1245,6 +1264,8 @@ static void up_set_format(struct uart_dev_s *dev) #endif up_serialout(priv, STM32_USART_CR3_OFFSET, regval); + up_serialout(priv, STM32_USART_CR1_OFFSET, cr1 | cr1_ue); + leave_critical_section(flags); } #endif /* CONFIG_SUPPRESS_UART_CONFIG */ @@ -1473,35 +1494,19 @@ static int up_dma_setup(struct uart_dev_s *dev) priv->rxdma = stm32_dmachannel(priv->rxdma_channel); -#ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow) - { - /* Configure for non-circular DMA reception into the RX FIFO */ + /* Configure for circular DMA reception into the RX FIFO */ - stm32_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_IFLOW_CONTROL_WORD); - } - else -#endif - { - /* Configure for circular DMA reception into the RX FIFO */ - - stm32_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_CONTROL_WORD); - } + stm32_dmasetup(priv->rxdma, + priv->usartbase + STM32_USART_RDR_OFFSET, + (uint32_t)priv->rxfifo, + RXDMA_BUFFER_SIZE, + SERIAL_DMA_CONTROL_WORD); /* Reset our DMA shadow pointer to match the address just * programmed above. */ - priv->rxdmaout = 0; - priv->rxdmain = 0; + priv->rxdmanext = 0; /* Enable receive DMA for the UART */ @@ -1509,26 +1514,12 @@ static int up_dma_setup(struct uart_dev_s *dev) regval |= USART_CR3_DMAR; up_serialout(priv, STM32_USART_CR3_OFFSET, regval); -#ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow) - { - /* Start the DMA channel, and arrange for callbacks at the full point - * in the FIFO. After buffer gets full, hardware flow-control kicks - * in and DMA transfer is stopped. - */ + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ - stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, false); - } - else -#endif - { - /* Start the DMA channel, and arrange for callbacks at the half and - * full points in the FIFO. This ensures that we have half a FIFO - * worth of time to claim bytes before they are overwritten. - */ - - stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true); - } + stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true); return OK; } @@ -2226,49 +2217,27 @@ static bool up_rxflowcontrol(struct uart_dev_s *dev, static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; - uint32_t rxdmain; int c = 0; /* If additional bytes have been added to the DMA buffer, then we will need * to invalidate the DMA buffer before reading the byte. */ - rxdmain = up_dma_nextrx(priv); - if (rxdmain != priv->rxdmain) + if (up_dma_nextrx(priv) != priv->rxdmanext) { /* Invalidate the DMA buffer */ arch_invalidate_dcache((uintptr_t)priv->rxfifo, (uintptr_t)priv->rxfifo + RXDMA_BUFFER_SIZE - 1); - /* Since DMA is ongoing, there are lots of race conditions here. We - * just have to hope that the rxdmaout stays well ahead of rxdmain. - */ + /* Now read from the DMA buffer */ - priv->rxdmain = rxdmain; - } + c = priv->rxfifo[priv->rxdmanext]; - /* Now check if there are any bytes to read from the DMA buffer */ - - if (rxdmain != priv->rxdmaout) - { - c = priv->rxfifo[priv->rxdmaout]; - - priv->rxdmaout++; - if (priv->rxdmaout == RXDMA_BUFFER_SIZE) + priv->rxdmanext++; + if (priv->rxdmanext == RXDMA_BUFFER_SIZE) { -#ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow) - { - /* RX DMA buffer full. RX paused, RTS line pulled up to prevent - * more input data from other end. - */ - } - else -#endif - { - priv->rxdmaout = 0; - } + priv->rxdmanext = 0; } } @@ -2276,41 +2245,6 @@ static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status) } #endif -/**************************************************************************** - * Name: up_dma_reenable - * - * Description: - * Call to re-enable RX DMA. - * - ****************************************************************************/ - -#if defined(SERIAL_HAVE_DMA) && defined(CONFIG_SERIAL_IFLOWCONTROL) -static void up_dma_reenable(struct up_dev_s *priv) -{ - /* Configure for non-circular DMA reception into the RX FIFO */ - - stm32_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_IFLOW_CONTROL_WORD); - - /* Reset our DMA shadow pointer to match the address just programmed - * above. - */ - - priv->rxdmaout = 0; - priv->rxdmain = 0; - - /* Start the DMA channel, and arrange for callbacks at the full point in - * the FIFO. After buffer gets full, hardware flow-control kicks in and - * DMA transfer is stopped. - */ - - stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, false); -} -#endif - /**************************************************************************** * Name: up_dma_rxint * @@ -2333,15 +2267,6 @@ static void up_dma_rxint(struct uart_dev_s *dev, bool enable) */ priv->rxenable = enable; - -#ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow && priv->rxenable && (priv->rxdmaout == RXDMA_BUFFER_SIZE)) - { - /* Re-enable RX DMA. */ - - up_dma_reenable(priv); - } -#endif } #endif @@ -2362,7 +2287,7 @@ static bool up_dma_rxavailable(struct uart_dev_s *dev) * do not match, then there are bytes to be received. */ - return (up_dma_nextrx(priv) != priv->rxdmaout); + return (up_dma_nextrx(priv) != priv->rxdmanext); } #endif @@ -2486,16 +2411,6 @@ static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg) if (priv->rxenable && up_dma_rxavailable(&priv->dev)) { uart_recvchars(&priv->dev); - -#ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow && priv->rxenable && - (priv->rxdmaout == RXDMA_BUFFER_SIZE)) - { - /* Re-enable RX DMA. */ - - up_dma_reenable(priv); - } -#endif } } #endif diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 5168c7796eb..5bf46951542 100644 --- a/arch/sim/Kconfig +++ b/arch/sim/Kconfig @@ -100,10 +100,19 @@ config SIM_WALLTIME correct for the system timer tick rate. With this definition in the configuration, sleep() behavior is more or less normal. +config SIM_NETDEV + bool "Simulated Network Device" + default y + depends on NET + ---help--- + Build in support for a simulated network device using a TAP device on Linux or + WPCAP on Windows. + if HOST_LINUX choice prompt "Simulation Network Type" default SIM_NET_HOST_ROUTE + depends on SIM_NETDEV config SIM_NET_HOST_ROUTE bool "Use local host route" diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 540c1c6fc87..5668604ce57 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -119,6 +119,7 @@ ifeq ($(CONFIG_ARCH_ROMGETC),y) CSRCS += up_romgetc.c endif +ifeq ($(CONFIG_SIM_NETDEV),y) ifeq ($(CONFIG_NET_ETHERNET),y) CSRCS += up_netdriver.c HOSTCFLAGS += -DNETDEV_BUFSIZE=$(CONFIG_NET_ETH_MTU) @@ -131,11 +132,12 @@ endif ifeq ($(CONFIG_SIM_NET_HOST_ROUTE),y) HOSTCFLAGS += -DCONFIG_SIM_NET_HOST_ROUTE endif -else +else # HOSTOS != Cygwin HOSTSRCS += up_wpcap.c up_netdev.c DRVLIB = /lib/w32api/libws2_32.a /lib/w32api/libiphlpapi.a -endif -endif +endif # HOSTOS != Cygwin +endif # CONFIG_NET_ETHERNET +endif # CONFIG_SIM_NETDEV ifeq ($(CONFIG_SMP),y) HOSTCFLAGS += -DCONFIG_SMP=1 -DCONFIG_SMP_NCPUS=$(CONFIG_SMP_NCPUS) diff --git a/arch/sim/src/up_idle.c b/arch/sim/src/up_idle.c index 1912bf755cc..05067a7c7ce 100644 --- a/arch/sim/src/up_idle.c +++ b/arch/sim/src/up_idle.c @@ -142,7 +142,7 @@ void up_idle(void) } #endif -#ifdef CONFIG_NET_ETHERNET +#if defined(CONFIG_NET_ETHERNET) && defined(CONFIG_SIM_NETDEV) /* Run the network if enabled */ netdriver_loop(); diff --git a/arch/sim/src/up_initialize.c b/arch/sim/src/up_initialize.c index ada5bb33ba0..fa8468196e0 100644 --- a/arch/sim/src/up_initialize.c +++ b/arch/sim/src/up_initialize.c @@ -270,7 +270,7 @@ void up_initialize(void) up_registerblockdevice(); /* Our FAT ramdisk at /dev/ram0 */ #endif -#ifdef CONFIG_NET_ETHERNET +#if defined(CONFIG_NET_ETHERNET) && defined(CONFIG_SIM_NETDEV) netdriver_init(); /* Our "real" network driver */ #endif diff --git a/arch/sim/src/up_netdriver.c b/arch/sim/src/up_netdriver.c index d9c12ae3397..bb37ec9fcc7 100644 --- a/arch/sim/src/up_netdriver.c +++ b/arch/sim/src/up_netdriver.c @@ -209,7 +209,7 @@ void netdriver_loop(void) { pkt_input(&g_sim_dev); } -#endif +#endif /* CONFIG_NET_PKT */ /* We only accept IP packets of the configured type and ARP packets */ @@ -253,7 +253,7 @@ void netdriver_loop(void) } } else -#endif +#endif /* CONFIG_NET_IPv4 */ #ifdef CONFIG_NET_IPv6 if (eth->type == HTONS(ETHTYPE_IP6) && is_ours) { @@ -283,7 +283,7 @@ void netdriver_loop(void) { neighbor_out(&g_sim_dev); } -#endif +#endif /* CONFIG_NET_IPv6 */ /* And send the packet */ @@ -291,7 +291,7 @@ void netdriver_loop(void) } } else -#endif +#endif/* CONFIG_NET_IPv6 */ #ifdef CONFIG_NET_ARP if (eth->type == htons(ETHTYPE_ARP)) { @@ -307,7 +307,11 @@ void netdriver_loop(void) netdev_send(g_sim_dev.d_buf, g_sim_dev.d_len); } } + else #endif + { + nwarn("WARNING: Unsupported Ethernet type %u\n", eth->type) + } } } diff --git a/configs/sim/README.txt b/configs/sim/README.txt index e3712b11f91..7fa886b5bda 100644 --- a/configs/sim/README.txt +++ b/configs/sim/README.txt @@ -807,6 +807,12 @@ pashello Configures to use apps/examples/pashello. +sixlowpan + + This configuration was intended only for unit-level testing of the + 6loWPAN stack. It enables networking with 6loWPAN support and uses + only a IEEE802.15.4 MAC loopback network device to supported testing. + touchscreen This configuration uses the simple touchscreen test at diff --git a/configs/sim/nettest/defconfig b/configs/sim/nettest/defconfig index 14b60be76ef..2c94b1e3a66 100644 --- a/configs/sim/nettest/defconfig +++ b/configs/sim/nettest/defconfig @@ -77,6 +77,7 @@ CONFIG_HOST_X86_64=y CONFIG_SIM_X8664_SYSTEMV=y # CONFIG_SIM_X8664_MICROSOFT is not set # CONFIG_SIM_WALLTIME is not set +CONFIG_SIM_NETDEV=y CONFIG_SIM_NET_HOST_ROUTE=y # CONFIG_SIM_NET_BRIDGE is not set # CONFIG_SIM_FRAMEBUFFER is not set @@ -203,6 +204,8 @@ CONFIG_MAX_TASKS=64 # # CONFIG_PTHREAD_MUTEX_TYPES is not set CONFIG_PTHREAD_MUTEX_ROBUST=y +# CONFIG_PTHREAD_MUTEX_UNSAFE is not set +# CONFIG_PTHREAD_MUTEX_BOTH is not set CONFIG_NPTHREAD_KEYS=4 # CONFIG_PTHREAD_CLEANUP is not set # CONFIG_CANCELLATION_POINTS is not set @@ -368,6 +371,7 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_PSEUDOTERM is not set # CONFIG_USBDEV is not set # CONFIG_USBHOST is not set +# CONFIG_USBMISC is not set # CONFIG_HAVE_USBTRACE is not set # CONFIG_DRIVERS_WIRELESS is not set # CONFIG_DRIVERS_CONTACTLESS is not set @@ -408,10 +412,12 @@ CONFIG_NET_GUARDSIZE=2 CONFIG_NET_ETHERNET=y # CONFIG_NET_LOOPBACK is not set # CONFIG_NET_TUN is not set +# CONFIG_NET_USRSOCK is not set # # Network Device Operations # +# CONFIG_NETDEV_IOCTL is not set # CONFIG_NETDEV_PHY_IOCTL is not set # @@ -442,6 +448,7 @@ CONFIG_NET_SOCKOPTS=y # TCP/IP Networking # CONFIG_NET_TCP=y +# CONFIG_NET_TCP_NO_STACK is not set # CONFIG_NET_TCPURGDATA is not set CONFIG_NET_TCP_CONNS=40 CONFIG_NET_MAX_LISTENPORTS=40 @@ -456,6 +463,7 @@ CONFIG_NET_TCP_RECVDELAY=0 # UDP Networking # # CONFIG_NET_UDP is not set +# CONFIG_NET_UDP_NO_STACK is not set # # ICMP Networking Support @@ -484,6 +492,10 @@ CONFIG_NET_IOB=y CONFIG_IOB_NBUFFERS=24 CONFIG_IOB_BUFSIZE=196 CONFIG_IOB_NCHAINS=8 + +# +# User-space networking stack API +# # CONFIG_NET_ARCH_INCR32 is not set # CONFIG_NET_ARCH_CHKSUM is not set CONFIG_NET_STATISTICS=y @@ -717,7 +729,6 @@ CONFIG_EXAMPLES_NETTEST_IPADDR=0xc0a80080 CONFIG_EXAMPLES_NETTEST_DRIPADDR=0xc0a80001 CONFIG_EXAMPLES_NETTEST_NETMASK=0xffffff00 CONFIG_EXAMPLES_NETTEST_CLIENTIP=0xc0a8006a -# CONFIG_EXAMPLES_NRF24L01TERM is not set # CONFIG_EXAMPLES_NSH is not set # CONFIG_EXAMPLES_NULL is not set # CONFIG_EXAMPLES_NX is not set @@ -748,6 +759,7 @@ CONFIG_EXAMPLES_NETTEST_CLIENTIP=0xc0a8006a # CONFIG_EXAMPLES_WATCHDOG is not set # CONFIG_EXAMPLES_WEBSERVER is not set # CONFIG_EXAMPLES_WGET is not set +# CONFIG_EXAMPLES_XBC_TEST is not set # # File System Utilities diff --git a/configs/sim/sixlowpan/Make.defs b/configs/sim/sixlowpan/Make.defs new file mode 100644 index 00000000000..f2a79385754 --- /dev/null +++ b/configs/sim/sixlowpan/Make.defs @@ -0,0 +1,128 @@ +############################################################################ +# configs/sim/sixlowpan/Make.defs +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +include ${TOPDIR}/.config +include ${TOPDIR}/tools/Config.mk + +HOSTOS = ${shell uname -o 2>/dev/null || echo "Other"} + +ifeq ($(CONFIG_DEBUG_SYMBOLS),y) + ARCHOPTIMIZATION = -g +endif + +ifneq ($(CONFIG_DEBUG_NOOPT),y) + ARCHOPTIMIZATION += -O2 +endif + +ARCHCPUFLAGS = -fno-builtin +ARCHCPUFLAGSXX = -fno-builtin -fno-exceptions -fcheck-new -fno-rtti +ARCHPICFLAGS = -fpic +ARCHWARNINGS = -Wall -Wstrict-prototypes -Wshadow -Wundef +ARCHWARNINGSXX = -Wall -Wshadow -Wundef +ARCHDEFINES = +ARCHINCLUDES = -I. -isystem $(TOPDIR)/include +ARCHINCLUDESXX = -I. -isystem $(TOPDIR)/include -isystem $(TOPDIR)/include/cxx +ARCHSCRIPT = + +ifeq ($(CONFIG_SIM_M32),y) + ARCHCPUFLAGS += -m32 + ARCHCPUFLAGSXX += -m32 +endif + +CROSSDEV = +CC = $(CROSSDEV)gcc +CXX = $(CROSSDEV)g++ +CPP = $(CROSSDEV)gcc -E +LD = $(CROSSDEV)ld +AR = $(CROSSDEV)ar rcs +NM = $(CROSSDEV)nm +OBJCOPY = $(CROSSDEV)objcopy +OBJDUMP = $(CROSSDEV)objdump + +CFLAGS = $(ARCHWARNINGS) $(ARCHOPTIMIZATION) \ + $(ARCHCPUFLAGS) $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRADEFINES) -pipe +CXXFLAGS = $(ARCHWARNINGSXX) $(ARCHOPTIMIZATION) \ + $(ARCHCPUFLAGSXX) $(ARCHINCLUDESXX) $(ARCHDEFINES) $(EXTRADEFINES) -pipe +CPPFLAGS = $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRADEFINES) +AFLAGS = $(CFLAGS) -D__ASSEMBLY__ + + +# ELF module definitions + +CELFFLAGS = $(CFLAGS) +CXXELFFLAGS = $(CXXFLAGS) + +LDELFFLAGS = -r -e main +ifeq ($(WINTOOL),y) + LDELFFLAGS += -T "${shell cygpath -w $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/gnu-elf.ld}" +else + LDELFFLAGS += -T $(TOPDIR)/configs/$(CONFIG_ARCH_BOARD)/scripts/gnu-elf.ld +endif + + +ASMEXT = .S +OBJEXT = .o +LIBEXT = .a + +ifeq ($(HOSTOS),Cygwin) + EXEEXT = .exe +else + EXEEXT = +endif + +LDLINKFLAGS = $(ARCHSCRIPT) # Link flags used with $(LD) +CCLINKFLAGS = $(ARCHSCRIPT) # Link flags used with $(CC) +LDFLAGS = $(ARCHSCRIPT) # For backward compatibility, same as CCLINKFLAGS + +ifeq ($(CONFIG_DEBUG_SYMBOLS),y) + LDLINKFLAGS += -g + CCLINKFLAGS += -g + LDFLAGS += -g +endif + +ifeq ($(CONFIG_SIM_M32),y) + LDLINKFLAGS += -melf_i386 + CCLINKFLAGS += -m32 + LDFLAGS += -m32 +endif + + +MKDEP = $(TOPDIR)/tools/mkdeps$(HOSTEXEEXT) + +HOSTCC = gcc +HOSTINCLUDES = -I. +HOSTCFLAGS = $(ARCHWARNINGS) $(ARCHOPTIMIZATION) \ + $(ARCHCPUFLAGS) $(HOSTINCLUDES) $(ARCHDEFINES) $(EXTRADEFINES) -pipe +HOSTLDFLAGS = diff --git a/configs/sim/sixlowpan/defconfig b/configs/sim/sixlowpan/defconfig new file mode 100644 index 00000000000..8a5d9f766d8 --- /dev/null +++ b/configs/sim/sixlowpan/defconfig @@ -0,0 +1,1175 @@ +# +# Automatically generated file; DO NOT EDIT. +# Nuttx/ Configuration +# + +# +# Build Setup +# +CONFIG_EXPERIMENTAL=y +# CONFIG_DEFAULT_SMALL is not set +# CONFIG_HOST_LINUX is not set +# CONFIG_HOST_OSX is not set +CONFIG_HOST_WINDOWS=y +# CONFIG_HOST_OTHER is not set +CONFIG_TOOLCHAIN_WINDOWS=y +# CONFIG_WINDOWS_NATIVE is not set +CONFIG_WINDOWS_CYGWIN=y +# CONFIG_WINDOWS_UBUNTU is not set +# CONFIG_WINDOWS_MSYS is not set +# CONFIG_WINDOWS_OTHER is not set + +# +# Build Configuration +# +# CONFIG_APPS_DIR="../apps" +CONFIG_BUILD_FLAT=y +# CONFIG_BUILD_2PASS is not set + +# +# Binary Output Formats +# +# CONFIG_RRLOAD_BINARY is not set +# CONFIG_INTELHEX_BINARY is not set +# CONFIG_MOTOROLA_SREC is not set +CONFIG_RAW_BINARY=y +# CONFIG_UBOOT_UIMAGE is not set + +# +# Customize Header Files +# +# CONFIG_ARCH_STDINT_H is not set +# CONFIG_ARCH_STDBOOL_H is not set +# CONFIG_ARCH_MATH_H is not set +# CONFIG_ARCH_FLOAT_H is not set +# CONFIG_ARCH_STDARG_H is not set +# CONFIG_ARCH_DEBUG_H is not set + +# +# Debug Options +# +CONFIG_DEBUG_ALERT=y +# CONFIG_DEBUG_FEATURES is not set +# CONFIG_ARCH_HAVE_STACKCHECK is not set +# CONFIG_ARCH_HAVE_HEAPCHECK is not set +CONFIG_DEBUG_SYMBOLS=y +# CONFIG_ARCH_HAVE_CUSTOMOPT is not set +CONFIG_DEBUG_NOOPT=y +# CONFIG_DEBUG_FULLOPT is not set + +# +# System Type +# +# CONFIG_ARCH_ARM is not set +# CONFIG_ARCH_AVR is not set +# CONFIG_ARCH_HC is not set +# CONFIG_ARCH_MIPS is not set +# CONFIG_ARCH_MISOC is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_ARCH_RISCV is not set +CONFIG_ARCH_SIM=y +# CONFIG_ARCH_X86 is not set +# CONFIG_ARCH_XTENSA is not set +# CONFIG_ARCH_Z16 is not set +# CONFIG_ARCH_Z80 is not set +CONFIG_ARCH="sim" + +# +# Simulation Configuration Options +# +CONFIG_HOST_X86_64=y +# CONFIG_HOST_X86 is not set +# CONFIG_SIM_M32 is not set +# CONFIG_SIM_CYGWIN_DECORATED is not set +# CONFIG_SIM_X8664_SYSTEMV is not set +CONFIG_SIM_X8664_MICROSOFT=y +# CONFIG_SIM_WALLTIME is not set +# CONFIG_SIM_NETDEV is not set +# CONFIG_SIM_FRAMEBUFFER is not set +# CONFIG_SIM_SPIFLASH is not set +# CONFIG_SIM_QSPIFLASH is not set + +# +# Architecture Options +# +# CONFIG_ARCH_NOINTC is not set +# CONFIG_ARCH_VECNOTIRQ is not set +# CONFIG_ARCH_DMA is not set +# CONFIG_ARCH_HAVE_IRQPRIO is not set +# CONFIG_ARCH_L2CACHE is not set +# CONFIG_ARCH_HAVE_COHERENT_DCACHE is not set +# CONFIG_ARCH_HAVE_ADDRENV is not set +# CONFIG_ARCH_NEED_ADDRENV_MAPPING is not set +CONFIG_ARCH_HAVE_MULTICPU=y +# CONFIG_ARCH_HAVE_VFORK is not set +# CONFIG_ARCH_HAVE_MMU is not set +# CONFIG_ARCH_HAVE_MPU is not set +# CONFIG_ARCH_NAND_HWECC is not set +# CONFIG_ARCH_HAVE_EXTCLK is not set +CONFIG_ARCH_HAVE_POWEROFF=y +# CONFIG_ARCH_HAVE_RESET is not set +CONFIG_ARCH_STACKDUMP=y +# CONFIG_ENDIAN_BIG is not set +# CONFIG_ARCH_IDLE_CUSTOM is not set +# CONFIG_ARCH_HAVE_RAMFUNCS is not set +# CONFIG_ARCH_HAVE_RAMVECTORS is not set +# CONFIG_ARCH_MINIMAL_VECTORTABLE is not set + +# +# Board Settings +# +CONFIG_BOARD_LOOPSPERMSEC=51262 +# CONFIG_ARCH_CALIBRATION is not set + +# +# Interrupt options +# +# CONFIG_ARCH_HAVE_INTERRUPTSTACK is not set +# CONFIG_ARCH_HAVE_HIPRI_INTERRUPT is not set + +# +# Boot options +# +# CONFIG_BOOT_RUNFROMEXTSRAM is not set +CONFIG_BOOT_RUNFROMFLASH=y +# CONFIG_BOOT_RUNFROMISRAM is not set +# CONFIG_BOOT_RUNFROMSDRAM is not set +# CONFIG_BOOT_COPYTORAM is not set + +# +# Boot Memory Configuration +# +CONFIG_RAM_START=0x20400000 +CONFIG_RAM_SIZE=393216 +# CONFIG_ARCH_HAVE_SDRAM is not set + +# +# Board Selection +# +CONFIG_ARCH_BOARD_SIM=y +# CONFIG_ARCH_BOARD_CUSTOM is not set +CONFIG_ARCH_BOARD="sim" + +# +# Common Board Options +# + +# +# Board-Specific Options +# +# CONFIG_BOARD_CRASHDUMP is not set +CONFIG_LIB_BOARDCTL=y +CONFIG_BOARDCTL_POWEROFF=y +# CONFIG_BOARDCTL_UNIQUEID is not set +# CONFIG_BOARDCTL_TSCTEST is not set +# CONFIG_BOARDCTL_GRAPHICS is not set +# CONFIG_BOARDCTL_IOCTL is not set + +# +# RTOS Features +# +CONFIG_DISABLE_OS_API=y +# CONFIG_DISABLE_POSIX_TIMERS is not set +# CONFIG_DISABLE_PTHREAD is not set +# CONFIG_DISABLE_SIGNALS is not set +# CONFIG_DISABLE_MQUEUE is not set +# CONFIG_DISABLE_ENVIRON is not set + +# +# Clocks and Timers +# +CONFIG_ARCH_HAVE_TICKLESS=y +# CONFIG_SCHED_TICKLESS is not set +CONFIG_USEC_PER_TICK=10000 +# CONFIG_SYSTEM_TIME64 is not set +# CONFIG_CLOCK_MONOTONIC is not set +# CONFIG_ARCH_HAVE_TIMEKEEPING is not set +# CONFIG_JULIAN_TIME is not set +CONFIG_START_YEAR=2014 +CONFIG_START_MONTH=3 +CONFIG_START_DAY=10 +CONFIG_MAX_WDOGPARMS=2 +CONFIG_PREALLOC_WDOGS=32 +CONFIG_WDOG_INTRESERVE=4 +CONFIG_PREALLOC_TIMERS=4 + +# +# Tasks and Scheduling +# +# CONFIG_SPINLOCK is not set +# CONFIG_SMP is not set +# CONFIG_INIT_NONE is not set +CONFIG_INIT_ENTRYPOINT=y +# CONFIG_INIT_FILEPATH is not set +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_RR_INTERVAL=200 +# CONFIG_SCHED_SPORADIC is not set +CONFIG_TASK_NAME_SIZE=31 +CONFIG_MAX_TASKS=16 +# CONFIG_SCHED_HAVE_PARENT is not set +CONFIG_SCHED_WAITPID=y + +# +# Pthread Options +# +# CONFIG_PTHREAD_MUTEX_TYPES is not set +CONFIG_PTHREAD_MUTEX_ROBUST=y +# CONFIG_PTHREAD_MUTEX_UNSAFE is not set +# CONFIG_PTHREAD_MUTEX_BOTH is not set +CONFIG_NPTHREAD_KEYS=4 +# CONFIG_PTHREAD_CLEANUP is not set +# CONFIG_CANCELLATION_POINTS is not set + +# +# Performance Monitoring +# +# CONFIG_SCHED_CPULOAD is not set +# CONFIG_SCHED_INSTRUMENTATION is not set + +# +# Files and I/O +# +CONFIG_DEV_CONSOLE=y +# CONFIG_FDCLONE_DISABLE is not set +# CONFIG_FDCLONE_STDIO is not set +CONFIG_SDCLONE_DISABLE=y +CONFIG_NFILE_DESCRIPTORS=8 +CONFIG_NFILE_STREAMS=8 +CONFIG_NAME_MAX=32 +# CONFIG_PRIORITY_INHERITANCE is not set + +# +# RTOS hooks +# +# CONFIG_BOARD_INITIALIZE is not set +# CONFIG_SCHED_STARTHOOK is not set +# CONFIG_SCHED_ATEXIT is not set +# CONFIG_SCHED_ONEXIT is not set +# CONFIG_SIG_EVTHREAD is not set + +# +# Signal Numbers +# +CONFIG_SIG_SIGUSR1=1 +CONFIG_SIG_SIGUSR2=2 +CONFIG_SIG_SIGALARM=3 +CONFIG_SIG_SIGCONDTIMEDOUT=16 +CONFIG_SIG_SIGWORK=17 + +# +# POSIX Message Queue Options +# +CONFIG_PREALLOC_MQ_MSGS=4 +CONFIG_MQ_MAXMSGSIZE=32 +# CONFIG_MODULE is not set + +# +# Work queue support +# +CONFIG_SCHED_WORKQUEUE=y +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=224 +CONFIG_SCHED_HPWORKPERIOD=50000 +CONFIG_SCHED_HPWORKSTACKSIZE=4096 +# CONFIG_SCHED_LPWORK is not set + +# +# Stack and heap information +# +CONFIG_IDLETHREAD_STACKSIZE=4096 +CONFIG_USERMAIN_STACKSIZE=8192 +CONFIG_PTHREAD_STACK_MIN=256 +CONFIG_PTHREAD_STACK_DEFAULT=4096 +# CONFIG_LIB_SYSCALL is not set + +# +# Device Drivers +# +CONFIG_DISABLE_POLL=y +CONFIG_DEV_NULL=y +# CONFIG_DEV_ZERO is not set +# CONFIG_DEV_URANDOM is not set +# CONFIG_DEV_LOOP is not set + +# +# Buffering +# +# CONFIG_DRVR_WRITEBUFFER is not set +# CONFIG_DRVR_READAHEAD is not set +# CONFIG_RAMDISK is not set +# CONFIG_CAN is not set +# CONFIG_ARCH_HAVE_PWM_PULSECOUNT is not set +# CONFIG_ARCH_HAVE_PWM_MULTICHAN is not set +# CONFIG_PWM is not set +# CONFIG_ARCH_HAVE_I2CRESET is not set +CONFIG_I2C=y +# CONFIG_I2C_SLAVE is not set +# CONFIG_I2C_POLLED is not set +# CONFIG_I2C_TRACE is not set +CONFIG_I2C_DRIVER=y +# CONFIG_ARCH_HAVE_SPI_CRCGENERATION is not set +# CONFIG_ARCH_HAVE_SPI_CS_CONTROL is not set +# CONFIG_ARCH_HAVE_SPI_BITORDER is not set +CONFIG_SPI=y +# CONFIG_SPI_SLAVE is not set +CONFIG_SPI_EXCHANGE=y +# CONFIG_SPI_CMDDATA is not set +# CONFIG_SPI_CALLBACK is not set +# CONFIG_SPI_HWFEATURES is not set +# CONFIG_SPI_CS_DELAY_CONTROL is not set +# CONFIG_SPI_DRIVER is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_I2S is not set + +# +# Timer Driver Support +# +# CONFIG_TIMER is not set +# CONFIG_ONESHOT is not set +# CONFIG_RTC is not set +# CONFIG_WATCHDOG is not set +# CONFIG_TIMERS_CS2100CP is not set +# CONFIG_ANALOG is not set +# CONFIG_AUDIO_DEVICES is not set +# CONFIG_VIDEO_DEVICES is not set +# CONFIG_BCH is not set +# CONFIG_INPUT is not set + +# +# IO Expander/GPIO Support +# +# CONFIG_IOEXPANDER is not set +# CONFIG_DEV_GPIO is not set + +# +# LCD Driver Support +# +# CONFIG_LCD is not set +# CONFIG_SLCD is not set + +# +# LED Support +# +# CONFIG_RGBLED is not set +# CONFIG_PCA9635PW is not set +# CONFIG_NCP5623C is not set +CONFIG_MMCSD=y +CONFIG_MMCSD_NSLOTS=1 +# CONFIG_MMCSD_READONLY is not set +CONFIG_MMCSD_MULTIBLOCK_DISABLE=y +# CONFIG_MMCSD_MMCSUPPORT is not set +CONFIG_MMCSD_HAVECARDDETECT=y +# CONFIG_MMCSD_SPI is not set +# CONFIG_ARCH_HAVE_SDIO is not set +# CONFIG_SDIO_DMA is not set +# CONFIG_ARCH_HAVE_SDIOWAIT_WRCOMPLETE is not set +# CONFIG_MODEM is not set +CONFIG_MTD=y + +# +# MTD Configuration +# +# CONFIG_MTD_PARTITION is not set +# CONFIG_MTD_SECT512 is not set +# CONFIG_MTD_BYTE_WRITE is not set +# CONFIG_MTD_PROGMEM is not set +CONFIG_MTD_CONFIG=y +# CONFIG_MTD_CONFIG_RAM_CONSOLIDATE is not set +CONFIG_MTD_CONFIG_ERASEDVALUE=0xff + +# +# MTD Device Drivers +# +# CONFIG_MTD_NAND is not set +# CONFIG_RAMMTD is not set +# CONFIG_FILEMTD is not set +CONFIG_MTD_AT24XX=y +# CONFIG_AT24XX_MULTI is not set +CONFIG_AT24XX_SIZE=2 +CONFIG_AT24XX_ADDR=0x57 +CONFIG_AT24XX_EXTENDED=y +CONFIG_AT24XX_EXTSIZE=160 +CONFIG_AT24XX_FREQUENCY=100000 +CONFIG_MTD_AT25=y +CONFIG_AT25_SPIMODE=0 +CONFIG_AT25_SPIFREQUENCY=20000000 +# CONFIG_MTD_AT45DB is not set +# CONFIG_MTD_IS25XP is not set +# CONFIG_MTD_M25P is not set +# CONFIG_MTD_MX25L is not set +# CONFIG_MTD_S25FL1 is not set +# CONFIG_MTD_N25QXXX is not set +# CONFIG_MTD_SMART is not set +# CONFIG_MTD_RAMTRON is not set +# CONFIG_MTD_SST25 is not set +# CONFIG_MTD_SST25XX is not set +# CONFIG_MTD_SST26 is not set +# CONFIG_MTD_SST39FV is not set +# CONFIG_MTD_W25 is not set +# CONFIG_EEPROM is not set +CONFIG_NETDEVICES=y + +# +# General Ethernet MAC Driver Options +# +# CONFIG_NETDEV_LOOPBACK is not set +CONFIG_NETDEV_TELNET=y +CONFIG_TELNET_RXBUFFER_SIZE=256 +CONFIG_TELNET_TXBUFFER_SIZE=256 +CONFIG_NETDEV_MULTINIC=y +CONFIG_ARCH_HAVE_NETDEV_STATISTICS=y +CONFIG_NETDEV_STATISTICS=y +# CONFIG_NETDEV_LATEINIT is not set + +# +# External Ethernet MAC Device Support +# +# CONFIG_NET_DM90x0 is not set +# CONFIG_NET_CS89x0 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ENCX24J600 is not set +# CONFIG_NET_SLIP is not set +# CONFIG_NET_FTMAC100 is not set +# CONFIG_PIPES is not set +# CONFIG_PM is not set +# CONFIG_POWER is not set +# CONFIG_SENSORS is not set +CONFIG_SERIAL=y +# CONFIG_DEV_LOWCONSOLE is not set +# CONFIG_SERIAL_REMOVABLE is not set +CONFIG_SERIAL_CONSOLE=y +# CONFIG_16550_UART is not set +# CONFIG_UART_SERIALDRIVER is not set +# CONFIG_UART0_SERIALDRIVER is not set +# CONFIG_UART1_SERIALDRIVER is not set +# CONFIG_UART2_SERIALDRIVER is not set +# CONFIG_UART3_SERIALDRIVER is not set +# CONFIG_UART4_SERIALDRIVER is not set +# CONFIG_UART5_SERIALDRIVER is not set +# CONFIG_UART6_SERIALDRIVER is not set +# CONFIG_UART7_SERIALDRIVER is not set +# CONFIG_UART8_SERIALDRIVER is not set +# CONFIG_SCI0_SERIALDRIVER is not set +# CONFIG_SCI1_SERIALDRIVER is not set +# CONFIG_USART0_SERIALDRIVER is not set +# CONFIG_USART1_SERIALDRIVER is not set +# CONFIG_USART2_SERIALDRIVER is not set +# CONFIG_USART3_SERIALDRIVER is not set +# CONFIG_USART4_SERIALDRIVER is not set +# CONFIG_USART5_SERIALDRIVER is not set +# CONFIG_USART6_SERIALDRIVER is not set +# CONFIG_USART7_SERIALDRIVER is not set +# CONFIG_USART8_SERIALDRIVER is not set +# CONFIG_OTHER_UART_SERIALDRIVER is not set +# CONFIG_MCU_SERIAL is not set +CONFIG_STANDARD_SERIAL=y +# CONFIG_SERIAL_IFLOWCONTROL is not set +# CONFIG_SERIAL_OFLOWCONTROL is not set +# CONFIG_SERIAL_DMA is not set +# CONFIG_ARCH_HAVE_SERIAL_TERMIOS is not set +# CONFIG_PSEUDOTERM is not set +# CONFIG_USBDEV is not set +# CONFIG_USBHOST is not set +# CONFIG_USBMISC is not set +# CONFIG_HAVE_USBTRACE is not set +# CONFIG_DRIVERS_WIRELESS is not set +# CONFIG_DRIVERS_CONTACTLESS is not set + +# +# System Logging +# +# CONFIG_ARCH_SYSLOG is not set +# CONFIG_RAMLOG is not set +# CONFIG_SYSLOG_INTBUFFER is not set +# CONFIG_SYSLOG_TIMESTAMP is not set +CONFIG_SYSLOG_SERIAL_CONSOLE=y +# CONFIG_SYSLOG_CHAR is not set +CONFIG_SYSLOG_CONSOLE=y +# CONFIG_SYSLOG_NONE is not set +# CONFIG_SYSLOG_FILE is not set +# CONFIG_SYSLOG_CHARDEV is not set + +# +# Networking Support +# +CONFIG_ARCH_HAVE_NET=y +# CONFIG_ARCH_HAVE_PHY is not set +CONFIG_NET=y +# CONFIG_NET_PROMISCUOUS is not set + +# +# Driver buffer configuration +# +CONFIG_NET_GUARDSIZE=2 + +# +# Data link support +# +# CONFIG_NET_MULTILINK is not set +# CONFIG_NET_USER_DEVFMT is not set +# CONFIG_NET_ETHERNET is not set +CONFIG_NET_6LOWPAN=y +# CONFIG_NET_LOOPBACK is not set +# CONFIG_NET_TUN is not set +# CONFIG_NET_USRSOCK is not set + +# +# Network Device Operations +# +CONFIG_NETDEV_IOCTL=y +CONFIG_NETDEV_PHY_IOCTL=y + +# +# Internet Protocol Selection +# +# CONFIG_NET_IPv4 is not set +CONFIG_NET_IPv6=y +CONFIG_NET_IPv6_NCONF_ENTRIES=8 +CONFIG_NET_6LOWPAN_FRAG=y +CONFIG_NET_6LOWPAN_FRAMELEN=127 +# CONFIG_NET_6LOWPAN_COMPRESSION_IPv6 is not set +# CONFIG_NET_6LOWPAN_COMPRESSION_HC1 is not set +CONFIG_NET_6LOWPAN_COMPRESSION_HC06=y +CONFIG_NET_6LOWPAN_COMPRESSION_THRESHOLD=63 +CONFIG_NET_6LOWPAN_MAXADDRCONTEXT=1 +CONFIG_NET_6LOWPAN_MAXADDRCONTEXT_PREFIX_0_0=0xaa +CONFIG_NET_6LOWPAN_MAXADDRCONTEXT_PREFIX_0_1=0xaa +# CONFIG_NET_6LOWPAN_MAXADDRCONTEXT_PREINIT_1 is not set +CONFIG_NET_6LOWPAN_RIMEADDR_SIZE=2 +CONFIG_NET_6LOWPAN_MAXAGE=20 +CONFIG_NET_6LOWPAN_MAX_MACTRANSMITS=4 +CONFIG_NET_6LOWPAN_MAXPAYLOAD=102 +CONFIG_NET_6LOWPAN_MTU=1294 +CONFIG_NET_6LOWPAN_TCP_RECVWNDO=102 + +# +# Socket Support +# +CONFIG_NSOCKET_DESCRIPTORS=8 +CONFIG_NET_NACTIVESOCKETS=16 +CONFIG_NET_SOCKOPTS=y +# CONFIG_NET_SOLINGER is not set + +# +# Raw Socket Support +# +# CONFIG_NET_PKT is not set + +# +# Unix Domain Socket Support +# +# CONFIG_NET_LOCAL is not set + +# +# TCP/IP Networking +# +CONFIG_NET_TCP=y +# CONFIG_NET_TCP_NO_STACK is not set +# CONFIG_NET_TCPURGDATA is not set +# CONFIG_NET_TCP_REASSEMBLY is not set +CONFIG_NET_TCP_CONNS=8 +CONFIG_NET_MAX_LISTENPORTS=20 +CONFIG_NET_TCP_READAHEAD=y +CONFIG_NET_TCP_WRITE_BUFFERS=y +CONFIG_NET_TCP_NWRBCHAINS=8 +CONFIG_NET_TCP_RECVDELAY=0 +CONFIG_NET_TCPBACKLOG=y +# CONFIG_NET_SENDFILE is not set + +# +# UDP Networking +# +CONFIG_NET_UDP=y +# CONFIG_NET_UDP_NO_STACK is not set +# CONFIG_NET_UDP_CHECKSUMS is not set +CONFIG_NET_UDP_CONNS=8 +CONFIG_NET_BROADCAST=y +# CONFIG_NET_RXAVAIL is not set +CONFIG_NET_UDP_READAHEAD=y + +# +# ICMPv6 Networking Support +# +# CONFIG_NET_ICMPv6 is not set + +# +# IGMPv2 Client Support +# +# CONFIG_NET_IGMP is not set + +# +# ARP Configuration +# + +# +# Network I/O Buffer Support +# +CONFIG_NET_IOB=y +CONFIG_IOB_NBUFFERS=36 +CONFIG_IOB_BUFSIZE=196 +CONFIG_IOB_NCHAINS=8 +CONFIG_IOB_THROTTLE=8 + +# +# User-space networking stack API +# +# CONFIG_NET_ARCH_INCR32 is not set +# CONFIG_NET_ARCH_CHKSUM is not set +CONFIG_NET_STATISTICS=y + +# +# Routing Table Configuration +# +# CONFIG_NET_ROUTE is not set +CONFIG_NET_HOSTNAME="SAMV71-XULT" + +# +# Crypto API +# +# CONFIG_CRYPTO is not set + +# +# File Systems +# + +# +# File system configuration +# +# CONFIG_DISABLE_MOUNTPOINT is not set +# CONFIG_FS_AUTOMOUNTER is not set +# CONFIG_DISABLE_PSEUDOFS_OPERATIONS is not set +# CONFIG_PSEUDOFS_SOFTLINKS is not set +CONFIG_FS_READABLE=y +CONFIG_FS_WRITABLE=y +# CONFIG_FS_NAMED_SEMAPHORES is not set +CONFIG_FS_MQUEUE_MPATH="/var/mqueue" +# CONFIG_FS_RAMMAP is not set +CONFIG_FS_FAT=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FAT_MAXFNAME=32 +# CONFIG_FS_FATTIME is not set +# CONFIG_FAT_FORCE_INDIRECT is not set +# CONFIG_FAT_DMAMEMORY is not set +# CONFIG_FAT_DIRECT_RETRY is not set +# CONFIG_FS_NXFFS is not set +# CONFIG_FS_ROMFS is not set +# CONFIG_FS_TMPFS is not set +# CONFIG_FS_SMARTFS is not set +# CONFIG_FS_BINFS is not set +CONFIG_FS_PROCFS=y +# CONFIG_FS_PROCFS_REGISTER is not set + +# +# Exclude individual procfs entries +# +# CONFIG_FS_PROCFS_EXCLUDE_PROCESS is not set +# CONFIG_FS_PROCFS_EXCLUDE_UPTIME is not set +# CONFIG_FS_PROCFS_EXCLUDE_MOUNTS is not set +# CONFIG_FS_PROCFS_EXCLUDE_NET is not set +# CONFIG_FS_PROCFS_EXCLUDE_MTD is not set +# CONFIG_FS_UNIONFS is not set +# CONFIG_FS_HOSTFS is not set + +# +# Graphics Support +# +# CONFIG_NX is not set + +# +# Memory Management +# +# CONFIG_MM_SMALL is not set +CONFIG_MM_REGIONS=1 +# CONFIG_ARCH_HAVE_HEAP2 is not set +# CONFIG_GRAN is not set + +# +# Audio Support +# +# CONFIG_AUDIO is not set + +# +# Wireless Support +# +CONFIG_WIRELESS=y +CONFIG_WIRELESS_IEEE802154=y +CONFIG_IEEE802154_LOOPBACK=y +CONFIG_IEEE802154_LOOPBACK_HPWORK=y + +# +# Binary Loader +# +# CONFIG_BINFMT_DISABLE is not set +# CONFIG_BINFMT_EXEPATH is not set +# CONFIG_NXFLAT is not set +# CONFIG_ELF is not set +CONFIG_BUILTIN=y +# CONFIG_PIC is not set +# CONFIG_SYMTAB_ORDEREDBYNAME is not set + +# +# Library Routines +# + +# +# Standard C Library Options +# + +# +# Standard C I/O +# +# CONFIG_STDIO_DISABLE_BUFFERING is not set +CONFIG_STDIO_BUFFER_SIZE=64 +CONFIG_STDIO_LINEBUFFER=y +CONFIG_NUNGET_CHARS=2 +# CONFIG_NOPRINTF_FIELDWIDTH is not set +# CONFIG_LIBC_FLOATINGPOINT is not set +CONFIG_LIBC_LONG_LONG=y +# CONFIG_LIBC_SCANSET is not set +# CONFIG_EOL_IS_CR is not set +# CONFIG_EOL_IS_LF is not set +# CONFIG_EOL_IS_BOTH_CRLF is not set +CONFIG_EOL_IS_EITHER_CRLF=y +# CONFIG_MEMCPY_VIK is not set +# CONFIG_LIBM is not set + +# +# Architecture-Specific Support +# +CONFIG_ARCH_LOWPUTC=y +# CONFIG_ARCH_ROMGETC is not set +# CONFIG_LIBC_ARCH_MEMCPY is not set +# CONFIG_LIBC_ARCH_MEMCMP is not set +# CONFIG_LIBC_ARCH_MEMMOVE is not set +# CONFIG_LIBC_ARCH_MEMSET is not set +# CONFIG_LIBC_ARCH_STRCHR is not set +# CONFIG_LIBC_ARCH_STRCMP is not set +# CONFIG_LIBC_ARCH_STRCPY is not set +# CONFIG_LIBC_ARCH_STRNCPY is not set +# CONFIG_LIBC_ARCH_STRLEN is not set +# CONFIG_LIBC_ARCH_STRNLEN is not set +# CONFIG_LIBC_ARCH_ELF is not set + +# +# stdlib Options +# +CONFIG_LIB_RAND_ORDER=1 +CONFIG_LIB_HOMEDIR="/" +CONFIG_LIBC_TMPDIR="/tmp" +CONFIG_LIBC_MAX_TMPFILE=32 + +# +# Program Execution Options +# +# CONFIG_LIBC_EXECFUNCS is not set +CONFIG_POSIX_SPAWN_PROXY_STACKSIZE=2048 +CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE=4096 + +# +# errno Decode Support +# +# CONFIG_LIBC_STRERROR is not set +# CONFIG_LIBC_PERROR_STDOUT is not set + +# +# memcpy/memset Options +# +# CONFIG_MEMSET_OPTSPEED is not set +# CONFIG_LIBC_DLLFCN is not set +# CONFIG_LIBC_MODLIB is not set +# CONFIG_LIBC_WCHAR is not set +# CONFIG_LIBC_LOCALE is not set + +# +# Time/Time Zone Support +# +# CONFIG_LIBC_LOCALTIME is not set +# CONFIG_TIME_EXTENDED is not set +CONFIG_ARCH_HAVE_TLS=y + +# +# Thread Local Storage (TLS) +# +# CONFIG_TLS is not set + +# +# Network-Related Options +# +# CONFIG_LIBC_IPv4_ADDRCONV is not set +CONFIG_LIBC_NETDB=y + +# +# NETDB Support +# +# CONFIG_NETDB_HOSTFILE is not set +CONFIG_NETDB_DNSCLIENT=y +CONFIG_NETDB_DNSCLIENT_ENTRIES=8 +CONFIG_NETDB_DNSCLIENT_NAMESIZE=32 +CONFIG_NETDB_DNSCLIENT_LIFESEC=3600 +CONFIG_NETDB_DNSCLIENT_MAXRESPONSE=96 +# CONFIG_NETDB_RESOLVCONF is not set +# CONFIG_NETDB_DNSSERVER_NOADDR is not set +CONFIG_NETDB_DNSSERVER_IPv6=y +CONFIG_NETDB_DNSSERVER_IPv6ADDR_1=0xfc00 +CONFIG_NETDB_DNSSERVER_IPv6ADDR_2=0x0000 +CONFIG_NETDB_DNSSERVER_IPv6ADDR_3=0x0000 +CONFIG_NETDB_DNSSERVER_IPv6ADDR_4=0x0000 +CONFIG_NETDB_DNSSERVER_IPv6ADDR_5=0x0000 +CONFIG_NETDB_DNSSERVER_IPv6ADDR_6=0x0000 +CONFIG_NETDB_DNSSERVER_IPv6ADDR_7=0x0000 +CONFIG_NETDB_DNSSERVER_IPv6ADDR_8=0x0001 +# CONFIG_LIBC_IOCTL_VARIADIC is not set +CONFIG_LIB_SENDFILE_BUFSIZE=512 + +# +# Non-standard Library Support +# +# CONFIG_LIB_CRC64_FAST is not set +# CONFIG_LIB_KBDCODEC is not set +# CONFIG_LIB_SLCDCODEC is not set +# CONFIG_LIB_HEX2BIN is not set + +# +# Basic CXX Support +# +# CONFIG_C99_BOOL8 is not set +# CONFIG_HAVE_CXX is not set + +# +# Application Configuration +# + +# +# Built-In Applications +# +CONFIG_BUILTIN_PROXY_STACKSIZE=2048 + +# +# CAN Utilities +# + +# +# Examples +# +# CONFIG_EXAMPLES_CCTYPE is not set +# CONFIG_EXAMPLES_CHAT is not set +# CONFIG_EXAMPLES_CONFIGDATA is not set +# CONFIG_EXAMPLES_DHCPD is not set +# CONFIG_EXAMPLES_DISCOVER is not set +# CONFIG_EXAMPLES_ELF is not set +# CONFIG_EXAMPLES_FSTEST is not set +# CONFIG_EXAMPLES_FTPC is not set +# CONFIG_EXAMPLES_FTPD is not set +# CONFIG_EXAMPLES_HELLO is not set +# CONFIG_EXAMPLES_HIDKBD is not set +# CONFIG_EXAMPLES_IGMP is not set +# CONFIG_EXAMPLES_JSON is not set +# CONFIG_EXAMPLES_KEYPADTEST is not set +# CONFIG_EXAMPLES_MEDIA is not set +# CONFIG_EXAMPLES_MM is not set +# CONFIG_EXAMPLES_MODBUS is not set +# CONFIG_EXAMPLES_MOUNT is not set +CONFIG_EXAMPLES_NETTEST=y +CONFIG_EXAMPLES_NETTEST_STACKSIZE=4096 +CONFIG_EXAMPLES_NETTEST_PRIORITY=100 +CONFIG_EXAMPLES_NETTEST_LOOPBACK=y +CONFIG_EXAMPLES_NETTEST_SERVER_STACKSIZE=4096 +CONFIG_EXAMPLES_NETTEST_SERVER_PRIORITY=100 +# CONFIG_EXAMPLES_NETTEST_PERFORMANCE is not set +CONFIG_EXAMPLES_NETTEST_IPv6=y + +# +# Target IPv6 address +# +# +# Client IPv6 address +# +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_1=0xfe80 +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_2=0x0000 +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_3=0x0000 +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_4=0x0000 +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_5=0x0000 +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_6=0x00ff +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_7=0xfe00 +CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_8=0x1034 +CONFIG_EXAMPLES_NSH=y +# CONFIG_EXAMPLES_NULL is not set +# CONFIG_EXAMPLES_NX is not set +# CONFIG_EXAMPLES_NXFFS is not set +# CONFIG_EXAMPLES_NXHELLO is not set +# CONFIG_EXAMPLES_NXIMAGE is not set +# CONFIG_EXAMPLES_NXLINES is not set +# CONFIG_EXAMPLES_NXTERM is not set +# CONFIG_EXAMPLES_NXTEXT is not set +# CONFIG_EXAMPLES_OSTEST is not set +# CONFIG_EXAMPLES_PCA9635 is not set +# CONFIG_EXAMPLES_POSIXSPAWN is not set +# CONFIG_EXAMPLES_PPPD is not set +# CONFIG_EXAMPLES_RFID_READUID is not set +# CONFIG_EXAMPLES_RGBLED is not set +# CONFIG_EXAMPLES_SENDMAIL is not set +# CONFIG_EXAMPLES_SERIALBLASTER is not set +# CONFIG_EXAMPLES_SERIALRX is not set +# CONFIG_EXAMPLES_SERLOOP is not set +# CONFIG_EXAMPLES_SLCD is not set +# CONFIG_EXAMPLES_SMART is not set +# CONFIG_EXAMPLES_SMART_TEST is not set +# CONFIG_EXAMPLES_SMP is not set +# CONFIG_EXAMPLES_STAT is not set +# CONFIG_EXAMPLES_TCPECHO is not set +# CONFIG_EXAMPLES_TELNETD is not set +# CONFIG_EXAMPLES_TIFF is not set +# CONFIG_EXAMPLES_TOUCHSCREEN is not set +# CONFIG_EXAMPLES_UDP is not set +# CONFIG_EXAMPLES_UDPBLASTER is not set +# CONFIG_EXAMPLES_USBSERIAL is not set +# CONFIG_EXAMPLES_WATCHDOG is not set +# CONFIG_EXAMPLES_WEBSERVER is not set +# CONFIG_EXAMPLES_XBC_TEST is not set + +# +# File System Utilities +# +# CONFIG_FSUTILS_FLASH_ERASEALL is not set +# CONFIG_FSUTILS_INIFILE is not set +# CONFIG_FSUTILS_PASSWD is not set + +# +# GPS Utilities +# +# CONFIG_GPSUTILS_MINMEA_LIB is not set + +# +# Graphics Support +# +# CONFIG_TIFF is not set +# CONFIG_GRAPHICS_TRAVELER is not set + +# +# Interpreters +# +# CONFIG_INTERPRETERS_BAS is not set +# CONFIG_INTERPRETERS_FICL is not set +# CONFIG_INTERPRETERS_MICROPYTHON is not set +# CONFIG_INTERPRETERS_MINIBASIC is not set +# CONFIG_INTERPRETERS_PCODE is not set + +# +# FreeModBus +# +# CONFIG_MODBUS is not set + +# +# Network Utilities +# +# CONFIG_NETUTILS_CODECS is not set +# CONFIG_NETUTILS_DISCOVER is not set +# CONFIG_NETUTILS_ESP8266 is not set +# CONFIG_NETUTILS_FTPC is not set +# CONFIG_NETUTILS_JSON is not set +CONFIG_NETUTILS_NETLIB=y +# CONFIG_NETUTILS_NTPCLIENT is not set +# CONFIG_NETUTILS_PPPD is not set +# CONFIG_NETUTILS_SMTP is not set +# CONFIG_NETUTILS_TELNETD is not set +# CONFIG_NETUTILS_TFTPC is not set +# CONFIG_NETUTILS_WEBCLIENT is not set +# CONFIG_NETUTILS_WEBSERVER is not set +# CONFIG_NETUTILS_XMLRPC is not set + +# +# NSH Library +# +CONFIG_NSH_LIBRARY=y +# CONFIG_NSH_MOTD is not set + +# +# Command Line Configuration +# +CONFIG_NSH_READLINE=y +# CONFIG_NSH_CLE is not set +CONFIG_NSH_LINELEN=64 +# CONFIG_NSH_DISABLE_SEMICOLON is not set +CONFIG_NSH_CMDPARMS=y +CONFIG_NSH_MAXARGUMENTS=6 +CONFIG_NSH_ARGCAT=y +CONFIG_NSH_NESTDEPTH=3 +# CONFIG_NSH_DISABLEBG is not set +CONFIG_NSH_BUILTIN_APPS=y + +# +# Disable Individual commands +# +# CONFIG_NSH_DISABLE_ADDROUTE is not set +# CONFIG_NSH_DISABLE_BASENAME is not set +# CONFIG_NSH_DISABLE_CAT is not set +# CONFIG_NSH_DISABLE_CD is not set +# CONFIG_NSH_DISABLE_CP is not set +# CONFIG_NSH_DISABLE_CMP is not set +CONFIG_NSH_DISABLE_DATE=y +# CONFIG_NSH_DISABLE_DD is not set +# CONFIG_NSH_DISABLE_DF is not set +# CONFIG_NSH_DISABLE_DELROUTE is not set +# CONFIG_NSH_DISABLE_DIRNAME is not set +# CONFIG_NSH_DISABLE_ECHO is not set +# CONFIG_NSH_DISABLE_EXEC is not set +# CONFIG_NSH_DISABLE_EXIT is not set +# CONFIG_NSH_DISABLE_FREE is not set +# CONFIG_NSH_DISABLE_GET is not set +# CONFIG_NSH_DISABLE_HELP is not set +# CONFIG_NSH_DISABLE_HEXDUMP is not set +# CONFIG_NSH_DISABLE_IFCONFIG is not set +# CONFIG_NSH_DISABLE_IFUPDOWN is not set +# CONFIG_NSH_DISABLE_KILL is not set +# CONFIG_NSH_DISABLE_LOSETUP is not set +CONFIG_NSH_DISABLE_LOSMART=y +# CONFIG_NSH_DISABLE_LS is not set +# CONFIG_NSH_DISABLE_MB is not set +# CONFIG_NSH_DISABLE_MKDIR is not set +# CONFIG_NSH_DISABLE_MKFATFS is not set +# CONFIG_NSH_DISABLE_MKRD is not set +# CONFIG_NSH_DISABLE_MH is not set +# CONFIG_NSH_DISABLE_MOUNT is not set +# CONFIG_NSH_DISABLE_MV is not set +# CONFIG_NSH_DISABLE_MW is not set +# CONFIG_NSH_DISABLE_NSLOOKUP is not set +# CONFIG_NSH_DISABLE_POWEROFF is not set +CONFIG_NSH_DISABLE_PRINTF=y +# CONFIG_NSH_DISABLE_PS is not set +# CONFIG_NSH_DISABLE_PUT is not set +# CONFIG_NSH_DISABLE_PWD is not set +# CONFIG_NSH_DISABLE_RM is not set +# CONFIG_NSH_DISABLE_RMDIR is not set +# CONFIG_NSH_DISABLE_SET is not set +# CONFIG_NSH_DISABLE_SH is not set +CONFIG_NSH_DISABLE_SHUTDOWN=y +# CONFIG_NSH_DISABLE_SLEEP is not set +# CONFIG_NSH_DISABLE_TIME is not set +# CONFIG_NSH_DISABLE_TEST is not set +# CONFIG_NSH_DISABLE_UMOUNT is not set +# CONFIG_NSH_DISABLE_UNAME is not set +# CONFIG_NSH_DISABLE_UNSET is not set +# CONFIG_NSH_DISABLE_USLEEP is not set +# CONFIG_NSH_DISABLE_WGET is not set +# CONFIG_NSH_DISABLE_XD is not set +CONFIG_NSH_MMCSDMINOR=0 +CONFIG_NSH_MMCSDSLOTNO=0 + +# +# Configure Command Options +# +# CONFIG_NSH_CMDOPT_DF_H is not set +# CONFIG_NSH_CMDOPT_DD_STATS is not set +CONFIG_NSH_CODECS_BUFSIZE=128 +CONFIG_NSH_CMDOPT_HEXDUMP=y +CONFIG_NSH_PROC_MOUNTPOINT="/proc" +CONFIG_NSH_FILEIOSIZE=512 + +# +# Scripting Support +# +# CONFIG_NSH_DISABLESCRIPT is not set +# CONFIG_NSH_DISABLE_ITEF is not set +# CONFIG_NSH_DISABLE_LOOPS is not set + +# +# Console Configuration +# +CONFIG_NSH_CONSOLE=y +# CONFIG_NSH_ALTCONDEV is not set +CONFIG_NSH_ARCHINIT=y + +# +# Networking Configuration +# +CONFIG_NSH_NETINIT=y +# CONFIG_NSH_NETINIT_THREAD is not set + +# +# IP Address Configuration +# + +# +# Target IPv6 address +# +CONFIG_NSH_IPv6ADDR_1=0xfe80 +CONFIG_NSH_IPv6ADDR_2=0x0000 +CONFIG_NSH_IPv6ADDR_3=0x0000 +CONFIG_NSH_IPv6ADDR_4=0x0000 +CONFIG_NSH_IPv6ADDR_5=0x0000 +CONFIG_NSH_IPv6ADDR_6=0x00ff +CONFIG_NSH_IPv6ADDR_7=0xfe00 +CONFIG_NSH_IPv6ADDR_8=0xa9cd + +# +# Router IPv6 address +# +CONFIG_NSH_DRIPv6ADDR_1=0xfe80 +CONFIG_NSH_DRIPv6ADDR_2=0x0000 +CONFIG_NSH_DRIPv6ADDR_3=0x0000 +CONFIG_NSH_DRIPv6ADDR_4=0x0000 +CONFIG_NSH_DRIPv6ADDR_5=0x0000 +CONFIG_NSH_DRIPv6ADDR_6=0x00ff +CONFIG_NSH_DRIPv6ADDR_7=0xfe00 +CONFIG_NSH_DRIPv6ADDR_8=0x1034 + +# +# IPv6 Network mask +# +CONFIG_NSH_IPv6NETMASK_1=0xffff +CONFIG_NSH_IPv6NETMASK_2=0xffff +CONFIG_NSH_IPv6NETMASK_3=0xffff +CONFIG_NSH_IPv6NETMASK_4=0xffff +CONFIG_NSH_IPv6NETMASK_5=0xffff +CONFIG_NSH_IPv6NETMASK_6=0xffff +CONFIG_NSH_IPv6NETMASK_7=0xffff +CONFIG_NSH_IPv6NETMASK_8=0x0000 +# CONFIG_NSH_DNS is not set +CONFIG_NSH_NOMAC=y +CONFIG_NSH_SWMAC=y +CONFIG_NSH_MACADDR=0xabcd +CONFIG_NSH_MAX_ROUNDTRIP=20 +# CONFIG_NSH_LOGIN is not set +# CONFIG_NSH_CONSOLE_LOGIN is not set + +# +# NxWidgets/NxWM +# + +# +# Platform-specific Support +# +# CONFIG_PLATFORM_CONFIGDATA is not set + +# +# System Libraries and NSH Add-Ons +# +# CONFIG_SYSTEM_CLE is not set +# CONFIG_SYSTEM_CUTERM is not set +# CONFIG_SYSTEM_FLASH_ERASEALL is not set +# CONFIG_SYSTEM_FREE is not set +# CONFIG_SYSTEM_HEX2BIN is not set +# CONFIG_SYSTEM_HEXED is not set +CONFIG_SYSTEM_I2CTOOL=y +CONFIG_I2CTOOL_MINBUS=0 +CONFIG_I2CTOOL_MAXBUS=0 +CONFIG_I2CTOOL_MINADDR=0x03 +CONFIG_I2CTOOL_MAXADDR=0x77 +CONFIG_I2CTOOL_MAXREGADDR=0xff +CONFIG_I2CTOOL_DEFFREQ=400000 +# CONFIG_SYSTEM_INSTALL is not set +# CONFIG_SYSTEM_MDIO is not set +# CONFIG_SYSTEM_NETDB is not set +# CONFIG_SYSTEM_RAMTEST is not set +CONFIG_READLINE_HAVE_EXTMATCH=y +CONFIG_SYSTEM_READLINE=y +CONFIG_READLINE_ECHO=y +# CONFIG_READLINE_TABCOMPLETION is not set +# CONFIG_READLINE_CMD_HISTORY is not set +# CONFIG_SYSTEM_SUDOKU is not set +# CONFIG_SYSTEM_SYSTEM is not set +# CONFIG_SYSTEM_TEE is not set +# CONFIG_SYSTEM_UBLOXMODEM is not set +# CONFIG_SYSTEM_VI is not set +# CONFIG_SYSTEM_ZMODEM is not set diff --git a/configs/sim/sixlowpan/setenv.sh b/configs/sim/sixlowpan/setenv.sh new file mode 100644 index 00000000000..65065ccc9bc --- /dev/null +++ b/configs/sim/sixlowpan/setenv.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# sim/sixlowpan/setenv.sh +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +if [ "$(basename $0)" = "setenv.sh" ] ; then + echo "You must source this script, not run it!" 1>&2 + exit 1 +fi + +if [ -z ${PATH_ORIG} ]; then export PATH_ORIG=${PATH}; fi + +#export NUTTX_BIN= +#export PATH=${NUTTX_BIN}:/sbin:/usr/sbin:${PATH_ORIG} + +echo "PATH : ${PATH}" diff --git a/configs/sim/src/sim_boot.c b/configs/sim/src/sim_boot.c index 826ca47f90d..6e0be20777f 100644 --- a/configs/sim/src/sim_boot.c +++ b/configs/sim/src/sim_boot.c @@ -42,22 +42,6 @@ #include "sim.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/configs/sim/src/sim_bringup.c b/configs/sim/src/sim_bringup.c index 8f0d85c078e..8dd0f75bdd1 100644 --- a/configs/sim/src/sim_bringup.c +++ b/configs/sim/src/sim_bringup.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "up_internal.h" #include "sim.h" @@ -139,5 +140,15 @@ int sim_bringup(void) } #endif +#ifdef CONFIG_IEEE802154_LOOPBACK + /* Initialize and register the IEEE802.15.4 MAC network loop device */ + + ret = ieee8021514_loopback(); + if (ret < 0) + { + _err("ERROR: ieee8021514_loopback() failed: %d\n", ret); + } +#endif + return OK; } diff --git a/configs/sim/udgram/defconfig b/configs/sim/udgram/defconfig index 87e97419c1b..aacb9eb00ef 100644 --- a/configs/sim/udgram/defconfig +++ b/configs/sim/udgram/defconfig @@ -77,6 +77,7 @@ CONFIG_HOST_X86_64=y CONFIG_SIM_X8664_SYSTEMV=y # CONFIG_SIM_X8664_MICROSOFT is not set CONFIG_SIM_WALLTIME=y +CONFIG_SIM_NETDEV=y CONFIG_SIM_NET_HOST_ROUTE=y # CONFIG_SIM_NET_BRIDGE is not set # CONFIG_SIM_FRAMEBUFFER is not set @@ -210,6 +211,8 @@ CONFIG_SCHED_WAITPID=y # # CONFIG_PTHREAD_MUTEX_TYPES is not set CONFIG_PTHREAD_MUTEX_ROBUST=y +# CONFIG_PTHREAD_MUTEX_UNSAFE is not set +# CONFIG_PTHREAD_MUTEX_BOTH is not set CONFIG_NPTHREAD_KEYS=4 # CONFIG_PTHREAD_CLEANUP is not set # CONFIG_CANCELLATION_POINTS is not set @@ -380,6 +383,7 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_PSEUDOTERM is not set # CONFIG_USBDEV is not set # CONFIG_USBHOST is not set +# CONFIG_USBMISC is not set # CONFIG_HAVE_USBTRACE is not set # CONFIG_DRIVERS_WIRELESS is not set # CONFIG_DRIVERS_CONTACTLESS is not set @@ -418,10 +422,12 @@ CONFIG_NET_GUARDSIZE=2 # CONFIG_NET_ETHERNET is not set # CONFIG_NET_LOOPBACK is not set # CONFIG_NET_TUN is not set +# CONFIG_NET_USRSOCK is not set # # Network Device Operations # +# CONFIG_NETDEV_IOCTL is not set # CONFIG_NETDEV_PHY_IOCTL is not set # @@ -453,11 +459,13 @@ CONFIG_NET_LOCAL_DGRAM=y # TCP/IP Networking # # CONFIG_NET_TCP is not set +# CONFIG_NET_TCP_NO_STACK is not set # # UDP Networking # # CONFIG_NET_UDP is not set +# CONFIG_NET_UDP_NO_STACK is not set # # IGMPv2 Client Support @@ -472,6 +480,10 @@ CONFIG_NET_LOCAL_DGRAM=y # Network I/O Buffer Support # # CONFIG_NET_IOB is not set + +# +# User-space networking stack API +# # CONFIG_NET_ARCH_INCR32 is not set # CONFIG_NET_ARCH_CHKSUM is not set # CONFIG_NET_STATISTICS is not set @@ -715,7 +727,6 @@ CONFIG_EXAMPLES_HELLO_STACKSIZE=2048 # CONFIG_EXAMPLES_MM is not set # CONFIG_EXAMPLES_MODBUS is not set # CONFIG_EXAMPLES_MOUNT is not set -# CONFIG_EXAMPLES_NRF24L01TERM is not set CONFIG_EXAMPLES_NSH=y # CONFIG_EXAMPLES_NULL is not set # CONFIG_EXAMPLES_NX is not set @@ -760,6 +771,7 @@ CONFIG_EXAMPLES_UDGRAM_CLIENT_PRIORITY=100 # CONFIG_EXAMPLES_USTREAM is not set # CONFIG_EXAMPLES_WATCHDOG is not set # CONFIG_EXAMPLES_WEBSERVER is not set +# CONFIG_EXAMPLES_XBC_TEST is not set # # File System Utilities diff --git a/configs/sim/ustream/defconfig b/configs/sim/ustream/defconfig index 3b4cf6bdd29..43bb73515db 100644 --- a/configs/sim/ustream/defconfig +++ b/configs/sim/ustream/defconfig @@ -77,6 +77,7 @@ CONFIG_HOST_X86_64=y CONFIG_SIM_X8664_SYSTEMV=y # CONFIG_SIM_X8664_MICROSOFT is not set CONFIG_SIM_WALLTIME=y +CONFIG_SIM_NETDEV=y CONFIG_SIM_NET_HOST_ROUTE=y # CONFIG_SIM_NET_BRIDGE is not set # CONFIG_SIM_FRAMEBUFFER is not set @@ -210,6 +211,8 @@ CONFIG_SCHED_WAITPID=y # # CONFIG_PTHREAD_MUTEX_TYPES is not set CONFIG_PTHREAD_MUTEX_ROBUST=y +# CONFIG_PTHREAD_MUTEX_UNSAFE is not set +# CONFIG_PTHREAD_MUTEX_BOTH is not set CONFIG_NPTHREAD_KEYS=4 # CONFIG_PTHREAD_CLEANUP is not set # CONFIG_CANCELLATION_POINTS is not set @@ -380,6 +383,7 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_PSEUDOTERM is not set # CONFIG_USBDEV is not set # CONFIG_USBHOST is not set +# CONFIG_USBMISC is not set # CONFIG_HAVE_USBTRACE is not set # CONFIG_DRIVERS_WIRELESS is not set # CONFIG_DRIVERS_CONTACTLESS is not set @@ -418,10 +422,12 @@ CONFIG_NET_GUARDSIZE=2 # CONFIG_NET_ETHERNET is not set # CONFIG_NET_LOOPBACK is not set # CONFIG_NET_TUN is not set +# CONFIG_NET_USRSOCK is not set # # Network Device Operations # +# CONFIG_NETDEV_IOCTL is not set # CONFIG_NETDEV_PHY_IOCTL is not set # @@ -453,11 +459,13 @@ CONFIG_NET_LOCAL_STREAM=y # TCP/IP Networking # # CONFIG_NET_TCP is not set +# CONFIG_NET_TCP_NO_STACK is not set # # UDP Networking # # CONFIG_NET_UDP is not set +# CONFIG_NET_UDP_NO_STACK is not set # # IGMPv2 Client Support @@ -472,6 +480,10 @@ CONFIG_NET_LOCAL_STREAM=y # Network I/O Buffer Support # # CONFIG_NET_IOB is not set + +# +# User-space networking stack API +# # CONFIG_NET_ARCH_INCR32 is not set # CONFIG_NET_ARCH_CHKSUM is not set # CONFIG_NET_STATISTICS is not set @@ -715,7 +727,6 @@ CONFIG_EXAMPLES_HELLO_STACKSIZE=2048 # CONFIG_EXAMPLES_MM is not set # CONFIG_EXAMPLES_MODBUS is not set # CONFIG_EXAMPLES_MOUNT is not set -# CONFIG_EXAMPLES_NRF24L01TERM is not set CONFIG_EXAMPLES_NSH=y # CONFIG_EXAMPLES_NULL is not set # CONFIG_EXAMPLES_NX is not set @@ -755,6 +766,7 @@ CONFIG_EXAMPLES_USTREAM_ADDR="/dev/fifo" # CONFIG_EXAMPLES_USTREAM_USE_POLL is not set # CONFIG_EXAMPLES_WATCHDOG is not set # CONFIG_EXAMPLES_WEBSERVER is not set +# CONFIG_EXAMPLES_XBC_TEST is not set # # File System Utilities diff --git a/crypto/random_pool.c b/crypto/random_pool.c index fed59bd727f..7fd761be003 100644 --- a/crypto/random_pool.c +++ b/crypto/random_pool.c @@ -367,7 +367,7 @@ static void rng_buf_internal(FAR void *bytes, size_t nbytes) static void rng_init(void) { - crypinfo("Initializing RNG\n"); + cryptinfo("Initializing RNG\n"); memset(&g_rng, 0, sizeof(struct rng_s)); sem_init(&g_rng.rd_sem, 0, 1); diff --git a/drivers/Kconfig b/drivers/Kconfig index 511d63c5f6c..d7bc2653746 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -552,6 +552,16 @@ if USBHOST source drivers/usbhost/Kconfig endif # USBHOST +menuconfig USBMISC + bool "USB Miscellaneous drivers" + default n + ---help--- + USB Miscellaneous drivers. + +if USBMISC +source drivers/usbmisc/Kconfig +endif # USBMISC + config HAVE_USBTRACE bool default n diff --git a/drivers/Makefile b/drivers/Makefile index 94f027afbbf..5946cc123a7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -72,6 +72,7 @@ include syslog$(DELIM)Make.defs include timers$(DELIM)Make.defs include usbdev$(DELIM)Make.defs include usbhost$(DELIM)Make.defs +include usbmisc$(DELIM)Make.defs include usbmonitor$(DELIM)Make.defs include video$(DELIM)Make.defs include wireless$(DELIM)Make.defs diff --git a/drivers/input/button_upper.c b/drivers/input/button_upper.c index b4432cbad35..f5486752fc5 100644 --- a/drivers/input/button_upper.c +++ b/drivers/input/button_upper.c @@ -158,8 +158,8 @@ static const struct file_operations btn_fops = btn_open, /* open */ btn_close, /* close */ btn_read, /* read */ - 0, /* write */ - 0, /* seek */ + NULL, /* write */ + NULL, /* seek */ btn_ioctl /* ioctl */ #ifndef CONFIG_DISABLE_POLL , btn_poll /* poll */ diff --git a/drivers/net/skeleton.c b/drivers/net/skeleton.c index b33c46e6e09..d354ab8130b 100644 --- a/drivers/net/skeleton.c +++ b/drivers/net/skeleton.c @@ -437,8 +437,8 @@ static void skel_receive(FAR struct skel_driver_s *priv) skel_transmit(priv); } } -#endif else +#endif { NETDEV_RXDROPPED(&priv->sk_dev); } diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 5871c18b815..b5fc76cb419 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -33,11 +33,11 @@ config BMP180 Enable driver support for the Bosch BMP180 barometer sensor. config HTS221 - bool "ST HTS221 humidity sensor" + bool "STMicro HTS221 humidity sensor" default n select I2C ---help--- - Enable driver support for the ST HTS221 humidity sensor. + Enable driver support for the STMicro HTS221 humidity sensor. if HTS221 @@ -56,11 +56,11 @@ config HTS221_NPOLLWAITERS endif # HTS221 config SENSORS_L3GD20 - bool "ST L3GD20 Gyroscope Sensor support" + bool "STMicro L3GD20 Gyroscope Sensor support" default n select SPI ---help--- - Enable driver support for the ST L3GD20 gyroscope sensor. + Enable driver support for the STMicro L3GD20 gyroscope sensor. config SENSOR_KXTJ9 bool "Kionix KXTJ9 Accelerometer support" @@ -75,15 +75,44 @@ config SENSOR_KXTJ9_I2C_BUS_SPEED endif # SENSOR_KXTJ9 +config LIS2DH + bool "STMicro LIS2DH device support" + default n + select I2C + ---help--- + Enable driver support for the STMicro LIS2DH accelerometer + +if LIS2DH + +config DEBUG_LIS2DH + bool "Debug support for the LIS2DH" + default n + ---help--- + Enables debug features for the LIS2DH + +config LIS2DH_NPOLLWAITERS + int "Number of waiters to poll" + default 2 + ---help--- + Maximum number of threads that can be waiting on poll() + +config LIS2DH_DRIVER_SELFTEST + bool "Enable selftest in LIS2DH driver" + default n + ---help--- + Enable selftest in LIS2DH driver + +endif # LIS2DH + config LIS3DSH - bool "STMicro LIS3DSH 3-Axis acclerometer support" + bool "STMicro LIS3DSH 3-Axis accelerometer support" default n select SPI ---help--- - Enable driver support for the STMicro LIS3DSH 3-Axis acclerometer. + Enable driver support for the STMicro LIS3DSH 3-Axis accelerometer. config LIS331DL - bool "ST LIS331DL device support" + bool "STMicro LIS331DL device support" default n select I2C @@ -106,6 +135,23 @@ config LSM9DS1_I2C_FREQUENCY range 1 400000 depends on SN_LSM9DS1 +config LPS25H + bool "STMicro LPS25H pressure sensor" + default n + select I2C + ---help--- + Enable driver support for the STMicro LPS25H barometer sensor. + +if LPS25H + +config DEBUG_LPS25H + bool "Debug support for the LPS25H" + default n + ---help--- + Enables debug features for the LPS25H + +endif # LPS25H + config MB7040 bool "MaxBotix MB7040 Sonar support" default n @@ -209,7 +255,7 @@ endif # SENSORS_ADXL345 config MAX31855 bool "Maxim MAX31855 Driver" default n - select SPI + select SPI ---help--- Enables support for the MAX31855 driver @@ -229,7 +275,7 @@ config LIS3MDL default n select SPI ---help--- - Enable driver support for the ST LIS3MDL 3-axis magnetometer. + Enable driver support for the STMicro LIS3MDL 3-axis magnetometer. config LM75 bool "STMicro LM-75 Temperature Sensor support" diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index d0bb9fa650f..7a5d5a0bba0 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # drivers/sensors/Make.defs # -# Copyright (C) 2011-2012, 2015-2016 Gregory Nutt. All rights reserved. +# Copyright (C) 2011-2012, 2015-2017 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -53,6 +53,10 @@ ifeq ($(CONFIG_SENSOR_KXTJ9),y) CSRCS += kxtj9.c endif +ifeq ($(CONFIG_LIS2DH),y) + CSRCS += lis2dh.c +endif + ifeq ($(CONFIG_LIS3DSH),y) CSRCS += lis3dsh.c endif @@ -65,6 +69,10 @@ ifeq ($(CONFIG_SN_LSM9DS1),y) CSRCS += lsm9ds1.c endif +ifeq ($(CONFIG_LPS25H),y) + CSRCS += lps25h.c +endif + ifeq ($(CONFIG_ADXL345_I2C),y) CSRCS += adxl345_i2c.c endif diff --git a/drivers/sensors/hts221.c b/drivers/sensors/hts221.c index 4dc9c0d2dd1..59c5790ad58 100644 --- a/drivers/sensors/hts221.c +++ b/drivers/sensors/hts221.c @@ -174,13 +174,11 @@ static const struct file_operations g_humidityops = #endif }; -static struct hts221_dev_s *g_humid_data; - /**************************************************************************** * Private Functions ****************************************************************************/ -static int hts221_do_transfer(FAR struct hts221_dev_s *dev, +static int hts221_do_transfer(FAR struct hts221_dev_s *priv, FAR struct i2c_msg_s *msgv, size_t nmsg) { @@ -189,7 +187,7 @@ static int hts221_do_transfer(FAR struct hts221_dev_s *dev, for (retries = 0; retries < HTS221_I2C_RETRIES; retries++) { - ret = I2C_TRANSFER(dev->i2c, msgv, nmsg); + ret = I2C_TRANSFER(priv->i2c, msgv, nmsg); if (ret >= 0) { return 0; @@ -203,7 +201,7 @@ static int hts221_do_transfer(FAR struct hts221_dev_s *dev, break; } - ret = up_i2creset(dev->i2c); + ret = up_i2creset(priv->i2c); if (ret < 0) { hts221_dbg("up_i2creset failed: %d\n", ret); @@ -217,51 +215,51 @@ static int hts221_do_transfer(FAR struct hts221_dev_s *dev, return ret; } -static int32_t hts221_write_reg8(FAR struct hts221_dev_s *dev, +static int32_t hts221_write_reg8(FAR struct hts221_dev_s *priv, const uint8_t *command) { struct i2c_msg_s msgv[2] = { { - .addr = dev->addr, + .addr = priv->addr, .flags = 0, .buffer = (FAR void *)&command[0], .length = 1 }, { - .addr = dev->addr, + .addr = priv->addr, .flags = I2C_M_NORESTART, .buffer = (FAR void *)&command[1], .length = 1 } }; - return hts221_do_transfer(dev, msgv, 2); + return hts221_do_transfer(priv, msgv, 2); } -static int hts221_read_reg(FAR struct hts221_dev_s *dev, +static int hts221_read_reg(FAR struct hts221_dev_s *priv, FAR const uint8_t *command, FAR uint8_t *value) { struct i2c_msg_s msgv[2] = { { - .addr = dev->addr, + .addr = priv->addr, .flags = 0, .buffer = (FAR void *)command, .length = 1 }, { - .addr = dev->addr, + .addr = priv->addr, .flags = I2C_M_READ, .buffer = value, .length = 1 } }; - return hts221_do_transfer(dev, msgv, 2); + return hts221_do_transfer(priv, msgv, 2); } -static int hts221_get_id(FAR struct hts221_dev_s *priv, uint8_t * value) +static int hts221_get_id(FAR struct hts221_dev_s *priv, uint8_t *value) { int ret = OK; uint8_t cmd = HTS221_WHO_AM_I; @@ -362,7 +360,7 @@ static int hts221_config_ctrl_reg2(FAR struct hts221_dev_s *priv, } static int hts221_config_ctrl_reg1(FAR struct hts221_dev_s *priv, - FAR hts221_settings_t * settings) + FAR hts221_settings_t *settings) { int ret = OK; uint8_t regval = 0; @@ -420,7 +418,7 @@ static int hts221_power_on_off(FAR struct hts221_dev_s *priv, bool on) } static int hts221_config(FAR struct hts221_dev_s *priv, - FAR hts221_settings_t * cfgr) + FAR hts221_settings_t *cfgr) { int ret = OK; @@ -477,7 +475,7 @@ static int hts221_start_conversion(FAR struct hts221_dev_s *priv) } static int hts221_check_status(FAR struct hts221_dev_s *priv, - FAR hts221_status_t * status) + FAR hts221_status_t *status) { int ret = OK; uint8_t addr = HTS221_STATUS_REG; @@ -498,7 +496,7 @@ static int hts221_check_status(FAR struct hts221_dev_s *priv, } static int hts221_read_raw_data(FAR struct hts221_dev_s *priv, - FAR hts221_raw_data_t * data) + FAR hts221_raw_data_t *data) { int ret = OK; uint8_t addr_humid_low = HTS221_HUM_OUT_L; @@ -1069,13 +1067,14 @@ out: static int hts221_int_handler(int irq, FAR void *context, FAR void *arg) { - if (!g_humid_data) - return OK; + FAR struct hts221_dev_s *priv = (FAR struct hts221_dev_s *)arg; - g_humid_data->int_pending = true; + DEBUGASSERT(priv != NULL); + + priv->int_pending = true; hts221_dbg("Hts221 interrupt\n"); #ifndef CONFIG_DISABLE_POLL - hts221_notify(g_humid_data); + hts221_notify(priv); #endif return OK; @@ -1095,7 +1094,6 @@ int hts221_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, return -ENOMEM; } - g_humid_data = priv; priv->addr = addr; priv->i2c = i2c; priv->config = config; @@ -1125,7 +1123,7 @@ int hts221_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, priv->config->irq_clear(priv->config); } - priv->config->irq_attach(priv->config, hts221_int_handler); + priv->config->irq_attach(priv->config, hts221_int_handler, priv); priv->config->irq_enable(priv->config, false); return OK; } diff --git a/drivers/sensors/lis2dh.c b/drivers/sensors/lis2dh.c new file mode 100644 index 00000000000..b24aa782e36 --- /dev/null +++ b/drivers/sensors/lis2dh.c @@ -0,0 +1,2040 @@ +/**************************************************************************** + * drivers/sensors/lis2dh.c + * LIS2DH accelerometer driver + * + * Copyright (C) 2014-2017 Haltian Ltd. All rights reserved. + * Authors: Timo Voutilainen + * Jussi Kivilinna + * Juha Niskanen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_LIS2DH +# define lis2dh_dbg(x, ...) _info(x, ##__VA_ARGS__) +#else +# define lis2dh_dbg(x, ...) sninfo(x, ##__VA_ARGS__) +#endif + +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST +# define LSB_AT_10BIT_RESOLUTION 4 +# define LSB_AT_12BIT_RESOLUTION 1 +# define SELFTEST_BUF_SIZE 5 +# define SELFTEST_MAX_READ_ATTEMPTS 200 +# define SELFTEST_ABS_DIFF_MIN_10BIT 17 +# define SELFTEST_ABS_DIFF_MAX_10_BIT 360 +# define SELFTEST_ABS_DIFF_MIN_12BIT (LSB_AT_10BIT_RESOLUTION * SELFTEST_ABS_DIFF_MIN_10BIT) +# define SELFTEST_ABS_DIFF_MAX_12BIT (LSB_AT_10BIT_RESOLUTION * SELFTEST_ABS_DIFF_MAX_10_BIT) +# define SELFTEST_0 0 +# define SELFTEST_1 1 +#endif + +/* Miscellaneous macros */ + +#define LIS2DH_I2C_RETRIES 10 +#define LIS2DH_COUNT_INTS + +/**************************************************************************** + * Private Data Types + ****************************************************************************/ + +enum interrupts +{ + LIS2DH_INT1 = 1, + LIS2DH_INT2 = 2 +}; + +struct lis2dh_dev_s +{ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ + FAR struct lis2dh_config_s *config; /* Platform specific configuration */ + struct lis2dh_setup *setup; /* User defined device operation mode setup */ + struct lis2dh_vector_s vector_data; /* Latest read data read from lis2dh */ + int scale; /* Full scale in milliG */ + sem_t devsem; /* Manages exclusive access to this structure */ + bool fifo_used; /* LIS2DH configured to use FIFO */ + bool fifo_stopped;/* FIFO got full and has stopped. */ +#ifdef LIS2DH_COUNT_INTS + volatile int16_t int_pending; /* Interrupt received but data not read, yet */ +#else + volatile bool int_pending; /* Interrupt received but data not read, yet */ +#endif +#ifndef CONFIG_DISABLE_POLL + struct pollfd *fds[CONFIG_LIS2DH_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function prototypes + ****************************************************************************/ + +static int lis2dh_open(FAR struct file *filep); +static int lis2dh_close(FAR struct file *filep); +static ssize_t lis2dh_read(FAR struct file *, FAR char *, size_t); +static ssize_t lis2dh_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen); +static int lis2dh_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +static int lis2dh_access(FAR struct lis2dh_dev_s *dev, + uint8_t subaddr, FAR uint8_t *buf, int length); +static int lis2dh_get_reading(FAR struct lis2dh_dev_s *dev, + FAR struct lis2dh_vector_s *res, bool force_read); +static int lis2dh_powerdown(FAR struct lis2dh_dev_s *dev); +static int lis2dh_reboot(FAR struct lis2dh_dev_s *dev); +static int lis2dh_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup); +static void lis2dh_notify(FAR struct lis2dh_dev_s *priv); +static int lis2dh_int_handler(int irq, FAR void *context, + FAR void *arg); +static int lis2dh_setup(FAR struct lis2dh_dev_s *dev, + FAR struct lis2dh_setup *new_setup); +static inline int16_t lis2dh_raw_to_mg(uint8_t raw_hibyte, + uint8_t raw_lobyte, int scale); +static int lis2dh_read_temp(FAR struct lis2dh_dev_s *dev, + FAR int16_t *temper); +static int lis2dh_clear_interrupts(FAR struct lis2dh_dev_s *priv, + uint8_t interrupts); +static unsigned int lis2dh_get_fifo_readings(FAR struct lis2dh_dev_s *priv, + FAR struct lis2dh_result *res, unsigned int readcount, + FAR int *perr); +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST +static int lis2dh_handle_selftest(FAR struct lis2dh_dev_s *priv); +static int16_t lis2dh_raw_convert_to_12bit(uint8_t raw_hibyte, + uint8_t raw_lobyte); +static FAR const struct lis2dh_vector_s * + lis2dh_get_raw_readings(FAR struct lis2dh_dev_s *dev, + FAR int *err); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_lis2dhops = +{ + lis2dh_open, /* open */ + lis2dh_close, /* close */ + lis2dh_read, /* read */ + lis2dh_write, /* write */ + NULL, /* seek */ + lis2dh_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , lis2dh_poll /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int lis2dh_who_am_i(FAR struct lis2dh_dev_s *dev, uint8_t *id) +{ + int ret; + + ret = lis2dh_access(dev, ST_LIS2DH_WHOAMI_REG, id, 1); + if (ret < 0) + { + lis2dh_dbg("Cannot read who am i value\n"); + return -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_open + * + * Description: + * This function is called whenever the LIS2DH device is opened. + * + ****************************************************************************/ + +static int lis2dh_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + uint8_t regval; + int ret = OK; + + /* Probe device */ + + if (lis2dh_access(priv, ST_LIS2DH_WHOAMI_REG, ®val, 1) > 0) + { + /* Check chip identification, in the future several more compatible parts + * may be added here. + */ + + if (regval == ST_LIS2DH_WHOAMI_VALUE) + { + priv->config->irq_enable(priv->config, true); + /* Normal exit point */ + ret = lis2dh_clear_interrupts(priv, LIS2DH_INT1 | LIS2DH_INT2); + return ret; + } + + /* Otherwise, we mark an invalid device found at given address */ + + ret = -ENODEV; + } + else + { + /* No response at given address is marked as */ + + ret = -EFAULT; + } + + /* Error exit */ + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_close + * + * Description: + * This routine is called when the LIS2DH device is closed. + * + ****************************************************************************/ + +static int lis2dh_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + + priv->config->irq_enable(priv->config, false); + return lis2dh_powerdown(priv); +} + +/**************************************************************************** + * Name: lis2dh_fifo_start + * + * Description: + * This function restarts FIFO reading. + * + ****************************************************************************/ + +static int lis2dh_fifo_start(FAR struct lis2dh_dev_s *priv) +{ + uint8_t buf; + int err = OK; + + buf = 0x00 | priv->setup->trigger_selection | + priv->setup->fifo_trigger_threshold; + if (lis2dh_access(priv, ST_LIS2DH_FIFO_CTRL_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write FIFO control register\n"); + err = -EIO; + } + else + { + buf = priv->setup->fifo_mode | priv->setup->trigger_selection | + priv->setup->fifo_trigger_threshold; + if (lis2dh_access(priv, ST_LIS2DH_FIFO_CTRL_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write FIFO control register\n"); + err = -EIO; + } + else + { + priv->fifo_stopped = false; + + lis2dh_dbg("lis2dh: FIFO restarted\n"); + } + } + + return err; +} + +/**************************************************************************** + * Name: lis2dh_read + * + * Description: + * This routine is called when the LIS2DH device is read. + * + ****************************************************************************/ + +static ssize_t lis2dh_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + FAR struct lis2dh_result *ptr; + int readcount = (buflen - sizeof(struct lis2dh_res_header)) / sizeof(struct lis2dh_vector_s); + uint8_t buf; + uint8_t int1_src = 0; + uint8_t int2_src = 0; + irqstate_t flags; + int err; + + if (buflen < sizeof(struct lis2dh_result) || + (buflen - sizeof(struct lis2dh_res_header)) % sizeof(struct lis2dh_vector_s) != 0) + { + lis2dh_dbg("lis2dh: Illegal amount of bytes to read: %d\n", buflen); + return -EINVAL; + } + + err = sem_wait(&priv->devsem); + if (err < 0) + { + return -EINTR; + } + + /* Do not allow read() if no SNIOC_WRITESETUP first. */ + + if (!priv->setup) + { + lis2dh_dbg("lis2dh: Read from unconfigured device\n"); + return -EINVAL; + } + + flags = enter_critical_section(); +#ifdef LIS2DH_COUNT_INTS + if (priv->int_pending > 0) + { + priv->int_pending--; + } + + DEBUGASSERT(priv->int_pending >= 0 && priv->int_pending < 10); +#else + priv->int_pending = false; +#endif + leave_critical_section(flags); + + /* Set pointer to first measurement data */ + + ptr = (FAR struct lis2dh_result *)buffer; + ptr->header.meas_count = 0; + + if (!priv->fifo_used) + { + /* FIFO not used, read only one sample. */ + + if (readcount > 0) + { + err = lis2dh_get_reading(priv, &ptr->measurements[0], true); + if (err < 0) + { + lis2dh_dbg("lis2dh: Failed to read xyz\n"); + } + else + { + ptr->header.meas_count = 1; + } + } + } + else /* FIFO modes */ + { + uint8_t fifo_mode = priv->setup->fifo_mode & ST_LIS2DH_FIFOCR_MODE_MASK; + bool fifo_empty = false; + uint8_t fifo_num_samples; + + ptr->header.meas_count = 0; + + do + { + /* Check if FIFO needs to be restarted after being read empty. + * We need to read SRC_REG before reading measurement, as reading + * sample from FIFO clears OVRN_FIFO flag. + */ + + if (lis2dh_access(priv, ST_LIS2DH_FIFO_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read FIFO source register\n"); + return -EIO; + } + + if (fifo_mode != LIS2DH_STREAM_MODE) + { + /* FIFO is full and has stopped. */ + + priv->fifo_stopped |= !!(buf & ST_LIS2DH_FIFOSR_OVRN_FIFO); + } + + if (buf & ST_LIS2DH_FIFOSR_OVRN_FIFO) + { + lis2dh_dbg("lis2dh: FIFO overrun\n"); + } + + if (buf & ST_LIS2DH_FIFOSR_EMPTY) + { + lis2dh_dbg("lis2dh: FIFO empty\n"); + + fifo_empty = true; + + if (fifo_mode != LIS2DH_STREAM_MODE) + { + priv->fifo_stopped = true; + } + + /* FIFO is empty, skip reading. */ + + break; + } + + /* How many samples available in FIFO? */ + + fifo_num_samples = (buf & ST_LIS2DH_FIFOSR_NUM_SAMP_MASK) + 1; + + if (fifo_num_samples > (readcount - ptr->header.meas_count)) + { + fifo_num_samples = (readcount - ptr->header.meas_count); + } + + ptr->header.meas_count += + lis2dh_get_fifo_readings(priv, ptr, fifo_num_samples, &err); + } + while (!fifo_empty && ptr->header.meas_count < readcount); + + if (!fifo_empty && fifo_mode != LIS2DH_TRIGGER_MODE) + { + /* FIFO was not read empty, more data available. */ + + flags = enter_critical_section(); + +#ifdef LIS2DH_COUNT_INTS + priv->int_pending++; +#else + priv->int_pending = true; +#endif + +#ifndef CONFIG_DISABLE_POLL + lis2dh_notify(priv); +#endif + + leave_critical_section(flags); + } + else if (fifo_mode != LIS2DH_STREAM_MODE && priv->fifo_stopped) + { + /* FIFO is empty and has stopped by overrun event. Reset FIFO for + * further reading. + */ + + err = lis2dh_fifo_start(priv); + } + } + + /* Make sure interrupt will get cleared (by reading this register) in case of + * latched configuration. + */ + + buf = 0; + if (lis2dh_access(priv, ST_LIS2DH_INT1_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT1_SRC_REG\n"); + err = -EIO; + } + if (buf & ST_LIS2DH_INT_SR_ACTIVE) + { + /* Interrupt has happened */ + + int1_src = buf; + ptr->header.int1_occurred = true; + } + else + { + ptr->header.int1_occurred = false; + } + + /* Make sure interrupt will get cleared (by reading this register) in case of + * latched configuration. + */ + + buf = 0; + if (lis2dh_access(priv, ST_LIS2DH_INT2_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT2_SRC_REG\n"); + err = -EIO; + } + if (buf & ST_LIS2DH_INT_SR_ACTIVE) + { + /* Interrupt has happened */ + + int2_src = buf; + ptr->header.int2_occurred = true; + } + else + { + ptr->header.int2_occurred = false; + } + ptr->header.int1_source = int1_src; + ptr->header.int2_source = int2_src; + + sem_post(&priv->devsem); + + /* 'err' was just for debugging, we do return partial reads here. */ + + return sizeof(ptr->header) + + ptr->header.meas_count * sizeof(struct lis2dh_vector_s); +} + +/**************************************************************************** + * Name: lis2dh_write + * Description: + * This routine is called when the LIS2DH device is written to. + ****************************************************************************/ + +static ssize_t lis2dh_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + DEBUGASSERT(filep != NULL && buffer != NULL && buflen > 0); + + return -ENOSYS; +} + +/**************************************************************************** + * Name: lis2dh_ioctl + * + * Description: + * This routine is called when ioctl function call + * for the LIS2DH device is done. + * + ****************************************************************************/ + +static int lis2dh_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lis2dh_dev_s *priv = inode->i_private; + int ret; + uint8_t buf; + + DEBUGASSERT(filep != NULL); + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + ret = OK; + switch (cmd) + { + case SNIOC_WRITESETUP: + { + /* Write to the configuration registers. Arg: uint8_t value */ + + ret = lis2dh_setup(priv, (struct lis2dh_setup *)arg); + lis2dh_dbg("lis2dh: conf: %02x ret: %d\n", *(uint8_t*)arg, ret); + + /* Make sure interrupt will get cleared (by reading this register) in + * case of latched configuration. + */ + + lis2dh_clear_interrupts(priv, LIS2DH_INT1 | LIS2DH_INT2); + } + break; + + case SNIOC_WRITE_INT1THRESHOLD: + { + buf = (uint8_t)arg; + + if (lis2dh_access(priv, ST_LIS2DH_INT1_THS_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write INT1_THS_REG\n"); + ret = -EIO; + } + + lis2dh_clear_interrupts(priv, LIS2DH_INT2); + } + break; + + case SNIOC_WRITE_INT2THRESHOLD: + { + buf = (uint8_t)arg; + + if (lis2dh_access(priv, ST_LIS2DH_INT2_THS_REG, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write INT2_THS_REG\n"); + ret = -EIO; + } + + lis2dh_clear_interrupts(priv, LIS2DH_INT2); + } + break; + + case SNIOC_RESET_HPFILTER: + { + /* Read reference register to reset/recalib DC offset for HP filter */ + + if (lis2dh_access(priv, ST_LIS2DH_REFERENCE_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write reference register\n"); + ret = -EIO; + } + + lis2dh_clear_interrupts(priv, LIS2DH_INT2); + } + break; + + case SNIOC_START_SELFTEST: +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST + { + priv->config->irq_enable(priv->config, false); + lis2dh_clear_interrupts(priv, LIS2DH_INT1 | LIS2DH_INT2); + ret = lis2dh_handle_selftest(priv); + priv->config->irq_enable(priv->config, true); + } +#else + { + ret = -EINVAL; + } +#endif + break; + + case SNIOC_READ_TEMP: + { + ret = lis2dh_read_temp(priv, (int16_t *)arg); + } + break; + + case SNIOC_WHO_AM_I: + { + ret = lis2dh_who_am_i(priv, (uint8_t *)arg); + } + break; + + default: + { + lis2dh_dbg("lis2dh: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + } + break; + } + + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: lis2dh_poll + * + * Description: + * This routine is called during LIS2DH device poll + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int lis2dh_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode; + FAR struct lis2dh_dev_s *priv; + int ret = OK; + int i; + + DEBUGASSERT(filep && fds); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct lis2dh_dev_s *)inode->i_private; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + if (setup) + { + /* Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + ret = -EDEADLK; + goto out; + } + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_LIS2DH_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!priv->fds[i]) + { + /* Bind the poll structure and this slot */ + + priv->fds[i] = fds; + fds->priv = &priv->fds[i]; + break; + } + } + + if (i >= CONFIG_LIS2DH_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto out; + } + + if (priv->int_pending) + { + lis2dh_notify(priv); + } + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + DEBUGASSERT(slot != NULL); + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +out: + sem_post(&priv->devsem); + return ret; +} + +static void lis2dh_notify(FAR struct lis2dh_dev_s *priv) +{ + DEBUGASSERT(priv != NULL); + + int i; + + /* If there are threads waiting on poll() for LIS2DH data to become available, + * then wake them up now. NOTE: we wake up all waiting threads because we + * do not know that they are going to do. If they all try to read the data, + * then some make end up blocking after all. + */ + + for (i = 0; i < CONFIG_LIS2DH_NPOLLWAITERS; i++) + { + struct pollfd *fds = priv->fds[i]; + if (fds) + { + fds->revents |= POLLIN; + lis2dh_dbg("lis2dh: Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } +} +#endif /* !CONFIG_DISABLE_POLL */ + +/**************************************************************************** + * Name: lis2dh_callback + * + * Description: + * lis2dh interrupt handler + * + ****************************************************************************/ + +static int lis2dh_int_handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct lis2dh_dev_s *priv = (FAR struct lis2dh_dev_s *)arg; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + + flags = enter_critical_section(); + +#ifdef LIS2DH_COUNT_INTS + priv->int_pending++; +#else + priv->int_pending = true; +#endif + +#ifndef CONFIG_DISABLE_POLL + lis2dh_notify(priv); +#endif + + leave_critical_section(flags); + + return OK; +} + +#ifdef CONFIG_LIS2DH_DRIVER_SELFTEST +/**************************************************************************** + * Name: lis2dh_clear_registers + * + * Description: + * Clear lis2dh registers + * + * Input Parameters: + * priv - pointer to LIS2DH Private Structure + * + * Returned Value: + * Returns OK in case of success, otherwise ERROR + * + ****************************************************************************/ + +static int lis2dh_clear_registers(FAR struct lis2dh_dev_s *priv) +{ + uint8_t i, buf = 0; + + DEBUGASSERT(priv); + + for (i = ST_LIS2DH_TEMP_CFG_REG; i <= ST_LIS2DH_ACT_DUR_REG; i++) + { + /* Skip read only registers */ + + if ((i <= 0x1e) || (i >= 0x27 && i <= 0x2d) || (i == 0x2f) || (i == 0x31)) + { + continue; + } + + if (lis2dh_access(priv, i, &buf, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to clear register 0x%02x\n", i); + return ERROR; + } + } + + return OK; +} + +/**************************************************************************** + * Name: lis2dh_write_register + * + * Description: + * Clear lis2dh registers + * + * Input Parameters: + * priv - pointer to LIS2DH Private Structure + * reg - target register + * value - value to write + * + * Returned Value: + * Returns OK in case of success, otherwise ERROR + * + ****************************************************************************/ + +static int lis2dh_write_register(FAR struct lis2dh_dev_s *priv, uint8_t reg, + uint8_t value) +{ + DEBUGASSERT(priv); + + if (lis2dh_access(priv, reg, &value, -1) != 1) + { + lis2dh_dbg("lis2dh: Failed to write %d to register 0x%02x\n", + value, reg); + return ERROR; + } + return OK; +} + +/**************************************************************************** + * Name: lis2dh_read_register + * + * Description: + * read lis2dh register + * + * Input Parameters: + * priv - pointer to LIS2DH Private Structure + * reg - register to read + * + * Returned Value: + * Returns positive register value in case of success, otherwise ERROR ( < 0) + ****************************************************************************/ + +static int lis2dh_read_register(FAR struct lis2dh_dev_s *priv, uint8_t reg) +{ + uint8_t buf; + + DEBUGASSERT(priv); + + if (lis2dh_access(priv, reg, &buf, sizeof(buf)) == sizeof(buf)) + { + return buf; + } + + return ERROR; +} + +/**************************************************************************** + * Name: lis2dh_handle_selftest + * + * Description: + * Handle selftest. Note, that after running selftest lis2dh is left in + * shutdown mode without valid setup. Therefore SNIOC_WRITESETUP must be + * sent again to proceed with normal operations. + * + ****************************************************************************/ + +static int lis2dh_handle_selftest(FAR struct lis2dh_dev_s *priv) +{ + const struct lis2dh_vector_s *results; + uint8_t i; + uint8_t j; + uint8_t buf; + int16_t avg_x_no_st = 0; + int16_t avg_y_no_st = 0; + int16_t avg_z_no_st = 0; + int16_t avg_x_with_st = 0; + int16_t avg_y_with_st = 0; + int16_t avg_z_with_st = 0; + int16_t abs_st_x_value; + int16_t abs_st_y_value; + int16_t abs_st_z_value; + int ret = OK; + int err = OK; + + DEBUGASSERT(priv); + + lis2dh_powerdown(priv); + + if (lis2dh_clear_registers(priv) != OK) + { + ret = -EIO; + goto out; + } + + /* Set the control register (23h) to ±2g FS, normal mode with BDU (Block + * Data Update) and HR (High Resolution) bits enabled. + */ + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG4, 0x88) != OK) + { + lis2dh_dbg("lis2dh: Failed to write CTRL4 REG for selftest\n"); + ret = -EIO; + goto out; + } + + /* Set the control register (20h) to 50Hz ODR (Output Data Rate) with + * X/Y/Z axis enabled. + */ + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG1, 0x47) != OK) + { + lis2dh_dbg("lis2dh: Failed to write CTRL1 REG for selftest\n"); + ret = -EIO; + goto out; + } + + /* Dummy reads so that values have stabilized */ + + for (i = 0; i < 20; i++) + { + if (lis2dh_get_raw_readings(priv, &err) == NULL) + { + ret = -EIO; + goto out; + } + } + + for (i = 0; i < SELFTEST_BUF_SIZE; i++) + { + results = lis2dh_get_raw_readings(priv, &err); + if (results == NULL) + { + ret = -EIO; + goto out; + } + + avg_x_no_st += results->x; + avg_y_no_st += results->y; + avg_z_no_st += results->z; + } + + avg_x_no_st = avg_x_no_st / SELFTEST_BUF_SIZE; + avg_y_no_st = avg_y_no_st / SELFTEST_BUF_SIZE; + avg_z_no_st = avg_z_no_st / SELFTEST_BUF_SIZE; + + for (i = SELFTEST_0; i <= SELFTEST_1; i++) + { + avg_x_with_st = 0; + avg_y_with_st = 0; + avg_z_with_st = 0; + + /* Enable self-test 0 or 1 at +/-2g FS with BDU and HR bits enabled. */ + + buf = (i == SELFTEST_0) ? 0x8a : 0x8c; + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG4, buf) != OK) + { + lis2dh_dbg("lis2dh: Failed to write CTRL4 REG for selftest\n"); + ret = -EIO; + goto out; + } + + /* Dummy reads so that values have stabilized */ + + for (i = 0; i < 10; i++) + { + if (lis2dh_get_raw_readings(priv, &err) == NULL) + { + ret = -EIO; + goto out; + } + } + + for (j = 0; j < SELFTEST_BUF_SIZE; j++) + { + results = lis2dh_get_raw_readings(priv, &err); + if (results == NULL) + { + ret = -EIO; + goto out; + } + + avg_x_with_st += results->x; + avg_y_with_st += results->y; + avg_z_with_st += results->z; + } + + avg_x_with_st = avg_x_with_st / SELFTEST_BUF_SIZE; + avg_y_with_st = avg_y_with_st / SELFTEST_BUF_SIZE; + avg_z_with_st = avg_z_with_st / SELFTEST_BUF_SIZE; + + abs_st_x_value = abs(avg_x_with_st - avg_x_no_st); + abs_st_y_value = abs(avg_y_with_st - avg_y_no_st); + abs_st_z_value = abs(avg_z_with_st - avg_z_no_st); + + dbg ("ST %d, ABSX: %d, ABSY: %d, ABSZ: %d\n", + i, abs_st_x_value, abs_st_y_value, abs_st_z_value); + + if (abs_st_x_value < SELFTEST_ABS_DIFF_MIN_12BIT || + abs_st_x_value > SELFTEST_ABS_DIFF_MAX_12BIT || + abs_st_y_value < SELFTEST_ABS_DIFF_MIN_12BIT || + abs_st_y_value > SELFTEST_ABS_DIFF_MAX_12BIT || + abs_st_z_value < SELFTEST_ABS_DIFF_MIN_12BIT || + abs_st_z_value > SELFTEST_ABS_DIFF_MAX_12BIT) + { + dbg("Selftest %d fail! Limits (%d <= value <= %d). " + "Results: x: %d, y: %d, z: %d ", + i, + SELFTEST_ABS_DIFF_MIN_12BIT, SELFTEST_ABS_DIFF_MAX_12BIT, + abs_st_x_value, abs_st_y_value, abs_st_z_value); + ret = -ERANGE; + goto out; + } + } + + /* Verify INT1 and INT2 lines */ + + if (lis2dh_clear_registers(priv) != OK) + { + ret = -EIO; + goto out; + } + + /* Both INT lines should be low */ + + if (priv->config->read_int1_pin() != 0) + { + dbg("INT1 line is HIGH - expected LOW\n"); + ret = -ENXIO; + goto out; + } + + if (priv->config->read_int2_pin) + { + if (priv->config->read_int2_pin() != 0) + { + dbg("INT2 line is HIGH - expected LOW\n"); + ret = -ENODEV; + goto out; + } + } + + /* 400Hz ODR all axes enabled + FIFO overrun & DATA READY on INT1 + FIFO enabled and INT1 & INT2 latched + FIFO mode, INT1 , THS 0 + OR combination, all events enabled */ + + if ((lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG1, 0x77) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG3, 0x12) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG5, 0x4a) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_FIFO_CTRL_REG, 0x40) != OK) || + (lis2dh_write_register(priv, ST_LIS2DH_INT1_CFG_REG, 0x3f) != OK)) + { + dbg("Writing registers for INT line check failed\n"); + ret = -EIO; + goto out; + } + + /* Clear INT1 & INT2 */ + + if ((lis2dh_read_register(priv, ST_LIS2DH_INT1_SRC_REG) == ERROR) || + (lis2dh_read_register(priv, ST_LIS2DH_INT2_SRC_REG) == ERROR)) + { + dbg("Failed to clear INT1 / INT2 registers\n"); + ret = -EIO; + goto out; + } + + usleep(20000); + + /* Now INT1 should have been latched high and INT2 should be still low */ + + if (priv->config->read_int1_pin() != 1) + { + dbg("INT1 line is LOW - expected HIGH\n"); + ret = -ENXIO; + goto out; + } + + if (priv->config->read_int2_pin) + { + if (priv->config->read_int2_pin() != 0) + { + dbg("INT2 line is HIGH - expected LOW\n"); + ret = -ENODEV; + goto out; + } + + /* Enable interupt 1 on INT2 pin */ + + if (lis2dh_write_register(priv, ST_LIS2DH_CTRL_REG6, 0x40) != OK) + { + dbg("Failed to enable interrupt 1 on INT2 pin"); + ret = -EIO; + goto out; + } + + usleep(20000); + + if (priv->config->read_int2_pin() != 1) + { + dbg("INT2 line is LOW - expected HIGH\n"); + ret = -ENODEV; + goto out; + } + } + +out: + (void)lis2dh_clear_registers(priv); + lis2dh_powerdown(priv); + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_raw_to_mg + * + * Description: + * Convert raw acceleration value to mg + * + * Input Parameters: + * raw_hibyte - Hi byte of raw data + * raw_lobyte - Lo byte of raw data + * + * Returned Value: + * Returns acceleration value in mg + ****************************************************************************/ + +static int16_t lis2dh_raw_convert_to_12bit(uint8_t raw_hibyte, + uint8_t raw_lobyte) +{ + int16_t value; + + value = (raw_hibyte << 8) | raw_lobyte; + value = value >> 4; + + value &= 0xfff; + if (value & 0x800) + { + value = ~value; + value &= 0xfff; + value += 1; + value = -value; + } + + return value; +} + +/**************************************************************************** + * Name: lis2dh_data_available + * + * Description: + * Check if new data is available to read + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * + * Returned Value: + * Return true if new data is available. Otherwise returns false + * + ****************************************************************************/ + +static bool lis2dh_data_available(FAR struct lis2dh_dev_s *dev) +{ + uint8_t retval; + + DEBUGASSERT(dev); + + if (lis2dh_access(dev, ST_LIS2DH_STATUS_REG, &retval, + sizeof(retval)) == sizeof(retval)) + { + return ((retval & ST_LIS2DH_SR_ZYXDA) != 0); + } + return false; +} + +/**************************************************************************** + * Name: lis2dh_get_raw_readings + * + * Description: + * Read X, Y, Z - acceleration values from chip + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * + * Returned Value: + * Returns acceleration vectors (High resolution = 12bit values) on + * success, NULL otherwise. + * + ****************************************************************************/ + +static FAR const struct lis2dh_vector_s * + lis2dh_get_raw_readings(FAR struct lis2dh_dev_s *dev, int *err) +{ + uint8_t retval[6]; + uint8_t retries_left = SELFTEST_MAX_READ_ATTEMPTS; + + DEBUGASSERT(dev); + + *err = 0; + + while (--retries_left > 0) + { + usleep(20000); + if (lis2dh_data_available(dev)) + { + if (lis2dh_access(dev, ST_LIS2DH_OUT_X_L_REG, retval, + sizeof(retval)) == sizeof(retval)) + { + dev->vector_data.x = lis2dh_raw_convert_to_12bit(retval[1], retval[0]); + dev->vector_data.y = lis2dh_raw_convert_to_12bit(retval[3], retval[2]); + dev->vector_data.z = lis2dh_raw_convert_to_12bit(retval[5], retval[4]); + return &dev->vector_data; + } + + return NULL; + } + } + + return NULL; +} +#endif /* CONFIG_LIS2DH_DRIVER_SELFTEST */ + +/**************************************************************************** + * Name: lis2dh_clear_interrupts + * + * Description: + * Clear interrupts from LIS2DH chip + * + ****************************************************************************/ + +static int lis2dh_clear_interrupts(FAR struct lis2dh_dev_s *priv, + uint8_t interrupts) +{ + uint8_t buf; + int ret = OK; + + if (interrupts & LIS2DH_INT1) + { + /* Make sure interrupt will get cleared (by reading this register) in + * case of latched configuration. + */ + + if (lis2dh_access(priv, ST_LIS2DH_INT1_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT1_SRC_REG\n"); + ret = -EIO; + } + } + + if (interrupts & LIS2DH_INT2) + { + /* Make sure interrupt will get cleared (by reading this register) in + * case of latched configuration. + */ + + if (lis2dh_access(priv, ST_LIS2DH_INT2_SRC_REG, &buf, 1) != 1) + { + lis2dh_dbg("lis2dh: Failed to read INT2_SRC_REG\n"); + ret = -EIO; + } + } + + return ret; +} + +/**************************************************************************** + * Name: lis2dh_get_reading + * + * Description: + * Read X, Y, Z - acceleration value from chip + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * force_read - Read even if new data is not available (old data) + * + * Returned Value: + * Returns OK if success, negative error code otherwise + * + ****************************************************************************/ + +static int lis2dh_get_reading(FAR struct lis2dh_dev_s *dev, + FAR struct lis2dh_vector_s *res, + bool force_read) +{ + int scale = dev->scale; + uint8_t retval[7]; + int16_t x; + int16_t y; + int16_t z; + + if (lis2dh_access(dev, ST_LIS2DH_STATUS_REG, retval, 7) == 7) + { + /* If result is not yet ready, return NULL */ + + if (!force_read && !(retval[0] & ST_LIS2DH_SR_ZYXDA)) + { + lis2dh_dbg("lis2dh: Results were not ready\n"); + return -EAGAIN; + } + + /* Add something to entropy pool. */ + + add_sensor_randomness((((uint32_t)retval[6] << 25) | + ((uint32_t)retval[6] >> 7)) ^ + ((uint32_t)retval[5] << 20) ^ + ((uint32_t)retval[4] << 15) ^ + ((uint32_t)retval[3] << 10) ^ + ((uint32_t)retval[2] << 5) ^ + ((uint32_t)retval[1] << 0)); + + x = lis2dh_raw_to_mg(retval[2], retval[1], scale); + y = lis2dh_raw_to_mg(retval[4], retval[3], scale); + z = lis2dh_raw_to_mg(retval[6], retval[5], scale); + + if (dev->setup->xy_axis_fixup) + { + res->x = y; + res->y = -x; + } + else + { + res->x = x; + res->y = y; + } + + res->z = z; + return OK; + } + + return -EIO; +} + +/**************************************************************************** + * Name: lis2dh_get_fifo_readings + * + * Description: + * Bulk read from FIFO + * + ****************************************************************************/ + +static unsigned int lis2dh_get_fifo_readings(FAR struct lis2dh_dev_s *priv, + FAR struct lis2dh_result *res, + unsigned int readcount, + FAR int *perr) +{ + int scale = priv->scale; + union + { + uint8_t raw[6]; + struct lis2dh_vector_s sample; + } *buf = (void *)&res->measurements[res->header.meas_count]; + bool xy_axis_fixup = priv->setup->xy_axis_fixup; + size_t buflen = readcount * 6; + int16_t x; + int16_t y; + int16_t z; + unsigned int i; + + if (readcount == 0) + { + return 0; + } + + if (lis2dh_access(priv, ST_LIS2DH_OUT_X_L_REG, (void *)buf, buflen) != buflen) + { + lis2dh_dbg("lis2dh: Failed to read FIFO (%d bytes, %d samples)\n", + buflen, readcount); + *perr = -EIO; + return 0; + } + + /* Add something to entropy pool. */ + + up_rngaddentropy(RND_SRC_SENSOR, (void *)buf, buflen / 4); + + /* Convert raw values to mG */ + + for (i = 0; i < readcount; i++) + { + x = lis2dh_raw_to_mg(buf[i].raw[1], buf[i].raw[0], scale); + y = lis2dh_raw_to_mg(buf[i].raw[3], buf[i].raw[2], scale); + z = lis2dh_raw_to_mg(buf[i].raw[5], buf[i].raw[4], scale); + + if (xy_axis_fixup) + { + buf[i].sample.x = y; + buf[i].sample.y = -x; + } + else + { + buf[i].sample.x = x; + buf[i].sample.y = y; + } + + buf[i].sample.z = z; + } + + return readcount; +} + +/**************************************************************************** + * Name: lis2dh_raw_to_mg + * + * Description: + * Convert raw acceleration value to mg + * + * Input Parameters: + * raw_hibyte - Hi byte of raw data + * raw_lobyte - Lo byte of raw data + * scale - full scale in milliG + * + * Returned Value: + * Returns acceleration value in mg + * + ****************************************************************************/ + +static inline int16_t lis2dh_raw_to_mg(uint8_t raw_hibyte, uint8_t raw_lobyte, + int scale) +{ + int16_t value; + + /* Value is signed integer, range INT16_MIN..INT16_MAX. */ + + value = (raw_hibyte << 8) | raw_lobyte; + + /* Scale to mg, INT16_MIN..INT16_MAX => -scale..scale */ + + return (int32_t)value * scale / INT16_MAX; +} + +/**************************************************************************** + * Name: lis2dh_read_temp + * + * Description: + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int lis2dh_read_temp(FAR struct lis2dh_dev_s *dev, FAR int16_t *temper) +{ + int ret; + uint8_t buf[2] = { 0 }; + + ret = lis2dh_access(dev, ST_LIS2DH_OUT_TEMP_L_REG, buf, 2); + if (ret < 0) + { + lis2dh_dbg("Cannot read temperature\n"); + return -EIO; + } + + *temper = buf[0] | ((int16_t)buf[1] << 8); + + return OK; +} + +/**************************************************************************** + * LIS2DH Access with range check + * + * Description: + * Read or write data via I2C + * + * Input Parameters: + * dev LIS2DH Private Structure + * subaddr LIS2DH Sub Address + * buf Pointer to buffer, either for read or write access + * length When >0 it denotes read access, when <0 it denotes write access + * of -length + * + * Returned Value: + * Returns actual length of data on success or negated errno. + * + ****************************************************************************/ + +static int lis2dh_access(FAR struct lis2dh_dev_s *dev, uint8_t subaddr, + FAR uint8_t *buf, int length) +{ + uint16_t flags = 0; + int retval; + int retries; + + DEBUGASSERT(dev != NULL && buf != NULL && length != 0); + + if (length > 0) + { + flags = I2C_M_READ; + } + else + { + flags = I2C_M_NORESTART; + length = -length; + } + + /* Check valid address ranges and set auto address increment flag */ + + if (subaddr == ST_LIS2DH_STATUS_AUX_REG) + { + if (length > 1) + { + length = 1; + } + } + else if (subaddr >= ST_LIS2DH_OUT_TEMP_L_REG && subaddr < 0x10) + { + if (length > (0x10 - subaddr)) + { + length = 0x10 - subaddr; + } + } + + else if (subaddr >= ST_LIS2DH_TEMP_CFG_REG && subaddr <= ST_LIS2DH_ACT_DUR_REG) + { + if (subaddr == ST_LIS2DH_OUT_X_L_REG) + { + /* FIFO bulk read, length maximum 6*32 = 192 bytes. */ + if (length > 6 * 32) + { + length = 6 * 32; + } + } + else + { + if (length > (ST_LIS2DH_ACT_DUR_REG + 1 - subaddr)) + { + length = ST_LIS2DH_ACT_DUR_REG + 1 - subaddr; + } + } + } + else + { + return -EFAULT; + } + + if (length > 1) + { + subaddr |= 0x80; + } + + for (retries = 0; retries < LIS2DH_I2C_RETRIES; retries++) + { + /* Create message and send */ + + struct i2c_msg_s msgv[2] = + { + { + .addr = dev->addr, + .flags = 0, + .buffer = &subaddr, + .length = 1 + }, + { + .addr = dev->addr, + .flags = flags, + .buffer = buf, + .length = length + } + }; + + retval = I2C_TRANSFER(dev->i2c, msgv, 2); + if (retval == OK) + { + return length; + } + else + { + /* Some error. Try to reset I2C bus and keep trying. */ +#ifdef CONFIG_I2C_RESET + int ret = up_i2creset(dev->i2c); + if (ret < 0) + { + lis2dh_dbg("up_i2creset failed: %d\n", ret); + return ret; + } +#endif + continue; + } + } + + lis2dh_dbg("failed, error: %d\n", retval); + return retval; +} + +/**************************************************************************** + * Name: lis2dh_reboot + * + * Description: + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int lis2dh_reboot(FAR struct lis2dh_dev_s *dev) +{ + struct timespec start, curr; + int32_t diff_msec; + uint8_t value; + + (void)clock_gettime(CLOCK_MONOTONIC, &start); + + /* Reboot to reset chip. */ + + value = ST_LIS2DH_CR5_BOOT; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG5, &value, -1) != 1) + { + return -EIO; + } + + /* Reboot is completed when reboot bit is cleared. */ + + do + { + value = 0; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG5, &value, 1) != 1) + { + return -EIO; + } + + if (!(value & ST_LIS2DH_CR5_BOOT)) + { + break; + } + + (void)clock_gettime(CLOCK_MONOTONIC, &curr); + + diff_msec = (curr.tv_sec - start.tv_sec) * 1000; + diff_msec += (curr.tv_nsec - start.tv_nsec) / (1000 * 1000); + + if (diff_msec > 100) + { + return -ETIMEDOUT; + } + + usleep(1); + } + while (true); + + /* Reboot completed, chip is now in power-down state. */ + + return OK; +} + +/**************************************************************************** + * Name: lis2dh_powerdown + * + * Description: + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int lis2dh_powerdown(FAR struct lis2dh_dev_s * dev) +{ + uint8_t buf = 0; + int ret = OK; + + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG1, &buf, -1) != 1) + { + lis2dh_dbg("Failed to clear CTRL_REG1\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * LIS2DH Setup + * + * Description: + * Apply new register setup + * + * Input Parameters: + * dev - pointer to LIS2DH Private Structure + * new_setup - pointer to new setup data to be configured + * + * Returned Value: + * Returns OK on success, ERROR otherwise. + * + ****************************************************************************/ + +static int lis2dh_setup(FAR struct lis2dh_dev_s * dev, + FAR struct lis2dh_setup *new_setup) +{ + uint8_t value; + + dev->setup = new_setup; + + /* Clear old configuration. On first boot after power-loss, reboot bit does + * not get cleared, and lis2dh_reboot() times out. Anyway, chip accepts + * new configuration and functions correctly. */ + + (void)lis2dh_reboot(dev); + + /* TEMP_CFG_REG */ + + value = dev->setup->temp_enable ? (0x3 << 6): 0; + if (lis2dh_access(dev, ST_LIS2DH_TEMP_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG2 */ + + value = dev->setup->hpmode | dev->setup->hpcf | dev->setup->fds | + dev->setup->hpclick | dev->setup->hpis2 | dev->setup->hpis1; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG2, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG3 */ + + value = dev->setup->int1_click_enable | dev->setup->int1_aoi_enable | + dev->setup->int2_aoi_enable | dev->setup->int1_drdy_enable | + dev->setup->int2_drdy_enable | dev->setup->int_wtm_enable | + dev->setup->int_overrun_enable; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG3, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG4 */ + + value = dev->setup->bdu | dev->setup->endian | dev->setup->fullscale | + dev->setup->high_resolution_enable | dev->setup->selftest | + dev->setup->spi_mode; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG4, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG5 */ + + value = dev->setup->reboot | dev->setup->fifo_enable | dev->setup->int1_latch | + dev->setup->int1_4d_enable | dev->setup->int2_latch | + dev->setup->int2_4d_enable; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG5, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG6 */ + + value = dev->setup->int2_click_enable | dev->setup->int_enable | + dev->setup->boot_int1_enable | dev->setup->high_low_active; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG6, &value, -1) != 1) + { + goto error; + } + + /* REFERENCE */ + + value = dev->setup->reference; + if (lis2dh_access(dev, ST_LIS2DH_REFERENCE_REG, &value, -1) != 1) + { + goto error; + } + + /* FIFO_CTRL_REG */ + + value = dev->setup->fifo_mode | dev->setup->trigger_selection | + dev->setup->fifo_trigger_threshold; + if (lis2dh_access(dev, ST_LIS2DH_FIFO_CTRL_REG, &value, -1) != 1) + { + goto error; + } + + /* INT1_CFG */ + + value = dev->setup->int1_interrupt_mode | dev->setup->int1_enable_6d | dev->setup->int1_int_z_high_enable | + dev->setup->int1_int_z_low_enable | dev->setup->int1_int_y_high_enable | + dev->setup->int1_int_y_low_enable | dev->setup->int1_int_x_high_enable | + dev->setup->int1_int_x_low_enable; + if (lis2dh_access(dev, ST_LIS2DH_INT1_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* INT1_THS */ + + value = dev->setup->int1_int_threshold; + if (lis2dh_access(dev, ST_LIS2DH_INT1_THS_REG, &value, -1) != 1) + { + goto error; + } + + /* INT1_DURATION */ + + value = dev->setup->int1_int_duration; + if (lis2dh_access(dev, ST_LIS2DH_INT1_DUR_REG, &value, -1) != 1) + { + goto error; + } + + /* INT2_CFG */ + + value = dev->setup->int2_interrupt_mode | dev->setup->int2_enable_6d | dev->setup->int2_int_z_high_enable | + dev->setup->int2_int_z_low_enable | dev->setup->int2_int_y_high_enable | + dev->setup->int2_int_y_low_enable | dev->setup->int2_int_x_high_enable | + dev->setup->int2_int_x_low_enable; + if (lis2dh_access(dev, ST_LIS2DH_INT2_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* INT2_THS */ + + value = dev->setup->int2_int_threshold; + if (lis2dh_access(dev, ST_LIS2DH_INT2_THS_REG, &value, -1) != 1) + { + goto error; + } + + /* INT2_DURATION */ + + value = dev->setup->int2_int_duration; + if (lis2dh_access(dev, ST_LIS2DH_INT2_DUR_REG, &value, -1) != 1) + { + goto error; + } + + /* CLICK_CFG */ + + value = dev->setup->z_double_click_enable | dev->setup->z_single_click_enable | + dev->setup->y_double_click_enable | dev->setup->y_single_click_enable | + dev->setup->x_double_click_enable | dev->setup->x_single_click_enable; + if (lis2dh_access(dev, ST_LIS2DH_CLICK_CFG_REG, &value, -1) != 1) + { + goto error; + } + + /* CLICK_THS */ + + value = dev->setup->click_threshold; + if (lis2dh_access(dev, ST_LIS2DH_CLICK_THS_REG, &value, -1) != 1) + { + goto error; + } + + /* TIME_LIMIT */ + + value = dev->setup->click_time_limit; + if (lis2dh_access(dev, ST_LIS2DH_TIME_LIMIT_REG, &value, -1) != 1) + { + goto error; + } + + /* TIME_LATENCY */ + + value = dev->setup->click_time_latency; + if (lis2dh_access(dev, ST_LIS2DH_TIME_LATENCY_REG, &value, -1) != 1) + { + goto error; + } + + /* TIME_WINDOW */ + + value = dev->setup->click_time_window; + if (lis2dh_access(dev, ST_LIS2DH_TIME_WINDOW_REG, &value, -1) != 1) + { + goto error; + } + + /* CTRL_REG1 */ + + value = dev->setup->data_rate | dev->setup->low_power_mode_enable | + dev->setup->zen | dev->setup->yen | dev->setup->xen; + if (lis2dh_access(dev, ST_LIS2DH_CTRL_REG1, &value, -1) != 1) + { + goto error; + } + + switch (dev->setup->fullscale & 0x30) + { + default: + case ST_LIS2DH_CR4_FULL_SCALE_2G: + dev->scale = 2000; + break; + + case ST_LIS2DH_CR4_FULL_SCALE_4G: + dev->scale = 4000; + break; + + case ST_LIS2DH_CR4_FULL_SCALE_8G: + dev->scale = 8000; + break; + + case ST_LIS2DH_CR4_FULL_SCALE_16G: + dev->scale = 16000; + break; + } + + if (dev->setup->fifo_enable) + { + dev->fifo_used = true; + + if (lis2dh_fifo_start(dev) < 0) + { + goto error; + } + } + else + { + dev->fifo_used = false; + } + + return OK; + +error: + + /* Setup failed - power down */ + + lis2dh_powerdown(dev); + return -EIO; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lis2dh_register + * + * Description: + * Register the LIS2DH character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/acc0" + * i2c - An instance of the I2C interface to use to communicate with LIS2DH + * addr - The I2C address of the LIS2DH. The base I2C address of the LIS2DH + * is 0x18. Bit 0 can be controlled via SA0 pad - when connected to + * voltage supply the address is 0x19. + * config - Pointer to LIS2DH configuration + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int lis2dh_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct lis2dh_config_s *config) +{ + FAR struct lis2dh_dev_s *priv; + int ret; + + DEBUGASSERT(devpath != NULL && i2c != NULL && config != NULL); + + priv = (FAR struct lis2dh_dev_s *)kmm_zalloc(sizeof(struct lis2dh_dev_s)); + if (!priv) + { + lis2dh_dbg("lis2dh: Failed to allocate instance\n"); + return -ENOMEM; + } + + sem_init(&priv->devsem, 0, 1); + + priv->fifo_used = false; +#ifdef LIS2DH_COUNT_INTS + priv->int_pending = 0; +#else + priv->int_pending = false; +#endif + + priv->i2c = i2c; + priv->addr = addr; + priv->config = config; + + ret = register_driver(devpath, &g_lis2dhops, 0666, priv); + if (ret < 0) + { + lis2dh_dbg("lis2dh: Failed to register driver: %d\n", ret); + goto errout_with_priv; + } + + if (priv->config->irq_clear) + { + priv->config->irq_clear(config); + } + priv->config->irq_attach(config, lis2dh_int_handler, priv); + priv->config->irq_enable(config, false); + return OK; + +errout_with_priv: + sem_destroy(&priv->devsem); + kmm_free(priv); + + return ret; +} diff --git a/drivers/sensors/lps25h.c b/drivers/sensors/lps25h.c new file mode 100644 index 00000000000..3ebba982857 --- /dev/null +++ b/drivers/sensors/lps25h.c @@ -0,0 +1,793 @@ +/**************************************************************************** + * drivers/sensors/lps25h.c + * + * Copyright (C) 2014-2017 Haltian Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_LPS25H +# define lps25h_dbg(x, ...) _info(x, ##__VA_ARGS__) +#else +# define lps25h_dbg(x, ...) sninfo(x, ##__VA_ARGS__) +#endif + +#define LPS25H_PRESSURE_INTERNAL_DIVIDER 4096 + +/* 'AN4450 - Hardware and software guidelines for use of LPS25H pressure + * sensors' - '6.2 One-shot mode conversion time estimation' gives estimates + * for conversion times: + * + * Typical conversion time ≈ 62*(Pavg+Tavg) + 975 μs + * ex: Tavg = 64; Pavg = 512; Typ. conversation time ≈ 36.7 ms (compatible with + * ODT=25 Hz) + * ex: Tavg = 32; Pavg = 128; Typ. conversation time ≈ 10.9 ms + * The formula is accurate within +/- 3% at room temperature + * + * Set timeout to 2 * max.conversation time (2*36.7*1.03 = 76 ms). + */ + +#define LPS25H_RETRY_TIMEOUT_MSECS 76 +#define LPS25H_MAX_RETRIES 5 + +#define LPS25H_I2C_RETRIES 10 + +/* Registers */ + +#define LPS25H_REF_P_XL 0x08 +#define LPS25H_REF_P_L 0x09 +#define LPS25H_REF_P_H 0x0a +#define LPS25H_WHO_AM_I 0x0f +#define LPS25H_RES_CONF 0x10 +#define LPS25H_CTRL_REG1 0x20 +#define LPS25H_CTRL_REG2 0x21 +#define LPS25H_CTRL_REG3 0x22 +#define LPS25H_CTRL_REG4 0x23 +#define LPS25H_INT_CFG 0x24 +#define LPS25H_INT_SOURCE 0x25 +#define LPS25H_STATUS_REG 0x27 +#define LPS25H_PRESS_POUT_XL 0x28 +#define LPS25H_PRESS_OUT_L 0x29 +#define LPS25H_PRESS_OUT_H 0x2a +#define LPS25H_TEMP_OUT_L 0x2b +#define LPS25H_TEMP_OUT_H 0x2c +#define LPS25H_FIFO_CTRL 0x2e +#define LPS25H_FIFO_STATUS 0x2f +#define LPS25H_THS_P_L 0x30 +#define LPS25H_THS_P_H 0x31 +#define LPS25H_RPDS_L 0x39 +#define LPS25H_RPDS_H 0x3a + +/* Bits in registers */ + +#define LPS25H_AUTO_ZERO (1 << 2) +#define LPS25H_BDU (1 << 2) +#define LPS25H_DIFF_EN (1 << 3) +#define LPS25H_FIFO_EN (1 << 6) +#define LPS25H_WTM_EN (1 << 5) +#define LPS25H_FIFO_MEAN_DEC (1 << 4) +#define LPS25H_PD (1 << 7) +#define LPS25H_ONE_SHOT (1 << 0) +#define LPS25H_INT_H_L (1 << 7) +#define LPS25H_PP_OD (1 << 6) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct lps25h_dev_s +{ + struct i2c_master_s *i2c; + uint8_t addr; + bool irqenabled; + volatile bool int_pending; + sem_t devsem; + sem_t waitsem; + lps25h_config_t *config; +}; + +enum LPS25H_RES_CONF_AVG_PRES +{ + PRES_AVG_8 = 0, + PRES_AVG_32, + PRES_AVG_128, + PRES_AVG_512 +}; + +enum LPS25H_RES_CONF_AVG_TEMP +{ + TEMP_AVG_8 = 0, + TEMP_AVG_16, + TEMP_AVG_32, + TEMP_AVG_64 +}; + +enum LPS25H_CTRL_REG1_ODR +{ + CTRL_REG1_ODR_ONE_SHOT = 0, + CTRL_REG1_ODR_1Hz, + CTRL_REG1_ODR_7Hz, + CTRL_REG1_ODR_12_5Hz, + CTRL_REG1_ODR_25Hz +}; + +enum LPS25H_CTRL_REG4_P1 +{ + P1_DRDY = 0x1, + P1_OVERRUN = 0x02, + P1_WTM = 0x04, + P1_EMPTY = 0x08 +}; + +enum LPS25H_FIFO_CTRL_MODE +{ + BYPASS_MODE = 0x0, + FIFO_STOP_WHEN_FULL, + STREAM_NEWEST_IN_FIFO, + STREAM_DEASSERTED, + BYPASS_DEASSERTED_STREAM, + FIFO_MEAN = 0x06, + BYPASS_DEASSERTED_FIFO +}; + +enum LPS25H_FIFO_CTRL_WTM +{ + SAMPLE_2 = 0x01, + SAMPLE_4 = 0x03, + SAMPLE_8 = 0x07, + SAMPLE_16 = 0x0f, + SAMPLE_32 = 0x1f +}; + +enum LPS25H_INT_CFG_OP +{ + PH_E = 0x1, + PL_E = 0x2, + LIR = 0x4 +}; + +/************************************************************************************ + * Private Function Prototypes + ************************************************************************************/ + +static int lps25h_open(FAR struct file *filep); +static int lps25h_close(FAR struct file *filep); +static ssize_t lps25h_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t lps25h_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int lps25h_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +static int lps25h_configure_dev(FAR struct lps25h_dev_s *dev); +static int lps25h_read_pressure(FAR struct lps25h_dev_s *dev, + FAR lps25h_pressure_data_t *pres); +static int lps25h_read_temper(FAR struct lps25h_dev_s *dev, + FAR lps25h_temper_data_t *temper); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_lps25hops = +{ + lps25h_open, /* open */ + lps25h_close, /* close */ + lps25h_read, /* read */ + lps25h_write, /* write */ + NULL, /* seek */ + lps25h_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , NULL /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ + #endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int lps25h_do_transfer(FAR struct lps25h_dev_s *dev, + FAR struct i2c_msg_s *msgv, + size_t nmsg) +{ + int ret = -EIO; + int retries; + + for (retries = 0; retries < LPS25H_I2C_RETRIES; retries++) + { + ret = I2C_TRANSFER(dev->i2c, msgv, nmsg); + if (ret >= 0) + { + return 0; + } + else + { + /* Some error. Try to reset I2C bus and keep trying. */ +#ifdef CONFIG_I2C_RESET + if (retries == LPS25H_I2C_RETRIES - 1) + { + break; + } + + ret = up_i2creset(dev->i2c); + if (ret < 0) + { + lps25h_dbg("up_i2creset failed: %d\n", ret); + return ret; + } +#endif + } + } + + lps25h_dbg("xfer failed: %d\n", ret); + return ret; +} + +static int lps25h_write_reg8(struct lps25h_dev_s *dev, uint8_t reg_addr, + const uint8_t value) +{ + struct i2c_msg_s msgv[2] = + { + { + .addr = dev->addr, + .flags = 0, + .buffer = ®_addr, + .length = 1 + }, + { + .addr = dev->addr, + .flags = I2C_M_NORESTART, + .buffer = (void *)&value, + .length = 1 + } + }; + + return lps25h_do_transfer(dev, msgv, 2); +} + +static int lps25h_read_reg8(FAR struct lps25h_dev_s *dev, + FAR uint8_t *reg_addr, + FAR uint8_t *value) +{ + struct i2c_msg_s msgv[2] = + { + { + .addr = dev->addr, + .flags = 0, + .buffer = reg_addr, + .length = 1 + }, + { + .addr = dev->addr, + .flags = I2C_M_READ, + .buffer = value, + .length = 1 + } + }; + + return lps25h_do_transfer(dev, msgv, 2); +} + +static int lps25h_power_on_off(FAR struct lps25h_dev_s *dev, bool on) +{ + int ret; + uint8_t value; + + value = on ? LPS25H_PD : 0; + ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG1, value); + return ret; +} + +static int lps25h_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lps25h_dev_s *dev = inode->i_private; + uint8_t value = 0; + uint8_t addr = LPS25H_WHO_AM_I; + int32_t ret; + + while (sem_wait(&dev->devsem) != 0) + { + assert(errno == EINTR); + } + + dev->config->set_power(dev->config, true); + ret = lps25h_read_reg8(dev, &addr, &value); + if (ret < 0) + { + lps25h_dbg("Cannot read device's ID\n"); + dev->config->set_power(dev->config, false); + goto out; + } + + lps25h_dbg("WHO_AM_I: 0x%2x\n", value); + + dev->config->irq_enable(dev->config, true); + dev->irqenabled = true; + +out: + sem_post(&dev->devsem); + return ret; +} + +static int lps25h_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lps25h_dev_s *dev = inode->i_private; + int ret; + + while (sem_wait(&dev->devsem) != 0) + { + assert(errno == EINTR); + } + + dev->config->irq_enable(dev->config, false); + dev->irqenabled = false; + ret = lps25h_power_on_off(dev, false); + dev->config->set_power(dev->config, false); + lps25h_dbg("CLOSED\n"); + + sem_post(&dev->devsem); + return ret; +} + +static ssize_t lps25h_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lps25h_dev_s *dev = inode->i_private; + int ret; + ssize_t length = 0; + lps25h_pressure_data_t data; + + while (sem_wait(&dev->devsem) != 0) + { + assert(errno == EINTR); + } + + ret = lps25h_configure_dev(dev); + if (ret < 0) + { + lps25h_dbg("cannot configure sensor: %d\n", ret); + goto out; + } + + ret = lps25h_read_pressure(dev, &data); + if (ret < 0) + { + lps25h_dbg("cannot read data: %d\n", ret); + } + else + { + /* This interface is mainly intended for easy debugging in nsh. */ + + length = snprintf(buffer, buflen, "%u\n", data.pressure_Pa); + if (length > buflen) + { + length = buflen; + } + } + +out: + sem_post(&dev->devsem); + return length; +} + +static ssize_t lps25h_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + ssize_t length = 0; + + return length; +} + +static void lps25h_notify(FAR struct lps25h_dev_s *dev) +{ + DEBUGASSERT(dev != NULL); + + dev->int_pending = true; + sem_post(&dev->waitsem); +} + +static int lps25h_int_handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct lps25h_dev_s *dev = (FAR struct lps25h_dev_s *)arg; + + DEBUGASSERT(dev != NULL); + + lps25h_notify(dev); + lps25h_dbg("lps25h interrupt\n"); + return OK; +} + +static int lps25h_configure_dev(FAR struct lps25h_dev_s *dev) +{ + int ret = 0; + + ret = lps25h_power_on_off(dev, false); + if (ret < 0) + { + return ret; + } + + /* Enable FIFO */ + + ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG2, LPS25H_FIFO_EN); + if (ret < 0) + { + return ret; + } + + ret = lps25h_write_reg8(dev, LPS25H_FIFO_CTRL, (BYPASS_MODE << 5)); + if (ret < 0) + { + return ret; + } + + ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG4, P1_DRDY); + if (ret < 0) + { + return ret; + } + + /* Write CTRL_REG1 to turn device on */ + + ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG1, + LPS25H_PD | (CTRL_REG1_ODR_1Hz << 4)); + + return ret; +} + +static int lps25h_one_shot(FAR struct lps25h_dev_s *dev) +{ + int ret = ERROR; + int retries; + struct timespec abstime; + irqstate_t flags; + + if (!dev->irqenabled) + { + lps25h_dbg("IRQ disabled!\n"); + } + + /* Retry one-shot measurement multiple times. */ + + for (retries = 0; retries < LPS25H_MAX_RETRIES; retries++) + { + /* Power off so we start from a known state. */ + + ret = lps25h_power_on_off(dev, false); + if (ret < 0) + { + return ret; + } + + /* Initiate a one shot mode measurement */ + + ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG2, LPS25H_ONE_SHOT); + if (ret < 0) + { + return ret; + } + + /* Power on to start measurement. */ + + ret = lps25h_power_on_off(dev, true); + if (ret < 0) + { + return ret; + } + + (void)clock_gettime(CLOCK_REALTIME, &abstime); + abstime.tv_sec += (LPS25H_RETRY_TIMEOUT_MSECS / 1000); + abstime.tv_nsec += (LPS25H_RETRY_TIMEOUT_MSECS % 1000) * 1000 * 1000; + while (abstime.tv_nsec >= (1000 * 1000 * 1000)) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } + + while ((ret = sem_timedwait(&dev->waitsem, &abstime)) != 0) + { + int err = errno; + if (err == EINTR) + { + continue; + } + else if (err == ETIMEDOUT) + { + uint8_t reg = LPS25H_CTRL_REG2; + uint8_t value; + + /* In 'AN4450 - Hardware and software guidelines for use of + * LPS25H pressure sensors' - '4.3 One-shot mode measurement + * sequence', one-shot mode example is given where interrupt line + * is not used, but CTRL_REG2 is polled until ONE_SHOT bit is + * unset (as it is self-clearing). Check ONE_SHOT bit status here + * to see if we just missed interrupt. + */ + + ret = lps25h_read_reg8(dev, ®, &value); + if (ret < 0) + { + break; + } + + if ((value & LPS25H_ONE_SHOT) == 0) + { + /* One-shot completed. */ + + ret = OK; + break; + } + } + else + { + /* Some unknown mystery error */ + + DEBUGASSERT(false); + return -err; + } + } + + if (ret == OK) + { + break; + } + + lps25h_dbg("Retrying one-shot measurement: retries=%d\n", retries); + } + + if (ret != OK) + { + return -ETIMEDOUT; + } + + flags = enter_critical_section(); + dev->int_pending = false; + leave_critical_section(flags); + + return ret; +} + +static int lps25h_read_pressure(FAR struct lps25h_dev_s *dev, + FAR lps25h_pressure_data_t *pres) +{ + int ret; + uint8_t pres_addr_h = LPS25H_PRESS_OUT_H; + uint8_t pres_addr_l = LPS25H_PRESS_OUT_L; + uint8_t pres_addr_xl = LPS25H_PRESS_POUT_XL; + uint8_t pres_value_h = 0; + uint8_t pres_value_l = 0; + uint8_t pres_value_xl = 0; + int32_t pres_res = 0; + + ret = lps25h_one_shot(dev); + if (ret < 0) + { + return ret; + } + + ret = lps25h_read_reg8(dev, &pres_addr_h, &pres_value_h); + if (ret < 0) + { + return ret; + } + + ret = lps25h_read_reg8(dev, &pres_addr_l, &pres_value_l); + if (ret < 0) + { + return ret; + } + + ret = lps25h_read_reg8(dev, &pres_addr_xl, &pres_value_xl); + if (ret < 0) + { + return ret; + } + + pres_res = ((int32_t) pres_value_h << 16) | + ((int16_t) pres_value_l << 8) | + pres_value_xl; + + /* Add to entropy pool. */ + + add_sensor_randomness(pres_res); + + /* Convert to more usable format. */ + + pres->pressure_int_hP = pres_res / LPS25H_PRESSURE_INTERNAL_DIVIDER; + pres->pressure_Pa = (uint64_t)pres_res * 100000 / LPS25H_PRESSURE_INTERNAL_DIVIDER; + pres->raw_data = pres_res; + lps25h_dbg("Pressure: %u Pa\n", pres->pressure_Pa); + + return ret; +} + +static int lps25h_read_temper(FAR struct lps25h_dev_s *dev, + FAR lps25h_temper_data_t *temper) +{ + int ret; + uint8_t temper_addr_h = LPS25H_TEMP_OUT_H; + uint8_t temper_addr_l = LPS25H_TEMP_OUT_L; + uint8_t temper_value_h = 0; + uint8_t temper_value_l = 0; + int32_t temper_res; + int16_t raw_data; + + ret = lps25h_read_reg8(dev, &temper_addr_h, &temper_value_h); + if (ret < 0) + { + return ret; + } + + ret = lps25h_read_reg8(dev, &temper_addr_l, &temper_value_l); + if (ret < 0) + { + return ret; + } + + raw_data = (temper_value_h << 8) | temper_value_l; + + /* Add to entropy pool. */ + + add_sensor_randomness(raw_data); + + /* T(â°C) = 42.5 + (raw / 480) + * => + * T(â°C) * scale = (425 * 48 + raw) * scale / 480; + */ + + temper_res = (425 * 48 + raw_data); + temper_res *= LPS25H_TEMPER_DIVIDER; + temper_res /= 480; + + temper->int_temper = temper_res; + temper->raw_data = raw_data; + lps25h_dbg("Temperature: %d\n", temper_res); + + return ret; +} + +static int lps25h_who_am_i(struct lps25h_dev_s *dev, + lps25h_who_am_i_data * who_am_i_data) +{ + uint8_t who_addr = LPS25H_WHO_AM_I; + return lps25h_read_reg8(dev, &who_addr, &who_am_i_data->who_am_i); +} + +static int lps25h_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lps25h_dev_s *dev = inode->i_private; + int ret = 0; + + while (sem_wait(&dev->devsem) != 0) + { + assert(errno == EINTR); + } + + switch (cmd) + { + case SNIOC_CFGR: + ret = lps25h_configure_dev(dev); + break; + + case SNIOC_PRESSURE_OUT: + ret = lps25h_read_pressure(dev, (lps25h_pressure_data_t *) arg); + break; + + case SNIOC_TEMPERATURE_OUT: + /* NOTE: call SNIOC_PRESSURE_OUT before this one, + * or results are bogus. + */ + + ret = lps25h_read_temper(dev, (lps25h_temper_data_t *) arg); + break; + + case SNIOC_SENSOR_OFF: + ret = lps25h_power_on_off(dev, false); + break; + + case SNIOC_GET_DEV_ID: + ret = lps25h_who_am_i(dev, (lps25h_who_am_i_data *) arg); + break; + + default: + ret = -EINVAL; + break; + } + + sem_post(&dev->devsem); + return ret; +} + +int lps25h_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR lps25h_config_t *config) +{ + int ret = 0; + FAR struct lps25h_dev_s *dev; + + dev = (struct lps25h_dev_s *)kmm_zalloc(sizeof(struct lps25h_dev_s)); + if (!dev) + { + lps25h_dbg("Memory cannot be allocated for LPS25H sensor\n"); + return -ENOMEM; + } + + sem_init(&dev->devsem, 0, 1); + sem_init(&dev->waitsem, 0, 0); + + dev->addr = addr; + dev->i2c = i2c; + dev->config = config; + + if (dev->config->irq_clear) + { + dev->config->irq_clear(dev->config); + } + + ret = register_driver(devpath, &g_lps25hops, 0666, dev); + + lps25h_dbg("Registered with %d\n", ret); + + if (ret < 0) + { + kmm_free(dev); + lps25h_dbg("Error occurred during the driver registering\n"); + return ERROR; + } + + dev->config->irq_attach(config, lps25h_int_handler, dev); + dev->config->irq_enable(config, false); + dev->irqenabled = false; + return OK; +} diff --git a/drivers/usbmisc/Kconfig b/drivers/usbmisc/Kconfig new file mode 100644 index 00000000000..68bd619ee3a --- /dev/null +++ b/drivers/usbmisc/Kconfig @@ -0,0 +1,29 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +comment "USB Miscellaneous drivers" + +config FUSB301 + bool "Fairchild FUSB301 USB type-C controller support" + default n + select I2C + ---help--- + Enable device driver for Fairchild USB type-C controller + +if FUSB301 + +config DEBUG_FUSB301 + bool "Enable debug support for the FUSB301" + default n + ---help--- + Enables debug support for the FUSB301 + +config FUSB301_NPOLLWAITERS + int "Number of waiters to poll" + default 2 + ---help--- + Maximum number of threads that can be waiting on poll() + +endif diff --git a/drivers/usbmisc/Make.defs b/drivers/usbmisc/Make.defs new file mode 100644 index 00000000000..fd9dd6af37e --- /dev/null +++ b/drivers/usbmisc/Make.defs @@ -0,0 +1,49 @@ +############################################################################ +# drivers/usbmisc/Make.defs +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_USBMISC),y) + +# Include USB miscellaneous drivers + +ifeq ($(CONFIG_FUSB301),y) + CSRCS += fusb301.c +endif + +# Include USB miscellaneous build support + +DEPPATH += --dep-path usbmisc +VPATH += :usbmisc +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)usbmisc} +endif diff --git a/drivers/usbmisc/fusb301.c b/drivers/usbmisc/fusb301.c new file mode 100644 index 00000000000..c3627fd909d --- /dev/null +++ b/drivers/usbmisc/fusb301.c @@ -0,0 +1,855 @@ +/**************************************************************************** + * drivers/usbmisc/fusb301.c + * + * FUSB301 USB-C controller driver + * + * Copyright (C) 2016-2017 Haltian Ltd. All rights reserved. + * Authors: Harri Luhtala + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_FUSB301 +# define fusb301_err(x, ...) _err(x, ##__VA_ARGS__) +# define fusb301_info(x, ...) _info(x, ##__VA_ARGS__) +#else +# define fusb301_err(x, ...) uerr(x, ##__VA_ARGS__) +# define fusb301_info(x, ...) uinfo(x, ##__VA_ARGS__) +#endif + +/* Other macros */ + +#define FUSB301_I2C_RETRIES 10 + +/**************************************************************************** + * Private Data Types + ****************************************************************************/ + +struct fusb301_dev_s +{ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ + volatile bool int_pending; /* Interrupt received but handled */ + sem_t devsem; /* Manages exclusive access */ + FAR struct fusb301_config_s *config; /* Platform specific configuration */ +#ifndef CONFIG_DISABLE_POLL + FAR struct pollfd *fds[CONFIG_FUSB301_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function prototypes + ****************************************************************************/ + +static int fusb301_open(FAR struct file *filep); +static int fusb301_close(FAR struct file *filep); +static ssize_t fusb301_read(FAR struct file *, FAR char *, size_t); +static ssize_t fusb301_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int fusb301_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +#ifndef CONFIG_DISABLE_POLL +static int fusb301_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +static void fusb301_notify(FAR struct fusb301_dev_s *priv); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_fusb301ops = +{ + fusb301_open, /* open */ + fusb301_close, /* close */ + fusb301_read, /* read */ + fusb301_write, /* write */ + NULL, /* seek */ + fusb301_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , fusb301_poll /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ + #endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fusb301_getreg + * + * Description: + * Read from an 8-bit FUSB301 register + * + * Input Parameters: + * priv - pointer to FUSB301 Private Structure + * reg - register to read + * + * Returned Value: + * Returns positive register value in case of success, otherwise ERROR + ****************************************************************************/ + +static int fusb301_getreg(FAR struct fusb301_dev_s *priv, uint8_t reg) +{ + int ret = -EIO; + int retries; + uint8_t regval; + struct i2c_msg_s msg[2]; + + DEBUGASSERT(priv); + + msg[0].addr = priv->addr; + msg[0].flags = 0; + msg[0].buffer = ® + msg[0].length = 1; + + msg[1].addr = priv->addr; + msg[1].flags = I2C_M_READ; + msg[1].buffer = ®val; + msg[1].length = 1; + + /* Perform the transfer */ + + for (retries = 0; retries < FUSB301_I2C_RETRIES; retries++) + { + ret = I2C_TRANSFER(priv->i2c, msg, 2); + if (ret == OK) + { + fusb301_info("reg:%02X, value:%02X\n", reg, regval); + return regval; + } + else + { + /* Some error. Try to reset I2C bus and keep trying. */ + +#ifdef CONFIG_I2C_RESET + if (retries == FUSB301_I2C_RETRIES - 1) + { + break; + } + + ret = up_i2creset(priv->i2c); + if (ret < 0) + { + fusb301_err("ERROR: up_i2creset failed: %d\n", ret); + return ret; + } +#endif + } + } + + fusb301_info("reg:%02X, error:%d\n", reg, ret); + return ret; +} + +/**************************************************************************** + * Name: fusb301_putreg + * + * Description: + * Write a value to an 8-bit FUSB301 register + * + * Input Parameters: + * priv - pointer to FUSB301 Private Structure + * regaddr - register to read + * regval - value to be written + * + * Returned Value: + * None + ****************************************************************************/ + +static int fusb301_putreg(FAR struct fusb301_dev_s *priv, uint8_t regaddr, + uint8_t regval) +{ + int ret = -EIO; + int retries; + struct i2c_msg_s msg; + uint8_t txbuffer[2]; + + /* Setup to the data to be transferred (register address and data). */ + + txbuffer[0] = regaddr; + txbuffer[1] = regval; + + /* Setup 8-bit FUSB301 address write message */ + + msg.addr = priv->addr; + msg.flags = 0; + msg.buffer = txbuffer; + msg.length = 2; + + /* Perform the transfer */ + + for (retries = 0; retries < FUSB301_I2C_RETRIES; retries++) + { + ret = I2C_TRANSFER(priv->i2c, &msg, 1); + if (ret == OK) + { + fusb301_info("reg:%02X, value:%02X\n", regaddr, regval); + + return OK; + } + else + { + /* Some error. Try to reset I2C bus and keep trying. */ + +#ifdef CONFIG_I2C_RESET + if (retries == FUSB301_I2C_RETRIES - 1) + { + break; + } + + ret = up_i2creset(priv->i2c); + if (ret < 0) + { + fusb301_err("ERROR: up_i2creset failed: %d\n", ret); + return ret; + } +#endif + } + } + + fusb301_err("ERROR: failed reg:%02X, value:%02X, error:%d\n", + regaddr, regval, ret); + return ret; +} + +/**************************************************************************** + * Name: fusb301_read_device_id + * + * Description: + * Read device version and revision IDs + * + ****************************************************************************/ + +static int fusb301_read_device_id(FAR struct fusb301_dev_s * priv, + FAR uint8_t * arg) +{ + int ret; + + ret = fusb301_getreg(priv, FUSB301_DEV_ID_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to read device ID\n"); + return -EIO; + } + + *arg = ret; + return OK; +} + +/**************************************************************************** + * Name: fusb301_clear_interrupts + * + * Description: + * Clear interrupts from FUSB301 chip + * + ****************************************************************************/ + +static int fusb301_clear_interrupts(FAR struct fusb301_dev_s *priv) +{ + int ret = OK; + + ret = fusb301_getreg(priv, FUSB301_INTERRUPT_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to clear interrupts\n"); + return -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_setup + * + * Description: + * Setup FUSB301 chip + * + ****************************************************************************/ + +static int fusb301_setup(FAR struct fusb301_dev_s *priv, + struct fusb301_setup_s *setup) +{ + int ret = OK; + + fusb301_info("drp_tgl:%02X, host_curr:%02X, global_int:%X, mask:%02X\n", + setup->drp_toggle_timing, setup->host_current, setup->global_int_mask, + setup->int_mask); + + ret = fusb301_putreg(priv, FUSB301_CONTROL_REG, setup->drp_toggle_timing | + setup->host_current | setup->global_int_mask); + + if (ret < 0) + { + fusb301_err("ERROR: Failed to write control register\n"); + goto err_out; + } + + ret = fusb301_putreg(priv, FUSB301_MASK_REG, setup->int_mask); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write mask register\n"); + } + +err_out: + return ret; +} + +/**************************************************************************** + * Name: fusb301_set_mode + * + * Description: + * Configure supported device modes (sink, source, DRP, accessory) + * + ****************************************************************************/ + +static int fusb301_set_mode(FAR struct fusb301_dev_s *priv, + enum fusb301_mode_e mode) +{ + int ret = OK; + + if (mode > MODE_DRP_ACC) + { + return -EINVAL; + } + + ret = fusb301_putreg(priv, FUSB301_MODE_REG, mode); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write mode register\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_set_state + * + * Description: + * Force device in specified state + * + ****************************************************************************/ + +static int fusb301_set_state(FAR struct fusb301_dev_s *priv, + enum fusb301_manual_e state) +{ + int ret = OK; + + if (state > MANUAL_UNATT_SNK) + { + return -EINVAL; + } + + ret = fusb301_putreg(priv, FUSB301_MANUAL_REG, state); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write manual register\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_read_status + * + * Description: + * Clear read status register + * + ****************************************************************************/ + +static int fusb301_read_status(FAR struct fusb301_dev_s *priv, + FAR uint8_t *arg) +{ + int ret; + + ret = fusb301_getreg(priv, FUSB301_STATUS_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to read status\n"); + return -EIO; + } + + *arg = ret; + return OK; +} + +/**************************************************************************** + * Name: fusb301_read_devtype + * + * Description: + * Read type of attached device + * + ****************************************************************************/ + +static int fusb301_read_devtype(FAR struct fusb301_dev_s *priv, + FAR uint8_t *arg) +{ + int ret; + + ret = fusb301_getreg(priv, FUSB301_TYPE_REG); + if (ret < 0) + { + fusb301_err("ERROR: Failed to read type\n"); + return -EIO; + } + + *arg = ret; + return OK; +} + +/**************************************************************************** + * Name: fusb301_reset + * + * Description: + * Reset FUSB301 HW and clear I2C registers + * + ****************************************************************************/ + +static int fusb301_reset(FAR struct fusb301_dev_s *priv) +{ + int ret = OK; + + ret = fusb301_putreg(priv, FUSB301_RESET_REG, RESET_SW_RES); + if (ret < 0) + { + fusb301_err("ERROR: Failed to write reset register\n"); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: fusb301_open + * + * Description: + * This function is called whenever the FUSB301 device is opened. + * + ****************************************************************************/ + +static int fusb301_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + int ret = OK; + + /* Probe device */ + + ret = fusb301_getreg(priv, FUSB301_DEV_ID_REG); + if (ret < 0) + { + fusb301_err("ERROR: No response at given address 0x%02X\n", priv->addr); + ret = -EFAULT; + } + else + { + fusb301_info("device id: 0x%02X\n", ret); + + (void)fusb301_clear_interrupts(priv); + priv->config->irq_enable(priv->config, true); + } + + /* Error exit */ + + return ret; +} + +/**************************************************************************** + * Name: fusb301_close + * + * Description: + * This routine is called when the FUSB301 device is closed. + * + ****************************************************************************/ + +static int fusb301_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + + priv->config->irq_enable(priv->config, false); + + return OK; +} + +/**************************************************************************** + * Name: fusb301_read + * Description: + * This routine is called when the FUSB301 device is read. + ****************************************************************************/ + +static ssize_t fusb301_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + FAR struct fusb301_result_s *ptr; + irqstate_t flags; + int ret; + + if (buflen < sizeof(struct fusb301_result_s)) + { + return 0; + } + + ptr = (struct fusb301_result_s *)buffer; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + flags = enter_critical_section(); + priv->int_pending = false; + leave_critical_section(flags); + + (void)fusb301_clear_interrupts(priv); + + ptr->status = fusb301_getreg(priv, FUSB301_STATUS_REG); + ptr->dev_type = fusb301_getreg(priv, FUSB301_TYPE_REG); + + sem_post(&priv->devsem); + return sizeof(struct fusb301_result_s); +} + +/**************************************************************************** + * Name: fusb301_write + * Description: + * This routine is called when the FUSB301 device is written to. + ****************************************************************************/ + +static ssize_t fusb301_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + ssize_t length = 0; + + return length; +} + +/**************************************************************************** + * Name: fusb301_ioctl + * Description: + * This routine is called when ioctl function call is performed for + * the FUSB301 device. + ****************************************************************************/ + +static int fusb301_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct fusb301_dev_s *priv = inode->i_private; + int ret; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + fusb301_info("cmd: 0x%02X, arg:%lu\n", cmd, arg); + + switch (cmd) + { + case USBCIOC_READ_DEVID: + { + ret = fusb301_read_device_id(priv, (uint8_t *)arg); + } + break; + + case USBCIOC_SETUP: + { + ret = fusb301_setup(priv, (struct fusb301_setup_s *)arg); + } + break; + + case USBCIOC_SET_MODE: + { + ret = fusb301_set_mode(priv, (uint8_t)arg); + } + break; + + case USBCIOC_SET_STATE: + { + ret = fusb301_set_state(priv, (uint8_t)arg); + } + break; + + case USBCIOC_READ_STATUS: + { + ret = fusb301_read_status(priv, (uint8_t *)arg); + } + break; + + case USBCIOC_READ_DEVTYPE: + { + ret = fusb301_read_devtype(priv, (uint8_t *)arg); + } + break; + + case USBCIOC_RESET: + { + ret = fusb301_reset(priv); + } + break; + + default: + { + fusb301_err("ERROR: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + } + break; + } + + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: fusb301_poll + * Description: + * This routine is called during FUSB301 device poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int fusb301_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) +{ + FAR struct inode *inode; + FAR struct fusb301_dev_s *priv; + irqstate_t flags; + int ret = OK; + int i; + + DEBUGASSERT(filep && fds); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct fusb301_dev_s *)inode->i_private; + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + return -EINTR; + } + + if (setup) + { + /* Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + ret = -EDEADLK; + goto out; + } + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference. + */ + + for (i = 0; i < CONFIG_FUSB301_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!priv->fds[i]) + { + /* Bind the poll structure and this slot */ + + priv->fds[i] = fds; + fds->priv = &priv->fds[i]; + break; + } + } + + if (i >= CONFIG_FUSB301_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto out; + } + + flags = enter_critical_section(); + if (priv->int_pending) + { + fusb301_notify(priv); + } + + leave_critical_section(flags); + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + DEBUGASSERT(slot != NULL); + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +out: + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: fusb301_notify + * + * Description: + * Notify thread about data to be available + * + ****************************************************************************/ + +static void fusb301_notify(FAR struct fusb301_dev_s *priv) +{ + DEBUGASSERT(priv != NULL); + + int i; + + /* If there are threads waiting on poll() for FUSB301 data to become available, + * then wake them up now. NOTE: we wake up all waiting threads because we + * do not know that they are going to do. If they all try to read the data, + * then some make end up blocking after all. + */ + + for (i = 0; i < CONFIG_FUSB301_NPOLLWAITERS; i++) + { + struct pollfd *fds = priv->fds[i]; + if (fds) + { + fds->revents |= POLLIN; + fusb301_info("Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } +} +#endif /* !CONFIG_DISABLE_POLL */ + +/**************************************************************************** + * Name: fusb301_callback + * + * Description: + * FUSB301 interrupt handler + * + ****************************************************************************/ + +static int fusb301_int_handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct fusb301_dev_s *priv = (FAR struct fusb301_dev_s *)arg; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + + flags = enter_critical_section(); + priv->int_pending = true; + +#ifndef CONFIG_DISABLE_POLL + fusb301_notify(priv); +#endif + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int fusb301_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct fusb301_config_s *config) +{ + FAR struct fusb301_dev_s *priv; + int ret; + + DEBUGASSERT(devpath != NULL && i2c != NULL && config != NULL); + + /* Initialize the FUSB301 device structure */ + + priv = (FAR struct fusb301_dev_s *)kmm_zalloc(sizeof(struct fusb301_dev_s)); + if (!priv) + { + fusb301_err("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + /* Initialize device structure semaphore */ + + sem_init(&priv->devsem, 0, 1); + + priv->int_pending = false; + priv->i2c = i2c; + priv->addr = addr; + priv->config = config; + + /* Register the character driver */ + + ret = register_driver(devpath, &g_fusb301ops, 0666, priv); + if (ret < 0) + { + fusb301_err("ERROR: Failed to register driver: %d\n", ret); + goto errout_with_priv; + } + + /* Prepare interrupt line and handler. */ + + priv->config->irq_clear(config); + priv->config->irq_attach(config, fusb301_int_handler, priv); + priv->config->irq_enable(config, false); + + return OK; + +errout_with_priv: + sem_destroy(&priv->devsem); + kmm_free(priv); + + return ret; +} diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 5582ccd0fee..86aaefae180 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -86,6 +86,7 @@ #define _SPIBASE (0x2100) /* SPI driver commands */ #define _GPIOBASE (0x2200) /* GPIO driver commands */ #define _CLIOCBASE (0x2300) /* Contactless modules ioctl commands */ +#define _USBCBASE (0x2400) /* USB-C controller ioctl commands */ /* boardctl() commands share the same number space */ @@ -371,19 +372,19 @@ #define _LOOPIOCVALID(c) (_IOC_TYPE(c)==_LOOPBASE) #define _LOOPIOC(nr) _IOC(_LOOPBASE,nr) -/* Modem driver ioctl definitions ********************************************/ +/* Modem driver ioctl definitions *******************************************/ /* see nuttx/include/modem/ioctl.h */ #define _MODEMIOCVALID(c) (_IOC_TYPE(c)==_MODEMBASE) #define _MODEMIOC(nr) _IOC(_MODEMBASE,nr) -/* I2C driver ioctl definitions **********************************************/ +/* I2C driver ioctl definitions *********************************************/ /* see nuttx/include/i2c/i2c_master.h */ #define _I2CIOCVALID(c) (_IOC_TYPE(c)==_I2CBASE) #define _I2CIOC(nr) _IOC(_I2CBASE,nr) -/* SPI driver ioctl definitions **********************************************/ +/* SPI driver ioctl definitions *********************************************/ /* see nuttx/include/spi/spi_transfer.h */ #define _SPIIOCVALID(c) (_IOC_TYPE(c)==_SPIBASE) @@ -395,12 +396,18 @@ #define _GPIOCVALID(c) (_IOC_TYPE(c)==_GPIOBASE) #define _GPIOC(nr) _IOC(_GPIOBASE,nr) -/* Contactless driver ioctl definitions ****************************************/ +/* Contactless driver ioctl definitions *************************************/ /* (see nuttx/include/contactless/ioctl.h */ #define _CLIOCVALID(c) (_IOC_TYPE(c)==_CLIOCBASE) #define _CLIOC(nr) _IOC(_CLIOCBASE,nr) +/* USB-C controller driver ioctl definitions ********************************/ +/* (see nuttx/include/usb/xxx.h */ + +#define _USBCIOCVALID(c) (_IOC_TYPE(c)==_USBCBASE) +#define _USBCIOC(nr) _IOC(_USBCBASE,nr) + /* boardctl() command definitions *******************************************/ #define _BOARDIOCVALID(c) (_IOC_TYPE(c)==_BOARDBASE) diff --git a/include/nuttx/net/ip.h b/include/nuttx/net/ip.h index 6a07edd585b..84df3520740 100644 --- a/include/nuttx/net/ip.h +++ b/include/nuttx/net/ip.h @@ -483,7 +483,7 @@ EXTERN const net_ipv6addr_t g_ipv6_llnetmask; /* Netmask for local link addres #endif /**************************************************************************** - * Function: net_ipv4addr_maskcmp and net_ipv6addr_maskcmp + * Name: net_ipv4addr_maskcmp and net_ipv6addr_maskcmp * * Description: * Compare two IP addresses under a netmask. The mask is used to mask @@ -527,7 +527,86 @@ bool net_ipv6addr_maskcmp(const net_ipv6addr_t addr1, #endif /**************************************************************************** - * Function: net_ipaddr_mask + * Name: net_ipv6addr_prefixcmp + * + * Description: + * Compare two IPv6 address prefixes. + * + ****************************************************************************/ + +#define net_ipv6addr_prefixcmp(addr1, addr2, length) \ + (memcmp(addr1, addr2, length >> 3) == 0) + +/**************************************************************************** + * Name: net_is_addr_loopback + * + * Description: + * Is Ithe Pv6 address a the loopback address? + * + ****************************************************************************/ + +#define net_is_addr_loopback(a) \ + ((a)[0] == 0 && (a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (a)[5] == 0 && (a)[6] == 0 && (a)[7] == HTONS(0x0001)) + +/**************************************************************************** + * Name: net_is_addr_unspecified + * + * Description: + * Is Ithe Pv6 address the unspecified address? + * + ****************************************************************************/ + +#define net_is_addr_unspecified(a) \ + ((a)[0] == 0 && (a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (a)[5] == 0 && (a)[6] == 0 && (a)[7] == 0) + +/**************************************************************************** + * Name: net_is_addr_mcast + * + * Description: + * s address a multicast address? see RFC 3513. + * + ****************************************************************************/ + +#define net_is_addr_mcast(a) (((a)[0] & HTONS(0xff00)) == HTONS(0xff00)) + +/**************************************************************************** + * Name: net_is_addr_linklocal_allnodes_mcast + * + * Description: + * Is IPv6 address a the link local all-nodes multicast address? + * + ****************************************************************************/ + +#define net_is_addr_linklocal_allnodes_mcast(a) \ + ((a)[0] == HTONS(0xff02) && (a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (a)[5] == 0 && (a)[6] == 0 && (a)[7] == HTONS(0x0001)) + +/**************************************************************************** + * Name: net_is_addr_linklocal_allrouters_mcast + * + * Description: + * Is IPv6 address a the link local all-routers multicast address? + * + ****************************************************************************/ + +#define net_is_addr_linklocal_allrouters_mcast(a) \ + ((a)[0] == HTONS(0xff02) && (a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (a)[5] == 0 && (a)[6] == 0 && (a)[7] == HTOS(0x0002)) + +/**************************************************************************** + * Name: net_is_addr_linklocal + * + * Description: + * Checks whether the address a is link local. + * + ****************************************************************************/ + +#define net_is_addr_linklocal(a) ((a)[0] == HTONS(0xfe80)) + +/**************************************************************************** + * Name: net_ipaddr_mask * * Description: * Mask out the network part of an IP address, given the address and diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 337e0a63665..d99c2f584a6 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -79,7 +79,7 @@ enum net_lltype_e NET_LL_SLIP, /* Serial Line Internet Protocol (SLIP) */ NET_LL_TUN, /* TUN Virtual Network Device */ NET_LL_IEEE80211, /* IEEE 802.11 */ - NET_LL_IEEE805154 /* IEEE 802.15.4 MAC */ + NET_LL_IEEE802154 /* IEEE 802.15.4 MAC */ }; /* This defines a bitmap big enough for one bit for each socket option */ diff --git a/include/nuttx/net/netconfig.h b/include/nuttx/net/netconfig.h index 34561329013..9bbd0e9929c 100644 --- a/include/nuttx/net/netconfig.h +++ b/include/nuttx/net/netconfig.h @@ -97,7 +97,7 @@ * * - Maximum Transfer Unit (MTU) * - TCP Receive Window size (See TCP configuration options below) - * + * * A better solution would be to support device-by-device MTU and receive * window sizes. This minimum support is require to support the optimal * SLIP MTU of 296 bytes and the standard Ethernet MTU of 1500 @@ -158,19 +158,19 @@ # endif # ifdef CONFIG_NET_SLIP -# define _MIN_SLIP_MTU MIN(_MIN_LO_MTU,CONFIG_NET_6LOWPAN_MTU) -# define _MAX_SLIP_MTU MAX(_MAX_LO_MTU,CONFIG_NET_6LOWPAN_MTU) +# define _MIN_SLIP_MTU MIN(_MIN_LO_MTU,CONFIG_NET_SLIP_MTU) +# define _MAX_SLIP_MTU MAX(_MAX_LO_MTU,CONFIG_NET_SLIP_MTU) # else # define _MIN_SLIP_MTU _MIN_LO_MTU # define _MAX_SLIP_MTU _MAX_LO_MTU # endif # ifdef CONFIG_NET_6LOWPAN -# define _MIN_6LOWPAN_MTU MIN(_MIN_LO_MTU,CONFIG_NET_SLIP_MTU) -# define _MAX_6LOWPAN_MTU MAX(_MAX_LO_MTU,CONFIG_NET_SLIP_MTU) +# define _MIN_6LOWPAN_MTU MIN(_MIN_SLIP_MTU,CONFIG_NET_6LOWPAN_MTU) +# define _MAX_6LOWPAN_MTU MAX(_MAX_SLIP_MTU,CONFIG_NET_6LOWPAN_MTU) # else -# define _MIN_6LOWPAN_MTU _MIN_LO_MTU -# define _MAX_6LOWPAN_MTU _MAX_LO_MTU +# define _MIN_6LOWPAN_MTU _MIN_SLIP_MTU +# define _MAX_6LOWPAN_MTU _MAX_SLIP_MTU # endif # define MIN_NET_DEV_MTU _MIN_6LOWPAN_MTU @@ -226,6 +226,12 @@ # define MIN_NET_DEV_MTU CONFIG_NET_6LOWPAN_MTU # define MAX_NET_DEV_MTU CONFIG_NET_6LOWPAN_MTU +/* For the IEEE802.15.4 MAC device, we will use the packet MTU + * (which is probably much larger than the IEEE802.15.4 fram size) + */ + +# define NET_LO_MTU MAX_NET_DEV_MTU + #else /* Perhaps only Unix domain sockets or the loopback device */ @@ -289,47 +295,97 @@ #endif /* The UDP maximum packet size. This is should not be to set to more - * than NET_DEV_MTU(d) - NET_LL_HDRLEN(dev) - IPv4UDP_HDRLEN. + * than NET_DEV_MTU(d) - NET_LL_HDRLEN(dev) - IPv*_HDRLEN. + * + * REVISIT: It is unclear to me if the UDP_HDRLEN should subtracted + * or not. */ -#define UDP_MSS(d,h) (NET_DEV_MTU(d) - NET_LL_HDRLEN(d) - (h)) - -/* If Ethernet is supported, then it will have the smaller MSS */ - -#if defined(CONFIG_NET_SLIP) -# define SLIP_UDP_MSS(h) (CONFIG_NET_SLIP_MTU - (h)) -# define __MIN_UDP_MSS(h) SLIP_UDP_MSS(h) -#elif defined(CONFIG_NET_LOOPBACK) -# define LO_UDP_MSS(h) (NET_LO_MTU - (h)) -# define __MIN_UDP_MSS(h) LO_UDP_MSS(h) -#endif +#define UDP_MSS(d,h) (NET_DEV_MTU(d) - NET_LL_HDRLEN(d) (h)) #ifdef CONFIG_NET_ETHERNET -# define ETH_UDP_MSS(h) (CONFIG_NET_ETH_MTU - ETH_HDRLEN - (h)) -# undef __MIN_UDP_MSS -# define __MIN_UDP_MSS(h) ETH_UDP_MSS(h) -# define __MAX_UDP_MSS(h) ETH_UDP_MSS(h) +# define ETH_UDP_MSS(h) (CONFIG_NET_ETH_MTU - ETH_HDRLEN - (h)) +# ifndef CONFIG_NET_MULTILINK +# define __MIN_UDP_MSS(h) ETH_UDP_MSS(h) +# define __MAX_UDP_MSS(h) ETH_UDP_MSS(h) +# endif #endif -/* If SLIP is supported, then it will have the larger MSS */ +#ifdef CONFIG_NET_6LOWPAN +# define IEEE802154_UDP_MSS(h) (CONFIG_NET_6LOWPAN_MAXPAYLOAD - (h)) +# ifndef CONFIG_NET_MULTILINK +# define __MIN_UDP_MSS(h) IEEE802154_UDP_MSS(h) +# define __MAX_UDP_MSS(h) IEEE802154_UDP_MSS(h) +# endif +#endif + +#ifdef CONFIG_NET_LOOPBACK +# define LO_UDP_MSS(h) (NET_LO_MTU - (h)) +# ifndef CONFIG_NET_MULTILINK +# define __MIN_UDP_MSS(h) LO_UDP_MSS(h) +# define __MAX_UDP_MSS(h) LO_UDP_MSS(h) +# endif +#endif #ifdef CONFIG_NET_SLIP -# undef __MAX_UDP_MSS -# define __MAX_UDP_MSS(h) SLIP_UDP_MSS(h) +# define SLIP_UDP_MSS(h) (CONFIG_NET_SLIP_MTU - (h)) +# ifndef CONFIG_NET_MULTILINK +# define __MIN_UDP_MSS(h) SLIP_UDP_MSS(h) +# define __MAX_UDP_MSS(h) SLIP_UDP_MSS(h) +# endif #endif -/* If IPv4 is supported, it will have the larger MSS */ +#ifdef CONFIG_NET_MULTILINK +# ifdef CONFIG_NET_ETHERNET +# define __MIN_UDP_MSS(h) ETH_UDP_MSS(h) +# define __MAX_UDP_MSS(h) ETH_UDP_MSS(h) +# define __ETH_MIN_UDP_MSS(h) ETH_UDP_MSS(h) +# define __ETH_MAX_UDP_MSS(h) ETH_UDP_MSS(h) +# else +# define __ETH_MIN_UDP_MSS(h) INT_MAX +# define __ETH_MAX_UDP_MSS(h) 0 +# endif -#ifdef CONFIG_NET_IPv6 -# define UDP_IPv6_MSS(d) UDP_MSS(d,IPv6_HDRLEN) -# define ETH_IPv6_UDP_MSS ETH_UDP_MSS(IPv6_HDRLEN) -# define SLIP_IPv6_UDP_MSS SLIP_UDP_MSS(IPv6_HDRLEN) +# ifdef CONFIG_NET_6LOWPAN +# undef __MIN_UDP_MSS +# undef __MIN_UDP_MSS +# define __MIN_UDP_MSS(h) MIN(IEEE802154_UDP_MSS(h),__ETH_MIN_UDP_MSS(h)) +# define __MAX_UDP_MSS(h) MAX(IEEE802154_UDP_MSS(h),__ETH_MAX_UDP_MSS(h)) +# define __6LOWPAN_MIN_UDP_MSS(h) MIN(IEEE802154_UDP_MSS(h),__ETH_MIN_UDP_MSS(h)) +# define __6LOWPAN_MAX_UDP_MSS(h) MAX(IEEE802154_UDP_MSS(h),__ETH_MAX_UDP_MSS(h)) +# else +# define __6LOWPAN_MIN_UDP_MSS(h) __ETH_MIN_UDP_MSS(h) +# define __6LOWPAN_MAX_UDP_MSS(h) __ETH_MAX_UDP_MSS(h) +# endif -# define MAX_IPv6_UDP_MSS __MAX_UDP_MSS(IPv6_HDRLEN) -# define MAX_UDP_MSS __MAX_UDP_MSS(IPv6_HDRLEN) +# ifdef CONFIG_NET_LOOPBACK +# undef __MIN_UDP_MSS +# undef __MIN_UDP_MSS +# define __MIN_UDP_MSS(h) MIN(LO_UDP_MSS(h),__6LOWPAN_MIN_UDP_MSS(h)) +# define __MAX_UDP_MSS(h) MAX(LO_UDP_MSS(h),__6LOWPAN_MAX_UDP_MSS(h)) +# define __LOOP_MIN_UDP_MSS(h) MIN(LO_UDP_MSS(h),__6LOWPAN_MIN_UDP_MSS(h)) +# define __LOOP_MAX_UDP_MSS(h) MAX(LO_UDP_MSS(h),__6LOWPAN_MAX_UDP_MSS(h)) +# else +# define __LOOP_MIN_UDP_MSS(h) __6LOWPAN_MIN_UDP_MSS(h) +# define __LOOP_MAX_UDP_MSS(h) __6LOWPAN_MAX_UDP_MSS(h) +# endif + +# ifdef CONFIG_NET_SLIP +# undef __MIN_UDP_MSS +# undef __MIN_UDP_MSS +# define __MIN_UDP_MSS(h) MIN(SLIP_UDP_MSS(h),__LOOP_MIN_UDP_MSS(h)) +# define __MAX_UDP_MSS(h) MAX(SLIP_UDP_MSS(h),__LOOP_MAX_UDP_MSS(h)) +# define __SLIP_MIN_UDP_MSS(h) MIN(SLIP_UDP_MSS(h),__LOOP_MIN_UDP_MSS(h)) +# define __SLIP_MAX_UDP_MSS(h) MAX(SLIP_UDP_MSS(h),__LOOP_MAX_UDP_MSS(h)) +# else +# define __SLIP_MIN_UDP_MSS(h) __LOOP_MIN_UDP_MSS(h) +# define __SLIP_MAX_UDP_MSS(h) __LOOP_MAX_UDP_MSS(h) +# endif #endif -#ifdef CONFIG_NET_IPv4 +/* NOTE: MSS calcuation excludes the UDP_HDRLEN. */ + + #ifdef CONFIG_NET_IPv4 # define UDP_IPv4_MSS(d) UDP_MSS(d,IPv4_HDRLEN) # define ETH_IPv4_UDP_MSS ETH_UDP_MSS(IPv4_HDRLEN) # define SLIP_IPv4_UDP_MSS SLIP_UDP_MSS(IPv4_HDRLEN) @@ -416,36 +472,100 @@ * link layer protocols (CONFIG_NET_MULTILINK), each network device * may support a different UDP MSS value. Here we arbitrarily select * the minimum MSS for that case. + * + * REVISIT: It is unclear to me if the TCP_HDRLEN should subtracted + * or not. */ -#define TCP_MSS(d,h) (NET_DEV_MTU(d) - NET_LL_HDRLEN(d) - TCP_HDRLEN - (h)) -#define LO_TCP_MSS(h) (NET_LO_MTU - (h)) +#define TCP_MSS(d,h) (NET_DEV_MTU(d) - NET_LL_HDRLEN(d) - (h)) +#define LO_TCP_MSS(h) (NET_LO_MTU - (h)) -/* If Ethernet is supported, then it will have the smaller MSS */ - -#if defined(CONFIG_NET_SLIP) -# define SLIP_TCP_MSS(h) (CONFIG_NET_SLIP_MTU - (h)) -# define __MIN_TCP_MSS(h) SLIP_TCP_MSS(h) -#elif defined(CONFIG_NET_LOOPBACK) -# define LO_TCP_MSS(h) (NET_LO_MTU - (h)) -# define __MIN_TCP_MSS(h) LO_TCP_MSS(h) -#endif +/* Get the smallest and largest MSS */ #ifdef CONFIG_NET_ETHERNET -# define ETH_TCP_MSS(h) (CONFIG_NET_ETH_MTU - ETH_HDRLEN - (h)) -# undef __MIN_TCP_MSS -# define __MIN_TCP_MSS(h) ETH_TCP_MSS(h) -# define __MAX_TCP_MSS(h) ETH_TCP_MSS(h) +# define ETH_TCP_MSS(h) (CONFIG_NET_ETH_MTU - ETH_HDRLEN - (h)) +# ifndef CONFIG_NET_MULTILINK +# define __MIN_TCP_MSS(h) ETH_TCP_MSS(h) +# define __MAX_TCP_MSS(h) ETH_TCP_MSS(h) +# endif #endif -/* If SLIP is supported, then it will have the larger MSS */ +#ifdef CONFIG_NET_6LOWPAN +# define IEEE802154_TCP_MSS(h) CONFIG_NET_6LOWPAN_MAXPAYLOAD +# ifndef CONFIG_NET_MULTILINK +# define __MIN_TCP_MSS(h) IEEE802154_TCP_MSS(h) +# define __MAX_TCP_MSS(h) IEEE802154_TCP_MSS(h) +# endif +#endif + +#ifdef CONFIG_NET_LOOPBACK +# define LO_TCP_MSS(h) (NET_LO_MTU - (h)) +# ifndef CONFIG_NET_MULTILINK +# define __MIN_TCP_MSS(h) LO_TCP_MSS(h) +# define __MAX_TCP_MSS(h) LO_TCP_MSS(h) +# endif +#endif #ifdef CONFIG_NET_SLIP -# undef __MAX_TCP_MSS -# define __MAX_TCP_MSS(h) SLIP_TCP_MSS(h) +# define SLIP_TCP_MSS(h) (CONFIG_NET_SLIP_MTU - (h)) +# ifndef CONFIG_NET_MULTILINK +# define __MIN_TCP_MSS(h) SLIP_TCP_MSS(h) +# define __MAX_TCP_MSS(h) SLIP_TCP_MSS(h) +# endif #endif -/* If IPv4 is support, it will have the larger MSS */ +#ifdef CONFIG_NET_MULTILINK + +# ifdef CONFIG_NET_ETHERNET +# define __MIN_TCP_MSS(h) ETH_TCP_MSS(h) +# define __MAX_TCP_MSS(h) ETH_TCP_MSS(h) +# define __ETH_MIN_TCP_MSS(h) ETH_TCP_MSS(h) +# define __ETH_MAX_TCP_MSS(h) ETH_TCP_MSS(h) +# else +# define __ETH_MIN_TCP_MSS(h) INT_MAX +# define __ETH_MAX_TCP_MSS(h) 0 +# endif + +# ifdef CONFIG_NET_6LOWPAN +# undef __MIN_TCP_MSS +# undef __MAX_TCP_MSS +# define __MIN_TCP_MSS(h) MIN(IEEE802154_TCP_MSS(h),__ETH_MIN_TCP_MSS(h)) +# define __MAX_TCP_MSS(h) MAX(IEEE802154_TCP_MSS(h),__ETH_MAX_TCP_MSS(h)) +# define __6LOWPAN_MIN_TCP_MSS(h) MIN(IEEE802154_TCP_MSS(h),__ETH_MIN_TCP_MSS(h)) +# define __6LOWPAN_MAX_TCP_MSS(h) MAX(IEEE802154_TCP_MSS(h),__ETH_MAX_TCP_MSS(h)) +# else +# define __6LOWPAN_MIN_TCP_MSS(h) __ETH_MIN_TCP_MSS(h) +# define __6LOWPAN_MAX_TCP_MSS(h) __ETH_MAX_TCP_MSS(h) +# endif + +# ifdef CONFIG_NET_LOOPBACK +# undef __MIN_TCP_MSS +# undef __MAX_TCP_MSS +# define __MIN_TCP_MSS(h) MIN(LO_TCP_MSS(h),__6LOWPAN_MIN_TCP_MSS(h)) +# define __MAX_TCP_MSS(h) MAX(LO_TCP_MSS(h),__6LOWPAN_MAX_TCP_MSS(h)) +# define __LOOP_MIN_TCP_MSS(h) MIN(LO_TCP_MSS(h),__6LOWPAN_MIN_TCP_MSS(h)) +# define __LOOP_MAX_TCP_MSS(h) MAX(LO_TCP_MSS(h),__6LOWPAN_MAX_TCP_MSS(h)) +# else +# define __LOOP_MIN_TCP_MSS(h) _6LOWPAN_MIN_TCP_MSS(h) +# define __LOOP_MAX_TCP_MSS(h) __6LOWPAN_MAX_TCP_MSS(h) +# endif + +# ifdef CONFIG_NET_SLIP +# undef __MIN_TCP_MSS +# undef __MAX_TCP_MSS +# define __MIN_TCP_MSS(h) MIN(SLIP_TCP_MSS(h),__LOOP_MIN_TCP_MSS(h)) +# define __MAX_TCP_MSS(h) MAX(SLIP_TCP_MSS(h),__LOOP_MAX_TCP_MSS(h)) +# define __SLIP_MIN_TCP_MSS(h) MIN(SLIP_TCP_MSS(h),__LOOP_MIN_TCP_MSS(h)) +# define __SLIP_MAX_TCP_MSS(h) MAX(SLIP_TCP_MSS(h),__LOOP_MAX_TCP_MSS(h)) +# else +# define __SLIP_MIN_TCP_MSS(h) __LOOP_MIN_TCP_MSS(h) +# define __SLIP_MAX_TCP_MSS(h) __LOOP_MAX_TCP_MSS(h) +# endif +#endif + +/* If IPv4 is supported, it will have the larger MSS. + * NOTE: MSS calcuation excludes the TCP_HDRLEN. + */ #ifdef CONFIG_NET_IPv6 # define TCP_IPv6_MSS(d) TCP_MSS(d,IPv6_HDRLEN) @@ -547,6 +667,18 @@ # define CONFIG_NET_ARP_MAXAGE 120 #endif +/* Usrsock configuration options */ + +/* The maximum amount of concurrent usrsock connections, Default: 6 */ + +#ifndef CONFIG_NET_USRSOCK_CONNS +# ifdef CONFIG_NET_USRSOCK +# define CONFIG_NET_USRSOCK_CONNS 6 +# else +# define CONFIG_NET_USRSOCK_CONNS 0 +# endif +#endif + /* General configuration options */ /* Delay after receive to catch a following packet. No delay should be diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index 72bddc067cc..12b71840d72 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -419,7 +419,8 @@ int ipv6_input(FAR struct net_driver_s *dev); #endif #ifdef CONFIG_NET_6LOWPAN -int sixlowpan_input(FAR struct net_driver_s *dev); +struct ieee802154_driver_s; /* See sixlowpan.h */ +int sixlowpan_input(FAR struct ieee802154_driver_s *ieee); #endif /**************************************************************************** diff --git a/include/nuttx/net/sixlowpan.h b/include/nuttx/net/sixlowpan.h index 7463763bff5..0211d17ce6f 100644 --- a/include/nuttx/net/sixlowpan.h +++ b/include/nuttx/net/sixlowpan.h @@ -42,8 +42,8 @@ * ****************************************************************************/ -#ifndef __INCLUDE_NUTTX_NET_SIXLOWOAN_H -#define __INCLUDE_NUTTX_NET_SIXLOWOAN_H +#ifndef __INCLUDE_NUTTX_NET_SIXLOWPAN_H +#define __INCLUDE_NUTTX_NET_SIXLOWPAN_H /**************************************************************************** * Included Files @@ -53,8 +53,12 @@ #include +#include +#include #include +#ifdef CONFIG_NET_6LOWPAN + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -62,17 +66,21 @@ /* Min and Max compressible UDP ports - HC06 */ #define SIXLOWPAN_UDP_4_BIT_PORT_MIN 0xf0b0 -#define SIXLOWPAN_UDP_4_BIT_PORT_MAX 0xf0bf /* F0B0 + 15 */ -#define SIXLOWPAN_UDP_8_BIT_PORT_MIN 0xF000 -#define SIXLOWPAN_UDP_8_BIT_PORT_MAX 0xf0ff /* F000 + 255 */ +#define SIXLOWPAN_UDP_4_BIT_PORT_MAX 0xf0bf /* f0b0 + 15 */ +#define SIXLOWPAN_UDP_8_BIT_PORT_MIN 0xf000 +#define SIXLOWPAN_UDP_8_BIT_PORT_MAX 0xf0ff /* f000 + 255 */ /* 6lowpan dispatches */ #define SIXLOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */ #define SIXLOWPAN_DISPATCH_HC1 0x42 /* 01000010 = 66 */ -#define SIXLOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */ + +#define SIXLOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx */ +#define SIXLOWPAN_DISPATCH_IPHC_MASK 0xe0 /* 11100000 */ + #define SIXLOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */ #define SIXLOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */ +#define SIXLOWPAN_DISPATCH_FRAG_MASK 0xf1 /* 11111000 */ /* HC1 encoding */ @@ -86,38 +94,40 @@ /* IPHC encoding * - * Values of fields within the IPHC encoding first byte (C stands for - * compressed and I for inline) + * Values of fields within the IPHC encoding first byte + * (Using MS-to-LS bit numbering of the draft RFC) */ - -#define SIXLOWPAN_IPHC_FL_C 0x10 -#define SIXLOWPAN_IPHC_TC_C 0x08 -#define SIXLOWPAN_IPHC_NH_C 0x04 -#define SIXLOWPAN_IPHC_TTL_1 0x01 -#define SIXLOWPAN_IPHC_TTL_64 0x02 -#define SIXLOWPAN_IPHC_TTL_255 0x03 -#define SIXLOWPAN_IPHC_TTL_I 0x00 - + /* Bits 0-2: 011 */ +#define SIXLOWPAN_IPHC_TC_MASK 0x18 /* Bits 3-4: Traffic Class, Flow Label */ +# define SIXLOWPAN_IPHC_TC_00 0x00 /* ECN+DSCP+4-bit Pad+Flow Label (4 bytes) */ +# define SIXLOWPAN_IPHC_TC_01 0x08 /* ECN+2-bit Pad+ Flow Label (3 bytes), DSCP is elided. */ +# define SIXLOWPAN_IPHC_TC_10 0x10 /* ECN+DSCP (1 byte), Flow Label is elided */ +# define SIXLOWPAN_IPHC_TC_11 0x11 /* Traffic Class and Flow Label are elided */ +#define SIXLOWPAN_IPHC_NH 0x04 /* Bit 5: Next Header Compressed */ +#define SIXLOWPAN_IPHC_HLIM_MASK 0x03 /* Bits 6-7: Hop Limit */ +# define SIXLOWPAN_IPHC_HLIM_INLINE 0x00 /* Carried in-line */ +# define SIXLOWPAN_IPHC_HLIM_1 0x01 /* Compressed hop limit of 1 */ +# define SIXLOWPAN_IPHC_HLIM_64 0x02 /* Compressed hop limit of 64 */ +# define SIXLOWPAN_IPHC_HLIM_255 0x03 /* Compressed hop limit of 255 */ /* Values of fields within the IPHC encoding second byte */ -#define SIXLOWPAN_IPHC_CID 0x80 - -#define SIXLOWPAN_IPHC_SAC 0x40 -#define SIXLOWPAN_IPHC_SAM_00 0x00 -#define SIXLOWPAN_IPHC_SAM_01 0x10 -#define SIXLOWPAN_IPHC_SAM_10 0x20 -#define SIXLOWPAN_IPHC_SAM_11 0x30 +#define SIXLOWPAN_IPHC_CID 0x80 /* Bit 8: Context identifier extension */ +#define SIXLOWPAN_IPHC_SAC 0x40 /* Bit 9: Source address compression */ +#define SIXLOWPAN_IPHC_SAM_MASK 0x30 /* Bits 10-11: Source address mode */ +# define SIXLOWPAN_IPHC_SAM_128 0x00 /* 128-bits */ +# define SIXLOWPAN_IPHC_SAM_64 0x10 /* 64-bits */ +# define SIXLOWPAN_IPHC_SAM_16 0x20 /* 16-bits */ +# define SIXLOWPAN_IPHC_SAM_0 0x30 /* 0-bits */ +#define SIXLOWPAN_IPHC_M 0x08 /* Bit 12: Multicast compression */ +#define SIXLOWPAN_IPHC_DAC 0x04 /* Bit 13: Destination address compression */ +#define SIXLOWPAN_IPHC_DAM_MASK 0x03 /* Bits 14-15: Destination address mode */ +# define SIXLOWPAN_IPHC_DAM_128 0x00 /* 128-bits */ +# define SIXLOWPAN_IPHC_DAM_64 0x01 /* 64-bits */ +# define SIXLOWPAN_IPHC_DAM_16 0x02 /* 16-bits */ +# define SIXLOWPAN_IPHC_DAM_0 0x03 /* 0-bits */ #define SIXLOWPAN_IPHC_SAM_BIT 4 - -#define SIXLOWPAN_IPHC_M 0x08 -#define SIXLOWPAN_IPHC_DAC 0x04 -#define SIXLOWPAN_IPHC_DAM_00 0x00 -#define SIXLOWPAN_IPHC_DAM_01 0x01 -#define SIXLOWPAN_IPHC_DAM_10 0x02 -#define SIXLOWPAN_IPHC_DAM_11 0x03 - #define SIXLOWPAN_IPHC_DAM_BIT 0 /* Link local context number */ @@ -155,139 +165,67 @@ #define SIXLOWPAN_FRAG1_HDR_LEN 4 #define SIXLOWPAN_FRAGN_HDR_LEN 5 -/* Address compressibility test macros */ - -/* Check whether we can compress the IID in address 'a' to 16 bits. This is - * used for unicast addresses only, and is true if the address is on the - * format ::0000:00ff:fe00:XXXX - * - * NOTE: we currently assume 64-bits prefixes - */ - -#define SIXLOWPAN_IS_IID_16BIT_COMPRESSABLE(a) \ - ((((a)->u16[4]) == 0) && \ - // (((a)->u8[10]) == 0)&& \ - (((a)->u8[11]) == 0xff)&& \ - (((a)->u8[12]) == 0xfe)&& \ - (((a)->u8[13]) == 0)) - -/* Check whether the 9-bit group-id of the compressed multicast address is - * known. It is true if the 9-bit group is the all nodes or all routers - * group. Parameter 'a' is typed uint8_t * - */ - -#define SIXLOWPAN_IS_MCASTADDR_DECOMPRESSABLE(a) \ - (((*a & 0x01) == 0) && \ - ((*(a + 1) == 0x01) || (*(a + 1) == 0x02))) - -/* Check whether the 112-bit group-id of the multicast address is mappable - * to a 9-bit group-id. It is true if the group is the all nodes or all - * routers group. - */ - -#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE(a) \ - ((((a)->u16[1]) == 0) && \ - (((a)->u16[2]) == 0) && \ - (((a)->u16[3]) == 0) && \ - (((a)->u16[4]) == 0) && \ - (((a)->u16[5]) == 0) && \ - (((a)->u16[6]) == 0) && \ - (((a)->u8[14]) == 0) && \ - ((((a)->u8[15]) == 1) || (((a)->u8[15]) == 2))) - -/* FFXX::00XX:XXXX:XXXX */ - -#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE48(a) \ - ((((a)->u16[1]) == 0) && \ - (((a)->u16[2]) == 0) && \ - (((a)->u16[3]) == 0) && \ - (((a)->u16[4]) == 0) && \ - (((a)->u8[10]) == 0)) - -/* FFXX::00XX:XXXX */ - -#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE32(a) \ - ((((a)->u16[1]) == 0) && \ - (((a)->u16[2]) == 0) && \ - (((a)->u16[3]) == 0) && \ - (((a)->u16[4]) == 0) && \ - (((a)->u16[5]) == 0) && \ - (((a)->u8[12]) == 0)) - -/* FF02::00XX */ - -#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE8(a) \ - ((((a)->u8[1]) == 2) && \ - (((a)->u16[1]) == 0) && \ - (((a)->u16[2]) == 0) && \ - (((a)->u16[3]) == 0) && \ - (((a)->u16[4]) == 0) && \ - (((a)->u16[5]) == 0) && \ - (((a)->u16[6]) == 0) && \ - (((a)->u8[14]) == 0)) - /* This maximum size of an IEEE802.15.4 frame. Certain, non-standard * devices may exceed this value, however. */ #define SIXLOWPAN_MAC_STDFRAME 127 -/* Packet buffer Definitions */ +/* Frame buffer helper macros. + * + * The IEEE802.15.4 MAC driver structures includes a list of IOB + * structures, i_framelist, containing frames to be sent by the driver or + * that were received by the driver. The IOB structure is defined in + * include/nuttx/net/iob.h. The length of data in the IOB is provided by + * the io_len field of the IOB structure. + * + * NOTE that IOBs must be configured such that CONFIG_IOB_BUFSIZE >= + * CONFIG_NET_6LOWPAN_FRAMELEN + * + * 1. On a TX poll, the IEEE802.15.4 MAC driver should provide its driver + * structure with i_framelist set to NULL. At the conclusion of the + * poll, if there are frames to be sent, they will have been added to + * the i_framelist. The non-empty frame list is the indication that + * there is data to be sent. + * + * The IEEE802.15.4 may use the FRAME_IOB_EMPTY() macro to determine + * if there there frames to be sent. If so, it should remove each + * frame from the frame list using the FRAME_IOB_REMOVE() macro and send + * it. That macro will return NULL when all of the frames have been + * sent. + * + * After sending each frame, the driver must return the IOB to the pool + * of free IOBs using the FROM_IOB_FREE() macro. + */ -#define PACKETBUF_HDR_SIZE 48 -#define PACKETBUF_ATTR_PACKET_TYPE_DATA 0 -#define PACKETBUF_ATTR_PACKET_TYPE_ACK 1 -#define PACKETBUF_ATTR_PACKET_TYPE_STREAM 2 -#define PACKETBUF_ATTR_PACKET_TYPE_STREAM_END 3 -#define PACKETBUF_ATTR_PACKET_TYPE_TIMESTAMP 4 +#define FRAME_IOB_EMPTY(ieee) ((ieee)->i_framelist == NULL) +#define FRAME_IOB_REMOVE(ieee, iob) \ + do \ + { \ + (iob) = (ieee)->i_framelist; \ + (ieee)->i_framelist = (iob)->io_flink; \ + (iob)->io_flink = NULL; \ + } \ + while (0) +#define FRAME_IOB_FREE(iob) iob_free(iob) -/* Packet buffer attributes (indices into i_pktattr) */ +/* 2. When receiving data, the IEEE802.15.4 MAC driver should receive the + * frame data directly into the payload area of an IOB structure. That + * IOB structure may be obtained using the FRAME_IOB_ALLOC() macro. The + * single frame should be added to the frame list using FRAME_IOB_ADD() + * (it will be a list of length one) . The MAC driver should then inform + * the network of the by calling sixlowpan_input(). + */ -#define PACKETBUF_ATTR_NONE 0 - -/* Scope 0 attributes: used only on the local node. */ - -#define PACKETBUF_ATTR_CHANNEL 1 -#define PACKETBUF_ATTR_NETWORK_ID 2 -#define PACKETBUF_ATTR_LINK_QUALITY 3 -#define PACKETBUF_ATTR_RSSI 4 -#define PACKETBUF_ATTR_TIMESTAMP 5 -#define PACKETBUF_ATTR_RADIO_TXPOWER 6 -#define PACKETBUF_ATTR_LISTEN_TIME 7 -#define PACKETBUF_ATTR_TRANSMIT_TIME 8 -#define PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS 9 -#define PACKETBUF_ATTR_MAC_SEQNO 10 -#define PACKETBUF_ATTR_MAC_ACK 11 - -/* Scope 1 attributes: used between two neighbors only. */ - -#define PACKETBUF_ATTR_RELIABLE 12 -#define PACKETBUF_ATTR_PACKET_ID 13 -#define PACKETBUF_ATTR_PACKET_TYPE 14 -#define PACKETBUF_ATTR_REXMIT 15 -#define PACKETBUF_ATTR_MAX_REXMIT 16 -#define PACKETBUF_ATTR_NUM_REXMIT 17 -#define PACKETBUF_ATTR_PENDING 18 - -/* Scope 2 attributes: used between end-to-end nodes. */ - -#define PACKETBUF_ATTR_HOPS 11 -#define PACKETBUF_ATTR_TTL 20 -#define PACKETBUF_ATTR_EPACKET_ID 21 -#define PACKETBUF_ATTR_EPACKET_TYPE 22 -#define PACKETBUF_ATTR_ERELIABLE 23 - -#define PACKETBUF_NUM_ATTRS 24 - - /* Addresses (indices into i_pktaddr) */ - -#define PACKETBUF_ADDR_SENDER 0 -#define PACKETBUF_ADDR_RECEIVER 1 -#define PACKETBUF_ADDR_ESENDER 2 -#define PACKETBUF_ADDR_ERECEIVER 3 - -#define PACKETBUF_NUM_ADDRS 4 +#define FRAME_IOB_ALLOC() iob_alloc(false) +#define FRAME_IOB_ADD(ieee, iob) \ + do \ + { \ + (iob)->io_flink = (ieee)->i_framelist; \ + (ieee)->i_framelist = (iob); \ + } \ + while (0) /**************************************************************************** * Public Types @@ -305,18 +243,20 @@ struct rimeaddr_s * difference is that fragmentation must be supported. * * The IEEE802.15.4 MAC does not use the d_buf packet buffer directly. - * Rather, it uses a smaller frame buffer, i_frame. + * Rather, it uses a list smaller frame buffers, i_framelist. * - * - The packet fragment data is provided to the i_frame buffer each time - * that the IEEE802.15.4 MAC needs to send more data. The length of - * the frame is provided in i_frame. + * - The packet fragment data is provided to an IOB in the i_framelist + * buffer each time that the IEEE802.15.4 MAC needs to send more data. + * The length of the frame is provided in the io_len field of the IOB. * - * In this case, the d_buf holds the packet data yet to be sent; d_len - * holds the size of entire packet. + * In this case, the d_buf is not used at all and, if fact, may be + * NULL. * * - Received frames are provided by IEEE802.15.4 MAC to the network - * via i_frame with length i_framelen for reassembly in d_buf; d_len - * will hold the size of the reassembled packet. + * via and IOB in i_framelist with length io_len for reassembly in + * d_buf; d_len will hold the size of the reassembled packet. + * + * In this case, a d_buf of size CONFIG_NET_6LOWPAN_MTU must be provided. * * This is accomplished by "inheriting" the standard 'struct net_driver_s' * and appending the frame buffer as well as other metadata needed to @@ -328,36 +268,51 @@ struct rimeaddr_s * this structure. In general, all fields must be set to NULL. In * addtion: * - * 1) i_panid must be set to identify the network. It may be set to 0xfff + * 1. i_panid must be set to identify the network. It may be set to 0xfff * if the device is not associated. - * 2) i_dsn must be set to a random value. After that, it will be managed + * + * 2. i_dsn must be set to a random value. After that, it will be managed * by the network. - * 3) i_nodeaddr must be set after the MAC is assigned an address. - * 4) On network TX poll operations, the IEEE802.15.4 MAC needs to provide - * the i_frame buffer with size greater than or equal to - * CONFIG_NET_6LOWPAN_FRAMELEN. No dev.d_buf need be provided in this - * case. The entire is TX is performed using only the i_frame buffer. - * 5) On network input RX oprations, both buffers must be provided. The size - * of the i_frame buffer is, again, greater than or equal to - * CONFIG_NET_6LOWPAN_FRAMELEN. The larger dev.d_buf must have a size - * of at least the advertised MTU of the protocol, CONFIG_NET_6LOWPAN_MTU. - * If fragmentation is enabled, then the logical packet size may be - * significantly larger than the size of the frame buffer. The dev.d_buf - * is used for de-compressing each frame and reassembling any fragmented - * packets to create the full input packet that is provided to the - * application. * - * Frame Organization: + * 3. i_nodeaddr must be set after the MAC is assigned an address. * - * Content Offset - * +------------------+ 0 - * | Frame Header | - * +------------------+ i_dataoffset - * | Procotol Headers | - * | Data Payload | - * +------------------+ i_framelen - * | Unused | - * +------------------+ CONFIG_NET_6LOWPAN_FRAMELEN + * 4. On a TX poll, the IEEE802.15.4 MAC driver should provide its driver + * structure with i_framelist set to NULL. At the conclusion of the + * poll, if there are frames to be sent, they will have been added to + * the i_framelist. The non-empty frame list at the conclusion of the + * TX poll is the indication that is data to be sent. + * + * The IEEE802.15.4 may use the FRAME_IOB_EMPTY() macro to determine + * if there there frames to be sent. If so, it should remove each + * frame from the frame list using the FRAME_IOB_REMOVE() macro and send + * it. That macro will return NULL when all of the frames have been + * sent. + * + * After sending each frame, the driver must return the IOB to the pool + * of free IOBs using the FROM_IOB_FREE() macro. + * + * 5. When receiving data both buffers must be provided: + * + * The IEEE802.15.4 MAC driver should receive the frame data directly + * into the payload area of an IOB structure. That IOB structure may be + * obtained using the FRAME_IOB_ALLOC() macro. The single frame should + * be added to the frame list using FRAME_IOB_ADD() (it will be a list of + * length one). + * + * The larger dev.d_buf must have a size of at least the advertised MTU + * of the protocol, CONFIG_NET_6LOWPAN_MTU. If fragmentation is enabled, + * then the logical packet size may be significantly larger than the + * size of the frame buffer. The dev.d_buf is used for de-compressing + * each frame and reassembling any fragmented packets to create the full + * input packet that is provided to the application. + * + * The MAC driver should then inform the network of the by calling + * sixlowpan_input(). + * + * Normally, the network will free the IOB and will nullify the frame + * list. But ss a complexity, the result of receiving a frame may be + * that the network may respond provide an outgoing frames in the + * frame list. */ struct ieee802154_driver_s @@ -370,32 +325,23 @@ struct ieee802154_driver_s /* IEEE802.15.4 MAC-specific definitions follow. */ - /* The i_frame array is used to hold outgoing frame. When the - * IEEE802.15.4 device polls for new data, the outgoing frame containing - * the next fragment is placed in i_frame. + /* The i_framelist is used to hold a outgoing frames contained in IOB + * structures. When the IEEE802.15.4 device polls for new TX data, the + * outgoing frame(s) containing the packet fragments are placed in IOBs + * and queued in i_framelist. * - * The network will handle only a single outgong frame at a time. The - * IEEE802.15.4 MAC driver design may be concurrently sending and - * requesting new framesusing break-off fram buffers. That frame buffer - * management must be controlled by the IEEE802.15.4 MAC driver. + * The i_framelist is similary used to hold incoming frames in IOB + * structures. The IEEE802.15.4 MAC driver must receive frames in an IOB, + * place the IOB in the i_framelist, and call sixlowpan_input(). * - * Driver provided frame buffers should of size CONFIG_NET_6LOWPAN_FRAMELEN - * and should be 16-bit aligned. + * The IEEE802.15.4 MAC driver design may be concurrently sending and + * requesting new frames using lists of IOBs. That IOB frame buffer + * management must be managed by the IEEE802.15.4 MAC driver. */ - FAR uint8_t *i_frame; - - /* The length of valid data in the i_frame buffer. - * - * When the network device driver calls the network input function, - * i_framelen should be set to zero. If there is frame to be sent - * by the network, i_framelen will be set to indicate the size of - * frame to be sent. The value zero means that there is no frame - * to be sent. - */ - - uint16_t i_framelen; + FAR struct iob_s *i_framelist; + /* Driver Configuration ***************************************************/ /* i_panid. The PAN ID is 16-bit number that identifies the network. It * must be unique to differentiate a network. All the nodes in the same * network should have the same PAN ID. This value must be provided to @@ -421,71 +367,48 @@ struct ieee802154_driver_s uint8_t i_dsn; - /* The following fields are device-specific metadata used by the 6loWPAN - * stack and should not be modified by the IEEE802.15.4 MAC network drvier. +#if CONFIG_NET_6LOWPAN_FRAG + /* Fragmentation Support *************************************************/ + /* Fragmentation is handled frame by frame and requires that certain + * state information be retained from frame to frame. */ - /* A pointer to the rime buffer. - * - * We initialize it to the beginning of the rime buffer, then access - * different fields by updating the offset ieee->i_rime_hdrlen. + /* i_dgramtag. Datagram tag to be put in the header of the set of + * fragments. It is used by the recipient to match fragments of the + * same payload. */ - FAR uint8_t *i_rimeptr; + uint16_t i_dgramtag; - /* i_uncomp_hdrlen is the length of the headers before compression (if HC2 - * is used this includes the UDP header in addition to the IP header). + /* i_pktlen. The total length of the IPv6 packet to be re-assembled in + * d_buf. */ - uint8_t i_uncomp_hdrlen; + uint16_t i_pktlen; - /* i_rime_hdrlen is the total length of (the processed) 6lowpan headers - * (fragment headers, IPV6 or HC1, HC2, and HC1 and HC2 non compressed - * fields). + /* The current accumulated length of the packet being received in d_buf. + * Included IPv6 and protocol headers. */ - uint8_t i_rime_hdrlen; + uint16_t i_accumlen; - /* Offset first available byte for the payload after header region. */ - - uint8_t i_dataoffset; - - /* Packet buffer metadata: Attributes and addresses */ - - uint16_t i_pktattrs[PACKETBUF_NUM_ATTRS]; - struct rimeaddr_s i_pktaddrs[PACKETBUF_NUM_ADDRS]; -}; - -/* The structure of a next header compressor. This compressor is provided - * by architecture-specific logic outside of the network stack. - * - * TODO: needs more parameters when compressing extension headers, etc. - */ - -struct sixlowpan_nhcompressor_s -{ - CODE int (*is_compressable)(uint8_t next_header); - - /* Compress next header (TCP/UDP, etc) - ptr points to next header to - * compress. + /* i_reasstag. Each frame in the reassembly has a tag. That tag must + * match the reassembly tag in the fragments being merged. */ - CODE int (*compress)(FAR uint8_t *compressed, FAR uint8_t *uncompressed_len); + uint16_t i_reasstag; - /* Uncompress next header (TCP/UDP, etc) - ptr points to next header to - * uncompress. + /* The source MAC address of the fragments being merged */ + + struct rimeaddr_s i_fragsrc; + + /* That time at which reassembly was started. If the elapsed time + * exceeds CONFIG_NET_6LOWPAN_MAXAGE, then the reassembly will + * be cancelled. */ - CODE int (*uncompress)(FAR uint8_t *compressed, FAR uint8_t *lowpanbuf, - FAR uint8_t *uncompressed_len); -}; - -/* RIME sniffer callbacks */ - -struct sixlowpan_rime_sniffer_s -{ - CODE void (*input)(void); - CODE void (*output)(int mac_status); + systime_t i_time; +#endif /* CONFIG_NET_6LOWPAN_FRAG */ }; /**************************************************************************** @@ -493,40 +416,59 @@ struct sixlowpan_rime_sniffer_s ****************************************************************************/ /**************************************************************************** - * Function: sixlowpan_set_compressor + * Name: sixlowpan_input * * Description: - * Configure to use the architecture-specific compressor. + * Process an incoming 6loWPAN frame. * - * Input parameters: - * compressor - A reference to the new compressor to be used. This may - * be a NULL value to disable the compressor. + * This function is called when the device driver has received a 6loWPAN + * frame from the network. The frame from the device driver must be + * provided in a IOB present in the i_framelist: The frame data is in the + * IOB io_data[] buffer and the length of the frame is in the IOB io_len + * field. Only a single IOB is expected in the i_framelist. This incoming + * data will be processed one frame at a time. + * + * An non-NULL d_buf of size CONFIG_NET_6LOWPAN_MTU must also be provided. + * The frame will be decompressed and placed in the d_buf. Fragmented + * packets will also be reassembled in the d_buf as they are received + * (meaning for the driver, that two packet buffers are required: One for + * reassembly of RX packets and one used for TX polling). + * + * After each frame is processed into d_buf, the IOB is removed and + * deallocated. i_framelist will be nullified. If reassembly is + * incomplete, this function will return to called with i_framelist + * equal to NULL. The partially reassembled packet must be preserved by + * the IEEE802.15.4 MAC and provided again when the next frame is + * received. + * + * When the packet in the d_buf is fully reassembled, it will be provided + * to the network as with any other received packet. d_len will be set + * the the length of the uncompressed, reassembled packet. + * + * After the network processes the packet, d_len will be set to zero. + * Network logic may also decide to send a response to the packet. In + * that case, the outgoing network packet will be placed in d_buf the + * d_buf and d_len will be set to a non-zero value. That case is handled + * by this function. + * + * If that case occurs, the packet will be converted to a list of + * compressed and possibly fragmented frames in i_framelist as with other + * TX operations. + * + * So from the standpoint of the IEEE802.15.4 MAC driver, there are two + * possible results: (1) i_framelist is NULL meaning that the frame + * was fully processed and freed, or (2) i_framelist is non-NULL meaning + * that there are outgoing frame(s) to be sent. + * + * Input Parameters: + * ieee - The IEEE802.15.4 MAC network driver interface. * * Returned Value: - * None + * Ok is returned on success; Othewise a negated errno value is returned. * ****************************************************************************/ -void sixlowpan_set_compressor(FAR struct sixlowpan_nhcompressor_s *compressor); +int sixlowpan_input(FAR struct ieee802154_driver_s *ieee); -/**************************************************************************** - * Function: sixlowpan_set_sniffer - * - * Description: - * Configure to use an architecture-specific sniffer to enable tracing of - * IP. - * - * Input parameters: - * sniffer - A reference to the new sniffer to be used. This may - * be a NULL value to disable the sniffer. - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_NET_6LOWPAN_SNIFFER -void sixlowpan_set_sniffer(FAR struct sixlowpan_rime_sniffer_s *sniffer); -#endif - -#endif /* __INCLUDE_NUTTX_NET_SIXLOWOAN_H */ +#endif /* CONFIG_NET_6LOWPAN */ +#endif /* __INCLUDE_NUTTX_NET_SIXLOWPAN_H */ diff --git a/include/nuttx/net/usrsock.h b/include/nuttx/net/usrsock.h new file mode 100644 index 00000000000..3ca990d44e3 --- /dev/null +++ b/include/nuttx/net/usrsock.h @@ -0,0 +1,233 @@ +/**************************************************************************** + * include/nuttx/net/usrsock.h + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_NET_USRSOCK_H +#define __INCLUDE_NUTTX_NET_USRSOCK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Event message flags */ + +#define USRSOCK_EVENT_ABORT (1 << 0) +#define USRSOCK_EVENT_SENDTO_READY (1 << 1) +#define USRSOCK_EVENT_RECVFROM_AVAIL (1 << 2) +#define USRSOCK_EVENT_REMOTE_CLOSED (1 << 3) + +/* Response message flags */ + +#define USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS (1 << 0) +#define USRSOCK_MESSAGE_FLAG_EVENT (1 << 1) + +#define USRSOCK_MESSAGE_IS_EVENT(flags) \ + (!!((flags) & USRSOCK_MESSAGE_FLAG_EVENT)) +#define USRSOCK_MESSAGE_IS_REQ_RESPONSE(flags) \ + (!USRSOCK_MESSAGE_IS_EVENT(flags)) + +#define USRSOCK_MESSAGE_REQ_IN_PROGRESS(flags) \ + (!!((flags) & USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS)) +#define USRSOCK_MESSAGE_REQ_COMPLETED(flags) \ + (!USRSOCK_MESSAGE_REQ_IN_PROGRESS(flags)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Request types */ + +enum usrsock_request_types_e +{ + USRSOCK_REQUEST_SOCKET = 0, + USRSOCK_REQUEST_CLOSE, + USRSOCK_REQUEST_CONNECT, + USRSOCK_REQUEST_SENDTO, + USRSOCK_REQUEST_RECVFROM, + USRSOCK_REQUEST_SETSOCKOPT, + USRSOCK_REQUEST_GETSOCKOPT, + USRSOCK_REQUEST_GETSOCKNAME, + USRSOCK_REQUEST_BIND, + USRSOCK_REQUEST__MAX +}; + +/* Response/event message types */ + +enum usrsock_message_types_e +{ + USRSOCK_MESSAGE_RESPONSE_ACK = 0, + USRSOCK_MESSAGE_RESPONSE_DATA_ACK, + USRSOCK_MESSAGE_SOCKET_EVENT, +}; + +/* Request structures (kernel => /dev/usrsock => daemon) */ + +begin_packed_struct struct usrsock_request_common_s +{ + int8_t reqid; + uint8_t xid; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_socket_s +{ + struct usrsock_request_common_s head; + + int16_t domain; + int16_t type; + int16_t protocol; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_close_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_bind_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; + uint16_t addrlen; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_connect_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; + uint16_t addrlen; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_sendto_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; + uint16_t addrlen; + uint16_t buflen; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_recvfrom_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; + uint16_t max_buflen; + uint16_t max_addrlen; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_setsockopt_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; + int16_t level; + int16_t option; + uint16_t valuelen; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_getsockopt_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; + int16_t level; + int16_t option; + uint16_t max_valuelen; +} end_packed_struct; + +begin_packed_struct struct usrsock_request_getsockname_s +{ + struct usrsock_request_common_s head; + + int16_t usockid; + uint16_t max_addrlen; +} end_packed_struct; + +/* Response/event message structures (kernel <= /dev/usrsock <= daemon) */ + +begin_packed_struct struct usrsock_message_common_s +{ + int8_t msgid; + int8_t flags; +} end_packed_struct; + +/* Request acknowledgment/completion message */ + +begin_packed_struct struct usrsock_message_req_ack_s +{ + struct usrsock_message_common_s head; + + uint8_t xid; + int32_t result; +} end_packed_struct; + +/* Request acknowledgment/completion message */ + +begin_packed_struct struct usrsock_message_datareq_ack_s +{ + struct usrsock_message_req_ack_s reqack; + + /* head.result => positive buflen, negative error-code. */ + + uint16_t valuelen; /* length of value returned after buffer */ + uint16_t valuelen_nontrunc; /* actual non-truncated length of value at + * daemon-sïde. */ +} end_packed_struct; + +/* Socket event message */ + +begin_packed_struct struct usrsock_message_socket_event_s +{ + struct usrsock_message_common_s head; + + int16_t usockid; + uint16_t events; +} end_packed_struct; + +#endif /* __INCLUDE_NUTTX_NET_USRSOCK_H */ diff --git a/include/nuttx/sensors/hts221.h b/include/nuttx/sensors/hts221.h index 47142c3f82d..1b9ddc56251 100644 --- a/include/nuttx/sensors/hts221.h +++ b/include/nuttx/sensors/hts221.h @@ -117,10 +117,11 @@ typedef struct hts221_settings_s typedef struct hts221_config_s { int irq; - CODE int (*irq_attach)(FAR struct hts221_config_s * state, xcpt_t isr); - CODE void (*irq_enable)(FAR const struct hts221_config_s * state, + CODE int (*irq_attach)(FAR struct hts221_config_s * state, xcpt_t isr, + FAR void *arg); + CODE void (*irq_enable)(FAR const struct hts221_config_s *state, bool enable); - CODE void (*irq_clear)(FAR const struct hts221_config_s * state); + CODE void (*irq_clear)(FAR const struct hts221_config_s *state); CODE int (*set_power)(FAR const struct hts221_config_s *state, bool on); } hts221_config_t; diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h index 231c2056012..18b414773fe 100644 --- a/include/nuttx/sensors/ioctl.h +++ b/include/nuttx/sensors/ioctl.h @@ -125,4 +125,22 @@ #define SNIOC_READ_CONVERT_DATA _SNIOC(0x002f) #define SNIOC_DUMP_REGS _SNIOC(0x0030) +/* IOCTL commands unique to the LPS25H */ + +#define SNIOC_GET_DEV_ID _SNIOC(0x0031) +#define SNIOC_CFGR _SNIOC(0x0032) +#define SNIOC_TEMPERATURE_OUT _SNIOC(0x0033) +#define SNIOC_PRESSURE_OUT _SNIOC(0x0034) +#define SNIOC_SENSOR_OFF _SNIOC(0x0035) + +/* IOCTL commands unique to the LIS2DH */ + +#define SNIOC_WRITESETUP _SNIOC(0x0036) /* Arg: uint8_t value */ +#define SNIOC_WRITE_INT1THRESHOLD _SNIOC(0x0037) /* Arg: uint8_t value */ +#define SNIOC_WRITE_INT2THRESHOLD _SNIOC(0x0038) /* Arg: uint8_t value */ +#define SNIOC_RESET_HPFILTER _SNIOC(0x0039) /* Arg: uint8_t value */ +#define SNIOC_START_SELFTEST _SNIOC(0x003a) /* Arg: uint8_t value */ +#define SNIOC_WHO_AM_I _SNIOC(0x003b) +#define SNIOC_READ_TEMP _SNIOC(0x003c) /* Arg: int16_t value */ + #endif /* __INCLUDE_NUTTX_SENSORS_IOCTL_H */ diff --git a/include/nuttx/sensors/lis2dh.h b/include/nuttx/sensors/lis2dh.h new file mode 100644 index 00000000000..bede2795c23 --- /dev/null +++ b/include/nuttx/sensors/lis2dh.h @@ -0,0 +1,445 @@ +/**************************************************************************** + * include/nuttx/sensors/lis2dh.h + * LIS2DH accelerometer driver + * + * Copyright (C) 2014-2017 Haltian Ltd. All rights reserved. + * Authors: Timo Voutilainen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_LIS2DH_H +#define __INCLUDE_NUTTX_SENSORS_LIS2DH_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-Processor Declarations + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#define ST_LIS2DH_WHOAMI_VALUE 0x33 /* Valid WHOAMI register value */ + +/* LIS2DH Internal Registers **********************************************/ + +#define ST_LIS2DH_WHOAMI_REG 0x0f /* WHOAMI register */ + +#define ST_LIS2DH_STATUS_AUX_REG 0x07 /* Temperature status */ +#define ST_LIS2DH_OUT_TEMP_L_REG 0x0c /* Temperature data */ +#define ST_LIS2DH_OUT_TEMP_H_REG 0x0d /* Temperature data */ + +#define ST_LIS2DH_TEMP_CFG_REG 0x1f + +#define ST_LIS2DH_CTRL_REG1 0x20 + +/* CR1 ODR 4 MSBs (XXXX---) */ + +#define ST_LIS2DH_CR1_ODR_PWR_DWN 0x00 +#define ST_LIS2DH_CR1_ODR_1HZ 0x10 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_10HZ 0x20 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_25HZ 0x30 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_50HZ 0x40 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_100HZ 0x50 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_200HZ 0x60 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_400HZ 0x70 /* HR / Normal / Low Power */ +#define ST_LIS2DH_CR1_ODR_1620HZ 0x80 /* Low Power */ +#define ST_LIS2DH_CR1_ODR_1344_5376HZ 0x90 /* HR / Normal: 1344Hz, Low power: 5376Hz*/ +#define ST_LIS2DH_CR1_LOWP_ENABLE 0x08 /* Low power mode enable */ +#define ST_LIS2DH_CR1_ZEN 0x04 /* Z-Axis Enable */ +#define ST_LIS2DH_CR1_YEN 0x02 /* Y-Axis Enable */ +#define ST_LIS2DH_CR1_XEN 0x01 /* X-Axis Enable */ + +#define ST_LIS2DH_CTRL_REG2 0x21 + +/* HPM1 .... HP FILT_MODE (XX------) */ + +#define ST_LIS2DH_CR2_HPFILT_M_NORM 0x00 /* Normal mode (reset reading REFERENCE/DATACAPTURE (26h) register) */ +#define ST_LIS2DH_CR2_HPFILT_M_REFSIG 0x40 /* Reference signal for filtering */ +#define ST_LIS2DH_CR2_HPFILT_M_NORM2 0x80 /* Normal mode */ +#define ST_LIS2DH_CR2_HPFILT_M_AUTOR 0xc0 /* Autoreset on interrupt event */ +#define ST_LIS2DH_CR2_FDS 0x08 + +/* HPIS1 HPFILT ENABLE for INT1 (-------X) */ + +#define ST_LIS2DH_CR2_HPENABLED_INT1 0x01 /* HP filter enabled for INT1 */ + +/* HPIS2 HPFILT ENABLE for INT2 (------X-) */ + +#define ST_LIS2DH_CR2_HPENABLED_INT2 0x02 /* HP filter enabled for INT2 */ + +#define ST_LIS2DH_CTRL_REG3 0x22 + +/* I1_AOI1 ENABLE for INT2 (-X------) */ + +#define ST_LIS2DH_CR3_I1_AOI1_ENABLED 0x40 /* AOI1 interrupt on INT1 pin. */ +#define ST_LIS2DH_CR3_I1_AOI2_ENABLED 0x20 /* AOI2 interrupt on INT1 pin. */ +#define ST_LIS2DH_CR3_I1_DRDY1 0x10 /* DRDY1 interrupt on INT1 pin. */ +#define ST_LIS2DH_CR3_I1_DRDY2 0x08 /* DRDY2 interrupt on INT1 pin. */ +#define ST_LIS2DH_CR3_I1_WTM 0x04 /* FIFO Watermark interrupt on INT1 pin. */ +#define ST_LIS2DH_CR3_I1_OVERRUN 0x02 /* FIFO Overrun interrupt on INT1 pin. */ + +#define ST_LIS2DH_CTRL_REG4 0x23 + +/* BDU ... Block Data Update (X-------) */ + +#define ST_LIS2DH_CR4_BDU_CONT 0x00 /* Continuous update (Default) */ +#define ST_LIS2DH_CR4_BDU_UPD_ON_READ 0x80 /* Output registers not updated until MSB and LSB have been read */ +#define ST_LIS2DH_CR4_FULL_SCALE_2G 0x0 +#define ST_LIS2DH_CR4_FULL_SCALE_4G 0x10 +#define ST_LIS2DH_CR4_FULL_SCALE_8G 0x20 +#define ST_LIS2DH_CR4_FULL_SCALE_16G 0x30 + +/* HR .. Operation mode selector (----X---) */ + +#define ST_LIS2DH_CR4_HR_ENABLED 0x08 /* See section 2.6.3 in datasheet */ + +#define ST_LIS2DH_CTRL_REG5 0x24 +#define ST_LIS2DH_CR5_BOOT 0x80 +#define ST_LIS2DH_CR5_FIFO_EN 0x40 +#define ST_LIS2DH_CR5_LIR_INT1 0x08 +#define ST_LIS2DH_CR5_D4D_INT1 0x04 +#define ST_LIS2DH_CR5_LIR_INT2 0x02 +#define ST_LIS2DH_CR5_D4D_INT2 0x01 + +#define ST_LIS2DH_CTRL_REG6 0x25 +#define ST_LIS2DH_REFERENCE_REG 0x26 + +#define ST_LIS2DH_STATUS_REG 0x27 /* Status Register */ +#define ST_LIS2DH_SR_ZYXOR 0x80 /* OR'ed X,Y and Z data over-run */ +#define ST_LIS2DH_SR_ZOR 0x40 /* individual data over-run ... */ +#define ST_LIS2DH_SR_YOR 0x20 +#define ST_LIS2DH_SR_XOR 0x10 +#define ST_LIS2DH_SR_ZYXDA 0x08 /* OR'ed X,Y and Z data available */ +#define ST_LIS2DH_SR_ZDA 0x04 /* individual data available ... */ +#define ST_LIS2DH_SR_YDA 0x02 +#define ST_LIS2DH_SR_XDA 0x01 + +#define ST_LIS2DH_OUT_X_L_REG 0x28 +#define ST_LIS2DH_OUT_X_H_REG 0x29 +#define ST_LIS2DH_OUT_Y_L_REG 0x2a +#define ST_LIS2DH_OUT_Y_H_REG 0x2b +#define ST_LIS2DH_OUT_Z_L_REG 0x2c +#define ST_LIS2DH_OUT_Z_H_REG 0x2d + +#define ST_LIS2DH_FIFO_CTRL_REG 0x2e +#define ST_LIS2DH_FIFOCR_THRESHOLD_MASK 0x1f +#define ST_LIS2DH_FIFOCR_THRESHOLD(x) ((x) & ST_LIS2DH_FIFOCR_THRESHOLD_MASK) +#define ST_LIS2DH_FIFOCR_INT1 0x00 +#define ST_LIS2DH_FIFOCR_INT2 0x20 +#define ST_LIS2DH_FIFOCR_MODE_MASK 0xc0 + +#define ST_LIS2DH_FIFO_SRC_REG 0x2f +#define ST_LIS2DH_FIFOSR_NUM_SAMP_MASK 0x1f +#define ST_LIS2DH_FIFOSR_EMPTY 0x20 +#define ST_LIS2DH_FIFOSR_OVRN_FIFO 0x40 +#define ST_LIS2DH_FIFOSR_WTM 0x80 + +#define ST_LIS2DH_INT1_CFG_REG 0x30 +#define ST_LIS2DH_INT_CFG_AOI 0x80 +#define ST_LIS2DH_INT_CFG_6D 0x40 +#define ST_LIS2DH_INT_CFG_ZHIE 0x20 +#define ST_LIS2DH_INT_CFG_ZLIE 0x10 +#define ST_LIS2DH_INT_CFG_YHIE 0x08 +#define ST_LIS2DH_INT_CFG_YLIE 0x04 +#define ST_LIS2DH_INT_CFG_XHIE 0x02 +#define ST_LIS2DH_INT_CFG_XLIE 0x01 + +#define ST_LIS2DH_INT1_SRC_REG 0x31 +#define ST_LIS2DH_INT_SR_XLOW 0x01 +#define ST_LIS2DH_INT_SR_XHIGH 0x02 +#define ST_LIS2DH_INT_SR_YLOW 0x04 +#define ST_LIS2DH_INT_SR_YHIGH 0x08 +#define ST_LIS2DH_INT_SR_ZLOW 0x10 +#define ST_LIS2DH_INT_SR_ZHIGH 0x20 +#define ST_LIS2DH_INT_SR_ACTIVE 0x40 + +#define ST_LIS2DH_INT1_THS_REG 0x32 /* 7-bit value for threshold */ + +#define ST_LIS2DH_INT1_DUR_REG 0x33 /* 7-bit value for duration */ + +#define ST_LIS2DH_INT2_CFG_REG 0x34 + +#define ST_LIS2DH_INT2_SRC_REG 0x35 + +#define ST_LIS2DH_INT2_THS_REG 0x36 /* 7-bit value for threshold */ + +#define ST_LIS2DH_INT2_DUR_REG 0x37 /* 7-bit value for duration */ + +#define ST_LIS2DH_CLICK_CFG_REG 0x38 + +#define ST_LIS2DH_CLICK_SRC_REG 0x39 + +#define ST_LIS2DH_CLICK_THS_REG 0x3a + +#define ST_LIS2DH_TIME_LIMIT_REG 0x3b + +#define ST_LIS2DH_TIME_LATENCY_REG 0x3c + +#define ST_LIS2DH_TIME_WINDOW_REG 0x3d + +#define ST_LIS2DH_ACT_DUR_REG 0x3f + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +enum lis2dh_ouput_data_rate +{ + LIS2DH_ODR_POWER_DOWN = 0x00, + LIS2DH_ODR_1HZ = 0x10, + LIS2DH_ODR_10HZ = 0x20, + LIS2DH_ODR_25HZ = 0x30, + LIS2DH_ODR_50HZ = 0x40, + LIS2DH_ODR_100HZ = 0x50, + LIS2DH_ODR_200HZ = 0x60, + LIS2DH_ODR_400HZ = 0x70, + LIS2DH_ODR_1620HZ = 0x80, + LIS2DH_ODR_5376HZ = 0x90, +}; + +enum lis2dh_high_pass_filter_mode +{ + LIS2DH_REFERENCE_SIGNAL = 0x40, + LIS2DH_NORMAL_MODE = 0x80, + LIS2DH_AUTORESET_ON_INTERRUPT = 0xc0, +}; + +enum lis2dh_scale_range +{ + LIS2DH_RANGE_2G = 0x00, + LIS2DH_RANGE_4G = 0x10, + LIS2DH_RANGE_8G = 0x20, + LIS2DH_RANGE_16G = 0x30, +}; + +enum lis2dh_self_test +{ + LIS2DH_NORMAL = 0x00, + LIS2DH_SELF_TEST0 = 0x02, + LIS2DH_SELF_TEST1 = 0x04, +}; + +enum lis2dh_fifo_mode +{ + LIS2DH_BYPASS_MODE = 0x00, + LIS2DH_FIFO_MODE = 0x40, + LIS2DH_STREAM_MODE = 0x80, + LIS2DH_TRIGGER_MODE = 0xc0, +}; + +enum lis2dh_interrupt_mode +{ + LIS2DH_OR_COMBINATION = 0x00, + LIS2DH_6D_MOVEMENT = 0x40, + LIS2DH_AND_COMBINATION = 0x80, + LIS2DH_6D_POSITION = 0xc0, +}; + +struct lis2dh_vector_s +{ + int16_t x, y, z; +} packed_struct; + +struct lis2dh_res_header +{ + uint8_t meas_count; + bool int1_occurred; + uint8_t int1_source; + bool int2_occurred; + uint8_t int2_source; +} packed_struct; + +struct lis2dh_result +{ + struct lis2dh_res_header header; + struct lis2dh_vector_s measurements[0]; +} packed_struct; + +struct lis2dh_setup +{ + bool temp_enable:1; + bool xy_axis_fixup:1; + + uint8_t data_rate; + uint8_t low_power_mode_enable; + uint8_t zen; + uint8_t yen; + uint8_t xen; + + uint8_t hpmode; + uint8_t hpcf; + uint8_t fds; + uint8_t hpclick; + uint8_t hpis2; + uint8_t hpis1; + + uint8_t int1_click_enable; + uint8_t int1_aoi_enable; + uint8_t int2_aoi_enable; + uint8_t int1_drdy_enable; + uint8_t int2_drdy_enable; + uint8_t int_wtm_enable; + uint8_t int_overrun_enable; + + uint8_t bdu; + uint8_t endian; + uint8_t fullscale; + uint8_t high_resolution_enable; + uint8_t selftest; + uint8_t spi_mode; + + uint8_t reboot; + uint8_t fifo_enable; + uint8_t int1_latch; + uint8_t int1_4d_enable; + uint8_t int2_latch; + uint8_t int2_4d_enable; + + uint8_t int2_click_enable; + uint8_t int_enable; + uint8_t boot_int1_enable; + uint8_t high_low_active; + + uint8_t reference; + + uint8_t fifo_mode; + uint8_t trigger_selection; + uint8_t fifo_trigger_threshold; + + uint8_t int1_interrupt_mode; + uint8_t int1_enable_6d; + uint8_t int1_int_z_high_enable; + uint8_t int1_int_z_low_enable; + uint8_t int1_int_y_high_enable; + uint8_t int1_int_y_low_enable; + uint8_t int1_int_x_high_enable; + uint8_t int1_int_x_low_enable; + uint8_t int1_int_threshold; + uint8_t int1_int_duration; + + uint8_t int2_interrupt_mode; + uint8_t int2_enable_6d; + uint8_t int2_int_z_high_enable; + uint8_t int2_int_z_low_enable; + uint8_t int2_int_y_high_enable; + uint8_t int2_int_y_low_enable; + uint8_t int2_int_x_high_enable; + uint8_t int2_int_x_low_enable; + uint8_t int2_int_threshold; + uint8_t int2_int_duration; + + uint8_t z_double_click_enable; + uint8_t z_single_click_enable; + uint8_t y_double_click_enable; + uint8_t y_single_click_enable; + uint8_t x_double_click_enable; + uint8_t x_single_click_enable; + + uint8_t click_threshold; + uint8_t click_time_limit; + uint8_t click_time_latency; + uint8_t click_time_window; +}; + +struct lis2dh_config_s +{ + /* Device characterization */ + + int irq; /* IRQ number received by interrupt handler. */ + + /* IRQ/GPIO access callbacks. These operations all hidden behind + * callbacks to isolate the lis2dh driver from differences in GPIO + * interrupt handling by varying boards and MCUs. + * + * irq_attach - Attach the lis2dh interrupt handler to the GPIO interrupt + * irq_enable - Enable or disable the GPIO interrupt + * clear_irq - Acknowledge/clear any pending GPIO interrupt + * + */ + + CODE int (*irq_attach)(FAR struct lis2dh_config_s *state, xcpt_t isr, FAR void *arg); + CODE void (*irq_enable)(FAR const struct lis2dh_config_s *state, bool enable); + CODE void (*irq_clear)(FAR const struct lis2dh_config_s *state); + CODE bool (*read_int1_pin)(void); + CODE bool (*read_int2_pin)(void); +}; + +struct lis2dh_raw_data_s +{ + uint16_t out_x; + uint16_t out_y; + uint16_t out_z; +} packed_struct; + +typedef struct lis2dh_raw_data_s lis2dh_raw_data_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: lis2dh_register + * + * Description: + * Register the LIS2DH character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/acc0" + * i2c - An instance of the I2C interface to use to communicate with LIS2DH + * addr - The I2C address of the LIS2DH. The base I2C address of the LIS2DH + * is 0x18. Bit 0 can be controlled via SA0 pad - when connected to + * voltage supply the address is 0x19. + * config - Pointer to LIS2DH configuration + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int lis2dh_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct lis2dh_config_s *config); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_SENSORS_LIS2DH_H */ diff --git a/include/nuttx/sensors/lps25h.h b/include/nuttx/sensors/lps25h.h new file mode 100644 index 00000000000..6f30d61cfc4 --- /dev/null +++ b/include/nuttx/sensors/lps25h.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * include/nuttx/sensors/lps25h.h + * + * Copyright (C) 2014, 2017 Haltian Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTT_SENSORS_LPS25H_H +#define __INCLUDE_NUTT_SENSORS_LPS25H_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LPS25H_TEMPER_DIVIDER 1000 + +#define LPS25H_VALID_WHO_AM_I 0xbd + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct lps25h_temper_data_s +{ + int32_t int_temper; /* int_temper value must be divided by + * LPS25H_TEMPER_DIVIDER in your app code */ + int16_t raw_data; +} lps25h_temper_data_t; + +typedef struct lps25h_pressure_data_s +{ + uint32_t pressure_int_hP; + uint32_t pressure_Pa; + uint32_t raw_data; +} lps25h_pressure_data_t; + +typedef struct lps25h_who_am_i_data +{ + uint8_t who_am_i; +} lps25h_who_am_i_data; + +typedef struct lps25h_config_s +{ + /* Device characterization */ + + int irq; /* IRQ number received by interrupt handler. */ + + /* IRQ/GPIO access callbacks. These operations all hidden behind callbacks + * to isolate the driver from differences in GPIO interrupt handling + * by varying boards and MCUs. + * irq_attach - Attach the interrupt handler to the GPIO interrupt + * irq_enable - Enable or disable the GPIO + * irq_clear - Acknowledge/clear any pending GPIO interrupt + * set_power - Ask board to turn on regulator + */ + + CODE int (*irq_attach)(FAR struct lps25h_config_s *state, xcpt_t isr, + FAR void *arg); + CODE void (*irq_enable)(FAR const struct lps25h_config_s *state, + bool enable); + CODE void (*irq_clear)(FAR const struct lps25h_config_s *state); + CODE int (*set_power)(FAR const struct lps25h_config_s *state, bool on); +} lps25h_config_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int lps25h_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR lps25h_config_t *config); + +#endif /* __INCLUDE_NUTT_SENSORS_LPS25H_H */ diff --git a/include/nuttx/usb/fusb301.h b/include/nuttx/usb/fusb301.h new file mode 100644 index 00000000000..deecff2b752 --- /dev/null +++ b/include/nuttx/usb/fusb301.h @@ -0,0 +1,243 @@ +/**************************************************************************** + * include/nuttx/usb/fusb301.h + * FUSB301 USB type-C controller driver + * + * Copyright (C) 2016-2017 Haltian Ltd. All rights reserved. + * Authors: Harri Luhtala + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_USB_FUSB301_H +#define __INCLUDE_NUTTX_USB_FUSB301_H + +#include + +/************************************************************************************ + * Pre-Processor Declarations + ************************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* IOCTL Commands ***********************************************************/ + +#define USBCIOC_READ_DEVID _USBCIOC(0x0001) /* Arg: uint8_t* pointer */ +#define USBCIOC_SETUP _USBCIOC(0x0002) /* Arg: uint8_t* pointer */ +#define USBCIOC_SET_MODE _USBCIOC(0x0003) /* Arg: uint8_t value */ +#define USBCIOC_SET_STATE _USBCIOC(0x0004) /* Arg: uint8_t value */ +#define USBCIOC_READ_STATUS _USBCIOC(0x0005) /* Arg: uint8_t* pointer*/ +#define USBCIOC_READ_DEVTYPE _USBCIOC(0x0006) /* Arg: uint8_t* pointer*/ +#define USBCIOC_RESET _USBCIOC(0x0007) /* Arg: None */ + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +enum fusb301_reg_address_e +{ + FUSB301_DEV_ID_REG = 0x01, + FUSB301_MODE_REG, + FUSB301_CONTROL_REG, + FUSB301_MANUAL_REG, + FUSB301_RESET_REG, + FUSB301_MASK_REG = 0x10, + FUSB301_STATUS_REG, + FUSB301_TYPE_REG, + FUSB301_INTERRUPT_REG +}; + +/* Device ID - 0x01 */ + +enum fusb301_devid_mask_e +{ + DEV_ID_REVISION_MASK = 0x0F, + DEV_ID_VERSION_MASK = 0xF0 +}; + +#define DEV_ID_VER_A 0x10 +#define DEV_ID_REV_C 0x02 + +/* Modes - 0x02 */ + +enum fusb301_mode_e +{ + MODE_SRC = (1 << 0), + MODE_SRC_ACC = (1 << 1), + MODE_SNK = (1 << 2), + MODE_SNK_ACC = (1 << 3), + MODE_DRP = (1 << 4), + MODE_DRP_ACC = (1 << 5) +}; + +/* Control - 0x03 */ + +enum fusb301_control_e +{ + CONTROL_INT_ENABLE = (0 << 0), + CONTROL_INT_DISABLE = (1 << 0), + CONTROL_CUR_DISABLED = (0 << 1), + CONTROL_CUR_DEFAULT = (1 << 1), + CONTROL_CUR_1500 = (2 << 1), + CONTROL_CUR_3000 = (3 << 1), + CONTROL_TGL_35_15MS = (0 << 4), + CONTROL_TGL_30_20MS = (1 << 4), + CONTROL_TGL_25_25MS = (2 << 4), + CONTROL_TGL_20_30MS = (3 << 4) +}; + +/* Manual - 0x04 */ + +enum fusb301_manual_e +{ + MANUAL_ERROR_REC = (1 << 0), + MANUAL_DISABLED = (1 << 1), + MANUAL_UNATT_SRC = (1 << 2), + MANUAL_UNATT_SNK = (1 << 3) +}; + +/* Reset - 0x05 */ + +enum fusb301_reset_e +{ + RESET_SW_RES = (1 << 0) +}; + +/* Interrupt mask - 0x10 */ + +enum fusb301_int_mask_e +{ + INT_MASK_ATTACK = (1 << 0), + INT_MASK_DETACH = (1 << 1), + INT_MASK_BC_LVL = (1 << 2), + INT_MASK_ACC_CH = (1 << 3) +}; + +/* Status - 0x11 */ + +enum fusb301_status_e +{ + STATUS_ATTACH = (1 << 0), + STATUS_BC_SINK_UNATT = (0 << 1), + STATUS_BC_SINK_DEF = (1 << 1), + STATUS_BC_SINK_1500 = (2 << 1), + STATUS_BC_SINK_3000 = (3 << 1), + STATUS_VBUS_OK = (1 << 3), + STATUS_CC_NO_CONN = (0 << 4), + STATUS_CC_1 = (1 << 4), + STATUS_CC_2 = (2 << 4), + STATUS_CC_FAULT = (3 << 4) +}; + +/* Type - 0x12 */ + +enum fusb301_type_e +{ + TYPE_AUDIOACC = (1 << 0), + TYPE_DEBUGACC = (1 << 1), + TYPE_SOURCE = (1 << 3), + TYPE_SINK = (1 << 4) +}; + +/* Interrupt - 0x13 */ + +enum fusb301_interrupt_e +{ + INTERRUPT_ATTACH = (1 << 0), + INTERRUPT_DETACH = (1 << 1), + INTERRUPT_BC_LVL = (1 << 2), + INTERRUPT_ACC_CH = (1 << 3) +}; + +struct fusb301_result_s +{ + uint8_t status; + uint8_t dev_type; +}; + +struct fusb301_setup_s +{ + uint8_t drp_toggle_timing; + uint8_t host_current; + uint8_t int_mask; + bool global_int_mask; +}; + +struct fusb301_config_s +{ + /* Device characterization */ + + int irq; + + CODE int (*irq_attach)(FAR struct fusb301_config_s *state, xcpt_t isr, + FAR void *arg); + CODE void (*irq_enable)(FAR struct fusb301_config_s *state, bool enable); + CODE void (*irq_clear)(FAR struct fusb301_config_s *state); +}; + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +/**************************************************************************** + * Name: fusb301_register + * + * Description: + * Register the FUSB301 character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/usbc0" + * i2c - An instance of the I2C interface to use to communicate with FUSB301 + * addr - The I2C address of the FUSB301. The I2C address of the FUSB301 is 0x25. + * config - Pointer to FUSB301 configuration + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int fusb301_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr, FAR struct fusb301_config_s *config); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_USB_FUSB301_H */ diff --git a/net/sixlowpan/sixlowpan_compressor.c b/include/nuttx/wireless/ieee802154/ieee802154_loopback.h similarity index 74% rename from net/sixlowpan/sixlowpan_compressor.c rename to include/nuttx/wireless/ieee802154/ieee802154_loopback.h index 837bad77602..d1952df24f3 100644 --- a/net/sixlowpan/sixlowpan_compressor.c +++ b/include/nuttx/wireless/ieee802154/ieee802154_loopback.h @@ -1,9 +1,12 @@ /**************************************************************************** - * net/sixlowpan/sixlowpan_compressor.c + * include/nuttx/net/ieee802154.h * - * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * + * Includes some definitions that a compatible with the LGPL GNU C Library + * header file of the same name. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -33,48 +36,38 @@ * ****************************************************************************/ +#ifndef __INCLUDE_NUTTX_WIRELESS_IEEE802154_IEEE802154_LOOPBACK_H +#define __INCLUDE_NUTTX_WIRELESS_IEEE802154_IEEE802154_LOOPBACK_H + /**************************************************************************** * Included Files ****************************************************************************/ #include -#include "nuttx/net/net.h" -#include "nuttx/net/sixlowpan.h" - -#include "sixlowpan/sixlowpan_internal.h" - -#ifdef CONFIG_NET_6LOWPAN +#ifdef CONFIG_IEEE802154_LOOPBACK /**************************************************************************** - * Public Functions + * Public Function Prototypes ****************************************************************************/ /**************************************************************************** - * Function: sixlowpan_set_compressor + * Function: ieee8021514_loopback * * Description: - * Configure to use the architecture-specific compressor. + * Initialize and register the Ieee802.15.4 MAC loopback network driver. * - * Input parameters: - * compressor - A reference to the new compressor to be used. This may - * be a NULL value to disable the compressor. + * Parameters: + * None * * Returned Value: - * None + * OK on success; Negated errno on failure. + * + * Assumptions: * ****************************************************************************/ -void sixlowpan_set_compressor(FAR struct sixlowpan_nhcompressor_s *compressor) -{ - /* Make sure that the compressor is not in use */ +int ieee8021514_loopback(void); - net_lock(); - - /* Then instantiate the new compressor */ - - g_sixlowpan_compressor = compressor; - net_unlock(); -} - -#endif /* CONFIG_NET_6LOWPAN */ +#endif /* CONFIG_IEEE802154_LOOPBACK */ +#endif /* __INCLUDE_NUTTX_WIRELESS_IEEE802154_IEEE802154_LOOPBACK_H */ diff --git a/libc/netdb/lib_dnscache.c b/libc/netdb/lib_dnscache.c index 5a95dc9b1b5..30cfdcd577f 100644 --- a/libc/netdb/lib_dnscache.c +++ b/libc/netdb/lib_dnscache.c @@ -268,8 +268,8 @@ int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, /* We have a match. Return the resolved host address */ #ifdef CONFIG_NET_IPv4 - if (entry->addr.addr.sa_family == AF_INET) #ifdef CONFIG_NET_IPv6 + if (entry->addr.addr.sa_family == AF_INET) #endif { inlen = sizeof(struct sockaddr_in); @@ -277,8 +277,8 @@ int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, #endif #ifdef CONFIG_NET_IPv6 - else #ifdef CONFIG_NET_IPv4 + else #endif { inlen = sizeof(struct sockaddr_in6); diff --git a/libc/netdb/lib_dnsinit.c b/libc/netdb/lib_dnsinit.c index 04b490a1675..b6e0f8d0e03 100644 --- a/libc/netdb/lib_dnsinit.c +++ b/libc/netdb/lib_dnsinit.c @@ -39,6 +39,7 @@ #include +#include #include #include #include diff --git a/net/Kconfig b/net/Kconfig index 2741c678169..da5eab15d2c 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -123,28 +123,37 @@ config NET_USER_DEVFMT config NET_ETHERNET bool "Ethernet support" - default y if !NET_SLIP - default n if NET_SLIP - select NETDEV_MULTINIC if NET_LOOPBACK || NET_SLIP || NET_TUN - select NET_MULTILINK if NET_LOOPBACK || NET_SLIP || NET_TUN + default y + select NETDEV_MULTINIC if NET_6LOWPAN || NET_LOOPBACK || NET_SLIP || NET_TUN + select NET_MULTILINK if NET_6LOWPAN || NET_LOOPBACK || NET_SLIP || NET_TUN ---help--- If NET_SLIP is not selected, then Ethernet will be used (there is no need to define anything special in the configuration file to use Ethernet -- it is the default). +config NET_6LOWPAN + bool "IEEE 802.15.4 6LoWPAN support" + default n + select NETDEV_MULTINIC if NET_ETHERNET || NET_LOOPBACK || NET_SLIP || NET_TUN + select NET_MULTILINK if NET_ETHERNET || NET_LOOPBACK || NET_SLIP || NET_TUN + depends on EXPERIMENTAL && NET_IPv6 + ---help--- + Enable support for IEEE 802.15.4 Low power Wireless Personal Area + Networking (6LoWPAN). + config NET_LOOPBACK bool "Local loopback" default n - select NETDEV_MULTINIC if NET_ETHERNET || NET_SLIP || NET_TUN - select NET_MULTILINK if NET_ETHERNET || NET_SLIP || NET_TUN + select NETDEV_MULTINIC if NET_ETHERNET || NET_6LOWPAN || NET_SLIP || NET_TUN + select NET_MULTILINK if NET_ETHERNET || NET_6LOWPAN || NET_SLIP || NET_TUN ---help--- Add support for the local network loopback device, lo. config NET_SLIP bool "SLIP support" default n - select NETDEV_MULTINIC if NET_ETHERNET || NET_LOOPBACK || NET_TUN - select NET_MULTILINK if NET_ETHERNET || NET_LOOPBACK || NET_TUN + select NETDEV_MULTINIC if NET_ETHERNET || NET_6LOWPAN || NET_LOOPBACK || NET_TUN + select NET_MULTILINK if NET_ETHERNET || NET_6LOWPAN || NET_LOOPBACK || NET_TUN ---help--- Enables building of the SLIP driver. SLIP requires at least one IP protocol selected. @@ -190,8 +199,8 @@ endif # NET_SLIP config NET_TUN bool "TUN Virtual Network Device support" default n - select NETDEV_MULTINIC if NET_ETHERNET || NET_LOOPBACK || NET_SLIP - select NET_MULTILINK if NET_ETHERNET || NET_LOOPBACK || NET_SLIP + select NETDEV_MULTINIC if NET_ETHERNET || NET_6LOWPAN || NET_LOOPBACK || NET_SLIP + select NET_MULTILINK if NET_ETHERNET || NET_6LOWPAN || NET_LOOPBACK || NET_SLIP select ARCH_HAVE_NETDEV_STATISTICS if NET_TUN @@ -236,6 +245,23 @@ config TUN_LPWORK endchoice # Work queue endif # NET_TUN +config NET_USRSOCK + bool "User-space networking stack API" + default n + ---help--- + Enable or disable user-space networking stack support. + + User-space networking stack API allows user-space daemon to + provide TCP/IP stack implementation for NuttX network. + + Main use for this is to allow use and integration of + HW-provided TCP/IP stacks for NuttX. + + For example, user-space daemon can translate /dev/usrsock API + requests to HW TCP/IP API requests while rest of the user-space + can access standard socket API, with socket descriptors that + can be used with NuttX system calls. + endmenu # Data link support source "net/netdev/Kconfig" @@ -271,6 +297,7 @@ source "net/arp/Kconfig" source "net/loopback/Kconfig" source "net/iob/Kconfig" source "net/procfs/Kconfig" +source "net/usrsock/Kconfig" source "net/utils/Kconfig" config NET_STATISTICS diff --git a/net/Makefile b/net/Makefile index 6898bad40e1..cd6979a4145 100644 --- a/net/Makefile +++ b/net/Makefile @@ -72,6 +72,7 @@ include devif/Make.defs include loopback/Make.defs include route/Make.defs include procfs/Make.defs +include usrsock/Make.defs include utils/Make.defs endif diff --git a/net/README.txt b/net/README.txt index 0f93c075813..e87a0889b51 100644 --- a/net/README.txt +++ b/net/README.txt @@ -22,21 +22,25 @@ Directory Structure +- route - Routing table support +- tcp - Transmission Control Protocol +- udp - User Datagram Protocol + +- usrsock - User socket API for user-space networking stack `- utils - Miscellaneous utility functions - +----------------------------------------------------------------+ - | Application layer | - +----------------------------------------------------------------+ - +----------------------------------------------------------------+ - | Socket layer (socket/) | - +----------------------------------------------------------------+ - +------------++--------------------------------------------------+ - | Network || Protocol stacks (arp, ipv6, icmp, pkt, tcp, udp) | - | Device |+--------------------------------------------------+ - | Interface |+------------------------------------++------------+ - | (netdev/) || Network Device Interface (devif/) || Utilities | - +------------++------------------------------------++------------+ - +----------------------------------------------------------------+ - | Network Device Drivers | - +----------------------------------------------------------------+ + +-------------------------------------------------------------------++------------------------+ + | Application layer || usrsock daemon | + +-------------------------------------------------------------------++------------------------+ + +-------------------------------------------------------------------++----------------+ +-----+ + | Socket layer (socket/) || /dev/usrsock | | | + +-------------------------------------------------------------------++----------------+ | | + +------------++--------------------------------------------------++-------------------+ | | + | Network || Protocol stacks (arp, ipv6, icmp, pkt, tcp, udp) || usrsock/ | | | + | Device |+--------------------------------------------------++-------------------+ | | + | Interface |+------------------------------------++---------------------------------+ | | + | (netdev/) || Network Device Interface (devif/) || Utilities | | | + +------------++------------------------------------++---------------------------------+ | | + +----------------------------------------------------------------+ | | + | Network Device Drivers | | HAL | + +----------------------------------------------------------------+ +-----+ + +----------------------------------------------------------------+ +--------------------------+ + | Networking Hardware | | Hardware TCP/IP Stack | + +----------------------------------------------------------------+ +--------------------------+ \ No newline at end of file diff --git a/net/devif/devif.h b/net/devif/devif.h index bca09878968..28135f4d30f 100644 --- a/net/devif/devif.h +++ b/net/devif/devif.h @@ -1,7 +1,7 @@ /**************************************************************************** * net/devif/devif.h * - * Copyright (C) 2007-2009, 2013-2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2013-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * This logic was leveraged from uIP which also has a BSD-style license: @@ -170,11 +170,13 @@ #define TCP_NEWDATA (1 << 1) #define UDP_NEWDATA TCP_NEWDATA #define PKT_NEWDATA TCP_NEWDATA +#define WPAN_NEWDATA TCP_NEWDATA #define TCP_SNDACK (1 << 2) #define TCP_REXMIT (1 << 3) #define TCP_POLL (1 << 4) #define UDP_POLL TCP_POLL #define PKT_POLL TCP_POLL +#define WPAN_POLL TCP_POLL #define TCP_BACKLOG (1 << 5) #define TCP_CLOSE (1 << 6) #define TCP_ABORT (1 << 7) @@ -234,12 +236,17 @@ */ struct net_driver_s; /* Forward reference */ + +typedef CODE uint16_t (*devif_callback_event_t)(FAR struct net_driver_s *dev, + FAR void *pvconn, + FAR void *pvpriv, + uint16_t flags); + struct devif_callback_s { FAR struct devif_callback_s *nxtconn; FAR struct devif_callback_s *nxtdev; - uint16_t (*event)(FAR struct net_driver_s *dev, FAR void *pvconn, - FAR void *pvpriv, uint16_t flags); + FAR devif_callback_event_t event; FAR void *priv; uint16_t flags; }; diff --git a/net/devif/devif_poll.c b/net/devif/devif_poll.c index 928b779fb38..235c58bcf28 100644 --- a/net/devif/devif_poll.c +++ b/net/devif/devif_poll.c @@ -45,6 +45,7 @@ #include #include #include +#include #include "devif/devif.h" #include "arp/arp.h" @@ -55,6 +56,21 @@ #include "icmp/icmp.h" #include "icmpv6/icmpv6.h" #include "igmp/igmp.h" +#include "sixlowpan/sixlowpan.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum devif_packet_type +{ + DEVIF_PKT = 0, + DEVIF_ICMP, + DEVIF_IGMP, + DEVIF_TCP, + DEVIF_UDP, + DEVIF_ICMP6 +}; /**************************************************************************** * Public Data @@ -68,6 +84,78 @@ systime_t g_polltime; * Private Functions ****************************************************************************/ +/**************************************************************************** + * Function: devif_packet_conversion + * + * Description: + * Generic output conversion hook. Only needed for IEEE802.15.4 for now + * is a point where support for other conversions may be provided. + * + * TCP output comes through three different mechansims. Either from: + * + * 1. TCP socket output. For the case of TCP output to an + * IEEE802.15.4, the TCP output is caught in the socket + * send()/sendto() logic and and redirected to 6loWPAN logic. + * 2. TCP output from the TCP state machine. That will occur + * during TCP packet processing by the TCP state meachine. + * 3. TCP output resulting from TX or timer polling + * + * Cases 2 is handled here. Logic here detected if (1) an attempt + * to return with d_len > 0 and (2) that the device is an + * IEEE802.15.4 MAC network driver. Under those conditions, 6loWPAN + * logic will be called to create the IEEE80215.4 frames. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_6LOWPAN +static void devif_packet_conversion(FAR struct net_driver_s *dev, + enum devif_packet_type pkttype) +{ +#ifdef CONFIG_NET_MULTILINK + /* Handle the case where multiple link layer protocols are supported */ + + if (dev->d_len > 0 && dev->d_lltype == NET_LL_IEEE802154) +#else + if (dev->d_len > 0) +#endif + { + if (pkttype == DEVIF_TCP) + { + FAR struct ipv6_hdr_s *ipv6 = (FAR struct ipv6_hdr_s *)dev->d_buf; + + /* This packet came from a response to TCP polling and is directed + * to an IEEE802.15.4 device using 6loWPAN. Verify that the outgoing + * packet is IPv6 with TCP protocol. + */ + + if (ipv6->vtc == IPv6_VERSION && ipv6->proto == IP_PROTO_TCP) + { + /* Let 6loWPAN convert IPv6 TCP output into IEEE802.15.4 frames. */ + + sixlowpan_tcp_send(dev); + } + else + { + nerr("ERROR: IPv6 version or protocol error. Packet dropped\n"); + nerr(" IP version: %02x proocol: %u\n", + ipv6->vtc, ipv6->proto); + } + } + else + { + nerr("ERROR: Non-TCP packet dropped. Packet type: %u\n", pkttype); + } + + dev->d_len = 0; + } +} +#else +# define devif_packet_conversion(dev,pkttype) +#endif /* CONFIG_NET_6LOWPAN */ + /**************************************************************************** * Function: devif_poll_pkt_connections * @@ -95,6 +183,10 @@ static int devif_poll_pkt_connections(FAR struct net_driver_s *dev, pkt_poll(dev, pkt_conn); + /* Perform any necessary conversions on outgoing packets */ + + devif_packet_conversion(dev, DEVIF_PKT); + /* Call back into the driver */ bstop = callback(dev); @@ -120,6 +212,10 @@ static inline int devif_poll_icmp(FAR struct net_driver_s *dev, icmp_poll(dev); + /* Perform any necessary conversions on outgoing packets */ + + devif_packet_conversion(dev, DEVIF_ICMP); + /* Call back into the driver */ return callback(dev); @@ -142,6 +238,10 @@ static inline int devif_poll_icmpv6(FAR struct net_driver_s *dev, icmpv6_poll(dev); + /* Perform any necessary conversions on outgoing packets */ + + devif_packet_conversion(dev, DEVIF_ICMP6); + /* Call back into the driver */ return callback(dev); @@ -168,6 +268,10 @@ static inline int devif_poll_igmp(FAR struct net_driver_s *dev, igmp_poll(dev); + /* Perform any necessary conversions on outgoing packets */ + + devif_packet_conversion(dev, DEVIF_IGMP); + /* Call back into the driver */ return callback(dev); @@ -186,7 +290,7 @@ static inline int devif_poll_igmp(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static int devif_poll_udp_connections(FAR struct net_driver_s *dev, devif_poll_callback_t callback) { @@ -201,6 +305,10 @@ static int devif_poll_udp_connections(FAR struct net_driver_s *dev, udp_poll(dev, conn); + /* Perform any necessary conversions on outgoing packets */ + + devif_packet_conversion(dev, DEVIF_UDP); + /* Call back into the driver */ bstop = callback(dev); @@ -208,7 +316,7 @@ static int devif_poll_udp_connections(FAR struct net_driver_s *dev, return bstop; } -#endif /* CONFIG_NET_UDP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Function: devif_poll_tcp_connections @@ -222,7 +330,7 @@ static int devif_poll_udp_connections(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, devif_poll_callback_t callback) { @@ -237,6 +345,10 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, tcp_poll(dev, conn); + /* Perform any necessary conversions on outgoing packets */ + + devif_packet_conversion(dev, DEVIF_TCP); + /* Call back into the driver */ bstop = callback(dev); @@ -261,7 +373,7 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline int devif_poll_tcp_timer(FAR struct net_driver_s *dev, devif_poll_callback_t callback, int hsec) @@ -277,6 +389,10 @@ static inline int devif_poll_tcp_timer(FAR struct net_driver_s *dev, tcp_timer(dev, conn, hsec); + /* Perform any necessary conversions on outgoing packets */ + + devif_packet_conversion(dev, DEVIF_TCP); + /* Call back into the driver */ bstop = callback(dev); @@ -349,7 +465,7 @@ int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback) if (!bstop) #endif -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK { /* Traverse all of the active TCP connections and perform the poll * action. @@ -360,7 +476,7 @@ int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback) if (!bstop) #endif -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK { /* Traverse all of the allocated UDP connections and perform * the poll action @@ -467,7 +583,7 @@ int devif_timer(FAR struct net_driver_s *dev, devif_poll_callback_t callback) neighbor_periodic(hsec); #endif -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK /* Traverse all of the active TCP connections and perform the * timer action. */ diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c index 5a773b1ef8d..faa23ad547d 100644 --- a/net/devif/ipv4_input.c +++ b/net/devif/ipv4_input.c @@ -391,7 +391,7 @@ int ipv4_input(FAR struct net_driver_s *dev) #endif /* CONFIG_NET_TCP_REASSEMBLY */ } -#if defined(CONFIG_NET_BROADCAST) && defined(CONFIG_NET_UDP) +#if defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK) /* If IP broadcast support is configured, we check for a broadcast * UDP packet, which may be destined to us (even if there is no IP * address yet assigned to the device as is the case when we are @@ -459,13 +459,13 @@ int ipv4_input(FAR struct net_driver_s *dev) switch (pbuf->proto) { -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK case IP_PROTO_TCP: /* TCP input */ tcp_ipv4_input(dev); break; #endif -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK case IP_PROTO_UDP: /* UDP input */ udp_ipv4_input(dev); break; diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c index 08166306798..24be107a47d 100644 --- a/net/devif/ipv6_input.c +++ b/net/devif/ipv6_input.c @@ -95,6 +95,7 @@ #include "neighbor/neighbor.h" #include "tcp/tcp.h" #include "udp/udp.h" +#include "sixlowpan/sixlowpan.h" #include "pkt/pkt.h" #include "icmpv6/icmpv6.h" @@ -197,7 +198,7 @@ int ipv6_input(FAR struct net_driver_s *dev) * negotiating over DHCP for an address). */ -#if defined(CONFIG_NET_BROADCAST) && defined(CONFIG_NET_UDP) +#if defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK) if (ipv6->proto == IP_PROTO_UDP && net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_alloneaddr)) { @@ -253,14 +254,52 @@ int ipv6_input(FAR struct net_driver_s *dev) switch (ipv6->proto) { -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK case IP_PROTO_TCP: /* TCP input */ - tcp_ipv6_input(dev); - break; -#endif + /* Forward the IPv6 TCP packet */ -#ifdef CONFIG_NET_UDP + tcp_ipv6_input(dev); + +#ifdef CONFIG_NET_6LOWPAN + /* TCP output comes through three different mechansims. Either from: + * + * 1. TCP socket output. For the case of TCP output to an + * IEEE802.15.4, the TCP output is caught in the socket + * send()/sendto() logic and and redirected to 6loWPAN logic. + * 2. TCP output from the TCP state machine. That will occur + * during TCP packet processing by the TCP state meachine. + * 3. TCP output resulting from TX or timer polling + * + * Cases 2 is handled here. Logic here detected if (1) an attempt + * to return with d_len > 0 and (2) that the device is an + * IEEE802.15.4 MAC network driver. Under those conditions, 6loWPAN + * logic will be called to create the IEEE80215.4 frames. + */ + +#ifdef CONFIG_NET_MULTILINK + /* Handle the case where multiple link layer protocols are supported */ + + if (dev->d_len > 0 && dev->d_lltype == CONFIG_NET_6LOWPAN) +#else + if (dev->d_len > 0) +#endif + { + /* Let 6loWPAN handle the TCP output */ + + sixlowpan_tcp_send(dev); + + /* Drop the packet in the d_buf */ + + goto drop; + } +#endif /* CONFIG_NET_6LOWPAN */ + break; +#endif /* NET_TCP_HAVE_STACK */ + +#ifdef NET_UDP_HAVE_STACK case IP_PROTO_UDP: /* UDP input */ + /* Forward the IPv6 UDP packet */ + udp_ipv6_input(dev); break; #endif @@ -269,6 +308,8 @@ int ipv6_input(FAR struct net_driver_s *dev) #ifdef CONFIG_NET_ICMPv6 case IP_PROTO_ICMP6: /* ICMP6 input */ + /* Forward the ICMPv6 packet */ + icmpv6_input(dev); break; #endif diff --git a/net/neighbor/Make.defs b/net/neighbor/Make.defs index cf13cbb732a..7f6b56244ec 100644 --- a/net/neighbor/Make.defs +++ b/net/neighbor/Make.defs @@ -39,7 +39,16 @@ ifeq ($(CONFIG_NET_IPv6),y) NET_CSRCS += neighbor_initialize.c neighbor_add.c neighbor_lookup.c NET_CSRCS += neighbor_update.c neighbor_periodic.c neighbor_findentry.c -NET_CSRCS += neighbor_out.c + +# Link layer specific support + +ifeq ($(CONFIG_NET_ETHERNET),y) +NET_CSRCS += neighbor_ethernet_out.c +endif + +ifeq ($(CONFIG_NET_6LOWPAN),y) +# NET_CSRCS += neighbor_6lowpan_out.c +endif # Include utility build support diff --git a/net/neighbor/neighbor_out.c b/net/neighbor/neighbor_ethernet_out.c similarity index 98% rename from net/neighbor/neighbor_out.c rename to net/neighbor/neighbor_ethernet_out.c index 4725a90ea27..ddd9a3a5ffd 100644 --- a/net/neighbor/neighbor_out.c +++ b/net/neighbor/neighbor_ethernet_out.c @@ -1,7 +1,7 @@ /**************************************************************************** - * net/neighbor/neighbor_out.c + * net/neighbor/neighbor_ethernet_out.c * - * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without diff --git a/net/net_initialize.c b/net/net_initialize.c index 988faf6b547..3edbd6849b2 100644 --- a/net/net_initialize.c +++ b/net/net_initialize.c @@ -57,6 +57,7 @@ #include "local/local.h" #include "igmp/igmp.h" #include "route/route.h" +#include "usrsock/usrsock.h" #include "utils/utils.h" /**************************************************************************** @@ -130,7 +131,7 @@ void net_setup(void) local_initialize(); #endif -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK /* Initialize the listening port structures */ tcp_listen_initialize(); @@ -146,7 +147,7 @@ void net_setup(void) #endif #endif /* CONFIG_NET_TCP */ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK /* Initialize the UDP connection structures */ udp_initialize(); @@ -163,6 +164,12 @@ void net_setup(void) net_initroute(); #endif + +#ifdef CONFIG_NET_USRSOCK + /* Initialize the user-space socket API */ + + usrsock_initialize(); +#endif } /**************************************************************************** diff --git a/net/procfs/netdev_statistics.c b/net/procfs/netdev_statistics.c index 80154c91faa..d7673507b73 100644 --- a/net/procfs/netdev_statistics.c +++ b/net/procfs/netdev_statistics.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/procfs/netdev_statistics.c * - * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -47,6 +47,7 @@ #include #include +#include #include "netdev/netdev.h" #include "utils/utils.h" @@ -106,6 +107,9 @@ static const linegen_t g_linegen[] = static int netprocfs_linklayer(FAR struct netprocfs_file_s *netfile) { FAR struct net_driver_s *dev; +#ifdef CONFIG_NET_6LOWPAN + FAR struct ieee802154_driver_s *ieee; +#endif FAR const char *status; int len = 0; @@ -142,6 +146,30 @@ static int netprocfs_linklayer(FAR struct netprocfs_file_s *netfile) break; #endif +#ifdef CONFIG_NET_6LOWPAN + case NET_LL_IEEE802154: + { + ieee = (FAR struct ieee802154_driver_s *)dev; + +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:6loWPAN HWaddr %02x:%02x", + dev->d_ifname, + ieee->i_nodeaddr.u8[0], ieee->i_nodeaddr.u8[1]); +#else /* CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 8 */ + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:6loWPAN HWaddr " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + dev->d_ifname, + ieee->i_nodeaddr.u8[0], ieee->i_nodeaddr.u8[1], + ieee->i_nodeaddr.u8[2], ieee->i_nodeaddr.u8[3], + ieee->i_nodeaddr.u8[4], ieee->i_nodeaddr.u8[5], + ieee->i_nodeaddr.u8[6], ieee->i_nodeaddr.u8[7]); +#endif + } + break; +#endif + #ifdef CONFIG_NET_LOOPBACK case NET_LL_LOOPBACK: len += snprintf(&netfile->line[len], NET_LINELEN - len, @@ -184,6 +212,26 @@ static int netprocfs_linklayer(FAR struct netprocfs_file_s *netfile) "%s\tLink encap:Ethernet HWaddr %s at %s\n", dev->d_ifname, ether_ntoa(&dev->d_mac), status); +#elif defined(CONFIG_NET_6LOWPAN) + ieee = (FAR struct ieee802154_driver_s *)dev; + +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:6loWPAN HWaddr %02x:%02x at %s", + dev->d_ifname, + ieee->i_nodeaddr.u8[0], ieee->i_nodeaddr.u8[1], + status); +#else /* CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 8 */ + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:6loWPAN HWaddr " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x at %s", + dev->d_ifname, + ieee->i_nodeaddr.u8[0], ieee->i_nodeaddr.u8[1], + ieee->i_nodeaddr.u8[2], ieee->i_nodeaddr.u8[3], + ieee->i_nodeaddr.u8[4], ieee->i_nodeaddr.u8[5], + ieee->i_nodeaddr.u8[6], ieee->i_nodeaddr.u8[7], + status); +#endif #elif defined(CONFIG_NET_LOOPBACK) len += snprintf(&netfile->line[len], NET_LINELEN - len, "%s\tLink encap:Local Loopback at %s\n", diff --git a/net/sixlowpan/Kconfig b/net/sixlowpan/Kconfig index 406a5e8d34d..02d17a42f1c 100644 --- a/net/sixlowpan/Kconfig +++ b/net/sixlowpan/Kconfig @@ -3,15 +3,8 @@ # see the file kconfig-language.txt in the NuttX tools repository. # -menuconfig NET_6LOWPAN - bool "IEEE 802.15.4 6LoWPAN support" - default n - depends on EXPERIMENTAL && NET_IPv6 - ---help--- - Enable support for IEEE 802.15.4 Low power Wireless Personal Area - Networking (6LoWPAN). - -if NET_6LOWPAN +menu "6LoWPAN Configuration" + depends on NET_6LOWPAN config NET_6LOWPAN_FRAG bool "6loWPAN Fragmentation" @@ -59,6 +52,17 @@ config NET_6LOWPAN_COMPRESSION_THRESHOLD CONFIG_NET_6LOWPAN_COMPRESSION_THRESHOLD sets a lower threshold for when packets should not be compressed. +config NET_6LOWPAN_MINPORT + hex "Minimum port nubmer" + default 0xf0b0 + depends on NET_6LOWPAN_COMPRESSION_HC1 + ---help--- + HC1 compression of UDP headersis feasible only if both src and dest + ports are between CONFIG_NET_6LOWPAN_MINPORT and + CONFIG_NET_6LOWPAN_MINPORT + 15, inclusive. + + All nodes must agree on the value of CONFIG_NET_6LOWPAN_MINPORT + if NET_6LOWPAN_COMPRESSION_HC06 config NET_6LOWPAN_MAXADDRCONTEXT @@ -140,7 +144,8 @@ config NET_6LOWPAN_MAXAGE int "Packet reassembly timeout" default 20 ---help--- - Timeout for packet reassembly at the 6lowpan layer (should be < 60s) + Timeout for packet reassembly at the 6lowpan layer in units of + seconds (should be < 60s) config NET_6LOWPAN_MAX_MACTRANSMITS int "Max MAC transmissions" @@ -186,10 +191,13 @@ config NET_6LOWPAN_TCP_RECVWNDO the application is slow to process incoming data, or high (32768 bytes) if the application processes data quickly. -config NET_6LOWPAN_SNIFFER +config NET_6LOWPAN_DUMPBUFFER + bool "Enable dumping of buffer data" default n + depends on DEBUG_NET_INFO ---help--- - Enable use use an architecture-specific sniffer to support tracing - of IP. + Enable dumping of all packet and frame buffers coming into and out + of the 6loWPAN logic. This will generate a large volume of data if + selected. -endif # NET_6LOWPAN +endmenu # 6LoWPAN Configuration diff --git a/net/sixlowpan/Make.defs b/net/sixlowpan/Make.defs index e0370185e68..d1faae0eacc 100644 --- a/net/sixlowpan/Make.defs +++ b/net/sixlowpan/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # net/sixlowpan/Make.defs # -# Copyright (C) 2016 Gregory Nutt. All rights reserved. +# Copyright (C) 2017 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -41,7 +41,7 @@ ifeq ($(CONFIG_NET_6LOWPAN),y) NET_CSRCS += sixlowpan_initialize.c sixlowpan_globals.c sixlowpan_utils.c NET_CSRCS += sixlowpan_input.c sixlowpan_send.c sixlowpan_framer.c -NET_CSRCS += sixlowpan_compressor.c +NET_CSRCS += sixlowpan_framelist.c ifeq ($(CONFIG_NET_TCP),y) NET_CSRCS += sixlowpan_tcpsend.c @@ -59,10 +59,6 @@ ifeq ($(CONFIG_NET_6LOWPAN_COMPRESSION_HC06),y) NET_CSRCS += sixlowpan_hc06.c endif -ifeq ($(CONFIG_NET_6LOWPAN_SNIFFER),y) -NET_CSRCS += sixlowpan_sniffer.c -endif - # Include the sixlowpan directory in the build DEPPATH += --dep-path sixlowpan diff --git a/net/sixlowpan/sixlowpan.h b/net/sixlowpan/sixlowpan.h index d3660cb2ceb..05ce9496c9c 100644 --- a/net/sixlowpan/sixlowpan.h +++ b/net/sixlowpan/sixlowpan.h @@ -53,7 +53,9 @@ * Public Function Prototypes ****************************************************************************/ -struct socket; /* Forward reference */ +struct net_driver_s; /* Forward reference */ +struct socket; /* Forward reference */ +struct sockaddr; /* Forward reference */ /**************************************************************************** * Name: sixlowpan_initialize @@ -104,6 +106,38 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf, size_t len); #endif +/**************************************************************************** + * Function: sixlowpan_tcp_send + * + * Description: + * TCP output comes through three different mechansims. Either from: + * + * 1. TCP socket output. For the case of TCP output to an + * IEEE802.15.4, the TCP output is caught in the socket + * send()/sendto() logic and and redirected to psock_6lowpan_tcp_send(). + * 2. TCP output from the TCP state machine. That will occur + * during TCP packet processing by the TCP state meachine. + * 3. TCP output resulting from TX or timer polling + * + * Cases 2 and 3 will be handled here. Logic in ipv6_tcp_input(), + * devif_poll(), and devif_timer() detect if (1) an attempt to return with + * d_len > 0 and (2) that the device is an IEEE802.15.4 MAC network + * driver. Under those conditions, this function will be called to create + * the IEEE80215.4 frames. + * + * Parameters: + * dev - An instance of nework device state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called with the network locked. + * + ****************************************************************************/ + +void sixlowpan_tcp_send(FAR struct net_driver_s *dev); + /**************************************************************************** * Function: psock_6lowpan_udp_send * @@ -132,5 +166,39 @@ ssize_t psock_6lowpan_udp_send(FAR struct socket *psock, FAR const void *buf, size_t len); #endif +/**************************************************************************** + * Function: psock_6lowpan_udp_sendto + * + * Description: + * If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) + * socket, the parameters to and 'tolen' are ignored (and the error EISCONN + * may be returned when they are not NULL and 0), and the error ENOTCONN is + * returned when the socket was not actually connected. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters sent. On error, + * -1 is returned, and errno is set appropriately. Returned error + * number must be consistent with definition of errors reported by + * sendto(). + * + * Assumptions: + * Called with the network locked. + * + ****************************************************************************/ + +ssize_t psock_6lowpan_udp_sendto(FAR struct socket *psock, + FAR const void *buf, + size_t len, int flags, + FAR const struct sockaddr *to, + socklen_t tolen); + #endif /* CONFIG_NET_6LOWPAN */ #endif /* _NET_SIXLOWPAN_SIXLOWPAN_H */ diff --git a/net/sixlowpan/sixlowpan_framelist.c b/net/sixlowpan/sixlowpan_framelist.c new file mode 100644 index 00000000000..bd4dd2e8eb6 --- /dev/null +++ b/net/sixlowpan/sixlowpan_framelist.c @@ -0,0 +1,477 @@ +/**************************************************************************** + * net/sixlowpan/sixlowpan_framelist.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Parts of this file derive from Contiki: + * + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * Authors: Adam Dunkels + * Nicolas Tsiftes + * Niclas Finne + * Mathilde Durvy + * Julien Abeille + * Joakim Eriksson + * Joel Hoglund + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "sixlowpan/sixlowpan_internal.h" + +#ifdef CONFIG_NET_6LOWPAN + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* A single IOB must be big enough to hold a full frame */ + +#if CONFIG_IOB_BUFSIZE < CONFIG_NET_6LOWPAN_FRAMELEN +# error IOBs must be large enough to hold full IEEE802.14.5 frame +#endif + +/* There must be at least enough IOBs to hold the full MTU. Probably still + * won't work unless there are a few more. + */ + +#if CONFIG_NET_6LOWPAN_MTU > (CONFIG_IOB_BUFSIZE * CONFIG_IOB_NBUFFERS) +# error Not enough IOBs to hold one full IEEE802.14.5 packet +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sixlowpan_compress_ipv6hdr + * + * Description: + * IPv6 dispatch "compression" function. Packets "Compression" when only + * IPv6 dispatch is used + * + * There is no compression in this case, all fields are sent + * inline. We just add the IPv6 dispatch byte before the packet. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IPv6 Dsp | IPv6 header and payload ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Input Parameters: + * ieee - Pointer to IEEE802.15.4 MAC driver structure. + * destip - Pointer to the IPv6 header to "compress" + * fptr - Pointer to the beginning of the frame under construction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sixlowpan_compress_ipv6hdr(FAR struct ieee802154_driver_s *ieee, + FAR const struct ipv6_hdr_s *destip, + FAR uint8_t *fptr) +{ + /* Indicate the IPv6 dispatch and length */ + + fptr[g_frame_hdrlen] = SIXLOWPAN_DISPATCH_IPV6; + g_frame_hdrlen += SIXLOWPAN_IPV6_HDR_LEN; + + /* Copy the IPv6 header and adjust pointers */ + + memcpy(&fptr[g_frame_hdrlen] , destip, IPv6_HDRLEN); + g_frame_hdrlen += IPv6_HDRLEN; + g_uncomp_hdrlen += IPv6_HDRLEN; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sixlowpan_queue_frames + * + * Description: + * Process an outgoing UDP or TCP packet. This function is called from + * send interrupt logic when a TX poll is received. It formats the + * list of frames to be sent by the IEEE802.15.4 MAC driver. + * + * The payload data is in the caller 'buf' and is of length 'buflen'. + * Compressed headers will be added and if necessary the packet is + * fragmented. The resulting packet/fragments are put in ieee->i_framelist + * and the entire list of frames will be delivered to the 802.15.4 MAC via + * ieee->i_framelist. + * + * Input Parameters: + * ieee - The IEEE802.15.4 MAC driver instance + * ipv6hdr - IPv6 header followed by TCP or UDP header. + * buf - Beginning of the packet packet to send (with IPv6 + protocol + * headers) + * buflen - Length of data to send (include IPv6 and protocol headers) + * destmac - The IEEE802.15.4 MAC address of the destination + * + * Returned Value: + * Ok is returned on success; Othewise a negated errno value is returned. + * This function is expected to fail if the driver is not an IEEE802.15.4 + * MAC network driver. In that case, the UDP/TCP will fall back to normal + * IPv4/IPv6 formatting. + * + * Assumptions: + * Called with the network locked. + * + ****************************************************************************/ + +int sixlowpan_queue_frames(FAR struct ieee802154_driver_s *ieee, + FAR const struct ipv6_hdr_s *destip, + FAR const void *buf, size_t buflen, + FAR const struct rimeaddr_s *destmac) +{ + FAR struct iob_s *iob; + FAR uint8_t *fptr; + int framer_hdrlen; + struct rimeaddr_s bcastmac; +#ifdef CONFIG_NET_6LOWPAN_FRAG + uint16_t outlen = 0; +#endif + + /* Initialize global data. Locking the network guarantees that we have + * exclusive use of the global values for intermediate calculations. + */ + + g_uncomp_hdrlen = 0; + g_frame_hdrlen = 0; + + /* Reset rime buffer, packet buffer metatadata */ + + memset(g_pktattrs, 0, PACKETBUF_NUM_ATTRS * sizeof(uint16_t)); + memset(g_pktaddrs, 0, PACKETBUF_NUM_ADDRS * sizeof(struct rimeaddr_s)); + + g_pktattrs[PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS] = + CONFIG_NET_6LOWPAN_MAX_MACTRANSMITS; + + /* Set stream mode for all TCP packets, except FIN packets. */ + + if (destip->proto == IP_PROTO_TCP) + { + FAR const struct tcp_hdr_s *tcp = + &((FAR const struct ipv6tcp_hdr_s *)destip)->tcp; + + if ((tcp->flags & TCP_FIN) == 0 && + (tcp->flags & TCP_CTL) != TCP_ACK) + { + g_pktattrs[PACKETBUF_ATTR_PACKET_TYPE] = PACKETBUF_ATTR_PACKET_TYPE_STREAM; + } + else if ((tcp->flags & TCP_FIN) == TCP_FIN) + { + g_pktattrs[PACKETBUF_ATTR_PACKET_TYPE] = PACKETBUF_ATTR_PACKET_TYPE_STREAM_END; + } + } + + /* The destination address will be tagged to each outbound packet. If the + * argument destmac is NULL, we are sending a broadcast packet. + */ + + if (destmac == NULL) + { + memset(&bcastmac, 0, sizeof(struct rimeaddr_s)); + destmac = &bcastmac; + } + + /* Pre-allocate the IOB to hold frame or the first fragment, waiting if + * necessary. + */ + + iob = iob_alloc(false); + DEBUGASSERT(iob != NULL); + + /* Initialize the IOB */ + + iob->io_flink = NULL; + iob->io_len = 0; + iob->io_offset = 0; + iob->io_pktlen = 0; + fptr = iob->io_data; + + ninfo("Sending packet length %d\n", buflen); + + /* Pre-calculate frame header length. */ + + framer_hdrlen = sixlowpan_send_hdrlen(ieee, ieee->i_panid); + if (framer_hdrlen < 0) + { + /* Failed to determine the size of the header failed. */ + + nerr("ERROR: sixlowpan_send_hdrlen() failed: %d\n", framer_hdrlen); + return framer_hdrlen; + } + + g_frame_hdrlen = framer_hdrlen; + +#ifndef CONFIG_NET_6LOWPAN_COMPRESSION_IPv6 + if (buflen >= CONFIG_NET_6LOWPAN_COMPRESSION_THRESHOLD) + { + /* Try to compress the headers */ + +#if defined(CONFIG_NET_6LOWPAN_COMPRESSION_HC1) + sixlowpan_compresshdr_hc1(ieee, destip, destmac, fptr); +#elif defined(CONFIG_NET_6LOWPAN_COMPRESSION_HC06) + sixlowpan_compresshdr_hc06(ieee, destip, destmac, fptr); +#else +# error No compression specified +#endif + } + else +#endif /* !CONFIG_NET_6LOWPAN_COMPRESSION_IPv6 */ + { + /* Small.. use IPv6 dispatch (no compression) */ + + sixlowpan_compress_ipv6hdr(ieee, destip, fptr); + } + + ninfo("Header of length %d\n", g_frame_hdrlen); + + rimeaddr_copy(&g_pktaddrs[PACKETBUF_ADDR_RECEIVER], destmac); + + /* Check if we need to fragment the packet into several frames */ + + if ((int)buflen - (int)g_uncomp_hdrlen > + (int)CONFIG_NET_6LOWPAN_MAXPAYLOAD - framer_hdrlen - + (int)g_frame_hdrlen) + { +#ifdef CONFIG_NET_6LOWPAN_FRAG + /* ieee->i_framelist will hold the generated frames; frames will be + * added at qtail. + */ + + FAR struct iob_s *qtail; + int verify; + + /* The outbound IPv6 packet is too large to fit into a single 15.4 + * packet, so we fragment it into multiple packets and send them. + * The first fragment contains frag1 dispatch, then + * IPv6/HC1/HC06/HC_UDP dispatchs/headers. + * The following fragments contain only the fragn dispatch. + */ + + ninfo("Fragmentation sending packet length %d\n", buflen); + + /* Create 1st Fragment */ + /* Add the frame header using the pre-allocated IOB. */ + + verify = sixlowpan_framecreate(ieee, iob, ieee->i_panid); + DEBUGASSERT(verify == framer_hdrlen); + UNUSED(verify); + + /* Move HC1/HC06/IPv6 header */ + + memmove(fptr + SIXLOWPAN_FRAG1_HDR_LEN, fptr, g_frame_hdrlen); + + /* Setup up the fragment header. + * + * The fragment header contains three fields: Datagram size, datagram + * tag and datagram offset: + * + * 1. Datagram size describes the total (un-fragmented) payload. + * 2. Datagram tag identifies the set of fragments and is used to + * match fragments of the same payload. + * 3. Datagram offset identifies the fragment’s offset within the un- + * fragmented payload. + * + * The fragment header length is 4 bytes for the first header and 5 + * bytes for all subsequent headers. + */ + + PUTINT16(fptr, RIME_FRAG_DISPATCH_SIZE, + ((SIXLOWPAN_DISPATCH_FRAG1 << 8) | buflen)); + PUTINT16(fptr, RIME_FRAG_TAG, ieee->i_dgramtag); + ieee->i_dgramtag++; + + /* Copy payload and enqueue */ + + g_frame_hdrlen += SIXLOWPAN_FRAG1_HDR_LEN; + g_rime_payloadlen = + (CONFIG_NET_6LOWPAN_MAXPAYLOAD - framer_hdrlen - g_frame_hdrlen) & 0xf8; + + memcpy(fptr + g_frame_hdrlen, + (FAR uint8_t *)destip + g_uncomp_hdrlen, g_rime_payloadlen); + iob->io_len += g_rime_payloadlen + g_frame_hdrlen; + + /* Set outlen to what we already sent from the IP payload */ + + outlen = g_rime_payloadlen + g_uncomp_hdrlen; + + ninfo("First fragment: length %d, tag %d\n", + g_rime_payloadlen, ieee->i_dgramtag); + sixlowpan_dumpbuffer("Outgoing frame", + (FAR const uint8_t *)iob->io_data, iob->io_len); + + /* Add the first frame to the IOB queue */ + + ieee->i_framelist = iob; + qtail = iob; + + /* Keep track of the total amount of data queue */ + + iob->io_pktlen = iob->io_len; + + /* Create following fragments */ + + g_frame_hdrlen = SIXLOWPAN_FRAGN_HDR_LEN; + + while (outlen < buflen) + { + /* Allocate an IOB to hold the next fragment, waiting if + * necessary. + */ + + iob = iob_alloc(false); + DEBUGASSERT(iob != NULL); + + /* Initialize the IOB */ + + iob->io_flink = NULL; + iob->io_len = 0; + iob->io_offset = 0; + iob->io_pktlen = 0; + fptr = iob->io_data; + + /* Add the frame header */ + + verify = sixlowpan_framecreate(ieee, iob, ieee->i_panid); + DEBUGASSERT(verify == framer_hdrlen); + UNUSED(verify); + + /* Move HC1/HC06/IPv6 header */ + + memmove(fptr + SIXLOWPAN_FRAGN_HDR_LEN, fptr, g_frame_hdrlen); + + /* Setup up the fragment header */ + + PUTINT16(fptr, RIME_FRAG_DISPATCH_SIZE, + ((SIXLOWPAN_DISPATCH_FRAGN << 8) | buflen)); + PUTINT16(fptr, RIME_FRAG_TAG, ieee->i_dgramtag); + fptr[RIME_FRAG_OFFSET] = outlen >> 3; + + /* Copy payload and enqueue */ + + if (buflen - outlen < g_rime_payloadlen) + { + /* Last fragment */ + + g_rime_payloadlen = buflen - outlen; + } + else + { + g_rime_payloadlen = + (CONFIG_NET_6LOWPAN_MAXPAYLOAD - framer_hdrlen - g_frame_hdrlen) & 0xf8; + } + + memcpy(fptr + g_frame_hdrlen, (FAR uint8_t *)destip + outlen, + g_rime_payloadlen); + iob->io_len = g_rime_payloadlen + g_frame_hdrlen; + + /* Set outlen to what we already sent from the IP payload */ + + outlen += (g_rime_payloadlen + g_uncomp_hdrlen); + + ninfo("sixlowpan output: fragment offset %d, length %d, tag %d\n", + outlen >> 3, g_rime_payloadlen, ieee->i_dgramtag); + sixlowpan_dumpbuffer("Outgoing frame", + (FAR const uint8_t *)iob->io_data, + iob->io_len); + + /* Add the next frame to the tail of the IOB queue */ + + qtail->io_flink = iob; + + /* Keep track of the total amount of data queue */ + + ieee->i_framelist->io_pktlen += iob->io_len; + } +#else + nerr("ERROR: Packet too large: %d\n", buflen); + nerr(" Cannot to be sent without fragmentation support\n"); + nerr(" dropping packet\n"); + + return -E2BIG; +#endif + } + else + { + int verify; + + /* The packet does not need to be fragmented just copy the "payload" + * and send in one frame. + */ + + /* Add the frame header to the preallocated IOB. */ + + verify = sixlowpan_framecreate(ieee, iob, ieee->i_panid); + DEBUGASSERT(verify == framer_hdrlen); + UNUSED(verify); + + /* Copy the payload and queue */ + + memcpy(fptr + g_frame_hdrlen, (FAR uint8_t *)destip + g_uncomp_hdrlen, + buflen - g_uncomp_hdrlen); + iob->io_len = buflen - g_uncomp_hdrlen + g_frame_hdrlen; + + ninfo("Non-fragmented: length %d\n", iob->io_len); + sixlowpan_dumpbuffer("Outgoing frame", + (FAR const uint8_t *)iob->io_data, iob->io_len); + + /* Add the first frame to the IOB queue */ + + ieee->i_framelist = iob; + + /* Keep track of the total amount of data queue */ + + iob->io_pktlen = iob->io_len; + } + + return OK; +} + +#endif /* CONFIG_NET_6LOWPAN */ diff --git a/net/sixlowpan/sixlowpan_framer.c b/net/sixlowpan/sixlowpan_framer.c index 2b528dea10d..b0953db648c 100644 --- a/net/sixlowpan/sixlowpan_framer.c +++ b/net/sixlowpan/sixlowpan_framer.c @@ -55,7 +55,6 @@ #include #include "nuttx/net/net.h" -#include "nuttx/net/sixlowpan.h" #include "sixlowpan/sixlowpan_internal.h" @@ -277,9 +276,10 @@ static int sixlowpan_802154_hdrlen(FAR struct frame802154_s *finfo) * * Input parameters: * ieee - A reference IEEE802.15.4 MAC network device structure. - * params - Where to put the parmeters + * iob - The IOB in which to create the frame. * dest_panid - PAN ID of the destination. May be 0xffff if the destination * is not associated. + * params - Where to put the parmeters * * Returned Value: * None. @@ -287,33 +287,28 @@ static int sixlowpan_802154_hdrlen(FAR struct frame802154_s *finfo) ****************************************************************************/ static void sixlowpan_setup_params(FAR struct ieee802154_driver_s *ieee, - FAR struct frame802154_s *params, - uint16_t dest_panid) + uint16_t dest_panid, + FAR struct frame802154_s *params) { bool rcvrnull; /* Initialize all prameters to all zero */ - memset(¶ms, 0, sizeof(params)); - - /* Reset to an empty frame */ - - ieee->i_framelen = 0; - ieee->i_dataoffset = 0; + memset(params, 0, sizeof(params)); /* Build the FCF (Only non-zero elements need to be initialized). */ params->fcf.frame_type = FRAME802154_DATAFRAME; - params->fcf.frame_pending = ieee->i_pktattrs[PACKETBUF_ATTR_PENDING]; + params->fcf.frame_pending = g_pktattrs[PACKETBUF_ATTR_PENDING]; /* If the output address is NULL in the Rime buf, then it is broadcast * on the 802.15.4 network. */ - rcvrnull = sixlowpan_addrnull(ieee->i_pktaddrs[PACKETBUF_ADDR_RECEIVER].u8); + rcvrnull = sixlowpan_addrnull(g_pktaddrs[PACKETBUF_ADDR_RECEIVER].u8); if (rcvrnull) { - params->fcf.ack_required = ieee->i_pktattrs[PACKETBUF_ATTR_MAC_ACK]; + params->fcf.ack_required = g_pktattrs[PACKETBUF_ATTR_MAC_ACK]; } /* Insert IEEE 802.15.4 (2003) version bit. */ @@ -322,14 +317,14 @@ static void sixlowpan_setup_params(FAR struct ieee802154_driver_s *ieee, /* Increment and set the data sequence number. */ - if (ieee->i_pktattrs[PACKETBUF_ATTR_MAC_SEQNO] != 0) + if (g_pktattrs[PACKETBUF_ATTR_MAC_SEQNO] != 0) { - params->seq = ieee->i_pktattrs[PACKETBUF_ATTR_MAC_SEQNO]; + params->seq = g_pktattrs[PACKETBUF_ATTR_MAC_SEQNO]; } else { params->seq = ieee->i_dsn++; - ieee->i_pktattrs[PACKETBUF_ATTR_MAC_SEQNO] = params->seq; + g_pktattrs[PACKETBUF_ATTR_MAC_SEQNO] = params->seq; } /* Complete the addressing fields. */ @@ -355,7 +350,7 @@ static void sixlowpan_setup_params(FAR struct ieee802154_driver_s *ieee, /* Copy the destination address */ rimeaddr_copy((struct rimeaddr_s *)¶ms->dest_addr, - ieee->i_pktaddrs[PACKETBUF_ADDR_RECEIVER].u8); + g_pktaddrs[PACKETBUF_ADDR_RECEIVER].u8); /* Use short address mode if so configured */ @@ -369,11 +364,6 @@ static void sixlowpan_setup_params(FAR struct ieee802154_driver_s *ieee, /* Set the source address to the node address assigned to the device */ rimeaddr_copy((struct rimeaddr_s *)¶ms->src_addr, &ieee->i_nodeaddr.u8); - - /* Configure the payload address and length */ - - params->payload = FRAME_DATA_START(ieee); - params->payload_len = FRAME_DATA_SIZE(ieee); } /**************************************************************************** @@ -381,7 +371,7 @@ static void sixlowpan_setup_params(FAR struct ieee802154_driver_s *ieee, ****************************************************************************/ /**************************************************************************** - * Function: sixlowpan_hdrlen + * Function: sixlowpan_send_hdrlen * * Description: * This function is before the first frame has been sent in order to @@ -399,14 +389,14 @@ static void sixlowpan_setup_params(FAR struct ieee802154_driver_s *ieee, * ****************************************************************************/ -int sixlowpan_hdrlen(FAR struct ieee802154_driver_s *ieee, - uint16_t dest_panid) +int sixlowpan_send_hdrlen(FAR struct ieee802154_driver_s *ieee, + uint16_t dest_panid) { struct frame802154_s params; /* Set up the frame parameters */ - sixlowpan_setup_params(ieee, ¶ms, dest_panid); + sixlowpan_setup_params(ieee, dest_panid, ¶ms); /* Return the length of the header */ @@ -425,7 +415,7 @@ int sixlowpan_hdrlen(FAR struct ieee802154_driver_s *ieee, * the frame to send. * buf - Pointer to the buffer to use for the frame. * buflen - The length of the buffer to use for the frame. - * finfo - I that specifies the frame to send. + * finfo - Specifies the frame to send. * * Returned Value: * The length of the frame header or 0 if there was insufficient space in @@ -451,18 +441,18 @@ int sixlowpan_802154_framecreate(FAR struct frame802154_s *finfo, return 0; } - /* OK, now we have field lengths. Time to actually construct - * the outgoing frame, and store it in the provided buffer + /* OK, now we have field lengths. Time to actually construct the outgoing + * frame, and store it in the provided buffer */ - buf[0] = (finfo->fcf.frame_type & 7) | - ((finfo->fcf.security_enabled & 1) << 3) | - ((finfo->fcf.frame_pending & 1) << 4) | - ((finfo->fcf.ack_required & 1) << 5) | - ((finfo->fcf.panid_compression & 1) << 6); - buf[1] = ((finfo->fcf.dest_addr_mode & 3) << 2) | - ((finfo->fcf.frame_version & 3) << 4) | - ((finfo->fcf.src_addr_mode & 3) << 6); + buf[0] = ((finfo->fcf.frame_type & 7) << FRAME802154_FRAMETYPE_SHIFT) | + ((finfo->fcf.security_enabled & 1) << FRAME802154_SECENABLED_SHIFT) | + ((finfo->fcf.frame_pending & 1) << FRAME802154_FRAMEPENDING_SHIFT) | + ((finfo->fcf.ack_required & 1) << FRAME802154_ACKREQUEST_SHIFT) | + ((finfo->fcf.panid_compression & 1) << FRAME802154_PANIDCOMP_SHIFT); + buf[1] = ((finfo->fcf.dest_addr_mode & 3) << FRAME802154_DSTADDR_SHIFT) | + ((finfo->fcf.frame_version & 3) << FRAME802154_VERSION_SHIFT) | + ((finfo->fcf.src_addr_mode & 3) << FRAME802154_SRCADDR_SHIFT); /* Sequence number */ @@ -481,7 +471,7 @@ int sixlowpan_802154_framecreate(FAR struct frame802154_s *finfo, for (i = flen.dest_addr_len; i > 0; i--) { - buf[pos++] = finfo->dest_addr[i - 1]; + buf[pos++] = finfo->dest_addr[i - 1]; } /* Source PAN ID */ @@ -516,11 +506,14 @@ int sixlowpan_802154_framecreate(FAR struct frame802154_s *finfo, * Function: sixlowpan_framecreate * * Description: - * This function is called after the IEEE802.15.4 MAC driver polls for - * TX data. It creates the IEEE802.15.4 header in the frame buffer. + * This function is called after eiether (1) the IEEE802.15.4 MAC driver + * polls for TX data or (2) after the IEEE802.15.4 MAC driver provides an + * in frame and the network responds with an outgoing packet. It creates + * the IEEE802.15.4 header in the frame buffer. * * Input parameters: * ieee - A reference IEEE802.15.4 MAC network device structure. + * iob - The IOB in which to create the frame. * dest_panid - PAN ID of the destination. May be 0xffff if the destination * is not associated. * @@ -531,36 +524,25 @@ int sixlowpan_802154_framecreate(FAR struct frame802154_s *finfo, ****************************************************************************/ int sixlowpan_framecreate(FAR struct ieee802154_driver_s *ieee, - uint16_t dest_panid) + FAR struct iob_s *iob, uint16_t dest_panid) { struct frame802154_s params; - int len; - int ret; + int hdrlen; /* Set up the frame parameters */ - sixlowpan_setup_params(ieee, ¶ms, dest_panid); + sixlowpan_setup_params(ieee, dest_panid, ¶ms); /* Get the length of the header */ - len = sixlowpan_802154_hdrlen(¶ms); - - /* Allocate space for the header in the frame buffer */ - - ret = sixlowpan_frame_hdralloc(ieee, len); - if (ret < 0) - { - wlerr("ERROR: Header too large: %u\n", len); - return ret; - } + hdrlen = sixlowpan_802154_hdrlen(¶ms); /* Then create the frame */ - sixlowpan_802154_framecreate(¶ms, FRAME_HDR_START(ieee), len); + sixlowpan_802154_framecreate(¶ms, iob->io_data, hdrlen); - wlinfo("Frame type: %02x Data len: %d %u (%u)\n", - params.fcf.frame_type, len, FRAME_DATA_SIZE(ieee), - FRAME_SIZE(ieee)); + wlinfo("Frame type: %02x hdrlen: %d\n", + params.fcf.frame_type, hdrlen); #if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 wlinfo("Dest address: %02x:%02x\n", params.dest_addr[0], params.dest_addr[1]); @@ -571,7 +553,7 @@ int sixlowpan_framecreate(FAR struct ieee802154_driver_s *ieee, params.dest_addr[6], params.dest_addr[7]); #endif - return len; + return hdrlen; } #endif /* CONFIG_NET_6LOWPAN */ diff --git a/net/sixlowpan/sixlowpan_globals.c b/net/sixlowpan/sixlowpan_globals.c index 4be92354dd0..8f48ade9f2f 100644 --- a/net/sixlowpan/sixlowpan_globals.c +++ b/net/sixlowpan/sixlowpan_globals.c @@ -39,8 +39,6 @@ #include -#include "nuttx/net/sixlowpan.h" - #include "sixlowpan/sixlowpan_internal.h" #ifdef CONFIG_NET_6LOWPAN @@ -49,14 +47,38 @@ * Public Data ****************************************************************************/ -/* A pointer to the optional, architecture-specific compressor */ +/* The following data values are used to hold intermediate settings while + * processing IEEE802.15.4 frames. These globals are shared with incoming + * and outgoing frame processing and possibly with mutliple IEEE802.15.4 MAC + * devices. The network lock provides exclusive use of these globals + * during that processing + */ -FAR struct sixlowpan_nhcompressor_s *g_sixlowpan_compressor; +/* The length of the payload in the Rime buffer. + * + * The payload is what comes after the compressed or uncompressed headers + * (can be the IP payload if the IP header only is compressed or the UDP + * payload if the UDP header is also compressed) + */ -#ifdef CONFIG_NET_6LOWPAN_SNIFFER -/* A pointer to the optional, architecture-specific sniffer */ +uint8_t g_rime_payloadlen; -FAR struct sixlowpan_rime_sniffer_s *g_sixlowpan_sniffer; -#endif +/* g_uncomp_hdrlen is the length of the headers before compression (if HC2 + * is used this includes the UDP header in addition to the IP header). + */ + +uint8_t g_uncomp_hdrlen; + +/* g_frame_hdrlen is the total length of (the processed) 6lowpan headers + * (fragment headers, IPV6 or HC1, HC2, and HC1 and HC2 non compressed + * fields). + */ + +uint8_t g_frame_hdrlen; + +/* Packet buffer metadata: Attributes and addresses */ + +uint16_t g_pktattrs[PACKETBUF_NUM_ATTRS]; +struct rimeaddr_s g_pktaddrs[PACKETBUF_NUM_ADDRS]; #endif /* CONFIG_NET_6LOWPAN */ diff --git a/net/sixlowpan/sixlowpan_hc06.c b/net/sixlowpan/sixlowpan_hc06.c index 534cb1adbf5..9e51f88c17b 100644 --- a/net/sixlowpan/sixlowpan_hc06.c +++ b/net/sixlowpan/sixlowpan_hc06.c @@ -18,7 +18,7 @@ * Joel Hoglund * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions + * modification, are permitted provided that the following c/onditions * are met: * * 1. Redistributions of source code must retain the above copyright @@ -58,13 +58,26 @@ #include +#include +#include + #include -#include +#include #include "sixlowpan/sixlowpan_internal.h" #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC06 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IPv6BUF(ieee) \ + ((FAR struct ipv6_hdr_s *)((ieee)->i_dev.d_buf)) +#define UDPIPv6BUF(ieee) \ + ((FAR struct udp_hdr_s *)&((ieee)->i_dev.d_buf[IPv6_HDRLEN])) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -84,7 +97,12 @@ struct sixlowpan_addrcontext_s * Private Data ****************************************************************************/ -/* HC06 specific variables */ +/* HC06 specific variables **************************************************/ +/* Use of global variables simplifies the logic and is safe in the multi- + * device environment because access is serialized via the network lock. + * + * But note that state may NOT be preserved from packet-to-packet. + */ #if CONFIG_NET_6LOWPAN_MAXADDRCONTEXT > 0 /* Addresses contexts for IPHC. */ @@ -93,6 +111,222 @@ static struct sixlowpan_addrcontext_s g_hc06_addrcontexts[CONFIG_NET_6LOWPAN_MAXADDRCONTEXT]; #endif +/* Pointer to the byte where to write next inline field. */ + +static FAR uint8_t *g_hc06ptr; + +/* Constant Data ************************************************************/ +/* Uncompression of linklocal + * + * 0 -> 16 bytes from packet + * 1 -> 2 bytes from prefix - bunch of zeroes and 8 from packet + * 2 -> 2 bytes from prefix - 0000::00ff:fe00:XXXX from packet + * 3 -> 2 bytes from prefix - infer 8 bytes from MAC address + * + * NOTE: => the uncompress function does change 0xf to 0x10 + * NOTE: 0x00 => no-autoconfig => unspecified + */ + +static const uint8_t g_unc_llconf[] = { 0x0f, 0x28, 0x22, 0x20 }; + +/* Uncompression of ctx-based + * + * 0 -> 0 bits from packet [unspecified / reserved] + * 1 -> 8 bytes from prefix - bunch of zeroes and 8 from packet + * 2 -> 8 bytes from prefix - 0000::00ff:fe00:XXXX + 2 from packet + * 3 -> 8 bytes from prefix - infer 8 bytes from MAC address + */ + +static const uint8_t g_unc_ctxconf[] = { 0x00, 0x88, 0x82, 0x80 }; + +/* Uncompression of ctx-based + * + * 0 -> 0 bits from packet + * 1 -> 2 bytes from prefix - bunch of zeroes 5 from packet + * 2 -> 2 bytes from prefix - zeroes + 3 from packet + * 3 -> 2 bytes from prefix - infer 1 bytes from MAC address + */ + +static const uint8_t g_unc_mxconf[] = { 0x0f, 0x25, 0x23, 0x21 }; + +/* Link local prefix */ + +static const uint8_t g_llprefix[] = { 0xfe, 0x80 }; + +/* TTL uncompression values */ + +static const uint8_t g_ttl_values[] = { 0, 1, 64, 255 }; + + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: find_addrcontext_bynumber + * + * Description: + * Find the address context with the given number. + * + ****************************************************************************/ + +static FAR struct sixlowpan_addrcontext_s * + find_addrcontext_bynumber(uint8_t number) +{ + /* Remove code to avoid warnings and save flash if no address context is + * used. + */ + +#if CONFIG_NET_6LOWPAN_MAXADDRCONTEXT > 0 + int i; + + for (i = 0; i < CONFIG_NET_6LOWPAN_MAXADDRCONTEXT; i++) + { + if ((g_hc06_addrcontexts[i].used == 1) && + g_hc06_addrcontexts[i].number == number) + { + return &g_hc06_addrcontexts[i]; + } + } +#endif /* CONFIG_NET_6LOWPAN_MAXADDRCONTEXT > 0 */ + + return NULL; +} + +/**************************************************************************** + * Name: find_addrcontext_bynumber + * + * Description: + * Find the address context corresponding to the prefix ipaddr. + * + ****************************************************************************/ + +static FAR struct sixlowpan_addrcontext_s * + find_addrcontext_byprefix(FAR const net_ipv6addr_t ipaddr) +{ +#if CONFIG_NET_6LOWPAN_MAXADDRCONTEXT > 0 + int i; + + /* Remove code to avoid warnings and save flash if no address context is used */ + + for (i = 0; i < CONFIG_NET_6LOWPAN_MAXADDRCONTEXT; i++) + { + if ((g_hc06_addrcontexts[i].used == 1) && + net_ipv6addr_prefixcmp(&g_hc06_addrcontexts[i].prefix, ipaddr, 64)) + { + return &g_hc06_addrcontexts[i]; + } + } +#endif /* CONFIG_NET_6LOWPAN_MAXADDRCONTEXT > 0 */ + + return NULL; +} + +/**************************************************************************** + * Name: uncompress_addr + * + * Description: + * Uncompress addresses based on a prefix and a postfix with zeroes in + * between. If the postfix is zero in length it will use the link address + * to configure the IP address (autoconf style). + * + * prefpost takes a byte where the first nibble specify prefix count + * and the second postfix count (NOTE: 15/0xf => 16 bytes copy). + * + ****************************************************************************/ + +static uint8_t compress_addr_64(FAR const net_ipv6addr_t ipaddr, + FAR const struct rimeaddr_s *macaddr, + uint8_t bitpos) +{ + ninfo("ipaddr=%p macaddr=%p bitpos=%u g_hc06ptr=%p\n", + ipaddr, macaddr, bitpos, g_hc06ptr); + + if (sixlowpan_ismacbased(ipaddr, macaddr)) + { + return 3 << bitpos; /* 0-bits */ + } + else if (SIXLOWPAN_IS_IID_16BIT_COMPRESSABLE(ipaddr)) + { + /* Compress IID to 16 bits: xxxx:xxxx:xxxx:xxxx:0000:00ff:fe00:XXXX */ + + memcpy(g_hc06ptr, &ipaddr[7], 2); + g_hc06ptr += 2; + return 2 << bitpos; /* 16-bits */ + } + else + { + /* Do not compress IID: xxxx:xxxx:xxxx:xxxx:IID:IID:IID:IID */ + + memcpy(g_hc06ptr, &ipaddr[4], 8); + g_hc06ptr += 8; + return 1 << bitpos; /* 64-bits */ + } +} + +/**************************************************************************** + * Name: uncompress_addr + * + * Description: + * Uncompress addresses based on a prefix and a postfix with zeroes in + * between. If the postfix is zero in length it will use the link address + * to configure the IP address (autoconf style). + * + * prefpost takes a byte where the first nibble specify prefix count + * and the second postfix count (NOTE: 15/0xf => 16 bytes copy). + * + ****************************************************************************/ + +static void uncompress_addr(FAR net_ipv6addr_t ipaddr, uint8_t const prefix[], + uint8_t prefpost, FAR struct rimeaddr_s *macaddr) +{ + uint8_t prefcount = prefpost >> 4; + uint8_t postcount = prefpost & 0x0f; + + /* Full nibble 15 => 16 */ + + prefcount = prefcount == 15 ? 16 : prefcount; + postcount = postcount == 15 ? 16 : postcount; + + if (prefcount > 0) + { + memcpy(ipaddr, prefix, prefcount); + } + + if (prefcount + postcount < 16) + { + FAR uint8_t *iptr = (FAR uint8_t *)&ipaddr[0]; + + memset(&iptr[prefcount], 0, 16 - (prefcount + postcount)); + } + + if (postcount > 0) + { + FAR uint8_t *iptr = (FAR uint8_t *)&ipaddr[0]; + + memcpy(&iptr[16 - postcount], g_hc06ptr, postcount); + if (postcount == 2 && prefcount < 11) + { + /* 16 bits uncompression => 0000:00ff:fe00:XXXX */ + + iptr[11] = 0xff; + iptr[12] = 0xfe; + } + + g_hc06ptr += postcount; + } + else if (prefcount > 0) + { + /* No IID based configuration if no prefix and no data => unspec */ + + sixlowpan_ipfromrime(macaddr, ipaddr); + } + + ninfo("Uncompressing %d + %d => %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + prefcount, postcount, ipaddr[0], ipaddr[2], ipaddr[3], ipaddr[5], + ipaddr[5], ipaddr[6], ipaddr[7]); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -169,14 +403,13 @@ void sixlowpan_hc06_initialize(void) } /**************************************************************************** - * Name: sixlowpan_hc06_initialize + * Name: sixlowpan_compresshdr_hc06 * * Description: * Compress IP/UDP header * * This function is called by the 6lowpan code to create a compressed - * 6lowpan packet in the packetbuf buffer from a full IPv6 packet in the - * uip_buf buffer. + * 6lowpan packet in the frame buffer from a full IPv6 packet. * * HC-06 (draft-ietf-6lowpan-hc, version 6) * http://tools.ietf.org/html/draft-ietf-6lowpan-hc-06 @@ -198,27 +431,398 @@ void sixlowpan_hc06_initialize(void) * | L4 data ... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * - * NOTE: The g_addr_context number 00 is reserved for the link local prefix. - * For unicast addresses, if we cannot compress the prefix, we neither - * compress the IID. + * NOTE: The address context number 00 is reserved for the link local + * prefix. For unicast addresses, if we cannot compress the prefix, we + * neither compress the IID. * * Input Parameters: - * dev - A reference to the IEE802.15.4 network device state - * destaddr - L2 destination address, needed to compress IP dest + * ieee - A reference to the IEE802.15.4 network device state + * ipv6 - The IPv6 header to be compressed + * destmac - L2 destination address, needed to compress the IP + * destination field + * fptr - Pointer to frame data payload. * * Returned Value: * None * ****************************************************************************/ -void sixlowpan_compresshdr_hc06(FAR struct net_driver_s *dev, - FAR struct rimeaddr_s *destaddr) +void sixlowpan_compresshdr_hc06(FAR struct ieee802154_driver_s *ieee, + FAR const struct ipv6_hdr_s *ipv6, + FAR const struct rimeaddr_s *destmac, + FAR uint8_t *fptr) { - /* REVISIT: To be provided */ + FAR uint8_t *iphc = fptr + g_frame_hdrlen; + FAR struct sixlowpan_addrcontext_s *addrcontext; + uint8_t iphc0; + uint8_t iphc1; + uint8_t tmp; + + ninfo("fptr=%p g_frame_hdrlen=%u iphc=%p\n", fptr, g_frame_hdrlen, iphc); + + /* As we copy some bit-length fields, in the IPHC encoding bytes, + * we sometimes use |= + * If the field is 0, and the current bit value in memory is 1, + * this does not work. We therefore reset the IPHC encoding here + */ + + iphc0 = SIXLOWPAN_DISPATCH_IPHC; + iphc1 = 0; + iphc[2] = 0; /* Might not be used - but needs to be cleared */ + + /* Point to just after the two IPHC bytes we have committed to */ + + g_hc06ptr = iphc + 2; + + /* Address handling needs to be made first since it might cause an extra + * byte with [ SCI | DCI ] + */ + + /* Check if dest address context exists (for allocating third byte) + * + * TODO: fix this so that it remembers the looked up values for avoiding two + * lookups - or set the lookup values immediately + */ + + if (find_addrcontext_byprefix(ipv6->destipaddr) != NULL || + find_addrcontext_byprefix(ipv6->srcipaddr) != NULL) + { + /* set address context flag and increase g_hc06ptr */ + + ninfo("Decompressing dest or src ipaddr. Setting CID\n"); + iphc1 |= SIXLOWPAN_IPHC_CID; + g_hc06ptr++; + } + + /* Traffic class, flow label + * + * If flow label is 0, compress it. If traffic class is 0, compress it + * We have to process both in the same time as the offset of traffic class + * depends on the presence of version and flow label + */ + + /* hc06 format of tc is ECN | DSCP , original is DSCP | ECN */ + + tmp = (ipv6->vtc << 4) | (ipv6->tcf >> 4); + tmp = ((tmp & 0x03) << 6) | (tmp >> 2); + + if (((ipv6->tcf & 0x0f) == 0) && (ipv6->flow == 0)) + { + /* Flow label can be compressed */ + + iphc0 |= SIXLOWPAN_IPHC_TC_10; + if (((ipv6->vtc & 0x0f) == 0) && ((ipv6->tcf & 0xf0) == 0)) + { + /* Compress (elide) all */ + + iphc0 |= SIXLOWPAN_IPHC_TC_01; + } + else + { + /* Compress only the flow label */ + + *g_hc06ptr = tmp; + g_hc06ptr += 1; + } + } + else + { + /* Flow label cannot be compressed */ + + if (((ipv6->vtc & 0x0f) == 0) && ((ipv6->tcf & 0xf0) == 0)) + { + /* Compress only traffic class */ + + iphc0 |= SIXLOWPAN_IPHC_TC_01; + *g_hc06ptr = (tmp & 0xc0) | (ipv6->tcf & 0x0f); + memcpy(g_hc06ptr + 1, &ipv6->flow, 2); + g_hc06ptr += 3; + } + else + { + /* Compress nothing */ + + memcpy(g_hc06ptr, &ipv6->vtc, 4); + + /* But replace the top byte with the new ECN | DSCP format */ + + *g_hc06ptr = tmp; + g_hc06ptr += 4; + } + } + + /* Note that the payload length is always compressed */ + + /* Next header. We compress it if UDP */ + +#if CONFIG_NET_UDP || UIP_CONF_ROUTER + if (ipv6->proto == IP_PROTO_UDP) + { + iphc0 |= SIXLOWPAN_IPHC_NH; + } +#endif /* CONFIG_NET_UDP */ + + if ((iphc0 & SIXLOWPAN_IPHC_NH) == 0) + { + *g_hc06ptr = ipv6->proto; + g_hc06ptr += 1; + } + + /* Hop limit + * + * if 1: compress, encoding is 01 + * if 64: compress, encoding is 10 + * if 255: compress, encoding is 11 + * else do not compress + */ + + switch (ipv6->ttl) + { + case 1: + iphc0 |= SIXLOWPAN_IPHC_HLIM_1; + break; + + case 64: + iphc0 |= SIXLOWPAN_IPHC_HLIM_64; + break; + + case 255: + iphc0 |= SIXLOWPAN_IPHC_HLIM_255; + break; + + default: + *g_hc06ptr = ipv6->ttl; + g_hc06ptr += 1; + break; + } + + /* Source address - cannot be multicast */ + + if (net_is_addr_unspecified(ipv6->srcipaddr)) + { + ninfo("Compressing unspecified. Setting SAC\n"); + + iphc1 |= SIXLOWPAN_IPHC_SAC; + iphc1 |= SIXLOWPAN_IPHC_SAM_128; + } + else if ((addrcontext = find_addrcontext_byprefix(ipv6->srcipaddr)) != NULL) + { + /* Elide the prefix - indicate by CID and set address context + SAC */ + + ninfo("Compressing src with address context. Setting CID and SAC context: %d\n", + addrcontext->number); + + iphc1 |= SIXLOWPAN_IPHC_CID | SIXLOWPAN_IPHC_SAC; + iphc[2] |= addrcontext->number << 4; + + /* Compression compare with this nodes address (source) */ + + iphc1 |= compress_addr_64(ipv6->srcipaddr, &ieee->i_nodeaddr, + SIXLOWPAN_IPHC_SAM_BIT); + } + + /* No address context found for this address */ + + else if (net_is_addr_linklocal(ipv6->srcipaddr) && + ipv6->destipaddr[1] == 0 && ipv6->destipaddr[2] == 0 && + ipv6->destipaddr[3] == 0) + { + iphc1 |= compress_addr_64(ipv6->srcipaddr, &ieee->i_nodeaddr, + SIXLOWPAN_IPHC_SAM_BIT); + } + else + { + /* Send the full address => SAC = 0, SAM = 00 */ + + iphc1 |= SIXLOWPAN_IPHC_SAM_128; /* 128-bits */ + memcpy(g_hc06ptr, ipv6->srcipaddr, 16); + g_hc06ptr += 16; + } + + /* Destination address */ + + if (net_is_addr_mcast(ipv6->destipaddr)) + { + /* Address is multicast, try to compress */ + + iphc1 |= SIXLOWPAN_IPHC_M; + if (SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE8(ipv6->destipaddr)) + { + iphc1 |= SIXLOWPAN_IPHC_DAM_0; + + /* Use last byte */ + + *g_hc06ptr = ipv6->destipaddr[7] & 0x00ff; + g_hc06ptr += 1; + } + else if (SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE32(ipv6->destipaddr)) + { + FAR uint8_t *iptr = (FAR uint8_t *)ipv6->destipaddr; + + iphc1 |= SIXLOWPAN_IPHC_DAM_16; + + /* Second byte + the last three */ + + *g_hc06ptr = iptr[1]; + memcpy(g_hc06ptr + 1, &iptr[13], 3); + g_hc06ptr += 4; + } + else if (SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE48(ipv6->destipaddr)) + { + FAR uint8_t *iptr = (FAR uint8_t *)ipv6->destipaddr; + + iphc1 |= SIXLOWPAN_IPHC_DAM_64; + + /* Second byte + the last five */ + + *g_hc06ptr = iptr[1]; + memcpy(g_hc06ptr + 1, &iptr[11], 5); + g_hc06ptr += 6; + } + else + { + iphc1 |= SIXLOWPAN_IPHC_DAM_128; + + /* Full address */ + + memcpy(g_hc06ptr, ipv6->destipaddr, 16); + g_hc06ptr += 16; + } + } + else + { + /* Address is unicast, try to compress */ + + if ((addrcontext = find_addrcontext_byprefix(ipv6->destipaddr)) != NULL) + { + /* Elide the prefix */ + + iphc1 |= SIXLOWPAN_IPHC_DAC; + iphc[2] |= addrcontext->number; + + /* Compession compare with link adress (destination) */ + + iphc1 |= compress_addr_64(ipv6->destipaddr, destmac, + SIXLOWPAN_IPHC_DAM_BIT); + + /* No address context found for this address */ + } + else if (net_is_addr_linklocal(ipv6->destipaddr) && + ipv6->destipaddr[1] == 0 && ipv6->destipaddr[2] == 0 && + ipv6->destipaddr[3] == 0) + { + iphc1 |= compress_addr_64(ipv6->destipaddr, destmac, + SIXLOWPAN_IPHC_DAM_BIT); + } + else + { + /* Send the full address */ + + iphc1 |= SIXLOWPAN_IPHC_DAM_128; /* 128-bits */ + memcpy(g_hc06ptr, ipv6->destipaddr, 16); + g_hc06ptr += 16; + } + } + + g_uncomp_hdrlen = IPv6_HDRLEN; + +#if CONFIG_NET_UDP + /* UDP header compression */ + + if (ipv6->proto == IP_PROTO_UDP) + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF(ieee); + + ninfo("Uncompressed UDP ports on send side: %x, %x\n", + ntohs(udp->srcport), ntohs(udp->destport)); + + /* Mask out the last 4 bits can be used as a mask */ + + if (((ntohs(udp->srcport) & 0xfff0) == SIXLOWPAN_UDP_4_BIT_PORT_MIN) && + ((ntohs(udp->destport) & 0xfff0) == SIXLOWPAN_UDP_4_BIT_PORT_MIN)) + { + /* We can compress 12 bits of both source and dest */ + + *g_hc06ptr = SIXLOWPAN_NHC_UDP_CS_P_11; + + ninfo("Remove 12b of both source & dest with prefix 0xfob\n"); + + *(g_hc06ptr + 1) = + (uint8_t)((ntohs(udp->srcport) - SIXLOWPAN_UDP_4_BIT_PORT_MIN) << 4) + + (uint8_t)((ntohs(udp->destport) - SIXLOWPAN_UDP_4_BIT_PORT_MIN)); + + g_hc06ptr += 2; + } + else if ((ntohs(udp->destport) & 0xff00) == + SIXLOWPAN_UDP_8_BIT_PORT_MIN) + { + /* We can compress 8 bits of dest, leave source. */ + + *g_hc06ptr = SIXLOWPAN_NHC_UDP_CS_P_01; + + ninfo("Leave source, remove 8 bits of dest with prefix 0xF0\n"); + + memcpy(g_hc06ptr + 1, &udp->srcport, 2); + *(g_hc06ptr + 3) = + (uint8_t) ((ntohs(udp->destport) - + SIXLOWPAN_UDP_8_BIT_PORT_MIN)); + g_hc06ptr += 4; + } + else if ((ntohs(udp->srcport) & 0xff00) == + SIXLOWPAN_UDP_8_BIT_PORT_MIN) + { + /* We can compress 8 bits of src, leave dest. Copy compressed port */ + + *g_hc06ptr = SIXLOWPAN_NHC_UDP_CS_P_10; + + ninfo("Remove 8 bits of source with prefix 0xF0, leave dest. hch: %u\n", + *g_hc06ptr); + + *(g_hc06ptr + 1) = + (uint8_t)((ntohs(udp->srcport) - SIXLOWPAN_UDP_8_BIT_PORT_MIN)); + + memcpy(g_hc06ptr + 2, &udp->destport, 2); + g_hc06ptr += 4; + } + else + { + /* we cannot compress. Copy uncompressed ports, full checksum */ + + *g_hc06ptr = SIXLOWPAN_NHC_UDP_CS_P_00; + + nwarn("WARNING: Cannot compress headers\n"); + + memcpy(g_hc06ptr + 1, &udp->srcport, 4); + g_hc06ptr += 5; + } + + /* Always inline the checksum */ + + if (1) + { + memcpy(g_hc06ptr, &udp->udpchksum, 2); + g_hc06ptr += 2; + } + + g_uncomp_hdrlen += UDP_HDRLEN; + } +#endif /* CONFIG_NET_UDP */ + + /* Before the g_frame_hdrlen operation */ + + iphc[0] = iphc0; + iphc[1] = iphc1; + + g_frame_hdrlen = g_hc06ptr - fptr; + + ninfo("fptr=%p g_frame_hdrlen=%u iphc=%02x:%02x:%02x g_hc06ptr=%p\n", + fptr, g_frame_hdrlen, iphc[0], iphc[1], iphc[2], g_hc06ptr); + + return; } /**************************************************************************** - * Name: sixlowpan_hc06_initialize + * Name: sixlowpan_uncompresshdr_hc06 * * Description: * Uncompress HC06 (i.e., IPHC and LOWPAN_UDP) headers and put them in @@ -227,25 +831,373 @@ void sixlowpan_compresshdr_hc06(FAR struct net_driver_s *dev, * This function is called by the input function when the dispatch is HC06. * We process the packet in the rime buffer, uncompress the header fields, * and copy the result in the sixlowpan buffer. At the end of the - * decompression, g_rime_hdrlen and g_uncompressed_hdrlen are set to the + * decompression, g_frame_hdrlen and g_uncompressed_hdrlen are set to the * appropriate values * * Input Parmeters: - * dev - A reference to the IEE802.15.4 network device state - * iplen - Equal to 0 if the packet is not a fragment (IP length is then - * inferred from the L2 length), non 0 if the packet is a 1st - * fragment. + * ieee - A reference to the IEE802.15.4 network device state + * iplen - Equal to 0 if the packet is not a fragment (IP length is then + * inferred from the L2 length), non 0 if the packet is a first + * fragment. + * iob - Pointer to the IOB containing the received frame. + * payptr - Pointer to the frame data payload. * * Returned Value: * None * ****************************************************************************/ -void sixlowpan_uncompresshdr_hc06(FAR struct net_driver_s *dev, - uint16_t iplen) +void sixlowpan_uncompresshdr_hc06(FAR struct ieee802154_driver_s *ieee, + uint16_t iplen, FAR struct iob_s *iob, + FAR uint8_t *payptr) { - /* REVISIT: To be provided */ + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF(ieee); + FAR uint8_t *iphc; + uint8_t iphc0; + uint8_t iphc1; + uint8_t tmp; + + /* payptr points to IPHC. At least two byte will be used for the encoding. */ + + iphc = payptr; + iphc0 = iphc[0]; + iphc1 = iphc[1]; + + /* g_hc96ptr points to just after the 2-byte minimum IPHC */ + + g_hc06ptr = payptr + 2; + + ninfo("payptr=%p g_frame_hdrlen=%u iphc=%02x:%02x:%02x g_hc06ptr=%p\n", + payptr, g_frame_hdrlen, iphc[0], iphc[1], iphc[2], g_hc06ptr); + + /* Another if the CID flag is set */ + + if ((iphc1 & SIXLOWPAN_IPHC_CID) != 0) + { + ninfo("CID flag set. Increase header by one\n"); + g_hc06ptr++; + } + + /* Traffic class and flow label */ + + if ((iphc0 & SIXLOWPAN_IPHC_TC_10) == 0) + { + /* Flow label are carried inline */ + + if ((iphc0 & SIXLOWPAN_IPHC_TC_01) == 0) + { + /* Traffic class is carried inline */ + + memcpy(&ipv6->tcf, g_hc06ptr + 1, 3); + tmp = *g_hc06ptr; + g_hc06ptr += 4; + + /* hc06 format of tc is ECN | DSCP , original is DSCP | ECN */ + /* set version, pick highest DSCP bits and set in vtc */ + + ipv6->vtc = 0x60 | ((tmp >> 2) & 0x0f); + + /* ECN rolled down two steps + lowest DSCP bits at top two bits */ + + ipv6->tcf = ((tmp >> 2) & 0x30) | (tmp << 6) | (ipv6->tcf & 0x0f); + } + else + { + /* Traffic class is compressed (set version and no TC) */ + + ipv6->vtc = 0x60; + + /* Highest flow label bits + ECN bits */ + + ipv6->tcf = (*g_hc06ptr & 0x0f) | ((*g_hc06ptr >> 2) & 0x30); + memcpy(&ipv6->flow, g_hc06ptr + 1, 2); + g_hc06ptr += 3; + } + } + else + { + /* Version is always 6! */ + /* Version and flow label are compressed */ + + if ((iphc0 & SIXLOWPAN_IPHC_TC_01) == 0) + { + /* Traffic class is inline */ + + ipv6->vtc = 0x60 | ((*g_hc06ptr >> 2) & 0x0f); + ipv6->tcf = ((*g_hc06ptr << 6) & 0xC0) | ((*g_hc06ptr >> 2) & 0x30); + ipv6->flow = 0; + g_hc06ptr += 1; + } + else + { + /* Traffic class is compressed */ + + ipv6->vtc = 0x60; + ipv6->tcf = 0; + ipv6->flow = 0; + } + } + + /* Next Header */ + + if ((iphc0 & SIXLOWPAN_IPHC_NH) == 0) + { + /* Next header is carried inline */ + + ipv6->proto = *g_hc06ptr; + ninfo("Next header inline: %d\n", ipv6->proto); + g_hc06ptr += 1; + } + + /* Hop limit */ + + if ((iphc0 & SIXLOWPAN_IPHC_HLIM_MASK) != SIXLOWPAN_IPHC_HLIM_INLINE) + { + ipv6->ttl = g_ttl_values[iphc0 & SIXLOWPAN_IPHC_HLIM_MASK]; + } + else + { + ipv6->ttl = *g_hc06ptr; + g_hc06ptr += 1; + } + + /* Put the source address compression mode SAM in the tmp var */ + + tmp = ((iphc1 & SIXLOWPAN_IPHC_SAM_MASK) >> SIXLOWPAN_IPHC_SAM_BIT) & 0x03; + + /* Address context based compression */ + + if ((iphc1 & SIXLOWPAN_IPHC_SAC) != 0) + { + FAR struct sixlowpan_addrcontext_s *addrcontext; + uint8_t sci = (iphc1 & SIXLOWPAN_IPHC_CID) ? iphc[2] >> 4 : 0; + + /* Source address - check address context != NULL only if SAM bits are != 0 */ + + if (tmp != 0) + { + addrcontext = find_addrcontext_bynumber(sci); + if (addrcontext == NULL) + { + nerr("ERROR: Address context not found\n"); + return; + } + } + + /* If tmp == 0 we do not have a Address context and therefore no prefix */ + + uncompress_addr(ipv6->srcipaddr, + tmp != 0 ? addrcontext->prefix : NULL, g_unc_ctxconf[tmp], + (FAR struct rimeaddr_s *)&g_pktaddrs[PACKETBUF_ADDR_SENDER]); + } + else + { + /* No compression and link local */ + + uncompress_addr(ipv6->srcipaddr, g_llprefix, g_unc_llconf[tmp], + (FAR struct rimeaddr_s *)&g_pktaddrs[PACKETBUF_ADDR_SENDER]); + } + + /* Destination address */ + /* Put the destination address compression mode into tmp */ + + tmp = ((iphc1 & SIXLOWPAN_IPHC_DAM_MASK) >> SIXLOWPAN_IPHC_DAM_BIT) & 0x03; + + /* Multicast compression */ + + if ((iphc1 & SIXLOWPAN_IPHC_M) != 0) + { + /* Address context based multicast compression */ + + if ((iphc1 & SIXLOWPAN_IPHC_DAC) != 0) + { + /* TODO: implement this */ + } + else + { + /* Non-address context based multicast compression + * + * DAM 00: 128 bits + * DAM 01: 48 bits FFXX::00XX:XXXX:XXXX + * DAM 0: 32 bits FFXX::00XX:XXXX + * DAM 11: 8 bits FF02::00XX + */ + + uint8_t prefix[] = { 0xff, 0x02 }; + if (tmp > 0 && tmp < 3) + { + prefix[1] = *g_hc06ptr; + g_hc06ptr++; + } + + uncompress_addr(ipv6->destipaddr, prefix, g_unc_mxconf[tmp], NULL); + } + } + else + { + /* no multicast */ + /* Context based */ + + if ((iphc1 & SIXLOWPAN_IPHC_DAC) != 0) + { + FAR struct sixlowpan_addrcontext_s *addrcontext; + uint8_t dci = ((iphc1 & SIXLOWPAN_IPHC_CID) != 0) ? iphc[2] & 0x0f : 0; + + addrcontext = find_addrcontext_bynumber(dci); + + /* All valid cases below need the address context! */ + + if (addrcontext == NULL) + { + ninfo("ERROR: Address context not found\n"); + return; + } + + uncompress_addr(ipv6->destipaddr, addrcontext->prefix, g_unc_ctxconf[tmp], + (FAR struct rimeaddr_s *)&g_pktaddrs[PACKETBUF_ADDR_RECEIVER]); + } + else + { + /* Not address context based => link local M = 0, DAC = 0 - same as SAC */ + + uncompress_addr(ipv6->destipaddr, g_llprefix, g_unc_llconf[tmp], + (FAR struct rimeaddr_s *)&g_pktaddrs[PACKETBUF_ADDR_RECEIVER]); + } + } + + g_uncomp_hdrlen += IPv6_HDRLEN; + + /* Next header processing - continued */ + + if ((iphc0 & SIXLOWPAN_IPHC_NH) != 0) + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF(ieee); + + /* The next header is compressed, NHC is following */ + + if ((*g_hc06ptr & SIXLOWPAN_NHC_UDP_MASK) == SIXLOWPAN_NHC_UDP_ID) + { + uint8_t checksum_compressed; + + ipv6->proto = IP_PROTO_UDP; + checksum_compressed = *g_hc06ptr & SIXLOWPAN_NHC_UDP_CHECKSUMC; + + ninfo("Incoming header value: %i\n", *g_hc06ptr); + + switch (*g_hc06ptr & SIXLOWPAN_NHC_UDP_CS_P_11) + { + case SIXLOWPAN_NHC_UDP_CS_P_00: + /* 1 byte for NHC, 4 byte for ports, 2 bytes chksum */ + + memcpy(&udp->srcport, g_hc06ptr + 1, 2); + memcpy(&udp->destport, g_hc06ptr + 3, 2); + + ninfo("Uncompressed UDP ports (ptr+5): %x, %x\n", + htons(udp->srcport), htons(udp->destport)); + + g_hc06ptr += 5; + break; + + case SIXLOWPAN_NHC_UDP_CS_P_01: + /* 1 byte for NHC + source 16bit inline, dest = 0xF0 + 8 bit + * inline + */ + + ninfo("Decompressing destination\n"); + + memcpy(&udp->srcport, g_hc06ptr + 1, 2); + udp->destport = + htons(SIXLOWPAN_UDP_8_BIT_PORT_MIN + (*(g_hc06ptr + 3))); + + ninfo("Uncompressed UDP ports (ptr+4): %x, %x\n", + htons(udp->srcport), htons(udp->destport)); + + g_hc06ptr += 4; + break; + + case SIXLOWPAN_NHC_UDP_CS_P_10: + /* 1 byte for NHC + source = 0xF0 + 8bit inline, dest = 16 bit + * inline + */ + + ninfo("Decompressing source\n"); + + udp->srcport = + htons(SIXLOWPAN_UDP_8_BIT_PORT_MIN + (*(g_hc06ptr + 1))); + memcpy(&udp->destport, g_hc06ptr + 2, 2); + + ninfo("Uncompressed UDP ports (ptr+4): %x, %x\n", + htons(udp->srcport), htons(udp->destport)); + + g_hc06ptr += 4; + break; + + case SIXLOWPAN_NHC_UDP_CS_P_11: + /* 1 byte for NHC, 1 byte for ports */ + + udp->srcport = + htons(SIXLOWPAN_UDP_4_BIT_PORT_MIN + + (*(g_hc06ptr + 1) >> 4)); + udp->destport = + htons(SIXLOWPAN_UDP_4_BIT_PORT_MIN + + ((*(g_hc06ptr + 1)) & 0x0f)); + + ninfo("Uncompressed UDP ports (ptr+2): %x, %x\n", + htons(udp->srcport), htons(udp->destport)); + + g_hc06ptr += 2; + break; + + default: + nerr("ERROR: Error unsupported UDP compression\n"); + return; + } + + if (!checksum_compressed) + { + /* Has_checksum, default */ + + memcpy(&udp->udpchksum, g_hc06ptr, 2); + g_hc06ptr += 2; + + ninfo("Checksum included\n"); + } + else + { + nwarn("WARNING: checksum *NOT* included\n"); + } + + g_uncomp_hdrlen += UDP_HDRLEN; + } + } + + g_frame_hdrlen = g_hc06ptr - payptr; + + /* IP length field. */ + + if (iplen == 0) + { + /* This is not a fragmented packet */ + + ipv6->len[0] = 0; + ipv6->len[1] = iob->io_len - g_frame_hdrlen + g_uncomp_hdrlen - + IPv6_HDRLEN; + } + else + { + /* This is a first fragment */ + + ipv6->len[0] = (iplen - IPv6_HDRLEN) >> 8; + ipv6->len[1] = (iplen - IPv6_HDRLEN) & 0x00ff; + } + + /* Length field in UDP header */ + + if (ipv6->proto == IP_PROTO_UDP) + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF(ieee); + memcpy(&udp->udplen, &ipv6->len[0], 2); + } } - #endif /* CONFIG_NET_6LOWPAN_COMPRESSION_HC06 */ diff --git a/net/sixlowpan/sixlowpan_hc1.c b/net/sixlowpan/sixlowpan_hc1.c index 245a7465c2c..a7040d5bcb3 100644 --- a/net/sixlowpan/sixlowpan_hc1.c +++ b/net/sixlowpan/sixlowpan_hc1.c @@ -48,11 +48,24 @@ #include +#include +#include +#include + #include #include "sixlowpan/sixlowpan_internal.h" #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC1 +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Buffer access helpers */ + +#define IPv6BUF(dev) ((FAR struct ipv6_hdr_s *)((dev)->d_buf)) +#define UDPIPv6BUF(dev) ((FAR struct udp_hdr_s *)&(dev)->d_buf[IPv6_HDRLEN]) + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -102,19 +115,127 @@ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Input Parmeters: - * dev - A reference to the IEE802.15.4 network device state - * destaddr - L2 destination address, needed to compress the IP - * destination field + * ieee - A reference to the IEE802.15.4 network device state + * ipv6 - The IPv6 header to be compressed + * destmac - L2 destination address, needed to compress the IP + * destination field + * fptr - Pointer to frame to be compressed. * * Returned Value: * None * ****************************************************************************/ -void sixlowpan_compresshdr_hc1(FAR struct net_driver_s *dev, - FAR struct rimeaddr_s *destaddr) +void sixlowpan_compresshdr_hc1(FAR struct ieee802154_driver_s *ieee, + FAR const struct ipv6_hdr_s *ipv6, + FAR const struct rimeaddr_s *destmac, + FAR uint8_t *fptr) { -/* REVISIT: To be provided */ + FAR uint8_t *hc1 = fptr + g_frame_hdrlen; + + /* Check if all the assumptions for full compression are valid */ + + if (ipv6->vtc != 0x60 || ipv6->tcf != 0 || ipv6->flow != 0 || + !sixlowpan_islinklocal(ipv6->srcipaddr) || + !sixlowpan_ismacbased(ipv6->srcipaddr, &ieee->i_nodeaddr) || + !sixlowpan_islinklocal(ipv6->destipaddr) || + !sixlowpan_ismacbased(ipv6->destipaddr, destmac) || + (ipv6->proto != IP_PROTO_ICMP6 && ipv6->proto != IP_PROTO_UDP && + ipv6->proto != IP_PROTO_TCP)) + { + /* IPV6 DISPATCH + * Something cannot be compressed, use IPV6 DISPATCH, compress + * nothing, copy IPv6 header in rime buffer + */ + + /* IPv6 dispatch header (1 byte) */ + + hc1[RIME_HC1_DISPATCH] = SIXLOWPAN_DISPATCH_IPV6; + g_frame_hdrlen += SIXLOWPAN_IPV6_HDR_LEN; + + memcpy(fptr + g_frame_hdrlen, ipv6, IPv6_HDRLEN); + g_frame_hdrlen += IPv6_HDRLEN; + g_uncomp_hdrlen += IPv6_HDRLEN; + } + else + { + /* HC1 DISPATCH maximum compresssion: + * All fields in the IP header but Hop Limit are elided. If next + * header is UDP, we compress UDP header using HC2 + */ + + hc1[RIME_HC1_DISPATCH] = SIXLOWPAN_DISPATCH_HC1; + g_uncomp_hdrlen += IPv6_HDRLEN; + switch (ipv6->proto) + { + case IP_PROTO_ICMP6: + /* HC1 encoding and ttl */ + + hc1[RIME_HC1_ENCODING] = 0xfc; + hc1[RIME_HC1_TTL] = ipv6->ttl; + g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; + break; + +#if CONFIG_NET_TCP + case IP_PROTO_TCP: + /* HC1 encoding and ttl */ + + hc1[RIME_HC1_ENCODING] = 0xfe; + hc1[RIME_HC1_TTL] = ipv6->ttl; + g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; + break; +#endif /* CONFIG_NET_TCP */ + +#if CONFIG_NET_UDP + case IP_PROTO_UDP: + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF(&ieee->i_dev); + + /* Try to compress UDP header (we do only full compression). + * This is feasible if both src and dest ports are between + * CONFIG_NET_6LOWPAN_MINPORT and CONFIG_NET_6LOWPAN_MINPORT + + * 15 + */ + + ninfo("local/remote port %u/%u\n", udp->srcport, udp->destport); + + if (htons(udp->srcport) >= CONFIG_NET_6LOWPAN_MINPORT && + htons(udp->srcport) < (CONFIG_NET_6LOWPAN_MINPORT + 16) && + htons(udp->destport) >= CONFIG_NET_6LOWPAN_MINPORT && + htons(udp->destport) < (CONFIG_NET_6LOWPAN_MINPORT + 16)) + { + FAR uint8_t *hcudp = fptr + g_frame_hdrlen; + + /* HC1 encoding */ + + hcudp[RIME_HC1_HC_UDP_HC1_ENCODING] = 0xfb; + + /* HC_UDP encoding, ttl, src and dest ports, checksum */ + + hcudp[RIME_HC1_HC_UDP_UDP_ENCODING] = 0xe0; + hcudp[RIME_HC1_HC_UDP_TTL] = ipv6->ttl; + hcudp[RIME_HC1_HC_UDP_PORTS] = + (uint8_t)((htons(udp->srcport) - CONFIG_NET_6LOWPAN_MINPORT) << 4) + + (uint8_t)((htons(udp->destport) - CONFIG_NET_6LOWPAN_MINPORT)); + + memcpy(&hcudp[RIME_HC1_HC_UDP_CHKSUM], &udp->udpchksum, 2); + + g_frame_hdrlen += SIXLOWPAN_HC1_HC_UDP_HDR_LEN; + g_uncomp_hdrlen += UDP_HDRLEN; + } + else + { + /* HC1 encoding and ttl */ + + hc1[RIME_HC1_ENCODING] = 0xfa; + hc1[RIME_HC1_TTL] = ipv6->ttl; + g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; + } + } + break; +#endif /* CONFIG_NET_UDP */ + } + } } /**************************************************************************** @@ -126,24 +247,140 @@ void sixlowpan_compresshdr_hc1(FAR struct net_driver_s *dev, * This function is called by the input function when the dispatch is * HC1. It processes the packet in the rime buffer, uncompresses the * header fields, and copies the result in the sixlowpan buffer. At the - * end of the decompression, g_rime_hdrlen and uncompressed_hdr_len + * end of the decompression, g_frame_hdrlen and uncompressed_hdr_len * are set to the appropriate values * * Input Parameters: - * dev - A reference to the IEE802.15.4 network device state - * iplen - Equal to 0 if the packet is not a fragment (IP length is then - * inferred from the L2 length), non 0 if the packet is a 1st - * fragment. + * ieee - A reference to the IEE802.15.4 network device state + * iplen - Equal to 0 if the packet is not a fragment (IP length is then + * inferred from the L2 length), non 0 if the packet is a 1st + * fragment. + * iob - Pointer to the IOB containing the received frame. + * payptr - Pointer to the frame data payload. * * Returned Value: - * None + * Zero (OK) is returned on success, on failure a negater errno value is + * returned. * ****************************************************************************/ -void sixlowpan_uncompresshdr_hc1(FAR struct net_driver_s *dev, - uint16_t iplen) +int sixlowpan_uncompresshdr_hc1(FAR struct ieee802154_driver_s *ieee, + uint16_t iplen, FAR struct iob_s *iob, + FAR uint8_t *payptr) { -/* REVISIT: To be provided */ + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF(&ieee->i_dev); + FAR uint8_t *hc1 = payptr + g_frame_hdrlen; + + /* Format the IPv6 header in the device d_buf */ + /* Set version, traffic clase, and flow label */ + + ipv6->vtc = 0x60; /* Bits 0-3: version, bits 4-7: traffic class (MS) */ + ipv6->tcf = 0; /* Bits 0-3: traffic class (LS), 4-bits: flow label (MS) */ + ipv6->flow = 0; /* 16-bit flow label (LS) */ + + /* Use stateless auto-configuration to set source and destination IP + * addresses. + */ + + sixlowpan_ipfromrime(&g_pktaddrs[PACKETBUF_ADDR_SENDER], + ipv6->srcipaddr); + sixlowpan_ipfromrime(&g_pktaddrs[PACKETBUF_ADDR_RECEIVER], + ipv6->destipaddr); + g_uncomp_hdrlen += IPv6_HDRLEN; + + /* len[], proto, and ttl depend on the encoding */ + + switch (hc1[RIME_HC1_ENCODING] & 0x06) + { + case SIXLOWPAN_HC1_NH_ICMP6: + ipv6->proto = IP_PROTO_ICMP6; + ipv6->ttl = hc1[RIME_HC1_TTL]; + g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; + break; + +#if CONFIG_NET_TCP + case SIXLOWPAN_HC1_NH_TCP: + ipv6->proto = IP_PROTO_TCP; + ipv6->ttl = hc1[RIME_HC1_TTL]; + g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; + break; +#endif /* CONFIG_NET_TCP */ + +#if CONFIG_NET_UDP + case SIXLOWPAN_HC1_NH_UDP: + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF(&ieee->i_dev); + FAR uint8_t *hcudp = payptr + g_frame_hdrlen; + + ipv6->proto = IP_PROTO_UDP; + if ((hcudp[RIME_HC1_HC_UDP_HC1_ENCODING] & 0x01) != 0) + { + /* UDP header is compressed with HC_UDP */ + + if (hcudp[RIME_HC1_HC_UDP_UDP_ENCODING] != + SIXLOWPAN_HC_UDP_ALL_C) + { + nwarn("WARNING: sixlowpan (uncompress_hdr), packet not supported"); + return -EOPNOTSUPP; + } + + /* IP TTL */ + + ipv6->ttl = hcudp[RIME_HC1_HC_UDP_TTL]; + + /* UDP ports, len, checksum */ + + udp->srcport = + htons(CONFIG_NET_6LOWPAN_MINPORT + (hcudp[RIME_HC1_HC_UDP_PORTS] >> 4)); + udp->destport = + htons(CONFIG_NET_6LOWPAN_MINPORT + (hcudp[RIME_HC1_HC_UDP_PORTS] & 0x0F)); + + memcpy(&udp->udpchksum, &hcudp[RIME_HC1_HC_UDP_CHKSUM], 2); + + g_uncomp_hdrlen += UDP_HDRLEN; + g_frame_hdrlen += SIXLOWPAN_HC1_HC_UDP_HDR_LEN; + } + else + { + g_frame_hdrlen += SIXLOWPAN_HC1_HDR_LEN; + } + } + break; +#endif /* CONFIG_NET_UDP */ + + default: + return -EPROTONOSUPPORT; + } + + /* IP length field. */ + + if (iplen == 0) + { + /* This is not a fragmented packet */ + + ipv6->len[0] = 0; + ipv6->len[1] = iob->io_len - g_frame_hdrlen + g_uncomp_hdrlen - + IPv6_HDRLEN; + } + else + { + /* This is a 1st fragment */ + + ipv6->len[0] = (iplen - IPv6_HDRLEN) >> 8; + ipv6->len[1] = (iplen - IPv6_HDRLEN) & 0x00FF; + } + +#if CONFIG_NET_UDP + /* Length field in UDP header */ + + if (ipv6->proto == IP_PROTO_UDP) + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF(&ieee->i_dev); + memcpy(&udp->udplen, &ipv6->len[0], 2); + } +#endif + + return OK; } #endif /* CONFIG_NET_6LOWPAN_COMPRESSION_HC1 */ diff --git a/net/sixlowpan/sixlowpan_input.c b/net/sixlowpan/sixlowpan_input.c index ce520f8e5e3..03a78f3684e 100644 --- a/net/sixlowpan/sixlowpan_input.c +++ b/net/sixlowpan/sixlowpan_input.c @@ -1,35 +1,45 @@ /**************************************************************************** * net/sixlowpan/sixlowpan_input.c + * 6loWPAN implementation (RFC4944 and draft-ietf-6loWPAN-hc-06) * - * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2017, Gregory Nutt, all rights reserved * Author: Gregory Nutt * + * Derives in large part from Contiki: + * + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * Authors: Adam Dunkels + * Nicolas Tsiftes + * Niclas Finne + * Mathilde Durvy + * Julien Abeille + * Joakim Eriksson + * Joel Hoglund + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. * ****************************************************************************/ @@ -39,44 +49,571 @@ #include +#include +#include #include +#include + +#ifdef CONFIG_NET_6LOWPAN_FRAG +# include "nuttx/clock.h" +#endif #include "nuttx/net/netdev.h" +#include "nuttx/net/ip.h" +#include "nuttx/net/sixlowpan.h" + +#ifdef CONFIG_NET_PKT +# include "pkt/pkt.h" +#endif + #include "sixlowpan/sixlowpan_internal.h" #ifdef CONFIG_NET_6LOWPAN +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Success return values from sixlowpan_frame_process */ + +#define INPUT_PARTIAL 0 /* Frame processed successful, packet incomplete */ +#define INPUT_COMPLETE 1 /* Frame processed successful, packet complete */ + +/* Re-assembly timeout in clock ticks */ + +#define NET_6LOWPAN_TIMEOUT SEC2TICK(CONFIG_NET_6LOWPAN_MAXAGE) + +/* Buffer access helpers */ + +#define IPv6BUF(dev) ((FAR struct ipv6_hdr_s *)((dev)->d_buf)) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sixlowpan_recv_hdrlen + * + * Description: + * Get the length of the IEEE802.15.4 header on the received frame. + * + * Input Parameters: + * ieee - The IEEE802.15.4 MAC network driver interface. + * iob - The IOB containing the frame. + * + * Returned Value: + * Ok is returned on success; Othewise a negated errno value is returned. + * + * Assumptions: + * Network is locked + * + ****************************************************************************/ + +int sixlowpan_recv_hdrlen(FAR const uint8_t *fptr) +{ + uint16_t hdrlen; + uint8_t addrmode; + + /* Minimum header: 2 byte FCF + 1 byte sequence number */ + + hdrlen = 3; + + /* Account for destination address size */ + + addrmode = (fptr[1] & FRAME802154_DSTADDR_MASK) >> FRAME802154_DSTADDR_SHIFT; + if (addrmode == FRAME802154_SHORTADDRMODE) + { + /* 2 byte dest PAN + 2 byte dest short address */ + + hdrlen += 4; + } + else if (addrmode == FRAME802154_LONGADDRMODE) + { + /* 2 byte dest PAN + 6 byte dest long address */ + + hdrlen += 10; + } + else if (addrmode != FRAME802154_NOADDR) + { + nwarn("WARNING: Unrecognized address mode\n"); + + return -ENOSYS; + } + else if ((fptr[0] & (1 << FRAME802154_PANIDCOMP_SHIFT)) != 0) + { + nwarn("WARNING: PAN compression, but no destination address\n"); + + return -EINVAL; + } + + /* Account for source address size */ + + addrmode = (fptr[1] & FRAME802154_SRCADDR_MASK) >> FRAME802154_SRCADDR_SHIFT; + if (addrmode == FRAME802154_NOADDR) + { + return hdrlen; + } + else + { + if ((fptr[0] & (1 << FRAME802154_PANIDCOMP_SHIFT)) == 0) + { + hdrlen += 2; + } + + /* Add the length of the source address */ + + if (addrmode == FRAME802154_SHORTADDRMODE) + { + return hdrlen + 2; + } + else if (addrmode == FRAME802154_LONGADDRMODE) + { + return hdrlen + 8; + } + } + + return 0; +} + /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Function: sixlowpan_isbroadcast + * Name: sixlowpan_frame_process * * Description: - * Return the address length associated with a 2-bit address mode + * Process an incoming 6loWPAN frame in 'iob'. * - * Input parameters: - * addrmode - The address mode + * If its a FRAG1 or a non-fragmented frame we first uncompress the IP + * header. The 6loWPAN payload and possibly the uncompressed IP header + * are then copied into d_buf. An indication is returned if the packet + * in d_buf is complete (i.e., non-fragmented frame or and the last + * FRAGN frame). + * + * NOTE: We do not check for overlapping sixlowpan fragments (that is a + * SHALL in the RFC 4944 and should never happen) + * + * Input Parameters: + * ieee - The IEEE802.15.4 MAC network driver interface. + * iob - The IOB containing the frame. * * Returned Value: - * The address length associated with the address mode. + * Ok is returned on success; Othewise a negated errno value is returned. + * + * Assumptions: + * Network is locked * ****************************************************************************/ -static bool sixlowpan_isbroadcast(uint8_t mode, FAR uint8_t *addr) +static int sixlowpan_frame_process(FAR struct ieee802154_driver_s *ieee, + FAR struct iob_s *iob) { - int i = ((mode == FRAME802154_SHORTADDRMODE) ? 2 : 8); + FAR uint8_t *payptr; /* Pointer to the frame payload */ + FAR uint8_t *hc1; /* Convenience pointer to HC1 data */ - while (i-- > 0) + uint16_t fragsize = 0; /* Size of the IP packet (read from fragment) */ + uint8_t fragoffset = 0; /* Offset of the fragment in the IP packet */ + int reqsize; /* Required buffer size */ + int hdrsize; /* Size of the IEEE802.15.4 header */ + +#ifdef CONFIG_NET_6LOWPAN_FRAG + bool isfrag = false; + bool isfirstfrag = false; + bool islastfrag = false; + uint16_t fragtag = 0; /* Tag of the fragment */ + systime_t elapsed; /* Elapsed time */ +#endif /* CONFIG_NET_6LOWPAN_FRAG */ + + /* Get a pointer to the payload following the IEEE802.15.4 frame header. */ + + hdrsize = sixlowpan_recv_hdrlen(iob->io_data); + if (hdrsize < 0) { - if (addr[i] != 0xff) + nwarn("Invalid IEEE802.15.2 header: %d\n", hdrsize); + return hdrsize; + } + + /* Initialize global data. Locking the network guarantees that we have + * exclusive use of the global values for intermediate calculations. + */ + + g_uncomp_hdrlen = 0; + g_frame_hdrlen = hdrsize; + + /* Payload starts after the IEEE802.15.4 header */ + + payptr = &iob->io_data[hdrsize]; + +#ifdef CONFIG_NET_6LOWPAN_FRAG + /* Since we don't support the mesh and broadcast header, the first header + * we look for is the fragmentation header + */ + + switch ((GETINT16(payptr, RIME_FRAG_DISPATCH_SIZE) & 0xf800) >> 8) + { + /* First fragment of new reassembly */ + + case SIXLOWPAN_DISPATCH_FRAG1: + { + /* Set up for the reassembly */ + + fragoffset = 0; + fragsize = GETINT16(payptr, RIME_FRAG_DISPATCH_SIZE) & 0x07ff; + fragtag = GETINT16(payptr, RIME_FRAG_TAG); + + ninfo("FRAG1: size %d, tag %d, offset %d\n", + fragsize, fragtag, fragoffset); + + g_frame_hdrlen += SIXLOWPAN_FRAG1_HDR_LEN; + + /* Indicate the first fragment of the reassembly */ + + isfirstfrag = true; + isfrag = true; + } + break; + + case SIXLOWPAN_DISPATCH_FRAGN: + { + /* Set offset, tag, size. Offset is in units of 8 bytes. */ + + fragoffset = payptr[RIME_FRAG_OFFSET]; + fragtag = GETINT16(payptr, RIME_FRAG_TAG); + fragsize = GETINT16(payptr, RIME_FRAG_DISPATCH_SIZE) & 0x07ff; + + ninfo("FRAGN: size %d, tag %d, offset %d\n", + fragsize, fragtag, fragoffset); + + g_frame_hdrlen += SIXLOWPAN_FRAGN_HDR_LEN; + + ninfo("FRAGN: i_accumlen %d g_rime_payloadlen %d fragsize %d\n", + ieee->i_accumlen, iob->io_len - g_frame_hdrlen, fragsize); + + /* Indicate that this frame is a another fragment for reassembly */ + + isfrag = true; + + /* Check if it is the last fragement to be processed. + * + * If this is the last fragment, we may shave off any extrenous + * bytes at the end. We must be liberal in what we accept. + */ + + if (ieee->i_accumlen + iob->io_len - g_frame_hdrlen >= fragsize) + { + islastfrag = true; + } + } + break; + + /* Not a fragment */ + + default: + break; + } + + /* Check if we are currently reassembling a packet */ + + if (ieee->i_accumlen > 0) + { + /* If reassembly timed out, cancel it */ + + elapsed = clock_systimer() - ieee->i_time; + if (elapsed > NET_6LOWPAN_TIMEOUT) { - return false; + nwarn("WARNING: Reassembly timed out\n"); + ieee->i_pktlen = 0; + ieee->i_accumlen = 0; + } + + /* In this case what we expect is that the next frame will hold the + * next FRAGN of the sequence. We have to handle a few exeptional + * cases that we need to handle: + * + * 1. If we are currently reassembling a packet, but have just received + * the first fragment of another packet. We can either ignore it and + * hope to receive the rest of the under-reassembly packet fragments, + * or we can discard the previous packet altogether, and start + * reassembling the new packet. Here we discard the previous packet, + * and start reassembling the new packet. + * 2. The new frame is not a fragment. We should be able to handle this + * case, but we cannot because that would require two packet buffers. + * It could be handled with a more extensive design. + * 3. The fragment came from a different sender. What would this mean? + * + */ + + else if (!isfrag) + { + /* Discard the partially assembled packet */ + + nwarn("WARNING: Non-fragment frame received during reassembly\n"); + ieee->i_pktlen = 0; + ieee->i_accumlen = 0; + } + + /* It is a fragment of some kind. Drop any zero length fragments */ + + else if (fragsize == 0) + { + nwarn("WARNING: Dropping zero-length 6loWPAN fragment\n"); + return INPUT_PARTIAL; + } + + /* A non-zero, first fragement received while we are in the middle of + * rassembly. Discard the partially assembled packet and start over. + */ + + else if (isfirstfrag) + { + nwarn("WARNING: First fragment frame received during reassembly\n"); + ieee->i_pktlen = 0; + ieee->i_accumlen = 0; + } + + /* Verify that this fragment is part of that reassembly sequence */ + + else if (fragsize != ieee->i_pktlen || ieee->i_reasstag != fragtag || + !rimeaddr_cmp(&ieee->i_fragsrc, &g_pktaddrs[PACKETBUF_ADDR_SENDER])) + { + /* The packet is a fragment that does not belong to the packet + * being reassembled or the packet is not a fragment. + */ + + nwarn("WARNING: Dropping 6loWPAN packet that is not a fragment of " + "the packet currently being reassembled\n"); + return INPUT_PARTIAL; + } + else + { + /* Looks good. We are currently processing a reassembling sequence + * and we recieved a valid FRAGN fragment. Skip the header + * compression dispatch logic. + */ + + goto copypayload; } } - return true; + /* There is no reassembly in progress. Check if we received a fragment */ + + else if (isfrag) + { + /* Another case that we have to handle is if a FRAGN fragment of a + * reassembly is received, but we are not currently reassembling a + * packet. I think we have no choice but to drop the packet in this + * case. + */ + + if (!isfirstfrag) + { + nwarn("WARNING: FRAGN 6loWPAN fragment while not reassembling\n"); + return OK; + } + + /* Start reassembly if we received a non-zero length, first fragment */ + + if (fragsize > 0) + { + /* Drop the packet if it cannot fit into the d_buf */ + + if (fragsize > CONFIG_NET_6LOWPAN_MTU) + { + nwarn("WARNING: Reassembled packet size exeeds CONFIG_NET_6LOWPAN_MTU\n"); + return OK; + } + + /* Set up for the reassembly */ + + ieee->i_pktlen = fragsize; + ieee->i_reasstag = fragtag; + ieee->i_time = clock_systimer(); + + ninfo("Starting reassembly: i_pktlen %d, i_pktlen %d\n", + ieee->i_pktlen, ieee->i_reasstag); + + rimeaddr_copy(&ieee->i_fragsrc, &g_pktaddrs[PACKETBUF_ADDR_SENDER]); + } + } +#endif /* CONFIG_NET_6LOWPAN_FRAG */ + + /* Process next dispatch and headers */ + + hc1 = &iob->io_data[g_frame_hdrlen]; + +#ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC06 + if ((hc1[RIME_HC1_DISPATCH] & SIXLOWPAN_DISPATCH_IPHC_MASK) == SIXLOWPAN_DISPATCH_IPHC) + { + ninfo("IPHC Dispatch\n"); + sixlowpan_uncompresshdr_hc06(ieee, fragsize, iob, payptr); + } + else +#endif /* CONFIG_NET_6LOWPAN_COMPRESSION_HC06 */ + +#ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC1 + if (hc1[RIME_HC1_DISPATCH] == SIXLOWPAN_DISPATCH_HC1) + { + ninfo("HC1 Dispatch\n"); + sixlowpan_uncompresshdr_hc1(ieee, fragsize, iob, payptr); + } + else +#endif /* CONFIG_NET_6LOWPAN_COMPRESSION_HC1 */ + + if (hc1[RIME_HC1_DISPATCH] == SIXLOWPAN_DISPATCH_IPV6) + { + ninfo("IPV6 Dispatch\n"); + g_frame_hdrlen += SIXLOWPAN_IPV6_HDR_LEN; + + /* Put uncompressed IP header in d_buf. */ + + memcpy(ieee->i_dev.d_buf, payptr + g_frame_hdrlen, IPv6_HDRLEN); + + /* Update g_uncomp_hdrlen and g_frame_hdrlen. */ + + g_frame_hdrlen += IPv6_HDRLEN; + g_uncomp_hdrlen += IPv6_HDRLEN; + } + else + { + /* Unknown or unsupported header */ + + nwarn("WARNING unknown dispatch: %u\n", hc1[RIME_HC1_DISPATCH]); + return OK; + } + +#ifdef CONFIG_NET_6LOWPAN_FRAG +copypayload: +#endif /* CONFIG_NET_6LOWPAN_FRAG */ + + /* Copy "payload" from the rime buffer to the IEEE802.15.4 MAC driver's + * d_buf. If this frame is a first fragment or not part of a fragmented + * packet, we have already copied the compressed headers, g_uncomp_hdrlen + * and g_frame_hdrlen are non-zerio, fragoffset is. + */ + + g_rime_payloadlen = iob->io_len - g_frame_hdrlen; + if (g_rime_payloadlen > CONFIG_NET_6LOWPAN_MTU) + { + nwarn("WARNING: Packet dropped due to payload (%u) > packet buffer (%u)\n", + g_rime_payloadlen, CONFIG_NET_6LOWPAN_MTU); + return OK; + } + + /* Sanity-check size of incoming packet to avoid buffer overflow */ + + reqsize = g_uncomp_hdrlen + (uint16_t) (fragoffset << 3) + g_rime_payloadlen; + if (reqsize > CONFIG_NET_6LOWPAN_MTU) + { + ninfo("Required buffer size: %d+%d+%d=%d Available: %d\n", + g_uncomp_hdrlen, (int)(fragoffset << 3), g_rime_payloadlen, + reqsize, CONFIG_NET_6LOWPAN_MTU); + return -ENOMEM; + } + + memcpy((FAR uint8_t *)ieee->i_dev.d_buf + g_uncomp_hdrlen + + (int)(fragoffset << 3), payptr + g_frame_hdrlen, + g_rime_payloadlen); + +#ifdef CONFIG_NET_6LOWPAN_FRAG + /* Update ieee->i_accumlen if the frame is a fragment, ieee->i_pktlen + * otherwise. + */ + + if (isfrag) + { + /* Add the size of the header only for the first fragment. */ + + if (isfirstfrag) + { + ieee->i_accumlen += g_uncomp_hdrlen; + } + + /* For the last fragment, we are OK if there is extraneous bytes at the + * end of the packet. + */ + + if (islastfrag) + { + ieee->i_accumlen = fragsize; + } + else + { + ieee->i_accumlen += g_rime_payloadlen; + } + + ninfo("i_accumlen %d, g_rime_payloadlen %d\n", + ieee->i_accumlen, g_rime_payloadlen); + } + else + { + ieee->i_pktlen = g_rime_payloadlen + g_uncomp_hdrlen; + } + + /* If we have a full IP packet in sixlowpan_buf, deliver it to + * the IP stack + */ + + ninfo("sixlowpan_init i_accumlen %d, ieee->i_pktlen %d\n", + ieee->i_accumlen, ieee->i_pktlen); + + if (ieee->i_accumlen == 0 || ieee->i_accumlen == ieee->i_pktlen) + { + ninfo("IP packet ready (length %d)\n", ieee->i_pktlen); + + ieee->i_dev.d_len = ieee->i_pktlen; + ieee->i_pktlen = 0; + ieee->i_accumlen = 0; + return INPUT_COMPLETE; + } + + return INPUT_PARTIAL; +#else + /* Deliver the packet to the IP stack */ + + ieee->i_dev.d_len = g_rime_payloadlen + g_uncomp_hdrlen; + return INPUT_COMPLETE; +#endif /* CONFIG_NET_6LOWPAN_FRAG */ +} + +/**************************************************************************** + * Function: sixlowpan_dispatch + * + * Description: + * Inject the packet in d_buf into the network for normal packet processing. + * + * Parameters: + * ieee - The IEEE802.15.4 MAC network driver interface. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int sixlowpan_dispatch(FAR struct ieee802154_driver_s *ieee) +{ + sixlowpan_dumpbuffer("Incoming packet", + (FAR const uint8_t *)IPv6BUF(&ieee->i_dev), + ieee->i_dev.d_len); + +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the packet tap */ + + ninfo("Packet tap\n"); + pkt_input(&ieee->i_dev); +#endif + + /* We only accept IPv6 packets. */ + + ninfo("Iv6 packet dispatch\n"); + NETDEV_RXIPV6(&ieee->i_dev); + + /* Give the IPv6 packet to the network layer. NOTE: If there is a + * problem with IPv6 header, it will be silently dropped and d_len will + * be set to zero. Oddly, ipv6_input() will return OK in this case. + */ + + return ipv6_input(&ieee->i_dev); } /**************************************************************************** @@ -87,30 +624,163 @@ static bool sixlowpan_isbroadcast(uint8_t mode, FAR uint8_t *addr) * Name: sixlowpan_input * * Description: - * Process an incoming IP packet. + * Process an incoming 6loWPAN frame. * * This function is called when the device driver has received a 6loWPAN - * packet from the network. The packet from the device driver must be - * present in the d_buf buffer, and the length of the packet should be - * placed in the d_len field. + * frame from the network. The frame from the device driver must be + * provided in a IOB present in the i_framelist: The frame data is in the + * IOB io_data[] buffer and the length of the frame is in the IOB io_len + * field. Only a single IOB is expected in the i_framelist. This incoming + * data will be processed one frame at a time. * - * When the function returns, there may be an outbound packet placed - * in the d_buf packet buffer. If so, the d_len field is set to - * the length of the packet. If no packet is to be sent out, the - * d_len field is set to 0. + * An non-NULL d_buf of size CONFIG_NET_6LOWPAN_MTU must also be provided. + * The frame will be decompressed and placed in the d_buf. Fragmented + * packets will also be reassembled in the d_buf as they are received + * (meaning for the driver, that two packet buffers are required: One for + * reassembly of RX packets and one used for TX polling). + * + * After each frame is processed into d_buf, the IOB is removed and + * deallocated. i_framelist will be nullified. If reassembly is + * incomplete, this function will return to called with i_framelist + * equal to NULL. The partially reassembled packet must be preserved by + * the IEEE802.15.4 MAC and provided again when the next frame is + * received. + * + * When the packet in the d_buf is fully reassembled, it will be provided + * to the network as with any other received packet. d_len will be set + * the the length of the uncompressed, reassembled packet. + * + * After the network processes the packet, d_len will be set to zero. + * Network logic may also decide to send a response to the packet. In + * that case, the outgoing network packet will be placed in d_buf the + * d_buf and d_len will be set to a non-zero value. That case is handled + * by this function. + * + * If that case occurs, the packet will be converted to a list of + * compressed and possibly fragmented frames in i_framelist as with other + * TX operations. + * + * So from the standpoint of the IEEE802.15.4 MAC driver, there are two + * possible results: (1) i_framelist is NULL meaning that the frame + * was fully processed and freed, or (2) i_framelist is non-NULL meaning + * that there are outgoing frame(s) to be sent. * * Input Parameters: - * dev - The IEEE802.15.4 MAC network driver interface. + * ieee - The IEEE802.15.4 MAC network driver interface. * * Returned Value: * Ok is returned on success; Othewise a negated errno value is returned. * ****************************************************************************/ -int sixlowpan_input(FAR struct net_driver_s *dev) +int sixlowpan_input(FAR struct ieee802154_driver_s *ieee) { - /* REVISIT: To be provided */ - return -ENOSYS; + int ret = -EINVAL; + + DEBUGASSERT(ieee != NULL && !FRAME_IOB_EMPTY(ieee)); + + /* Verify that an IOB is provided in the device structure */ + + while (!FRAME_IOB_EMPTY(ieee)) + { + FAR struct iob_s *iob; + + /* Remove the IOB containing the frame from the device structure */ + + FRAME_IOB_REMOVE(ieee, iob); + DEBUGASSERT(iob != NULL); + + sixlowpan_dumpbuffer("Incoming frame", + (FAR const uint8_t *)iob->io_data, iob->io_len); + + /* Process the frame, decompressing it into the packet buffer */ + + ret = sixlowpan_frame_process(ieee, iob); + + /* Was the frame successfully processed? Is the packet in d_buf fully + * reassembled? + */ + + if (ret == INPUT_COMPLETE) + { + /* Inject the uncompressed, reassembled packet into the network */ + + ret = sixlowpan_dispatch(ieee); + if (ret >= 0) + { + /* Check if this resulted in a request to send an outgoing + * packet. + */ + + if (ieee->i_dev.d_len > 0) + { + FAR struct ipv6_hdr_s *ipv6hdr; + FAR uint8_t *buffer; + struct rimeaddr_s destmac; + size_t hdrlen; + size_t buflen; + + /* The IPv6 header followed by TCP or UDP headers should + * lie at the beginning of d_buf since there is no link + * layer protocol header. + */ + + ipv6hdr = (FAR struct ipv6_hdr_s *)(ieee->i_dev.d_buf); + + /* Get the Rime MAC address of the destination. This + * assumes an encoding of the MAC address in the IPv6 + * address. + */ + + sixlowpan_rimefromip(ipv6hdr->destipaddr, &destmac); + + /* The data payload should follow the IPv6 header plus + * the protocol header. + */ + + if (ipv6hdr->proto != IP_PROTO_TCP) + { + hdrlen = IPv6_HDRLEN + TCP_HDRLEN; + } + else if (ipv6hdr->proto != IP_PROTO_UDP) + { + hdrlen = IPv6_HDRLEN + UDP_HDRLEN; + } + else if (ipv6hdr->proto != IP_PROTO_ICMP6) + { + hdrlen = IPv6_HDRLEN + ICMPv6_HDRLEN; + } + else + { + nwarn("WARNING: Unsupported protoype: %u\n", + ipv6hdr->proto); + ret = -EPROTO; + goto drop; + } + + if (hdrlen < ieee->i_dev.d_len) + { + nwarn("WARNING: Packet to small: Have %u need >%u\n", + ieee->i_dev.d_len, hdrlen); + ret = -ENOBUFS; + goto drop; + } + + /* Convert the outgoing packet into a frame list. */ + + buffer = ieee->i_dev.d_buf + hdrlen; + buflen = ieee->i_dev.d_len - hdrlen; + + ret = sixlowpan_queue_frames(ieee, ipv6hdr, buffer, buflen, + &destmac); +drop: + ieee->i_dev.d_len = 0; + } + } + } + } + + return ret; } #endif /* CONFIG_NET_6LOWPAN */ diff --git a/net/sixlowpan/sixlowpan_internal.h b/net/sixlowpan/sixlowpan_internal.h index 6828c141285..8f3e52dfd39 100644 --- a/net/sixlowpan/sixlowpan_internal.h +++ b/net/sixlowpan/sixlowpan_internal.h @@ -63,6 +63,7 @@ #include #include #include +#include #ifdef CONFIG_NET_6LOWPAN @@ -81,57 +82,221 @@ #define rimeaddr_cmp(addr1,addr2) \ (memcmp(addr1, addr2, CONFIG_NET_6LOWPAN_RIMEADDR_SIZE) == 0) -/* Frame buffer helpers */ +/* Pointers in the Rime buffer */ -#define FRAME_RESET(ieee) \ - do \ - { \ - (ieee)->i_dataoffset = 0; \ - (ieee)->i_framelen = 0; \ - } \ - while (0) +/* Fragment header. + * + * The fragment header is used when the payload is too large to fit in a + * single IEEE 802.15.4 frame. The fragment header contains three fields: + * Datagram size, datagram tag and datagram offset. + * + * 1. Datagram size describes the total (un-fragmented) payload. + * 2. Datagram tag identifies the set of fragments and is used to match + * fragments of the same payload. + * 3. Datagram offset identifies the fragment’s offset within the un- + * fragmented payload. + * + * The fragment header length is 4 bytes for the first header and 5 + * bytes for all subsequent headers. + */ -#define FRAME_HDR_START(ieee) \ - ((ieee)->i_frame) -#define FRAME_HDR_SIZE(ieee) \ - ((ieee)->i_dataoffset) +#define RIME_FRAG_DISPATCH_SIZE 0 /* 16 bit */ +#define RIME_FRAG_TAG 2 /* 16 bit */ +#define RIME_FRAG_OFFSET 4 /* 8 bit */ -#define FRAME_DATA_START(ieee) \ - ((FAR uint8_t *)((ieee)->i_frame) + (ieee)->i_dataoffset) -#define FRAME_DATA_SIZE(ieee) \ - ((ieee)->i_framelen - (ieee)->i_dataoffset) +/* Define the Rime buffer as a byte array */ -#define FRAME_REMAINING(ieee) \ - (CONFIG_NET_6LOWPAN_FRAMELEN - (ieee)->i_framelen) -#define FRAME_SIZE(ieee) \ - ((ieee)->i_framelen) +#define RIME_HC1_DISPATCH 0 /* 8 bit */ +#define RIME_HC1_ENCODING 1 /* 8 bit */ +#define RIME_HC1_TTL 2 /* 8 bit */ + +#define RIME_HC1_HC_UDP_DISPATCH 0 /* 8 bit */ +#define RIME_HC1_HC_UDP_HC1_ENCODING 1 /* 8 bit */ +#define RIME_HC1_HC_UDP_UDP_ENCODING 2 /* 8 bit */ +#define RIME_HC1_HC_UDP_TTL 3 /* 8 bit */ +#define RIME_HC1_HC_UDP_PORTS 4 /* 8 bit */ +#define RIME_HC1_HC_UDP_CHKSUM 5 /* 16 bit */ /* These are some definitions of element values used in the FCF. See the * IEEE802.15.4 spec for details. */ -#define FRAME802154_BEACONFRAME 0x00 -#define FRAME802154_DATAFRAME 0x01 -#define FRAME802154_ACKFRAME 0x02 -#define FRAME802154_CMDFRAME 0x03 +#define FRAME802154_FRAMETYPE_SHIFT (0) /* Bits 0-2: Frame type */ +#define FRAME802154_FRAMETYPE_MASK (7 << FRAME802154_FRAMETYPE_SHIFT) +#define FRAME802154_SECENABLED_SHIFT (3) /* Bit 3: Security enabled */ +#define FRAME802154_FRAMEPENDING_SHIFT (4) /* Bit 4: Frame pending */ +#define FRAME802154_ACKREQUEST_SHIFT (5) /* Bit 5: ACK request */ +#define FRAME802154_PANIDCOMP_SHIFT (6) /* Bit 6: PANID compression */ + /* Bits 7-9: Reserved */ +#define FRAME802154_DSTADDR_SHIFT (2) /* Bits 10-11: Dest address mode */ +#define FRAME802154_DSTADDR_MASK (3 << FRAME802154_DSTADDR_SHIFT) +#define FRAME802154_VERSION_SHIFT (4) /* Bit 12-13: Frame version */ +#define FRAME802154_VERSION_MASK (3 << FRAME802154_VERSION_SHIFT) +#define FRAME802154_SRCADDR_SHIFT (6) /* Bits 14-15: Source address mode */ +#define FRAME802154_SRCADDR_MASK (3 << FRAME802154_SRCADDR_SHIFT) -#define FRAME802154_BEACONREQ 0x07 +/* Unshifted values for use in struct frame802154_fcf_s */ -#define FRAME802154_IEEERESERVED 0x00 -#define FRAME802154_NOADDR 0x00 /* Only valid for ACK or Beacon frames */ -#define FRAME802154_SHORTADDRMODE 0x02 -#define FRAME802154_LONGADDRMODE 0x03 +#define FRAME802154_BEACONFRAME (0) +#define FRAME802154_DATAFRAME (1) +#define FRAME802154_ACKFRAME (2) +#define FRAME802154_CMDFRAME (3) + +#define FRAME802154_BEACONREQ (7) + +#define FRAME802154_IEEERESERVED (0) +#define FRAME802154_NOADDR (0) /* Only valid for ACK or Beacon frames */ +#define FRAME802154_SHORTADDRMODE (2) +#define FRAME802154_LONGADDRMODE (3) #define FRAME802154_NOBEACONS 0x0f #define FRAME802154_BROADCASTADDR 0xffff #define FRAME802154_BROADCASTPANDID 0xffff -#define FRAME802154_IEEE802154_2003 0x00 -#define FRAME802154_IEEE802154_2006 0x01 +#define FRAME802154_IEEE802154_2003 (0) +#define FRAME802154_IEEE802154_2006 (1) -#define FRAME802154_SECURITY_LEVEL_NONE 0 -#define FRAME802154_SECURITY_LEVEL_128 3 +#define FRAME802154_SECURITY_LEVEL_NONE (0) +#define FRAME802154_SECURITY_LEVEL_128 (3) + +/* Packet buffer Definitions */ + +#define PACKETBUF_ATTR_PACKET_TYPE_DATA 0 +#define PACKETBUF_ATTR_PACKET_TYPE_ACK 1 +#define PACKETBUF_ATTR_PACKET_TYPE_STREAM 2 +#define PACKETBUF_ATTR_PACKET_TYPE_STREAM_END 3 +#define PACKETBUF_ATTR_PACKET_TYPE_TIMESTAMP 4 + +/* Packet buffer attributes (indices into i_pktattr) */ + +#define PACKETBUF_ATTR_NONE 0 + +/* Scope 0 attributes: used only on the local node. */ + +#define PACKETBUF_ATTR_CHANNEL 1 +#define PACKETBUF_ATTR_NETWORK_ID 2 +#define PACKETBUF_ATTR_LINK_QUALITY 3 +#define PACKETBUF_ATTR_RSSI 4 +#define PACKETBUF_ATTR_TIMESTAMP 5 +#define PACKETBUF_ATTR_RADIO_TXPOWER 6 +#define PACKETBUF_ATTR_LISTEN_TIME 7 +#define PACKETBUF_ATTR_TRANSMIT_TIME 8 +#define PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS 9 +#define PACKETBUF_ATTR_MAC_SEQNO 10 +#define PACKETBUF_ATTR_MAC_ACK 11 + +/* Scope 1 attributes: used between two neighbors only. */ + +#define PACKETBUF_ATTR_RELIABLE 12 +#define PACKETBUF_ATTR_PACKET_ID 13 +#define PACKETBUF_ATTR_PACKET_TYPE 14 +#define PACKETBUF_ATTR_REXMIT 15 +#define PACKETBUF_ATTR_MAX_REXMIT 16 +#define PACKETBUF_ATTR_NUM_REXMIT 17 +#define PACKETBUF_ATTR_PENDING 18 + +/* Scope 2 attributes: used between end-to-end nodes. */ + +#define PACKETBUF_ATTR_HOPS 11 +#define PACKETBUF_ATTR_TTL 20 +#define PACKETBUF_ATTR_EPACKET_ID 21 +#define PACKETBUF_ATTR_EPACKET_TYPE 22 +#define PACKETBUF_ATTR_ERELIABLE 23 + +#define PACKETBUF_NUM_ATTRS 24 + +/* Addresses (indices into i_pktaddr) */ + +#define PACKETBUF_ADDR_SENDER 0 +#define PACKETBUF_ADDR_RECEIVER 1 +#define PACKETBUF_ADDR_ESENDER 2 +#define PACKETBUF_ADDR_ERECEIVER 3 + +#define PACKETBUF_NUM_ADDRS 4 + +/* Address compressibility test macros **************************************/ + +/* Check whether we can compress the IID in address 'a' to 16 bits. This is + * used for unicast addresses only, and is true if the address is on the + * format ::0000:00ff:fe00:XXXX + * + * NOTE: we currently assume 64-bits prefixes + */ + +/* Check whether we can compress the IID in address 'a' to 16 bits. This is + * used for unicast addresses only, and is true if the address is on the + * format ::0000:00ff:fe00:XXXX. + * + * NOTE: we currently assume 64-bits prefixes. Big-endian, network order is + * assumed. + */ + +#define SIXLOWPAN_IS_IID_16BIT_COMPRESSABLE(a) \ + ((((a)[4]) == 0x0000) && (((a)[5]) == HTONS(0x00ff)) && \ + (((a)[6]) == 0xfe00)) + +/* Check whether the 9-bit group-id of the compressed multicast address is + * known. It is true if the 9-bit group is the all nodes or all routers + * group. Parameter 'a' is typed uint8_t * + */ + +#define SIXLOWPAN_IS_MCASTADDR_DECOMPRESSABLE(a) \ + (((*a & 0x01) == 0) && \ + ((*(a + 1) == 0x01) || (*(a + 1) == 0x02))) + +/* Check whether the 112-bit group-id of the multicast address is mappable + * to a 9-bit group-id. It is true if the group is the all nodes or all + * routers group: + * + * XXXX:0000:0000:0000:0000:0000:0000:0001 All nodes address + * XXXX:0000:0000:0000:0000:0000:0000:0002 All routers address + */ + +#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE(a) \ + ((a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (a)[5] == 0 && (a)[6] == 0 && \ + ((a)[7] == HTONS(0x0001) || (a)[7] == HTONS(0x0002))) + +/* FFXX:0000:0000:0000:0000:00XX:XXXX:XXXX */ + +#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE48(a) \ + ((a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (((a)[5] & HTONS(0xff00)) == 0)) + +/* FFXX:0000:0000:0000:0000:0000:00XX:XXXX */ + +#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE32(a) \ + ((a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (a)[5] == 0 && ((a)[6] & HTONS(0xff00)) == 0) + +/* FF02:0000:0000:0000:0000:0000:0000:00XX */ + +#define SIXLOWPAN_IS_MCASTADDR_COMPRESSABLE8(a) \ + ((((a)[0] & HTONS(0x00ff)) == HTONS(0x0002)) && \ + (a)[1] == 0 && (a)[2] == 0 && (a)[3] == 0 && \ + (a)[4] == 0 && (a)[5] == 0 && (a)[6] == 0 && \ + (((a)[7] & HTONS(0xff00)) == 0x0000)) + +/* General helper macros ****************************************************/ + +#define GETINT16(ptr,index) \ + ((((uint16_t)((ptr)[index])) << 8) | ((uint16_t)(((ptr)[(index) + 1])))) +#define PUTINT16(ptr,index,value) \ + do \ + { \ + (ptr)[index] = ((uint16_t)(value) >> 8) & 0xff; \ + (ptr)[index + 1] = (uint16_t)(value) & 0xff; \ + } \ + while(0) + +/* Debug ********************************************************************/ + +#ifdef CONFIG_NET_6LOWPAN_DUMPBUFFER +# define sixlowpan_dumpbuffer(m,b,s) ninfodumpbuffer(m,b,s) +#else +# define sixlowpan_dumpbuffer(m,b,s) +#endif /**************************************************************************** * Public Types @@ -223,25 +388,53 @@ struct frame802154_s uint16_t src_pid; /* Source PAN ID */ uint8_t src_addr[8]; /* Source address */ struct frame802154_aux_hdr_s aux_hdr; /* Aux security header */ - uint8_t *payload; /* Pointer to 802.15.4 frame payload */ - uint8_t payload_len; /* Length of payload field */ }; /**************************************************************************** * Public Data ****************************************************************************/ -/* A pointer to the optional, architecture-specific compressor */ +/* The following data values are used to hold intermediate settings while + * processing IEEE802.15.4 frames. These globals are shared with incoming + * and outgoing frame processing and possibly with mutliple IEEE802.15.4 MAC + * devices. The network lock provides exclusive use of these globals + * during that processing + */ -struct sixlowpan_nhcompressor_s; /* Foward reference */ -extern FAR struct sixlowpan_nhcompressor_s *g_sixlowpan_compressor; +/* A pointer to the rime buffer. + * + * We initialize it to the beginning of the rime buffer, then access + * different fields by updating the offset ieee->g_frame_hdrlen. + */ -#ifdef CONFIG_NET_6LOWPAN_SNIFFER -/* Rime Sniffer support for one single listener to enable trace of IP */ +extern FAR uint8_t *g_rimeptr; -struct sixlowpan_rime_sniffer_s; /* Foward reference */ -extern FAR struct sixlowpan_rime_sniffer_s *g_sixlowpan_sniffer; -#endif +/* The length of the payload in the Rime buffer. + * + * The payload is what comes after the compressed or uncompressed headers + * (can be the IP payload if the IP header only is compressed or the UDP + * payload if the UDP header is also compressed) + */ + +extern uint8_t g_rime_payloadlen; + +/* g_uncomp_hdrlen is the length of the headers before compression (if HC2 + * is used this includes the UDP header in addition to the IP header). + */ + +extern uint8_t g_uncomp_hdrlen; + +/* g_frame_hdrlen is the total length of (the processed) 6lowpan headers + * (fragment headers, IPV6 or HC1, HC2, and HC1 and HC2 non compressed + * fields). + */ + +extern uint8_t g_frame_hdrlen; + +/* Packet buffer metadata: Attributes and addresses */ + +extern uint16_t g_pktattrs[PACKETBUF_NUM_ATTRS]; +extern struct rimeaddr_s g_pktaddrs[PACKETBUF_NUM_ADDRS]; /**************************************************************************** * Public Types @@ -253,7 +446,9 @@ extern FAR struct sixlowpan_rime_sniffer_s *g_sixlowpan_sniffer; struct net_driver_s; /* Forward reference */ struct ieee802154_driver_s; /* Forward reference */ +struct ipv6_hdr_s; /* Forward reference */ struct rimeaddr_s; /* Forward reference */ +struct iob_s; /* Forward reference */ /**************************************************************************** * Name: sixlowpan_send @@ -263,19 +458,19 @@ struct rimeaddr_s; /* Forward reference */ * it to be sent on an 802.15.4 network using 6lowpan. Called from common * UDP/TCP send logic. * - * The payload data is in the caller 'buf' and is of length 'len'. - * Compressed headers will be added and if necessary the packet is - * fragmented. The resulting packet/fragments are put in dev->d_buf and - * the first frame will be delivered to the 802.15.4 MAC. via ieee->i_frame. - * - * Input Parmeters: + * The payload data is in the caller 'buf' and is of length 'buflen'. + * Compressed headers will be added and if necessary the packet is + * fragmented. The resulting packet/fragments are put in ieee->i_framelist + * and the entire list of frames will be delivered to the 802.15.4 MAC via + * ieee->i_framelist. * * Input Parameters: - * dev - The IEEE802.15.4 MAC network driver interface. - * ipv6 - IPv6 plus TCP or UDP headers. - * buf - Data to send - * len - Length of data to send - * raddr - The MAC address of the destination + * dev - The IEEE802.15.4 MAC network driver interface. + * destip - IPv6 plus TCP or UDP headers. + * buf - Data to send + * buflen - Length of data to send + * raddr - The MAC address of the destination + * timeout - Send timeout in deciseconds * * Returned Value: * Ok is returned on success; Othewise a negated errno value is returned. @@ -289,11 +484,12 @@ struct rimeaddr_s; /* Forward reference */ ****************************************************************************/ int sixlowpan_send(FAR struct net_driver_s *dev, - FAR const struct ipv6_hdr_s *ipv6, FAR const void *buf, - size_t len, FAR const struct rimeaddr_s *raddr); + FAR const struct ipv6_hdr_s *destip, FAR const void *buf, + size_t buflen, FAR const struct rimeaddr_s *raddr, + uint16_t timeout); /**************************************************************************** - * Function: sixlowpan_hdrlen + * Function: sixlowpan_send_hdrlen * * Description: * This function is before the first frame has been sent in order to @@ -311,7 +507,7 @@ int sixlowpan_send(FAR struct net_driver_s *dev, * ****************************************************************************/ -int sixlowpan_hdrlen(FAR struct ieee802154_driver_s *ieee, +int sixlowpan_send_hdrlen(FAR struct ieee802154_driver_s *ieee, uint16_t dest_panid); /**************************************************************************** @@ -323,6 +519,7 @@ int sixlowpan_hdrlen(FAR struct ieee802154_driver_s *ieee, * * Input parameters: * ieee - A reference IEEE802.15.4 MAC network device structure. + * iob - The IOB in which to create the frame. * dest_panid - PAN ID of the destination. May be 0xffff if the destination * is not associated. * @@ -333,7 +530,45 @@ int sixlowpan_hdrlen(FAR struct ieee802154_driver_s *ieee, ****************************************************************************/ int sixlowpan_framecreate(FAR struct ieee802154_driver_s *ieee, - uint16_t dest_panid); + FAR struct iob_s *iob, uint16_t dest_panid); + +/**************************************************************************** + * Name: sixlowpan_queue_frames + * + * Description: + * Process an outgoing UDP or TCP packet. This function is called from + * send interrupt logic when a TX poll is received. It formates the + * list of frames to be sent by the IEEE802.15.4 MAC driver. + * + * The payload data is in the caller 'buf' and is of length 'buflen'. + * Compressed headers will be added and if necessary the packet is + * fragmented. The resulting packet/fragments are put in ieee->i_framelist + * and the entire list of frames will be delivered to the 802.15.4 MAC via + * ieee->i_framelist. + * + * Input Parameters: + * ieee - The IEEE802.15.4 MAC driver instance + * ipv6hdr - IPv6 header followed by TCP or UDP header. + * buf - Beginning of the packet packet to send (with IPv6 + protocol + * headers) + * buflen - Length of data to send (include IPv6 and protocol headers) + * destmac - The IEEE802.15.4 MAC address of the destination + * + * Returned Value: + * Ok is returned on success; Othewise a negated errno value is returned. + * This function is expected to fail if the driver is not an IEEE802.15.4 + * MAC network driver. In that case, the UDP/TCP will fall back to normal + * IPv4/IPv6 formatting. + * + * Assumptions: + * Called with the network locked. + * + ****************************************************************************/ + +int sixlowpan_queue_frames(FAR struct ieee802154_driver_s *ieee, + FAR const struct ipv6_hdr_s *ipv6hdr, + FAR const void *buf, size_t buflen, + FAR const struct rimeaddr_s *destmac); /**************************************************************************** * Name: sixlowpan_hc06_initialize @@ -359,7 +594,7 @@ void sixlowpan_hc06_initialize(void); #endif /**************************************************************************** - * Name: sixlowpan_hc06_initialize + * Name: sixlowpan_compresshdr_hc06 * * Description: * Compress IP/UDP header @@ -375,8 +610,11 @@ void sixlowpan_hc06_initialize(void); * compression * * Input Parameters: - * dev - A reference to the IEE802.15.4 network device state - * destaddr - L2 destination address, needed to compress IP dest + * ieee - A reference to the IEE802.15.4 network device state + * ipv6 - The IPv6 header to be compressed + * destmac - L2 destination address, needed to compress the IP + * destination field + * fptr - Pointer to frame to be compressed. * * Returned Value: * None @@ -384,12 +622,14 @@ void sixlowpan_hc06_initialize(void); ****************************************************************************/ #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC06 -void sixlowpan_compresshdr_hc06(FAR struct net_driver_s *dev, - FAR struct rimeaddr_s *destaddr); +void sixlowpan_compresshdr_hc06(FAR struct ieee802154_driver_s *ieee, + FAR const struct ipv6_hdr_s *ipv6, + FAR const struct rimeaddr_s *destmac, + FAR uint8_t *fptr); #endif /**************************************************************************** - * Name: sixlowpan_hc06_initialize + * Name: sixlowpan_uncompresshdr_hc06 * * Description: * Uncompress HC06 (i.e., IPHC and LOWPAN_UDP) headers and put them in @@ -398,14 +638,16 @@ void sixlowpan_compresshdr_hc06(FAR struct net_driver_s *dev, * This function is called by the input function when the dispatch is HC06. * We process the packet in the rime buffer, uncompress the header fields, * and copy the result in the sixlowpan buffer. At the end of the - * decompression, g_rime_hdrlen and g_uncompressed_hdrlen are set to the + * decompression, g_frame_hdrlen and g_uncompressed_hdrlen are set to the * appropriate values * * Input Parmeters: - * dev - A reference to the IEE802.15.4 network device state - * iplen - Equal to 0 if the packet is not a fragment (IP length is then - * inferred from the L2 length), non 0 if the packet is a 1st - * fragment. + * ieee - A reference to the IEE802.15.4 network device state + * iplen - Equal to 0 if the packet is not a fragment (IP length is then + * inferred from the L2 length), non 0 if the packet is a first + * fragment. + * iob - Pointer to the IOB containing the received frame. + * payptr - Pointer to the frame data payload. * * Returned Value: * None @@ -413,8 +655,9 @@ void sixlowpan_compresshdr_hc06(FAR struct net_driver_s *dev, ****************************************************************************/ #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC06 -void sixlowpan_uncompresshdr_hc06(FAR struct net_driver_s *dev, - uint16_t iplen); +void sixlowpan_uncompresshdr_hc06(FAR struct ieee802154_driver_s *ieee, + uint16_t iplen, FAR struct iob_s *iob, + FAR uint8_t *payptr); #endif /**************************************************************************** @@ -428,9 +671,11 @@ void sixlowpan_uncompresshdr_hc06(FAR struct net_driver_s *dev, * uip_buf buffer. * * Input Parmeters: - * dev - A reference to the IEE802.15.4 network device state - * destaddr - L2 destination address, needed to compress the IP - * destination field + * ieee - A reference to the IEE802.15.4 network device state + * ipv6 - The IPv6 header to be compressed + * destmac - L2 destination address, needed to compress the IP + * destination field + * fptr - Pointer to frame to be compressed. * * Returned Value: * None @@ -438,8 +683,10 @@ void sixlowpan_uncompresshdr_hc06(FAR struct net_driver_s *dev, ****************************************************************************/ #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC1 -void sixlowpan_compresshdr_hc1(FAR struct net_driver_s *dev, - FAR struct rimeaddr_s *destaddr); +void sixlowpan_compresshdr_hc1(FAR struct ieee802154_driver_s *ieee, + FAR const struct ipv6_hdr_s *ipv6, + FAR const struct rimeaddr_s *destmac, + FAR uint8_t *fptr); #endif /**************************************************************************** @@ -451,14 +698,16 @@ void sixlowpan_compresshdr_hc1(FAR struct net_driver_s *dev, * This function is called by the input function when the dispatch is * HC1. It processes the packet in the rime buffer, uncompresses the * header fields, and copies the result in the sixlowpan buffer. At the - * end of the decompression, g_rime_hdrlen and uncompressed_hdr_len + * end of the decompression, g_frame_hdrlen and uncompressed_hdr_len * are set to the appropriate values * * Input Parameters: - * dev - A reference to the IEE802.15.4 network device state + * ieee - A reference to the IEE802.15.4 network device state * iplen - Equal to 0 if the packet is not a fragment (IP length is then - * inferred from the L2 length), non 0 if the packet is a 1st + * inferred from the L2 length), non 0 if the packet is a first * fragment. + * iob - Pointer to the IOB containing the received frame. + * payptr - Pointer to the frame data payload. * * Returned Value: * None @@ -466,20 +715,40 @@ void sixlowpan_compresshdr_hc1(FAR struct net_driver_s *dev, ****************************************************************************/ #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC1 -void sixlowpan_uncompresshdr_hc1(FAR struct net_driver_s *dev, - uint16_t ip_len); +int sixlowpan_uncompresshdr_hc1(FAR struct ieee802154_driver_s *ieee, + uint16_t ip_len, FAR struct iob_s *iob, + FAR uint8_t *payptr); #endif /**************************************************************************** - * Name: sixlowpan_frame_hdralloc + * Name: sixlowpan_islinklocal, sixlowpan_ipfromrime, sixlowpan_rimefromip, + * and sixlowpan_ismacbased * * Description: - * Allocate space for a header within the frame buffer (i_frame). + * sixlowpan_ipfromrime: Create a link local IPv6 address from a rime + * address. + * + * sixlowpan_rimefromip: Extract the rime address from a link local IPv6 + * address. + * + * sixlowpan_islinklocal and sixlowpan_ismacbased will return true for + * address created in this fashion. + * + * 128 112 96 80 64 48 32 16 + * ---- ---- ---- ---- ---- ---- ---- ---- + * fe80 0000 0000 0000 xxxx 0000 0000 0000 2-byte Rime address (VALID?) + * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte Rime address * ****************************************************************************/ -int sixlowpan_frame_hdralloc(FAR struct ieee802154_driver_s *ieee, - int size); +#define sixlowpan_islinklocal(ipaddr) ((ipaddr)[0] == NTOHS(0xfe80)) + +void sixlowpan_ipfromrime(FAR const struct rimeaddr_s *rime, + net_ipv6addr_t ipaddr); +void sixlowpan_rimefromip(const net_ipv6addr_t ipaddr, + FAR struct rimeaddr_s *rime); +bool sixlowpan_ismacbased(const net_ipv6addr_t ipaddr, + FAR const struct rimeaddr_s *rime); #endif /* CONFIG_NET_6LOWPAN */ #endif /* _NET_SIXLOWPAN_SIXLOWPAN_INTERNAL_H */ diff --git a/net/sixlowpan/sixlowpan_send.c b/net/sixlowpan/sixlowpan_send.c index 9a7b0095cdb..98cc577f5f5 100644 --- a/net/sixlowpan/sixlowpan_send.c +++ b/net/sixlowpan/sixlowpan_send.c @@ -4,18 +4,6 @@ * Copyright (C) 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * - * Parts of this file derive from Contiki: - * - * Copyright (c) 2008, Swedish Institute of Computer Science. - * All rights reserved. - * Authors: Adam Dunkels - * Nicolas Tsiftes - * Niclas Finne - * Mathilde Durvy - * Julien Abeille - * Joakim Eriksson - * Joel Hoglund - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -23,23 +11,25 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ @@ -49,160 +39,182 @@ #include -#include +#include #include #include #include -#include "nuttx/net/netdev.h" -#include "nuttx/net/ip.h" -#include "nuttx/net/tcp.h" -#include "nuttx/net/udp.h" -#include "nuttx/net/icmpv6.h" -#include "nuttx/net/sixlowpan.h" +#include +#include +#include #include "netdev/netdev.h" -#include "socket/socket.h" -#include "tcp/tcp.h" -#include "udp/udp.h" +#include "devif/devif.h" + #include "sixlowpan/sixlowpan_internal.h" #ifdef CONFIG_NET_6LOWPAN +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* These are temporary stubs. Something like this would be needed to + * monitor the health of a IPv6 neighbor. + */ + +#define neighbor_reachable(dev) +#define neighbor_notreachable(dev) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This is the state data provided to the send interrupt logic. No actions + * can be taken until the until we receive the TX poll, then we can call + * sixlowpan_queue_frames() with this data strurcture. + */ + +struct sixlowpan_send_s +{ + FAR struct devif_callback_s *s_cb; /* Reference to callback instance */ + sem_t s_waitsem; /* Supports waiting for driver events */ + int s_result; /* The result of the transfer */ + uint16_t s_timeout; /* Send timeout in deciseconds */ + systime_t s_time; /* Last send time for determining timeout */ + FAR const struct ipv6_hdr_s *s_ipv6hdr; /* IPv6 header, followed by UDP or TCP header. */ + FAR const struct rimeaddr_s *s_destmac; /* Destination MAC address */ + FAR const void *s_buf; /* Data to send */ + size_t s_len; /* Length of data in buf */ +}; + /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: sixlowpan_set_pktattrs + * Function: send_timeout * * Description: - * Setup some packet buffer attributes + * Check for send timeout. * * Input Parameters: - * ieee - Pointer to IEEE802.15.4 MAC driver structure. - * ipv6 - Pointer to the IPv6 header to "compress" + * sinfo - Send state structure reference * * Returned Value: - * None + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * The network is locked * ****************************************************************************/ -static void sixlowpan_set_pktattrs(FAR struct ieee802154_driver_s *ieee, - FAR const struct ipv6_hdr_s *ipv6) +static inline bool send_timeout(FAR struct sixlowpan_send_s *sinfo) { - int attr = 0; - - /* Set protocol in NETWORK_ID */ - - ieee->i_pktattrs[PACKETBUF_ATTR_NETWORK_ID] = ipv6->proto; - - /* Assign values to the channel attribute (port or type + code) */ - - if (ipv6->proto == IP_PROTO_UDP) - { - FAR struct udp_hdr_s *udp = &((FAR struct ipv6udp_hdr_s *)ipv6)->udp; - - attr = udp->srcport; - if (udp->destport < attr) - { - attr = udp->destport; - } - } - else if (ipv6->proto == IP_PROTO_TCP) - { - FAR struct tcp_hdr_s *tcp = &((FAR struct ipv6tcp_hdr_s *)ipv6)->tcp; - - attr = tcp->srcport; - if (tcp->destport < attr) - { - attr = tcp->destport; - } - } - else if (ipv6->proto == IP_PROTO_ICMP6) - { - FAR struct icmpv6_iphdr_s *icmp = &((FAR struct ipv6icmp_hdr_s *)ipv6)->icmp; - - attr = icmp->type << 8 | icmp->code; - } - - ieee->i_pktattrs[PACKETBUF_ATTR_CHANNEL] = attr; -} - -/**************************************************************************** - * Name: sixlowpan_compress_ipv6hdr - * - * Description: - * IPv6 dispatch "compression" function. Packets "Compression" when only - * IPv6 dispatch is used - * - * There is no compression in this case, all fields are sent - * inline. We just add the IPv6 dispatch byte before the packet. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | IPv6 Dsp | IPv6 header and payload ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * Input Parameters: - * ieee - Pointer to IEEE802.15.4 MAC driver structure. - * ipv6 - Pointer to the IPv6 header to "compress" - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void sixlowpan_compress_ipv6hdr(FAR struct ieee802154_driver_s *ieee, - FAR const struct ipv6_hdr_s *ipv6) -{ - /* Indicate the IPv6 dispatch and length */ - - *ieee->i_rimeptr = SIXLOWPAN_DISPATCH_IPV6; - ieee->i_rime_hdrlen += SIXLOWPAN_IPV6_HDR_LEN; - - /* Copy the IPv6 header and adjust pointers */ - - memcpy(ieee->i_rimeptr + ieee->i_rime_hdrlen, ipv6, IPv6_HDRLEN); - ieee->i_rime_hdrlen += IPv6_HDRLEN; - ieee->i_uncomp_hdrlen += IPv6_HDRLEN; -} - -/**************************************************************************** - * Name: sixlowpan_send_frame - * - * Description: - * Send one frame when the IEEE802.15.4 MAC device next polls. - * - * Input Parameters: - * ieee - Pointer to IEEE802.15.4 MAC driver structure. - * ipv6 - Pointer to the IPv6 header to "compress" - * - * Returned Value: - * None - * - ****************************************************************************/ - -static int sixlowpan_send_frame(FAR struct ieee802154_driver_s *ieee) -{ - /* Prepare the frame */ -#warning Missing logic - /* Set up for the TX poll */ - /* When polled, then we need to call sixlowpan_framecreate() to create the - * frame and copy the payload data into the frame. + /* Check for a timeout. Zero means none and, in that case, we will let + * the send wait forever. */ -#if 0 /* Just some notes of what needs to be done in interrupt handler */ - framer_hdrlen = sixlowpan_createframe(ieee, ieee->i_panid); - memcpy(ieee->i_rimeptr + ieee->i_rime_hdrlen, (uint8_t *)ipv6 + ieee->i_uncomp_hdrlen, len - ieee->i_uncomp_hdrlen); - dev->i_framelen = len - ieee->i_uncomp_hdrlen + ieee->i_rime_hdrlen; -#endif -#warning Missing logic - /* Notify the IEEE802.14.5 MAC driver that we have data to be sent */ -#warning Missing logic - /* Wait for the transfer to complete */ -#warning Missing logic - return -ENOSYS; + + if (sinfo->s_timeout != 0) + { + /* Check if the configured timeout has elapsed */ + + systime_t timeo_ticks = DSEC2TICK(sinfo->s_timeout); + systime_t elapsed = clock_systimer() - sinfo->s_time; + + if (elapsed >= timeo_ticks) + { + return true; + } + } + + /* No timeout */ + + return false; +} + +/**************************************************************************** + * Function: send_interrupt + * + * Description: + * This function is called from the interrupt level to perform the actual + * send operation when polled by the lower, device interfacing layer. + * + * Parameters: + * dev - The structure of the network driver that caused the interrupt + * conn - The connection structure associated with the socket + * flags - Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static uint16_t send_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct sixlowpan_send_s *sinfo = (FAR struct sixlowpan_send_s *)pvpriv; + + ninfo("flags: %04x: %d\n", flags); + + /* Check if the IEEE802.15.4 went down */ + + if ((flags & NETDEV_DOWN) != 0) + { + ninfo("Device is down\n"); + sinfo->s_result = -ENOTCONN; + goto end_wait; + } + + /* Check for a poll for TX data. */ + + if ((flags & WPAN_NEWDATA) == 0) + { + DEBUGASSERT((flags & WPAN_POLL) != 0); + + /* Transfer the frame listto the IEEE802.15.4 MAC device */ + + sinfo->s_result = + sixlowpan_queue_frames((FAR struct ieee802154_driver_s *)dev, + sinfo->s_ipv6hdr, sinfo->s_buf, sinfo->s_len, + sinfo->s_destmac); + + flags &= ~WPAN_POLL; + neighbor_reachable(dev); + goto end_wait; + } + + /* Check for a timeout. */ + + if (send_timeout(sinfo)) + { + /* Yes.. report the timeout */ + + nwarn("WARNING: SEND timeout\n"); + sinfo->s_result = -ETIMEDOUT; + neighbor_notreachable(dev); + goto end_wait; + } + + /* Continue waiting */ + + return flags; + +end_wait: + /* Do not allow any further callbacks */ + + sinfo->s_cb->flags = 0; + sinfo->s_cb->priv = NULL; + sinfo->s_cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&sinfo->s_waitsem); + return flags; } /**************************************************************************** @@ -217,19 +229,19 @@ static int sixlowpan_send_frame(FAR struct ieee802154_driver_s *ieee) * it to be sent on an 802.15.4 network using 6lowpan. Called from common * UDP/TCP send logic. * - * The payload data is in the caller 'buf' and is of length 'len'. - * Compressed headers will be added and if necessary the packet is - * fragmented. The resulting packet/fragments are put in dev->d_buf and - * the first frame will be delivered to the 802.15.4 MAC. via ieee->i_frame. - * - * Input Parmeters: + * The payload data is in the caller 'buf' and is of length 'len'. + * Compressed headers will be added and if necessary the packet is + * fragmented. The resulting packet/fragments are put in ieee->i_framelist + * and the entire list of frames will be delivered to the 802.15.4 MAC via + * ieee->i_framelist. * * Input Parameters: - * dev - The IEEE802.15.4 MAC network driver interface. - * ipv6 - IPv6 plus TCP or UDP headers. - * buf - Data to send - * len - Length of data to send - * raddr - The IEEE802.15.4 MAC address of the destination + * dev - The IEEE802.15.4 MAC network driver interface. + * ipv6hdr - IPv6 header followed by TCP or UDP header. + * buf - Data to send + * len - Length of data to send + * destmac - The IEEE802.15.4 MAC address of the destination + * timeout - Send timeout in deciseconds * * Returned Value: * Ok is returned on success; Othewise a negated errno value is returned. @@ -243,200 +255,71 @@ static int sixlowpan_send_frame(FAR struct ieee802154_driver_s *ieee) ****************************************************************************/ int sixlowpan_send(FAR struct net_driver_s *dev, - FAR const struct ipv6_hdr_s *ipv6, FAR const void *buf, - size_t len, FAR const struct rimeaddr_s *raddr) + FAR const struct ipv6_hdr_s *ipv6hdr, FAR const void *buf, + size_t len, FAR const struct rimeaddr_s *destmac, + uint16_t timeout) { - FAR struct ieee802154_driver_s *ieee = (FAR struct ieee802154_driver_s *)dev; + struct sixlowpan_send_s sinfo; - int framer_hdrlen; /* Framer header length */ - struct rimeaddr_s dest; /* The MAC address of the destination of the packet */ - uint16_t outlen = 0; /* Number of bytes processed. */ + /* Initialize the send state structure */ - /* Initialize device-specific data */ + sem_init(&sinfo.s_waitsem, 0, 0); + (void)sem_setprotocol(&sinfo.s_waitsem, SEM_PRIO_NONE); - FRAME_RESET(ieee); - ieee->i_uncomp_hdrlen = 0; - ieee->i_rime_hdrlen = 0; - /* REVISIT: Do I need this rimeptr? */ - ieee->i_rimeptr = &dev->d_buf[PACKETBUF_HDR_SIZE]; + sinfo.s_result = -EBUSY; + sinfo.s_timeout = timeout; + sinfo.s_time = clock_systimer(); + sinfo.s_ipv6hdr = ipv6hdr; + sinfo.s_destmac = destmac; + sinfo.s_buf = buf; + sinfo.s_len = len; - /* Reset rime buffer, packet buffer metatadata */ - - memset(ieee->i_pktattrs, 0, PACKETBUF_NUM_ATTRS * sizeof(uint16_t)); - memset(ieee->i_pktaddrs, 0, PACKETBUF_NUM_ADDRS * sizeof(struct rimeaddr_s)); - - ieee->i_pktattrs[PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS] = - CONFIG_NET_6LOWPAN_MAX_MACTRANSMITS; - -#ifdef CONFIG_NET_6LOWPAN_SNIFFER - if (g_sixlowpan_sniffer != NULL) + net_lock(); + if (len > 0) { - /* Reset rime buffer, packet buffer metatadata */ + /* Allocate resources to receive a callback. + * + * The second parameter is NULL meaning that we can get only + * device related events, no connect-related events. + */ - memset(ieee->i_pktattrs, 0, PACKETBUF_NUM_ATTRS * sizeof(uint16_t)); - memset(ieee->i_pktaddrs, 0, PACKETBUF_NUM_ADDRS * sizeof(struct rimeaddr_s)); - - ieee->i_pktattrs[PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS] = - CONFIG_NET_6LOWPAN_MAX_MACTRANSMITS; - - /* Call the attribution when the callback comes, but set attributes here */ - - sixlowpan_set_pktattrs(ieee, ipv6); - } -#endif - - /* Reset rime buffer, packet buffer metatadata */ - - memset(ieee->i_pktattrs, 0, PACKETBUF_NUM_ATTRS * sizeof(uint16_t)); - memset(ieee->i_pktaddrs, 0, PACKETBUF_NUM_ADDRS * sizeof(struct rimeaddr_s)); - - ieee->i_pktattrs[PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS] = - CONFIG_NET_6LOWPAN_MAX_MACTRANSMITS; - - /* Set stream mode for all TCP packets, except FIN packets. */ - - if (ipv6->proto == IP_PROTO_TCP) - { - FAR const struct tcp_hdr_s *tcp = &((FAR const struct ipv6tcp_hdr_s *)ipv6)->tcp; - - if ((tcp->flags & TCP_FIN) == 0 && - (tcp->flags & TCP_CTL) != TCP_ACK) + sinfo.s_cb = devif_callback_alloc(dev, NULL); + if (sinfo.s_cb != NULL) { - ieee->i_pktattrs[PACKETBUF_ATTR_PACKET_TYPE] = PACKETBUF_ATTR_PACKET_TYPE_STREAM; - } - else if ((tcp->flags & TCP_FIN) == TCP_FIN) - { - ieee->i_pktattrs[PACKETBUF_ATTR_PACKET_TYPE] = PACKETBUF_ATTR_PACKET_TYPE_STREAM_END; + int ret; + + /* Set up the callback in the connection */ + + sinfo.s_cb->flags = (NETDEV_DOWN | WPAN_POLL); + sinfo.s_cb->priv = (FAR void *)&sinfo; + sinfo.s_cb->event = send_interrupt; + + /* Notify the the IEEE802.15.4 MAC that we have data to send. */ + + netdev_txnotify_dev(dev); + + /* Wait for the send to complete or an error to occur: NOTES: (1) + * net_lockedwait will also terminate if a signal is received, (2) interrupts + * may be disabled! They will be re-enabled while the task sleeps and + * automatically re-enabled when the task restarts. + */ + + ret = net_lockedwait(&sinfo.s_waitsem); + if (ret < 0) + { + sinfo.s_result = -get_errno(); + } + + /* Make sure that no further interrupts are processed */ + + devif_dev_callback_free(dev, sinfo.s_cb); } } - /* The destination address will be tagged to each outbound packet. If the - * argument raddr is NULL, we are sending a broadcast packet. - */ + sem_destroy(&sinfo.s_waitsem); + net_unlock(); - if (raddr == NULL) - { - memset(&dest, 0, sizeof(struct rimeaddr_s)); - } - else - { - rimeaddr_copy(&dest, (FAR const struct rimeaddr_s *)raddr); - } - - ninfo("Sending packet len %d\n", len); - -#ifndef CONFIG_NET_6LOWPAN_COMPRESSION_IPv6 - if (len >= CONFIG_NET_6LOWPAN_COMPRESSION_THRESHOLD) - { - /* Try to compress the headers */ - -#if defined(CONFIG_NET_6LOWPAN_COMPRESSION_HC1) - sixlowpan_compresshdr_hc1(dev, &dest); -#elif defined(CONFIG_NET_6LOWPAN_COMPRESSION_HC06) - sixlowpan_compresshdr_hc06(dev, &dest); -#else -# error No compression specified -#endif - } - else -#endif /* !CONFIG_NET_6LOWPAN_COMPRESSION_IPv6 */ - { - /* Small.. use IPv6 dispatch (no compression) */ - - sixlowpan_compress_ipv6hdr(ieee, ipv6); - } - - ninfo("Header of len %d\n", ieee->i_rime_hdrlen); - - rimeaddr_copy(&ieee->i_pktaddrs[PACKETBUF_ADDR_RECEIVER], &dest); - - /* Pre-calculate frame header length. */ - - framer_hdrlen = sixlowpan_hdrlen(ieee, ieee->i_panid); - if (framer_hdrlen < 0) - { - /* Failed to determine the size of the header failed. */ - - nerr("ERROR: sixlowpan_framecreate() failed: %d\n", framer_hdrlen); - return framer_hdrlen; - } - - /* Check if we need to fragment the packet into several frames */ - - if ((int)len - (int)ieee->i_uncomp_hdrlen > - (int)CONFIG_NET_6LOWPAN_MAXPAYLOAD - framer_hdrlen - - (int)ieee->i_rime_hdrlen) - { -#if CONFIG_NET_6LOWPAN_FRAG - /* The outbound IPv6 packet is too large to fit into a single 15.4 - * packet, so we fragment it into multiple packets and send them. - * The first fragment contains frag1 dispatch, then - * IPv6/HC1/HC06/HC_UDP dispatchs/headers. - * The following fragments contain only the fragn dispatch. - */ - - ninfo("Fragmentation sending packet len %d\n", len); - - /* Create 1st Fragment */ -# warning Missing logic - - /* Move HC1/HC06/IPv6 header */ -# warning Missing logic - - /* FRAG1 dispatch + header - * Note that the length is in units of 8 bytes - */ -# warning Missing logic - - /* Copy payload and send */ -# warning Missing logic - - /* Check TX result. */ -# warning Missing logic - - /* Set outlen to what we already sent from the IP payload */ -# warning Missing logic - - /* Create following fragments - * Datagram tag is already in the buffer, we need to set the - * FRAGN dispatch and for each fragment, the offset - */ -# warning Missing logic - - while (outlen < len) - { - /* Copy payload and send */ -# warning Missing logic - - ninfo("sixlowpan output: fragment offset %d, len %d, tag %d\n", - outlen >> 3, g_rime_payloadlen, g_mytag); - -# warning Missing logic - sixlowpan_send_frame(ieee); - - /* Check tx result. */ -# warning Missing logic - } - - return -ENOSYS; -#else - nerr("ERROR: Packet too large: %d\n", len); - nerr(" Cannot to be sent without fragmentation support\n"); - nerr(" dropping packet\n"); - - return -E2BIG; -#endif - } - else - { - /* The packet does not need to be fragmented just copy the "payload" - * and send in one frame. - */ - - return sixlowpan_send_frame(ieee); - } - - return -ENOSYS; + return (sinfo.s_result < 0 ? sinfo.s_result : len); } #endif /* CONFIG_NET_6LOWPAN */ diff --git a/net/sixlowpan/sixlowpan_tcpsend.c b/net/sixlowpan/sixlowpan_tcpsend.c index 9928bc3d115..1ad8068a317 100644 --- a/net/sixlowpan/sixlowpan_tcpsend.c +++ b/net/sixlowpan/sixlowpan_tcpsend.c @@ -39,21 +39,92 @@ #include +#include #include #include #include #include "nuttx/net/netdev.h" -#include "nuttx/net/tcp.h" -#include "nuttx/net/sixlowpan.h" +#include "nuttx/net/netstats.h" #include "netdev/netdev.h" #include "socket/socket.h" #include "tcp/tcp.h" +#include "utils/utils.h" #include "sixlowpan/sixlowpan_internal.h" #if defined(CONFIG_NET_6LOWPAN) && defined(CONFIG_NET_TCP) +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Buffer access helpers */ + +#define IPv6BUF(dev) ((FAR struct ipv6_hdr_s *)((dev)->d_buf)) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sixlowpan_tcp_chksum + * + * Description: + * Perform the checksum calcaultion over the IPv6, protocol headers, and + * data payload as necessary. + * + * Input Parameters: + * ipv6tcp - A reference to a structure containing the IPv6 and TCP headers. + * buf - The beginning of the payload data + * buflen - The length of the payload data. + * + * Returned Value: + * The calculated checksum + * + ****************************************************************************/ + +static uint16_t sixlowpan_tcp_chksum(FAR struct ipv6tcp_hdr_s *ipv6tcp, + FAR const uint8_t *buf, uint16_t buflen) +{ + uint16_t upperlen; + uint16_t sum; + + /* The length reported in the IPv6 header is the length of the payload + * that follows the header. + */ + + upperlen = ((uint16_t)ipv6tcp->ipv6.len[0] << 8) + ipv6tcp->ipv6.len[1]; + + /* Verify some minimal assumptions */ + + if (upperlen > CONFIG_NET_6LOWPAN_MTU) + { + return 0; + } + + /* The checksum is calculated starting with a pseudo-header of IPv6 header + * fields according to the IPv6 standard, which consists of the source + * and destination addresses, the packet length and the next header field. + */ + + sum = upperlen + ipv6tcp->ipv6.proto; + + /* Sum IP source and destination addresses. */ + + sum = chksum(sum, (FAR uint8_t *)ipv6tcp->ipv6.srcipaddr, + 2 * sizeof(net_ipv6addr_t)); + + /* Sum the TCP header */ + + sum = chksum(sum, (FAR uint8_t *)&ipv6tcp->tcp, TCP_HDRLEN); + + /* Sum payload data. */ + + sum = chksum(sum, buf, buflen); + return (sum == 0) ? 0xffff : htons(sum); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -68,7 +139,7 @@ * Parameters: * psock - An instance of the internal socket structure. * buf - Data to send - * len - Length of data to send + * bulen - Length of data to send * * Returned Value: * On success, returns the number of characters sent. On error, @@ -82,14 +153,19 @@ ****************************************************************************/ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf, - size_t len) + size_t buflen) { FAR struct tcp_conn_s *conn; FAR struct net_driver_s *dev; struct ipv6tcp_hdr_s ipv6tcp; - struct rimeaddr_s dest; + struct rimeaddr_s destmac; + uint16_t timeout; + uint16_t iplen; int ret; + ninfo("buflen %lu\n", (unsigned long)buflen); + sixlowpan_dumpbuffer("Outgoing TCP payload", buf, buflen); + DEBUGASSERT(psock != NULL && psock->s_crefs > 0); DEBUGASSERT(psock->s_type == SOCK_STREAM); @@ -114,7 +190,7 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf, conn = (FAR struct tcp_conn_s *)psock->s_conn; DEBUGASSERT(conn != NULL); -#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) +#ifdef CONFIG_NET_IPv4 /* Ignore if not IPv6 domain */ if (conn->domain != PF_INET6) @@ -128,14 +204,22 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf, #ifdef CONFIG_NETDEV_MULTINIC dev = netdev_findby_ipv6addr(conn->u.ipv6.laddr, conn->u.ipv6.raddr); - if (dev == NULL || dev->d_lltype != NET_LL_IEEE805154) +#ifdef CONFIG_NETDEV_MULTILINK + if (dev == NULL || dev->d_lltype != NET_LL_IEEE802154) +#else + if (dev == NULL) +#endif { nwarn("WARNING: Not routable or not IEEE802.15.4 MAC\n"); return (ssize_t)-ENETUNREACH; } #else dev = netdev_findby_ipv6addr(conn->u.ipv6.raddr); +#ifdef CONFIG_NETDEV_MULTILINK + if (dev == NULL || dev->d_lltype != NET_LL_IEEE802154) +#else if (dev == NULL) +#endif { nwarn("WARNING: Not routable\n"); return (ssize_t)-ENETUNREACH; @@ -154,27 +238,186 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf, #endif /* Initialize the IPv6/TCP headers */ -#warning Missing logic + + /* Initialize the IPv6/UDP headers */ + + ipv6tcp.ipv6.vtc = 0x60; + ipv6tcp.ipv6.tcf = 0x00; + ipv6tcp.ipv6.flow = 0x00; + ipv6tcp.ipv6.proto = IP_PROTO_TCP; + ipv6tcp.ipv6.ttl = IP_TTL; + + /* The IPv6 header length field does not include the size of IPv6 IP + * header. + */ + + iplen = buflen + TCP_HDRLEN; + ipv6tcp.ipv6.len[0] = (iplen >> 8); + ipv6tcp.ipv6.len[1] = (iplen & 0xff); + + /* Copy the source and destination addresses */ + + net_ipv6addr_hdrcopy(ipv6tcp.ipv6.srcipaddr, conn->u.ipv6.laddr); + net_ipv6addr_hdrcopy(ipv6tcp.ipv6.destipaddr, conn->u.ipv6.raddr); + + ninfo("IPv6 length: %d\n", + ((int)ipv6tcp.ipv6.len[0] << 8) + ipv6tcp.ipv6.len[1]); + +#ifdef CONFIG_NET_STATISTICS + g_netstats.ipv6.sent++; +#endif + + /* Initialize the TCP header */ + + ipv6tcp.tcp.srcport = conn->lport; /* Local port */ + ipv6tcp.tcp.destport = conn->rport; /* Connected remote port */ + + memcpy(ipv6tcp.tcp.ackno, conn->rcvseq, 4); /* ACK number */ + memcpy(ipv6tcp.tcp.seqno, conn->sndseq, 4); /* Sequence number */ + + ipv6tcp.tcp.tcpoffset = (TCP_HDRLEN / 4) << 4; /* No optdata */ + ipv6tcp.tcp.urgp[0] = 0; /* No urgent data */ + ipv6tcp.tcp.urgp[1] = 0; + + /* Set the TCP window */ + + if (conn->tcpstateflags & TCP_STOPPED) + { + /* If the connection has issued TCP_STOPPED, we advertise a zero + * window so that the remote host will stop sending data. + */ + + ipv6tcp.tcp.wnd[0] = 0; + ipv6tcp.tcp.wnd[1] = 0; + } + else + { + ipv6tcp.tcp.wnd[0] = ((NET_DEV_RCVWNDO(dev)) >> 8); + ipv6tcp.tcp.wnd[1] = ((NET_DEV_RCVWNDO(dev)) & 0xff); + } + + /* Calculate TCP checksum. */ + + ipv6tcp.tcp.tcpchksum = 0; + ipv6tcp.tcp.tcpchksum = ~sixlowpan_tcp_chksum(&ipv6tcp, buf, buflen); + + ninfo("Outgoing TCP packet length: %d bytes\n", iplen + IPv6_HDRLEN); + +#ifdef CONFIG_NET_STATISTICS + g_netstats.tcp.sent++; +#endif /* Set the socket state to sending */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); - /* Get the Rime MAC address of the destination */ -#warning Missing logic + /* Get the Rime MAC address of the destination This assumes an encoding + * of the MAC address in the IPv6 address. + */ + + sixlowpan_rimefromip(conn->u.ipv6.raddr, &destmac); /* If routable, then call sixlowpan_send() to format and send the 6loWPAN * packet. */ +#ifdef CONFIG_NET_SOCKOPTS + timeout = psock->s_sndtimeo; +#else + timeout = 0; +#endif + ret = sixlowpan_send(dev, (FAR const struct ipv6_hdr_s *)&ipv6tcp, - buf, len, &dest); + buf, buflen, &destmac, timeout); if (ret < 0) { nerr("ERROR: sixlowpan_send() failed: %d\n", ret); } + /* Set the socket state to idle */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); return ret; } +/**************************************************************************** + * Function: sixlowpan_tcp_send + * + * Description: + * TCP output comes through three different mechansims. Either from: + * + * 1. TCP socket output. For the case of TCP output to an + * IEEE802.15.4, the TCP output is caught in the socket + * send()/sendto() logic and and redirected to psock_6lowpan_tcp_send(). + * 2. TCP output from the TCP state machine. That will occur + * during TCP packet processing by the TCP state meachine. + * 3. TCP output resulting from TX or timer polling + * + * Cases 2 and 3 will be handled here. Logic in ipv6_tcp_input(), + * devif_poll(), and devif_timer() detect if (1) an attempt to return with + * d_len > 0 and (2) that the device is an IEEE802.15.4 MAC network + * driver. Under those conditions, this function will be called to create + * the IEEE80215.4 frames. + * + * Parameters: + * dev - An instance of nework device state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called with the network locked. + * + ****************************************************************************/ + +void sixlowpan_tcp_send(FAR struct net_driver_s *dev) +{ + DEBUGASSERT(dev != NULL && dev->d_len > 0); + + /* Double check */ + + ninfo("d_len %u\n", dev->d_len); + sixlowpan_dumpbuffer("Outgoing TCP packet", + (FAR const uint8_t *)IPv6BUF(dev), dev->d_len); + + if (dev != NULL && dev->d_len > 0) + { + FAR struct ipv6_hdr_s *ipv6hdr; + + /* The IPv6 header followed by a TCP headers should lie at the + * beginning of d_buf since there is no link layer protocol header + * and the TCP state machine should only response with TCP packets. + */ + + ipv6hdr = (FAR struct ipv6_hdr_s *)(dev->d_buf); + + /* The TCP data payload should follow the IPv6 header plus the + * protocol header. + */ + + if (ipv6hdr->proto != IP_PROTO_TCP) + { + nwarn("WARNING: Expected TCP protoype: %u\n", ipv6hdr->proto); + } + else + { + struct rimeaddr_s destmac; + + /* Get the Rime MAC address of the destination. This assumes an + * encoding of the MAC address in the IPv6 address. + */ + + sixlowpan_rimefromip(ipv6hdr->destipaddr, &destmac); + + /* Convert the outgoing packet into a frame list. */ + + (void)sixlowpan_queue_frames( + (FAR struct ieee802154_driver_s *)dev, ipv6hdr, + dev->d_buf, dev->d_len, &destmac); + } + } + + dev->d_len = 0; +} + #endif /* CONFIG_NET_6LOWPAN && CONFIG_NET_TCP */ diff --git a/net/sixlowpan/sixlowpan_udpsend.c b/net/sixlowpan/sixlowpan_udpsend.c index 9946516e009..717df424a27 100644 --- a/net/sixlowpan/sixlowpan_udpsend.c +++ b/net/sixlowpan/sixlowpan_udpsend.c @@ -39,25 +39,295 @@ #include +#include +#include #include #include #include #include "nuttx/net/netdev.h" -#include "nuttx/net/udp.h" -#include "nuttx/net/sixlowpan.h" +#include "nuttx/net/netstats.h" #include "netdev/netdev.h" #include "socket/socket.h" #include "udp/udp.h" +#include "utils/utils.h" #include "sixlowpan/sixlowpan_internal.h" #if defined(CONFIG_NET_6LOWPAN) && defined(CONFIG_NET_UDP) +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sixlowpan_udp_chksum + * + * Description: + * Perform the checksum calcaultion over the IPv6, protocol headers, and + * data payload as necessary. + * + * Input Parameters: + * ipv6udp - A reference to a structure containing the IPv6 and UDP headers. + * buf - The beginning of the payload data + * buflen - The length of the payload data. + * + * Returned Value: + * The calculated checksum + * + ****************************************************************************/ + +#ifdef CONFIG_NET_UDP_CHECKSUMS +static uint16_t sixlowpan_udp_chksum(FAR struct ipv6udp_hdr_s *ipv6udp, + FAR const uint8_t *buf, uint16_t buflen) +{ + uint16_t upperlen; + uint16_t sum; + + /* The length reported in the IPv6 header is the length of the payload + * that follows the header. + */ + + upperlen = ((uint16_t)ipv6udp->ipv6.len[0] << 8) + ipv6udp->ipv6.len[1]; + + /* Verify some minimal assumptions */ + + if (upperlen > CONFIG_NET_6LOWPAN_MTU) + { + return 0; + } + + /* The checksum is calculated starting with a pseudo-header of IPv6 header + * fields according to the IPv6 standard, which consists of the source + * and destination addresses, the packet length and the next header field. + */ + + sum = upperlen + ipv6udp->ipv6.proto; + + /* Sum IP source and destination addresses. */ + + sum = chksum(sum, (FAR uint8_t *)ipv6udp->ipv6.srcipaddr, + 2 * sizeof(net_ipv6addr_t)); + + /* Sum the UDP header */ + + sum = chksum(sum, (FAR uint8_t *)&ipv6udp->udp, UDP_HDRLEN); + + /* Sum payload data. */ + + sum = chksum(sum, buf, buflen); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Function: psock_6lowpan_udp_sendto + * + * Description: + * If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) + * socket, the parameters to and 'tolen' are ignored (and the error EISCONN + * may be returned when they are not NULL and 0), and the error ENOTCONN is + * returned when the socket was not actually connected. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * buflen Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters sent. On error, + * -1 is returned, and errno is set appropriately. Returned error + * number must be consistent with definition of errors reported by + * sendto(). + * + * Assumptions: + * Called with the network locked. + * + ****************************************************************************/ + +ssize_t psock_6lowpan_udp_sendto(FAR struct socket *psock, + FAR const void *buf, + size_t buflen, int flags, + FAR const struct sockaddr *to, + socklen_t tolen) +{ + FAR struct sockaddr_in6 *to6 = (FAR struct sockaddr_in6 *)to; + FAR struct udp_conn_s *conn; + FAR struct net_driver_s *dev; + struct ipv6udp_hdr_s ipv6udp; + struct rimeaddr_s destmac; + uint16_t iplen; + uint16_t timeout; + int ret; + + ninfo("buflen %lu\n", (unsigned long)buflen); + + DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && to != NULL); + DEBUGASSERT(psock->s_type == SOCK_DGRAM); + + sixlowpan_dumpbuffer("Outgoing UDP payload", buf, buflen); + + if (psock == NULL || to == NULL) + { + return (ssize_t)-EINVAL; + } + + /* Make sure that this is a datagram valid socket */ + + if (psock->s_crefs <= 0 || psock->s_type != SOCK_DGRAM) + { + nerr("ERROR: Invalid socket\n"); + return (ssize_t)-EBADF; + } + + /* Make sure that the destination address is valid */ + + if (to6->sin6_family != AF_INET6 || tolen < sizeof(struct sockaddr_in6)) + { + nerr("ERROR: Invalid destination address\n"); + return (ssize_t)-EAFNOSUPPORT; + } + + /* Get the underlying UDP "connection" structure */ + + conn = (FAR struct udp_conn_s *)psock->s_conn; + DEBUGASSERT(conn != NULL); + + /* Ignore if not IPv6 domain */ + + if (conn->domain != PF_INET6) + { + nwarn("WARNING: Not IPv6\n"); + return (ssize_t)-EPROTOTYPE; + } + + /* Route outgoing message to the correct device */ + +#ifdef CONFIG_NETDEV_MULTINIC + dev = netdev_findby_ipv6addr(conn->u.ipv6.laddr, + to6->sin6_addr.in6_u.u6_addr16); +#ifdef CONFIG_NETDEV_MULTILINK + if (dev == NULL || dev->d_lltype != NET_LL_IEEE802154) +#else + if (dev == NULL) +#endif + { + nwarn("WARNING: Not routable or not IEEE802.15.4 MAC\n"); + return (ssize_t)-ENETUNREACH; + } +#else + dev = netdev_findby_ipv6addr(to6->sin6_addr.in6_u.u6_addr16); +#ifdef CONFIG_NETDEV_MULTILINK + if (dev == NULL || dev->d_lltype != NET_LL_IEEE802154) +#else + if (dev == NULL) +#endif + { + nwarn("WARNING: Not routable\n"); + return (ssize_t)-ENETUNREACH; + } +#endif + +#ifdef CONFIG_NET_ICMPv6_NEIGHBOR + /* Make sure that the IP address mapping is in the Neighbor Table */ + + ret = icmpv6_neighbor(to6->sin6_addr.in6_u.u6_addr16); + if (ret < 0) + { + nerr("ERROR: Not reachable\n"); + return (ssize_t)-ENETUNREACH; + } +#endif + + /* Initialize the IPv6/UDP headers */ + + ipv6udp.ipv6.vtc = 0x60; + ipv6udp.ipv6.tcf = 0x00; + ipv6udp.ipv6.flow = 0x00; + ipv6udp.ipv6.proto = IP_PROTO_UDP; + ipv6udp.ipv6.ttl = conn->ttl; + + /* The IPv6 header length field does not include the size of IPv6 IP + * header. + */ + + iplen = buflen + UDP_HDRLEN; + ipv6udp.ipv6.len[0] = (iplen >> 8); + ipv6udp.ipv6.len[1] = (iplen & 0xff); + + /* Copy the source and destination addresses */ + + net_ipv6addr_hdrcopy(ipv6udp.ipv6.srcipaddr, to6->sin6_addr.in6_u.u6_addr16); + net_ipv6addr_hdrcopy(ipv6udp.ipv6.destipaddr, conn->u.ipv6.raddr); + + ninfo("IPv6 length: %d\n", + ((int)ipv6udp.ipv6.len[0] << 8) + ipv6udp.ipv6.len[1]); + +#ifdef CONFIG_NET_STATISTICS + g_netstats.ipv6.sent++; +#endif + + /* Initialize the UDP header */ + + ipv6udp.udp.srcport = conn->lport; + ipv6udp.udp.destport = to6->sin6_port; + ipv6udp.udp.udplen = htons(iplen); + ipv6udp.udp.udpchksum = 0; + +#ifdef CONFIG_NET_UDP_CHECKSUMS + ipv6udp.udp.udpchksum = ~sixlowpan_udp_chksum(ipv6udp, buf, buflen); + if (ipv6udp.udp.udpchksum == 0) + { + ipv6udp.udp.udpchksum = 0xffff; + } +#endif /* CONFIG_NET_UDP_CHECKSUMS */ + + ninfo("Outgoing UDP packet length: %d\n", iplen + IPv6_HDRLEN); + +#ifdef CONFIG_NET_STATISTICS + g_netstats.udp.sent++; +#endif + + /* Set the socket state to sending */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); + + /* Get the Rime MAC address of the destination This assumes an encoding + * of the MAC address in the IPv6 address. + */ + + sixlowpan_rimefromip(to6->sin6_addr.in6_u.u6_addr16, &destmac); + + /* If routable, then call sixlowpan_send() to format and send the 6loWPAN + * packet. + */ + +#ifdef CONFIG_NET_SOCKOPTS + timeout = psock->s_sndtimeo; +#else + timeout = 0; +#endif + + ret = sixlowpan_send(dev, (FAR const struct ipv6_hdr_s *)&ipv6udp, + buf, buflen, &destmac, timeout); + if (ret < 0) + { + nerr("ERROR: sixlowpan_send() failed: %d\n", ret); + } + + /* Set the socket state to idle */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); + return ret; +} + /**************************************************************************** * Function: psock_6lowpan_udp_send * @@ -66,15 +336,14 @@ * sockets. * * Parameters: - * psock - An instance of the internal socket structure. - * buf - Data to send - * len - Length of data to send + * psock - An instance of the internal socket structure. + * buf - Data to send + * buflen - Length of data to send * * Returned Value: * On success, returns the number of characters sent. On error, * -1 is returned, and errno is set appropriately. Returned error numbers - * must be consistent with definition of errors reported by send() or - * sendto(). + * must be consistent with definition of errors reported by send(). * * Assumptions: * Called with the network locked. @@ -82,17 +351,18 @@ ****************************************************************************/ ssize_t psock_6lowpan_udp_send(FAR struct socket *psock, FAR const void *buf, - size_t len) + size_t buflen) { FAR struct udp_conn_s *conn; - FAR struct net_driver_s *dev; - struct ipv6udp_hdr_s ipv6udp; - struct rimeaddr_s dest; - int ret; + struct sockaddr_in6 to; + + ninfo("buflen %lu\n", (unsigned long)buflen); DEBUGASSERT(psock != NULL && psock->s_crefs > 0); DEBUGASSERT(psock->s_type == SOCK_DGRAM); + sixlowpan_dumpbuffer("Outgoing UDP payload", buf, buflen); + /* Make sure that this is a valid socket */ if (psock != NULL || psock->s_crefs <= 0) @@ -115,7 +385,6 @@ ssize_t psock_6lowpan_udp_send(FAR struct socket *psock, FAR const void *buf, conn = (FAR struct udp_conn_s *)psock->s_conn; DEBUGASSERT(conn != NULL); -#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) /* Ignore if not IPv6 domain */ if (conn->domain != PF_INET6) @@ -123,59 +392,16 @@ ssize_t psock_6lowpan_udp_send(FAR struct socket *psock, FAR const void *buf, nwarn("WARNING: Not IPv6\n"); return (ssize_t)-EPROTOTYPE; } -#endif - /* Route outgoing message to the correct device */ + /* Create the 'to' address */ -#ifdef CONFIG_NETDEV_MULTINIC - dev = netdev_findby_ipv6addr(conn->u.ipv6.laddr, conn->u.ipv6.raddr); - if (dev == NULL || dev->d_lltype != NET_LL_IEEE805154) - { - nwarn("WARNING: Not routable or not IEEE802.15.4 MAC\n"); - return (ssize_t)-ENETUNREACH; - } -#else - dev = netdev_findby_ipv6addr(conn->u.ipv6.raddr); - if (dev == NULL) - { - nwarn("WARNING: Not routable\n"); - return (ssize_t)-ENETUNREACH; - } -#endif + to.sin6_family = AF_INET6; + to.sin6_port = conn->rport; /* Already network order */ + memcpy(to.sin6_addr.in6_u.u6_addr16, conn->u.ipv6.raddr, 16); -#ifdef CONFIG_NET_ICMPv6_NEIGHBOR - /* Make sure that the IP address mapping is in the Neighbor Table */ - - ret = icmpv6_neighbor(conn->u.ipv6.raddr); - if (ret < 0) - { - nerr("ERROR: Not reachable\n"); - return (ssize_t)-ENETUNREACH; - } -#endif - - /* Initialize the IPv6/UDP headers */ -#warning Missing logic - - /* Set the socket state to sending */ - - psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); - - /* Get the Rime MAC address of the destination */ -#warning Missing logic - - /* If routable, then call sixlowpan_send() to format and send the 6loWPAN - * packet. - */ - - ret = sixlowpan_send(dev, (FAR const struct ipv6_hdr_s *)&ipv6udp, - buf, len, &dest); - if (ret < 0) - { - nerr("ERROR: sixlowpan_send() failed: %d\n", ret); - } - - return ret; + return psock_6lowpan_udp_sendto(psock, buf, buflen, 0, + (FAR const struct sockaddr *)&to, + sizeof(struct sockaddr_in6)); } #endif /* CONFIG_NET_6LOWPAN && CONFIG_NET_UDP */ diff --git a/net/sixlowpan/sixlowpan_utils.c b/net/sixlowpan/sixlowpan_utils.c index f00eaeae4c3..fc322ec38e1 100644 --- a/net/sixlowpan/sixlowpan_utils.c +++ b/net/sixlowpan/sixlowpan_utils.c @@ -44,18 +44,6 @@ * SUCH DAMAGE. * ****************************************************************************/ -/* Frame Organization: - * - * Content Offset - * +------------------+ 0 - * | Frame Header | - * +------------------+ i_dataoffset - * | Procotol Headers | - * | Data Payload | - * +------------------+ i_framelen - * | Unused | - * +------------------+ CONFIG_NET_6LOWPAN_FRAMELEN - */ /**************************************************************************** * Included Files @@ -64,9 +52,10 @@ #include #include +#include #include -#include "nuttx/net/sixlowpan.h" +#include #include "sixlowpan/sixlowpan_internal.h" @@ -77,24 +66,94 @@ ****************************************************************************/ /**************************************************************************** - * Name: sixlowpan_frame_hdralloc + * Name: sixlowpan_ipfromrime * * Description: - * Allocate space for a header within the packet buffer (dev->d_buf). + * Create a link local IPv6 address from a rime address: + * + * 128 112 96 80 64 48 32 16 + * ---- ---- ---- ---- ---- ---- ---- ---- + * fe80 0000 0000 0000 0000 00ff fe00 xxxx 2-byte Rime address IEEE 48-bit MAC + * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte Rime address IEEE EUI-64 * ****************************************************************************/ -int sixlowpan_frame_hdralloc(FAR struct ieee802154_driver_s *ieee, - int size) +void sixlowpan_ipfromrime(FAR const struct rimeaddr_s *rime, + net_ipv6addr_t ipaddr) { - if (size <= FRAME_REMAINING(ieee)) - { - ieee->i_dataoffset += size; - ieee->i_framelen += size; - return OK; - } + /* We consider only links with IEEE EUI-64 identifier or IEEE 48-bit MAC + * addresses. + */ - return -ENOMEM; + memset(ipaddr, 0, sizeof(net_ipv6addr_t)); + ipaddr[0] = HTONS(0xfe80); + +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + ipaddr[5] = HTONS(0x00ff); + ipaddr[6] = HTONS(0xfe00); + memcpy(&ipaddr[7], rime, CONFIG_NET_6LOWPAN_RIMEADDR_SIZE); + ipaddr[7] ^= HTONS(0x0200); +#else + memcpy(&ipaddr[4], rime, CONFIG_NET_6LOWPAN_RIMEADDR_SIZE); + ipaddr[4] ^= HTONS(0x0200); +#endif +} + +/**************************************************************************** + * Name: sixlowpan_rimefromip + * + * Description: + * Extract the rime address from a link local IPv6 address: + * + * 128 112 96 80 64 48 32 16 + * ---- ---- ---- ---- ---- ---- ---- ---- + * fe80 0000 0000 0000 0000 00ff fe00 xxxx 2-byte Rime address IEEE 48-bit MAC + * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte Rime address IEEE EUI-64 + * + ****************************************************************************/ + +void sixlowpan_rimefromip(const net_ipv6addr_t ipaddr, + FAR struct rimeaddr_s *rime) +{ + /* REVISIT: See notes about 2 byte addresses in sixlowpan_ipfromrime() */ + + DEBUGASSERT(ipaddr[0] == HTONS(0xfe80)); + +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + memcpy(rime, &ipaddr[7], CONFIG_NET_6LOWPAN_RIMEADDR_SIZE); +#else + memcpy(rime, &ipaddr[4], CONFIG_NET_6LOWPAN_RIMEADDR_SIZE); +#endif + rime->u8[0] ^= 0x02; +} + +/**************************************************************************** + * Name: sixlowpan_ismacbased + * + * Description: + * Check if the MAC address is encoded in the IP address: + * + * 128 112 96 80 64 48 32 16 + * ---- ---- ---- ---- ---- ---- ---- ---- + * fe80 0000 0000 0000 0000 00ff fe00 xxxx 2-byte Rime address IEEE 48-bit MAC + * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte Rime address IEEE EUI-64 + * + ****************************************************************************/ + +bool sixlowpan_ismacbased(const net_ipv6addr_t ipaddr, + FAR const struct rimeaddr_s *rime) +{ + FAR const uint8_t *rimeptr = rime->u8; + +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + return (ipaddr[5] == HTONS(0x00ff) && ipaddr[6] == HTONS(0xfe00) && + ipaddr[7] == htons((GETINT16(rimeptr, 0) ^ 0x0200))); +#else /* CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 8 */ + return (ipaddr[4] == htons((GETINT16(rimeptr, 0) ^ 0x0200)) && + ipaddr[5] == GETINT16(rimeptr, 2) && + ipaddr[6] == GETINT16(rimeptr, 4) && + ipaddr[7] == GETINT16(rimeptr, 6)); +#endif } #endif /* CONFIG_NET_6LOWPAN */ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index cfb6233bd1f..6f433cd97a8 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -42,7 +42,10 @@ SOCK_CSRCS += net_dupsd2.c net_clone.c net_poll.c net_vfcntl.c # TCP/IP support ifeq ($(CONFIG_NET_TCP),y) -SOCK_CSRCS += listen.c accept.c net_monitor.c +SOCK_CSRCS += listen.c accept.c +ifneq ($(CONFIG_NET_TCP_NO_STACK),y) +SOCK_CSRCS += net_monitor.c +endif # Local Unix domain support diff --git a/net/socket/accept.c b/net/socket/accept.c index 4bf35dc7f21..b4db581dbad 100644 --- a/net/socket/accept.c +++ b/net/socket/accept.c @@ -54,6 +54,7 @@ #include "tcp/tcp.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -129,7 +130,9 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen, FAR struct socket *newsock) { int errcode; +#if defined(NET_TCP_HAVE_STACK) || defined(CONFIG_NET_LOCAL_STREAM) int ret; +#endif DEBUGASSERT(psock != NULL); @@ -141,6 +144,13 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, if (psock->s_type != SOCK_STREAM) { +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { +#warning "Missing logic" + } +#endif + errcode = EOPNOTSUPP; goto errout; } @@ -238,6 +248,7 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, else #endif { +#ifdef NET_TCP_HAVE_STACK /* Perform the local accept operation (with the network locked) */ net_lock(); @@ -267,6 +278,10 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, } net_unlock(); +#else + errcode = EOPNOTSUPP; + goto errout; +#endif /* NET_TCP_HAVE_STACK */ } #endif /* CONFIG_NET_TCP */ @@ -278,8 +293,10 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, leave_cancellation_point(); return OK; +#ifdef NET_TCP_HAVE_STACK errout_after_accept: psock_close(newsock); +#endif errout: set_errno(errcode); diff --git a/net/socket/bind.c b/net/socket/bind.c index 1197460ce79..8ffc1274e8b 100644 --- a/net/socket/bind.c +++ b/net/socket/bind.c @@ -46,6 +46,7 @@ #include #include #include +#include #ifdef CONFIG_NET_PKT # include @@ -60,6 +61,7 @@ #include "udp/udp.h" #include "pkt/pkt.h" #include "local/local.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Private Functions @@ -212,6 +214,20 @@ int psock_bind(FAR struct socket *psock, const struct sockaddr *addr, switch (psock->s_type) { +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + + DEBUGASSERT(conn); + + /* Perform the usrsock bind operation */ + + ret = usrsock_bind(conn, addr, addrlen); + } + break; +#endif + #ifdef CONFIG_NET_PKT case SOCK_RAW: ret = pkt_bind(psock->s_conn, lladdr); @@ -243,9 +259,13 @@ int psock_bind(FAR struct socket *psock, const struct sockaddr *addr, else #endif { +#ifdef NET_TCP_HAVE_STACK /* Bind the TCP/IP connection structure */ ret = tcp_bind(psock->s_conn, addr); +#else + ret = -ENOSYS; +#endif } #endif /* CONFIG_NET_TCP */ @@ -284,9 +304,13 @@ int psock_bind(FAR struct socket *psock, const struct sockaddr *addr, else #endif { +#ifdef NET_UDP_HAVE_STACK /* Bind the UDPP/IP connection structure */ ret = udp_bind(psock->s_conn, addr); +#else + ret = -ENOSYS; +#endif } #endif /* CONFIG_NET_UDP */ diff --git a/net/socket/connect.c b/net/socket/connect.c index 6a0140ea55a..dd3ee97e096 100644 --- a/net/socket/connect.c +++ b/net/socket/connect.c @@ -62,12 +62,13 @@ #include "udp/udp.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Private Types ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK struct tcp_connect_s { FAR struct tcp_conn_s *tc_conn; /* Reference to TCP connection structure */ @@ -82,7 +83,7 @@ struct tcp_connect_s * Private Function Prototypes ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline int psock_setup_callbacks(FAR struct socket *psock, FAR struct tcp_connect_s *pstate); static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, @@ -92,7 +93,7 @@ static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, uint16_t flags); static inline int psock_tcp_connect(FAR struct socket *psock, FAR const struct sockaddr *addr); -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Private Functions @@ -101,7 +102,7 @@ static inline int psock_tcp_connect(FAR struct socket *psock, * Name: psock_setup_callbacks ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline int psock_setup_callbacks(FAR struct socket *psock, FAR struct tcp_connect_s *pstate) { @@ -137,13 +138,13 @@ static inline int psock_setup_callbacks(FAR struct socket *psock, return ret; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Name: psock_teardown_callbacks ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, int status) { @@ -165,7 +166,7 @@ static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, net_stopmonitor(conn); } } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Name: psock_connect_interrupt @@ -187,7 +188,7 @@ static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, FAR void *pvconn, FAR void *pvpriv, uint16_t flags) @@ -299,7 +300,7 @@ static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, else #endif { - pstate->tc_conn->mss = TCP_IPv4_INITIAL_MSS(dev); + pstate->tc_conn->mss = TCP_IPv6_INITIAL_MSS(dev); } #endif /* CONFIG_NET_IPv6 */ @@ -322,7 +323,7 @@ static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, return flags; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Name: psock_tcp_connect @@ -342,7 +343,7 @@ static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline int psock_tcp_connect(FAR struct socket *psock, FAR const struct sockaddr *addr) { @@ -434,7 +435,7 @@ static inline int psock_tcp_connect(FAR struct socket *psock, net_unlock(); return ret; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Public Functions @@ -512,7 +513,8 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen) { FAR const struct sockaddr_in *inaddr = (FAR const struct sockaddr_in *)addr; -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL) +#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) || \ + defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_USRSOCK) int ret; #endif int errcode; @@ -570,6 +572,13 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, #endif default: +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + break; + } +#endif + DEBUGPANIC(); errcode = EAFNOSUPPORT; goto errout; @@ -608,9 +617,13 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, else #endif { +#ifdef NET_TCP_HAVE_STACK /* Connect the TCP/IP socket */ ret = psock_tcp_connect(psock, addr); +#else + ret = -ENOSYS; +#endif } #endif /* CONFIG_NET_TCP */ @@ -642,6 +655,7 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, else #endif { +#ifdef NET_UDP_HAVE_STACK ret = udp_connect(psock->s_conn, addr); if (ret < 0 || addr == NULL) { @@ -651,6 +665,9 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, { psock->s_flags |= _SF_CONNECTED; } +#else + ret = -ENOSYS; +#endif } #endif /* CONFIG_NET_UDP */ @@ -663,6 +680,19 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, break; #endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + ret = usrsock_connect(psock, addr, addrlen); + if (ret < 0) + { + errcode = -ret; + goto errout; + } + } + break; +#endif /* CONFIG_NET_USRSOCK */ + default: errcode = EBADF; goto errout; diff --git a/net/socket/getsockname.c b/net/socket/getsockname.c index 7bf87c29d12..703136e0d9e 100644 --- a/net/socket/getsockname.c +++ b/net/socket/getsockname.c @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -54,6 +55,7 @@ #include "tcp/tcp.h" #include "udp/udp.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" #ifdef CONFIG_NET @@ -92,7 +94,7 @@ int ipv4_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { FAR struct net_driver_s *dev; -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) +#if defined(NET_TCP_HAVE_STACK) || defined(NET_UDP_HAVE_STACK) FAR struct sockaddr_in *outaddr = (FAR struct sockaddr_in *)addr; #endif #ifdef CONFIG_NETDEV_MULTINIC @@ -116,7 +118,7 @@ int ipv4_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, switch (psock->s_type) { -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK case SOCK_STREAM: { FAR struct tcp_conn_s *tcp_conn = (FAR struct tcp_conn_s *)psock->s_conn; @@ -129,7 +131,7 @@ int ipv4_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, break; #endif -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK case SOCK_DGRAM: { FAR struct udp_conn_s *udp_conn = (FAR struct udp_conn_s *)psock->s_conn; @@ -171,7 +173,7 @@ int ipv4_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, /* Set the address family and the IP address */ -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) +#if defined(NET_TCP_HAVE_STACK) || defined(NET_UDP_HAVE_STACK) outaddr->sin_family = AF_INET; outaddr->sin_addr.s_addr = dev->d_ipaddr; *addrlen = sizeof(struct sockaddr_in); @@ -215,7 +217,7 @@ int ipv6_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { FAR struct net_driver_s *dev; -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) +#if defined(NET_TCP_HAVE_STACK) || defined(NET_UDP_HAVE_STACK) FAR struct sockaddr_in6 *outaddr = (FAR struct sockaddr_in6 *)addr; #endif #ifdef CONFIG_NETDEV_MULTINIC @@ -239,7 +241,7 @@ int ipv6_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, switch (psock->s_type) { -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK case SOCK_STREAM: { FAR struct tcp_conn_s *tcp_conn = (FAR struct tcp_conn_s *)psock->s_conn; @@ -252,7 +254,7 @@ int ipv6_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, break; #endif -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK case SOCK_DGRAM: { FAR struct udp_conn_s *udp_conn = (FAR struct udp_conn_s *)psock->s_conn; @@ -294,7 +296,7 @@ int ipv6_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, /* Set the address family and the IP address */ -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) +#if defined(NET_TCP_HAVE_STACK) || defined(NET_UDP_HAVE_STACK) outaddr->sin6_family = AF_INET6; memcpy(outaddr->sin6_addr.in6_u.u6_addr8, dev->d_ipv6addr, 16); *addrlen = sizeof(struct sockaddr_in6); @@ -371,6 +373,26 @@ int getsockname(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) } #endif +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + + DEBUGASSERT(conn); + + /* Handle usrsock getsockname */ + + ret = usrsock_getsockname(conn, addr, addrlen); + if (ret < 0) + { + errcode = -ret; + goto errout; + } + + return OK; + } +#endif + /* Handle by address domain */ switch (psock->s_domain) diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c index 5072f02067c..ab15d511aaf 100644 --- a/net/socket/getsockopt.c +++ b/net/socket/getsockopt.c @@ -43,9 +43,12 @@ #include #include #include +#include +#include #include #include "socket/socket.h" +#include "usrsock/usrsock.h" #include "utils/utils.h" /**************************************************************************** @@ -106,6 +109,40 @@ int psock_getsockopt(FAR struct socket *psock, int level, int option, goto errout; } +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + int ret; + + DEBUGASSERT(conn); + + /* Some of the socket options are handled from this function. */ + + switch (option) + { + case SO_TYPE: /* Type can be read from NuttX psock structure. */ + case SO_RCVTIMEO: /* Rx timeouts can be handled at NuttX side, thus + * simplify daemon implementation. */ + case SO_SNDTIMEO: /* Rx timeouts can be handled at NuttX side, thus + * simplify daemon implementation. */ + break; + + default: /* Other options are passed to usrsock daemon. */ + { + ret = usrsock_getsockopt(conn, level, option, value, value_len); + if (ret < 0) + { + errcode = -ret; + goto errout; + } + + return OK; + } + } + } +#endif + /* Process the option */ switch (option) @@ -159,6 +196,20 @@ int psock_getsockopt(FAR struct socket *psock, int level, int option, goto errout; } +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + + /* Return the actual socket type */ + + *(int*)value = conn->type; + *value_len = sizeof(int); + + break; + } +#endif + /* Return the socket type */ *(FAR int *)value = psock->s_type; diff --git a/net/socket/listen.c b/net/socket/listen.c index 0d91ccb5867..3b0189d019d 100644 --- a/net/socket/listen.c +++ b/net/socket/listen.c @@ -48,6 +48,7 @@ #include "tcp/tcp.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -92,6 +93,13 @@ int psock_listen(FAR struct socket *psock, int backlog) if (psock->s_type != SOCK_STREAM || !psock->s_conn) { +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { +#warning "Missing logic" + } +#endif + errcode = EOPNOTSUPP; goto errout; } @@ -118,6 +126,7 @@ int psock_listen(FAR struct socket *psock, int backlog) else #endif { +#ifdef NET_TCP_HAVE_STACK FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)psock->s_conn; @@ -143,6 +152,10 @@ int psock_listen(FAR struct socket *psock, int backlog) */ tcp_listen(conn); +#else + errcode = EOPNOTSUPP; + goto errout; +#endif /* NET_TCP_HAVE_STACK */ } #endif /* CONFIG_NET_TCP */ diff --git a/net/socket/net_clone.c b/net/socket/net_clone.c index 5398c614fdf..83a7c5150c4 100644 --- a/net/socket/net_clone.c +++ b/net/socket/net_clone.c @@ -51,6 +51,7 @@ #include "tcp/tcp.h" #include "udp/udp.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -96,7 +97,7 @@ int net_clone(FAR struct socket *psock1, FAR struct socket *psock2) DEBUGASSERT(psock2->s_conn); psock2->s_crefs = 1; /* One reference on the new socket itself */ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK if (psock2->s_type == SOCK_STREAM) { FAR struct tcp_conn_s *conn = psock2->s_conn; @@ -105,7 +106,7 @@ int net_clone(FAR struct socket *psock1, FAR struct socket *psock2) } else #endif -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK if (psock2->s_type == SOCK_DGRAM) { FAR struct udp_conn_s *conn = psock2->s_conn; @@ -113,6 +114,15 @@ int net_clone(FAR struct socket *psock1, FAR struct socket *psock2) conn->crefs++; } else +#endif +#ifdef CONFIG_NET_USRSOCK + if (psock2->s_type == SOCK_USRSOCK_TYPE) + { + FAR struct usrsock_conn_s *conn = psock2->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); + conn->crefs++; + } + else #endif { nerr("ERROR: Unsupported type: %d\n", psock2->s_type); diff --git a/net/socket/net_close.c b/net/socket/net_close.c index ba0fb85dc1d..6bbc01b725f 100644 --- a/net/socket/net_close.c +++ b/net/socket/net_close.c @@ -67,12 +67,13 @@ #include "pkt/pkt.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Private Types ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK struct tcp_close_s { FAR struct devif_callback_s *cl_cb; /* Reference to TCP callback instance */ @@ -106,7 +107,7 @@ struct tcp_close_s * ****************************************************************************/ -#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_SOLINGER) +#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_SOLINGER) static inline int close_timeout(FAR struct tcp_close_s *pstate) { FAR struct socket *psock = 0; @@ -132,7 +133,7 @@ static inline int close_timeout(FAR struct tcp_close_s *pstate) return FALSE; } -#endif /* CONFIG_NET_SOCKOPTS && CONFIG_NET_SOLINGER */ +#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_SOLINGER */ /**************************************************************************** * Function: netclose_interrupt @@ -151,7 +152,7 @@ static inline int close_timeout(FAR struct tcp_close_s *pstate) * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static uint16_t netclose_interrupt(FAR struct net_driver_s *dev, FAR void *pvconn, FAR void *pvpriv, uint16_t flags) @@ -256,7 +257,7 @@ end_wait: return 0; #endif } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: netclose_txnotify @@ -274,7 +275,7 @@ end_wait: * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline void netclose_txnotify(FAR struct socket *psock, FAR struct tcp_conn_s *conn) { @@ -313,7 +314,7 @@ static inline void netclose_txnotify(FAR struct socket *psock, } #endif /* CONFIG_NET_IPv6 */ } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: netclose_disconnect @@ -332,7 +333,7 @@ static inline void netclose_txnotify(FAR struct socket *psock, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline int netclose_disconnect(FAR struct socket *psock) { struct tcp_close_s state; @@ -451,7 +452,7 @@ static inline int netclose_disconnect(FAR struct socket *psock) net_unlock(); return ret; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: local_close @@ -557,6 +558,7 @@ int psock_close(FAR struct socket *psock) else #endif { +#ifdef NET_TCP_HAVE_STACK FAR struct tcp_conn_s *conn = psock->s_conn; /* Is this the last reference to the connection structure @@ -592,6 +594,7 @@ int psock_close(FAR struct socket *psock) conn->crefs--; } +#endif /* NET_TCP_HAVE_STACK */ } #endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ } @@ -617,6 +620,7 @@ int psock_close(FAR struct socket *psock) else #endif { +#ifdef NET_UDP_HAVE_STACK FAR struct udp_conn_s *conn = psock->s_conn; /* Is this the last reference to the connection structure @@ -636,6 +640,7 @@ int psock_close(FAR struct socket *psock) conn->crefs--; } +#endif /* NET_UDP_HAVE_STACK */ } #endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ } @@ -668,6 +673,44 @@ int psock_close(FAR struct socket *psock) break; #endif +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... inform user-space daemon of socket close. */ + + errcode = usrsock_close(conn); + + /* Free the connection structure */ + + conn->crefs = 0; + usrsock_free(psock->s_conn); + + if (errcode < 0) + { + /* Return with error code, but free resources. */ + + errcode = -errcode; + goto errout_with_psock; + } + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } + } + break; +#endif + default: errcode = EBADF; goto errout; @@ -679,7 +722,7 @@ int psock_close(FAR struct socket *psock) sock_release(psock); return OK; -#ifdef CONFIG_NET_TCP +#if defined(NET_TCP_HAVE_STACK) || defined(CONFIG_NET_USRSOCK) errout_with_psock: sock_release(psock); #endif diff --git a/net/socket/net_monitor.c b/net/socket/net_monitor.c index cee6a13cd94..d44a6fe5152 100644 --- a/net/socket/net_monitor.c +++ b/net/socket/net_monitor.c @@ -50,6 +50,8 @@ #include "tcp/tcp.h" #include "socket/socket.h" +#ifdef NET_TCP_HAVE_STACK + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -345,4 +347,6 @@ void net_lostconnection(FAR struct socket *psock, uint16_t flags) net_unlock(); } +#endif /* NET_TCP_HAVE_STACK */ + #endif /* CONFIG_NET && CONFIG_NET_TCP */ diff --git a/net/socket/net_poll.c b/net/socket/net_poll.c index e534e9d0220..0fe2b23db15 100644 --- a/net/socket/net_poll.c +++ b/net/socket/net_poll.c @@ -45,6 +45,7 @@ #include "udp/udp.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" #if defined(CONFIG_NET) && !defined(CONFIG_DISABLE_POLL) @@ -57,7 +58,8 @@ */ #undef HAVE_NET_POLL -#if defined(HAVE_TCP_POLL) || defined(HAVE_UDP_POLL) || defined(HAVE_LOCAL_POLL) +#if defined(HAVE_TCP_POLL) || defined(HAVE_UDP_POLL) || \ + defined(HAVE_LOCAL_POLL) || defined(CONFIG_NET_USRSOCK) # define HAVE_NET_POLL 1 #endif @@ -233,6 +235,15 @@ int psock_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) #else int ret; +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + /* Perform usrsock setup/teardown. */ + + return usrsock_poll(psock, fds, setup); + } +#endif + /* Check if we are setting up or tearing down the poll */ if (setup) diff --git a/net/socket/net_sendfile.c b/net/socket/net_sendfile.c index 617bce53972..81f527abe76 100644 --- a/net/socket/net_sendfile.c +++ b/net/socket/net_sendfile.c @@ -72,6 +72,8 @@ #include "tcp/tcp.h" #include "socket/socket.h" +#ifdef NET_TCP_HAVE_STACK + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -615,6 +617,21 @@ ssize_t net_sendfile(int outfd, struct file *infile, off_t *offset, goto errout; } +#ifdef CONFIG_NET_USRSOCK + /* If this is a usrsock socket, use generic sendfile implementation. */ + + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + int infd; + + list = sched_getfiles(); + DEBUGASSERT(list != NULL); + + infd = infile - list->fl_files; + return lib_sendfile(outfd, infd, offset, count); + } +#endif /* CONFIG_NET_USRSOCK */ + /* If this is an un-connected socket, then return ENOTCONN */ if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags)) @@ -776,4 +793,6 @@ errout: } } +#endif /* NET_TCP_HAVE_STACK */ + #endif /* CONFIG_NET && CONFIG_NET_TCP */ diff --git a/net/socket/net_timeo.c b/net/socket/net_timeo.c index 47099d205d5..81b7123a312 100644 --- a/net/socket/net_timeo.c +++ b/net/socket/net_timeo.c @@ -78,6 +78,7 @@ int net_timeo(systime_t start_time, socktimeo_t timeo) { return TRUE; } + return FALSE; } diff --git a/net/socket/net_vfcntl.c b/net/socket/net_vfcntl.c index b260edb5b0a..79df78379bc 100644 --- a/net/socket/net_vfcntl.c +++ b/net/socket/net_vfcntl.c @@ -49,6 +49,7 @@ #include #include #include "socket/socket.h" +#include "usrsock/usrsock.h" #if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 @@ -164,6 +165,13 @@ int net_vfcntl(int sockfd, int cmd, va_list ap) ret |= O_NONBLOCK; } #endif /* CONFIG_NET_LOCAL || CONFIG_NET_TCP_READAHEAD || CONFIG_NET_UDP_READAHEAD */ + +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE && _SS_ISNONBLOCK(psock->s_flags)) + { + ret |= O_NONBLOCK; + } +#endif } break; @@ -178,7 +186,7 @@ int net_vfcntl(int sockfd, int cmd, va_list ap) { #if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_TCP_READAHEAD) || \ - defined(CONFIG_NET_UDP_READAHEAD) + defined(CONFIG_NET_UDP_READAHEAD) || defined(CONFIG_NET_USRSOCK) /* Non-blocking is the only configurable option. And it applies * only Unix domain sockets and to read operations on TCP/IP * and UDP/IP sockets when read-ahead is enabled. @@ -213,7 +221,22 @@ int net_vfcntl(int sockfd, int cmd, va_list ap) } else #endif -#endif /* CONFIG_NET_LOCAL || CONFIG_NET_TCP_READAHEAD || CONFIG_NET_UDP_READAHEAD */ +#if defined(CONFIG_NET_USRSOCK) + if (psock->s_type == SOCK_USRSOCK_TYPE) /* usrsock socket */ + { + if ((mode & O_NONBLOCK) != 0) + { + psock->s_flags |= _SF_NONBLOCK; + } + else + { + psock->s_flags &= ~_SF_NONBLOCK; + } + } + else +#endif +#endif /* CONFIG_NET_LOCAL || CONFIG_NET_TCP_READAHEAD || + CONFIG_NET_UDP_READAHEAD || CONFIG_NET_USRSOCK */ { nerr("ERROR: Non-blocking not supported for this socket\n"); } diff --git a/net/socket/recvfrom.c b/net/socket/recvfrom.c index 64789b06720..fc713375cbd 100644 --- a/net/socket/recvfrom.c +++ b/net/socket/recvfrom.c @@ -72,6 +72,7 @@ #include "pkt/pkt.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Pre-processor Definitions @@ -90,7 +91,8 @@ * Private Types ****************************************************************************/ -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) || defined(CONFIG_NET_PKT) +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ + || defined(CONFIG_NET_PKT) struct recvfrom_s { FAR struct socket *rf_sock; /* The parent socket structure */ @@ -106,7 +108,7 @@ struct recvfrom_s ssize_t rf_recvlen; /* The received length */ int rf_result; /* Success:OK, failure:negated errno */ }; -#endif /* CONFIG_NET_UDP || CONFIG_NET_TCP */ +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** * Private Functions @@ -129,7 +131,8 @@ struct recvfrom_s * ****************************************************************************/ -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) || defined(CONFIG_NET_PKT) +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ + || defined(CONFIG_NET_PKT) static inline void recvfrom_add_recvlen(FAR struct recvfrom_s *pstate, size_t recvlen) @@ -143,7 +146,7 @@ static inline void recvfrom_add_recvlen(FAR struct recvfrom_s *pstate, pstate->rf_buffer += recvlen; pstate->rf_buflen -= recvlen; } -#endif /* CONFIG_NET_UDP || CONFIG_NET_TCP || CONFIG_NET_PKT */ +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK || CONFIG_NET_PKT */ /**************************************************************************** * Function: recvfrom_newdata @@ -163,7 +166,7 @@ static inline void recvfrom_add_recvlen(FAR struct recvfrom_s *pstate, * ****************************************************************************/ -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) static size_t recvfrom_newdata(FAR struct net_driver_s *dev, FAR struct recvfrom_s *pstate) { @@ -191,7 +194,7 @@ static size_t recvfrom_newdata(FAR struct net_driver_s *dev, return recvlen; } -#endif /* CONFIG_NET_UDP || CONFIG_NET_TCP */ +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_newpktdata @@ -255,7 +258,7 @@ static void recvfrom_newpktdata(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline void recvfrom_newtcpdata(FAR struct net_driver_s *dev, FAR struct recvfrom_s *pstate) { @@ -307,7 +310,7 @@ static inline void recvfrom_newtcpdata(FAR struct net_driver_s *dev, dev->d_len = 0; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_newudpdata @@ -327,7 +330,7 @@ static inline void recvfrom_newtcpdata(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static inline void recvfrom_newudpdata(FAR struct net_driver_s *dev, FAR struct recvfrom_s *pstate) { @@ -339,7 +342,7 @@ static inline void recvfrom_newudpdata(FAR struct net_driver_s *dev, dev->d_len = 0; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_tcpreadahead @@ -359,7 +362,7 @@ static inline void recvfrom_newudpdata(FAR struct net_driver_s *dev, * ****************************************************************************/ -#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_TCP_READAHEAD) +#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_TCP_READAHEAD) static inline void recvfrom_tcpreadahead(struct recvfrom_s *pstate) { FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pstate->rf_sock->s_conn; @@ -419,9 +422,9 @@ static inline void recvfrom_tcpreadahead(struct recvfrom_s *pstate) } } } -#endif /* CONFIG_NET_UDP || CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_TCP_READAHEAD */ -#if defined(CONFIG_NET_UDP) && defined(CONFIG_NET_UDP_READAHEAD) +#if defined(NET_UDP_HAVE_STACK) && defined(CONFIG_NET_UDP_READAHEAD) static inline void recvfrom_udpreadahead(struct recvfrom_s *pstate) { @@ -526,7 +529,7 @@ out: * ****************************************************************************/ -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) #ifdef CONFIG_NET_SOCKOPTS static int recvfrom_timeout(struct recvfrom_s *pstate) { @@ -581,7 +584,7 @@ static int recvfrom_timeout(struct recvfrom_s *pstate) return FALSE; } #endif /* CONFIG_NET_SOCKOPTS */ -#endif /* CONFIG_NET_UDP || CONFIG_NET_TCP */ +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_pktsender @@ -684,7 +687,7 @@ static uint16_t recvfrom_pktinterrupt(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static inline void recvfrom_tcpsender(FAR struct net_driver_s *dev, FAR struct recvfrom_s *pstate) { @@ -735,7 +738,7 @@ static inline void recvfrom_tcpsender(FAR struct net_driver_s *dev, } #endif /* CONFIG_NET_IPv4 */ } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_tcpinterrupt @@ -757,7 +760,7 @@ static inline void recvfrom_tcpsender(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static uint16_t recvfrom_tcpinterrupt(FAR struct net_driver_s *dev, FAR void *pvconn, FAR void *pvpriv, uint16_t flags) @@ -958,7 +961,7 @@ static uint16_t recvfrom_tcpinterrupt(FAR struct net_driver_s *dev, return flags; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_udpsender @@ -978,7 +981,7 @@ static uint16_t recvfrom_tcpinterrupt(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static inline void recvfrom_udpsender(struct net_driver_s *dev, struct recvfrom_s *pstate) { /* Get the family from the packet type, IP address from the IP header, and @@ -1059,7 +1062,7 @@ static inline void recvfrom_udpsender(struct net_driver_s *dev, struct recvfrom_ } #endif /* CONFIG_NET_IPv4 */ } -#endif /* CONFIG_NET_UDP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_udp_terminate @@ -1076,7 +1079,7 @@ static inline void recvfrom_udpsender(struct net_driver_s *dev, struct recvfrom_ * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static void recvfrom_udp_terminate(FAR struct recvfrom_s *pstate, int result) { /* Don't allow any further UDP call backs. */ @@ -1095,7 +1098,7 @@ static void recvfrom_udp_terminate(FAR struct recvfrom_s *pstate, int result) sem_post(&pstate->rf_sem); } -#endif /* CONFIG_NET_UDP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_udp_interrupt @@ -1117,7 +1120,7 @@ static void recvfrom_udp_terminate(FAR struct recvfrom_s *pstate, int result) * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static uint16_t recvfrom_udp_interrupt(FAR struct net_driver_s *dev, FAR void *pvconn, FAR void *pvpriv, uint16_t flags) @@ -1189,7 +1192,7 @@ static uint16_t recvfrom_udp_interrupt(FAR struct net_driver_s *dev, return flags; } -#endif /* CONFIG_NET_UDP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_init @@ -1210,7 +1213,8 @@ static uint16_t recvfrom_udp_interrupt(FAR struct net_driver_s *dev, * ****************************************************************************/ -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) || defined(CONFIG_NET_PKT) +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ + || defined(CONFIG_NET_PKT) static void recvfrom_init(FAR struct socket *psock, FAR void *buf, size_t len, FAR struct sockaddr *infrom, FAR socklen_t *fromlen, @@ -1246,7 +1250,7 @@ static void recvfrom_init(FAR struct socket *psock, FAR void *buf, #define recvfrom_uninit(s) sem_destroy(&(s)->rf_sem) -#endif /* CONFIG_NET_UDP || CONFIG_NET_TCP */ +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: recvfrom_result @@ -1265,7 +1269,8 @@ static void recvfrom_init(FAR struct socket *psock, FAR void *buf, * ****************************************************************************/ -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) || defined(CONFIG_NET_PKT) +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ + || defined(CONFIG_NET_PKT) static ssize_t recvfrom_result(int result, struct recvfrom_s *pstate) { int save_errno = get_errno(); /* In case something we do changes it */ @@ -1294,7 +1299,7 @@ static ssize_t recvfrom_result(int result, struct recvfrom_s *pstate) return pstate->rf_recvlen; } -#endif /* CONFIG_NET_UDP || CONFIG_NET_TCP */ +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** * Function: recvfromo_pkt_rxnotify @@ -1334,7 +1339,7 @@ static void recvfromo_pkt_rxnotify(FAR struct pkt_conn_s *conn) * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static inline void recvfrom_udp_rxnotify(FAR struct socket *psock, FAR struct udp_conn_s *conn) { @@ -1373,7 +1378,7 @@ static inline void recvfrom_udp_rxnotify(FAR struct socket *psock, } #endif /* CONFIG_NET_IPv6 */ } -#endif /* CONFIG_NET_UDP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Function: pkt_recvfrom @@ -1489,7 +1494,7 @@ errout_with_state: * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static ssize_t udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, FAR struct sockaddr *from, FAR socklen_t *fromlen) { @@ -1607,7 +1612,7 @@ errout_with_state: recvfrom_uninit(&state); return ret; } -#endif /* CONFIG_NET_UDP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Function: tcp_recvfrom @@ -1629,7 +1634,7 @@ errout_with_state: * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static ssize_t tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, FAR struct sockaddr *from, FAR socklen_t *fromlen) { @@ -1785,7 +1790,7 @@ static ssize_t tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, recvfrom_uninit(&state); return (ssize_t)ret; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Public Functions @@ -1880,6 +1885,22 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, goto errout; } +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + /* Perform the usrsock recvfrom operation */ + + ret = usrsock_recvfrom(psock, buf, len, from, fromlen); + if (ret < 0) + { + errcode = -ret; + goto errout; + } + + return ret; + } +#endif + /* If a 'from' address has been provided, verify that it is large * enough to hold this address family. */ @@ -1964,7 +1985,11 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, else #endif { +#ifdef NET_TCP_HAVE_STACK ret = tcp_recvfrom(psock, buf, len, from, fromlen); +#else + ret = -ENOSYS; +#endif } #endif /* CONFIG_NET_TCP */ } @@ -1989,7 +2014,11 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, else #endif { +#ifdef NET_UDP_HAVE_STACK ret = udp_recvfrom(psock, buf, len, from, fromlen); +#else + ret = -ENOSYS; +#endif } #endif /* CONFIG_NET_UDP */ } diff --git a/net/socket/send.c b/net/socket/send.c index fd37860a1a1..340e4caa62b 100644 --- a/net/socket/send.c +++ b/net/socket/send.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/send.c * - * Copyright (C) 2007-2014, 2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2014, 2016-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -51,6 +51,7 @@ #include "sixlowpan/sixlowpan.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -123,7 +124,7 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags) { - int ret; + ssize_t ret; /* Treat as a cancellation point */ @@ -165,20 +166,18 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, ret = psock_6lowpan_tcp_send(psock, buf, len); -#ifdef CONFIG_NETDEV_MULTINIC +#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_TCP_HAVE_STACK) if (ret < 0) { - /* UDP/IP packet send */ + /* TCP/IP packet send */ ret = psock_tcp_send(psock, buf, len); } - -#endif /* CONFIG_NETDEV_MULTINIC */ -#else /* CONFIG_NET_6LOWPAN */ - - /* Only TCP/IP packet send */ - +#endif /* CONFIG_NETDEV_MULTINIC && NET_TCP_HAVE_STACK */ +#elif defined(NET_TCP_HAVE_STACK) ret = psock_tcp_send(psock, buf, len); +#else + ret = -ENOSYS; #endif /* CONFIG_NET_6LOWPAN */ } #endif /* CONFIG_NET_TCP */ @@ -205,25 +204,25 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, else #endif { -#ifdef CONFIG_NET_6LOWPAN +#if defined(CONFIG_NET_6LOWPAN) /* Try 6loWPAN UDP packet send */ ret = psock_6lowpan_udp_send(psock, buf, len); -#ifdef CONFIG_NETDEV_MULTINIC +#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_UDP_HAVE_STACK) if (ret < 0) { /* UDP/IP packet send */ ret = psock_udp_send(psock, buf, len); } - -#endif /* CONFIG_NETDEV_MULTINIC */ -#else /* CONFIG_NET_6LOWPAN */ - +#endif /* CONFIG_NETDEV_MULTINIC && NET_UDP_HAVE_STACK */ +#elif defined(NET_UDP_HAVE_STACK) /* Only UDP/IP packet send */ ret = psock_udp_send(psock, buf, len); +#else + ret = -ENOSYS; #endif /* CONFIG_NET_6LOWPAN */ } #endif /* CONFIG_NET_UDP */ @@ -231,6 +230,14 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, break; #endif /* CONFIG_NET_UDP */ +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + ret = usrsock_sendto(psock, buf, len, NULL, 0); + } + break; +#endif /*CONFIG_NET_USRSOCK*/ + default: { /* EDESTADDRREQ. Signifies that the socket is not connection-mode @@ -243,6 +250,11 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, } leave_cancellation_point(); + if (ret < 0) + { + set_errno(-ret); + ret = ERROR; + } return ret; } diff --git a/net/socket/sendto.c b/net/socket/sendto.c index db51b73c79a..4c2798cd6ca 100644 --- a/net/socket/sendto.c +++ b/net/socket/sendto.c @@ -49,8 +49,10 @@ #include #include "udp/udp.h" +#include "sixlowpan/sixlowpan.h" #include "local/local.h" #include "socket/socket.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -126,7 +128,8 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, socklen_t tolen) { socklen_t minlen; -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) +#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) || \ + defined(CONFIG_NET_USRSOCK) ssize_t nsent; #endif int errcode; @@ -137,7 +140,8 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, if (!to || !tolen) { -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) +#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) || \ + defined(CONFIG_NET_USRSOCK) return psock_send(psock, buf, len, flags); #else nerr("ERROR: No 'to' address\n"); @@ -146,6 +150,22 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, #endif } +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + /* Perform the usrsock sendto operation */ + + nsent = usrsock_sendto(psock, buf, len, to, tolen); + if (nsent < 0) + { + errcode = -nsent; + goto errout; + } + + return nsent; + } +#endif + /* Verify that a valid address has been provided */ switch (to->sa_family) @@ -218,7 +238,24 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, else #endif { +#if defined(CONFIG_NET_6LOWPAN) + /* Try 6loWPAN UDP packet sendto() */ + + nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, tolen); + +#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_UDP_HAVE_STACK) + if (nsent < 0) + { + /* UDP/IP packet sendto */ + + nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); + } +#endif /* CONFIG_NETDEV_MULTINIC && NET_UDP_HAVE_STACK */ +#elif defined(NET_UDP_HAVE_STACK) nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); +#else + nsent = -ENOSYS; +#endif /* CONFIG_NET_6LOWPAN */ } #endif /* CONFIG_NET_UDP */ diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c index cdcd8d2e851..fd41045f3e3 100644 --- a/net/socket/setsockopt.c +++ b/net/socket/setsockopt.c @@ -45,11 +45,14 @@ #include #include #include +#include +#include #include #include #include "socket/socket.h" +#include "usrsock/usrsock.h" #include "utils/utils.h" /**************************************************************************** @@ -115,6 +118,39 @@ int psock_setsockopt(FAR struct socket *psock, int level, int option, goto errout; } +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + int ret; + + DEBUGASSERT(conn); + + /* Some of the socket options are handled from this function. */ + + switch (option) + { + case SO_RCVTIMEO: /* Rx timeouts can be handled at NuttX side, thus + * simplify daemon implementation. */ + case SO_SNDTIMEO: /* Rx timeouts can be handled at NuttX side, thus + * simplify daemon implementation. */ + break; + + default: /* Other options are passed to usrsock daemon. */ + { + ret = usrsock_setsockopt(conn, level, option, value, value_len); + if (ret < 0) + { + errcode = -ret; + goto errout; + } + + return OK; + } + } + } +#endif + /* Process the option */ switch (option) diff --git a/net/socket/socket.c b/net/socket/socket.c index 56f6ddfe9c2..04d595a8315 100644 --- a/net/socket/socket.c +++ b/net/socket/socket.c @@ -52,6 +52,7 @@ #include "udp/udp.h" #include "pkt/pkt.h" #include "local/local.h" +#include "usrsock/usrsock.h" /**************************************************************************** * Private Functions @@ -65,7 +66,7 @@ * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK static int psock_tcp_alloc(FAR struct socket *psock) { /* Allocate the TCP connection structure */ @@ -90,7 +91,7 @@ static int psock_tcp_alloc(FAR struct socket *psock) psock->s_conn = conn; return OK; } -#endif /* CONFIG_NET_TCP */ +#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** * Name: psock_udp_alloc @@ -100,7 +101,7 @@ static int psock_tcp_alloc(FAR struct socket *psock) * ****************************************************************************/ -#ifdef CONFIG_NET_UDP +#ifdef NET_UDP_HAVE_STACK static int psock_udp_alloc(FAR struct socket *psock) { /* Allocate the UDP connection structure */ @@ -125,7 +126,7 @@ static int psock_udp_alloc(FAR struct socket *psock) psock->s_conn = conn; return OK; } -#endif /* CONFIG_NET_UDP */ +#endif /* NET_UDP_HAVE_STACK */ /**************************************************************************** * Name: psock_pkt_alloc @@ -250,6 +251,47 @@ int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) int ret; int errcode; +#ifdef CONFIG_NET_USRSOCK + switch (domain) + { + default: + break; + + case PF_INET: + case PF_INET6: + { +#ifndef CONFIG_NET_USRSOCK_UDP + if (type == SOCK_DGRAM) + break; +#endif +#ifndef CONFIG_NET_USRSOCK_TCP + if (type == SOCK_STREAM) + break; +#endif + psock->s_type = 0; + psock->s_conn = NULL; + + ret = usrsock_socket(domain, type, protocol, psock); + if (ret >= 0) + { + /* Successfully handled and opened by usrsock daemon. */ + + return OK; + } + else if (ret == -ENETDOWN) + { + /* Net down means that usrsock daemon is not running. + * Attempt to open socket with kernel networking stack. */ + } + else + { + errcode = -ret; + goto errout; + } + } + } +#endif /* CONFIG_NET_USRSOCK */ + /* Only PF_INET, PF_INET6 or PF_PACKET domains supported */ switch (domain) @@ -400,9 +442,13 @@ int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) if (ipdomain) #endif { +#ifdef NET_TCP_HAVE_STACK /* Allocate and attach the TCP connection structure */ ret = psock_tcp_alloc(psock); +#else + ret = -ENETDOWN; +#endif } #endif /* CONFIG_NET_TCP */ @@ -438,9 +484,13 @@ int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) if (ipdomain) #endif { +#ifdef NET_UDP_HAVE_STACK /* Allocate and attach the UDP connection structure */ ret = psock_udp_alloc(psock); +#else + ret = -ENETDOWN; +#endif } #endif /* CONFIG_NET_UDP */ diff --git a/net/socket/socket.h b/net/socket/socket.h index e9bfa161db3..4141cf7477d 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -49,6 +49,7 @@ #include #include +#include "tcp/tcp.h" /**************************************************************************** * Pre-processor Definitions @@ -152,7 +153,7 @@ extern "C" * Public Function Prototypes ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK struct tcp_conn_s; /* Forward reference */ #endif @@ -243,7 +244,7 @@ FAR struct socket *sockfd_socket(int sockfd); * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK int net_startmonitor(FAR struct socket *psock); #endif @@ -265,7 +266,7 @@ int net_startmonitor(FAR struct socket *psock); * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK void net_stopmonitor(FAR struct tcp_conn_s *conn); #endif @@ -287,7 +288,7 @@ void net_stopmonitor(FAR struct tcp_conn_s *conn); * ****************************************************************************/ -#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK void net_lostconnection(FAR struct socket *psock, uint16_t flags); #endif diff --git a/net/tcp/Kconfig b/net/tcp/Kconfig index 59fa04d018b..8ac6aab1288 100644 --- a/net/tcp/Kconfig +++ b/net/tcp/Kconfig @@ -9,9 +9,16 @@ config NET_TCP bool "TCP/IP Networking" default n ---help--- - TCP support on or off + Enable or disable TCP networking support. -if NET_TCP +config NET_TCP_NO_STACK + bool "Disable TCP/IP Stack" + default n + select NET_TCP + ---help--- + Build without TCP/IP stack even if TCP networking support enabled. + +if NET_TCP && !NET_TCP_NO_STACK config NET_TCPURGDATA bool "Urgent data" @@ -184,5 +191,5 @@ config NET_SENDFILE Support larger, higher performance sendfile() for transferring files out a TCP connection. -endif # NET_TCP +endif # NET_TCP && !NET_TCP_NO_STACK endmenu # TCP/IP Networking diff --git a/net/tcp/Make.defs b/net/tcp/Make.defs index 520e601f4c1..29b6167a65f 100644 --- a/net/tcp/Make.defs +++ b/net/tcp/Make.defs @@ -36,6 +36,7 @@ # TCP/IP source files ifeq ($(CONFIG_NET_TCP),y) +ifneq ($(CONFIG_NET_TCP_NO_STACK),y) # Socket layer @@ -73,4 +74,5 @@ endif DEPPATH += --dep-path tcp VPATH += :tcp +endif # !CONFIG_NET_TCP_NO_STACK endif # CONFIG_NET_TCP diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index c53475d75a4..f2a5115e53a 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -48,11 +48,14 @@ #include #include -#ifdef CONFIG_NET_TCP +#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ + +#define NET_TCP_HAVE_STACK 1 + /* Conditions for support TCP poll/select operations */ #if !defined(CONFIG_DISABLE_POLL) && CONFIG_NSOCKET_DESCRIPTORS > 0 && \ @@ -1306,5 +1309,5 @@ int tcp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds); } #endif -#endif /* CONFIG_NET_TCP */ +#endif /* CONFIG_NET_TCP && !CONFIG_NET_TCP_NO_STACK */ #endif /* _NET_TCP_TCP_H */ diff --git a/net/tcp/tcp_devpoll.c b/net/tcp/tcp_devpoll.c index 6a7f16c513b..d292d464738 100644 --- a/net/tcp/tcp_devpoll.c +++ b/net/tcp/tcp_devpoll.c @@ -46,6 +46,7 @@ #if defined(CONFIG_NET) && defined(CONFIG_NET_TCP) #include +#include #include #include diff --git a/net/tcp/tcp_finddev.c b/net/tcp/tcp_finddev.c index ef6ae8f0d25..e3cf0b32884 100644 --- a/net/tcp/tcp_finddev.c +++ b/net/tcp/tcp_finddev.c @@ -250,7 +250,6 @@ int tcp_local_ipv6_device(FAR struct tcp_conn_s *conn) #else return tcp_find_ipv6_device(conn, NULL); #endif - } #endif /* CONFIG_NET_IPv6 */ diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c index 184dfebb01c..1918bd34924 100644 --- a/net/tcp/tcp_input.c +++ b/net/tcp/tcp_input.c @@ -2,7 +2,7 @@ * net/tcp/tcp_input.c * Handling incoming TCP input * - * Copyright (C) 2007-2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2014, 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Adapted for NuttX from logic in uIP which also has a BSD-like license: diff --git a/net/tcp/tcp_send_unbuffered.c b/net/tcp/tcp_send_unbuffered.c index 6c3688d85ad..5a2ccc98c42 100644 --- a/net/tcp/tcp_send_unbuffered.c +++ b/net/tcp/tcp_send_unbuffered.c @@ -129,7 +129,7 @@ struct send_s * TRUE:timeout FALSE:no timeout * * Assumptions: - * Running at the interrupt level + * The network is locked. * ****************************************************************************/ @@ -139,7 +139,7 @@ static inline int send_timeout(FAR struct send_s *pstate) FAR struct socket *psock; /* Check for a timeout configured via setsockopts(SO_SNDTIMEO). - * If none... we well let the send wait forever. + * If none... we will let the send wait forever. */ psock = pstate->snd_sock; @@ -173,7 +173,7 @@ static inline int send_timeout(FAR struct send_s *pstate) * None * * Assumptions: - * Running at the interrupt level + * The network is locked. * ****************************************************************************/ @@ -224,7 +224,7 @@ static inline void tcpsend_ipselect(FAR struct net_driver_s *dev, * None * * Assumptions: - * Running at the interrupt level + * The network is locked. * ****************************************************************************/ @@ -278,7 +278,7 @@ static inline bool psock_send_addrchck(FAR struct tcp_conn_s *conn) * None * * Assumptions: - * Running at the interrupt level + * The network is locked. * ****************************************************************************/ @@ -708,8 +708,6 @@ static inline void send_txnotify(FAR struct socket *psock, * In this case the process will also receive a SIGPIPE unless * MSG_NOSIGNAL is set. * - * Assumptions: - * ****************************************************************************/ ssize_t psock_tcp_send(FAR struct socket *psock, diff --git a/net/tcp/tcp_timer.c b/net/tcp/tcp_timer.c index eeabb0910c4..245e849ec67 100644 --- a/net/tcp/tcp_timer.c +++ b/net/tcp/tcp_timer.c @@ -46,6 +46,7 @@ #if defined(CONFIG_NET) && defined(CONFIG_NET_TCP) #include +#include #include #include diff --git a/net/udp/Kconfig b/net/udp/Kconfig index a32f34534db..2c8e85d70b4 100644 --- a/net/udp/Kconfig +++ b/net/udp/Kconfig @@ -12,7 +12,14 @@ config NET_UDP ---help--- Enable or disable UDP networking support. -if NET_UDP +config NET_UDP_NO_STACK + bool "Disable UDP/IP Stack" + default n + select NET_UDP + ---help--- + Build without UDP/IP stack even if UDP networking support enabled. + +if NET_UDP && !NET_UDP_NO_STACK config NET_UDP_CHECKSUMS bool "UDP checksums" @@ -55,5 +62,5 @@ config NET_UDP_READAHEAD default y select NET_IOB -endif # NET_UDP +endif # NET_UDP && !NET_UDP_NO_STACK endmenu # UDP Networking diff --git a/net/udp/Make.defs b/net/udp/Make.defs index 76c72b33ab0..4b3992911df 100644 --- a/net/udp/Make.defs +++ b/net/udp/Make.defs @@ -36,6 +36,7 @@ # UDP source files ifeq ($(CONFIG_NET_UDP),y) +ifneq ($(CONFIG_NET_UDP_NO_STACK),y) # Socket layer @@ -57,4 +58,5 @@ NET_CSRCS += udp_callback.c udp_ipselect.c DEPPATH += --dep-path udp VPATH += :udp +endif # !CONFIG_NET_UDP_NO_STACK endif # CONFIG_NET_UDP diff --git a/net/udp/udp.h b/net/udp/udp.h index f8584bff0b9..1aac4a83280 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -51,11 +51,14 @@ # include #endif -#ifdef CONFIG_NET_UDP +#if defined(CONFIG_NET_UDP) && !defined(CONFIG_NET_UDP_NO_STACK) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ + +#define NET_UDP_HAVE_STACK 1 + /* Conditions for support UDP poll/select operations */ #if !defined(CONFIG_DISABLE_POLL) && CONFIG_NSOCKET_DESCRIPTORS > 0 && \ @@ -510,5 +513,5 @@ int udp_pollteardown(FAR struct socket *psock, FAR struct pollfd *fds); } #endif -#endif /* CONFIG_NET_UDP */ +#endif /* CONFIG_NET_UDP && !CONFIG_NET_UDP_NO_STACK */ #endif /* __NET_UDP_UDP_H */ diff --git a/net/usrsock/Kconfig b/net/usrsock/Kconfig new file mode 100644 index 00000000000..677200cb678 --- /dev/null +++ b/net/usrsock/Kconfig @@ -0,0 +1,39 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +menu "User-space networking stack API" + +config NET_USRSOCK + bool "User-space networking stack API" + default n + depends on NET + ---help--- + Enable or disable user-space networking stack support. + +if NET_USRSOCK + +config NET_USRSOCK_CONNS + int "Number of usrsock connections" + default 6 + ---help--- + Maximum number of usrsock connections (all tasks). + + Note: Usrsock daemon can impose additional restrictions for + maximum number of concurrent connections supported. + +config NET_USRSOCK_UDP + bool "User-space daemon provides UDP sockets" + default n + select NET_UDP + ---help--- + +config NET_USRSOCK_TCP + bool "User-space daemon provides TCP sockets" + default n + select NET_TCP + ---help--- + +endif # NET_USRSOCK +endmenu # User-space networking stack API diff --git a/net/usrsock/Make.defs b/net/usrsock/Make.defs new file mode 100644 index 00000000000..2aa069b077f --- /dev/null +++ b/net/usrsock/Make.defs @@ -0,0 +1,51 @@ +############################################################################ +# net/usrsock/Make.defs +# +# Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. +# Author: Jussi Kivilinna +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +# User Socket source files + +ifeq ($(CONFIG_NET_USRSOCK),y) + +NET_CSRCS += usrsock_close.c usrsock_conn.c usrsock_bind.c usrsock_connect.c +NET_CSRCS += usrsock_dev.c +NET_CSRCS += usrsock_event.c usrsock_getsockname.c usrsock_getsockopt.c +NET_CSRCS += usrsock_poll.c usrsock_recvfrom.c usrsock_sendto.c +NET_CSRCS += usrsock_setsockopt.c usrsock_socket.c + +# Include User Socket build support + +DEPPATH += --dep-path usrsock +VPATH += :usrsock + +endif # CONFIG_NET_USRSOCK diff --git a/net/usrsock/usrsock.h b/net/usrsock/usrsock.h new file mode 100644 index 00000000000..55d92187e28 --- /dev/null +++ b/net/usrsock/usrsock.h @@ -0,0 +1,553 @@ +/**************************************************************************** + * net/usrsock/usrsock.h + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __NET_USRSOCK_USRSOCK_H +#define __NET_USRSOCK_USRSOCK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_NET_USRSOCK + +#include +#include +#include + +#include "devif/devif.h" +#include "socket/socket.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/* Internal socket type/domain for marking usrsock sockets */ + +#define SOCK_USRSOCK_TYPE 0x7f +#define PF_USRSOCK_DOMAIN 0x7f + +/* Internal event flags */ + +#define USRSOCK_EVENT_REQ_COMPLETE (1 << 14) +#define USRSOCK_EVENT_CONNECT_RESP (1 << 15) +#define USRSOCK_EVENT_INTERNAL_MASK 0xf000U + +/**************************************************************************** + * Public Type Definitions + ****************************************************************************/ + +struct usrsockdev_s; + +enum usrsock_conn_state_e +{ + USRSOCK_CONN_STATE_UNINITIALIZED = 0, + USRSOCK_CONN_STATE_ABORTED, + USRSOCK_CONN_STATE_READY, + USRSOCK_CONN_STATE_CONNECTING, +}; + +struct iovec +{ + FAR void *iov_base; /* Starting address */ + size_t iov_len; /* Number of bytes to transfer */ +}; + +struct usrsock_conn_s +{ + dq_entry_t node; /* Supports a doubly linked list */ + uint8_t crefs; /* Reference counts on this instance */ + + enum usrsock_conn_state_e state; /* State of kernel<->daemon link for conn */ + bool connected; /* Socket has been connected */ + int8_t type; /* Socket type (SOCK_STREAM, etc) */ + int16_t usockid; /* Connection number used for kernel<->daemon */ + uint16_t flags; /* Socket state flags */ + struct usrsockdev_s *dev; /* Device node used for this conn */ + + struct + { + uint8_t xid; /* Expected message exchange id */ + bool inprogress; /* Request was received but daemon is still processing */ + uint16_t valuelen; /* Length of value from daemon */ + uint16_t valuelen_nontrunc; /* Actual length of value at daemon */ + int result; /* Result for request */ + + struct + { + FAR struct iovec *iov; /* Data request input buffers */ + int iovcnt; /* Number of input buffers */ + size_t total; /* Total length of buffers */ + size_t pos; /* Writer position on input buffer */ + } datain; + } resp; + + /* Defines the list of usrsock callbacks */ + + FAR struct devif_callback_s *list; +}; + +struct usrsock_reqstate_s +{ + FAR struct usrsock_conn_s *conn; /* Reference to connection structure */ + FAR struct devif_callback_s *cb; /* Reference to callback instance */ + sem_t recvsem; /* Semaphore signals recv completion */ + int result; /* OK on success, otherwise a negated errno. */ + bool completed; +}; + +struct usrsock_data_reqstate_s +{ + struct usrsock_reqstate_s reqstate; + uint16_t valuelen; + uint16_t valuelen_nontrunc; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +# define EXTERN extern "C" +extern "C" +{ +#else +# define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: usrsock_initialize() + * + * Description: + * Initialize the User Socket connection structures. Called once and only + * from the networking layer. + * + ****************************************************************************/ + +void usrsock_initialize(void); + +/**************************************************************************** + * Name: usrsock_alloc() + * + * Description: + * Allocate a new, uninitialized usrsock connection structure. This is + * normally something done by the implementation of the socket() API + * + ****************************************************************************/ + +FAR struct usrsock_conn_s *usrsock_alloc(void); + +/**************************************************************************** + * Name: usrsock_free() + * + * Description: + * Free a usrsock connection structure that is no longer in use. This should + * be done by the implementation of close(). + * + ****************************************************************************/ + +void usrsock_free(FAR struct usrsock_conn_s *conn); + +/**************************************************************************** + * Name: usrsock_nextconn() + * + * Description: + * Traverse the list of allocated usrsock connections + * + * Assumptions: + * This function is called from usrsock device logic. + * + ****************************************************************************/ + +FAR struct usrsock_conn_s *usrsock_nextconn(FAR struct usrsock_conn_s *conn); + +/**************************************************************************** + * Name: usrsock_connidx() + ****************************************************************************/ + +int usrsock_connidx(FAR struct usrsock_conn_s *conn); + +/**************************************************************************** + * Name: usrsock_active() + * + * Description: + * Find a connection structure that is the appropriate + * connection for usrsock + * + * Assumptions: + * + ****************************************************************************/ + +FAR struct usrsock_conn_s *usrsock_active(int16_t usockid); + +/**************************************************************************** + * Name: usrsock_setup_request_callback() + ****************************************************************************/ + +int usrsock_setup_request_callback(FAR struct usrsock_conn_s *conn, + FAR struct usrsock_reqstate_s *pstate, + FAR devif_callback_event_t event, + uint16_t flags); + +/**************************************************************************** + * Name: usrsock_setup_data_request_callback() + ****************************************************************************/ + +int usrsock_setup_data_request_callback(FAR struct usrsock_conn_s *conn, + FAR struct usrsock_data_reqstate_s *pstate, + FAR devif_callback_event_t event, + uint16_t flags); + +/**************************************************************************** + * Name: usrsock_teardown_request_callback() + ****************************************************************************/ + +void usrsock_teardown_request_callback(FAR struct usrsock_reqstate_s *pstate); + +/**************************************************************************** + * Name: usrsock_teardown_data_request_callback() + ****************************************************************************/ + +#define usrsock_teardown_data_request_callback(datastate) \ + usrsock_teardown_request_callback(&(datastate)->reqstate) + +/**************************************************************************** + * Name: usrsock_event + * + * Description: + * Handler for received connection events + * + ****************************************************************************/ + +int usrsock_event(FAR struct usrsock_conn_s *conn, uint16_t events); + +/**************************************************************************** + * Name: usrsockdev_do_request + ****************************************************************************/ + +int usrsockdev_do_request(FAR struct usrsock_conn_s *conn, + FAR struct iovec *iov, unsigned int iovcnt); + +/**************************************************************************** + * Name: usrsockdev_register + * + * Description: + * Register /dev/usrsock + * + ****************************************************************************/ + +void usrsockdev_register(void); + +/**************************************************************************** + * Name: usrsock_socket + * + * Description: + * socket() creates an endpoint for communication and returns a socket + * structure. + * + * Input Parameters: + * domain (see sys/socket.h) + * type (see sys/socket.h) + * protocol (see sys/socket.h) + * psock A pointer to a user allocated socket structure to be initialized. + * + * Returned Value: + * 0 on success; negative error-code on error + * + * EACCES + * Permission to create a socket of the specified type and/or protocol + * is denied. + * EAFNOSUPPORT + * The implementation does not support the specified address family. + * EINVAL + * Unknown protocol, or protocol family not available. + * EMFILE + * Process file table overflow. + * ENFILE + * The system limit on the total number of open files has been reached. + * ENOBUFS or ENOMEM + * Insufficient memory is available. The socket cannot be created until + * sufficient resources are freed. + * EPROTONOSUPPORT + * The protocol type or the specified protocol is not supported within + * this domain. + * + * Assumptions: + * + ****************************************************************************/ + +int usrsock_socket(int domain, int type, int protocol, FAR struct socket *psock); + +/**************************************************************************** + * Name: usrsock_close + * + * Description: + * Performs the close operation on a usrsock connection instance + * + * Input Parameters: + * conn usrsock connection instance + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately. + * + * Assumptions: + * + ****************************************************************************/ + +int usrsock_close(FAR struct usrsock_conn_s *conn); + +/**************************************************************************** + * Name: usrsock_bind + * + * Description: + * usrsock_bind() gives the socket 'conn' the local address 'addr'. 'addr' + * is 'addrlen' bytes long. Traditionally, this is called "assigning a name + * to a socket." When a socket is created with socket, it exists in a name + * space (address family) but has no name assigned. + * + * Input Parameters: + * conn usrsock socket connection structure + * addr Socket local address + * addrlen Length of 'addr' + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately + * + * EACCES + * The address is protected, and the user is not the superuser. + * EADDRINUSE + * The given address is already in use. + * EINVAL + * The socket is already bound to an address. + * ENOTSOCK + * psock is a descriptor for a file, not a socket. + * + * Assumptions: + * + ****************************************************************************/ + +int usrsock_bind(FAR struct usrsock_conn_s *conn, + FAR const struct sockaddr *addr, + socklen_t addrlen); + +/**************************************************************************** + * Name: usrsock_connect + * + * Description: + * Perform a usrsock connection + * + * Input Parameters: + * psock A reference to the socket structure of the socket to be connected + * addr The address of the remote server to connect to + * addrlen Length of address buffer + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +int usrsock_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); + +/**************************************************************************** + * Name: usrsock_poll + * + * Description: + * The standard poll() operation redirects operations on socket descriptors + * to this function. + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * fds - The structure describing the events to be monitored. + * setup - true: Setup up the poll; false: Teardown the poll + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +int usrsock_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup); +#endif + +/**************************************************************************** + * Name: usrsock_sendto + * + * Description: + * If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) + * socket, the parameters to and 'tolen' are ignored (and the error EISCONN + * may be returned when they are not NULL and 0), and the error ENOTCONN is + * returned when the socket was not actually connected. + * + * Input Parameters: + * psock A reference to the socket structure of the socket to be connected + * buf Data to send + * len Length of data to send + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +ssize_t usrsock_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, FAR const struct sockaddr *to, + socklen_t tolen); + +/**************************************************************************** + * Name: usrsock_recvfrom + * + * Description: + * recvfrom() receives messages from a socket, and may be used to receive + * data on a socket whether or not it is connection-oriented. + * + * If from is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument fromlen + * initialized to the size of the buffer associated with from, and modified + * on return to indicate the actual size of the address stored there. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + ****************************************************************************/ + +ssize_t usrsock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + FAR struct sockaddr *from, FAR socklen_t *fromlen); + +/**************************************************************************** + * Name: usrsock_getsockopt + * + * Description: + * getsockopt() retrieve thse value for the option specified by the + * 'option' argument for the socket specified by the 'psock' argument. If + * the size of the option value is greater than 'value_len', the value + * stored in the object pointed to by the 'value' argument will be silently + * truncated. Otherwise, the length pointed to by the 'value_len' argument + * will be modified to indicate the actual length of the'value'. + * + * The 'level' argument specifies the protocol level of the option. To + * retrieve options at the socket level, specify the level argument as + * SOL_SOCKET. + * + * See a complete list of values for the 'option' argument. + * + * Input Parameters: + * conn usrsock socket connection structure + * level Protocol level to set the option + * option identifies the option to get + * value Points to the argument value + * value_len The length of the argument value + * + ****************************************************************************/ + +int usrsock_getsockopt(FAR struct usrsock_conn_s *conn, int level, int option, + FAR void *value, FAR socklen_t *value_len); + +/**************************************************************************** + * Name: usrsock_setsockopt + * + * Description: + * psock_setsockopt() sets the option specified by the 'option' argument, + * at the protocol level specified by the 'level' argument, to the value + * pointed to by the 'value' argument for the socket on the 'psock' argument. + * + * The 'level' argument specifies the protocol level of the option. To set + * options at the socket level, specify the level argument as SOL_SOCKET. + * + * See a complete list of values for the 'option' argument. + * + * Input Parameters: + * conn usrsock socket connection structure + * level Protocol level to set the option + * option identifies the option to set + * value Points to the argument value + * value_len The length of the argument value + * + ****************************************************************************/ + +int usrsock_setsockopt(FAR struct usrsock_conn_s *conn, int level, int option, + FAR const void *value, FAR socklen_t value_len); + +/**************************************************************************** + * Name: usrsock_getsockname + * + * Description: + * The getsockname() function retrieves the locally-bound name of the + * specified socket, stores this address in the sockaddr structure pointed + * to by the 'addr' argument, and stores the length of this address in the + * object pointed to by the 'addrlen' argument. + * + * If the actual length of the address is greater than the length of the + * supplied sockaddr structure, the stored address will be truncated. + * + * If the socket has not been bound to a local name, the value stored in + * the object pointed to by address is unspecified. + * + * Input Parameters: + * conn usrsock socket connection structure + * addr sockaddr structure to receive data [out] + * addrlen Length of sockaddr structure [in/out] + * + ****************************************************************************/ + +int usrsock_getsockname(FAR struct usrsock_conn_s *conn, + FAR struct sockaddr *addr, FAR socklen_t *addrlen); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_NET_USRSOCK */ +#endif /* __NET_USRSOCK_USRSOCK_H */ diff --git a/net/usrsock/usrsock_bind.c b/net/usrsock/usrsock_bind.c new file mode 100644 index 00000000000..8ce21f07623 --- /dev/null +++ b/net/usrsock/usrsock_bind.c @@ -0,0 +1,221 @@ +/**************************************************************************** + * net/usrsock/usrsock_bind.c + * + * Copyright (C) 2015-2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t bind_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->result = -ECONNABORTED; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->result = conn->resp.result; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_bind_request + ****************************************************************************/ + +static int do_bind_request(FAR struct usrsock_conn_s *conn, + FAR const struct sockaddr *addr, + socklen_t addrlen) +{ + struct usrsock_request_bind_s req = {}; + struct iovec bufs[2]; + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_BIND; + req.usockid = conn->usockid; + req.addrlen = addrlen; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + bufs[1].iov_base = (FAR void *)addr; + bufs[1].iov_len = req.addrlen; + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usrsock_bind + * + * Description: + * usrsock_bind() gives the socket 'conn' the local address 'addr'. 'addr' + * is 'addrlen' bytes long. Traditionally, this is called "assigning a name + * to a socket." When a socket is created with socket, it exists in a name + * space (address family) but has no name assigned. + * + * Parameters: + * conn usrsock socket connection structure + * addr Socket local address + * addrlen Length of 'addr' + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately + * + * EACCES + * The address is protected, and the user is not the superuser. + * EADDRINUSE + * The given address is already in use. + * EINVAL + * The socket is already bound to an address. + * ENOTSOCK + * psock is a descriptor for a file, not a socket. + * + * Assumptions: + * + ****************************************************************************/ + +int usrsock_bind(FAR struct usrsock_conn_s *conn, + FAR const struct sockaddr *addr, + socklen_t addrlen) +{ + struct usrsock_reqstate_s state = {}; + ssize_t ret; + + DEBUGASSERT(conn); + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Invalid state or closed by daemon. */ + + ninfo("usockid=%d; connect() with uninitialized usrsock.\n", + conn->usockid); + + ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE : -ECONNRESET; + goto errout_unlock; + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_request_callback(conn, &state, bind_event, + USRSOCK_EVENT_ABORT | + USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + /* Request user-space daemon to close socket. */ + + ret = do_bind_request(conn, addr, addrlen); + if (ret >= 0) + { + /* Wait for completion of request. */ + + while (net_lockedwait(&state.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + ret = state.result; + } + + usrsock_teardown_request_callback(&state); + +errout_unlock: + net_unlock(); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_close.c b/net/usrsock/usrsock_close.c new file mode 100644 index 00000000000..bb6f052732f --- /dev/null +++ b/net/usrsock/usrsock_close.c @@ -0,0 +1,205 @@ +/**************************************************************************** + * net/usrsock/usrsock_close.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t close_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + conn->state = USRSOCK_CONN_STATE_ABORTED; + pstate->result = -ECONNABORTED; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->result = conn->resp.result; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_close_request + ****************************************************************************/ + +static int do_close_request(FAR struct usrsock_conn_s *conn) +{ + struct usrsock_request_close_s req = {}; + struct iovec bufs[1]; + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_CLOSE; + req.usockid = conn->usockid; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_close + * + * Description: + * + ****************************************************************************/ + +int usrsock_close(FAR struct usrsock_conn_s *conn) +{ + struct usrsock_reqstate_s state = {}; + int ret; + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Already closed? */ + + ninfo("usockid=%d; already closed.\n", conn->usockid); + + ret = OK; + goto close_out; + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_request_callback(conn, &state, close_event, + USRSOCK_EVENT_ABORT | + USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout; + } + + /* Request user-space daemon to close socket. */ + + ret = do_close_request(conn); + if (ret < 0) + { + ret = OK; /* Error? return OK for close. */ + } + else + { + /* Wait for completion of request. */ + + while (net_lockedwait(&state.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + ret = state.result; + if (ret < 0) + { + /* TODO: Error handling for close? Mark closed anyway? There is not + * much we can do if this happens. + */ + ninfo("user-space daemon reported error %d for usockid=%d\n", + state.result, conn->usockid); + + ret = OK; + } + } + + usrsock_teardown_request_callback(&state); + +close_out: + conn->state = USRSOCK_CONN_STATE_UNINITIALIZED; + conn->usockid = -1; + +errout: + net_unlock(); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_conn.c b/net/usrsock/usrsock_conn.c new file mode 100644 index 00000000000..0b022762467 --- /dev/null +++ b/net/usrsock/usrsock_conn.c @@ -0,0 +1,347 @@ +/**************************************************************************** + * net/usrsock/usrsock_conn.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The array containing all usrsock connections. */ + +struct usrsock_conn_s g_usrsock_connections[CONFIG_NET_USRSOCK_CONNS]; + +/* A list of all free usrsock connections */ + +static dq_queue_t g_free_usrsock_connections; +static sem_t g_free_sem; + +/* A list of all allocated usrsock connections */ + +static dq_queue_t g_active_usrsock_connections; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: _usrsock_semtake() and _usrsock_semgive() + * + * Description: + * Take/give semaphore + * + ****************************************************************************/ + +static void _usrsock_semtake(FAR sem_t *sem) +{ + /* Take the semaphore (perhaps waiting) */ + + while (net_lockedwait(sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + DEBUGASSERT(*get_errno_ptr() == EINTR); + } +} + +static void _usrsock_semgive(FAR sem_t *sem) +{ + (void)sem_post(sem); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usrsock_alloc() + * + * Description: + * Allocate a new, uninitialized usrsock connection structure. This is + * normally something done by the implementation of the socket() API + * + ****************************************************************************/ + +FAR struct usrsock_conn_s *usrsock_alloc(void) +{ + FAR struct usrsock_conn_s *conn; + + /* The free list is only accessed from user, non-interrupt level and + * is protected by a semaphore (that behaves like a mutex). + */ + + _usrsock_semtake(&g_free_sem); + conn = (FAR struct usrsock_conn_s *)dq_remfirst(&g_free_usrsock_connections); + if (conn) + { + /* Make sure that the connection is marked as uninitialized */ + + memset(conn, 0, sizeof(*conn)); + conn->dev = NULL; + conn->usockid = -1; + conn->state = USRSOCK_CONN_STATE_UNINITIALIZED; + conn->list = NULL; + conn->connected = false; + + /* Enqueue the connection into the active list */ + + dq_addlast(&conn->node, &g_active_usrsock_connections); + } + + _usrsock_semgive(&g_free_sem); + return conn; +} + +/**************************************************************************** + * Name: usrsock_free() + * + * Description: + * Free a usrsock connection structure that is no longer in use. This should + * be done by the implementation of close(). + * + ****************************************************************************/ + +void usrsock_free(FAR struct usrsock_conn_s *conn) +{ + /* The free list is only accessed from user, non-interrupt level and + * is protected by a semaphore (that behaves like a mutex). + */ + + DEBUGASSERT(conn->crefs == 0); + + _usrsock_semtake(&g_free_sem); + + /* Remove the connection from the active list */ + + dq_rem(&conn->node, &g_active_usrsock_connections); + + /* Reset structure */ + + memset(conn, 0, sizeof(*conn)); + conn->dev = NULL; + conn->usockid = -1; + conn->state = USRSOCK_CONN_STATE_UNINITIALIZED; + conn->list = NULL; + + /* Free the connection */ + + dq_addlast(&conn->node, &g_free_usrsock_connections); + _usrsock_semgive(&g_free_sem); +} + +/**************************************************************************** + * Name: usrsock_nextconn() + * + * Description: + * Traverse the list of allocated usrsock connections + * + * Assumptions: + * This function is called from usrsock device logic. + * + ****************************************************************************/ + +FAR struct usrsock_conn_s *usrsock_nextconn(FAR struct usrsock_conn_s *conn) +{ + if (!conn) + { + return (FAR struct usrsock_conn_s *)g_active_usrsock_connections.head; + } + else + { + return (FAR struct usrsock_conn_s *)conn->node.flink; + } +} + +/**************************************************************************** + * Name: usrsock_connidx() + ****************************************************************************/ + +int usrsock_connidx(FAR struct usrsock_conn_s *conn) +{ + int idx = conn - g_usrsock_connections; + + DEBUGASSERT(idx >= 0); + DEBUGASSERT(idx < ARRAY_SIZE(g_usrsock_connections)); + + return idx; +} + +/**************************************************************************** + * Name: usrsock_active() + * + * Description: + * Find a connection structure that is the appropriate + * connection for usrsock + * + * Assumptions: + * + ****************************************************************************/ + +FAR struct usrsock_conn_s *usrsock_active(int16_t usockid) +{ + FAR struct usrsock_conn_s *conn = NULL; + + while ((conn = usrsock_nextconn(conn)) != NULL) + { + if (conn->usockid == usockid) + return conn; + } + + return NULL; +} + +/**************************************************************************** + * Name: usrsock_setup_request_callback() + ****************************************************************************/ + +int usrsock_setup_request_callback(FAR struct usrsock_conn_s *conn, + FAR struct usrsock_reqstate_s *pstate, + FAR devif_callback_event_t event, + uint16_t flags) +{ + int ret = -EBUSY; + + (void)sem_init(&pstate->recvsem, 0, 0); + pstate->conn = conn; + pstate->result = -EAGAIN; + pstate->completed = false; + + /* Set up the callback in the connection */ + + pstate->cb = devif_callback_alloc(NULL, &conn->list); + if (pstate->cb) + { + /* Set up the connection "interrupt" handler */ + + pstate->cb->flags = flags; + pstate->cb->priv = (FAR void *)pstate; + pstate->cb->event = event; + + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: usrsock_setup_data_request_callback() + ****************************************************************************/ + +int usrsock_setup_data_request_callback(FAR struct usrsock_conn_s *conn, + FAR struct usrsock_data_reqstate_s *pstate, + FAR devif_callback_event_t event, + uint16_t flags) +{ + pstate->valuelen = 0; + pstate->valuelen_nontrunc = 0; + return usrsock_setup_request_callback(conn, &pstate->reqstate, event, flags); +} + +/**************************************************************************** + * Name: usrsock_teardown_request_callback() + ****************************************************************************/ + +void usrsock_teardown_request_callback(FAR struct usrsock_reqstate_s *pstate) +{ + FAR struct usrsock_conn_s *conn = pstate->conn; + + /* Make sure that no further events are processed */ + + devif_conn_callback_free(NULL, pstate->cb, &conn->list); + + pstate->cb = NULL; +} + +/**************************************************************************** + * Name: usrsock_initialize() + * + * Description: + * Initialize the User Socket connection structures. Called once and only + * from the networking layer. + * + ****************************************************************************/ + +void usrsock_initialize(void) +{ + int i; + + /* Initialize the queues */ + + dq_init(&g_free_usrsock_connections); + dq_init(&g_active_usrsock_connections); + sem_init(&g_free_sem, 0, 1); + + for (i = 0; i < CONFIG_NET_USRSOCK_CONNS; i++) + { + FAR struct usrsock_conn_s *conn = &g_usrsock_connections[i]; + + /* Mark the connection closed and move it to the free list */ + + memset(conn, 0, sizeof(*conn)); + conn->dev = NULL; + conn->usockid = -1; + conn->state = USRSOCK_CONN_STATE_UNINITIALIZED; + conn->list = NULL; + conn->flags = 0; + dq_addlast(&conn->node, &g_free_usrsock_connections); + } + + /* Register /dev/usrsock character device. */ + + usrsockdev_register(); +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_connect.c b/net/usrsock/usrsock_connect.c new file mode 100644 index 00000000000..45ed8c1dd64 --- /dev/null +++ b/net/usrsock/usrsock_connect.c @@ -0,0 +1,257 @@ +/**************************************************************************** + * net/usrsock/usrsock_connect.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t connect_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->result = -ECONNABORTED; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->result = conn->resp.result; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_connect_request + ****************************************************************************/ + +static int do_connect_request(FAR struct usrsock_conn_s *conn, + FAR const struct sockaddr *addr, + socklen_t addrlen) +{ + struct usrsock_request_connect_s req = {}; + struct iovec bufs[2]; + + if (addrlen > UINT16_MAX) + { + addrlen = UINT16_MAX; + } + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_CONNECT; + req.usockid = conn->usockid; + req.addrlen = addrlen; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + bufs[1].iov_base = (FAR void *)addr; + bufs[1].iov_len = addrlen; + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usrsock_connect + * + * Description: + * Perform a usrsock connection + * + * Parameters: + * psock - A reference to the socket structure of the socket to be connected + * addr The address of the remote server to connect to + * addrlen Length of address buffer + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +int usrsock_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen) +{ + FAR struct usrsock_conn_s *conn = psock->s_conn; + struct usrsock_reqstate_s state = {}; + int ret; + + DEBUGASSERT(conn); + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Invalid state or closed by daemon. */ + + ninfo("usockid=%d; connect() with uninitialized usrsock.\n", + conn->usockid); + + ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE : -ECONNREFUSED; + goto errout_unlock; + } + + if (conn->connected && + (conn->type == SOCK_STREAM || conn->type == SOCK_SEQPACKET)) + { + /* Already connected. */ + + ret = -EISCONN; + goto errout_unlock; + } + + if (conn->state == USRSOCK_CONN_STATE_CONNECTING) + { + /* Already connecting. */ + + ninfo("usockid=%d; socket already connecting.\n", + conn->usockid); + + ret = -EALREADY; + goto errout_unlock; + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_request_callback(conn, &state, connect_event, + USRSOCK_EVENT_ABORT | + USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + + goto errout_unlock; + } + + /* Mark conn as connecting one. */ + + conn->state = USRSOCK_CONN_STATE_CONNECTING; + + /* Send request. */ + + ret = do_connect_request(conn, addr, addrlen); + if (ret < 0) + { + goto errout_teardown; + } + + /* Do not block on waiting for request completion if nonblocking socket. */ + + if (!conn->resp.inprogress || !_SS_ISNONBLOCK(psock->s_flags)) + { + /* Wait for completion of request (or signal). */ + + if (net_lockedwait(&state.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + + /* Wait interrupted, exit early. */ + + ret = -EINTR; + goto errout_teardown; + } + + ret = state.result; + } + else + { + /* Request not completed and socket is non-blocking. */ + + ret = -EINPROGRESS; + } + +errout_teardown: + usrsock_teardown_request_callback(&state); +errout_unlock: + net_unlock(); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_dev.c b/net/usrsock/usrsock_dev.c new file mode 100644 index 00000000000..11b915e0d84 --- /dev/null +++ b/net/usrsock/usrsock_dev.c @@ -0,0 +1,1271 @@ +/**************************************************************************** + * net/usrsock/usrsock_dev.c + * + * Copyright (C) 2015-2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "devif/devif.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#ifndef CONFIG_NET_USRSOCKDEV_NPOLLWAITERS +# define CONFIG_NET_USRSOCKDEV_NPOLLWAITERS 1 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct usrsockdev_s +{ + sem_t devsem; /* Lock for device node */ + uint8_t ocount; /* The number of times the device has been opened */ + + struct + { + FAR const struct iovec *iov; /* Pending request buffers */ + int iovcnt; /* Number of request buffers */ + size_t pos; /* Reader position on request buffer */ + sem_t sem; /* Request semaphore (only one outstanding + * request) */ + sem_t acksem; /* Request acknowledgment notification */ + uint8_t ack_xid; /* Exchange id for which waiting ack */ + uint16_t nbusy; /* Number of requests blocked from different + threads */ + } req; + + FAR struct usrsock_conn_s *datain_conn; /* Connection instance to receive + * data buffers. */ + +#ifndef CONFIG_DISABLE_POLL + struct pollfd *pollfds[CONFIG_NET_USRSOCKDEV_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Character driver methods */ + +static ssize_t usrsockdev_read(FAR struct file *filep, FAR char *buffer, + size_t len); + +static ssize_t usrsockdev_write(FAR struct file *filep, + FAR const char *buffer, size_t len); + +static off_t usrsockdev_seek(FAR struct file *filep, off_t offset, + int whence); + +static int usrsockdev_open(FAR struct file *filep); + +static int usrsockdev_close(FAR struct file *filep); + +#ifndef CONFIG_DISABLE_POLL +static int usrsockdev_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_usrsockdevops = +{ + usrsockdev_open, /* open */ + usrsockdev_close, /* close */ + usrsockdev_read, /* read */ + usrsockdev_write, /* write */ + usrsockdev_seek, /* seek */ + NULL /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , usrsockdev_poll /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +static struct usrsockdev_s g_usrsockdev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: iovec_do() - copy to/from iovec from/to buffer. + ****************************************************************************/ + +static ssize_t iovec_do(FAR void *srcdst, size_t srcdstlen, + FAR struct iovec *iov, int iovcnt, size_t pos, + bool from_iov) +{ + ssize_t total; + size_t srclen; + FAR uint8_t *ioout = srcdst; + FAR uint8_t *iovbuf; + + /* Rewind to correct position. */ + + while (pos > 0 && iovcnt > 0) + { + if (iov->iov_len <= pos) + { + pos -= iov->iov_len; + iov++; + iovcnt--; + } + else + { + break; + } + } + + if (iovcnt == 0) + { + /* Position beyond iovec. */ + + return -1; + } + + iovbuf = iov->iov_base; + srclen = iov->iov_len; + iovbuf += pos; + srclen -= pos; + iov++; + iovcnt--; + total = 0; + + while ((srclen > 0 || iovcnt > 0) && srcdstlen > 0) + { + size_t clen = srclen; + + if (srclen == 0) + { + /* Skip empty iovec. */ + + iovbuf = iov->iov_base; + srclen = iov->iov_len; + iov++; + iovcnt--; + + continue; + } + + if (clen > srcdstlen) + { + clen = srcdstlen; + } + + if (from_iov) + { + memmove(ioout, iovbuf, clen); + } + else + { + memmove(iovbuf, ioout, clen); + } + + ioout += clen; + srcdstlen -= clen; + iovbuf += clen; + srclen -= clen; + total += clen; + + if (srclen == 0) + { + if (iovcnt == 0) + { + break; + } + + iovbuf = iov->iov_base; + srclen = iov->iov_len; + iov++; + iovcnt--; + } + } + + return total; +} + +/**************************************************************************** + * Name: iovec_get() - copy from iovec to buffer. + ****************************************************************************/ + +static ssize_t iovec_get(FAR void *dst, size_t dstlen, + FAR const struct iovec *iov, int iovcnt, size_t pos) +{ + return iovec_do(dst, dstlen, (FAR struct iovec *)iov, iovcnt, pos, true); +} + +/**************************************************************************** + * Name: iovec_put() - copy to iovec from buffer. + ****************************************************************************/ + +static ssize_t iovec_put(FAR struct iovec *iov, int iovcnt, size_t pos, + FAR const void *src, size_t srclen) +{ + return iovec_do((FAR void *)src, srclen, iov, iovcnt, pos, false); +} + +/**************************************************************************** + * Name: usrsockdev_get_xid() + ****************************************************************************/ + +static uint8_t usrsockdev_get_xid(FAR struct usrsock_conn_s *conn) +{ + int conn_idx; + +#if CONFIG_NET_USRSOCK_CONNS > 255 +# error "CONFIG_NET_USRSOCK_CONNS too large (over 255)" +#endif + + /* Each connection can one only one request/response pending. So map + * connection structure index to xid value. */ + + conn_idx = usrsock_connidx(conn); + + DEBUGASSERT(1 <= conn_idx + 1); + DEBUGASSERT(conn_idx + 1 <= UINT8_MAX); + + return conn_idx + 1; +} + +/**************************************************************************** + * Name: usrsockdev_semtake() and usrsockdev_semgive() + * + * Description: + * Take/give semaphore + * + ****************************************************************************/ + +static void usrsockdev_semtake(FAR sem_t *sem) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + DEBUGASSERT(*get_errno_ptr() == EINTR); + } +} + +static void usrsockdev_semgive(FAR sem_t *sem) +{ + (void)sem_post(sem); +} + +/**************************************************************************** + * Name: usrsockdev_is_opened + ****************************************************************************/ + +static bool usrsockdev_is_opened(FAR struct usrsockdev_s *dev) +{ + bool ret = true; + + if (dev->ocount == 0) + { + ret = false; /* No usrsock daemon running. */ + } + + return ret; +} + +/**************************************************************************** + * Name: usrsockdev_pollnotify + ****************************************************************************/ + +static void usrsockdev_pollnotify(FAR struct usrsockdev_s *dev, pollevent_t eventset) +{ +#ifndef CONFIG_DISABLE_POLL + int i; + for (i = 0; i < ARRAY_SIZE(dev->pollfds); i++) + { + struct pollfd *fds = dev->pollfds[i]; + if (fds) + { + fds->revents |= (fds->events & eventset); + if (fds->revents != 0) + { + ninfo("Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } + } +#endif +} + +/**************************************************************************** + * Name: usrsockdev_read + ****************************************************************************/ + +static ssize_t usrsockdev_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct usrsockdev_s *dev; + + if (len == 0) + { + return 0; + } + + if (buffer == NULL) + { + return -EINVAL; + } + + DEBUGASSERT(inode); + + dev = inode->i_private; + + DEBUGASSERT(dev); + + usrsockdev_semtake(&dev->devsem); + net_lock(); + + /* Is request available? */ + + if (dev->req.iov) + { + ssize_t rlen; + + /* Copy request to user-space. */ + + rlen = iovec_get(buffer, len, dev->req.iov, dev->req.iovcnt, + dev->req.pos); + if (rlen < 0) + { + /* Tried reading beyond buffer. */ + + len = 0; + } + else + { + dev->req.pos += rlen; + len = rlen; + } + } + else + { + len = 0; + } + + net_unlock(); + usrsockdev_semgive(&dev->devsem); + + return len; +} + +/**************************************************************************** + * Name: usrsockdev_seek + ****************************************************************************/ + +static off_t usrsockdev_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct usrsockdev_s *dev; + off_t pos; + + if (whence != SEEK_CUR && whence != SEEK_SET) + { + return -EINVAL; + } + + DEBUGASSERT(inode); + + dev = inode->i_private; + + DEBUGASSERT(dev); + + usrsockdev_semtake(&dev->devsem); + net_lock(); + + /* Is request available? */ + + if (dev->req.iov) + { + ssize_t rlen; + + if (whence == SEEK_CUR) + { + pos = dev->req.pos + offset; + } + else if (whence == SEEK_SET) + { + pos = offset; + } + + /* Copy request to user-space. */ + + rlen = iovec_get(NULL, 0, dev->req.iov, dev->req.iovcnt, pos); + if (rlen < 0) + { + /* Tried seek beyond buffer. */ + + pos = -EINVAL; + } + else + { + dev->req.pos = pos; + } + } + else + { + pos = 0; + } + + net_unlock(); + usrsockdev_semgive(&dev->devsem); + + return pos; +} + +/**************************************************************************** + * Name: usrsockdev_handle_event + ****************************************************************************/ + +static ssize_t usrsockdev_handle_event(FAR struct usrsockdev_s *dev, + FAR const void *buffer, + size_t len) +{ + FAR const struct usrsock_message_common_s *common = buffer; + + switch (common->msgid) + { + case USRSOCK_MESSAGE_SOCKET_EVENT: + { + FAR const struct usrsock_message_socket_event_s *hdr = buffer; + FAR struct usrsock_conn_s *conn; + int ret; + + if (len < sizeof(*hdr)) + { + nwarn("message too short, %d < %d.\n", len, sizeof(*hdr)); + + return -EINVAL; + } + + /* Get corresponding usrsock connection. */ + + conn = usrsock_active(hdr->usockid); + if (!conn) + { + nwarn("no active connection for usockid=%d.\n", hdr->usockid); + + return -ENOENT; + } + +#ifdef CONFIG_DEV_RANDOM + /* Add randomness. */ + + add_sw_randomness((hdr->events << 16) - hdr->usockid); +#endif + + /* Handle event. */ + + ret = usrsock_event(conn, hdr->events & ~USRSOCK_EVENT_INTERNAL_MASK); + if (ret < 0) + { + return ret; + } + + len = sizeof(*hdr); + } + break; + + default: + nwarn("Unknown event type: %d\n", common->msgid); + return -EINVAL; + } + + return len; +} + +/**************************************************************************** + * Name: usrsockdev_handle_response + ****************************************************************************/ + +static ssize_t usrsockdev_handle_response(FAR struct usrsockdev_s *dev, + FAR struct usrsock_conn_s *conn, + FAR const void *buffer) +{ + FAR const struct usrsock_message_req_ack_s *hdr = buffer; + + if (USRSOCK_MESSAGE_REQ_IN_PROGRESS(hdr->head.flags)) + { + /* In-progress response is acknowledgment that response was + * received. */ + + conn->resp.inprogress = true; + } + else + { + conn->resp.inprogress = false; + conn->resp.xid = 0; + + /* Get result for common request. */ + + conn->resp.result = hdr->result; + + /* Done with request/response. */ + + (void)usrsock_event(conn, USRSOCK_EVENT_REQ_COMPLETE); + } + + return sizeof(*hdr); +} + +/**************************************************************************** + * Name: usrsockdev_handle_datareq_response + ****************************************************************************/ + +static ssize_t +usrsockdev_handle_datareq_response(FAR struct usrsockdev_s *dev, + FAR struct usrsock_conn_s *conn, + FAR const void *buffer) +{ + FAR const struct usrsock_message_datareq_ack_s *datahdr = buffer; + FAR const struct usrsock_message_req_ack_s *hdr = &datahdr->reqack; + int num_inbufs, iovpos; + ssize_t ret; + + if (USRSOCK_MESSAGE_REQ_IN_PROGRESS(hdr->head.flags)) + { + if (datahdr->reqack.result > 0) + { + ninfo("error: request in progress, and result > 0.\n"); + ret = -EINVAL; + goto unlock_out; + } + else if (datahdr->valuelen > 0) + { + ninfo("error: request in progress, and valuelen > 0.\n"); + ret = -EINVAL; + goto unlock_out; + } + + /* In-progress response is acknowledgment that response was + * received. */ + + conn->resp.inprogress = true; + + ret = sizeof(*datahdr); + goto unlock_out; + } + + conn->resp.inprogress = false; + conn->resp.xid = 0; + + /* Prepare to read buffers. */ + + conn->resp.result = hdr->result; + conn->resp.valuelen = datahdr->valuelen; + conn->resp.valuelen_nontrunc = datahdr->valuelen_nontrunc; + + if (conn->resp.result < 0) + { + /* Error, valuelen must be zero. */ + + if (datahdr->valuelen > 0 || datahdr->valuelen_nontrunc > 0) + { + nerr("error: response result negative, and valuelen or valuelen_nontrunc non-zero.\n"); + + ret = -EINVAL; + goto unlock_out; + } + + /* Done with request/response. */ + + (void)usrsock_event(conn, USRSOCK_EVENT_REQ_COMPLETE); + + ret = sizeof(*datahdr); + goto unlock_out; + } + + /* Check that number of buffers match available. */ + + num_inbufs = (hdr->result > 0) + 1; + + if (conn->resp.datain.iovcnt < num_inbufs) + { + nwarn("not enough recv buffers (need: %d, have: %d).\n", num_inbufs, + conn->resp.datain.iovcnt); + + ret = -EINVAL; + goto unlock_out; + } + + /* Adjust length of receiving buffers. */ + + conn->resp.datain.total = 0; + iovpos = 0; + + /* Value buffer is always the first */ + + if (conn->resp.datain.iov[iovpos].iov_len < datahdr->valuelen) + { + nwarn("%dth buffer not large enough (need: %d, have: %d).\n", + iovpos, + datahdr->valuelen, + conn->resp.datain.iov[iovpos].iov_len); + + ret = -EINVAL; + goto unlock_out; + } + + /* Adjust read size. */ + + conn->resp.datain.iov[iovpos].iov_len = datahdr->valuelen; + conn->resp.datain.total += conn->resp.datain.iov[iovpos].iov_len; + iovpos++; + + if (hdr->result > 0) + { + /* Value buffer is always the first */ + + if (conn->resp.datain.iov[iovpos].iov_len < hdr->result) + { + nwarn("%dth buffer not large enough (need: %d, have: %d).\n", + iovpos, + hdr->result, + conn->resp.datain.iov[iovpos].iov_len); + + ret = -EINVAL; + goto unlock_out; + } + + /* Adjust read size. */ + + conn->resp.datain.iov[iovpos].iov_len = hdr->result; + conn->resp.datain.total += conn->resp.datain.iov[iovpos].iov_len; + iovpos++; + } + + DEBUGASSERT(num_inbufs == iovpos); + + conn->resp.datain.iovcnt = num_inbufs; + + /* Next written buffers are redirected to data buffers. */ + + dev->datain_conn = conn; + ret = sizeof(*datahdr); + +unlock_out: + return ret; +} + +/**************************************************************************** + * Name: usrsockdev_handle_req_response + ****************************************************************************/ + +static ssize_t usrsockdev_handle_req_response(FAR struct usrsockdev_s *dev, + FAR const void *buffer, + size_t len) +{ + FAR const struct usrsock_message_req_ack_s *hdr = buffer; + FAR struct usrsock_conn_s *conn; + unsigned int hdrlen; + ssize_t ret; + ssize_t (* handle_response)(FAR struct usrsockdev_s *dev, + FAR struct usrsock_conn_s *conn, + FAR const void *buffer); + + switch (hdr->head.msgid) + { + case USRSOCK_MESSAGE_RESPONSE_ACK: + hdrlen = sizeof(struct usrsock_message_req_ack_s); + handle_response = &usrsockdev_handle_response; + break; + + case USRSOCK_MESSAGE_RESPONSE_DATA_ACK: + hdrlen = sizeof(struct usrsock_message_datareq_ack_s); + handle_response = &usrsockdev_handle_datareq_response; + break; + + default: + nwarn("unknown message type: %d, flags: %d, xid: %02x, result: %d\n", + hdr->head.msgid, hdr->head.flags, hdr->xid, hdr->result); + return -EINVAL; + } + + if (len < hdrlen) + { + nwarn("message too short, %d < %d.\n", len, hdrlen); + + return -EINVAL; + } + + net_lock(); + + /* Get corresponding usrsock connection for this transfer */ + + conn = usrsock_nextconn(NULL); + while (conn) + { + if (conn->resp.xid == hdr->xid) + break; + + conn = usrsock_nextconn(conn); + } + + if (!conn) + { + /* No connection waiting for this message. */ + + nwarn("Could find connection waiting for response with xid=%d\n", + hdr->xid); + + ret = -EINVAL; + goto unlock_out; + } + + if (dev->req.ack_xid == hdr->xid && dev->req.iov) + { + /* Signal that request was received and read by daemon and acknowledgment + * response was received. */ + + dev->req.iov = NULL; + + sem_post(&dev->req.acksem); + } + + ret = handle_response(dev, conn, buffer); + +unlock_out: + net_unlock(); + return ret; +} + +/**************************************************************************** + * Name: usrsockdev_handle_message + ****************************************************************************/ + +static ssize_t usrsockdev_handle_message(FAR struct usrsockdev_s *dev, + FAR const void *buffer, + size_t len) +{ + FAR const struct usrsock_message_common_s *common = buffer; + + if (USRSOCK_MESSAGE_IS_EVENT(common->flags)) + { + return usrsockdev_handle_event(dev, buffer, len); + } + + if (USRSOCK_MESSAGE_IS_REQ_RESPONSE(common->flags)) + { + return usrsockdev_handle_req_response(dev, buffer, len); + } + + return -EINVAL; +} + +/**************************************************************************** + * Name: usrsockdev_write + ****************************************************************************/ + +static ssize_t usrsockdev_write(FAR struct file *filep, FAR const char *buffer, + size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct usrsock_conn_s *conn; + FAR struct usrsockdev_s *dev; + size_t origlen = len; + ssize_t ret = 0; + + if (len == 0) + { + return 0; + } + + if (buffer == NULL) + { + return -EINVAL; + } + + DEBUGASSERT(inode); + + dev = inode->i_private; + + DEBUGASSERT(dev); + + usrsockdev_semtake(&dev->devsem); + + if (!dev->datain_conn) + { + /* Start of message, buffer length should be at least size of common + * message header. */ + + if (len < sizeof(struct usrsock_message_common_s)) + { + nwarn("message too short, %d < %d.\n", len, + sizeof(struct usrsock_message_common_s)); + + ret = -EINVAL; + goto errout; + } + + /* Handle message. */ + + ret = usrsockdev_handle_message(dev, buffer, len); + if (ret >= 0) + { + buffer += ret; + len -= ret; + ret = origlen - len; + } + } + + /* Data input handling. */ + + if (dev->datain_conn) + { + conn = dev->datain_conn; + + /* Copy data from user-space. */ + + ret = iovec_put(conn->resp.datain.iov, conn->resp.datain.iovcnt, + conn->resp.datain.pos, buffer, len); + if (ret < 0) + { + /* Tried writing beyond buffer. */ + + ret = -EINVAL; + conn->resp.result = -EINVAL; + conn->resp.datain.pos = + conn->resp.datain.total; + } + else + { + conn->resp.datain.pos += ret; + buffer += ret; + len -= ret; + ret = origlen - len; + } + + if (conn->resp.datain.pos == conn->resp.datain.total) + { + dev->datain_conn = NULL; + + /* Done with data response. */ + + (void)usrsock_event(conn, USRSOCK_EVENT_REQ_COMPLETE); + } + } + +errout: + usrsockdev_semgive(&dev->devsem); + return ret; +} + +/**************************************************************************** + * Name: usrsockdev_open + ****************************************************************************/ + +static int usrsockdev_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct usrsockdev_s *dev; + int ret, tmp; + + DEBUGASSERT(inode); + + dev = inode->i_private; + + DEBUGASSERT(dev); + + usrsockdev_semtake(&dev->devsem); + + ninfo("opening /usr/usrsock\n"); + + /* Increment the count of references to the device. */ + + tmp = dev->ocount + 1; + if (tmp > 1) + { + /* Only one reference is allowed. */ + + nwarn("failed to open\n"); + + ret = -EPERM; + } + else + { + dev->ocount = tmp; + ret = OK; + } + + usrsockdev_semgive(&dev->devsem); + + return ret; +} + +/**************************************************************************** + * Name: usrsockdev_close + ****************************************************************************/ + +static int usrsockdev_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct usrsockdev_s *dev; + FAR struct usrsock_conn_s *conn; + struct timespec abstime; + int ret; + + DEBUGASSERT(inode); + + dev = inode->i_private; + + DEBUGASSERT(dev); + + usrsockdev_semtake(&dev->devsem); + + ninfo("closing /dev/usrsock\n"); + + /* Set active usrsock sockets to aborted state. */ + + conn = usrsock_nextconn(NULL); + while (conn) + { + net_lock(); + + conn->resp.inprogress = false; + conn->resp.xid = 0; + usrsock_event(conn, USRSOCK_EVENT_ABORT); + + net_unlock(); + + conn = usrsock_nextconn(conn); + } + + net_lock(); + + /* Decrement the references to the driver. */ + + dev->ocount--; + DEBUGASSERT(dev->ocount == 0); + ret = OK; + + do + { + /* Give other threads short time window to complete recently completed + * requests. + */ + + DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime)); + + abstime.tv_sec += 0; + abstime.tv_nsec += 10 * NSEC_PER_MSEC; + if (abstime.tv_nsec >= NSEC_PER_SEC) + { + abstime.tv_sec++; + abstime.tv_nsec -= NSEC_PER_SEC; + } + + ret = net_timedwait(&dev->req.sem, &abstime); + if (ret < 0) + { + ret = *get_errno_ptr(); + + if (ret != ETIMEDOUT && ret != EINTR) + { + ninfo("net_timedwait errno: %d\n", ret); + DEBUGASSERT(false); + } + } + else + { + usrsockdev_semgive(&dev->req.sem); + } + + /* Wake-up pending requests. */ + + if (dev->req.nbusy == 0) + { + break; + } + + dev->req.iov = NULL; + sem_post(&dev->req.acksem); + } + while (true); + + net_unlock(); + + /* Check if request line is active */ + + if (dev->req.iov != NULL) + { + dev->req.iov = NULL; + } + + usrsockdev_semgive(&dev->devsem); + + return ret; +} + +/**************************************************************************** + * Name: pipecommon_poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int usrsockdev_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct usrsockdev_s *dev; + pollevent_t eventset; + int ret = OK; + int i; + + DEBUGASSERT(inode); + + dev = inode->i_private; + + DEBUGASSERT(dev); + + /* Some sanity checking */ + + if (!dev || !fds) + { + return -ENODEV; + } + + /* Are we setting up the poll? Or tearing it down? */ + + usrsockdev_semtake(&dev->devsem); + net_lock(); + if (setup) + { + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < ARRAY_SIZE(dev->pollfds); i++) + { + /* Find an available slot */ + + if (!dev->pollfds[i]) + { + /* Bind the poll structure and this slot */ + + dev->pollfds[i] = fds; + fds->priv = &dev->pollfds[i]; + break; + } + } + + if (i >= ARRAY_SIZE(dev->pollfds)) + { + fds->priv = NULL; + ret = -EBUSY; + goto errout; + } + + /* Should immediately notify on any of the requested events? */ + + eventset = 0; + + /* Notify the POLLIN event if pending request. */ + + if (dev->req.iov != NULL && + !(iovec_get(NULL, 0, dev->req.iov, + dev->req.iovcnt, dev->req.pos) < 0)) + { + eventset |= POLLIN; + } + + if (eventset) + { + usrsockdev_pollnotify(dev, eventset); + } + } + else + { + /* This is a request to tear down the poll. */ + + FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; + + if (!slot) + { + ret = -EIO; + goto errout; + } + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +errout: + net_unlock(); + usrsockdev_semgive(&dev->devsem); + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usrsockdev_do_request + ****************************************************************************/ + +int usrsockdev_do_request(FAR struct usrsock_conn_s *conn, + FAR struct iovec *iov, unsigned int iovcnt) +{ + FAR struct usrsockdev_s *dev = conn->dev; + FAR struct usrsock_request_common_s *req_head = iov[0].iov_base; + + if (!dev) + { + /* Setup conn for new usrsock device. */ + + DEBUGASSERT(req_head->reqid == USRSOCK_REQUEST_SOCKET); + dev = &g_usrsockdev; + conn->dev = dev; + } + + if (!usrsockdev_is_opened(dev)) + { + ninfo("usockid=%d; daemon has closed /usr/usrsock.\n", conn->usockid); + + return -ENETDOWN; + } + + /* Get exchange id. */ + + req_head->xid = usrsockdev_get_xid(conn); + + /* Prepare connection for response. */ + + conn->resp.xid = req_head->xid; + conn->resp.result = -EACCES; + + ++dev->req.nbusy; /* net_lock held. */ + + /* Set outstanding request for daemon to handle. */ + + while (net_lockedwait(&dev->req.sem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + if (usrsockdev_is_opened(dev)) + { + DEBUGASSERT(dev->req.iov == NULL); + dev->req.ack_xid = req_head->xid; + dev->req.iov = iov; + dev->req.pos = 0; + dev->req.iovcnt = iovcnt; + + /* Notify daemon of new request. */ + + usrsockdev_pollnotify(dev, POLLIN); + + /* Wait ack for request. */ + + while (net_lockedwait(&dev->req.acksem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + } + else + { + ninfo("usockid=%d; daemon abruptly closed /usr/usrsock.\n", conn->usockid); + } + + /* Free request line for next command. */ + + usrsockdev_semgive(&dev->req.sem); + + --dev->req.nbusy; /* net_lock held. */ + + return OK; +} + +/**************************************************************************** + * Name: usrsockdev_register + * + * Description: + * Register /dev/usrsock + * + ****************************************************************************/ + +void usrsockdev_register(void) +{ + /* Initialize device private structure. */ + + g_usrsockdev.ocount = 0; + g_usrsockdev.req.nbusy = 0; + sem_init(&g_usrsockdev.devsem, 0, 1); + sem_init(&g_usrsockdev.req.sem, 0, 1); + sem_init(&g_usrsockdev.req.acksem, 0, 0); + + (void)register_driver("/dev/usrsock", &g_usrsockdevops, 0666, &g_usrsockdev); +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_event.c b/net/usrsock/usrsock_event.c new file mode 100644 index 00000000000..d16fb04fc9c --- /dev/null +++ b/net/usrsock/usrsock_event.c @@ -0,0 +1,136 @@ +/**************************************************************************** + * net/usrsock/usrsock_event.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "devif/devif.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_event + * + * Description: + * Handler for received connection events + * + ****************************************************************************/ + +int usrsock_event(FAR struct usrsock_conn_s *conn, uint16_t events) +{ + ninfo("events: %04X\n", events); + + if (!events) + { + return OK; + } + + net_lock(); + + /* Generic state updates. */ + + if (events & USRSOCK_EVENT_REQ_COMPLETE) + { + if (conn->state == USRSOCK_CONN_STATE_CONNECTING) + { + conn->state = USRSOCK_CONN_STATE_READY; + events |= USRSOCK_EVENT_CONNECT_RESP; + + if (conn->resp.result == 0) + { + conn->connected = true; + } + } + } + + if (events & USRSOCK_EVENT_ABORT) + { + conn->state = USRSOCK_CONN_STATE_ABORTED; + } + + if (events & USRSOCK_EVENT_REMOTE_CLOSED) + { + /* After reception of remote close event, clear input/output flags. */ + + conn->flags &= ~(USRSOCK_EVENT_SENDTO_READY | + USRSOCK_EVENT_RECVFROM_AVAIL); + + conn->flags |= USRSOCK_EVENT_REMOTE_CLOSED; + } + + if ((conn->state == USRSOCK_CONN_STATE_READY || + conn->state == USRSOCK_CONN_STATE_CONNECTING) && + !(conn->flags & USRSOCK_EVENT_REMOTE_CLOSED)) + { + if (events & USRSOCK_EVENT_SENDTO_READY) + { + conn->flags |= USRSOCK_EVENT_SENDTO_READY; + } + + if (events & USRSOCK_EVENT_RECVFROM_AVAIL) + { + conn->flags |= USRSOCK_EVENT_RECVFROM_AVAIL; + } + } + + /* Send events to callbacks */ + + (void)devif_conn_event(NULL, conn, events, conn->list); + net_unlock(); + + return OK; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_getsockname.c b/net/usrsock/usrsock_getsockname.c new file mode 100644 index 00000000000..5049ab8d213 --- /dev/null +++ b/net/usrsock/usrsock_getsockname.c @@ -0,0 +1,265 @@ +/**************************************************************************** + * net/usrsock/usrsock_getsockname.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t getsockname_event(FAR struct net_driver_s *dev, + FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_data_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->reqstate.result = -ECONNABORTED; + pstate->valuelen = 0; + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->reqstate.result = conn->resp.result; + if (pstate->reqstate.result < 0) + { + pstate->valuelen = 0; + pstate->valuelen_nontrunc = 0; + } + else + { + pstate->valuelen = conn->resp.valuelen; + pstate->valuelen_nontrunc = conn->resp.valuelen_nontrunc; + } + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_getsockopt_request + ****************************************************************************/ + +static int do_getsockname_request(FAR struct usrsock_conn_s *conn, + socklen_t addrlen) +{ + struct usrsock_request_getsockname_s req = {}; + struct iovec bufs[1]; + + if (addrlen > UINT16_MAX) + { + addrlen = UINT16_MAX; + } + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_GETSOCKNAME; + req.usockid = conn->usockid; + req.max_addrlen = addrlen; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Name: setup_conn_getsockopt + ****************************************************************************/ + +static void setup_conn_getsockname(FAR struct usrsock_conn_s *conn, + FAR struct iovec *iov, + unsigned int iovcnt) +{ + unsigned int i; + + conn->resp.datain.iov = iov; + conn->resp.datain.pos = 0; + conn->resp.datain.total = 0; + conn->resp.datain.iovcnt = iovcnt; + + for (i = 0; i < iovcnt; i++) + { + conn->resp.datain.total += iov[i].iov_len; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_getsockname + * + * Description: + * The getsockname() function retrieves the locally-bound name of the + * specified socket, stores this address in the sockaddr structure pointed + * to by the 'addr' argument, and stores the length of this address in the + * object pointed to by the 'addrlen' argument. + * + * If the actual length of the address is greater than the length of the + * supplied sockaddr structure, the stored address will be truncated. + * + * If the socket has not been bound to a local name, the value stored in + * the object pointed to by address is unspecified. + * + * Parameters: + * conn usrsock socket connection structure + * addr sockaddr structure to receive data [out] + * addrlen Length of sockaddr structure [in/out] + * + ****************************************************************************/ + +int usrsock_getsockname(FAR struct usrsock_conn_s *conn, + FAR struct sockaddr *addr, FAR socklen_t *addrlen) +{ + struct usrsock_data_reqstate_s state = {}; + struct iovec inbufs[1]; + ssize_t ret; + socklen_t outaddrlen = 0; + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Invalid state or closed by daemon. */ + + ninfo("usockid=%d; connect() with uninitialized usrsock.\n", + conn->usockid); + + ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE : -ECONNRESET; + goto errout_unlock; + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_data_request_callback( + conn, &state, getsockname_event, + USRSOCK_EVENT_ABORT | USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + inbufs[0].iov_base = (FAR void *)addr; + inbufs[0].iov_len = *addrlen; + + setup_conn_getsockname(conn, inbufs, ARRAY_SIZE(inbufs)); + + /* Request user-space daemon to close socket. */ + + ret = do_getsockname_request(conn, *addrlen); + if (ret >= 0) + { + /* Wait for completion of request. */ + + while (net_lockedwait(&state.reqstate.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + ret = state.reqstate.result; + + DEBUGASSERT(state.valuelen <= *addrlen); + DEBUGASSERT(state.valuelen <= state.valuelen_nontrunc); + + if (ret >= 0) + { + /* Store length of data that was written to 'value' buffer. */ + + outaddrlen = state.valuelen_nontrunc; + } + } + + setup_conn_getsockname(conn, NULL, 0); + usrsock_teardown_data_request_callback(&state); + +errout_unlock: + net_unlock(); + + *addrlen = outaddrlen; + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_getsockopt.c b/net/usrsock/usrsock_getsockopt.c new file mode 100644 index 00000000000..f4a8ccb0d46 --- /dev/null +++ b/net/usrsock/usrsock_getsockopt.c @@ -0,0 +1,275 @@ +/**************************************************************************** + * net/usrsock/usrsock_getsockopt.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) && \ + defined(CONFIG_NET_SOCKOPTS) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t getsockopt_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_data_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->reqstate.result = -ECONNABORTED; + pstate->valuelen = 0; + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->reqstate.result = conn->resp.result; + if (pstate->reqstate.result < 0) + { + pstate->valuelen = 0; + } + else + { + pstate->valuelen = conn->resp.valuelen; + } + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_getsockopt_request + ****************************************************************************/ + +static int do_getsockopt_request(FAR struct usrsock_conn_s *conn, int level, + int option, socklen_t value_len) +{ + struct usrsock_request_getsockopt_s req = {}; + struct iovec bufs[1]; + + if (level < INT16_MIN || level > INT16_MAX) + { + return -EINVAL; + } + + if (option < INT16_MIN || option > INT16_MAX) + { + return -EINVAL; + } + + if (value_len > UINT16_MAX) + { + value_len = UINT16_MAX; + } + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_GETSOCKOPT; + req.usockid = conn->usockid; + req.level = level; + req.option = option; + req.max_valuelen = value_len; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Name: setup_conn_getsockopt + ****************************************************************************/ + +static void setup_conn_getsockopt(FAR struct usrsock_conn_s *conn, + FAR struct iovec *iov, unsigned int iovcnt) +{ + unsigned int i; + + conn->resp.datain.iov = iov; + conn->resp.datain.pos = 0; + conn->resp.datain.total = 0; + conn->resp.datain.iovcnt = iovcnt; + + for (i = 0; i < iovcnt; i++) + { + conn->resp.datain.total += iov[i].iov_len; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_getsockopt + * + * Description: + * getsockopt() retrieve thse value for the option specified by the + * 'option' argument for the socket specified by the 'psock' argument. If + * the size of the option value is greater than 'value_len', the value + * stored in the object pointed to by the 'value' argument will be silently + * truncated. Otherwise, the length pointed to by the 'value_len' argument + * will be modified to indicate the actual length of the'value'. + * + * The 'level' argument specifies the protocol level of the option. To + * retrieve options at the socket level, specify the level argument as + * SOL_SOCKET. + * + * See a complete list of values for the 'option' argument. + * + * Parameters: + * conn usrsock socket connection structure + * level Protocol level to set the option + * option identifies the option to get + * value Points to the argument value + * value_len The length of the argument value + * + ****************************************************************************/ + +int usrsock_getsockopt(FAR struct usrsock_conn_s *conn, int level, int option, + FAR void *value, FAR socklen_t *value_len) +{ + struct usrsock_data_reqstate_s state = {}; + struct iovec inbufs[1]; + ssize_t ret; + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Invalid state or closed by daemon. */ + + ninfo("usockid=%d; connect() with uninitialized usrsock.\n", + conn->usockid); + + ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE : -ECONNRESET; + goto errout_unlock; + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_data_request_callback( + conn, &state, getsockopt_event, + USRSOCK_EVENT_ABORT | USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + inbufs[0].iov_base = (FAR void *)value; + inbufs[0].iov_len = *value_len; + + setup_conn_getsockopt(conn, inbufs, ARRAY_SIZE(inbufs)); + + /* Request user-space daemon to close socket. */ + + ret = do_getsockopt_request(conn, level, option, *value_len); + if (ret >= 0) + { + /* Wait for completion of request. */ + + while (net_lockedwait(&state.reqstate.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + ret = state.reqstate.result; + + DEBUGASSERT(state.valuelen <= *value_len); + + if (ret >= 0) + { + /* Store length of data that was written to 'value' buffer. */ + + *value_len = state.valuelen; + } + } + + setup_conn_getsockopt(conn, NULL, 0); + usrsock_teardown_data_request_callback(&state); + +errout_unlock: + net_unlock(); + + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK && CONFIG_NET_SOCKOPTS */ diff --git a/net/usrsock/usrsock_poll.c b/net/usrsock/usrsock_poll.c new file mode 100644 index 00000000000..41860f4d7f8 --- /dev/null +++ b/net/usrsock/usrsock_poll.c @@ -0,0 +1,388 @@ +/**************************************************************************** + * net/usrsock/usrsock_poll.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) && \ + !defined(CONFIG_DISABLE_POLL) + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct usrsock_poll_s +{ + FAR struct socket *psock; /* Needed to handle loss of connection */ + struct pollfd *fds; /* Needed to handle poll events */ + FAR struct devif_callback_s *cb; /* Needed to teardown the poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t poll_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_poll_s *info = (FAR struct usrsock_poll_s *)pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + pollevent_t eventset = 0; + + DEBUGASSERT(!info || (info->psock && info->fds)); + + if (!info) + return flags; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + /* Socket forcefully terminated. */ + + eventset |= (POLLERR | POLLHUP); + } + else if ((flags & USRSOCK_EVENT_CONNECT_RESP) && !conn->connected) + { + ninfo("socket connect failed.\n"); + + /* Non-blocking connect failed. */ + + eventset |= (POLLERR | POLLHUP); + } + else if (flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ninfo("remote closed.\n"); + + /* Remote closed. */ + + eventset |= (POLLHUP | POLLIN); + } + else + { + /* Check data events. */ + + if (flags & USRSOCK_EVENT_RECVFROM_AVAIL) + { + ninfo("socket recv avail.\n"); + + eventset |= POLLIN; + } + + if (flags & USRSOCK_EVENT_SENDTO_READY) + { + ninfo("socket send ready.\n"); + + eventset |= POLLOUT; + } + } + + /* Filter I/O events depending on requested events. */ + + eventset &= (~(POLLOUT | POLLIN) | info->fds->events); + + /* POLLOUT and PULLHUP are mutually exclusive. */ + + if ((eventset & POLLOUT) && (eventset & POLLHUP)) + { + eventset &= ~POLLOUT; + } + + /* Awaken the caller of poll() is requested event occurred. */ + + if (eventset) + { + info->fds->revents |= eventset; + sem_post(info->fds->sem); + } + + return flags; +} + +/**************************************************************************** + * Function: usrsock_poll + * + * Description: + * Setup to monitor events on an usrsock socket + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * fds - The structure describing the events to be monitored. + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +static int usrsock_pollsetup(FAR struct socket *psock, FAR struct pollfd *fds) +{ + FAR struct usrsock_conn_s *conn = psock->s_conn; + FAR struct usrsock_poll_s *info; + FAR struct devif_callback_s *cb; + int ret = OK; + + /* Sanity check */ + +#ifdef CONFIG_DEBUG + if (!conn || !fds) + { + return -EINVAL; + } +#endif + + /* Allocate a container to hold the poll information */ + + info = (FAR struct usrsock_poll_s *)kmm_malloc(sizeof(struct usrsock_poll_s)); + if (!info) + { + return -ENOMEM; + } + + net_lock(); + + /* Allocate a usrsock callback structure */ + + cb = devif_callback_alloc(NULL, &conn->list); + if (!cb) + { + ret = -EBUSY; + kmm_free(info); /* fds->priv not set, so we need to free info here. */ + goto errout_unlock; + } + + /* Initialize the poll info container */ + + info->psock = psock; + info->fds = fds; + info->cb = cb; + + /* Initialize the callback structure. Save the reference to the info + * structure as callback private data so that it will be available during + * callback processing. + */ + + cb->flags = USRSOCK_EVENT_ABORT | USRSOCK_EVENT_CONNECT_RESP | + USRSOCK_EVENT_SENDTO_READY | USRSOCK_EVENT_RECVFROM_AVAIL | + USRSOCK_EVENT_REMOTE_CLOSED; + cb->priv = (FAR void *)info; + cb->event = poll_event; + + /* Save the reference in the poll info structure as fds private as well + * for use during poll teardown as well. + */ + + fds->priv = (FAR void *)info; + + /* Check if socket is in error state */ + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + ninfo("socket %s.\n", + conn->state == USRSOCK_CONN_STATE_UNINITIALIZED ? + "uninitialized" : "aborted"); + + fds->revents |= (POLLERR | POLLHUP); + } + + /* Stream sockets need to be connected or connecting (or listening). */ + + else if ((conn->type == SOCK_STREAM || conn->type == SOCK_SEQPACKET) && + !(conn->connected || conn->state == USRSOCK_CONN_STATE_CONNECTING)) + { + ninfo("stream socket not connected and not connecting.\n"); + + fds->revents |= (POLLOUT | POLLIN | POLLHUP); + } + else if (conn->flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ninfo("socket remote closed.\n"); + + /* Remote closed. */ + + fds->revents |= (POLLHUP | POLLIN); + } + else + { + /* Check if daemon has room for send data or has data to receive. */ + + if (conn->flags & USRSOCK_EVENT_SENDTO_READY) + { + ninfo("socket send ready.\n"); + + fds->revents |= (POLLOUT & fds->events); + } + + if (conn->flags & USRSOCK_EVENT_RECVFROM_AVAIL) + { + ninfo("socket recv avail.\n"); + + fds->revents |= (POLLIN & fds->events); + } + } + + /* Filter I/O events depending on requested events. */ + + fds->revents &= (~(POLLOUT | POLLIN) | info->fds->events); + + /* POLLOUT and PULLHUP are mutually exclusive. */ + + if ((fds->revents & POLLOUT) && (fds->revents & POLLHUP)) + { + fds->revents &= ~POLLOUT; + } + + /* Check if any requested events are already in effect */ + + if (fds->revents != 0) + { + /* Yes.. then signal the poll logic */ + + sem_post(fds->sem); + } + +errout_unlock: + net_unlock(); + return ret; +} + +/**************************************************************************** + * Function: usrsock_pollteardown + * + * Description: + * Teardown monitoring of events on an usrsock socket + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * fds - The structure describing the events to be stopped being monitored. + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +static int usrsock_pollteardown(FAR struct socket *psock, + FAR struct pollfd *fds) +{ + FAR struct usrsock_conn_s *conn = psock->s_conn; + FAR struct usrsock_poll_s *info; + + /* Sanity check */ + +#ifdef CONFIG_DEBUG + if (!conn || !fds->priv) + { + return -EINVAL; + } +#endif + + /* Recover the socket descriptor poll state info from the poll structure */ + + info = (FAR struct usrsock_poll_s *)fds->priv; + DEBUGASSERT(info && info->fds && info->cb); + if (info) + { + /* Release the callback */ + + net_lock(); + devif_conn_callback_free(NULL, info->cb, &conn->list); + net_unlock(); + + /* Release the poll/select data slot */ + + info->fds->priv = NULL; + + /* Then free the poll info container */ + + kmm_free(info); + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_poll + * + * Description: + * The standard poll() operation redirects operations on socket descriptors + * to this function. + * + * Input Parameters: + * psock - An instance of the internal socket structure. + * fds - The structure describing the events to be monitored. + * setup - true: Setup up the poll; false: Teardown the poll + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +int usrsock_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) +{ + if (setup) + { + return usrsock_pollsetup(psock, fds); + } + else + { + return usrsock_pollteardown(psock, fds); + } +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK && !CONFIG_DISABLE_POLL */ diff --git a/net/usrsock/usrsock_recvfrom.c b/net/usrsock/usrsock_recvfrom.c new file mode 100644 index 00000000000..d97cba5c3a9 --- /dev/null +++ b/net/usrsock/usrsock_recvfrom.c @@ -0,0 +1,485 @@ +/**************************************************************************** + * net/usrsock/usrsock_recvfrom.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t recvfrom_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_data_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->reqstate.result = -ECONNABORTED; + pstate->valuelen = 0; + pstate->valuelen_nontrunc = 0; + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->reqstate.result = conn->resp.result; + if (pstate->reqstate.result < 0) + { + pstate->valuelen = 0; + pstate->valuelen_nontrunc = 0; + } + else + { + pstate->valuelen = conn->resp.valuelen; + pstate->valuelen_nontrunc = conn->resp.valuelen_nontrunc; + } + + if (pstate->reqstate.result >= 0 || + pstate->reqstate.result == -EAGAIN) + { + /* After reception of data, mark input not ready. Daemon will + * send event to restore this flag. */ + + conn->flags &= ~USRSOCK_EVENT_RECVFROM_AVAIL; + } + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + else if (flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ninfo("remote closed.\n"); + + pstate->reqstate.result = -EPIPE; + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + else if (flags & USRSOCK_EVENT_RECVFROM_AVAIL) + { + ninfo("recvfrom avail.\n"); + + flags &= ~USRSOCK_EVENT_RECVFROM_AVAIL; + + /* Stop further callbacks */ + + pstate->reqstate.cb->flags = 0; + pstate->reqstate.cb->priv = NULL; + pstate->reqstate.cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->reqstate.recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_recvfrom_request + ****************************************************************************/ + +static int do_recvfrom_request(FAR struct usrsock_conn_s *conn, size_t buflen, + socklen_t addrlen) +{ + struct usrsock_request_recvfrom_s req = {}; + struct iovec bufs[1]; + + if (addrlen > UINT16_MAX) + { + addrlen = UINT16_MAX; + } + + if (buflen > UINT16_MAX) + { + buflen = UINT16_MAX; + } + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_RECVFROM; + req.usockid = conn->usockid; + req.max_addrlen = addrlen; + req.max_buflen = buflen; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Name: setup_conn_recvfrom + ****************************************************************************/ + +static void setup_conn_recvfrom(FAR struct usrsock_conn_s *conn, + FAR struct iovec *iov, unsigned int iovcnt) +{ + unsigned int i; + + conn->resp.datain.iov = iov; + conn->resp.datain.pos = 0; + conn->resp.datain.total = 0; + conn->resp.datain.iovcnt = iovcnt; + + for (i = 0; i < iovcnt; i++) + { + conn->resp.datain.total += iov[i].iov_len; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_recvfrom + * + * Description: + * recvfrom() receives messages from a socket, and may be used to receive + * data on a socket whether or not it is connection-oriented. + * + * If from is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument fromlen + * initialized to the size of the buffer associated with from, and modified + * on return to indicate the actual size of the address stored there. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + ****************************************************************************/ + +ssize_t usrsock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + FAR struct sockaddr *from, FAR socklen_t *fromlen) +{ + FAR struct usrsock_conn_s *conn = psock->s_conn; + struct usrsock_data_reqstate_s state = {}; + struct iovec inbufs[2]; + socklen_t addrlen = 0; + socklen_t outaddrlen = 0; + ssize_t ret; +#ifdef CONFIG_NET_SOCKOPTS + struct timespec abstime; +#endif + struct timespec *ptimeo = NULL; + + DEBUGASSERT(conn); + + if (fromlen) + { + if (*fromlen > 0 && from == NULL) + { + return -EINVAL; + } + + addrlen = *fromlen; + } + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Invalid state or closed by daemon. */ + + ninfo("usockid=%d; connect() with uninitialized usrsock.\n", + conn->usockid); + + ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE : -ECONNRESET; + goto errout_unlock; + } + + if (conn->type == SOCK_STREAM || conn->type == SOCK_SEQPACKET) + { + if (!conn->connected) + { + if (conn->state == USRSOCK_CONN_STATE_CONNECTING) + { + /* Connecting. */ + + ninfo("usockid=%d; socket still connecting.\n", + conn->usockid); + + ret = -EAGAIN; + goto errout_unlock; + } + else + { + /* Not connected. */ + + ninfo("usockid=%d; socket not connected.\n", + conn->usockid); + + ret = -ENOTCONN; + goto errout_unlock; + } + } + } + + if (conn->state == USRSOCK_CONN_STATE_CONNECTING) + { + /* Non-blocking connecting. */ + + ninfo("usockid=%d; socket still connecting.\n", + conn->usockid); + + ret = -EAGAIN; + goto errout_unlock; + } + +#ifdef CONFIG_NET_SOCKOPTS + if (psock->s_rcvtimeo != 0) + { + DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime)); + + /* Prepare timeout value for recvfrom. */ + + abstime.tv_sec += psock->s_rcvtimeo / DSEC_PER_SEC; + abstime.tv_nsec += (psock->s_rcvtimeo % DSEC_PER_SEC) * NSEC_PER_DSEC; + if (abstime.tv_nsec >= NSEC_PER_SEC) + { + abstime.tv_sec++; + abstime.tv_nsec -= NSEC_PER_SEC; + } + + ptimeo = &abstime; + } +#endif + + do + { + /* Check if remote end has closed connection. */ + + if (conn->flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ninfo("usockid=%d; remote closed (EOF).\n", conn->usockid); + + ret = 0; + goto errout_unlock; + } + + /* Check if need to wait for receive data to become available. */ + + if (!(conn->flags & USRSOCK_EVENT_RECVFROM_AVAIL)) + { + if (_SS_ISNONBLOCK(psock->s_flags)) + { + /* Nothing to receive from daemon side. */ + + ret = -EAGAIN; + goto errout_unlock; + } + + /* Wait recv to become avail. */ + + ret = usrsock_setup_data_request_callback( + conn, &state, recvfrom_event, + USRSOCK_EVENT_ABORT | USRSOCK_EVENT_RECVFROM_AVAIL | + USRSOCK_EVENT_REMOTE_CLOSED); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + /* Wait for receive-avail (or abort, or timeout, or signal). */ + + ret = 0; + if (net_timedwait(&state.reqstate.recvsem, ptimeo) != OK) + { + ret = *get_errno_ptr(); + + if (ret == ETIMEDOUT) + { + ninfo("recvfrom timedout\n"); + + ret = -EAGAIN; + } + else if (ret == EINTR) + { + ninfo("recvfrom interrupted\n"); + + ret = -EINTR; + } + else + { + nerr("net_timedwait errno: %d\n", ret); + DEBUGASSERT(false); + } + } + + usrsock_teardown_data_request_callback(&state); + + /* Did wait timeout or got signal? */ + + if (ret != 0) + { + goto errout_unlock; + } + + /* Was socket aborted? */ + + if (conn->state == USRSOCK_CONN_STATE_ABORTED) + { + ret = -EPIPE; + goto errout_unlock; + } + + /* Did remote disconnect? */ + + if (conn->flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ret = 0; + goto errout_unlock; + } + + DEBUGASSERT(conn->flags & USRSOCK_EVENT_RECVFROM_AVAIL); + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_data_request_callback( + conn, &state, recvfrom_event, + USRSOCK_EVENT_ABORT | USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + inbufs[0].iov_base = (FAR void *)from; + inbufs[0].iov_len = addrlen; + inbufs[1].iov_base = (FAR void *)buf; + inbufs[1].iov_len = len; + + setup_conn_recvfrom(conn, inbufs, ARRAY_SIZE(inbufs)); + + /* Request user-space daemon to close socket. */ + + ret = do_recvfrom_request(conn, len, addrlen); + if (ret >= 0) + { + /* Wait for completion of request. */ + + while (net_lockedwait(&state.reqstate.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + ret = state.reqstate.result; + + DEBUGASSERT(ret <= (ssize_t)len); + DEBUGASSERT(state.valuelen <= addrlen); + DEBUGASSERT(state.valuelen <= state.valuelen_nontrunc); + + if (ret >= 0) + { + /* Store length of 'from' address that was available at + * daemon-side. */ + + outaddrlen = state.valuelen_nontrunc; + } + } + + setup_conn_recvfrom(conn, NULL, 0); + usrsock_teardown_data_request_callback(&state); + } + while (ret == -EAGAIN); + +errout_unlock: + net_unlock(); + + if (fromlen) + { + *fromlen = outaddrlen; + } + + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_sendto.c b/net/usrsock/usrsock_sendto.c new file mode 100644 index 00000000000..dc747d451cf --- /dev/null +++ b/net/usrsock/usrsock_sendto.c @@ -0,0 +1,426 @@ +/**************************************************************************** + * net/usrsock/usrsock_sendto.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t sendto_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->result = -ECONNABORTED; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->result = conn->resp.result; + + if (pstate->result >= 0 || pstate->result == -EAGAIN) + { + /* After reception of data, mark input not ready. Daemon will + * send event to restore this flag. */ + + conn->flags &= ~USRSOCK_EVENT_SENDTO_READY; + } + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ninfo("remote closed.\n"); + + pstate->result = -EPIPE; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_SENDTO_READY) + { + ninfo("sendto ready.\n"); + + /* Do not let other waiters to claim new data. */ + + flags &= ~USRSOCK_EVENT_SENDTO_READY; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_sendto_request + ****************************************************************************/ + +static int do_sendto_request(FAR struct usrsock_conn_s *conn, + FAR const void *buf, size_t buflen, + FAR const struct sockaddr *addr, + socklen_t addrlen) +{ + struct usrsock_request_sendto_s req = {}; + struct iovec bufs[3]; + + if (addrlen > UINT16_MAX) + { + addrlen = UINT16_MAX; + } + + if (buflen > UINT16_MAX) + { + buflen = UINT16_MAX; + } + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_SENDTO; + req.usockid = conn->usockid; + req.addrlen = addrlen; + req.buflen = buflen; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + bufs[1].iov_base = (FAR void *)addr; + bufs[1].iov_len = addrlen; + bufs[2].iov_base = (FAR void *)buf; + bufs[2].iov_len = buflen; + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_sendto + * + * Description: + * If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) + * socket, the parameters to and 'tolen' are ignored (and the error EISCONN + * may be returned when they are not NULL and 0), and the error ENOTCONN is + * returned when the socket was not actually connected. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + ****************************************************************************/ + +ssize_t usrsock_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, FAR const struct sockaddr *to, + socklen_t tolen) +{ + FAR struct usrsock_conn_s *conn = psock->s_conn; + struct usrsock_reqstate_s state = {}; + ssize_t ret; +#ifdef CONFIG_NET_SOCKOPTS + struct timespec abstime; +#endif + struct timespec *ptimeo = NULL; + + DEBUGASSERT(conn); + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Invalid state or closed by daemon. */ + + ninfo("usockid=%d; connect() with uninitialized usrsock.\n", + conn->usockid); + + ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE : -ECONNRESET; + goto errout_unlock; + } + + if (conn->type == SOCK_STREAM || conn->type == SOCK_SEQPACKET) + { + if (!conn->connected) + { + if (conn->state == USRSOCK_CONN_STATE_CONNECTING) + { + /* Connecting. */ + + ninfo("usockid=%d; socket still connecting.\n", + conn->usockid); + + ret = -EAGAIN; + goto errout_unlock; + } + else + { + /* Not connected. */ + + ret = -ENOTCONN; + goto errout_unlock; + } + } + + if (to || tolen) + { + /* Address provided for connection-mode socket */ + + ret = -EISCONN; + goto errout_unlock; + } + } + + if (conn->state == USRSOCK_CONN_STATE_CONNECTING) + { + /* Non-blocking connecting. */ + + ninfo("usockid=%d; socket still connecting.\n", conn->usockid); + + ret = -EAGAIN; + goto errout_unlock; + } + +#ifdef CONFIG_NET_SOCKOPTS + if (psock->s_sndtimeo != 0) + { + DEBUGVERIFY(clock_gettime(CLOCK_REALTIME, &abstime)); + + /* Prepare timeout value for sendto. */ + + abstime.tv_sec += psock->s_sndtimeo / DSEC_PER_SEC; + abstime.tv_nsec += (psock->s_sndtimeo % DSEC_PER_SEC) * NSEC_PER_DSEC; + if (abstime.tv_nsec >= NSEC_PER_SEC) + { + abstime.tv_sec++; + abstime.tv_nsec -= NSEC_PER_SEC; + } + + ptimeo = &abstime; + } +#endif + + do + { + /* Check if remote end has closed connection. */ + + if (conn->flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ninfo("usockid=%d; remote closed.\n", conn->usockid); + + ret = -EPIPE; + goto errout_unlock; + } + + /* Check if need to wait for send to become ready. */ + + if (!(conn->flags & USRSOCK_EVENT_SENDTO_READY)) + { + if (_SS_ISNONBLOCK(psock->s_flags)) + { + /* Send busy at daemon side. */ + + ret = -EAGAIN; + goto errout_unlock; + } + + /* Wait send to become ready. */ + + ret = usrsock_setup_request_callback(conn, &state, sendto_event, + USRSOCK_EVENT_ABORT | + USRSOCK_EVENT_SENDTO_READY | + USRSOCK_EVENT_REMOTE_CLOSED); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + /* Wait for send-ready (or abort, or timeout, or signal). */ + + ret = 0; + if (net_timedwait(&state.recvsem, ptimeo) != OK) + { + ret = *get_errno_ptr(); + + if (ret == ETIMEDOUT) + { + ninfo("sendto timedout\n"); + + ret = -EAGAIN; + } + else if (ret == EINTR) + { + ninfo("sendto interrupted\n"); + + ret = -EINTR; + } + else + { + nerr("net_timedwait errno: %d\n", ret); + DEBUGASSERT(false); + } + } + + usrsock_teardown_request_callback(&state); + + /* Did wait timeout or got signal? */ + + if (ret != 0) + { + goto errout_unlock; + } + + /* Was socket aborted? */ + + if (conn->state == USRSOCK_CONN_STATE_ABORTED) + { + ret = -EPIPE; + goto errout_unlock; + } + + /* Did remote disconnect? */ + + if (conn->flags & USRSOCK_EVENT_REMOTE_CLOSED) + { + ret = -EPIPE; + goto errout_unlock; + } + + DEBUGASSERT(conn->flags & USRSOCK_EVENT_SENDTO_READY); + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_request_callback(conn, &state, sendto_event, + USRSOCK_EVENT_ABORT | + USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + /* Request user-space daemon to close socket. */ + + ret = do_sendto_request(conn, buf, len, to, tolen); + if (ret >= 0) + { + /* Wait for completion of request. */ + + while (net_lockedwait(&state.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + ret = state.result; + + DEBUGASSERT(ret <= (ssize_t)len); + } + + usrsock_teardown_request_callback(&state); + } + while (ret == -EAGAIN); + +errout_unlock: + net_unlock(); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/usrsock/usrsock_setsockopt.c b/net/usrsock/usrsock_setsockopt.c new file mode 100644 index 00000000000..5795fdee948 --- /dev/null +++ b/net/usrsock/usrsock_setsockopt.c @@ -0,0 +1,230 @@ +/**************************************************************************** + * net/usrsock/usrsock_setsockopt.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) && \ + defined(CONFIG_NET_SOCKOPTS) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t setsockopt_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->result = -ECONNABORTED; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->result = conn->resp.result; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_setsockopt_request + ****************************************************************************/ + +static int do_setsockopt_request(FAR struct usrsock_conn_s *conn, + int level, int option, FAR const void *value, + socklen_t value_len) +{ + struct usrsock_request_setsockopt_s req = {}; + struct iovec bufs[2]; + + if (level < INT16_MIN || level > INT16_MAX) + { + return -EINVAL; + } + + if (option < INT16_MIN || option > INT16_MAX) + { + return -EINVAL; + } + + if (value_len > UINT16_MAX) + { + value_len = UINT16_MAX; + } + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_SETSOCKOPT; + req.usockid = conn->usockid; + req.level = level; + req.option = option; + req.valuelen = value_len; + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + bufs[1].iov_base = (FAR void *)value; + bufs[1].iov_len = req.valuelen; + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_setsockopt + * + * Description: + * psock_setsockopt() sets the option specified by the 'option' argument, + * at the protocol level specified by the 'level' argument, to the value + * pointed to by the 'value' argument for the socket on the 'psock' argument. + * + * The 'level' argument specifies the protocol level of the option. To set + * options at the socket level, specify the level argument as SOL_SOCKET. + * + * See a complete list of values for the 'option' argument. + * + * Parameters: + * conn usrsock socket connection structure + * level Protocol level to set the option + * option identifies the option to set + * value Points to the argument value + * value_len The length of the argument value + * + ****************************************************************************/ + +int usrsock_setsockopt(FAR struct usrsock_conn_s *conn, int level, int option, + FAR const void *value, FAR socklen_t value_len) +{ + struct usrsock_reqstate_s state = {}; + ssize_t ret; + + DEBUGASSERT(conn); + + net_lock(); + + if (conn->state == USRSOCK_CONN_STATE_UNINITIALIZED || + conn->state == USRSOCK_CONN_STATE_ABORTED) + { + /* Invalid state or closed by daemon. */ + + ninfo("usockid=%d; connect() with uninitialized usrsock.\n", + conn->usockid); + + ret = (conn->state == USRSOCK_CONN_STATE_ABORTED) ? -EPIPE : -ECONNRESET; + goto errout_unlock; + } + + /* Set up event callback for usrsock. */ + + ret = usrsock_setup_request_callback(conn, &state, setsockopt_event, + USRSOCK_EVENT_ABORT | + USRSOCK_EVENT_REQ_COMPLETE); + if (ret < 0) + { + nwarn("usrsock_setup_request_callback failed: %d\n", ret); + goto errout_unlock; + } + + /* Request user-space daemon to close socket. */ + + ret = do_setsockopt_request(conn, level, option, value, value_len); + if (ret >= 0) + { + /* Wait for completion of request. */ + + while (net_lockedwait(&state.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + ret = state.result; + } + + usrsock_teardown_request_callback(&state); + +errout_unlock: + net_unlock(); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK && CONFIG_NET_SOCKOPTS */ diff --git a/net/usrsock/usrsock_socket.c b/net/usrsock/usrsock_socket.c new file mode 100644 index 00000000000..71764befea6 --- /dev/null +++ b/net/usrsock/usrsock_socket.c @@ -0,0 +1,265 @@ +/**************************************************************************** + * net/usrsock/usrsock_socket.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_USRSOCK) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "devif/devif.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint16_t socket_event(FAR struct net_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct usrsock_reqstate_s *pstate = pvpriv; + FAR struct usrsock_conn_s *conn = pvconn; + + if (flags & USRSOCK_EVENT_ABORT) + { + ninfo("socket aborted.\n"); + + pstate->result = -ENETDOWN; + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + else if (flags & USRSOCK_EVENT_REQ_COMPLETE) + { + ninfo("request completed.\n"); + + pstate->result = conn->resp.result; + if (pstate->result >= 0) + { + /* We might start getting events for this socket right after + * returning to daemon, so setup 'conn' already here. */ + + conn->state = USRSOCK_CONN_STATE_READY; + conn->usockid = pstate->result; + } + + /* Stop further callbacks */ + + pstate->cb->flags = 0; + pstate->cb->priv = NULL; + pstate->cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->recvsem); + } + + return flags; +} + +/**************************************************************************** + * Name: do_socket_request + ****************************************************************************/ + +static int do_socket_request(FAR struct usrsock_conn_s *conn, int domain, + int type, int protocol) +{ + struct usrsock_request_socket_s req = {}; + struct iovec bufs[1]; + + /* Prepare request for daemon to read. */ + + req.head.reqid = USRSOCK_REQUEST_SOCKET; + req.domain = domain; + req.type = type; + req.protocol = protocol; + + if (req.domain != domain) + { + return -EINVAL; + } + + if (req.type != type) + { + return -EINVAL; + } + + if (req.protocol != protocol) + { + return -EINVAL; + } + + bufs[0].iov_base = (FAR void *)&req; + bufs[0].iov_len = sizeof(req); + + return usrsockdev_do_request(conn, bufs, ARRAY_SIZE(bufs)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: usrsock_socket + * + * Description: + * socket() creates an endpoint for communication and returns a socket + * structure. + * + * Parameters: + * domain (see sys/socket.h) + * type (see sys/socket.h) + * protocol (see sys/socket.h) + * psock A pointer to a user allocated socket structure to be initialized. + * + * Returned Value: + * 0 on success; negative error-code on error + * + * EACCES + * Permission to create a socket of the specified type and/or protocol + * is denied. + * EAFNOSUPPORT + * The implementation does not support the specified address family. + * EINVAL + * Unknown protocol, or protocol family not available. + * EMFILE + * Process file table overflow. + * ENFILE + * The system limit on the total number of open files has been reached. + * ENOBUFS or ENOMEM + * Insufficient memory is available. The socket cannot be created until + * sufficient resources are freed. + * EPROTONOSUPPORT + * The protocol type or the specified protocol is not supported within + * this domain. + * + * Assumptions: + * + ****************************************************************************/ + +int usrsock_socket(int domain, int type, int protocol, FAR struct socket *psock) +{ + struct usrsock_reqstate_s state = {}; + FAR struct usrsock_conn_s *conn; + int err; + + /* Allocate the usrsock socket connection structure and save in the new + * socket instance. + */ + + conn = usrsock_alloc(); + if (!conn) + { + /* Failed to reserve a connection structure */ + + return -ENOMEM; + } + + net_lock(); + + /* Set up event callback for usrsock. */ + + err = usrsock_setup_request_callback(conn, &state, socket_event, + USRSOCK_EVENT_ABORT | + USRSOCK_EVENT_REQ_COMPLETE); + if (err < 0) + { + goto errout_free_conn; + } + + /* Request user-space daemon for new socket. */ + + err = do_socket_request(conn, domain, type, protocol); + if (err < 0) + { + goto errout_teardown_callback; + } + + /* Wait for completion of request. */ + + while (net_lockedwait(&state.recvsem) != OK) + { + DEBUGASSERT(*get_errno_ptr() == EINTR); + } + + if (state.result < 0) + { + err = state.result; + goto errout_teardown_callback; + } + + psock->s_type = SOCK_USRSOCK_TYPE; + psock->s_domain = PF_USRSOCK_DOMAIN; + conn->type = type; + psock->s_conn = conn; + conn->crefs = 1; + + usrsock_teardown_request_callback(&state); + + net_unlock(); + + return OK; + +errout_teardown_callback: + usrsock_teardown_request_callback(&state); +errout_free_conn: + usrsock_free(conn); + net_unlock(); + + return err; +} + +#endif /* CONFIG_NET && CONFIG_NET_USRSOCK */ diff --git a/net/utils/Make.defs b/net/utils/Make.defs index 270b80bbbe8..daad84805ea 100644 --- a/net/utils/Make.defs +++ b/net/utils/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # net/utils/Make.defs # -# Copyright (C) 2014 Gregory Nutt. All rights reserved. +# Copyright (C) 2014, 2017 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -36,7 +36,7 @@ # Common utilities NET_CSRCS += net_dsec2tick.c net_dsec2timeval.c net_timeval2dsec.c -NET_CSRCS += net_chksum.c net_lock.c +NET_CSRCS += net_chksum.c net_ipchksum.c net_incr32.c net_lock.c # IPv6 utilities @@ -44,6 +44,26 @@ ifeq ($(CONFIG_NET_IPv6),y) NET_CSRCS += net_ipv6_maskcmp.c net_ipv6_mask2pref.c net_ipv6_pref2mask.c endif +# TCP utilities + +ifeq ($(CONFIG_NET_TCP),y) +NET_CSRCS += net_tcpchksum.c +endif + +# UDP utilities + +ifeq ($(CONFIG_NET_UDP),y) +NET_CSRCS += net_udpchksum.c +endif + +# ICMP utilities + +ifeq ($(CONFIG_NET_ICMP),y) +NET_CSRCS += net_icmpchksum.c +else ifeq ($(CONFIG_NET_ICMPv6),y) +NET_CSRCS += net_icmpchksum.c +endif + # Include utility build support DEPPATH += --dep-path utils diff --git a/net/utils/net_chksum.c b/net/utils/net_chksum.c index 9f4055c7564..65272387759 100644 --- a/net/utils/net_chksum.c +++ b/net/utils/net_chksum.c @@ -56,19 +56,31 @@ #define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) #define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) -#define ICMPBUF ((struct icmp_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) -#define ICMPv6BUF ((struct icmp_ipv6hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) /**************************************************************************** - * Private Functions + * Public Functions ****************************************************************************/ /**************************************************************************** * Name: chksum + * + * Description: + * Calculate the raw change some over the memory region described by + * data and len. + * + * Input Parameters: + * sum - Partial calculations carried over from a previous call to chksum(). + * This should be zero on the first time that check sum is called. + * data - Beginning of the data to include in the checksum. + * len - Length of the data to include in the checksum. + * + * Returned Value: + * The updated checksum value. + * ****************************************************************************/ #ifndef CONFIG_NET_ARCH_CHKSUM -static uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len) +uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len) { FAR const uint8_t *dataptr; FAR const uint8_t *last_byte; @@ -107,158 +119,6 @@ static uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len) } #endif /* CONFIG_NET_ARCH_CHKSUM */ -/**************************************************************************** - * Name: ipv4_upperlayer_chksum - ****************************************************************************/ - -#if !defined(CONFIG_NET_ARCH_CHKSUM) && defined(CONFIG_NET_IPv4) -static uint16_t ipv4_upperlayer_chksum(FAR struct net_driver_s *dev, - uint8_t proto) -{ - FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; - uint16_t upperlen; - uint16_t sum; - - /* The length reported in the IPv4 header is the length of both the IPv4 - * header and the payload that follows the header. We need to subtract - * the size of the IPv4 header to get the size of the payload. - */ - - upperlen = (((uint16_t)(ipv4->len[0]) << 8) + ipv4->len[1]) - IPv4_HDRLEN; - - /* Verify some minimal assumptions */ - - if (upperlen > NET_DEV_MTU(dev)) - { - return 0; - } - - /* First sum pseudo-header. */ - /* IP protocol and length fields. This addition cannot carry. */ - - sum = upperlen + proto; - - /* Sum IP source and destination addresses. */ - - sum = chksum(sum, (FAR uint8_t *)&ipv4->srcipaddr, 2 * sizeof(in_addr_t)); - - /* Sum IP payload data. */ - - sum = chksum(sum, &dev->d_buf[IPv4_HDRLEN + NET_LL_HDRLEN(dev)], upperlen); - return (sum == 0) ? 0xffff : htons(sum); -} -#endif /* CONFIG_NET_ARCH_CHKSUM */ - -/**************************************************************************** - * Name: ipv6_upperlayer_chksum - ****************************************************************************/ - -#if !defined(CONFIG_NET_ARCH_CHKSUM) && defined(CONFIG_NET_IPv6) -static uint16_t ipv6_upperlayer_chksum(FAR struct net_driver_s *dev, - uint8_t proto) -{ - FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - uint16_t upperlen; - uint16_t sum; - - /* The length reported in the IPv6 header is the length of the payload - * that follows the header. - */ - - upperlen = ((uint16_t)ipv6->len[0] << 8) + ipv6->len[1]; - - /* Verify some minimal assumptions */ - - if (upperlen > NET_DEV_MTU(dev)) - { - return 0; - } - - /* The checksum is calculated starting with a pseudo-header of IPv6 header - * fields according to the IPv6 standard, which consists of the source - * and destination addresses, the packet length and the next header field. - */ - - sum = upperlen + proto; - - /* Sum IP source and destination addresses. */ - - sum = chksum(sum, (FAR uint8_t *)&ipv6->srcipaddr, 2 * sizeof(net_ipv6addr_t)); - - /* Sum IP payload data. */ - - sum = chksum(sum, &dev->d_buf[IPv6_HDRLEN + NET_LL_HDRLEN(dev)], upperlen); - return (sum == 0) ? 0xffff : htons(sum); -} -#endif /* CONFIG_NET_ARCH_CHKSUM */ - -/**************************************************************************** - * Name: net_carry32 - * - * Description: - * Calculate the Internet checksum over a buffer. - * - ****************************************************************************/ - -#ifndef CONFIG_NET_ARCH_INCR32 -static inline void net_carry32(FAR uint8_t *sum, uint16_t op16) -{ - if (sum[2] < (op16 >> 8)) - { - ++sum[1]; - if (sum[1] == 0) - { - ++sum[0]; - } - } - - if (sum[3] < (op16 & 0xff)) - { - ++sum[2]; - if (sum[2] == 0) - { - ++sum[1]; - if (sum[1] == 0) - { - ++sum[0]; - } - } - } -} -#endif /* CONFIG_NET_ARCH_INCR32 */ - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: net_incr32 - * - * Description: - * - * Carry out a 32-bit addition. - * - * By defining CONFIG_NET_ARCH_INCR32, the architecture can replace - * net_incr32 with hardware assisted solutions. - * - * Input Parameters: - * op32 - A pointer to a 4-byte array representing a 32-bit integer in - * network byte order (big endian). This value may not be word - * aligned. The value pointed to by op32 is modified in place - * - * op16 - A 16-bit integer in host byte order. - * - ****************************************************************************/ - -#ifndef CONFIG_NET_ARCH_INCR32 -void net_incr32(FAR uint8_t *op32, uint16_t op16) -{ - op32[3] += (op16 & 0xff); - op32[2] += (op16 >> 8); - net_carry32(op32, op16); -} -#endif /* CONFIG_NET_ARCH_INCR32 */ - /**************************************************************************** * Name: net_chksum * @@ -291,141 +151,4 @@ uint16_t net_chksum(FAR uint16_t *data, uint16_t len) } #endif /* CONFIG_NET_ARCH_CHKSUM */ -/**************************************************************************** - * Name: ipv4_chksum - * - * Description: - * Calculate the IPv4 header checksum of the packet header in d_buf. - * - * The IPv4 header checksum is the Internet checksum of the 20 bytes of - * the IPv4 header. - * - * If CONFIG_NET_ARCH_CHKSUM is defined, then this function must be - * provided by architecture-specific logic. - * - * Returned Value: - * The IPv4 header checksum of the IPv4 header in the d_buf buffer. - * - ****************************************************************************/ - -#if defined(CONFIG_NET_IPv4) && !defined(CONFIG_NET_ARCH_CHKSUM) -uint16_t ipv4_chksum(FAR struct net_driver_s *dev) -{ - uint16_t sum; - - sum = chksum(0, &dev->d_buf[NET_LL_HDRLEN(dev)], IPv4_HDRLEN); - return (sum == 0) ? 0xffff : htons(sum); -} -#endif /* CONFIG_NET_ARCH_CHKSUM */ - -/**************************************************************************** - * Name: tcp_chksum, tcp_ipv4_chksum, and tcp_ipv6_chksum - * - * Description: - * Calculate the TCP checksum of the packet in d_buf and d_appdata. - * - * The TCP checksum is the Internet checksum of data contents of the - * TCP segment, and a pseudo-header as defined in RFC793. - * - * Note: The d_appdata pointer that points to the packet data may - * point anywhere in memory, so it is not possible to simply calculate - * the Internet checksum of the contents of the d_buf buffer. - * - * Returned Value: - * The TCP checksum of the TCP segment in d_buf and pointed to by - * d_appdata. - * - ****************************************************************************/ - -#ifndef CONFIG_NET_ARCH_CHKSUM -#ifdef CONFIG_NET_IPv4 -uint16_t tcp_ipv4_chksum(FAR struct net_driver_s *dev) -{ - return ipv4_upperlayer_chksum(dev, IP_PROTO_TCP); -} -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 -uint16_t tcp_ipv6_chksum(FAR struct net_driver_s *dev) -{ - return ipv6_upperlayer_chksum(dev, IP_PROTO_TCP); -} -#endif /* CONFIG_NET_IPv6 */ -#endif /* !CONFIG_NET_ARCH_CHKSUM */ - -#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) -uint16_t tcp_chksum(FAR struct net_driver_s *dev) -{ - if (IFF_IS_IPv6(dev->d_flags)) - { - return tcp_ipv6_chksum(dev); - } - else - { - return tcp_ipv4_chksum(dev); - } -} -#endif - -/**************************************************************************** - * Name: udp_ipv4_chksum - * - * Description: - * Calculate the UDP/IPv4 checksum of the packet in d_buf and d_appdata. - * - ****************************************************************************/ - -#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv4) -uint16_t udp_ipv4_chksum(FAR struct net_driver_s *dev) -{ - return ipv4_upperlayer_chksum(dev, IP_PROTO_UDP); -} -#endif - -/**************************************************************************** - * Name: udp_ipv6_chksum - * - * Description: - * Calculate the UDP/IPv6 checksum of the packet in d_buf and d_appdata. - * - ****************************************************************************/ - -#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv6) -uint16_t udp_ipv6_chksum(FAR struct net_driver_s *dev) -{ - return ipv6_upperlayer_chksum(dev, IP_PROTO_UDP); -} -#endif - -/**************************************************************************** - * Name: icmp_chksum - * - * Description: - * Calculate the checksum of the ICMP message - * - ****************************************************************************/ - -#if defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) -uint16_t icmp_chksum(FAR struct net_driver_s *dev, int len) -{ - FAR struct icmp_iphdr_s *icmp = ICMPBUF; - return net_chksum((FAR uint16_t *)&icmp->type, len); -} -#endif /* CONFIG_NET_ICMP && CONFIG_NET_ICMP_PING */ - -/**************************************************************************** - * Name: icmpv6_chksum - * - * Description: - * Calculate the checksum of the ICMPv6 message - * - ****************************************************************************/ - -#ifdef CONFIG_NET_ICMPv6 -uint16_t icmpv6_chksum(FAR struct net_driver_s *dev) -{ - return ipv6_upperlayer_chksum(dev, IP_PROTO_ICMP6); -} -#endif - #endif /* CONFIG_NET */ diff --git a/net/utils/net_icmpchksum.c b/net/utils/net_icmpchksum.c new file mode 100644 index 00000000000..c9d24148bd9 --- /dev/null +++ b/net/utils/net_icmpchksum.c @@ -0,0 +1,94 @@ +/**************************************************************************** + * net/utils/net_icmpchksum.c + * + * Copyright (C) 2007-2010, 2012, 2014-2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#ifdef CONFIG_NET + +#include +#include + +#include +#include +#include + +#include "utils/utils.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ICMPBUF ((struct icmp_iphdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define ICMPv6BUF ((struct icmp_ipv6hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: icmp_chksum + * + * Description: + * Calculate the checksum of the ICMP message + * + ****************************************************************************/ + +#if defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) +uint16_t icmp_chksum(FAR struct net_driver_s *dev, int len) +{ + FAR struct icmp_iphdr_s *icmp = ICMPBUF; + return net_chksum((FAR uint16_t *)&icmp->type, len); +} +#endif /* CONFIG_NET_ICMP && CONFIG_NET_ICMP_PING */ + +/**************************************************************************** + * Name: icmpv6_chksum + * + * Description: + * Calculate the checksum of the ICMPv6 message + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6 +uint16_t icmpv6_chksum(FAR struct net_driver_s *dev) +{ + return ipv6_upperlayer_chksum(dev, IP_PROTO_ICMP6); +} +#endif + +#endif /* CONFIG_NET */ diff --git a/net/utils/net_incr32.c b/net/utils/net_incr32.c new file mode 100644 index 00000000000..b80acb6f8d4 --- /dev/null +++ b/net/utils/net_incr32.c @@ -0,0 +1,121 @@ +/**************************************************************************** + * net/utils/net_incr32.c + * + * Copyright (C) 2007-2010, 2012, 2014-2015, 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +#include "utils/utils.h" + +#ifndef CONFIG_NET_ARCH_INCR32 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: net_carry32 + * + * Description: + * Calculate the Internet checksum over a buffer. + * + ****************************************************************************/ + +static inline void net_carry32(FAR uint8_t *sum, uint16_t op16) +{ + if (sum[2] < (op16 >> 8)) + { + ++sum[1]; + if (sum[1] == 0) + { + ++sum[0]; + } + } + + if (sum[3] < (op16 & 0xff)) + { + ++sum[2]; + if (sum[2] == 0) + { + ++sum[1]; + if (sum[1] == 0) + { + ++sum[0]; + } + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: net_incr32 + * + * Description: + * + * Carry out a 32-bit addition. + * + * By defining CONFIG_NET_ARCH_INCR32, the architecture can replace + * net_incr32 with hardware assisted solutions. + * + * Input Parameters: + * op32 - A pointer to a 4-byte array representing a 32-bit integer in + * network byte order (big endian). This value may not be word + * aligned. The value pointed to by op32 is modified in place + * + * op16 - A 16-bit integer in host byte order. + * + ****************************************************************************/ + +void net_incr32(FAR uint8_t *op32, uint16_t op16) +{ + op32[3] += (op16 & 0xff); + op32[2] += (op16 >> 8); + net_carry32(op32, op16); +} + +#endif /* CONFIG_NET_ARCH_INCR32 */ diff --git a/net/utils/net_ipchksum.c b/net/utils/net_ipchksum.c new file mode 100644 index 00000000000..2006033599d --- /dev/null +++ b/net/utils/net_ipchksum.c @@ -0,0 +1,199 @@ +/**************************************************************************** + * net/utils/net_ipchksum.c + * + * Copyright (C) 2007-2010, 2012, 2014-2015, 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include + +#include "utils/utils.h" + +#ifdef CONFIG_NET + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv4_upperlayer_chksum + * + * Description: + * Perform the checksum calcaultion over the IPv4, protocol headers, and + * data payload as necessary. + * + * Input Parameters: + * dev - The network driver instance. The packet data is in the d_buf + * of the device. + * proto - The protocol being supported + * + * Returned Value: + * The calculated checksum + * + ****************************************************************************/ + +#if !defined(CONFIG_NET_ARCH_CHKSUM) && defined(CONFIG_NET_IPv4) +uint16_t ipv4_upperlayer_chksum(FAR struct net_driver_s *dev, uint8_t proto) +{ + FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; + uint16_t upperlen; + uint16_t sum; + + /* The length reported in the IPv4 header is the length of both the IPv4 + * header and the payload that follows the header. We need to subtract + * the size of the IPv4 header to get the size of the payload. + */ + + upperlen = (((uint16_t)(ipv4->len[0]) << 8) + ipv4->len[1]) - IPv4_HDRLEN; + + /* Verify some minimal assumptions */ + + if (upperlen > NET_DEV_MTU(dev)) + { + return 0; + } + + /* First sum pseudo-header. */ + /* IP protocol and length fields. This addition cannot carry. */ + + sum = upperlen + proto; + + /* Sum IP source and destination addresses. */ + + sum = chksum(sum, (FAR uint8_t *)&ipv4->srcipaddr, 2 * sizeof(in_addr_t)); + + /* Sum IP payload data. */ + + sum = chksum(sum, &dev->d_buf[IPv4_HDRLEN + NET_LL_HDRLEN(dev)], upperlen); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif /* CONFIG_NET_ARCH_CHKSUM */ + +/**************************************************************************** + * Name: ipv6_upperlayer_chksum + * + * Description: + * Perform the checksum calcaultion over the IPv6, protocol headers, and + * data payload as necessary. + * + * Input Parameters: + * dev - The network driver instance. The packet data is in the d_buf + * of the device. + * proto - The protocol being supported + * + * Returned Value: + * The calculated checksum + * + ****************************************************************************/ + +#if !defined(CONFIG_NET_ARCH_CHKSUM) && defined(CONFIG_NET_IPv6) +uint16_t ipv6_upperlayer_chksum(FAR struct net_driver_s *dev, uint8_t proto) +{ + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + uint16_t upperlen; + uint16_t sum; + + /* The length reported in the IPv6 header is the length of the payload + * that follows the header. + */ + + upperlen = ((uint16_t)ipv6->len[0] << 8) + ipv6->len[1]; + + /* Verify some minimal assumptions */ + + if (upperlen > NET_DEV_MTU(dev)) + { + return 0; + } + + /* The checksum is calculated starting with a pseudo-header of IPv6 header + * fields according to the IPv6 standard, which consists of the source + * and destination addresses, the packet length and the next header field. + */ + + sum = upperlen + proto; + + /* Sum IP source and destination addresses. */ + + sum = chksum(sum, (FAR uint8_t *)&ipv6->srcipaddr, 2 * sizeof(net_ipv6addr_t)); + + /* Sum IP payload data. */ + + sum = chksum(sum, &dev->d_buf[IPv6_HDRLEN + NET_LL_HDRLEN(dev)], upperlen); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif /* CONFIG_NET_ARCH_CHKSUM */ + +/**************************************************************************** + * Name: ipv4_chksum + * + * Description: + * Calculate the IPv4 header checksum of the packet header in d_buf. + * + * The IPv4 header checksum is the Internet checksum of the 20 bytes of + * the IPv4 header. + * + * If CONFIG_NET_ARCH_CHKSUM is defined, then this function must be + * provided by architecture-specific logic. + * + * Returned Value: + * The IPv4 header checksum of the IPv4 header in the d_buf buffer. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IPv4) && !defined(CONFIG_NET_ARCH_CHKSUM) +uint16_t ipv4_chksum(FAR struct net_driver_s *dev) +{ + uint16_t sum; + + sum = chksum(0, &dev->d_buf[NET_LL_HDRLEN(dev)], IPv4_HDRLEN); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif /* CONFIG_NET_ARCH_CHKSUM */ + +#endif /* CONFIG_NET */ diff --git a/net/utils/net_tcpchksum.c b/net/utils/net_tcpchksum.c new file mode 100644 index 00000000000..b52226c6e1e --- /dev/null +++ b/net/utils/net_tcpchksum.c @@ -0,0 +1,104 @@ +/**************************************************************************** + * net/utils/net_tcpchksum.c + * + * Copyright (C) 2007-2010, 2012, 2014-2015, 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include "utils/utils.h" + +#ifdef CONFIG_NET_TCP + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_chksum, tcp_ipv4_chksum, and tcp_ipv6_chksum + * + * Description: + * Calculate the TCP checksum of the packet in d_buf and d_appdata. + * + * The TCP checksum is the Internet checksum of data contents of the + * TCP segment, and a pseudo-header as defined in RFC793. + * + * Note: The d_appdata pointer that points to the packet data may + * point anywhere in memory, so it is not possible to simply calculate + * the Internet checksum of the contents of the d_buf buffer. + * + * Returned Value: + * The TCP checksum of the TCP segment in d_buf and pointed to by + * d_appdata. + * + ****************************************************************************/ + +#ifndef CONFIG_NET_ARCH_CHKSUM +#ifdef CONFIG_NET_IPv4 +uint16_t tcp_ipv4_chksum(FAR struct net_driver_s *dev) +{ + return ipv4_upperlayer_chksum(dev, IP_PROTO_TCP); +} +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +uint16_t tcp_ipv6_chksum(FAR struct net_driver_s *dev) +{ + return ipv6_upperlayer_chksum(dev, IP_PROTO_TCP); +} +#endif /* CONFIG_NET_IPv6 */ +#endif /* !CONFIG_NET_ARCH_CHKSUM */ + +#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) +uint16_t tcp_chksum(FAR struct net_driver_s *dev) +{ + if (IFF_IS_IPv6(dev->d_flags)) + { + return tcp_ipv6_chksum(dev); + } + else + { + return tcp_ipv4_chksum(dev); + } +} +#endif + +#endif /* CONFIG_NET_TCP */ diff --git a/net/utils/net_udpchksum.c b/net/utils/net_udpchksum.c new file mode 100644 index 00000000000..3f3eca11abe --- /dev/null +++ b/net/utils/net_udpchksum.c @@ -0,0 +1,85 @@ +/**************************************************************************** + * net/utils/net_udpchksum.c + * + * Copyright (C) 2007-2010, 2012, 2014-2015, 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include "utils/utils.h" + +#ifdef CONFIG_NET_UDP + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: udp_ipv4_chksum + * + * Description: + * Calculate the UDP/IPv4 checksum of the packet in d_buf and d_appdata. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv4) +uint16_t udp_ipv4_chksum(FAR struct net_driver_s *dev) +{ + return ipv4_upperlayer_chksum(dev, IP_PROTO_UDP); +} +#endif + +/**************************************************************************** + * Name: udp_ipv6_chksum + * + * Description: + * Calculate the UDP/IPv6 checksum of the packet in d_buf and d_appdata. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_UDP_CHECKSUMS) && defined(CONFIG_NET_IPv6) +uint16_t udp_ipv6_chksum(FAR struct net_driver_s *dev) +{ + return ipv6_upperlayer_chksum(dev, IP_PROTO_UDP); +} +#endif + +#endif /* CONFIG_NET_UDP */ diff --git a/net/utils/utils.h b/net/utils/utils.h index 6e33b72a83b..ffeca8c3f69 100644 --- a/net/utils/utils.h +++ b/net/utils/utils.h @@ -191,6 +191,99 @@ uint8_t net_ipv6_mask2pref(FAR const uint16_t *mask); void net_ipv6_pref2mask(uint8_t preflen, net_ipv6addr_t mask); #endif +/**************************************************************************** + * Name: chksum + * + * Description: + * Calculate the raw change some over the memory region described by + * data and len. + * + * Input Parameters: + * sum - Partial calculations carried over from a previous call to chksum(). + * This should be zero on the first time that check sum is called. + * data - Beginning of the data to include in the checksum. + * len - Length of the data to include in the checksum. + * + * Returned Value: + * The updated checksum value. + * + ****************************************************************************/ + +#ifndef CONFIG_NET_ARCH_CHKSUM +uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len); +#endif + +/**************************************************************************** + * Name: net_chksum + * + * Description: + * Calculate the Internet checksum over a buffer. + * + * The Internet checksum is the one's complement of the one's complement + * sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * If CONFIG_NET_ARCH_CHKSUM is defined, then this function must be + * provided by architecture-specific logic. + * + * Input Parameters: + * + * buf - A pointer to the buffer over which the checksum is to be computed. + * + * len - The length of the buffer over which the checksum is to be computed. + * + * Returned Value: + * The Internet checksum of the buffer. + * + ****************************************************************************/ + +#ifndef CONFIG_NET_ARCH_CHKSUM +uint16_t net_chksum(FAR uint16_t *data, uint16_t len); +#endif + +/**************************************************************************** + * Name: ipv4_upperlayer_chksum + * + * Description: + * Perform the checksum calcaultion over the IPv4, protocol headers, and + * data payload as necessary. + * + * Input Parameters: + * dev - The network driver instance. The packet data is in the d_buf + * of the device. + * proto - The protocol being supported + * + * Returned Value: + * The calculated checksum + * + ****************************************************************************/ + +#if !defined(CONFIG_NET_ARCH_CHKSUM) && defined(CONFIG_NET_IPv4) +uint16_t ipv4_upperlayer_chksum(FAR struct net_driver_s *dev, uint8_t proto); +#endif + +/**************************************************************************** + * Name: ipv6_upperlayer_chksum + * + * Description: + * Perform the checksum calcaultion over the IPv6, protocol headers, and + * data payload as necessary. + * + * Input Parameters: + * dev - The network driver instance. The packet data is in the d_buf + * of the device. + * proto - The protocol being supported + * + * Returned Value: + * The calculated checksum + * + ****************************************************************************/ + +#if !defined(CONFIG_NET_ARCH_CHKSUM) && defined(CONFIG_NET_IPv6) +uint16_t ipv6_upperlayer_chksum(FAR struct net_driver_s *dev, uint8_t proto); +#endif + /**************************************************************************** * Name: tcp_chksum, tcp_ipv4_chksum, and tcp_ipv6_chksum * diff --git a/wireless/ieee802154/Kconfig b/wireless/ieee802154/Kconfig index 1b443ff9797..228779a5d44 100644 --- a/wireless/ieee802154/Kconfig +++ b/wireless/ieee802154/Kconfig @@ -10,6 +10,37 @@ config WIRELESS_IEEE802154 ---help--- Enables support for the IEEE 802.14.5 Wireless library. -if IEEE802154 +if WIRELESS_IEEE802154 -endif # IEEE802154 +config IEEE802154_LOOPBACK + bool "IEEE802154 6loWPAN Loopback" + default n + depends on NET_6LOWPAN && NET_IPv6 + select ARCH_HAVE_NETDEV_STATISTICS + ---help--- + Add support for the IEEE802154 6loWPAN Loopback test device. + +if IEEE802154_LOOPBACK + +choice + prompt "Work queue" + default IEEE802154_LOOPBACK_LPWORK if SCHED_LPWORK + default IEEE802154_LOOPBACK_HPWORK if !SCHED_LPWORK && SCHED_HPWORK + depends on SCHED_WORKQUEUE + ---help--- + Work queue support is required to use the loopback driver. If the + low priority work queue is available, then it should be used by the + loopback driver. + +config IEEE802154_LOOPBACK_HPWORK + bool "High priority" + depends on SCHED_HPWORK + +config IEEE802154_LOOPBACK_LPWORK + bool "Low priority" + depends on SCHED_LPWORK + +endchoice # Work queue +endif # IEEE802154_LOOPBACK + +endif # WIRELESS_IEEE802154 diff --git a/wireless/ieee802154/Make.defs b/wireless/ieee802154/Make.defs index 200d948dd79..2f5f19967f4 100644 --- a/wireless/ieee802154/Make.defs +++ b/wireless/ieee802154/Make.defs @@ -39,8 +39,12 @@ ifeq ($(CONFIG_WIRELESS_IEEE802154),y) # Include wireless devices build support -DEPPATH += --dep-path wireless/ieee802154 -VPATH += :wireless/ieee802154 +ifeq ($(CONFIG_IEEE802154_LOOPBACK),y) +CSRCS += mac802154_loopback.c +endif + +DEPPATH += --dep-path ieee802154 +VPATH += :ieee802154 CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)wireless$(DELIM)ieee802154} endif # CONFIG_WIRELESS_IEEE802154 diff --git a/wireless/ieee802154/mac802154_loopback.c b/wireless/ieee802154/mac802154_loopback.c new file mode 100644 index 00000000000..1f8dc4e8c38 --- /dev/null +++ b/wireless/ieee802154/mac802154_loopback.c @@ -0,0 +1,620 @@ +/**************************************************************************** + * wireless/iee802154/mac802154_loopback.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_IEEE802154_LOOPBACK + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* We need to have the work queue to handle SPI interrupts */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Worker thread support is required (CONFIG_SCHED_WORKQUEUE) +#else +# if defined(CONFIG_IEEE802154_LOOPBACK_HPWORK) +# define LPBKWORK HPWORK +# elif defined(CONFIG_IEEE802154_LOOPBACK_LPWORK) +# define LPBKWORK LPWORK +# else +# error Neither CONFIG_IEEE802154_LOOPBACK_HPWORK nor CONFIG_IEEE802154_LOOPBACK_LPWORK defined +# endif +#endif + +/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */ + +#define LO_WDDELAY (1*CLK_TCK) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The lo_driver_s encapsulates all state information for a single hardware + * interface + */ + +struct lo_driver_s +{ + bool lo_bifup; /* true:ifup false:ifdown */ + bool lo_txdone; /* One RX packet was looped back */ + WDOG_ID lo_polldog; /* TX poll timer */ + struct work_s lo_work; /* For deferring poll work to the work queue */ + + /* This holds the information visible to the NuttX network */ + + struct ieee802154_driver_s lo_ieee; /* Interface understood by the network */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct lo_driver_s g_loopback; +static uint8_t g_iobuffer[CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE]; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Polling logic */ + +static int lo_txpoll(FAR struct net_driver_s *dev); +static void lo_poll_work(FAR void *arg); +static void lo_poll_expiry(int argc, wdparm_t arg, ...); + +/* NuttX callback functions */ + +static int lo_ifup(FAR struct net_driver_s *dev); +static int lo_ifdown(FAR struct net_driver_s *dev); +static void lo_txavail_work(FAR void *arg); +static int lo_txavail(FAR struct net_driver_s *dev); +#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); +#ifdef CONFIG_NET_IGMP +static int lo_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); +#endif +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: lo_txpoll + * + * Description: + * Check if the network has any outgoing packets ready to send. This is + * a callback from devif_poll() or devif_timer(). devif_poll() will be + * called only during normal TX polling. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * the network is locked. + * + ****************************************************************************/ + +static int lo_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + FAR struct iob_s *head; + FAR struct iob_s *tail; + FAR struct iob_s *iob; + int ret; + + if (dev->d_len > 0 || priv->lo_ieee.i_framelist != NULL) + { + ninfo("d_len: %u i_framelist: %p\n", + dev->d_len, priv->lo_ieee.i_framelist); + + /* The only two valid settings are: + * + * 1. Nothing to send: + * dev->d_len == 0 && priv->lo_ieee.i_framelist == NULL + * 2. Outgoing packet has been converted to IEEE802.15.4 frames: + * dev->d_len == 0 && priv->lo_ieee.i_framelist != NULL + */ + + DEBUGASSERT(dev->d_len == 0 && priv->lo_ieee.i_framelist != NULL); + } + + /* Remove the queued IOBs from driver structure */ + + head = priv->lo_ieee.i_framelist; + + /* Find the tail of the IOB queue */ + + for (tail = NULL, iob = head; + iob != NULL; + tail = iob, iob = iob->io_flink); + + /* Loop while there frames to be sent, i.e., while the IOB list is not + * emtpy. Sending, of course, just means relaying back through the network + * for this driver. + */ + + while (!FRAME_IOB_EMPTY(&priv->lo_ieee)) + { + /* Remove the IOB from the queue */ + + FRAME_IOB_REMOVE(&priv->lo_ieee, iob); + + /* Is the queue now empty? */ + + if (FRAME_IOB_EMPTY(&priv->lo_ieee)) + { + tail = NULL; + } + + /* Return the next frame to the network */ + + iob->io_flink = NULL; + priv->lo_ieee.i_framelist = iob; + + ninfo("Send frame %p to the network. Length=%u\n", iob, iob->io_len); + ret = sixlowpan_input(&priv->lo_ieee); + if (ret < 0) + { + nerr("ERROR: sixlowpan_input returned %d\n", ret); + } + + /* What if the network responds with more frames to send? */ + + if (priv->lo_ieee.i_framelist != NULL) + { + /* Append the new list to the tail of the queue */ + + iob = priv->lo_ieee.i_framelist; + priv->lo_ieee.i_framelist = NULL; + + if (tail == NULL) + { + head = iob; + } + else + { + tail->io_flink = iob; + } + + /* Find the new tail of the IOB queue */ + + for (tail = iob, iob = iob->io_flink; + iob != NULL; + tail = iob, iob = iob->io_flink); + } + + priv->lo_txdone = true; + } + + return 0; +} + +/**************************************************************************** + * Function: lo_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +static void lo_poll_work(FAR void *arg) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg; + + /* Perform the poll */ + + net_lock(); + priv->lo_txdone = false; + (void)devif_timer(&priv->lo_ieee.i_dev, lo_txpoll); + + /* Was something received and looped back? */ + + while (priv->lo_txdone) + { + /* Yes, poll again for more TX data */ + + priv->lo_txdone = false; + (void)devif_poll(&priv->lo_ieee.i_dev, lo_txpoll); + } + + /* Setup the watchdog poll timer again */ + + (void)wd_start(priv->lo_polldog, LO_WDDELAY, lo_poll_expiry, 1, priv); + net_unlock(); +} + +/**************************************************************************** + * Function: lo_poll_expiry + * + * Description: + * Periodic timer handler. Called from the timer interrupt handler. + * + * Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void lo_poll_expiry(int argc, wdparm_t arg, ...) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg; + + if (!work_available(&priv->lo_work)) + { + nwarn("WARNING: lo_work NOT available\n"); + } + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(LPBKWORK, &priv->lo_work, lo_poll_work, priv, 0); +} + +/**************************************************************************** + * Function: lo_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int lo_ifup(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + + ninfo("Bringing up: IPv6 %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], + dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], + dev->d_ipv6addr[6], dev->d_ipv6addr[7]); + +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + ninfo(" Node: %02x:%02x PANID=%04x\n", + priv->lo_ieee.i_nodeaddr.u8[0], priv->lo_ieee.i_nodeaddr.u8[1], + priv->lo_ieee.i_panid); +#else /* CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 8 */ + ninfo(" Node: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x PANID=%04x\n", + priv->lo_ieee.i_nodeaddr.u8[0], priv->lo_ieee.i_nodeaddr.u8[1], + priv->lo_ieee.i_nodeaddr.u8[2], priv->lo_ieee.i_nodeaddr.u8[3], + priv->lo_ieee.i_nodeaddr.u8[4], priv->lo_ieee.i_nodeaddr.u8[5], + priv->lo_ieee.i_nodeaddr.u8[6], priv->lo_ieee.i_nodeaddr.u8[7], + priv->lo_ieee.i_panid); +#endif + + /* Set and activate a timer process */ + + (void)wd_start(priv->lo_polldog, LO_WDDELAY, lo_poll_expiry, + 1, (wdparm_t)priv); + + priv->lo_bifup = true; + return OK; +} + +/**************************************************************************** + * Function: lo_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int lo_ifdown(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + + ninfo("IP up: %u\n", priv->lo_bifup); + + /* Cancel the TX poll timer and TX timeout timers */ + + wd_cancel(priv->lo_polldog); + + /* Mark the device "down" */ + + priv->lo_bifup = false; + return OK; +} + +/**************************************************************************** + * Function: lo_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void lo_txavail_work(FAR void *arg) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg; + + ninfo("IP up: %u\n", priv->lo_bifup); + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->lo_bifup) + { + do + { + /* If so, then poll the network for new XMIT data */ + + priv->lo_txdone = false; + (void)devif_poll(&priv->lo_ieee.i_dev, lo_txpoll); + } + while (priv->lo_txdone); + } + + net_unlock(); +} + +/**************************************************************************** + * Function: lo_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static int lo_txavail(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + + ninfo("Available: %u\n", work_available(&priv->lo_work)); + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->lo_work)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(LPBKWORK, &priv->lo_work, lo_txavail_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Function: lo_addmac + * + * Description: + * NuttX Callback: Add the specified MAC address to the hardware multicast + * address filtering + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be added + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) +{ +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + ninfo("MAC: %02x:%02x\n", + mac[0], mac[1]); +#else /* CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 8 */ + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]); +#endif + + /* There is no multicast support in the loopback driver */ + + return OK; +} +#endif + +/**************************************************************************** + * Function: lo_rmmac + * + * Description: + * NuttX Callback: Remove the specified MAC address from the hardware multicast + * address filtering + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be removed + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IGMP +static int lo_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) +{ +#if CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 2 + ninfo("MAC: %02x:%02x\n", + mac[0], mac[1]); +#else /* CONFIG_NET_6LOWPAN_RIMEADDR_SIZE == 8 */ + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]); +#endif + + /* There is no multicast support in the loopback driver */ + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: ieee8021514_loopback + * + * Description: + * Initialize and register the Ieee802.15.4 MAC loopback network driver. + * + * Parameters: + * None + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +int ieee8021514_loopback(void) +{ + FAR struct lo_driver_s *priv; + FAR struct net_driver_s *dev; + + ninfo("Initializing\n"); + + /* Get the interface structure associated with this interface number. */ + + priv = &g_loopback; + + /* Initialize the driver structure */ + + memset(priv, 0, sizeof(struct lo_driver_s)); + + dev = &priv->lo_ieee.i_dev; + dev->d_ifup = lo_ifup; /* I/F up (new IP address) callback */ + dev->d_ifdown = lo_ifdown; /* I/F down callback */ + dev->d_txavail = lo_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_IGMP + dev->d_addmac = lo_addmac; /* Add multicast MAC address */ + dev->d_rmmac = lo_rmmac; /* Remove multicast MAC address */ +#endif + dev->d_buf = g_iobuffer; /* Attach the IO buffer */ + dev->d_private = (FAR void *)priv; /* Used to recover private state from dev */ + + /* Create a watchdog for timing polling for and timing of transmissions */ + + priv->lo_polldog = wd_create(); /* Create periodic poll timer */ + + /* Register the loopabck device with the OS so that socket IOCTLs can b + * performed. + */ + + (void)netdev_register(&priv->lo_ieee.i_dev, NET_LL_IEEE802154); + + /* Put the network in the UP state */ + + dev->d_flags = IFF_UP; + return lo_ifup(&priv->lo_ieee.i_dev); +} + +#endif /* CONFIG_IEEE802154_LOOPBACK */