From 24adc08d84129a9de1f3b22575c3d0d142c7a0b6 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 20 Sep 2021 15:32:00 +0800 Subject: [PATCH] LF config file --- config/config_official.json | 56 ++++++++++ config/config_rrgv4.13.json | 62 +++++++++++ module/lf.cpp | 199 +++++++++++++++++------------------- module/lf.h | 19 ++-- ui/mainwindow.cpp | 53 +++++----- ui/mainwindow.h | 14 +-- ui/mainwindow.ui | 37 ++++--- 7 files changed, 274 insertions(+), 166 deletions(-) diff --git a/config/config_official.json b/config/config_official.json index cc671c4..37ed8e2 100644 --- a/config/config_official.json +++ b/config/config_official.json @@ -1,4 +1,6 @@ { + "//": "Based on Proxmark3 official repo v3.1.0, commit 6116334", + "//": "You can change this file if the command format of client changes", "mifare classic": { "nested": { "cmd": "hf mf nested *", @@ -102,6 +104,7 @@ "cmd": "hf mf cgetsc ", "data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}" }, + "//": "When writing a block, if the result is not empty and doesn't contain the failed flag, the function will return true", "normal write block": { "cmd": "hf mf wrbl ", "key type": { @@ -135,5 +138,58 @@ "Magic Card set parameter": { "cmd": "hf mf csetuid " } + }, + "lf": { + "read": { + "cmd": "lf read", + "show cmd": "data plot" + }, + "sniff": { + "cmd": "lf snoop", + "show cmd": "data plot" + }, + "search": { + "cmd": "lf search u" + }, + "tune": { + "cmd": "hw tune l" + }, + "get config": { + "cmd": "hw status", + "field start": "LF Sampling config:", + "field end": "USB Speed:", + "divisor": { + "flag": "divisor:", + "pattern": "\\d+" + }, + "bits per sample": { + "flag": "bps:", + "pattern": "\\d+" + }, + "decimation": { + "flag": "decimation:", + "pattern": "\\d+" + }, + "averaging": { + "flag": "averaging:", + "pattern": "\\d+" + }, + "trigger threshold": { + "flag": "trigger threshold:", + "pattern": "\\d+" + }, + "samples to skip": { + "flag": "samples to skip:", + "pattern": "\\d+" + }, + "//": "execute 'cmd' then find parameters between 'field stard' and 'field end'", + "//": "for each line, if the line doesn't have any flag, skip", + "//": "otherwise, delete characters before 'flag' and 'flag' itself, then use 'pattern' to get the parameter", + "//": "If 'replace' dict exists, replace all keys with respective values before getting parameters" + }, + "set config": { + "cmd": "lf config q b d a t s ", + "divisor cmd": "hw setlfdivisor " + } } } \ No newline at end of file diff --git a/config/config_rrgv4.13.json b/config/config_rrgv4.13.json index ced4a21..eab77c1 100644 --- a/config/config_rrgv4.13.json +++ b/config/config_rrgv4.13.json @@ -1,4 +1,6 @@ { + "//": "Based on Proxmark3 rrg repo v4.13441, commit 35ddebc", + "//": "You can change this file if the command format of client changes", "mifare classic": { "nested": { "cmd": "hf mf nested -- --blk - -k ", @@ -102,6 +104,7 @@ "cmd": "hf mf cgetsc --sec ", "data pattern": "([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}" }, + "//": "When writing a block, if the result is not empty and doesn't contain the failed flag, the function will return true", "normal write block": { "cmd": "hf mf wrbl --blk - -k -d ", "key type": { @@ -137,5 +140,64 @@ "Magic Card set parameter": { "cmd": "hf mf csetuid --uid --atqa --sak " } + }, + "lf": { + "read": { + "cmd": "lf read -v", + "show cmd": "data plot" + }, + "sniff": { + "cmd": "lf sniff -v", + "show cmd": "data plot" + }, + "search": { + "cmd": "lf search -u" + }, + "tune": { + "cmd": "lf tune --divisor " + }, + "get config": { + "cmd": "hw status", + "field start": "LF Sampling config", + "field end": "LF Sampling Stack", + "divisor": { + "flag": "divisor", + "pattern": "\\d+" + }, + "bits per sample": { + "flag": "bits per sample", + "pattern": "\\d+" + }, + "decimation": { + "flag": "decimation", + "pattern": "\\d+" + }, + "averaging": { + "flag": "averaging", + "pattern": "\\d+", + "replace": { + "yes": "1", + "no": "0", + "Yes": "1", + "No": "0" + } + }, + "trigger threshold": { + "flag": "trigger threshold", + "pattern": "\\d+" + }, + "samples to skip": { + "flag": "samples to skip", + "pattern": "\\d+" + }, + "//": "execute 'cmd' then find parameters between 'field stard' and 'field end'", + "//": "for each line, if the line doesn't have any flag, skip", + "//": "otherwise, delete characters before 'flag' and 'flag' itself, then use 'pattern' to get the parameter", + "//": "If 'replace' dict exists, replace all keys with respective values before getting parameters" + }, + "set config": { + "cmd": "lf config --divisor --bps --dec --avg --trig --skip ", + "divisor cmd": "hw setlfdivisor -d " + } } } \ No newline at end of file diff --git a/module/lf.cpp b/module/lf.cpp index 7878f27..ee4aead 100644 --- a/module/lf.cpp +++ b/module/lf.cpp @@ -1,6 +1,7 @@ #include "lf.h" +#include -const LF::Config LF::defaultConfig; +const LF::LFConfig LF::defaultLFConfig; LF::LF(Ui::MainWindow *ui, Util *addr, QWidget *parent): QObject(parent) { @@ -8,142 +9,122 @@ LF::LF(Ui::MainWindow *ui, Util *addr, QWidget *parent): QObject(parent) util = addr; this->ui = ui; - configPattern = new QRegularExpression("(\\d+)|Yes|No"); - currConfig = defaultConfig; + LFconfigPattern = new QRegularExpression("(\\d+)|Yes|No"); + currLFConfig = defaultLFConfig; } void LF::read() { - if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) - util->execCMD("lf read"); - else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) - util->execCMD("lf read -v"); + QVariantMap config = configMap["read"].toMap(); + util->execCMD(config["cmd"].toString()); Util::gotoRawTab(); - util->execCMD("data plot"); + util->execCMD(config["show cmd"].toString()); } void LF::sniff() { - if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) - util->execCMD("lf snoop"); - else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) - util->execCMD("lf sniff -v"); + QVariantMap config = configMap["sniff"].toMap(); + util->execCMD(config["cmd"].toString()); Util::gotoRawTab(); - util->execCMD("data plot"); + util->execCMD(config["show cmd"].toString()); } void LF::search() { - if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) - util->execCMD("lf search u"); - else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) - util->execCMD("lf search -u"); + QVariantMap config = configMap["search"].toMap(); + util->execCMD(config["cmd"].toString()); Util::gotoRawTab(); } void LF::tune() { - if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) - util->execCMD("hw tune l"); - else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) - util->execCMD("lf tune --divisor " + QString::number(currConfig.divisor)); + QVariantMap config = configMap["tune"].toMap(); + QString cmd = config["cmd"].toString(); + cmd.replace("", QString::number(currLFConfig.divisor)); + util->execCMD(cmd); Util::gotoRawTab(); } -void LF::getConfig() +bool LF::getLFConfig_helper(const QVariantMap& map, QString& str, int* result) { + int len; + QString flag = map["flag"].toString(); QRegularExpressionMatch reMatch; - QString result; - QStringList resultList; - QStringList symbolList = - { - "divisor", - "bps", - "bits per sample", - "decimation", - "averaging", - "trigger threshold", - "samples to skip" - }; - int offset; - QStringList configList = {"", "", "", "", "", "", ""}; - result = util->execCMDWithOutput("hw status", 400); // not all output from "hw status will be processed". - result = result.right(result.length() - result.indexOf("LF Sampling config")); - offset = result.indexOf("samples to skip"); - offset = result.indexOf("\n", offset); - result = result.mid(0, offset + 2); - qDebug() << "LF CONFIG GET\n" << result; - resultList = result.split("\n"); - for(int i = 0; i < resultList.length(); i++) + if(!str.contains(flag)) + return false; + len = str.length() - (str.indexOf(flag) + flag.length()); + str = str.right(len); + if(map.contains("replace")) { - for(int j = 0; j < symbolList.length(); j++) + QVariantMap table = map["replace"].toMap(); + for(auto it = table.begin(); it != table.end(); it++) { - if(!configList[j].isEmpty()) - continue; - offset = resultList[i].indexOf(symbolList[j]); - if(offset != -1) - { - reMatch = configPattern->match(resultList[i]); - qDebug() << "finded: " << resultList[i]; - if(!reMatch.hasMatch()) - continue; - qDebug() << "captured: " << reMatch.captured(); - configList[j] = reMatch.captured(); - break; - } + str.replace(it.key(), it.value().toString()); } } - qDebug() << "configList: " << configList; - currConfig.divisor = configList[0].toUInt(); - currConfig.decimation = configList[3].toUInt(); - currConfig.triggerThreshold = configList[5].toUInt(); - currConfig.samplesToSkip = configList[6].toUInt(); - if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) - { - currConfig.bitPerSample = configList[1].toUInt(); - currConfig.averaging = (configList[4] == "1"); - } - else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + reMatch = QRegularExpression(map["pattern"].toString()).match(str); + if(!reMatch.hasMatch()) + return false; + *result = reMatch.captured().toInt(); + qDebug() << *result; + return true; + +} + +void LF::getLFConfig() +{ + + QRegularExpressionMatch reMatch; + QString result; + QStringList resultList; + int start, end, temp; + QVariantMap config = configMap["get config"].toMap(); + QString cmd = config["cmd"].toString(); + result = util->execCMDWithOutput(cmd, 400); + start = result.indexOf(config["field start"].toString()); + end = result.indexOf(config["field end"].toString()); + result = result.mid(start, end - start); + resultList = result.split("\n", Qt::SkipEmptyParts); + qDebug() << "LF CONFIG GET\n" << resultList; + for(auto it = resultList.begin(); it != resultList.end(); it++) { - currConfig.bitPerSample = configList[2].toUInt(); - currConfig.averaging = (configList[4] == "Yes"); + if(getLFConfig_helper(config["divisor"].toMap(), *it, &temp)) + currLFConfig.divisor = temp; + else if(getLFConfig_helper(config["bits per sample"].toMap(), *it, &temp)) + currLFConfig.bitsPerSample = temp; + else if(getLFConfig_helper(config["decimation"].toMap(), *it, &temp)) + currLFConfig.decimation = temp; + else if(getLFConfig_helper(config["averaging"].toMap(), *it, &temp)) + currLFConfig.averaging = (bool)temp; + else if(getLFConfig_helper(config["trigger threshold"].toMap(), *it, &temp)) + currLFConfig.triggerThreshold = temp; + else if(getLFConfig_helper(config["samples to skip"].toMap(), *it, &temp)) + currLFConfig.samplesToSkip = temp; } syncWithUI(); } -void LF::setConfig(LF::Config config) +void LF::setLFConfig(LF::LFConfig lfconfig) { - currConfig = config; - if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) - { - util->execCMDWithOutput(QString("lf config") - + " q " + QString::number(currConfig.divisor) - + " b " + QString::number(currConfig.bitPerSample) - + " d " + QString::number(currConfig.decimation) - + " a " + QString(currConfig.averaging ? "1" : "0") - + " t " + QString::number(currConfig.triggerThreshold) - + " s " + QString::number(currConfig.samplesToSkip), - 500); - util->execCMDWithOutput("hw setlfdivisor " + QString::number(currConfig.divisor), 500); - } - else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) - { - util->execCMDWithOutput(QString("lf config") - + " -a " + QString(currConfig.averaging ? "1" : "0") - + " -b " + QString::number(currConfig.bitPerSample) - + " --dec " + QString::number(currConfig.decimation) - + " --divisor " + QString::number(currConfig.divisor) - + " -s " + QString::number(currConfig.samplesToSkip) - + " -t " + QString::number(currConfig.triggerThreshold), - 500); - util->execCMDWithOutput("hw setlfdivisor -d " + QString::number(currConfig.divisor), 500); - } + currLFConfig = lfconfig; + QVariantMap config = configMap["set config"].toMap(); + QString cmd = config["cmd"].toString(); + cmd.replace("", QString::number(currLFConfig.divisor)); + cmd.replace("", QString::number(currLFConfig.bitsPerSample)); + cmd.replace("", QString::number(currLFConfig.decimation)); + cmd.replace("", currLFConfig.averaging ? "1" : "0"); + cmd.replace("", QString::number(currLFConfig.triggerThreshold)); + cmd.replace("", QString::number(currLFConfig.samplesToSkip)); + util->execCMDWithOutput(cmd, 500); + cmd = config["divisor cmd"].toString(); + cmd.replace("", QString::number(currLFConfig.divisor)); + util->execCMDWithOutput(cmd, 500); } -void LF::resetConfig() +void LF::resetLFConfig() { - setConfig(defaultConfig); - getConfig(); + setLFConfig(defaultLFConfig); + getLFConfig(); } float LF::divisor2Freq(uint8_t divisor) @@ -158,10 +139,16 @@ uint8_t LF::freq2Divisor(float freq) void LF::syncWithUI() { - ui->LF_Conf_freqDivisorBox->setValue(currConfig.divisor); // will trigger valueChanged() - ui->LF_Conf_bitPerSampleBox->setValue(currConfig.bitPerSample); - ui->LF_Conf_decimationBox->setValue(currConfig.decimation); - ui->LF_Conf_averagingBox->setChecked(currConfig.averaging); - ui->LF_Conf_thresholdBox->setValue(currConfig.triggerThreshold); - ui->LF_Conf_skipsBox->setValue(currConfig.samplesToSkip); + ui->LF_LFConf_freqDivisorBox->setValue(currLFConfig.divisor); // will trigger valueChanged() + ui->LF_LFConf_bitsPerSampleBox->setValue(currLFConfig.bitsPerSample); + ui->LF_LFConf_decimationBox->setValue(currLFConfig.decimation); + ui->LF_LFConf_averagingBox->setChecked(currLFConfig.averaging); + ui->LF_LFConf_thresholdBox->setValue(currLFConfig.triggerThreshold); + ui->LF_LFConf_skipsBox->setValue(currLFConfig.samplesToSkip); +} + +void LF::setConfigMap(const QVariantMap& configMap) +{ + this->configMap = configMap; + qDebug() << configMap; } diff --git a/module/lf.h b/module/lf.h index e361a12..fd909fe 100644 --- a/module/lf.h +++ b/module/lf.h @@ -12,17 +12,17 @@ class LF : public QObject public: explicit LF(Ui::MainWindow *ui, Util *addr, QWidget *parent = nullptr); - struct Config + struct LFConfig { uint8_t divisor; - uint8_t bitPerSample; + uint8_t bitsPerSample; uint8_t decimation; bool averaging; uint8_t triggerThreshold; uint16_t samplesToSkip; }; - static constexpr Config defaultConfig = + static constexpr LFConfig defaultLFConfig = { 95, 8, @@ -36,19 +36,22 @@ public: void sniff(); void search(); void tune(); - void getConfig(); - void setConfig(LF::Config config); - void resetConfig(); + void getLFConfig(); + void setLFConfig(LF::LFConfig lfconfig); + void resetLFConfig(); static float divisor2Freq(uint8_t divisor); static uint8_t freq2Divisor(float freq); + void setConfigMap(const QVariantMap &configMap); private: QWidget* parent; Ui::MainWindow *ui; Util* util; - Config currConfig; - QRegularExpression* configPattern; + LFConfig currLFConfig; + QRegularExpression* LFconfigPattern; + QVariantMap configMap; void syncWithUI(); + bool getLFConfig_helper(const QVariantMap& map, QString& str, int* result); signals: }; diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index d901c55..62f2bb6 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -84,6 +84,7 @@ void MainWindow::loadConfig() QByteArray configData = configList.readAll(); QJsonDocument configJson(QJsonDocument::fromJson(configData)); mifare->setConfigMap(configJson.object()["mifare classic"].toObject().toVariantMap()); + lf->setConfigMap(configJson.object()["lf"].toObject().toVariantMap()); } @@ -1182,7 +1183,7 @@ void MainWindow::setButtonsEnabled(bool st) ui->MF_sniffGroupBox->setEnabled(st); ui->Raw_CMDEdit->setEnabled(st); ui->Raw_sendCMDButton->setEnabled(st); - ui->LF_configGroupBox->setEnabled(st); + ui->LF_LFconfigGroupBox->setEnabled(st); ui->LF_operationGroupBox->setEnabled(st); } @@ -1279,37 +1280,37 @@ void MainWindow::on_Set_Client_keepClientActiveBox_stateChanged(int arg1) emit setSerialListener(!keepClientActive); } -void MainWindow::on_LF_Conf_freqSlider_valueChanged(int value) +void MainWindow::on_LF_LFConf_freqSlider_valueChanged(int value) { onLFfreqConfChanged(value, true); } void MainWindow::onLFfreqConfChanged(int value, bool isCustomized) { - ui->LF_Conf_freqDivisorBox->blockSignals(true); - ui->LF_Conf_freqSlider->blockSignals(true); + ui->LF_LFConf_freqDivisorBox->blockSignals(true); + ui->LF_LFConf_freqSlider->blockSignals(true); if(isCustomized) - ui->LF_Conf_freqOtherButton->setChecked(true); - ui->LF_Conf_freqLabel->setText(tr("Actural Freq: ") + QString("%1kHz").arg(LF::divisor2Freq(value), 0, 'f', 3)); - ui->LF_Conf_freqDivisorBox->setValue(value); - ui->LF_Conf_freqSlider->setValue(value); + ui->LF_LFConf_freqOtherButton->setChecked(true); + ui->LF_LFConf_freqLabel->setText(tr("Actural Freq: ") + QString("%1kHz").arg(LF::divisor2Freq(value), 0, 'f', 3)); + ui->LF_LFConf_freqDivisorBox->setValue(value); + ui->LF_LFConf_freqSlider->setValue(value); - ui->LF_Conf_freqDivisorBox->blockSignals(false); - ui->LF_Conf_freqSlider->blockSignals(false); + ui->LF_LFConf_freqDivisorBox->blockSignals(false); + ui->LF_LFConf_freqSlider->blockSignals(false); } -void MainWindow::on_LF_Conf_freqDivisorBox_valueChanged(int arg1) +void MainWindow::on_LF_LFConf_freqDivisorBox_valueChanged(int arg1) { onLFfreqConfChanged(arg1, true); } -void MainWindow::on_LF_Conf_freq125kButton_clicked() +void MainWindow::on_LF_LFConf_freq125kButton_clicked() { onLFfreqConfChanged(95, false); } -void MainWindow::on_LF_Conf_freq134kButton_clicked() +void MainWindow::on_LF_LFConf_freq134kButton_clicked() { onLFfreqConfChanged(88, false); } @@ -1375,32 +1376,32 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event) contextMenu->exec(event->globalPos()); } -void MainWindow::on_LF_Conf_getButton_clicked() +void MainWindow::on_LF_LFConf_getButton_clicked() { setState(false); - lf->getConfig(); + lf->getLFConfig(); setState(true); } -void MainWindow::on_LF_Conf_setButton_clicked() +void MainWindow::on_LF_LFConf_setButton_clicked() { - LF::Config config; + LF::LFConfig config; setState(false); - config.divisor = ui->LF_Conf_freqDivisorBox->value(); - config.bitPerSample = ui->LF_Conf_bitPerSampleBox->value(); - config.decimation = ui->LF_Conf_decimationBox->value(); - config.averaging = ui->LF_Conf_averagingBox->isChecked(); - config.triggerThreshold = ui->LF_Conf_thresholdBox->value(); - config.samplesToSkip = ui->LF_Conf_skipsBox->value(); - lf->setConfig(config); + config.divisor = ui->LF_LFConf_freqDivisorBox->value(); + config.bitsPerSample = ui->LF_LFConf_bitsPerSampleBox->value(); + config.decimation = ui->LF_LFConf_decimationBox->value(); + config.averaging = ui->LF_LFConf_averagingBox->isChecked(); + config.triggerThreshold = ui->LF_LFConf_thresholdBox->value(); + config.samplesToSkip = ui->LF_LFConf_skipsBox->value(); + lf->setLFConfig(config); Util::gotoRawTab(); setState(true); } -void MainWindow::on_LF_Conf_resetButton_clicked() +void MainWindow::on_LF_LFConf_resetButton_clicked() { setState(false); - lf->resetConfig(); + lf->resetLFConfig(); setState(true); } diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 615a027..43a4c34 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -179,13 +179,13 @@ private slots: void on_Set_Client_keepClientActiveBox_stateChanged(int arg1); - void on_LF_Conf_freqSlider_valueChanged(int value); + void on_LF_LFConf_freqSlider_valueChanged(int value); - void on_LF_Conf_freqDivisorBox_valueChanged(int arg1); + void on_LF_LFConf_freqDivisorBox_valueChanged(int arg1); - void on_LF_Conf_freq125kButton_clicked(); + void on_LF_LFConf_freq125kButton_clicked(); - void on_LF_Conf_freq134kButton_clicked(); + void on_LF_LFConf_freq134kButton_clicked(); void on_LF_Op_searchButton_clicked(); @@ -195,11 +195,11 @@ private slots: void on_LF_Op_sniffButton_clicked(); - void on_LF_Conf_getButton_clicked(); + void on_LF_LFConf_getButton_clicked(); - void on_LF_Conf_setButton_clicked(); + void on_LF_LFConf_setButton_clicked(); - void on_LF_Conf_resetButton_clicked(); + void on_LF_LFConf_resetButton_clicked(); void on_Set_Client_workingDirEdit_editingFinished(); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 6952485..a74db1e 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -1247,7 +1247,7 @@ - + LF Config @@ -1268,7 +1268,7 @@ 2 - + Frequency @@ -1291,7 +1291,7 @@ - + 0 @@ -1307,7 +1307,7 @@ - + 0 @@ -1320,7 +1320,7 @@ - + 0 @@ -1340,7 +1340,7 @@ - + 19 @@ -1353,7 +1353,7 @@ - + Actural Freq: 125.000kHz @@ -1375,7 +1375,7 @@ - + 19 @@ -1393,8 +1393,7 @@ - Note: -You might need a modified LF antenna if the freq is not 125k/134k. + You might need a modified LF antenna if the freq is not 125k/134k. When setting the freq, the "hw setlfdivisor" will also be called. @@ -1425,7 +1424,7 @@ When setting the freq, the "hw setlfdivisor" will also be called. - Bit per sample: + Bits per sample: @@ -1437,7 +1436,7 @@ When setting the freq, the "hw setlfdivisor" will also be called. - + 1 @@ -1454,7 +1453,7 @@ When setting the freq, the "hw setlfdivisor" will also be called. - + @@ -1468,7 +1467,7 @@ When setting the freq, the "hw setlfdivisor" will also be called. - + 128 @@ -1482,14 +1481,14 @@ When setting the freq, the "hw setlfdivisor" will also be called. - + 65535 - + 1 @@ -1534,7 +1533,7 @@ When setting the freq, the "hw setlfdivisor" will also be called. - + 0 @@ -1553,7 +1552,7 @@ When setting the freq, the "hw setlfdivisor" will also be called. - + 0 @@ -1572,7 +1571,7 @@ When setting the freq, the "hw setlfdivisor" will also be called. - + Reset