Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2444970
restore c3
thftgr Jun 26, 2026
d3d4470
Update `get_expected_signature` to use `panda` instance for MCU type …
thftgr Jun 26, 2026
93ea7e8
Add USB support to `pandad` with fallback to SPI communication
thftgr Jun 26, 2026
27a8188
Expand modem support for EC20 and simplify ICCID checks
thftgr Jun 26, 2026
66df50d
Add SIM state tracking and optimize ICCID polling frequency
thftgr Jun 26, 2026
f6e86ea
add carrot-agnos
thftgr Jun 26, 2026
e0d3e22
Reorder `start_carrot_recovery` for proper initialization sequence
thftgr Jun 26, 2026
b6fc94a
Install `jeepney` dependency if missing during initialization
thftgr Jun 26, 2026
3a38470
Update Agnos URLs and add `agnos-tici.json`
thftgr Jun 27, 2026
b039097
Adjust Agnos manifest selection based on model type
thftgr Jun 27, 2026
39ad5f6
Move `jeepney` installation to AGNOS update block
thftgr Jun 27, 2026
6483898
Adjust internet wait logic in AGNOS version check
thftgr Jun 27, 2026
db90d81
Add debug logs for AGNOS updater variables to improve visibility
thftgr Jun 27, 2026
1ca5a93
Add debug logs for device model and manifest path in AGNOS updater
thftgr Jun 27, 2026
42ce0d0
Add debug log for AGNOS updater completion
thftgr Jun 27, 2026
3e26691
Reorder `start_carrot_recovery` call for improved initialization logic
thftgr Jun 27, 2026
7e9513f
fix screen action (#417)
jominki354 Jun 29, 2026
b6c1f1a
Merge remote-tracking branch 'origin/carrot-wip' into tcw3-x
thftgr Jun 29, 2026
cd1c82e
Add helper function for BPS blob index selection and update related l…
thftgr Jun 30, 2026
caaef67
Improve first frame sync logic for Spectra camera and add detailed de…
thftgr Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions launch_chffrplus.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,44 @@ function agnos_init {

# Check if AGNOS update is required
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
echo "Waiting for internet..."

timeout=0
while [ $timeout -lt 120 ]; do
if getent hosts pypi.org >/dev/null 2>&1; then
break
fi
echo "Waiting for internet... (${timeout})"
sleep 5
timeout=$((timeout+5))

done

if python3 -c "import jeepney" > /dev/null 2>&1; then
echo "jeepney already installed."
else
echo "jeepney installing to pydeps."
python3 -m pip install --target "$PYDEPS" --upgrade jeepney
fi

AGNOS_PY="$DIR/system/hardware/tici/agnos.py"
MANIFEST="$DIR/system/hardware/tici/agnos.json"
MODEL="$(tr -d '\000\r\n' 2>/dev/null < /sys/firmware/devicetree/base/model | tr '[:upper:]' '[:lower:]')"
MODEL="${MODEL#comma }"
if [ "$MODEL" = "c3" ] || [ "$MODEL" = "tici" ]; then
MANIFEST="$DIR/system/hardware/tici/agnos-tici.json"
fi
if $AGNOS_PY --verify $MANIFEST; then
sudo reboot
fi
$DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST
echo "AGNOS_PY=${AGNOS_PY}"
echo "MANIFEST=${MANIFEST}"
echo "MODEL=${MODEL}"
if ! python3 $DIR/system/ui/updater.py $AGNOS_PY $MANIFEST; then
echo "python updater failed, falling back to bundled updater"
$DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST
fi
echo "end updater $AGNOS_PY $MANIFEST"
fi
}

Expand Down Expand Up @@ -124,14 +156,14 @@ function launch {
PYDEPS="$DIR/pydeps"
mkdir -p "$PYDEPS"
export PYTHONPATH="$PYDEPS:$PWD${PYTHONPATH:+:$PYTHONPATH}"

start_carrot_recovery

# hardware specific init
if [ -f /AGNOS ]; then
agnos_init
fi


FORCE_REBUILD=0
invalidate_modeld_build_if_needed
invalidate_native_build_if_needed
Expand Down
4 changes: 2 additions & 2 deletions selfdrive/pandad/SConscript
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Import('env', 'arch', 'common', 'messaging')

if arch != "Darwin":
libs = [common, messaging, 'pthread']
panda = env.Library('panda', ['panda.cc', 'spi.cc'])
libs = [common, messaging, 'pthread', 'usb-1.0']
panda = env.Library('panda', ['panda.cc', 'panda_comms.cc', 'spi.cc'])

env.Program('pandad', ['main.cc', 'pandad.cc', 'panda_safety.cc'], LIBS=[panda] + libs)

Expand Down
18 changes: 15 additions & 3 deletions selfdrive/pandad/panda.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <unistd.h>

#include <algorithm>
#include <cassert>
#include <stdexcept>
#include <vector>
Expand All @@ -13,8 +14,13 @@
const bool PANDAD_MAXOUT = getenv("PANDAD_MAXOUT") != nullptr;

Panda::Panda(std::string serial) {
handle = std::make_unique<PandaSpiHandle>(serial);
LOGW("connected to %s over SPI", serial.c_str());
try {
handle = std::make_unique<PandaUsbHandle>(serial);
LOGW("connected to %s over USB", serial.c_str());
} catch (const std::exception &e) {
handle = std::make_unique<PandaSpiHandle>(serial);
LOGW("connected to %s over SPI", serial.c_str());
}

hw_type = get_hw_type();
can_reset_communications();
Expand All @@ -33,7 +39,13 @@ std::string Panda::hw_serial() {
}

std::vector<std::string> Panda::list() {
return PandaSpiHandle::list();
std::vector<std::string> serials = PandaUsbHandle::list();
for (const auto &s : PandaSpiHandle::list()) {
if (std::find(serials.begin(), serials.end(), s) == serials.end()) {
serials.push_back(s);
}
}
return serials;
}

void Panda::set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param) {
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/pandad/panda.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct can_frame {

class Panda {
private:
std::unique_ptr<PandaSpiHandle> handle;
std::unique_ptr<PandaCommsHandle> handle;

public:
Panda(std::string serial);
Expand Down
224 changes: 224 additions & 0 deletions selfdrive/pandad/panda_comms.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#include "selfdrive/pandad/panda_comms.h"

#include <libusb-1.0/libusb.h>

#include <memory>
#include <stdexcept>

#include "common/swaglog.h"

static libusb_context *init_usb_ctx() {
libusb_context *context = nullptr;
int err = libusb_init(&context);
if (err != 0) {
LOGE("libusb initialization error");
return nullptr;
}

#if LIBUSB_API_VERSION >= 0x01000106
libusb_set_option(context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
#else
libusb_set_debug(context, 3);
#endif
return context;
}

PandaUsbHandle::PandaUsbHandle(std::string serial) {
ssize_t num_devices;
libusb_device **dev_list = nullptr;
int err = 0;
ctx = init_usb_ctx();
if (!ctx) { goto fail; }

num_devices = libusb_get_device_list(ctx, &dev_list);
if (num_devices < 0) { goto fail; }
for (size_t i = 0; i < num_devices; ++i) {
libusb_device_descriptor desc;
libusb_get_device_descriptor(dev_list[i], &desc);
if (desc.idVendor == 0x3801 && desc.idProduct == 0xddcc) {
int ret = libusb_open(dev_list[i], &dev_handle);
if (dev_handle == nullptr || ret < 0) { goto fail; }

unsigned char desc_serial[26] = {0};
ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, desc_serial, sizeof(desc_serial));
if (ret < 0) { goto fail; }

hw_serial = std::string((char *)desc_serial, ret);
if (serial.empty() || serial == hw_serial) {
break;
}
libusb_close(dev_handle);
dev_handle = nullptr;
}
}
if (dev_handle == nullptr) { goto fail; }
libusb_free_device_list(dev_list, 1);
dev_list = nullptr;

if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
libusb_detach_kernel_driver(dev_handle, 0);
}

err = libusb_set_configuration(dev_handle, 1);
if (err != 0) { goto fail; }

err = libusb_claim_interface(dev_handle, 0);
if (err != 0) { goto fail; }

return;

fail:
if (dev_list != nullptr) {
libusb_free_device_list(dev_list, 1);
}
cleanup();
throw std::runtime_error("Error connecting to panda over USB");
}

PandaUsbHandle::~PandaUsbHandle() {
std::lock_guard lk(hw_lock);
cleanup();
connected = false;
}

void PandaUsbHandle::cleanup() {
if (dev_handle != nullptr) {
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
dev_handle = nullptr;
}

if (ctx != nullptr) {
libusb_exit(ctx);
ctx = nullptr;
}
}

std::vector<std::string> PandaUsbHandle::list() {
static std::unique_ptr<libusb_context, decltype(&libusb_exit)> context(init_usb_ctx(), libusb_exit);
ssize_t num_devices;
libusb_device **dev_list = nullptr;
std::vector<std::string> serials;
if (!context) { return serials; }

num_devices = libusb_get_device_list(context.get(), &dev_list);
if (num_devices < 0) {
LOGE("libusb can't get device list");
goto finish;
}

for (size_t i = 0; i < num_devices; ++i) {
libusb_device *device = dev_list[i];
libusb_device_descriptor desc;
libusb_get_device_descriptor(device, &desc);
if (desc.idVendor == 0x3801 && desc.idProduct == 0xddcc) {
libusb_device_handle *handle = nullptr;
int ret = libusb_open(device, &handle);
if (ret < 0) { goto finish; }

unsigned char desc_serial[26] = {0};
ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, desc_serial, sizeof(desc_serial));
libusb_close(handle);
if (ret < 0) { goto finish; }

serials.push_back(std::string((char *)desc_serial, ret));
}
}

finish:
if (dev_list != nullptr) {
libusb_free_device_list(dev_list, 1);
}
return serials;
}

void PandaUsbHandle::handle_usb_issue(int err, const char func[]) {
LOGE_100("usb error %d \"%s\" in %s", err, libusb_strerror((enum libusb_error)err), func);
if (err == LIBUSB_ERROR_NO_DEVICE) {
LOGE("lost connection");
connected = false;
}
}

int PandaUsbHandle::control_write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout) {
int err;
const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;

if (!connected) {
return LIBUSB_ERROR_NO_DEVICE;
}

std::lock_guard lk(hw_lock);
do {
err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, nullptr, 0, timeout);
if (err < 0) { handle_usb_issue(err, __func__); }
} while (err < 0 && connected);

return err;
}

int PandaUsbHandle::control_read(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) {
int err;
const uint8_t bmRequestType = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;

if (!connected) {
return LIBUSB_ERROR_NO_DEVICE;
}

std::lock_guard lk(hw_lock);
do {
err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
if (err < 0) { handle_usb_issue(err, __func__); }
} while (err < 0 && connected);

return err;
}

int PandaUsbHandle::bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
int err;
int transferred = 0;

if (!connected) {
return 0;
}

std::lock_guard lk(hw_lock);
do {
// Panda can NAK when the receive buffer is full. After 5ms, drop the messages.
err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout);

if (err == LIBUSB_ERROR_TIMEOUT) {
LOGW("Transmit buffer full");
break;
} else if (err != 0 || length != transferred) {
handle_usb_issue(err, __func__);
}
} while (err != 0 && connected);

