{"id":29675706,"url":"https://github.com/oxidecomputer/lpc-link2-re","last_synced_at":"2025-07-22T23:38:05.058Z","repository":{"id":37178174,"uuid":"267445271","full_name":"oxidecomputer/lpc-link2-re","owner":"oxidecomputer","description":"Reverse engineering the LPC-Link2 USB interface","archived":false,"fork":false,"pushed_at":"2022-06-17T01:42:30.000Z","size":17,"stargazers_count":22,"open_issues_count":2,"forks_count":0,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-07-21T21:13:40.515Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oxidecomputer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-27T23:14:26.000Z","updated_at":"2022-08-18T15:56:29.000Z","dependencies_parsed_at":"2022-06-24T04:24:30.686Z","dependency_job_id":null,"html_url":"https://github.com/oxidecomputer/lpc-link2-re","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/oxidecomputer/lpc-link2-re","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Flpc-link2-re","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Flpc-link2-re/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Flpc-link2-re/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Flpc-link2-re/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxidecomputer","download_url":"https://codeload.github.com/oxidecomputer/lpc-link2-re/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Flpc-link2-re/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266591230,"owners_count":23953082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-22T23:38:01.278Z","updated_at":"2025-07-22T23:38:05.042Z","avatar_url":"https://github.com/oxidecomputer.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LPC-Link2 SWO Protocol\n\n(Written by @cbiffle with major contributions by @kc8apf and @bcantrill.)\n\nHere is a reverse engineered minimal specification of the LPC-Link2's USB\ninterface for capturing SWO trace data. There's also a reference implementation\nin this repo; see the end of this doc for instructions.\n\n## Context\n\nThe LPC-Link2 is presented as implementing the CMSIS-DAP interface, but only\nimplements parts of it -- and, in particular, uses a proprietary and\nundocumented interface for reading SWO trace.\n\nWe like being able to get trace output from microcontrollers, and there\ncurrently appears to be *not a single* DAP-level SWD debug probe on the market\nthat can do so using an open interface. (If you know of one, please let us know,\nwe'd love to support it.)\n\nThis document explains the proprietary USB interface in enough detail to\nsuccessfully capture data from the LPC-Link2, making it a potential option for\nour needs.\n\n## Protocol endpoint\n\nThe LPC-Link reports as *several* USB interfaces. Interface 1 is the CMSIS-DAP\nport; interface 4 is the SWO port.\n\nThis interface is identified as follows:\n\n- Interface name: `LPC-LINK2 DATA PORT`\n- Two endpoints, both interrupt, EP4 IN/OUT, max packet 1024 bytes\n\n## Command-response packet protocol\n\nPackets are sent from the host to the LPC-Link (OUT) and from the LPC-Link to\nthe host (IN) as USB interrupt transactions. Packets are padded: the host\nconsistently sends packets of either 1 or 1024 bytes, and the device always\nappears to send 1024. You must understand the length of each packet to\ndistinguish it from trailing garbage.\n\nAtop interrupt transactions, the protocol is command-response. Each operation\nconsists of a transmission from the host (a single OUT transaction) and a\ntransmission from the device (a single IN transaction). This is very similar to\nhow CMSIS-DAP works.\n\nFor OUT transactions, the first byte of each packet gives the command number\n(described below). For IN transactions, the first byte is *often* the command\nnumber that is being replied to, but varies by command.\n\nFor the rest of this document we'll refer to the OUT transactions as \"commands\"\nand the IN transactions as \"responses.\"\n\n## How to get data\n\nTo elicit SWO capture from the LPC-Link2, you need to perform the following\nsequence of commands (as described below).\n\n- One `Ohai` to kick things off.\n- One `Initialize UART`.\n- Some number of `Configure SWO bit rate` commands to find a rate that your\n  microcontroller's trace output can match.\n- Repeating `Poll capture buffer` for as long as you want data.\n\n## Command set\n\nBecause this information was generated by cleanroom reverse engineering, all the\nnames are made up, and we may have missed trailing fields in packets if they're\nalways sent as zero in practice.\n\n### Ohai (`0x1f`)\n\nSent by the host before other operations. Appears to request that the LPC-Link2\ntransition into a particular mode.\n\n#### Command\n\n```\nbyte[0] = 0x1f\nbyte[1] = mode  # ff requests UART-encoded SWO capture, other values unknown\n```\n\n#### Response\n\n```\nbyte[0] = 0x1f\nbyte[1] = response  # contents not understood, always 0x38 in practice\n```\n\n### Initialize UART (`0x03`)\n\nThis appears to do some important UART setup, and then return the maximum bit\nrate the UART could hope to achieve, along with some other data that is always\nzero in practice.\n\n#### Command\n\n```\nbyte[0] = 0x03\n```\n\n#### Response\n\n```\nbyte[0] = 0x03\nbyte[1:4] = zeroes\nbyte[5:8] = little endian u32 uart max bitrate in hertz\n```\n\n### Configure SWO bit rate (`0x01`)\n\nThis requests a UART bit rate. The LPC-Link2 will respond with an achievable\nbit rate near the requested one. USB captures of the proprietary tools show\nthem going back and forth several times to negotiate a mutually agreeable rate.\n\n#### Command\n\n```\nbyte[0] = 0x01\nbyte[1:4] = little endian u32 target bit rate in hertz\n```\n\n#### Response\n\n```\nbyte[0] = 0x01\nbyte[1:4] = little endian u32 achieved bit rate in hertz\n```\n\n### Poll capture buffer (`0x02`)\n\nThis requests an update if any new data has appeared in the SWO capture buffer.\nUSB captures of the proprietary tools show them sending this about every 12ms.\n\nThis command is unusual: there are two types of possible responses.\n\n#### Command\n\n```\nbyte[0] = 0x02\n```\n\n#### Response #1: Incremental\n\n```\nbyte[0] = 0x04\nbyte[1] = capture epoch\nbyte[2:4] = packed fill levels\nbyte[5+] = data\n```\n\nThis type of response indicates that data has (or has not) appeared in the\ncapture buffer since last poll.\n\nThe `capture epoch` starts at 1 and increments with each flush response (below).\nThis *sort of* gives the number of whole kibibytes transferred plus 1, except\nthat the buffer flush unit is 1022 bytes, not 1024, so... not really.\n\nIf no new data has appeared, the `packed fill levels` field in `byte[2:4]` will\ncontain all zeroes.\n\nOtherwise, `byte[2:4]` contains two packed 12-bit numbers:\n\n```\npacked = bytes[2:4] as little endian 24-bit integer\nfill level before this transmission = packed[11:0]\nfill level after this transmission = packed[23:12]\n```\n\nThe five-byte header is followed by some number of data bytes; the number can be\nfound by subtracting the \"before\" fill level from the \"after\" fill level.\n\nOne might assume that \"no new bytes\" would be signaled by sending this packet\nwith the current fill level repeated twice, but no, it's indicated by zeroes.\n\n#### Response #2: Flush\n\n```\nbyte[0] = 0x82\nbyte[1] = capture epoch\nbyte[2:1023] = data\n```\n\nWhen the capture buffer accumulates exactly 1022 bytes of data, it is flushed\nusing this type of response. This delivers the entire 1022 bytes, which repeats\nthe data you've received in `04`-type responses already.\n\nCapture buffers appear to be double-buffered: it looks like the LPC-Link2 goes\non receiving SWO data even after the buffer fills. As a result, if the\nproprietary software gets an `82`-type response, USB captures show it following\nup with a rapid succession of `04`-type responses to get any additional data\nthat may have piled up in the buffer while the `82` response was waiting.\n\nAn `82`-type response is the final packet sent in a given `capture epoch`. The\nnext response will increment it by one.\n\n## Reference implementation\n\nWe provide a simple demonstration of the protocol called `lpc-cat`. This is a\ncommand-line program that will connect to an LPC-Link2 and output raw SWO data\non stdout.\n\nThe program is written in Rust; to build it, you will need a Rust toolchain.\n\nBuild and run as follows:\n\n```\ncargo run \u003cbitrate\u003e | hexdump -C\n```\n\ne.g. for 3Mbps, `cargo run 3000000 | hexdump -C`.\n\n**Note:** This will only do something if your LPC-Link2 is receiving SWO input.\nGetting your microcontroller to produce UART-formatted SWO input at a particular\nbit rate is board-specific and out of scope here. We trust you can work it out.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Flpc-link2-re","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxidecomputer%2Flpc-link2-re","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Flpc-link2-re/lists"}