diff --git a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp new file mode 100644 index 00000000..0927fb8b --- /dev/null +++ b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp @@ -0,0 +1,80 @@ +#include "MplabSnap.hpp" +#include "src/Exceptions/Exception.hpp" + +using namespace Bloom::DebugToolDrivers; +using namespace Protocols::CmsisDap::Edbg::Avr; +using namespace Bloom::Exceptions; + +void MplabSnap::init() { + UsbDevice::init(); + + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); + + if (!usbHidInterface.isInitialised()) { + usbHidInterface.init(); + } + + this->getEdbgInterface().setMinimumCommandTimeGap(35); + + // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. + if (!this->sessionStarted) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + this->setInitialised(true); +} + +void MplabSnap::close() { + if (this->sessionStarted) { + this->endSession(); + } + + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); +} + +std::string MplabSnap::getSerialNumber() { + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) + ); + + if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { + throw Exception("Failed to fetch serial number from device - invalid Discovery Protocol response ID."); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); +} + +void MplabSnap::startSession() { + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + CommandFrames::HouseKeeping::StartSession() + ); + + if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { + // Failed response returned! + throw Exception("Failed to start session with MPLAB Snap!"); + } + + this->sessionStarted = true; +} + +void MplabSnap::endSession() { + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + CommandFrames::HouseKeeping::EndSession() + ); + + if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { + // Failed response returned! + throw Exception("Failed to end session with MPLAB Snap!"); + } + + this->sessionStarted = false; +} diff --git a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp new file mode 100644 index 00000000..5697abce --- /dev/null +++ b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include + +#include "src/DebugToolDrivers/DebugTool.hpp" +#include "src/DebugToolDrivers/USB/UsbDevice.hpp" +#include "src/DebugToolDrivers/USB/HID/HidInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrames.hpp" + +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap; + using Protocols::CmsisDap::Edbg::EdbgInterface; + using Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface; + + /** + * The MPLAB Snap device is a hybrid device - that is, it can present itself as an "MPLAB Snap ICD" device, as well + * as an EDBG (Embedded Debugger) device. The device switches between these two modes via a firmware update, issued + * by Microchip software (the MPLAB IDE and IPE). It appears that it can only interface with AVR targets using the + * EDBG firmware, which, apparently, is why it is known to be in "AVR mode" when it presents itself as an EDBG + * device. + * + * This debug tool driver currently only supports the device when in AVR mode. Because the device uses different + * vendor & product IDs depending on the mode, it is easy to determine which is which. In fact, Bloom will not even + * recognise the device if it's not in AVR mode. + * + * Communication: + * Uses the same EDBG protocol as described in the AtmelIce driver. See the AtmelIce debug tool class for more. + * + * USB Setup (when in AVR/EDBG mode): + * Vendor ID: 0x03eb (1003) + * Product ID: 0x2180 (8576) + */ + class MplabSnap: public DebugTool, public Usb::UsbDevice + { + private: + EdbgInterface edbgInterface = EdbgInterface(); + + /** + * The MPLAB Snap employs the EDBG AVR8 Generic protocol, for debugging AVR8 targets. This protocol is + * implemented in EdbgAvr8Interface. See the EdbgAvr8Interface class for more information. + */ + std::unique_ptr edbgAvr8Interface = nullptr; + + bool sessionStarted = false; + + public: + static const std::uint16_t USB_VENDOR_ID = 1003; + static const std::uint16_t USB_PRODUCT_ID = 8576; + + MplabSnap(): UsbDevice(MplabSnap::USB_VENDOR_ID, MplabSnap::USB_PRODUCT_ID) {} + + void init() override; + void close() override; + + EdbgInterface& getEdbgInterface() { + return this->edbgInterface; + } + + Avr8Interface* getAvr8Interface() override { + return this->edbgAvr8Interface.get(); + } + + std::string getName() override { + return "MPLAB Snap"; + }; + + /** + * Retrieves the device serial number via the Discovery Protocol. + * + * @return + */ + std::string getSerialNumber() override; + + /** + * Starts a session with the EDBG-based tool using the housekeeping protocol. + */ + void startSession(); + + /** + * Ends the active session with the debug tool. + */ + void endSession(); + }; +}