mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-25 01:50:21 -04:00 
			
		
		
		
	
		
			
	
	
		
			519 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			519 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | #include <QFileDialog>
 | ||
|  | #include <QMessageBox>
 | ||
|  | #include <QCursor>
 | ||
|  | #include <QThread>
 | ||
|  | #include <libusb.h>
 | ||
|  | #include "osmosdrupgrade.h"
 | ||
|  | #include "ui_osmosdrupgrade.h"
 | ||
|  | 
 | ||
|  | #define OSMOSDR_USB_VID 0x16c0
 | ||
|  | #define OSMOSDR_USB_PID 0x0763
 | ||
|  | 
 | ||
|  | // DFU commands
 | ||
|  | #define DFU_DETACH              0x00
 | ||
|  | #define DFU_DNLOAD              0x01
 | ||
|  | #define DFU_UPLOAD              0x02
 | ||
|  | #define DFU_GETSTATUS           0x03
 | ||
|  | #define DFU_CLRSTATUS           0x04
 | ||
|  | #define DFU_GETSTATE            0x05
 | ||
|  | #define DFU_ABORT               0x06
 | ||
|  | 
 | ||
|  | // DFU states
 | ||
|  | #define ST_appIDLE              0
 | ||
|  | #define ST_appDETACH            1
 | ||
|  | #define ST_dfuIDLE              2
 | ||
|  | #define ST_dfuDNLOAD_SYNC       3
 | ||
|  | #define ST_dfuDNBUSY            4
 | ||
|  | #define ST_dfuDNLOAD_IDLE       5
 | ||
|  | #define ST_dfuMANIFEST_SYNC     6
 | ||
|  | #define ST_dfuMANIFEST          7
 | ||
|  | #define ST_dfuMANIFEST_WAIT_RST 8
 | ||
|  | #define ST_dfuUPLOAD_IDLE       9
 | ||
|  | #define ST_dfuERROR             10
 | ||
|  | 
 | ||
|  | #define DFU_PACKETSIZE 512
 | ||
|  | 
 | ||
|  | #pragma pack(push, 1)
 | ||
|  | struct dfu_getstatus { | ||
|  | 	quint8 bStatus; | ||
|  | 	quint8 bwPollTimeout[3]; | ||
|  | 	quint8 bState; | ||
|  | 	quint8 iString; | ||
|  | }; | ||
|  | #pragma pack(pop)
 | ||
|  | 
 | ||
|  | 
 | ||
|  | OsmoSDRUpgrade::OsmoSDRUpgrade(QWidget* parent) : | ||
|  | 	QDialog(parent), | ||
|  | 	ui(new Ui::OsmoSDRUpgrade), | ||
|  | 	m_usb(NULL) | ||
|  | { | ||
|  | 	ui->setupUi(this); | ||
|  | 	connect(&m_searchDeviceTimer, SIGNAL(timeout()), this, SLOT(searchDeviceTick())); | ||
|  | } | ||
|  | 
 | ||
