[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\n# BasedOnStyle:  Google\nAccessModifierOffset: -1\nAlignAfterOpenBracket: Align\nAlignArrayOfStructures: None\nAlignConsecutiveMacros: true\nAlignConsecutiveAssignments: true\nAlignConsecutiveBitFields: true\nAlignConsecutiveDeclarations: true\nAlignEscapedNewlines: Left\nAlignOperands:   Align\nAlignTrailingComments: true\nAllowAllArgumentsOnNextLine: true\nAllowAllConstructorInitializersOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortEnumsOnASingleLine: true\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: All\nAllowShortLambdasOnASingleLine: All\nAllowShortIfStatementsOnASingleLine: WithoutElse\nAllowShortLoopsOnASingleLine: true\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: true\nAlwaysBreakTemplateDeclarations: Yes\nAttributeMacros:\n  - __capability\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:\n  AfterCaseLabel:  false\n  AfterClass:      false\n  AfterControlStatement: Never\n  AfterEnum:       false\n  AfterFunction:   false\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     false\n  AfterUnion:      false\n  AfterExternBlock: false\n  BeforeCatch:     false\n  BeforeElse:      false\n  BeforeLambdaBody: false\n  BeforeWhile:     false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeConceptDeclarations: true\nBreakBeforeBraces: Attach\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     160\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDeriveLineEnding: true\nDerivePointerAlignment: true\nDisableFormat:   false\nEmptyLineAfterAccessModifier: Never\nEmptyLineBeforeAccessModifier: LogicalBlock\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nForEachMacros:\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\nIfMacros:\n  - KJ_IF_MAYBE\nIncludeBlocks:   Regroup\nIncludeCategories:\n  - Regex:           '^<ext/.*\\.h>'\n    Priority:        2\n    SortPriority:    0\n    CaseSensitive:   false\n  - Regex:           '^<.*\\.h>'\n    Priority:        1\n    SortPriority:    0\n    CaseSensitive:   false\n  - Regex:           '^<.*'\n    Priority:        2\n    SortPriority:    0\n    CaseSensitive:   false\n  - Regex:           '.*'\n    Priority:        3\n    SortPriority:    0\n    CaseSensitive:   false\nIncludeIsMainRegex: '([-_](test|unittest))?$'\nIncludeIsMainSourceRegex: ''\nIndentAccessModifiers: false\nIndentCaseLabels: true\nIndentCaseBlocks: true\nIndentGotoLabels: true\nIndentPPDirectives: None\nIndentExternBlock: AfterExternBlock\nIndentRequires:  false\nIndentWidth:     4\nIndentWrappedFunctionNames: false\nInsertTrailingCommas: None\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nLambdaBodyIndentation: Signature\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Never\nObjCBlockIndentWidth: 2\nObjCBreakBeforeNestedBlockParam: true\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: true\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 1\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 200\nPenaltyIndentedWhitespace: 0\nPointerAlignment: Left\nPPIndentWidth:   -1\nRawStringFormats:\n  - Language:        Cpp\n    Delimiters:\n      - cc\n      - CC\n      - cpp\n      - Cpp\n      - CPP\n      - 'c++'\n      - 'C++'\n    CanonicalDelimiter: ''\n    BasedOnStyle:    google\n  - Language:        TextProto\n    Delimiters:\n      - pb\n      - PB\n      - proto\n      - PROTO\n    EnclosingFunctions:\n      - EqualsProto\n      - EquivToProto\n      - PARSE_PARTIAL_TEXT_PROTO\n      - PARSE_TEST_PROTO\n      - PARSE_TEXT_PROTO\n      - ParseTextOrDie\n      - ParseTextProtoOrDie\n      - ParseTestProto\n      - ParsePartialTestProto\n    CanonicalDelimiter: pb\n    BasedOnStyle:    google\nReferenceAlignment: Pointer\nReflowComments:  true\nShortNamespaceLines: 1\nSortIncludes:    CaseSensitive\nSortJavaStaticImport: Before\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: true\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCaseColon: false\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceAroundPointerQualifiers: Default\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyBlock: false\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 2\nSpacesInAngles:  Never\nSpacesInConditionalStatement: false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInLineCommentPrefix:\n  Minimum:         1\n  Maximum:         -1\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nSpaceBeforeSquareBrackets: false\nBitFieldColonSpacing: Both\nStandard:        Auto\nStatementAttributeLikeMacros:\n  - Q_EMIT\nStatementMacros:\n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\nTabWidth:        8\nUseCRLF:         false\nUseTab:          Never\nWhitespaceSensitiveMacros:\n  - STRINGIZE\n  - PP_STRINGIZE\n  - BOOST_PP_STRINGIZE\n  - NS_SWIFT_NAME\n  - CF_SWIFT_NAME\n...\n\n"
  },
  {
    "path": ".gitignore",
    "content": "build\nrelease\ngenerated\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.12)\n\nmessage(\"Build type: \\\"${CMAKE_BUILD_TYPE}\\\"\")\n\n# Project name\nset(NAME i2c_adapter)\n\n# Board type\nset(PICO_BOARD none)\n\n# Fixes that allow some MCH2022 badges with a slowly starting oscillator to boot properly\nadd_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1 PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)\n\n# SDK\ninclude($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake)\n\nproject(${NAME} C CXX ASM)\nset(CMAKE_C_STANDARD 11)\nset(CMAKE_CXX_STANDARD 17)\n\nif (PICO_SDK_VERSION_STRING VERSION_LESS \"1.3.0\")\n    message(FATAL_ERROR \"Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}\")\nendif()\n\npico_sdk_init()\n\n# Firmware\nadd_executable(${NAME}\n    main.c\n    usb_descriptors.c\n)\n\ntarget_include_directories(${NAME} PUBLIC\n        ${CMAKE_CURRENT_LIST_DIR})\n\ntarget_link_libraries(${NAME}\n    pico_stdlib\n    pico_unique_id\n    hardware_watchdog\n    hardware_flash\n    hardware_uart\n    hardware_pio\n    hardware_pwm\n    hardware_adc\n    hardware_i2c\n    tinyusb_device\n    tinyusb_board\n    cmsis_core\n)\n\npico_add_extra_outputs(${NAME})\n"
  },
  {
    "path": "LICENSE_MIT",
    "content": "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "# Copyright (c) 2022 Nicolai Electronics\n# SPDX-License-Identifier: MIT\n\nINSTALL_PREFIX := $PWD\nBUILD_DIR := build\nGENERATED_DIR := generated\n\n.PHONY: all firmware flash clean install_rules $(BUILD_DIR) format\n\nall: build flash\n\t@echo \"All tasks completed\"\n\nbuild:\n\tmkdir -p $(BUILD_DIR)\n\tmkdir -p $(GENERATED_DIR)\n\tcd $(BUILD_DIR); cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release ..\n\t$(MAKE) -C $(BUILD_DIR) --no-print-directory all\n\ndebug:\n\tmkdir -p $(BUILD_DIR)\n\tmkdir -p $(GENERATED_DIR)\n\tcd $(BUILD_DIR); cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Debug ..\n\t$(MAKE) -C $(BUILD_DIR) --no-print-directory all\n\nflash:\n\tpicotool load $(BUILD_DIR)/i2c_adapter.bin\n\tpicotool reboot\n\nclean:\n\trm -rf $(BUILD_DIR)\n\trm -rf $(GENERATED_DIR)\n\ninstall_rules:\n\tcp tools/99-pico.rules /etc/udev/rules.d/\n\t@echo \"reload rules with:\"\n\t@echo \"\\tudevadm control --reload-rules\"\n\t@echo \"\\tudevadm trigger\"\n\nformat:\n\tfind . -iname '*.h' -o -iname '*.c' -o -iname '*.cpp' | grep -v '$(BUILD_DIR)' | xargs clang-format -i\n"
  },
  {
    "path": "README.md",
    "content": "# I2C interface\n\nThis RP2040 firmware implements the USB protocol expected by the I2C-Tiny-USB kernel driver, allowing the use of a Raspberry Pi Pico as USB to I2C adapter.\n\nThe original I2C-Tiny-USB project can be found at https://github.com/harbaum/I2C-Tiny-USB, this firmware is a complete re-implementation of the firmware for use with the RP2040.\n\nMore testing is needed to verify that the firmware works correctly, this project currently has proof of concept status.\n\n## Pinout\n\n* SDA (i2c data): GPIO 2\n* SCL (i2c clock): GPIO 3\n"
  },
  {
    "path": "example/400kHz.sh",
    "content": "#!/usr/bin/env bash\n\n# The kernel module accepts a delay in us as a parameter\n# the default value of 10us converts to a clock speed of 1000000 / 10 = 100 kHz\n# the RP2040 supports 400kHz too, setting a delay of 2 us results in\n# 1000000 / 2 = 500 kHz but the firmware automatically limits the speed to 400 kHz\n#\n# This script disables the kernel module and then enables it again in 400kHz mode.\n\nrmmod i2c-tiny-usb\nmodprobe i2c-tiny-usb delay=2\n"
  },
  {
    "path": "example/README.md",
    "content": "# Example\n\nA simple demonstration showing how to control an OLED display (SSD1306) via I2C.\n\n## Contents\n\n* 400khz.sh - Run as root, this script switches I2C speed from 100kHz to 400kHz\n* disable_kde_compositor.sh - Run as your user, this script disables the compositor function of the KDE desktop, which causes problems when capturing the screen with FFMPEG\n* run.sh - Run as root, this script starts FFMPEG to record the screen, piping the raw video data to the Python script, run this script to start the demonstration\n* ssd1306.py - A very simple and ugly SSD1306 OLED driver in Python using the smbus python library\n\n\n![photo](photo.jpg)\n\n[![Demonstration video](http://img.youtube.com/vi/PMtY5OU9V3Q/0.jpg)](http://www.youtube.com/watch?v=PMtY5OU9V3Q \"Demonstration video\")\n\n\n## Linux tools\n\nYou can easily list and access the I2C busses of your computer, including the bus exposed by this project by loading the i2c-dev kernel module. Please do watch out: some devices on internal I2C busses  inside your computer may malfunction or cause damage when probing busses or performing read/write operations using these tools.\n\nYou can find the device number of the RP2040 I2C adapter by running the dmesg command right after plugging in the device. You are looking for the following line of information:\n\n```\n$ sudo dmesg\n...\ni2c i2c-1: connected i2c-tiny-usb device\n```\n\nIn this example the I2C bus number assigned to the RP2040 I2C adapter is 1. This means the device file is `/dev/i2c-1` and the sysfs interface is `/sys/bus/i2c/devices/i2c-1`.\n\nTo enable access to the I2C busses of the computer via the device file the i2c-dev kernel module needs to be loaded.\n\n```\nsudo modprobe i2c-dev\n```\n\nBusses can then be probed using i2cdetect:\n\n```\nsudo i2cdetect -y 1\n```\n\nIn which the number (1) is the bus number. To scan all I2C addresses add the `-a` flag before the bus number.\n\nTo read a value from a register in an I2C device the i2cget command can be used:\n\n```\nsudo i2cget -y 1 0x28 0x00\n```\n\nIn which 1 is the bus number, 0x28 is the I2C device address and 0x00 is the register number.\n\nTo write a value to a register in an I2C device the i2cset command can be used:\n\n```\nsudo i2cset -y 1 0x28 0x00 0x42\n```\n\nIn wich 1 is the bus number, 0x28 is the I2C device address, 0x00 is the register number and 0x42 is the value written to the register.\n"
  },
  {
    "path": "example/disable_kde_compositor.sh",
    "content": "#!/usr/bin/env bash\n\n# This script temporarily disables the compositor function of the KDE plasma desktop as it causes glitches when recording the screen with ffmpeg\n\nqdbus org.kde.KWin /Compositor suspend\n"
  },
  {
    "path": "example/run.sh",
    "content": "#!/usr/bin/env bash\nffmpeg -framerate 16 -f x11grab -video_size 1920x1080 -i :0+0,0 -f image2pipe -vf scale=128x64 -pix_fmt monob -vcodec rawvideo - | python -u ssd1306.py\n"
  },
  {
    "path": "example/ssd1306.py",
    "content": "import smbus, sys, time\nbus = smbus.SMBus(1)\n\ndef init():\n    bus.write_byte_data(0x3c, 0x00, 0xae)\n    bus.write_byte_data(0x3c, 0x00, 0xd5)\n    bus.write_byte_data(0x3c, 0x00, 0x80)\n    bus.write_byte_data(0x3c, 0x00, 0xa8)\n    bus.write_byte_data(0x3c, 0x00, 0x3f)\n    bus.write_byte_data(0x3c, 0x00, 0xd3)\n    bus.write_byte_data(0x3c, 0x00, 0x00)\n    bus.write_byte_data(0x3c, 0x00, 0x40)\n    bus.write_byte_data(0x3c, 0x00, 0x20)\n    bus.write_byte_data(0x3c, 0x00, 0x00)\n    bus.write_byte_data(0x3c, 0x00, 0xa1)\n    bus.write_byte_data(0x3c, 0x00, 0xc8)\n    bus.write_byte_data(0x3c, 0x00, 0xda)\n    bus.write_byte_data(0x3c, 0x00, 0x12)\n    bus.write_byte_data(0x3c, 0x00, 0x81)\n    bus.write_byte_data(0x3c, 0x00, 0xcf)\n    bus.write_byte_data(0x3c, 0x00, 0xd9)\n    bus.write_byte_data(0x3c, 0x00, 0xf1)\n    bus.write_byte_data(0x3c, 0x00, 0xdb)\n    bus.write_byte_data(0x3c, 0x00, 0x30)\n    bus.write_byte_data(0x3c, 0x00, 0x8d)\n    bus.write_byte_data(0x3c, 0x00, 0x14)\n    bus.write_byte_data(0x3c, 0x00, 0x2e)\n    bus.write_byte_data(0x3c, 0x00, 0xa4)\n    bus.write_byte_data(0x3c, 0x00, 0xa6)\n    bus.write_byte_data(0x3c, 0x00, 0xaf)\n\ndef position(x0 = 0, x1 = 127, y0 = 0, y1 = 7):\n    bus.write_byte_data(0x3c, 0x00, 0x21)\n    bus.write_byte_data(0x3c, 0x00, x0)\n    bus.write_byte_data(0x3c, 0x00, x1)\n    bus.write_byte_data(0x3c, 0x00, 0x22)\n    bus.write_byte_data(0x3c, 0x00, y0)\n    bus.write_byte_data(0x3c, 0x00, y1)\n\ninit()\nposition()\n\ndef convert(data_in):\n    data_out = [0x00] * 128 * 8\n    for x in range(128):\n        for y in range(64):\n            in_addr = x // 8 + (y * 128//8)\n            in_bit = 7 - x % 8\n            out_addr = x + ((y // 8)*128)\n            out_bit = y % 8\n            if data_in[in_addr] & 1 << in_bit:\n                data_out[out_addr] |= 1 << out_bit\n    return data_out\n\nfor x in range(64):\n    bus.write_i2c_block_data(0x3c, 0x40, [0x00] * 16)\n\nwhile True:\n    data_in = list(sys.stdin.buffer.read(128*64//8))\n    data_out = convert(data_in)\n    for position in range(128*8//32):\n        out = data_out[position*32:(position*32)+32]\n        bus.write_i2c_block_data(0x3c, 0x40, out)\n"
  },
  {
    "path": "kernel_i2c_flags.h",
    "content": "#pragma once\n\n/* linux kernel flags */\n#define I2C_M_TEN          0x10 /* we have a ten bit chip address */\n#define I2C_M_RD           0x01\n#define I2C_M_NOSTART      0x4000\n#define I2C_M_REV_DIR_ADDR 0x2000\n#define I2C_M_IGNORE_NAK   0x1000\n#define I2C_M_NO_RD_ACK    0x0800\n\n/* To determine what functionality is present */\n#define I2C_FUNC_I2C                        0x00000001\n#define I2C_FUNC_10BIT_ADDR                 0x00000002\n#define I2C_FUNC_PROTOCOL_MANGLING          0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */\n#define I2C_FUNC_SMBUS_HWPEC_CALC           0x00000008 /* SMBus 2.0 */\n#define I2C_FUNC_SMBUS_READ_WORD_DATA_PEC   0x00000800 /* SMBus 2.0 */\n#define I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC  0x00001000 /* SMBus 2.0 */\n#define I2C_FUNC_SMBUS_PROC_CALL_PEC        0x00002000 /* SMBus 2.0 */\n#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC  0x00004000 /* SMBus 2.0 */\n#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL      0x00008000 /* SMBus 2.0 */\n#define I2C_FUNC_SMBUS_QUICK                0x00010000\n#define I2C_FUNC_SMBUS_READ_BYTE            0x00020000\n#define I2C_FUNC_SMBUS_WRITE_BYTE           0x00040000\n#define I2C_FUNC_SMBUS_READ_BYTE_DATA       0x00080000\n#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA      0x00100000\n#define I2C_FUNC_SMBUS_READ_WORD_DATA       0x00200000\n#define I2C_FUNC_SMBUS_WRITE_WORD_DATA      0x00400000\n#define I2C_FUNC_SMBUS_PROC_CALL            0x00800000\n#define I2C_FUNC_SMBUS_READ_BLOCK_DATA      0x01000000\n#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA     0x02000000\n#define I2C_FUNC_SMBUS_READ_I2C_BLOCK       0x04000000 /* I2C-like block xfer  */\n#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK      0x08000000 /* w/ 1-byte reg. addr. */\n#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2     0x10000000 /* I2C-like block xfer  */\n#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2    0x20000000 /* w/ 2-byte reg. addr. */\n#define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC  0x40000000 /* SMBus 2.0 */\n#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC 0x80000000 /* SMBus 2.0 */\n\n#define I2C_FUNC_SMBUS_BYTE       I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE\n#define I2C_FUNC_SMBUS_BYTE_DATA  I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA\n#define I2C_FUNC_SMBUS_WORD_DATA  I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA\n#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA\n#define I2C_FUNC_SMBUS_I2C_BLOCK  I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK\n\n#define I2C_FUNC_SMBUS_EMUL                                                                                                       \\\n    I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | \\\n        I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | I2C_FUNC_SMBUS_I2C_BLOCK\n"
  },
  {
    "path": "main.c",
    "content": "/**\n * Copyright (c) 2022 Nicolai Electronics\n *\n * SPDX-License-Identifier: MIT\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"bsp/board.h\"\n#include \"hardware/adc.h\"\n#include \"hardware/i2c.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/pwm.h\"\n#include \"hardware/structs/watchdog.h\"\n#include \"hardware/uart.h\"\n#include \"hardware/watchdog.h\"\n#include \"kernel_i2c_flags.h\"\n#include \"pico/bootrom.h\"\n#include \"pico/stdlib.h\"\n#include \"tusb.h\"\n#include \"usb_descriptors.h\"\n\n#define I2C_INST i2c1\n#define I2C_SDA  2\n#define I2C_SCL  3\n\nint main(void) {\n    board_init();\n    tusb_init();\n\n    gpio_init(I2C_SDA);\n    gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);\n    gpio_pull_up(I2C_SDA);\n\n    gpio_init(I2C_SCL);\n    gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);\n    gpio_pull_up(I2C_SCL);\n\n    i2c_init(I2C_INST, 100000);\n\n    while (1) {\n        tud_task();\n    }\n\n    return 0;\n}\n\n// Invoked when device is mounted\nvoid tud_mount_cb(void) {}\n\n// Invoked when device is unmounted\nvoid tud_umount_cb(void) {}\n\n// Invoked when usb bus is suspended\nvoid tud_suspend_cb(bool remote_wakeup_en) {}\n\n// Invoked when usb bus is resumed\nvoid tud_resume_cb(void) {}\n\n/* commands from USB, must e.g. match command ids in kernel driver */\n#define CMD_ECHO       0\n#define CMD_GET_FUNC   1\n#define CMD_SET_DELAY  2\n#define CMD_GET_STATUS 3\n#define CMD_I2C_IO     4\n#define CMD_I2C_BEGIN  1  // flag fo I2C_IO\n#define CMD_I2C_END    2  // flag fo I2C_IO\n\nconst unsigned long i2c_func = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;\n\n#define STATUS_IDLE        0\n#define STATUS_ADDRESS_ACK 1\n#define STATUS_ADDRESS_NAK 2\n\nstatic uint8_t i2c_state = STATUS_IDLE;\n\nuint8_t i2c_data[1024] = {0};\n\n/*uint8_t buffer[256] = {0};\nvoid debug_print(const char* buffer) {\n    tud_cdc_n_write(0, buffer, strlen(buffer));\n    tud_cdc_n_write_flush(0);\n}*/\n\nbool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {\n    if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) {\n        switch (request->bRequest) {\n            case CMD_ECHO:\n                if (stage != CONTROL_STAGE_SETUP) return true;\n                return tud_control_xfer(rhport, request, (void*) &request->wValue, sizeof(request->wValue));\n            case CMD_GET_FUNC:\n                if (stage != CONTROL_STAGE_SETUP) return true;\n                return tud_control_xfer(rhport, request, (void*) &i2c_func, sizeof(i2c_func));\n                break;\n            case CMD_SET_DELAY:\n                if (stage != CONTROL_STAGE_SETUP) return true;\n                if (request->wValue == 0) {\n                    i2c_set_baudrate(I2C_INST, 100000);  // Use default: 100kHz\n                } else {\n                    int baudrate = 1000000 / request->wValue;\n                    if (baudrate > 400000) baudrate = 400000;  // Limit to 400kHz\n                    i2c_set_baudrate(I2C_INST, baudrate);\n                }\n                return tud_control_status(rhport, request);\n            case CMD_GET_STATUS:\n                if (stage != CONTROL_STAGE_SETUP) return true;\n                return tud_control_xfer(rhport, request, (void*) &i2c_state, sizeof(i2c_state));\n            case CMD_I2C_IO:\n            case CMD_I2C_IO + CMD_I2C_BEGIN:\n            case CMD_I2C_IO + CMD_I2C_END:\n            case CMD_I2C_IO + CMD_I2C_BEGIN + CMD_I2C_END:\n                {\n                    if (stage != CONTROL_STAGE_SETUP && stage != CONTROL_STAGE_DATA) return true;\n                    bool nostop = !(request->bRequest & CMD_I2C_END);\n\n                    //sprintf(buffer, \"%s i2c %s at 0x%02x, len = %d, nostop = %d\\r\\n\", (stage != CONTROL_STAGE_SETUP) ? \"[D]\" : \"[S]\", (request->wValue & I2C_M_RD)?\"rd\":\"wr\", request->wIndex, request->wLength, nostop);\n                    //debug_print(buffer);\n\n                    if (request->wLength > sizeof(i2c_data)) {\n                        return false;  // Prevent buffer overflow in case host sends us an impossible request\n                    }\n\n                    if (stage == CONTROL_STAGE_SETUP) {  // Before transfering data\n                        if (request->wValue & I2C_M_RD) {\n                            // Reading from I2C device\n                            int res = i2c_read_blocking(I2C_INST, request->wIndex, i2c_data, request->wLength, nostop);\n                            if (res == PICO_ERROR_GENERIC) {\n                                i2c_state = STATUS_ADDRESS_NAK;\n                            } else {\n                                i2c_state = STATUS_ADDRESS_ACK;\n                            }\n                        } else if (request->wLength == 0) {  // Writing with length of 0, this is used for bus scanning, do dummy read\n                            uint8_t dummy = 0x00;\n                            int     res   = i2c_read_blocking(I2C_INST, request->wIndex, (void*) &dummy, 1, nostop);\n                            if (res == PICO_ERROR_GENERIC) {\n                                i2c_state = STATUS_ADDRESS_NAK;\n                            } else {\n                                i2c_state = STATUS_ADDRESS_ACK;\n                            }\n                        }\n                        tud_control_xfer(rhport, request, (void*) i2c_data, request->wLength);\n                    }\n\n                    if (stage == CONTROL_STAGE_DATA) {        // After transfering data\n                        if (!(request->wValue & I2C_M_RD)) {  // I2C write operation\n                            int res = i2c_write_blocking(I2C_INST, request->wIndex, i2c_data, request->wLength, nostop);\n                            if (res == PICO_ERROR_GENERIC) {\n                                i2c_state = STATUS_ADDRESS_NAK;\n                            } else {\n                                i2c_state = STATUS_ADDRESS_ACK;\n                            }\n                        }\n                    }\n\n                    return true;\n                }\n            default:\n                if (stage != CONTROL_STAGE_SETUP) return true;\n                break;\n        }\n    } else {\n        if (stage != CONTROL_STAGE_SETUP) return true;\n    }\n\n    return false;  // stall unknown request\n}\n\nbool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_request_t const* request) {\n    (void) rhport;\n    (void) request;\n    return true;\n}\n"
  },
  {
    "path": "pico_sdk_import.cmake",
    "content": "# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake\n\n# This can be dropped into an external project to help locate this SDK\n# It should be include()ed prior to project()\n\nif (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))\n    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})\n    message(\"Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))\n    set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))\n    set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')\")\nendif ()\n\nset(PICO_SDK_PATH \"${PICO_SDK_PATH}\" CACHE PATH \"Path to the Raspberry Pi Pico SDK\")\nset(PICO_SDK_FETCH_FROM_GIT \"${PICO_SDK_FETCH_FROM_GIT}\" CACHE BOOL \"Set to ON to fetch copy of SDK from git if not otherwise locatable\")\nset(PICO_SDK_FETCH_FROM_GIT_PATH \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" CACHE FILEPATH \"location to download SDK\")\n\nif (NOT PICO_SDK_PATH)\n    if (PICO_SDK_FETCH_FROM_GIT)\n        include(FetchContent)\n        set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})\n        if (PICO_SDK_FETCH_FROM_GIT_PATH)\n            get_filename_component(FETCHCONTENT_BASE_DIR \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" REALPATH BASE_DIR \"${CMAKE_SOURCE_DIR}\")\n        endif ()\n        FetchContent_Declare(\n                pico_sdk\n                GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n                GIT_TAG master\n        )\n        if (NOT pico_sdk)\n            message(\"Downloading Raspberry Pi Pico SDK\")\n            FetchContent_Populate(pico_sdk)\n            set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})\n        endif ()\n        set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})\n    else ()\n        message(FATAL_ERROR\n                \"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git.\"\n                )\n    endif ()\nendif ()\n\nget_filename_component(PICO_SDK_PATH \"${PICO_SDK_PATH}\" REALPATH BASE_DIR \"${CMAKE_BINARY_DIR}\")\nif (NOT EXISTS ${PICO_SDK_PATH})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' not found\")\nendif ()\n\nset(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)\nif (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK\")\nendif ()\n\nset(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH \"Path to the Raspberry Pi Pico SDK\" FORCE)\n\ninclude(${PICO_SDK_INIT_CMAKE_FILE})\n"
  },
  {
    "path": "tools/99-pico.rules",
    "content": "# /etc/udev/rules.d/99-pico.rules\n\nSUBSYSTEM==\"usb\", ATTRS{idVendor}==\"2e8a\", ATTRS{idProduct}==\"0003\", MODE=\"0666\"\nSUBSYSTEM==\"tty\", ATTRS{idVendor}==\"2e8a\", ATTRS{idProduct}==\"0005\", SYMLINK+=\"pico\"\n"
  },
  {
    "path": "tusb_config.h",
    "content": "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2019 Ha Thach (tinyusb.org)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n */\n\n#ifndef _TUSB_CONFIG_H_\n#define _TUSB_CONFIG_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n//--------------------------------------------------------------------\n// COMMON CONFIGURATION\n//--------------------------------------------------------------------\n\n// defined by board.mk\n#ifndef CFG_TUSB_MCU\n#error CFG_TUSB_MCU must be defined\n#endif\n\n// RHPort number used for device can be defined by board.mk, default to port 0\n#ifndef BOARD_DEVICE_RHPORT_NUM\n#define BOARD_DEVICE_RHPORT_NUM 0\n#endif\n\n// RHPort max operational speed can defined by board.mk\n// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed\n#ifndef BOARD_DEVICE_RHPORT_SPEED\n#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 || \\\n     CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)\n#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED\n#else\n#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED\n#endif\n#endif\n\n// Device mode with rhport and speed defined by board.mk\n#if BOARD_DEVICE_RHPORT_NUM == 0\n#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)\n#elif BOARD_DEVICE_RHPORT_NUM == 1\n#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)\n#else\n#error \"Incorrect RHPort configuration\"\n#endif\n\n#ifndef CFG_TUSB_OS\n#define CFG_TUSB_OS OPT_OS_NONE\n#endif\n\n// CFG_TUSB_DEBUG is defined by compiler in DEBUG build\n// #define CFG_TUSB_DEBUG           0\n\n/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.\n * Tinyusb use follows macros to declare transferring memory so that they can be put\n * into those specific section.\n * e.g\n * - CFG_TUSB_MEM SECTION : __attribute__ (( section(\".usb_ram\") ))\n * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))\n */\n#ifndef CFG_TUSB_MEM_SECTION\n#define CFG_TUSB_MEM_SECTION\n#endif\n\n#ifndef CFG_TUSB_MEM_ALIGN\n#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))\n#endif\n\n//--------------------------------------------------------------------\n// DEVICE CONFIGURATION\n//--------------------------------------------------------------------\n\n#ifndef CFG_TUD_ENDPOINT0_SIZE\n#define CFG_TUD_ENDPOINT0_SIZE 64\n#endif\n\n//------------- CLASS -------------//\n#define CFG_TUD_HID    0\n#define CFG_TUD_CDC    0\n#define CFG_TUD_MSC    0\n#define CFG_TUD_MIDI   0\n#define CFG_TUD_VENDOR 1\n\n#define CFG_TUD_CDC_RX_BUFSIZE 256\n#define CFG_TUD_CDC_TX_BUFSIZE 256\n\n#define CFG_TUD_VENDOR_EPSIZE     32\n#define CFG_TUD_VENDOR_EP_BUFSIZE 512\n#define CFG_TUD_VENDOR_RX_BUFSIZE 512\n#define CFG_TUD_VENDOR_TX_BUFSIZE 512\n\n// HID buffer size Should be sufficient to hold ID (if any) + Data\n#define CFG_TUD_HID_EP_BUFSIZE 16\n\n// MSC Buffer size of Device Mass storage\n#define CFG_TUD_MSC_EP_BUFSIZE 512\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _TUSB_CONFIG_H_ */\n"
  },
  {
    "path": "usb_descriptors.c",
    "content": "/**\n * Copyright (c) 2022 Nicolai Electronics\n * Copyright (c) 2019 Ha Thach (tinyusb.org)\n *\n * SPDX-License-Identifier: MIT\n */\n\n#include \"usb_descriptors.h\"\n\n#include \"bsp/board.h\"\n#include \"pico/unique_id.h\"\n#include \"tusb.h\"\n\n// String descriptors\n\nchar const* string_desc_arr[] = {\n    (const char[]){0x09, 0x04},  // 0: is supported language is English (0x0409)\n    \"Nicolai Electronics\",       // 1: Manufacturer\n    \"I2C adapter\",               // 2: Product\n    \"I2C interface\",             // 3: I2C (vendor) interface\n    //\"Debug\"                      // 4: Debug (cdc) interface\n};\n\nenum {\n    STRING_DESC = 0,\n    STRING_DESC_MANUFACTURER,\n    STRING_DESC_PRODUCT,\n    STRING_DESC_VENDOR_0,\n    // STRING_DESC_CDC_0,\n    STRING_DESC_SERIAL  // (Not in the string description array)\n};\n\nstatic uint16_t _desc_str[32];\n\nuint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {\n    (void) langid;\n    uint8_t chr_count;\n    if (index == STRING_DESC) {\n        memcpy(&_desc_str[1], string_desc_arr[0], 2);\n        chr_count = 1;\n    } else if (index == STRING_DESC_SERIAL) {\n        pico_unique_board_id_t id;\n        pico_get_unique_board_id(&id);\n        const uint8_t* str = id.id;\n        chr_count          = 16;\n        for (uint8_t len = 0; len < chr_count; ++len) {\n            uint8_t c = str[len >> 1];\n            c         = ((c >> (((len & 1) ^ 1) << 2)) & 0x0F) + '0';\n            if (c > '9') {\n                c += 7;\n            }\n            _desc_str[1 + len] = c;\n        }\n    } else {\n        // Convert ASCII string into UTF-16\n        if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {\n            return NULL;\n        }\n        const char* str = string_desc_arr[index];\n        // Cap at max char\n        chr_count = strlen(str);\n        if (chr_count > 31) chr_count = 31;\n        for (uint8_t i = 0; i < chr_count; i++) {\n            _desc_str[1 + i] = str[i];\n        }\n    }\n\n    // first byte is length (including header), second byte is string type\n    _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);\n\n    return _desc_str;\n}\n\n// Device descriptors\n\ntusb_desc_device_t const desc_device = {\n    .bLength            = sizeof(tusb_desc_device_t),\n    .bDescriptorType    = TUSB_DESC_DEVICE,\n    .bcdUSB             = 0x0210,  // Supported USB standard (2.1)\n    .bDeviceClass       = TUSB_CLASS_MISC,\n    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,\n    .bDeviceProtocol    = MISC_PROTOCOL_IAD,\n    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,    // Endpoint 0 packet size\n    .idVendor           = 0x1c40,                    // Vendor identifier\n    .idProduct          = 0x0534,                    // Product identifier\n    .bcdDevice          = 0x0100,                    // Protocol version\n    .iManufacturer      = STRING_DESC_MANUFACTURER,  // Index of manufacturer name string\n    .iProduct           = STRING_DESC_PRODUCT,       // Index of product name string\n    .iSerialNumber      = STRING_DESC_SERIAL,        // Index of serial number string\n    .bNumConfigurations = 0x01                       // Number of configurations supported\n};\n\nuint8_t const* tud_descriptor_device_cb(void) { return (uint8_t const*) &desc_device; }\n\n// Configuration Descriptor\n\n#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN + CFG_TUD_VENDOR * TUD_VENDOR_DESC_LEN)\n\n#define EPNUM_VENDOR_0_OUT 0x01  // Endpoint 1\n#define EPNUM_VENDOR_0_IN  0x81\n\n/*#define EPNUM_CDC_0_NOTIF 0x82  // Endpoint 2: CDC serial port for ESP32 console, control\n#define EPNUM_CDC_0_OUT   0x03  // Endpoint 3: CDC serial port for ESP32 console, data\n#define EPNUM_CDC_0_IN    0x83*/\n\nuint8_t const desc_fs_configuration[] = {\n    // Config number, interface count, string index, total length, attribute, power in mA\n    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),\n\n    // WebUSB: Interface number, string index, EP Out & IN address, EP size\n    TUD_VENDOR_DESCRIPTOR(ITF_NUM_VENDOR_0, STRING_DESC_VENDOR_0, EPNUM_VENDOR_0_OUT, EPNUM_VENDOR_0_IN, 32),\n\n    // CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.\n    // TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, STRING_DESC_CDC_0, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64),\n};\n\nuint8_t const* tud_descriptor_configuration_cb(uint8_t index) {\n    (void) index;\n    return desc_fs_configuration;\n}\n"
  },
  {
    "path": "usb_descriptors.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n// enum { ITF_NUM_VENDOR_0, ITF_NUM_CDC_0, ITF_NUM_CDC_0_DATA, ITF_NUM_TOTAL };\nenum { ITF_NUM_VENDOR_0, ITF_NUM_TOTAL };\n"
  },
  {
    "path": "version.h",
    "content": "#pragma once\n\n#define FW_VERSION 0x01\n"
  }
]