return transferred;
}

int PandaUsbHandle::bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) {
int err;
int transferred = 0;

if (!connected) {
return 0;
}

std::lock_guard lk(hw_lock);
do {
err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout);

if (err == LIBUSB_ERROR_TIMEOUT) {
break;
} else if (err == LIBUSB_ERROR_OVERFLOW) {
comms_healthy = false;
LOGE_100("overflow got 0x%x", transferred);
} else if (err != 0) {
handle_usb_issue(err, __func__);
}
} while (err != 0 && connected);

return transferred;
}
37 changes: 36 additions & 1 deletion selfdrive/pandad/panda_comms.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,52 @@
#include <string>
#include <vector>

struct libusb_context;
struct libusb_device_handle;

#define TIMEOUT 0
#define SPI_BUF_SIZE 2048


class PandaSpiHandle {
class PandaCommsHandle {
public:
PandaCommsHandle() {}
virtual ~PandaCommsHandle() {}
virtual void cleanup() = 0;

std::string hw_serial;
std::atomic<bool> connected = true;
std::atomic<bool> comms_healthy = true;

virtual int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT) = 0;
virtual int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT) = 0;
virtual int bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT) = 0;
virtual int bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT) = 0;
};

class PandaUsbHandle : public PandaCommsHandle {
public:
PandaUsbHandle(std::string serial);
~PandaUsbHandle();

int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT);
int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT);
int bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
int bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT);
void cleanup();

static std::vector<std::string> list();

private:
libusb_context *ctx = nullptr;
libusb_device_handle *dev_handle = nullptr;
std::recursive_mutex hw_lock;

void handle_usb_issue(int err, const char func[]);
};

class PandaSpiHandle : public PandaCommsHandle {
public:
PandaSpiHandle(std::string serial);
~PandaSpiHandle();

Expand Down
Loading
Loading