|  | OsmoSDRUpgrade::~OsmoSDRUpgrade() | ||
|  | { | ||
|  | 	delete ui; | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::on_browse_clicked() | ||
|  | { | ||
|  | 	QString filename = QFileDialog::getOpenFileName(this, tr("OsmoSDR Firmware File"), QString(), tr("OsmoSDR Firmware (*.ofw);;All Files (*)")); | ||
|  | 	if(filename.isEmpty()) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	QFile file(filename); | ||
|  | 	if(file.size() > 10 * 1024 * 1024) { | ||
|  | 		QMessageBox::critical(this, tr("File Open Error"), tr("File is too big to be a firmware update for OsmoSDR.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	QByteArray dfuApp; | ||
|  | 	QByteArray radioApp; | ||
|  | 	QByteArray fpgaBin; | ||
|  | 	mz_zip_archive zip; | ||
|  | 	memset(&zip, 0x00, sizeof(zip)); | ||
|  | 
 | ||
|  | 	if(!mz_zip_reader_init_file(&zip, qPrintable(filename), 0)) { | ||
|  | 		mz_zip_reader_end(&zip); | ||
|  | 		QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	int dfuAppIndex = mz_zip_reader_locate_file(&zip, "dfuapp.bin", NULL, 0); | ||
|  | 	int radioAppIndex = mz_zip_reader_locate_file(&zip, "radioapp.bin", NULL, 0); | ||
|  | 	int fpgaIndex = mz_zip_reader_locate_file(&zip, "fpga.bin", NULL, 0); | ||
|  | 
 | ||
|  | 	if((dfuAppIndex < 0) && (radioAppIndex < 0) && (fpgaIndex < 0)) { | ||
|  | 		mz_zip_reader_end(&zip); | ||
|  | 		QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if(!mz_zip_reader_extract_to_callback(&zip, dfuAppIndex, zipHelper, &dfuApp, 0)) { | ||
|  | 		mz_zip_reader_end(&zip); | ||
|  | 		QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if(!mz_zip_reader_extract_to_callback(&zip, radioAppIndex, zipHelper, &radioApp, 0)) { | ||
|  | 		mz_zip_reader_end(&zip); | ||
|  | 		QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if(!mz_zip_reader_extract_to_callback(&zip, fpgaIndex, zipHelper, &fpgaBin, 0)) { | ||
|  | 		mz_zip_reader_end(&zip); | ||
|  | 		QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	mz_zip_reader_end(&zip); | ||
|  | 	m_dfuApp = dfuApp; | ||
|  | 	m_radioApp = radioApp; | ||
|  | 	m_fpgaBin = fpgaBin; | ||
|  | 
 | ||
|  | 	if(dfuAppIndex >= 0) { | ||
|  | 		ui->dfu->setEnabled(true); | ||
|  | 		ui->dfuSize->setText(tr("%1").arg(m_dfuApp.size())); | ||
|  | 	} else { | ||
|  | 		ui->dfu->setEnabled(false); | ||
|  | 		ui->dfuSize->setText(tr("-")); | ||
|  | 	} | ||
|  | 	if(radioAppIndex >= 0) { | ||
|  | 		ui->radio->setEnabled(true); | ||
|  | 		ui->radioSize->setText(tr("%1").arg(m_radioApp.size())); | ||
|  | 	} else { | ||
|  | 		ui->radio->setEnabled(false); | ||
|  | 		ui->dfuSize->setText(tr("-")); | ||
|  | 	} | ||
|  | 	if(fpgaIndex >= 0) { | ||
|  | 		ui->fpga->setEnabled(true); | ||
|  | 		ui->fpgaSize->setText(tr("%1").arg(m_fpgaBin.size())); | ||
|  | 	} else { | ||
|  | 		ui->fpga->setEnabled(false); | ||
|  | 		ui->fpgaSize->setText(tr("-")); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  | 	if((dfuAppIndex >= 0) && (radioAppIndex < 0) && (fpgaIndex < 0)) { | ||
|  | 		ui->dfu->setChecked(true); | ||
|  | 		ui->radio->setChecked(false); | ||
|  | 		ui->fpga->setChecked(false); | ||
|  | 	} else { | ||
|  | 		ui->dfu->setChecked(false); | ||
|  | 		ui->radio->setChecked(radioAppIndex >= 0); | ||
|  | 		ui->fpga->setChecked(fpgaIndex >= 0); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ui->fwBox->setEnabled(true); | ||
|  | 	ui->progressBox->setEnabled(true); | ||
|  | 
 | ||
|  | 	QFileInfo fi(file.fileName()); | ||
|  | 	ui->filename->setText(fi.fileName()); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::on_dfu_toggled(bool checked) | ||
|  | { | ||
|  | 	updateFlashButton(); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::on_radio_toggled(bool checked) | ||
|  | { | ||
|  | 	updateFlashButton(); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::on_fpga_toggled(bool checked) | ||
|  | { | ||
|  | 	updateFlashButton(); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::updateFlashButton() | ||
|  | { | ||
|  | 	if(ui->dfu->isChecked() || ui->radio->isChecked() || ui->fpga->isChecked()) | ||
|  | 		ui->progressBox->setEnabled(true); | ||
|  | 	else ui->progressBox->setEnabled(false); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::on_start_clicked() | ||
|  | { | ||
|  | 	ui->close->setEnabled(false); | ||
|  | 	ui->start->setEnabled(false); | ||
|  | 	QApplication::setOverrideCursor(Qt::WaitCursor); | ||
|  | 
 | ||
|  | 	ui->log->clear(); | ||
|  | 	log(tr("Starting flash operation...")); | ||
|  | 
 | ||
|  | 	if(libusb_init(&m_usb) < 0) { | ||
|  | 		fail(tr("Could not initialize libusb.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switchToDFU(); | ||
|  | 
 | ||
|  | 	m_searchTries = 0; | ||
|  | 	m_searchDeviceTimer.start(250); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::searchDeviceTick() | ||
|  | { | ||
|  | 	m_searchTries++; | ||
|  | 	log(tr("Searching device (try %1)").arg(m_searchTries)); | ||
|  | 
 | ||
|  | 	libusb_device_handle* device = libusb_open_device_with_vid_pid(m_usb, OSMOSDR_USB_VID, OSMOSDR_USB_PID); | ||
|  | 	if(device == NULL) { | ||
|  | 		if(m_searchTries >= 10) { | ||
|  | 			m_searchDeviceTimer.stop(); | ||
|  | 			finish(); | ||
|  | 		} | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	m_searchDeviceTimer.stop(); | ||
|  | 
 | ||
|  | 	libusb_device_descriptor deviceDescriptor; | ||
|  | 
 | ||
|  | 	if(libusb_get_descriptor(device, LIBUSB_DT_DEVICE, 0, (unsigned char*)&deviceDescriptor, sizeof(deviceDescriptor)) < 0) { | ||
|  | 		libusb_close(device); | ||
|  | 		fail(tr("Could not read device descriptor.")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	char sn[64]; | ||
|  | 	memset(sn, 0x00, sizeof(sn)); | ||
|  | 	libusb_get_string_descriptor_ascii(device, deviceDescriptor.iSerialNumber, (unsigned char*)sn, sizeof(sn)); | ||
|  | 	sn[sizeof(sn) - 1] = '\0'; | ||
|  | 
 | ||
|  | 	log(tr("OsmoSDR found (SN %1)").arg(sn)); | ||
|  | 
 | ||
|  | 	quint8 buffer[255]; | ||
|  | 	if(libusb_get_descriptor(device, LIBUSB_DT_CONFIG, 0, buffer, sizeof(buffer)) < 16) { | ||
|  | 		libusb_close(device); | ||
|  | 		fail(tr("Could not get a configuration descriptor")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if(buffer[15] != 1) { | ||
|  | 		libusb_close(device); | ||
|  | 		fail(tr("Device not in DFU mode as it should be")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	quint8 state; | ||
|  | 
 | ||
|  | 	if(dfuGetState(device, &state) < 0) { | ||
|  | 		libusb_reset_device(device); | ||
|  | 		if(dfuGetState(device, &state) < 0) { | ||
|  | 			libusb_close(device); | ||
|  | 			fail(tr("Cannot get device DFU state")); | ||
|  | 			return; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if((state != ST_dfuIDLE) && (state != ST_dfuDNLOAD_IDLE)) { | ||
|  | 		dfuGetStatus(device); | ||
|  | 		libusb_close(device); | ||
|  | 		fail(tr("Device is not in dfuIDLE or dfuDNLOAD_IDLE state")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if(libusb_claim_interface(device, 0) < 0) { | ||
|  | 		libusb_close(device); | ||
|  | 		fail(tr("Could not claim interface")); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	QByteArray* image; | ||
|  | 	int altSetting; | ||
|  | 
 | ||
|  | 	while(true) { | ||
|  | 		if(ui->dfu->isChecked()) { | ||
|  | 			log(tr("Starting download of DFU application")); | ||
|  | 			image = &m_dfuApp; | ||
|  | 			altSetting = 1; | ||
|  | 		} else if(ui->radio->isChecked()) { | ||
|  | 			log(tr("Starting download of radio application")); | ||
|  | 			image = &m_radioApp; | ||
|  | 			altSetting = 0; | ||
|  | 		} else if(ui->fpga->isChecked()) { | ||
|  | 			log(tr("Starting download of FPGA image")); | ||
|  | 			image = &m_fpgaBin; | ||
|  | 			altSetting = 2; | ||
|  | 		} else { | ||
|  | 			log(tr("Switching back to radio application mode")); | ||
|  | 			dfuDetach(device); | ||
|  | 			break; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if(libusb_set_interface_alt_setting(device, 0, altSetting) < 0) { | ||
|  | 			libusb_close(device); | ||
|  | 			fail(tr("Could not set alternate interface setting to %1").arg(altSetting)); | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		ui->progress->setMaximum((image->size() / DFU_PACKETSIZE) + ((image->size() % DFU_PACKETSIZE) ? 1 : 0)); | ||
|  | 
 | ||
|  | 		int blocknum = 0; | ||
|  | 		while((state == ST_dfuIDLE) || (state == ST_dfuDNLOAD_IDLE)) { | ||
|  | 			int offset = blocknum * DFU_PACKETSIZE; | ||
|  | 			int blocklen = (int)image->size() - offset; | ||
|  | 			if(blocklen < 0) | ||
|  | 				blocklen = 0; | ||
|  | 			if(blocklen > DFU_PACKETSIZE) | ||
|  | 				blocklen = DFU_PACKETSIZE; | ||
|  | 			if(dfuDownloadBlock(device, blocknum, (const quint8*)(image->data() + offset), blocklen) < 0) { | ||
|  | 				if(dfuGetState(device, &state) < 0) { | ||
|  | 					if(ui->dfu->isChecked()) | ||
|  | 						break; | ||
|  | 					libusb_close(device); | ||
|  | 					fail(tr("Cannot get device DFU state")); | ||
|  | 					return; | ||
|  | 				} | ||
|  | 				dfuGetStatus(device); | ||
|  | 				libusb_close(device); | ||
|  | 				fail(tr("Error downloading block %1").arg(blocknum)); | ||
|  | 				return; | ||
|  | 			} | ||
|  | 			dfuGetStatus(device); | ||
|  | 			if(blocklen == 0) | ||
|  | 				break; | ||
|  | 			blocknum++; | ||
|  | 			ui->progress->setValue(blocknum); | ||
|  | 			QApplication::processEvents(); | ||
|  | 		} | ||
|  | 		log(tr("Download complete (state %1)").arg(state)); | ||
|  | 
 | ||
|  | 		do { | ||
|  | 			if(dfuGetState(device, &state) < 0) { | ||
|  | 				if(ui->dfu->isChecked()) { | ||
|  | 					break; | ||
|  | 				} else { | ||
|  | 					libusb_close(device); | ||
|  | 					fail(tr("Cannot get device DFU state")); | ||
|  | 					return; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			dfuGetStatus(device); | ||
|  | 		} while(state == ST_dfuMANIFEST); | ||
|  | 
 | ||
|  | 		if(ui->dfu->isChecked()) { | ||
|  | 			ui->dfu->setChecked(false); | ||
|  | 			m_searchTries = 0; | ||
|  | 			m_searchDeviceTimer.start(250); | ||
|  | 			libusb_close(device); | ||
|  | 			return; | ||
|  | 		} else if(ui->radio->isChecked()) { | ||
|  | 			ui->radio->setChecked(false); | ||
|  | 		} else if(ui->fpga->isChecked()) { | ||
|  | 			ui->fpga->setChecked(false); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		QApplication::processEvents(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	log(tr("Upgrade finished successfully")); | ||
|  | 	libusb_close(device); | ||
|  | 	finish(); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::switchToDFU() | ||
|  | { | ||
|  | 	libusb_device_handle* device = libusb_open_device_with_vid_pid(m_usb, OSMOSDR_USB_VID, OSMOSDR_USB_PID); | ||
|  | 	if(device == NULL) { | ||
|  | 		log(tr("No OsmoSDR VID:PID %1:%2 found").arg(OSMOSDR_USB_VID, 4, 16, QChar('0')).arg(OSMOSDR_USB_PID, 4, 16, QChar('0'))); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	quint8 buffer[255]; | ||
|  | 	if(libusb_get_descriptor(device, LIBUSB_DT_CONFIG, 0, buffer, sizeof(buffer)) < 16) { | ||
|  | 		log(tr("Could not get a valid configuration descriptor")); | ||
|  | 		libusb_close(device); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if(buffer[15] != 1) { | ||
|  | 		if(libusb_claim_interface(device, 0) < 0) { | ||
|  | 			log(tr("Could not claim interface")); | ||
|  | 			libusb_close(device); | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		log(tr("Switching device to DFU mode")); | ||
|  | 		libusb_control_transfer(device, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0x07, 0x0003, 0, NULL, 0, 50); | ||
|  | 		libusb_release_interface(device, 0); | ||
|  | 	} else { | ||
|  | 		log(tr("Device is already in DFU mode")); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	libusb_close(device); | ||
|  | } | ||
|  | 
 | ||
|  | int OsmoSDRUpgrade::dfuGetState(libusb_device_handle* device, quint8* state) | ||
|  | { | ||
|  | 	int res; | ||
|  | 
 | ||
|  | 	res = libusb_control_transfer( | ||
|  | 		device, | ||
|  | 		0xa1, | ||
|  | 		DFU_GETSTATE, | ||
|  | 		0, | ||
|  | 		0, | ||
|  | 		(unsigned char*)state, | ||
|  | 		1, | ||
|  | 		1000); | ||
|  | 
 | ||
|  | 	return res; | ||
|  | } | ||
|  | 
 | ||
|  | int OsmoSDRUpgrade::dfuGetStatus(libusb_device_handle* device) | ||
|  | { | ||
|  | 	int res; | ||
|  | 	struct dfu_getstatus getstatus; | ||
|  | /*
 | ||
|  | 	static const char* statedesc[] = { | ||
|  | 		"appIDLE", | ||
|  | 		"appDETACH", | ||
|  | 		"dfuIDLE", | ||
|  | 		"dfuDNLOAD-SYNC", | ||
|  | 		"dfuDNBUSY", | ||
|  | 		"dfuDNLOAD-IDLE", | ||
|  | 		"dfuMANIFEST-SYNC", | ||
|  | 		"dfuMANIFEST", | ||
|  | 		"dfuMANIFEST-WAIT-RESET", | ||
|  | 		"dfuUPLOAD-IDLE", | ||
|  | 		"dfuERROR" | ||
|  | 	}; | ||
|  | */ | ||
|  | 
 | ||
|  | 	res = libusb_control_transfer( | ||
|  | 		device, | ||
|  | 		0xa1, | ||
|  | 		DFU_GETSTATUS, | ||
|  | 		0, | ||
|  | 		0, | ||
|  | 		(unsigned char*)&getstatus, | ||
|  | 		sizeof(getstatus), | ||
|  | 		1000); | ||
|  | /*
 | ||
|  | 	if(res >= 0) | ||
|  | 		log(tr("OsmoSDR bStatus: %1, bState: %2 (%3)").arg(getstatus.bStatus).arg(statedesc[getstatus.bState]).arg(getstatus.bState)); | ||
|  | */ | ||
|  | 	return res; | ||
|  | } | ||
|  | 
 | ||
|  | int OsmoSDRUpgrade::dfuClrStatus(libusb_device_handle* device) | ||
|  | { | ||
|  | 	return libusb_control_transfer( | ||
|  | 		device, | ||
|  | 		0x21, | ||
|  | 		DFU_CLRSTATUS, | ||
|  | 		0, | ||
|  | 		0, | ||
|  | 		NULL, | ||
|  | 		0, | ||
|  | 		1000); | ||
|  | } | ||
|  | 
 | ||
|  | int OsmoSDRUpgrade::dfuDownloadBlock(libusb_device_handle* device, quint16 block, const quint8* data, quint16 len) | ||
|  | { | ||
|  | 	return libusb_control_transfer( | ||
|  | 		device, | ||
|  | 		0x21, | ||
|  | 		DFU_DNLOAD, | ||
|  | 		block, | ||
|  | 		0, | ||
|  | 		(unsigned char*)data, | ||
|  | 		len, | ||
|  | 		10000); | ||
|  | } | ||
|  | 
 | ||
|  | int OsmoSDRUpgrade::dfuDetach(libusb_device_handle* device) | ||
|  | { | ||
|  | 	return libusb_control_transfer( | ||
|  | 		device, | ||
|  | 		0x21, | ||
|  | 		DFU_DETACH, | ||
|  | 		0, | ||
|  | 		0, | ||
|  | 		NULL, | ||
|  | 		0, | ||
|  | 		1000); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::fail(const QString& msg) | ||
|  | { | ||
|  | 	log(tr("Fatal error: %1").arg(msg)); | ||
|  | 	QMessageBox::critical(this, tr("OsmoSDR Upgrade Failed"), msg); | ||
|  | 	finish(); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::finish() | ||
|  | { | ||
|  | 	if(m_usb != NULL) { | ||
|  | 		libusb_exit(m_usb); | ||
|  | 		m_usb = NULL; | ||
|  | 	} | ||
|  | 	QApplication::restoreOverrideCursor(); | ||
|  | 	ui->start->setEnabled(true); | ||
|  | 	ui->close->setEnabled(true); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::log(const QString& msg) | ||
|  | { | ||
|  | 	ui->log->appendPlainText(msg); | ||
|  | 	ui->log->moveCursor(QTextCursor::End); | ||
|  | } | ||
|  | 
 | ||
|  | void OsmoSDRUpgrade::reject() | ||
|  | { | ||
|  | 	if(ui->close->isEnabled()) | ||
|  | 		return QDialog::reject(); | ||
|  | } | ||
|  | 
 | ||
|  | size_t OsmoSDRUpgrade::zipHelper(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n) | ||
|  | { | ||
|  | 	QByteArray* bytes = (QByteArray*)pOpaque; | ||
|  | 	bytes->append((const char*)pBuf, n); | ||
|  | 	return n; | ||
|  | } |