<commit_before>#pragma GCC diagnostic ignored "-Wwrite-strings"

#include "ImageViewPanel.h"

#include "res/btn_actual.xpm"
#include "res/btn_auto.xpm"
#include "res/btn_close.xpm"
#include "res/btn_fit.xpm"
#include "res/btn_next.xpm"
#include "res/btn_pause.xpm"
#include "res/btn_prev.xpm"
#include "res/btn_random.xpm"
#include "res/btn_save.xpm"
#include "res/img_picture.xpm"
#include <algorithm>
#include <cmath>
#include <ctime>
#include <wx/mstream.h>
#include <wx/notebook.h>
#include <wx/statline.h>
#include <wx/wfstream.h>

#ifdef USE_CIMG
using cimg_library::CImg;
#endif

ImageViewPanel::ImageViewPanel(wxWindow *parent, Entry *entry, wxWindowID id,
                               const wxPoint &pos, const wxSize &size,
                               long style, const wxString &name)
    : wxPanel(parent, id, pos, size, style, name), timer(this),
      randomEngine(r()), image(IMG_PICTURE_xpm) {
  auto outerSizer = new wxBoxSizer(wxVERTICAL);
  auto btnSizer = new wxBoxSizer(wxHORIZONTAL);
  btnClose = new wxButton(this, wxID_CLOSE, "Close", wxDefaultPosition,
                          wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnClose->SetBitmap(wxICON(IDI_ICON_CLOSE));
  btnClose->SetToolTip("Close this tab.");
  btnClose->Bind(wxEVT_BUTTON, &ImageViewPanel::OnCloseButtonClick, this);

  spnScale = new wxSpinCtrl(this, wxID_ANY);
  spnScale->SetToolTip("Set the percentage the image is scaled to.");
  spnScale->SetValue(100);
  spnScale->SetRange(1, 200);
  spnScale->Bind(wxEVT_SPINCTRL, &ImageViewPanel::OnScaleValueChanged, this);

  btnFitSize = new wxToggleButton(this, wxID_ANY, "Fit", wxDefaultPosition,
                                  wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnFitSize->SetBitmap(wxICON(IDI_ICON_FIT));
  btnFitSize->SetToolTip("Fit the image inside the tab.");

  btnFitSize->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnFitSizeToggle,
                   this);
  btnFitSize->SetValue(true);

  btnActualSize = new wxButton(this, wxID_ANY, "Actual", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnActualSize->Bind(wxEVT_BUTTON, &ImageViewPanel::OnBtnActualSizePressed,
                      this);
  btnActualSize->SetBitmap(wxICON(IDI_ICON_ACTUAL));
  btnActualSize->SetToolTip("Scale the image back to the original size.");
  btnActualSize->Disable();
  spnScale->Disable();
  btnSizer->Add(btnClose, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->AddSpacer(5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(spnScale, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "%", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnFitSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnActualSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);

  btnNext = new wxButton(this, wxID_ANY, "Next", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnNext->Bind(wxEVT_BUTTON, [this](wxCommandEvent &event) { Advance(1); },
                wxID_ANY);
  btnNext->SetBitmap(wxICON(IDI_ICON_NEXT));
  btnNext->SetToolTip("Load the next image from the same directory.");
  btnPrev = new wxButton(this, wxID_ANY, "Prev", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnPrev->Bind(wxEVT_BUTTON, &ImageViewPanel::OnPrevButtonClick, this);
  btnPrev->SetBitmap(wxICON(IDI_ICON_PREV));
  btnPrev->SetToolTip("Load the previous image from the same directory.");

  btnAuto = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnAuto->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnAuto->SetBitmap(wxICON(IDI_ICON_AUTO));
  btnAuto->SetBitmapPressed(wxICON(IDI_ICON_PAUSE));
  btnAuto->SetToolTip("Auto advance to the next image");

  btnRandom = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                                 wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnRandom->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnRandom->SetBitmap(wxICON(IDI_ICON_RANDOM));
  btnRandom->SetToolTip("Advance image randomly");

  spnRefreshTime = new wxSpinCtrl(this, wxID_ANY);
  spnRefreshTime->SetValue(30);
  spnRefreshTime->SetRange(1, 3000);
  spnRefreshTime->SetToolTip("Set the time interval (seconds) between the "
                             "current image to the next one.");

  btnSizer->Add(btnPrev, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnNext, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnAuto, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(spnRefreshTime, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "s", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnRandom, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  auto btnSave = new wxButton(this, wxID_ANY, "Save As", wxDefaultPosition,
                              wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnSave->SetBitmap(wxICON(IDI_ICON_SAVE));
  btnSave->SetToolTip("Save file as");
  btnSave->Bind(wxEVT_BUTTON, &ImageViewPanel::OnSaveButtonClick, this);

  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(btnSave, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  outerSizer->Add(btnSizer, 0, wxALL, 10);
  outerSizer->Add(new wxStaticLine(this), 0,
                  wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 10);
  scrollPanel = new wxScrolledWindow(this, wxID_ANY);
  scrollSizer = new wxBoxSizer(wxVERTICAL);

  scrollPanel->SetSizerAndFit(scrollSizer);
  scrollPanel->SetScrollRate(10, 10);

  outerSizer->Add(scrollPanel, 1, wxEXPAND | wxALL, 10);
  SetSizer(outerSizer);

  auto entryParent = entry->Parent();
  for (int i = 0; i < entryParent->Count(); ++i) {
    Entry *childEntry = (*entryParent)[i];

    if (childEntry->IsDirectory())
      continue;
    auto ext = childEntry->Name().AfterLast('.').Lower();

    if (ext != "jpg" && ext != "jpeg" && ext != "png" && ext != "gif")
      continue;

    entries.push_back(childEntry);
  }

  entryIter = std::find(entries.begin(), entries.end(), entry);

  bitmapStatic = new wxStaticBitmap(scrollPanel, wxID_ANY, wxBitmap(image));
  scrollSizer->Add(bitmapStatic);

  Bind(wxEVT_TIMER, &ImageViewPanel::OnTimerNotify, this);

  if (entries.size() > 0)
    random = std::uniform_int_distribution<int>(1, entries.size());
  else
    random = std::uniform_int_distribution<int>(0, 1);

  #ifdef USE_CIMG
    cimgImage = CImgFromWxImage(image);
  #endif
}

void ImageViewPanel::OnCloseButtonClick(wxCommandEvent &event) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->GetSelection();
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->DeletePage(currentPage);
}

void ImageViewPanel::OnScaleValueChanged(wxSpinEvent &event) {
  RefreshImage(event.GetPosition());
}

void ImageViewPanel::OnBtnActualSizePressed(wxCommandEvent &event) {
  spnScale->SetValue(100);
  RefreshImage();
}

void ImageViewPanel::OnBtnFitSizeToggle(wxCommandEvent &event) {
  if (btnFitSize->GetValue()) {
    FitImage();
    btnActualSize->Disable();
    spnScale->Disable();
  } else {
    btnActualSize->Enable();
    spnScale->Enable();
  }
}

void ImageViewPanel::FitImage() {
  auto ctrlSize = scrollPanel->GetSize();
  auto imageSize = image.GetSize();

  auto widthPercent = (ctrlSize.GetWidth() * 100) / imageSize.GetWidth();
  auto heightPercent = (ctrlSize.GetHeight() * 100) / imageSize.GetHeight();

  auto &percentage =
      widthPercent < heightPercent ? widthPercent : heightPercent;
  percentage = percentage > 200 ? 200 : percentage;
  spnScale->SetValue(percentage);
  RefreshImage(percentage);
}

void ImageViewPanel::RefreshImage(const int &percentage) {
  if (percentage == 100) {
    bitmapStatic->SetBitmap(wxBitmap(image));
  } else {
    int width = (image.GetWidth() * percentage) / 100;
    int height = (image.GetHeight() * percentage) / 100;

    bitmapStatic->SetBitmap(wxBitmap(CreateScaledImage(width, height, true)));
  }
  scrollSizer->FitInside(scrollPanel);
  spnScale->SetValue(percentage);
  Refresh();
  Update();
}

void ImageViewPanel::OnNextButtonClick(wxCommandEvent &event) { Advance(1); }

void ImageViewPanel::OnPrevButtonClick(wxCommandEvent &event) { Advance(-1); }

void ImageViewPanel::SetImageEntry(Entry *entry) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->GetSelection();
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->SetPageText(currentPage, entry->Name());

  image = entry->CreateImage();
#ifdef USE_CIMG
  cimgImage = CImgFromWxImage(image);
  if (image.HasAlpha()) {
    cimgAlpha = CImgAlphaFromWxImage(image);
  }
#endif
  if (btnFitSize->GetValue())
    FitImage();
  else
    RefreshImage();
}

bool ImageViewPanel::Layout() {
  wxPanel::Layout();
  if (btnFitSize->GetValue())
    FitImage();
  return false;
}

void ImageViewPanel::Advance(const int &step) {
  if (step == 0)
    return;
  bool isForward = step >= 0;
  for (int i = 0; i < std::abs(step); i++) {
    if (isForward) {
      entryIter++;
      if (entryIter == entries.end())
        entryIter = entries.begin();
    } else {
      entryIter--;
      if (entryIter < entries.begin())
        entryIter = entries.end() - 1;
    }
  }

  SetImageEntry(*entryIter);
}

void ImageViewPanel::OnBtnAutoToggle(wxCommandEvent &event) {
  if (btnAuto->GetValue()) {
    timer.Start(spnRefreshTime->GetValue() * 1000);
    btnRandom->Disable();
    spnRefreshTime->Disable();
    btnPrev->Disable();
    btnNext->Disable();
  } else {
    timer.Stop();
    btnRandom->Enable();
    spnRefreshTime->Enable();
    btnPrev->Enable();
    btnNext->Enable();
  }
}

void ImageViewPanel::OnTimerNotify(wxTimerEvent &timer) {
  Advance(btnRandom->GetValue() ? random(randomEngine) : 1);
}

void ImageViewPanel::OnSaveButtonClick(wxCommandEvent &event) {
  Entry *entry = *entryIter;
  wxFileDialog dialog(this, "Save As...", filename.GetPath(), entry->Name(),
                      wxFileSelectorDefaultWildcardStr, wxFD_SAVE);
  if (dialog.ShowModal() != wxID_OK)
    return;

  filename = dialog.GetPath();
  wxFileOutputStream output(dialog.GetPath());
  entry->WriteStream(output);
  output.Close();
}

void ImageViewPanel::SwitchToTheActualImage() { SetImageEntry(*entryIter); }

wxImage ImageViewPanel::CreateScaledImage(const unsigned int &width,
                                          const unsigned int &height,
                                          const bool isHighQuality) {
  if (width == 0 || height == 0)
    return wxImage(1, 1);

  if (!isHighQuality)
    return image.Scale(width, height);
#ifdef USE_CIMG
  constexpr auto quality = 6;
  auto scaled =
      cimgImage.get_resize(width, height, 1, cimgImage.spectrum(), quality);

  if (image.HasAlpha()) {
    auto scaledAlpha =
        cimgAlpha.get_resize(width, height, 1, cimgAlpha.spectrum(), quality);
    return wxImageFromCImg(scaled, &scaledAlpha);
  } else {
    return wxImageFromCImg(scaled);
  }
#else
  return image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
#endif
}

#ifdef USE_CIMG
/* FIXME:
Currently CImg is used for image processing. CImg expects deinterleaved image
data which is different than what wxImage expects. This results in having to
juggle arround the data buffer, dimension value, etc. to make things work.
Certainly it causes performance impacts.
CImg should be replaced with other code that process interleaved image
buffer.

This option is disabled by default due to performance problems.
*/

CImg<unsigned char> ImageViewPanel::CImgFromWxImage(const wxImage &image) {
  CImg<unsigned char> output(image.GetData(), 3, image.GetWidth(),
                             image.GetHeight(), 1);
  output.permute_axes("yzcx");
  return output;
}

CImg<unsigned char> ImageViewPanel::CImgAlphaFromWxImage(const wxImage &image) {
  if (!image.HasAlpha())
    return CImg<unsigned char>();

  return CImg<unsigned char>(image.GetAlpha(), image.GetWidth(),
                             image.GetHeight(), 1, 1);
}

wxImage ImageViewPanel::wxImageFromCImg(const CImg<unsigned char> &img,
                                        CImg<unsigned char> *alpha) {
  auto width = img.width();
  auto height = img.height();
  auto dataSize = width * height * 3;

  CImg<unsigned char> rearranged = img.get_permute_axes("cxyz");

  auto data = (unsigned char *)malloc(dataSize);
  memcpy(data, rearranged.data(), dataSize);

  wxImage output(width, height);
  output.SetData(data);

  if (alpha != nullptr) {
    auto alphaSize = width * height;

    auto alphaData = (unsigned char *)malloc(alphaSize);
    memcpy(alphaData, alpha->data(), alphaSize);

    output.SetAlpha(alphaData);
  }
  return output;
}
#endif
<commit_msg>wrong tab get renamed.<commit_after>#pragma GCC diagnostic ignored "-Wwrite-strings"

#include "ImageViewPanel.h"

#include "res/btn_actual.xpm"
#include "res/btn_auto.xpm"
#include "res/btn_close.xpm"
#include "res/btn_fit.xpm"
#include "res/btn_next.xpm"
#include "res/btn_pause.xpm"
#include "res/btn_prev.xpm"
#include "res/btn_random.xpm"
#include "res/btn_save.xpm"
#include "res/img_picture.xpm"
#include <algorithm>
#include <cmath>
#include <ctime>
#include <wx/mstream.h>
#include <wx/notebook.h>
#include <wx/statline.h>
#include <wx/wfstream.h>

#ifdef USE_CIMG
using cimg_library::CImg;
#endif

ImageViewPanel::ImageViewPanel(wxWindow *parent, Entry *entry, wxWindowID id,
                               const wxPoint &pos, const wxSize &size,
                               long style, const wxString &name)
    : wxPanel(parent, id, pos, size, style, name), timer(this),
      randomEngine(r()), image(IMG_PICTURE_xpm) {
  auto outerSizer = new wxBoxSizer(wxVERTICAL);
  auto btnSizer = new wxBoxSizer(wxHORIZONTAL);
  btnClose = new wxButton(this, wxID_CLOSE, "Close", wxDefaultPosition,
                          wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnClose->SetBitmap(wxICON(IDI_ICON_CLOSE));
  btnClose->SetToolTip("Close this tab.");
  btnClose->Bind(wxEVT_BUTTON, &ImageViewPanel::OnCloseButtonClick, this);

  spnScale = new wxSpinCtrl(this, wxID_ANY);
  spnScale->SetToolTip("Set the percentage the image is scaled to.");
  spnScale->SetValue(100);
  spnScale->SetRange(1, 200);
  spnScale->Bind(wxEVT_SPINCTRL, &ImageViewPanel::OnScaleValueChanged, this);

  btnFitSize = new wxToggleButton(this, wxID_ANY, "Fit", wxDefaultPosition,
                                  wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnFitSize->SetBitmap(wxICON(IDI_ICON_FIT));
  btnFitSize->SetToolTip("Fit the image inside the tab.");

  btnFitSize->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnFitSizeToggle,
                   this);
  btnFitSize->SetValue(true);

  btnActualSize = new wxButton(this, wxID_ANY, "Actual", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnActualSize->Bind(wxEVT_BUTTON, &ImageViewPanel::OnBtnActualSizePressed,
                      this);
  btnActualSize->SetBitmap(wxICON(IDI_ICON_ACTUAL));
  btnActualSize->SetToolTip("Scale the image back to the original size.");
  btnActualSize->Disable();
  spnScale->Disable();
  btnSizer->Add(btnClose, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->AddSpacer(5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(spnScale, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "%", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnFitSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnActualSize, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);

  btnNext = new wxButton(this, wxID_ANY, "Next", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnNext->Bind(wxEVT_BUTTON, [this](wxCommandEvent &event) { Advance(1); },
                wxID_ANY);
  btnNext->SetBitmap(wxICON(IDI_ICON_NEXT));
  btnNext->SetToolTip("Load the next image from the same directory.");
  btnPrev = new wxButton(this, wxID_ANY, "Prev", wxDefaultPosition,
                         wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnPrev->Bind(wxEVT_BUTTON, &ImageViewPanel::OnPrevButtonClick, this);
  btnPrev->SetBitmap(wxICON(IDI_ICON_PREV));
  btnPrev->SetToolTip("Load the previous image from the same directory.");

  btnAuto = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                               wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnAuto->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnAuto->SetBitmap(wxICON(IDI_ICON_AUTO));
  btnAuto->SetBitmapPressed(wxICON(IDI_ICON_PAUSE));
  btnAuto->SetToolTip("Auto advance to the next image");

  btnRandom = new wxToggleButton(this, wxID_ANY, "Auto", wxDefaultPosition,
                                 wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnRandom->Bind(wxEVT_TOGGLEBUTTON, &ImageViewPanel::OnBtnAutoToggle, this);
  btnRandom->SetBitmap(wxICON(IDI_ICON_RANDOM));
  btnRandom->SetToolTip("Advance image randomly");

  spnRefreshTime = new wxSpinCtrl(this, wxID_ANY);
  spnRefreshTime->SetValue(30);
  spnRefreshTime->SetRange(1, 3000);
  spnRefreshTime->SetToolTip("Set the time interval (seconds) between the "
                             "current image to the next one.");

  btnSizer->Add(btnPrev, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnNext, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnAuto, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(spnRefreshTime, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
  btnSizer->Add(new wxStaticText(this, wxID_ANY, "s", wxDefaultPosition,
                                 wxDefaultSize, wxST_NO_AUTORESIZE),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
  btnSizer->Add(btnRandom, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  auto btnSave = new wxButton(this, wxID_ANY, "Save As", wxDefaultPosition,
                              wxDefaultSize, wxBU_EXACTFIT | wxBU_NOTEXT);
  btnSave->SetBitmap(wxICON(IDI_ICON_SAVE));
  btnSave->SetToolTip("Save file as");
  btnSave->Bind(wxEVT_BUTTON, &ImageViewPanel::OnSaveButtonClick, this);

  btnSizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
                                 wxDefaultSize, wxLI_VERTICAL),
                0, wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 5);
  btnSizer->Add(btnSave, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);

  outerSizer->Add(btnSizer, 0, wxALL, 10);
  outerSizer->Add(new wxStaticLine(this), 0,
                  wxRIGHT | wxALIGN_CENTER_VERTICAL | wxEXPAND, 10);
  scrollPanel = new wxScrolledWindow(this, wxID_ANY);
  scrollSizer = new wxBoxSizer(wxVERTICAL);

  scrollPanel->SetSizerAndFit(scrollSizer);
  scrollPanel->SetScrollRate(10, 10);

  outerSizer->Add(scrollPanel, 1, wxEXPAND | wxALL, 10);
  SetSizer(outerSizer);

  auto entryParent = entry->Parent();
  for (int i = 0; i < entryParent->Count(); ++i) {
    Entry *childEntry = (*entryParent)[i];

    if (childEntry->IsDirectory())
      continue;
    auto ext = childEntry->Name().AfterLast('.').Lower();

    if (ext != "jpg" && ext != "jpeg" && ext != "png" && ext != "gif")
      continue;

    entries.push_back(childEntry);
  }

  entryIter = std::find(entries.begin(), entries.end(), entry);

  bitmapStatic = new wxStaticBitmap(scrollPanel, wxID_ANY, wxBitmap(image));
  scrollSizer->Add(bitmapStatic);

  Bind(wxEVT_TIMER, &ImageViewPanel::OnTimerNotify, this);

  if (entries.size() > 0)
    random = std::uniform_int_distribution<int>(1, entries.size());
  else
    random = std::uniform_int_distribution<int>(0, 1);

  #ifdef USE_CIMG
    cimgImage = CImgFromWxImage(image);
  #endif
}

void ImageViewPanel::OnCloseButtonClick(wxCommandEvent &event) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->FindPage(this);
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->DeletePage(currentPage);
}

void ImageViewPanel::OnScaleValueChanged(wxSpinEvent &event) {
  RefreshImage(event.GetPosition());
}

void ImageViewPanel::OnBtnActualSizePressed(wxCommandEvent &event) {
  spnScale->SetValue(100);
  RefreshImage();
}

void ImageViewPanel::OnBtnFitSizeToggle(wxCommandEvent &event) {
  if (btnFitSize->GetValue()) {
    FitImage();
    btnActualSize->Disable();
    spnScale->Disable();
  } else {
    btnActualSize->Enable();
    spnScale->Enable();
  }
}

void ImageViewPanel::FitImage() {
  auto ctrlSize = scrollPanel->GetSize();
  auto imageSize = image.GetSize();

  auto widthPercent = (ctrlSize.GetWidth() * 100) / imageSize.GetWidth();
  auto heightPercent = (ctrlSize.GetHeight() * 100) / imageSize.GetHeight();

  auto &percentage =
      widthPercent < heightPercent ? widthPercent : heightPercent;
  percentage = percentage > 200 ? 200 : percentage;
  spnScale->SetValue(percentage);
  RefreshImage(percentage);
}

void ImageViewPanel::RefreshImage(const int &percentage) {
  if (percentage == 100) {
    bitmapStatic->SetBitmap(wxBitmap(image));
  } else {
    int width = (image.GetWidth() * percentage) / 100;
    int height = (image.GetHeight() * percentage) / 100;

    bitmapStatic->SetBitmap(wxBitmap(CreateScaledImage(width, height, true)));
  }
  scrollSizer->FitInside(scrollPanel);
  spnScale->SetValue(percentage);
  Refresh();
  Update();
}

void ImageViewPanel::OnNextButtonClick(wxCommandEvent &event) { Advance(1); }

void ImageViewPanel::OnPrevButtonClick(wxCommandEvent &event) { Advance(-1); }

void ImageViewPanel::SetImageEntry(Entry *entry) {
  auto notebook = dynamic_cast<wxNotebook *>(GetParent());
  if (!notebook)
    return;
  auto currentPage = notebook->FindPage(this);
  if (currentPage == wxNOT_FOUND)
    return;
  notebook->SetPageText(currentPage, entry->Name());

  image = entry->CreateImage();
#ifdef USE_CIMG
  cimgImage = CImgFromWxImage(image);
  if (image.HasAlpha()) {
    cimgAlpha = CImgAlphaFromWxImage(image);
  }
#endif
  if (btnFitSize->GetValue())
    FitImage();
  else
    RefreshImage();
}

bool ImageViewPanel::Layout() {
  wxPanel::Layout();
  if (btnFitSize->GetValue())
    FitImage();
  return false;
}

void ImageViewPanel::Advance(const int &step) {
  if (step == 0)
    return;
  bool isForward = step >= 0;
  for (int i = 0; i < std::abs(step); i++) {
    if (isForward) {
      entryIter++;
      if (entryIter == entries.end())
        entryIter = entries.begin();
    } else {
      entryIter--;
      if (entryIter < entries.begin())
        entryIter = entries.end() - 1;
    }
  }

  SetImageEntry(*entryIter);
}

void ImageViewPanel::OnBtnAutoToggle(wxCommandEvent &event) {
  if (btnAuto->GetValue()) {
    timer.Start(spnRefreshTime->GetValue() * 1000);
    btnRandom->Disable();
    spnRefreshTime->Disable();
    btnPrev->Disable();
    btnNext->Disable();
  } else {
    timer.Stop();
    btnRandom->Enable();
    spnRefreshTime->Enable();
    btnPrev->Enable();
    btnNext->Enable();
  }
}

void ImageViewPanel::OnTimerNotify(wxTimerEvent &timer) {
  Advance(btnRandom->GetValue() ? random(randomEngine) : 1);
}

void ImageViewPanel::OnSaveButtonClick(wxCommandEvent &event) {
  Entry *entry = *entryIter;
  wxFileDialog dialog(this, "Save As...", filename.GetPath(), entry->Name(),
                      wxFileSelectorDefaultWildcardStr, wxFD_SAVE);
  if (dialog.ShowModal() != wxID_OK)
    return;

  filename = dialog.GetPath();
  wxFileOutputStream output(dialog.GetPath());
  entry->WriteStream(output);
  output.Close();
}

void ImageViewPanel::SwitchToTheActualImage() { SetImageEntry(*entryIter); }

wxImage ImageViewPanel::CreateScaledImage(const unsigned int &width,
                                          const unsigned int &height,
                                          const bool isHighQuality) {
  if (width == 0 || height == 0)
    return wxImage(1, 1);

  if (!isHighQuality)
    return image.Scale(width, height);
#ifdef USE_CIMG
  constexpr auto quality = 6;
  auto scaled =
      cimgImage.get_resize(width, height, 1, cimgImage.spectrum(), quality);

  if (image.HasAlpha()) {
    auto scaledAlpha =
        cimgAlpha.get_resize(width, height, 1, cimgAlpha.spectrum(), quality);
    return wxImageFromCImg(scaled, &scaledAlpha);
  } else {
    return wxImageFromCImg(scaled);
  }
#else
  return image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
#endif
}

#ifdef USE_CIMG
/* FIXME:
Currently CImg is used for image processing. CImg expects deinterleaved image
data which is different than what wxImage expects. This results in having to
juggle arround the data buffer, dimension value, etc. to make things work.
Certainly it causes performance impacts.
CImg should be replaced with other code that process interleaved image
buffer.

This option is disabled by default due to performance problems.
*/

CImg<unsigned char> ImageViewPanel::CImgFromWxImage(const wxImage &image) {
  CImg<unsigned char> output(image.GetData(), 3, image.GetWidth(),
                             image.GetHeight(), 1);
  output.permute_axes("yzcx");
  return output;
}

CImg<unsigned char> ImageViewPanel::CImgAlphaFromWxImage(const wxImage &image) {
  if (!image.HasAlpha())
    return CImg<unsigned char>();

  return CImg<unsigned char>(image.GetAlpha(), image.GetWidth(),
                             image.GetHeight(), 1, 1);
}

wxImage ImageViewPanel::wxImageFromCImg(const CImg<unsigned char> &img,
                                        CImg<unsigned char> *alpha) {
  auto width = img.width();
  auto height = img.height();
  auto dataSize = width * height * 3;

  CImg<unsigned char> rearranged = img.get_permute_axes("cxyz");

  auto data = (unsigned char *)malloc(dataSize);
  memcpy(data, rearranged.data(), dataSize);

  wxImage output(width, height);
  output.SetData(data);

  if (alpha != nullptr) {
    auto alphaSize = width * height;

    auto alphaData = (unsigned char *)malloc(alphaSize);
    memcpy(alphaData, alpha->data(), alphaSize);

    output.SetAlpha(alphaData);
  }
  return output;
}
#endif
<|endoftext|>


<commit_before>//
//  HFV.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 25/08/2019.
//  Copyright © 2019 Thomas Harte. All rights reserved.
//

#include "HFV.hpp"

using namespace Storage::MassStorage;

HFV::HFV(const std::string &file_name) : file_(file_name) {
	// Is the file a multiple of 512 bytes in size and larger than a floppy disk?
	const auto file_size = file_.stats().st_size;
	if(file_size & 511 || file_size <= 800*1024) throw std::exception();

	// TODO: check filing system for MFS, HFS or HFS+.
}

size_t HFV::get_block_size() {
	return 512;
}

size_t HFV::get_number_of_blocks() {
	return mapper_.get_number_of_blocks();
}

std::vector<uint8_t> HFV::get_block(size_t address) {
	const auto written = writes_.find(address);
	if(written != writes_.end()) return written->second;

	const auto source_address = mapper_.to_source_address(address);
	if(source_address >= 0 && size_t(source_address)*get_block_size() < size_t(file_.stats().st_size)) {
		const long file_offset = long(get_block_size()) * long(source_address);
		file_.seek(file_offset, SEEK_SET);
		return mapper_.convert_source_block(source_address, file_.read(get_block_size()));
	} else {
		return mapper_.convert_source_block(source_address);
	}
}

void HFV::set_block(size_t address, const std::vector<uint8_t> &contents) {
	writes_[address] = contents;

	printf("[%zu]: ", address);
	for(uint8_t v: contents) {
		printf("%02x ", v);
	}
	printf("\n");
//	const auto source_address = mapper_.to_source_address(address);
//	if(source_address >= 0 && size_t(source_address)*get_block_size() < size_t(file_.stats().st_size)) {
//		const long file_offset = long(get_block_size()) * long(source_address);
//		file_.seek(file_offset, SEEK_SET);
//		file_.write(contents);
//	}
}

void HFV::set_drive_type(Encodings::Macintosh::DriveType drive_type) {
	mapper_.set_drive_type(drive_type, size_t(file_.stats().st_size) / get_block_size());
}
<commit_msg>Enables write support.<commit_after>//
//  HFV.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 25/08/2019.
//  Copyright © 2019 Thomas Harte. All rights reserved.
//

#include "HFV.hpp"

using namespace Storage::MassStorage;

HFV::HFV(const std::string &file_name) : file_(file_name) {
	// Is the file a multiple of 512 bytes in size and larger than a floppy disk?
	const auto file_size = file_.stats().st_size;
	if(file_size & 511 || file_size <= 800*1024) throw std::exception();

	// TODO: check filing system for MFS, HFS or HFS+.
}

size_t HFV::get_block_size() {
	return 512;
}

size_t HFV::get_number_of_blocks() {
	return mapper_.get_number_of_blocks();
}

std::vector<uint8_t> HFV::get_block(size_t address) {
	const auto written = writes_.find(address);
	if(written != writes_.end()) return written->second;

	const auto source_address = mapper_.to_source_address(address);
	if(source_address >= 0 && size_t(source_address)*get_block_size() < size_t(file_.stats().st_size)) {
		const long file_offset = long(get_block_size()) * long(source_address);
		file_.seek(file_offset, SEEK_SET);
		return mapper_.convert_source_block(source_address, file_.read(get_block_size()));
	} else {
		return mapper_.convert_source_block(source_address);
	}
}

void HFV::set_block(size_t address, const std::vector<uint8_t> &contents) {
	const auto source_address = mapper_.to_source_address(address);
	if(source_address >= 0 && size_t(source_address)*get_block_size() < size_t(file_.stats().st_size)) {
		const long file_offset = long(get_block_size()) * long(source_address);
		file_.seek(file_offset, SEEK_SET);
		file_.write(contents);
	} else {
		writes_[address] = contents;
 	}
}

void HFV::set_drive_type(Encodings::Macintosh::DriveType drive_type) {
	mapper_.set_drive_type(drive_type, size_t(file_.stats().st_size) / get_block_size());
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2004-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "fboss/agent/hw/bcm/BcmPortGroup.h"
#include "fboss/agent/hw/bcm/BcmSwitch.h"
#include "fboss/agent/hw/bcm/BcmPortTable.h"
#include "fboss/agent/hw/bcm/BcmPort.h"
#include "fboss/agent/hw/bcm/BcmError.h"

#include "fboss/agent/state/Port.h"

extern "C" {
#include <opennsl/port.h>
}

namespace {
using facebook::fboss::BcmPortGroup;
using facebook::fboss::cfg::PortSpeed;
using facebook::fboss::FbossError;
BcmPortGroup::LaneMode neededLaneModeForSpeed(
    PortSpeed speed, PortSpeed maxLaneSpeed) {
  if (speed == PortSpeed::DEFAULT) {
    throw FbossError("Speed cannot be DEFAULT");
  }
  auto neededLanes =
    static_cast<uint32_t>(speed) / static_cast<uint32_t>(maxLaneSpeed);
  if (neededLanes <= 1) {
    return BcmPortGroup::LaneMode::QUAD;
  } else if (neededLanes == 2) {
    return BcmPortGroup::LaneMode::DUAL;
  } else if (neededLanes > 2 && neededLanes <= 4) {
    return BcmPortGroup::LaneMode::SINGLE;
  } else {
    throw FbossError("Cannot support speed ", speed);
  }
}

}

namespace facebook { namespace fboss {

BcmPortGroup::BcmPortGroup(BcmSwitch* hw,
                           BcmPort* controllingPort,
                           std::vector<BcmPort*> allPorts)
    : hw_(hw),
      controllingPort_(controllingPort),
      allPorts_(std::move(allPorts)) {

  if (allPorts_.size() != 4) {
    throw FbossError("Port groups must have exactly four members");
  }

  for (int i = 0; i < allPorts_.size(); ++i) {
    if (getLane(allPorts_[i]) != i) {
      throw FbossError("Ports passed in are not ordered by lane");
    }
  }

  // get the number of active lanes
  auto activeLanes = retrieveActiveLanes();
  switch (activeLanes) {
    case 1:
      laneMode_ = LaneMode::QUAD;
      break;
    case 2:
      laneMode_ = LaneMode::DUAL;
      break;
    case 4:
      laneMode_ = LaneMode::SINGLE;
      break;
    default:
      throw FbossError("Unexpected number of lanes retrieved for bcm port ",
                       controllingPort_->getBcmPortId());
  }

}

BcmPortGroup::~BcmPortGroup() {}

BcmPortGroup::LaneMode BcmPortGroup::calculateDesiredLaneMode(
    const std::vector<Port*>& ports, cfg::PortSpeed maxLaneSpeed) {
  auto desiredMode = LaneMode::QUAD;
  for (int lane = 0; lane < ports.size(); ++lane) {
    auto port = ports[lane];
    if (!port->isDisabled()) {
      auto neededMode = neededLaneModeForSpeed(port->getSpeed(), maxLaneSpeed);
      if (neededMode < desiredMode) {
        desiredMode = neededMode;
      }

      // Check that the lane is expected for SINGLE/DUAL modes
      if (desiredMode == LaneMode::SINGLE) {
        if (lane != 0) {
          throw FbossError("Only lane 0 can be enabled in SINGLE mode");
        }
      } else if (desiredMode == LaneMode::DUAL) {
        if (lane != 0 && lane != 2) {
          throw FbossError("Only lanes 0 or 2 can be enabled in DUAL mode");
        }
      }

      VLOG(3) << "Port " << port->getID() << " enabled with speed " <<
        static_cast<int>(port->getSpeed());
    }
  }
  return desiredMode;
}

BcmPortGroup::LaneMode BcmPortGroup::getDesiredLaneMode(
    const std::shared_ptr<SwitchState>& state) const {
  std::vector<Port*> ports;
  for (auto bcmPort : allPorts_) {
    auto swPort = bcmPort->getSwitchStatePort(state).get();
    // Make sure the ports support the configured speed.
    // We check this even if the port is disabled.
    if (!bcmPort->supportsSpeed(swPort->getSpeed())) {
      throw FbossError("Port ", swPort->getID(), " does not support speed ",
                       static_cast<int>(swPort->getSpeed()));
    }
    ports.push_back(swPort);
  }
  return calculateDesiredLaneMode(ports, controllingPort_->maxLaneSpeed());
}

uint8_t BcmPortGroup::getLane(const BcmPort* bcmPort) const {
  return bcmPort->getBcmPortId() - controllingPort_->getBcmPortId();
}

bool BcmPortGroup::validConfiguration(
    const std::shared_ptr<SwitchState>& state) const {
  try {
    getDesiredLaneMode(state);
  } catch (const std::exception& ex) {
    VLOG(1) << "Received exception determining lane mode: " << ex.what();
    return false;
  }
  return true;
}

void BcmPortGroup::reconfigureIfNeeded(
  const std::shared_ptr<SwitchState>& state) {
  // This logic is a bit messy. We could encode some notion of port
  // groups into the swith state somehow so it is easy to generate
  // deltas for these. For now, we need pass around the SwitchState
  // object and get the relevant ports manually.
  auto desiredLaneMode = getDesiredLaneMode(state);

  if (desiredLaneMode != laneMode_) {
    reconfigure(state, desiredLaneMode);
  }
}

void BcmPortGroup::reconfigure(
  const std::shared_ptr<SwitchState>& state,
  LaneMode newLaneMode
) {
  // The logic for this follows the steps required for flex-port support
  // outlined in the sdk documentation.
  VLOG(1) << "Reconfiguring port " << controllingPort_->getBcmPortId()
          << " from " << laneMode_ << " active ports to " << newLaneMode
          << " active ports";

  // 1. disable all group members
  for (auto& bcmPort : allPorts_) {
    auto swPort = bcmPort->getSwitchStatePort(state);
    bcmPort->disable(swPort);
  }

  // 2. remove all ports from the counter DMA and linkscan bitmaps
  // This is done in BcmPort::disable()

  // 3. set the opennslPortControlLanes setting
  setActiveLanes(newLaneMode);

  // 4. enable ports
  for (auto& bcmPort : allPorts_) {
    auto swPort = bcmPort->getSwitchStatePort(state);
    if (!swPort->isDisabled()) {
      bcmPort->enable(swPort);
    }
  }

  // 5. add ports to the counter DMA + linkscan
  // This is done in BcmPort::enable()
}

}} // namespace facebook::fboss
<commit_msg>port group fixes for 6.4.10<commit_after>/*
 *  Copyright (c) 2004-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "fboss/agent/hw/bcm/BcmPortGroup.h"
#include "fboss/agent/hw/bcm/BcmSwitch.h"
#include "fboss/agent/hw/bcm/BcmPortTable.h"
#include "fboss/agent/hw/bcm/BcmPort.h"
#include "fboss/agent/hw/bcm/BcmError.h"

#include "fboss/agent/state/Port.h"

extern "C" {
#include <opennsl/port.h>
}

namespace {
using facebook::fboss::BcmPortGroup;
using facebook::fboss::cfg::PortSpeed;
using facebook::fboss::FbossError;
BcmPortGroup::LaneMode neededLaneModeForSpeed(
    PortSpeed speed, PortSpeed maxLaneSpeed) {
  if (speed == PortSpeed::DEFAULT) {
    throw FbossError("Speed cannot be DEFAULT");
  }
  auto neededLanes =
    static_cast<uint32_t>(speed) / static_cast<uint32_t>(maxLaneSpeed);
  if (neededLanes <= 1) {
    return BcmPortGroup::LaneMode::QUAD;
  } else if (neededLanes == 2) {
    return BcmPortGroup::LaneMode::DUAL;
  } else if (neededLanes > 2 && neededLanes <= 4) {
    return BcmPortGroup::LaneMode::SINGLE;
  } else {
    throw FbossError("Cannot support speed ", speed);
  }
}

}

namespace facebook { namespace fboss {

BcmPortGroup::BcmPortGroup(BcmSwitch* hw,
                           BcmPort* controllingPort,
                           std::vector<BcmPort*> allPorts)
    : hw_(hw),
      controllingPort_(controllingPort),
      allPorts_(std::move(allPorts)) {

  if (allPorts_.size() != 4) {
    throw FbossError("Port groups must have exactly four members. Found ",
                     allPorts_.size());
  }

  for (int i = 0; i < allPorts_.size(); ++i) {
    if (getLane(allPorts_[i]) != i) {
      throw FbossError("Ports passed in are not ordered by lane");
    }
  }

  // get the number of active lanes
  auto activeLanes = retrieveActiveLanes();
  switch (activeLanes) {
    case 1:
      laneMode_ = LaneMode::QUAD;
      break;
    case 2:
      laneMode_ = LaneMode::DUAL;
      break;
    case 4:
      laneMode_ = LaneMode::SINGLE;
      break;
    default:
      throw FbossError("Unexpected number of lanes retrieved for bcm port ",
                       controllingPort_->getBcmPortId());
  }

}

BcmPortGroup::~BcmPortGroup() {}

BcmPortGroup::LaneMode BcmPortGroup::calculateDesiredLaneMode(
    const std::vector<Port*>& ports, cfg::PortSpeed maxLaneSpeed) {
  auto desiredMode = LaneMode::QUAD;
  for (int lane = 0; lane < ports.size(); ++lane) {
    auto port = ports[lane];
    if (!port->isDisabled()) {
      auto neededMode = neededLaneModeForSpeed(port->getSpeed(), maxLaneSpeed);
      if (neededMode < desiredMode) {
        desiredMode = neededMode;
      }

      // Check that the lane is expected for SINGLE/DUAL modes
      if (desiredMode == LaneMode::SINGLE) {
        if (lane != 0) {
          throw FbossError("Only lane 0 can be enabled in SINGLE mode");
        }
      } else if (desiredMode == LaneMode::DUAL) {
        if (lane != 0 && lane != 2) {
          throw FbossError("Only lanes 0 or 2 can be enabled in DUAL mode");
        }
      }

      VLOG(3) << "Port " << port->getID() << " enabled with speed " <<
        static_cast<int>(port->getSpeed());
    }
  }
  return desiredMode;
}

BcmPortGroup::LaneMode BcmPortGroup::getDesiredLaneMode(
    const std::shared_ptr<SwitchState>& state) const {
  std::vector<Port*> ports;
  for (auto bcmPort : allPorts_) {
    auto swPort = bcmPort->getSwitchStatePort(state).get();
    // Make sure the ports support the configured speed.
    // We check this even if the port is disabled.
    if (!bcmPort->supportsSpeed(swPort->getSpeed())) {
      throw FbossError("Port ", swPort->getID(), " does not support speed ",
                       static_cast<int>(swPort->getSpeed()));
    }
    ports.push_back(swPort);
  }
  return calculateDesiredLaneMode(ports, controllingPort_->maxLaneSpeed());
}

uint8_t BcmPortGroup::getLane(const BcmPort* bcmPort) const {
  return bcmPort->getBcmPortId() - controllingPort_->getBcmPortId();
}

bool BcmPortGroup::validConfiguration(
    const std::shared_ptr<SwitchState>& state) const {
  try {
    getDesiredLaneMode(state);
  } catch (const std::exception& ex) {
    VLOG(1) << "Received exception determining lane mode: " << ex.what();
    return false;
  }
  return true;
}

void BcmPortGroup::reconfigureIfNeeded(
  const std::shared_ptr<SwitchState>& state) {
  // This logic is a bit messy. We could encode some notion of port
  // groups into the swith state somehow so it is easy to generate
  // deltas for these. For now, we need pass around the SwitchState
  // object and get the relevant ports manually.
  auto desiredLaneMode = getDesiredLaneMode(state);

  if (desiredLaneMode != laneMode_) {
    reconfigure(state, desiredLaneMode);
  }
}

void BcmPortGroup::reconfigure(
  const std::shared_ptr<SwitchState>& state,
  LaneMode newLaneMode
) {
  // The logic for this follows the steps required for flex-port support
  // outlined in the sdk documentation.
  VLOG(1) << "Reconfiguring port " << controllingPort_->getBcmPortId()
          << " from " << laneMode_ << " active ports to " << newLaneMode
          << " active ports";

  // 1. disable all group members
  for (auto& bcmPort : allPorts_) {
    auto swPort = bcmPort->getSwitchStatePort(state);
    bcmPort->disable(swPort);
  }

  // 2. remove all ports from the counter DMA and linkscan bitmaps
  // This is done in BcmPort::disable()

  // 3. set the opennslPortControlLanes setting
  setActiveLanes(newLaneMode);

  // 4. enable ports
  for (auto& bcmPort : allPorts_) {
    auto swPort = bcmPort->getSwitchStatePort(state);
    if (!swPort->isDisabled()) {
      bcmPort->enable(swPort);
    }
  }

  // 5. add ports to the counter DMA + linkscan
  // This is done in BcmPort::enable()
}

}} // namespace facebook::fboss
<|endoftext|>


<commit_before>//
//  TapeParser.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 05/09/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#ifndef TapeParser_hpp
#define TapeParser_hpp

#include "../Tape.hpp"
#include "../../Disk/DigitalPhaseLockedLoop.hpp"

#include <memory>
#include <vector>
#include <assert.h>

namespace Storage {
namespace Tape {

template <typename SymbolType> class Parser {
	public:
		Parser() : has_next_symbol_(false), error_flag_(false) {}
		/// Resets the error flag.
		void reset_error_flag()		{	error_flag_ = false;		}
		/// @returns @c true if an error has occurred since the error flag was last reset; @c false otherwise.
		bool get_error_flag()		{	return error_flag_;			}

		/*!
			Asks the parser to continue taking pulses from the tape until either the subclass next declares a symbol
			or the tape runs out, returning the most-recently declared symbol.
		*/
		SymbolType get_next_symbol(const std::shared_ptr<Storage::Tape::Tape> &tape) {
			while(!has_next_symbol_ && !tape->is_at_end()) {
				process_pulse(tape->get_next_pulse());
			}
			if(tape->is_at_end()) mark_end();
			has_next_symbol_ = false;
			return next_symbol_;
		}

		/*!
			This class provides a single token of lookahead; return_symbol allows the single previous
			token supplied by get_next_symbol to be returned, in which case it will be the thing returned
			by the next call to get_next_symbol.
		*/
		void return_symbol(SymbolType symbol) {
			assert(!has_next_symbol_);
			has_next_symbol_ = true;
			next_symbol_ = symbol;
		}

		/*!
			@returns `true` if there is no data left on the tape and the WaveType queue has been exhausted; `false` otherwise.
		*/
		bool is_at_end(const std::shared_ptr<Storage::Tape::Tape> &tape) {
			return tape->is_at_end() && !has_next_symbol_;
		}

	protected:
		/*!
			Should be implemented by subclasses. Consumes @c pulse.
		*/
		virtual void process_pulse(const Storage::Tape::Tape::Pulse &pulse) = 0;

		/*!
			An optional implementation for subclasses; called to announce that the tape has ended: that
			no more process_pulse calls will occur.
		*/
		virtual void mark_end() {}

		/*!
			Sets @c symbol as the newly-recognised symbol.
		*/
		void push_symbol(SymbolType symbol) {
			has_next_symbol_ = true;
			next_symbol_ = symbol;
		}

		void set_error_flag() {
			error_flag_ = true;
		}

		bool error_flag_;
		SymbolType next_symbol_;
		bool has_next_symbol_;
};

/*!
	A partly-abstract base class to help in the authorship of tape format parsers;
	provides hooks for receipt of pulses, which are intended to be classified into waves,
	and for symbol identification from waves.

	Very optional, not intended to box in the approaches taken for analysis. See also
	the PLLParser.
*/
template <typename WaveType, typename SymbolType> class PulseClassificationParser: public Parser<SymbolType> {
	public:
		virtual void process_pulse(const Storage::Tape::Tape::Pulse &pulse) = 0;

		/*
			process_pulse should either call @c push_wave or to take no action.
		*/

	protected:
		/*!
			Sets @c symbol as the newly-recognised symbol and removes @c nunber_of_waves waves from the front of the list.

			Expected to be called by subclasses from @c process_pulse when it recognises that the first @c number_of_waves
			waves together represent @c symbol.
		*/
		void push_symbol(SymbolType symbol, int number_of_waves) {
			Parser<SymbolType>::push_symbol(symbol);
			remove_waves(number_of_waves);
		}

		/*!
			Adds @c wave to the back of the list of recognised waves and calls @c inspect_waves to check for a new symbol.

			Expected to be called by subclasses from @c process_pulse as and when recognised waves arise.
		*/
		void push_wave(WaveType wave) {
			wave_queue_.push_back(wave);
			inspect_waves(wave_queue_);
		}

		/*!
			Removes @c nunber_of_waves waves from the front of the list.

			Expected to be called by subclasses from @c process_pulse if it is recognised that the first set of waves
			do not form a valid symbol.
		*/
		void remove_waves(int number_of_waves) {
			wave_queue_.erase(wave_queue_.begin(), wave_queue_.begin()+number_of_waves);
		}

	private:
		/*!
			Should be implemented by subclasses. Inspects @c waves for a potential new symbol. If one is
			found should call @c push_symbol. May wish alternatively to call @c remove_waves to have entries
			removed from the start of @c waves that cannot form a valid symbol. Need not do anything while
			the waves at the start of @c waves may end up forming a symbol but the symbol is not yet complete.
		*/
		virtual void inspect_waves(const std::vector<WaveType> &waves) = 0;

		std::vector<WaveType> wave_queue_;
};

}
}

#endif /* TapeParser_hpp */
<commit_msg>Ensured that the mark_end step can't overwrite another recognised symbol.<commit_after>//
//  TapeParser.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 05/09/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#ifndef TapeParser_hpp
#define TapeParser_hpp

#include "../Tape.hpp"
#include "../../Disk/DigitalPhaseLockedLoop.hpp"

#include <memory>
#include <vector>
#include <assert.h>

namespace Storage {
namespace Tape {

template <typename SymbolType> class Parser {
	public:
		Parser() : has_next_symbol_(false), error_flag_(false) {}
		/// Resets the error flag.
		void reset_error_flag()		{	error_flag_ = false;		}
		/// @returns @c true if an error has occurred since the error flag was last reset; @c false otherwise.
		bool get_error_flag()		{	return error_flag_;			}

		/*!
			Asks the parser to continue taking pulses from the tape until either the subclass next declares a symbol
			or the tape runs out, returning the most-recently declared symbol.
		*/
		SymbolType get_next_symbol(const std::shared_ptr<Storage::Tape::Tape> &tape) {
			while(!has_next_symbol_ && !tape->is_at_end()) {
				process_pulse(tape->get_next_pulse());
			}
			if(!has_next_symbol_ && tape->is_at_end()) mark_end();
			has_next_symbol_ = false;
			return next_symbol_;
		}

		/*!
			This class provides a single token of lookahead; return_symbol allows the single previous
			token supplied by get_next_symbol to be returned, in which case it will be the thing returned
			by the next call to get_next_symbol.
		*/
		void return_symbol(SymbolType symbol) {
			assert(!has_next_symbol_);
			has_next_symbol_ = true;
			next_symbol_ = symbol;
		}

		/*!
			@returns `true` if there is no data left on the tape and the WaveType queue has been exhausted; `false` otherwise.
		*/
		bool is_at_end(const std::shared_ptr<Storage::Tape::Tape> &tape) {
			return tape->is_at_end() && !has_next_symbol_;
		}

	protected:
		/*!
			Should be implemented by subclasses. Consumes @c pulse.
		*/
		virtual void process_pulse(const Storage::Tape::Tape::Pulse &pulse) = 0;

		/*!
			An optional implementation for subclasses; called to announce that the tape has ended: that
			no more process_pulse calls will occur.
		*/
		virtual void mark_end() {}

		/*!
			Sets @c symbol as the newly-recognised symbol.
		*/
		void push_symbol(SymbolType symbol) {
			has_next_symbol_ = true;
			next_symbol_ = symbol;
		}

		void set_error_flag() {
			error_flag_ = true;
		}

		bool error_flag_;
		SymbolType next_symbol_;
		bool has_next_symbol_;
};

/*!
	A partly-abstract base class to help in the authorship of tape format parsers;
	provides hooks for receipt of pulses, which are intended to be classified into waves,
	and for symbol identification from waves.

	Very optional, not intended to box in the approaches taken for analysis. See also
	the PLLParser.
*/
template <typename WaveType, typename SymbolType> class PulseClassificationParser: public Parser<SymbolType> {
	public:
		virtual void process_pulse(const Storage::Tape::Tape::Pulse &pulse) = 0;

		/*
			process_pulse should either call @c push_wave or to take no action.
		*/

	protected:
		/*!
			Sets @c symbol as the newly-recognised symbol and removes @c nunber_of_waves waves from the front of the list.

			Expected to be called by subclasses from @c process_pulse when it recognises that the first @c number_of_waves
			waves together represent @c symbol.
		*/
		void push_symbol(SymbolType symbol, int number_of_waves) {
			Parser<SymbolType>::push_symbol(symbol);
			remove_waves(number_of_waves);
		}

		/*!
			Adds @c wave to the back of the list of recognised waves and calls @c inspect_waves to check for a new symbol.

			Expected to be called by subclasses from @c process_pulse as and when recognised waves arise.
		*/
		void push_wave(WaveType wave) {
			wave_queue_.push_back(wave);
			inspect_waves(wave_queue_);
		}

		/*!
			Removes @c nunber_of_waves waves from the front of the list.

			Expected to be called by subclasses from @c process_pulse if it is recognised that the first set of waves
			do not form a valid symbol.
		*/
		void remove_waves(int number_of_waves) {
			wave_queue_.erase(wave_queue_.begin(), wave_queue_.begin()+number_of_waves);
		}

	private:
		/*!
			Should be implemented by subclasses. Inspects @c waves for a potential new symbol. If one is
			found should call @c push_symbol. May wish alternatively to call @c remove_waves to have entries
			removed from the start of @c waves that cannot form a valid symbol. Need not do anything while
			the waves at the start of @c waves may end up forming a symbol but the symbol is not yet complete.
		*/
		virtual void inspect_waves(const std::vector<WaveType> &waves) = 0;

		std::vector<WaveType> wave_queue_;
};

}
}

#endif /* TapeParser_hpp */
<|endoftext|>


<commit_before>/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#include <gflags/gflags.h>
#include <glog/logging.h>
#include <gtest/gtest.h>

#include <cmath>

#include "paddle/fluid/inference/tests/api/tester_helper.h"

namespace paddle {
namespace inference {

TEST(AnalysisPredictor, use_gpu) {
  std::string model_dir = FLAGS_infer_model + "/" + "model";
  AnalysisConfig config;
  config.EnableUseGpu(100, 0);
  config.SetModel(model_dir + "/model", model_dir + "/params");
  config.EnableLiteEngine(paddle::AnalysisConfig::Precision::kFloat32, true);

  std::vector<PaddleTensor> inputs;
  auto predictor = CreatePaddlePredictor(config);
  const int batch = 1;
  const int channel = 3;
  const int height = 318;
  const int width = 318;
  const int input_num = batch * channel * height * width;
  std::vector<float> input(input_num, 1);

  PaddleTensor in;
  in.shape = {batch, channel, height, width};
  in.data =
      PaddleBuf(static_cast<void*>(input.data()), input_num * sizeof(float));
  in.dtype = PaddleDType::FLOAT32;
  inputs.emplace_back(in);

  std::vector<PaddleTensor> outputs;
  ASSERT_TRUE(predictor->Run(inputs, &outputs));

  const std::vector<float> truth_values = {
      127.779f,  738.165f,  1013.22f,  -438.17f,  366.401f,  927.659f,
      736.222f,  -633.684f, -329.927f, -430.155f, -633.062f, -146.548f,
      -1324.28f, -1349.36f, -242.675f, 117.448f,  -801.723f, -391.514f,
      -404.818f, 454.16f,   515.48f,   -133.031f, 69.293f,   590.096f,
      -1434.69f, -1070.89f, 307.074f,  400.525f,  -316.12f,  -587.125f,
      -161.056f, 800.363f,  -96.4708f, 748.706f,  868.174f,  -447.938f,
      112.737f,  1127.2f,   47.4355f,  677.72f,   593.186f,  -336.4f,
      551.362f,  397.823f,  78.3979f,  -715.398f, 405.969f,  404.256f,
      246.019f,  -8.42969f, 131.365f,  -648.051f};

  const size_t expected_size = 1;
  EXPECT_EQ(outputs.size(), expected_size);
  float* data_o = static_cast<float*>(outputs[0].data.data());
  for (size_t j = 0; j < outputs[0].data.length() / sizeof(float); j += 10) {
    EXPECT_NEAR((data_o[j] - truth_values[j / 10]) / truth_values[j / 10], 0.,
                12e-5);
  }
}

#ifdef LITE_SUBGRAPH_WITH_XPU
TEST(AnalysisPredictor, use_xpu) {
  std::string model_dir = FLAGS_infer_model + "/" + "model";
  AnalysisConfig config;
  config.EnableLiteEngine(paddle::AnalysisConfig::Precision::kFloat32, true);
  config.EnableXpu(100);
  config.SetModel(model_dir + "/model", model_dir + "/params");

  std::vector<PaddleTensor> inputs;
  auto predictor = CreatePaddlePredictor(config);
  const int batch = 1;
  const int channel = 3;
  const int height = 318;
  const int width = 318;
  const int input_num = batch * channel * height * width;
  std::vector<float> input(input_num, 1);

  PaddleTensor in;
  in.shape = {batch, channel, height, width};
  in.data =
      PaddleBuf(static_cast<void*>(input.data()), input_num * sizeof(float));
  in.dtype = PaddleDType::FLOAT32;
  inputs.emplace_back(in);

  std::vector<PaddleTensor> outputs;
  ASSERT_TRUE(predictor->Run(inputs, &outputs));

  const std::vector<float> truth_values = {
      127.84,   738.088,  1013.22,  -438.055, 366.451,  927.585,  736.341,
      -633.776, -329.904, -430.149, -633.082, -146.597, -1324.19, -1349.29,
      -242.68,  117.541,  -801.704, -391.428, -404.756, 453.995,  515.373,
      -133.003, 69.3941,  590.056,  -1434.66, -1070.81, 307.093,  400.463,
      -316.094, -587.089, -161.033, 800.357,  -96.4212, 748.706,  868.226,
      -447.936, 112.782,  1127.24,  47.4587,  677.698,  593.126,  -336.462,
      551.328,  397.816,  78.3572,  -715.269, 406.002,  404.149,  246.067,
      -8.4649,  131.345,  -647.951,
  };

  const size_t expected_size = 1;
  EXPECT_EQ(outputs.size(), expected_size);
  float* data_o = static_cast<float*>(outputs[0].data.data());
  for (size_t j = 0; j < outputs[0].data.length() / sizeof(float); j += 10) {
    EXPECT_NEAR((data_o[j] - truth_values[j / 10]) / truth_values[j / 10], 0.,
                10e-5);
  }
}
#endif

}  // namespace inference
}  // namespace paddle

namespace paddle_infer {

TEST(Predictor, use_gpu) {
  std::string model_dir = FLAGS_infer_model + "/" + "model";
  Config config;
  config.EnableUseGpu(100, 0);
  config.SetModel(model_dir + "/model", model_dir + "/params");
  config.EnableLiteEngine(PrecisionType::kFloat32);

  auto predictor = CreatePredictor(config);
  const int batch = 1;
  const int channel = 3;
  const int height = 318;
  const int width = 318;
  const int input_num = batch * channel * height * width;
  std::vector<float> input(input_num, 1);

  auto input_names = predictor->GetInputNames();
  auto input_t = predictor->GetInputHandle(input_names[0]);

  input_t->Reshape({batch, channel, height, width});
  input_t->CopyFromCpu(input.data());
  predictor->Run();

  auto output_names = predictor->GetOutputNames();
  auto output_t = predictor->GetOutputHandle(output_names[0]);
  std::vector<int> output_shape = output_t->shape();
  size_t out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                                   std::multiplies<int>());

  std::vector<float> out_data;
  out_data.resize(out_num);
  output_t->CopyToCpu(out_data.data());

  const std::vector<float> truth_values = {
      127.779f,  738.165f,  1013.22f,  -438.17f,  366.401f,  927.659f,
      736.222f,  -633.684f, -329.927f, -430.155f, -633.062f, -146.548f,
      -1324.28f, -1349.36f, -242.675f, 117.448f,  -801.723f, -391.514f,
      -404.818f, 454.16f,   515.48f,   -133.031f, 69.293f,   590.096f,
      -1434.69f, -1070.89f, 307.074f,  400.525f,  -316.12f,  -587.125f,
      -161.056f, 800.363f,  -96.4708f, 748.706f,  868.174f,  -447.938f,
      112.737f,  1127.2f,   47.4355f,  677.72f,   593.186f,  -336.4f,
      551.362f,  397.823f,  78.3979f,  -715.398f, 405.969f,  404.256f,
      246.019f,  -8.42969f, 131.365f,  -648.051f};

  float* data_o = out_data.data();
  for (size_t j = 0; j < out_num; j += 10) {
    EXPECT_NEAR((data_o[j] - truth_values[j / 10]) / truth_values[j / 10], 0.,
                10e-5);
  }
}

}  // namespace paddle_infer
<commit_msg>[XPU] Remove lite_xpu ut lite_resnet50_test since fusion pass changes introduced precision diff. test=develop (#30122)<commit_after>/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#include <gflags/gflags.h>
#include <glog/logging.h>
#include <gtest/gtest.h>

#include <cmath>

#include "paddle/fluid/inference/tests/api/tester_helper.h"

namespace paddle {
namespace inference {

TEST(AnalysisPredictor, use_gpu) {
  std::string model_dir = FLAGS_infer_model + "/" + "model";
  AnalysisConfig config;
  config.EnableUseGpu(100, 0);
  config.SetModel(model_dir + "/model", model_dir + "/params");
  config.EnableLiteEngine(paddle::AnalysisConfig::Precision::kFloat32, true);

  std::vector<PaddleTensor> inputs;
  auto predictor = CreatePaddlePredictor(config);
  const int batch = 1;
  const int channel = 3;
  const int height = 318;
  const int width = 318;
  const int input_num = batch * channel * height * width;
  std::vector<float> input(input_num, 1);

  PaddleTensor in;
  in.shape = {batch, channel, height, width};
  in.data =
      PaddleBuf(static_cast<void*>(input.data()), input_num * sizeof(float));
  in.dtype = PaddleDType::FLOAT32;
  inputs.emplace_back(in);

  std::vector<PaddleTensor> outputs;
  ASSERT_TRUE(predictor->Run(inputs, &outputs));

  const std::vector<float> truth_values = {
      127.779f,  738.165f,  1013.22f,  -438.17f,  366.401f,  927.659f,
      736.222f,  -633.684f, -329.927f, -430.155f, -633.062f, -146.548f,
      -1324.28f, -1349.36f, -242.675f, 117.448f,  -801.723f, -391.514f,
      -404.818f, 454.16f,   515.48f,   -133.031f, 69.293f,   590.096f,
      -1434.69f, -1070.89f, 307.074f,  400.525f,  -316.12f,  -587.125f,
      -161.056f, 800.363f,  -96.4708f, 748.706f,  868.174f,  -447.938f,
      112.737f,  1127.2f,   47.4355f,  677.72f,   593.186f,  -336.4f,
      551.362f,  397.823f,  78.3979f,  -715.398f, 405.969f,  404.256f,
      246.019f,  -8.42969f, 131.365f,  -648.051f};

  const size_t expected_size = 1;
  EXPECT_EQ(outputs.size(), expected_size);
  float* data_o = static_cast<float*>(outputs[0].data.data());
  for (size_t j = 0; j < outputs[0].data.length() / sizeof(float); j += 10) {
    EXPECT_NEAR((data_o[j] - truth_values[j / 10]) / truth_values[j / 10], 0.,
                12e-5);
  }
}

}  // namespace inference
}  // namespace paddle

namespace paddle_infer {

TEST(Predictor, use_gpu) {
  std::string model_dir = FLAGS_infer_model + "/" + "model";
  Config config;
  config.EnableUseGpu(100, 0);
  config.SetModel(model_dir + "/model", model_dir + "/params");
  config.EnableLiteEngine(PrecisionType::kFloat32);

  auto predictor = CreatePredictor(config);
  const int batch = 1;
  const int channel = 3;
  const int height = 318;
  const int width = 318;
  const int input_num = batch * channel * height * width;
  std::vector<float> input(input_num, 1);

  auto input_names = predictor->GetInputNames();
  auto input_t = predictor->GetInputHandle(input_names[0]);

  input_t->Reshape({batch, channel, height, width});
  input_t->CopyFromCpu(input.data());
  predictor->Run();

  auto output_names = predictor->GetOutputNames();
  auto output_t = predictor->GetOutputHandle(output_names[0]);
  std::vector<int> output_shape = output_t->shape();
  size_t out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                                   std::multiplies<int>());

  std::vector<float> out_data;
  out_data.resize(out_num);
  output_t->CopyToCpu(out_data.data());

  const std::vector<float> truth_values = {
      127.779f,  738.165f,  1013.22f,  -438.17f,  366.401f,  927.659f,
      736.222f,  -633.684f, -329.927f, -430.155f, -633.062f, -146.548f,
      -1324.28f, -1349.36f, -242.675f, 117.448f,  -801.723f, -391.514f,
      -404.818f, 454.16f,   515.48f,   -133.031f, 69.293f,   590.096f,
      -1434.69f, -1070.89f, 307.074f,  400.525f,  -316.12f,  -587.125f,
      -161.056f, 800.363f,  -96.4708f, 748.706f,  868.174f,  -447.938f,
      112.737f,  1127.2f,   47.4355f,  677.72f,   593.186f,  -336.4f,
      551.362f,  397.823f,  78.3979f,  -715.398f, 405.969f,  404.256f,
      246.019f,  -8.42969f, 131.365f,  -648.051f};

  float* data_o = out_data.data();
  for (size_t j = 0; j < out_num; j += 10) {
    EXPECT_NEAR((data_o[j] - truth_values[j / 10]) / truth_values[j / 10], 0.,
                10e-5);
  }
}

}  // namespace paddle_infer
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Andrew Duggan
 * Copyright (C) 2014 Synaptics Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <string>
#include <sstream>

#include "hiddevice.h"
#include "rmi4update.h"

#define VERSION_MAJOR		1
#define VERSION_MINOR		3
#define VERSION_SUBMINOR	2

#define RMI4UPDATE_GETOPTS	"hfd:t:pclv"

void printHelp(const char *prog_name)
{
	fprintf(stdout, "Usage: %s [OPTIONS] FIRMWAREFILE\n", prog_name);
	fprintf(stdout, "\t-h, --help\t\tPrint this message\n");
	fprintf(stdout, "\t-f, --force\t\tForce updating firmware even it the image provided is older\n\t\t\t\tthen the current firmware on the device.\n");
	fprintf(stdout, "\t-d, --device\t\thidraw device file associated with the device being updated.\n");
	fprintf(stdout, "\t-p, --fw-props\t\tPrint the firmware properties.\n");
	fprintf(stdout, "\t-c, --config-id\t\tPrint the config id.\n");
	fprintf(stdout, "\t-l, --lockdown\t\tPerform lockdown.\n");
	fprintf(stdout, "\t-v, --version\t\tPrint version number.\n");
	fprintf(stdout, "\t-t, --device-type\tFilter by device type [touchpad or touchscreen].\n");
}

void printVersion()
{
	fprintf(stdout, "rmi4update version %d.%d.%d\n",
		VERSION_MAJOR, VERSION_MINOR, VERSION_SUBMINOR);
}

int GetFirmwareProps(const char * deviceFile, std::string &props, bool configid)
{
	HIDDevice rmidevice;
	int rc = UPDATE_SUCCESS;
	std::stringstream ss;

	rc = rmidevice.Open(deviceFile);
	if (rc)
		return rc;

	rmidevice.ScanPDT(0x1);
	rmidevice.QueryBasicProperties();

	if (configid) {
		ss << std::hex << rmidevice.GetConfigID();
	} else {
		ss << rmidevice.GetFirmwareVersionMajor() << "."
			<< rmidevice.GetFirmwareVersionMinor() << "."
			<< rmidevice.GetFirmwareID();

		if (rmidevice.InBootloader())
			ss << " bootloader";
	}

	props = ss.str();

	return rc;
}

int main(int argc, char **argv)
{
	int rc;
	FirmwareImage image;
	int opt;
	int index;
	char *deviceName = NULL;
	const char *firmwareName = NULL;
	bool force = false;
	static struct option long_options[] = {
		{"help", 0, NULL, 'h'},
		{"force", 0, NULL, 'f'},
		{"device", 1, NULL, 'd'},
		{"fw-props", 0, NULL, 'p'},
		{"config-id", 0, NULL, 'c'},
		{"lockdown", 0, NULL, 'l'},
		{"version", 0, NULL, 'v'},
		{"device-type", 1, NULL, 't'},
		{0, 0, 0, 0},
	};
	bool printFirmwareProps = false;
	bool printConfigid = false;
	bool performLockdown = false;
	HIDDevice device;
	enum RMIDeviceType deviceType = RMI_DEVICE_TYPE_ANY;

	while ((opt = getopt_long(argc, argv, RMI4UPDATE_GETOPTS, long_options, &index)) != -1) {
		switch (opt) {
			case 'h':
				printHelp(argv[0]);
				return 0;
			case 'f':
				force = true;
				break;
			case 'd':
				deviceName = optarg;
				break;
			case 'p':
				printFirmwareProps = true;
				break;
			case 'c':
				printFirmwareProps = true;
				printConfigid = true;
				break;
			case 'l':
				performLockdown = true;
				break;
			case 't':
				if (!strcasecmp((const char *)optarg, "touchpad"))
					deviceType = RMI_DEVICE_TYPE_TOUCHPAD;
				else if (!strcasecmp((const char *)optarg, "touchscreen"))
					deviceType = RMI_DEVICE_TYPE_TOUCHSCREEN;
				break;
			case 'v':
				printVersion();
				return 0;
			default:
				break;

		}
	}

	if (printFirmwareProps) {
		std::string props;

		if (!deviceName) {
			fprintf(stderr, "Specifiy which device to query\n");
			return 1;
		}
		rc = GetFirmwareProps(deviceName, props, printConfigid);
		if (rc) {
			fprintf(stderr, "Failed to read properties from device: %s\n", update_err_to_string(rc));
			return 1;
		}
		fprintf(stdout, "%s\n", props.c_str());
		return 0;
	}

	if (optind < argc) {
		firmwareName = argv[optind];
	} else {
		printHelp(argv[0]);
		return -1;
	}

	rc = image.Initialize(firmwareName);
	if (rc != UPDATE_SUCCESS) {
		fprintf(stderr, "Failed to initialize the firmware image: %s\n", update_err_to_string(rc));
		return 1;
	}

	if (deviceName) {
		 rc = device.Open(deviceName);
		 if (rc) {
			fprintf(stderr, "%s: failed to initialize rmi device (%d): %s\n", argv[0], errno,
				strerror(errno));
			return 1;
		}
	} else {
		if (!device.FindDevice(deviceType))
			return 1;
	}

	RMI4Update update(device, image);
	rc = update.UpdateFirmware(force, performLockdown);

	if (rc != UPDATE_SUCCESS)
	{
		device.Reset();
		return 1;
	}

	return 0;
}
<commit_msg>Update version for v1.3.3 release<commit_after>/*
 * Copyright (C) 2014 Andrew Duggan
 * Copyright (C) 2014 Synaptics Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <string>
#include <sstream>

#include "hiddevice.h"
#include "rmi4update.h"

#define VERSION_MAJOR		1
#define VERSION_MINOR		3
#define VERSION_SUBMINOR	3

#define RMI4UPDATE_GETOPTS	"hfd:t:pclv"

void printHelp(const char *prog_name)
{
	fprintf(stdout, "Usage: %s [OPTIONS] FIRMWAREFILE\n", prog_name);
	fprintf(stdout, "\t-h, --help\t\tPrint this message\n");
	fprintf(stdout, "\t-f, --force\t\tForce updating firmware even it the image provided is older\n\t\t\t\tthen the current firmware on the device.\n");
	fprintf(stdout, "\t-d, --device\t\thidraw device file associated with the device being updated.\n");
	fprintf(stdout, "\t-p, --fw-props\t\tPrint the firmware properties.\n");
	fprintf(stdout, "\t-c, --config-id\t\tPrint the config id.\n");
	fprintf(stdout, "\t-l, --lockdown\t\tPerform lockdown.\n");
	fprintf(stdout, "\t-v, --version\t\tPrint version number.\n");
	fprintf(stdout, "\t-t, --device-type\tFilter by device type [touchpad or touchscreen].\n");
}

void printVersion()
{
	fprintf(stdout, "rmi4update version %d.%d.%d\n",
		VERSION_MAJOR, VERSION_MINOR, VERSION_SUBMINOR);
}

int GetFirmwareProps(const char * deviceFile, std::string &props, bool configid)
{
	HIDDevice rmidevice;
	int rc = UPDATE_SUCCESS;
	std::stringstream ss;

	rc = rmidevice.Open(deviceFile);
	if (rc)
		return rc;

	rmidevice.ScanPDT(0x1);
	rmidevice.QueryBasicProperties();

	if (configid) {
		ss << std::hex << rmidevice.GetConfigID();
	} else {
		ss << rmidevice.GetFirmwareVersionMajor() << "."
			<< rmidevice.GetFirmwareVersionMinor() << "."
			<< rmidevice.GetFirmwareID();

		if (rmidevice.InBootloader())
			ss << " bootloader";
	}

	props = ss.str();

	return rc;
}

int main(int argc, char **argv)
{
	int rc;
	FirmwareImage image;
	int opt;
	int index;
	char *deviceName = NULL;
	const char *firmwareName = NULL;
	bool force = false;
	static struct option long_options[] = {
		{"help", 0, NULL, 'h'},
		{"force", 0, NULL, 'f'},
		{"device", 1, NULL, 'd'},
		{"fw-props", 0, NULL, 'p'},
		{"config-id", 0, NULL, 'c'},
		{"lockdown", 0, NULL, 'l'},
		{"version", 0, NULL, 'v'},
		{"device-type", 1, NULL, 't'},
		{0, 0, 0, 0},
	};
	bool printFirmwareProps = false;
	bool printConfigid = false;
	bool performLockdown = false;
	HIDDevice device;
	enum RMIDeviceType deviceType = RMI_DEVICE_TYPE_ANY;

	while ((opt = getopt_long(argc, argv, RMI4UPDATE_GETOPTS, long_options, &index)) != -1) {
		switch (opt) {
			case 'h':
				printHelp(argv[0]);
				return 0;
			case 'f':
				force = true;
				break;
			case 'd':
				deviceName = optarg;
				break;
			case 'p':
				printFirmwareProps = true;
				break;
			case 'c':
				printFirmwareProps = true;
				printConfigid = true;
				break;
			case 'l':
				performLockdown = true;
				break;
			case 't':
				if (!strcasecmp((const char *)optarg, "touchpad"))
					deviceType = RMI_DEVICE_TYPE_TOUCHPAD;
				else if (!strcasecmp((const char *)optarg, "touchscreen"))
					deviceType = RMI_DEVICE_TYPE_TOUCHSCREEN;
				break;
			case 'v':
				printVersion();
				return 0;
			default:
				break;

		}
	}

	if (printFirmwareProps) {
		std::string props;

		if (!deviceName) {
			fprintf(stderr, "Specifiy which device to query\n");
			return 1;
		}
		rc = GetFirmwareProps(deviceName, props, printConfigid);
		if (rc) {
			fprintf(stderr, "Failed to read properties from device: %s\n", update_err_to_string(rc));
			return 1;
		}
		fprintf(stdout, "%s\n", props.c_str());
		return 0;
	}

	if (optind < argc) {
		firmwareName = argv[optind];
	} else {
		printHelp(argv[0]);
		return -1;
	}

	rc = image.Initialize(firmwareName);
	if (rc != UPDATE_SUCCESS) {
		fprintf(stderr, "Failed to initialize the firmware image: %s\n", update_err_to_string(rc));
		return 1;
	}

	if (deviceName) {
		 rc = device.Open(deviceName);
		 if (rc) {
			fprintf(stderr, "%s: failed to initialize rmi device (%d): %s\n", argv[0], errno,
				strerror(errno));
			return 1;
		}
	} else {
		if (!device.FindDevice(deviceType))
			return 1;
	}

	RMI4Update update(device, image);
	rc = update.UpdateFirmware(force, performLockdown);

	if (rc != UPDATE_SUCCESS)
	{
		device.Reset();
		return 1;
	}

	return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2012 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2003-2006 The Regents of The University of Michigan
 * Copyright (c) 2011 Regents of the University of California
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Lisa Hsu
 *          Nathan Binkert
 *          Ali Saidi
 *          Rick Strong
 */

#include "arch/isa_traits.hh"
#include "arch/remote_gdb.hh"
#include "arch/utility.hh"
#include "arch/vtophys.hh"
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"
#include "config/the_isa.hh"
#include "cpu/thread_context.hh"
#include "debug/Loader.hh"
#include "debug/WorkItems.hh"
#include "kern/kernel_stats.hh"
#include "mem/fs_translating_port_proxy.hh"
#include "mem/mem_object.hh"
#include "mem/physical.hh"
#include "params/System.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/full_system.hh"
#include "sim/system.hh"

using namespace std;
using namespace TheISA;

vector<System *> System::systemList;

int System::numSystemsRunning = 0;

System::System(Params *p)
    : MemObject(p), _systemPort("system_port", this),
      physmem(p->physmem),
      _numContexts(0),
      init_param(p->init_param),
      loadAddrMask(p->load_addr_mask),
      nextPID(0),
      memoryMode(p->mem_mode),
      workItemsBegin(0),
      workItemsEnd(0),
      numWorkIds(p->num_work_ids),
      _params(p),
      totalNumInsts(0),
      instEventQueue("system instruction-based event queue")
{
    // add self to global system list
    systemList.push_back(this);

    /** Keep track of all memories we can execute code out of
     * in our system
     */
    for (int x = 0; x < p->memories.size(); x++) {
        if (!p->memories[x])
            continue;
        memRanges.push_back(RangeSize(p->memories[x]->start(),
                                      p->memories[x]->size()));
    }

    if (FullSystem) {
        kernelSymtab = new SymbolTable;
        if (!debugSymbolTable)
            debugSymbolTable = new SymbolTable;

        /**
         * Get a port proxy to memory
         */
        physProxy = new PortProxy(*getSystemPort());
        virtProxy = new FSTranslatingPortProxy(*getSystemPort());
    }
}

System::~System()
{
    delete kernelSymtab;
    delete kernel;

    for (uint32_t j = 0; j < numWorkIds; j++)
        delete workItemStats[j];
}

void
System::init()
{
    // check that the system port is connected
    if (!_systemPort.isConnected())
        panic("System port on %s is not connected.\n", name());
}

Port*
System::getPort(const std::string &if_name, int idx)
{
    // no need to distinguish at the moment (besides checking)
    return &_systemPort;
}

void
System::setMemoryMode(Enums::MemoryMode mode)
{
    assert(getState() == Drained);
    memoryMode = mode;
}

bool System::breakpoint()
{
    if (remoteGDB.size())
        return remoteGDB[0]->breakpoint();
    return false;
}

/**
 * Setting rgdb_wait to a positive integer waits for a remote debugger to
 * connect to that context ID before continuing.  This should really
   be a parameter on the CPU object or something...
 */
int rgdb_wait = -1;

int
System::registerThreadContext(ThreadContext *tc, int assigned)
{
    int id;
    if (assigned == -1) {
        for (id = 0; id < threadContexts.size(); id++) {
            if (!threadContexts[id])
                break;
        }

        if (threadContexts.size() <= id)
            threadContexts.resize(id + 1);
    } else {
        if (threadContexts.size() <= assigned)
            threadContexts.resize(assigned + 1);
        id = assigned;
    }

    if (threadContexts[id])
        fatal("Cannot have two CPUs with the same id (%d)\n", id);

    threadContexts[id] = tc;
    _numContexts++;

    int port = getRemoteGDBPort();
    if (port) {
        RemoteGDB *rgdb = new RemoteGDB(this, tc);
        GDBListener *gdbl = new GDBListener(rgdb, port + id);
        gdbl->listen();

        if (rgdb_wait != -1 && rgdb_wait == id)
            gdbl->accept();

        if (remoteGDB.size() <= id) {
            remoteGDB.resize(id + 1);
        }

        remoteGDB[id] = rgdb;
    }

    activeCpus.push_back(false);

    return id;
}

int
System::numRunningContexts()
{
    int running = 0;
    for (int i = 0; i < _numContexts; ++i) {
        if (threadContexts[i]->status() != ThreadContext::Halted)
            ++running;
    }
    return running;
}

void
System::initState()
{
    int i;
    if (FullSystem) {
        for (i = 0; i < threadContexts.size(); i++)
            TheISA::startupCPU(threadContexts[i], i);
        // Moved from the constructor to here since it relies on the
        // address map being resolved in the interconnect
        /**
         * Load the kernel code into memory
         */
        if (params()->kernel == "") {
            inform("No kernel set for full system simulation. "
                    "Assuming you know what you're doing...\n");
        } else {
            // Load kernel code
            kernel = createObjectFile(params()->kernel);
            inform("kernel located at: %s", params()->kernel);

            if (kernel == NULL)
                fatal("Could not load kernel file %s", params()->kernel);

            // Load program sections into memory
            kernel->loadSections(physProxy, loadAddrMask);

            // setup entry points
            kernelStart = kernel->textBase();
            kernelEnd = kernel->bssBase() + kernel->bssSize();
            kernelEntry = kernel->entryPoint();

            // load symbols
            if (!kernel->loadGlobalSymbols(kernelSymtab))
                fatal("could not load kernel symbols\n");

            if (!kernel->loadLocalSymbols(kernelSymtab))
                fatal("could not load kernel local symbols\n");

            if (!kernel->loadGlobalSymbols(debugSymbolTable))
                fatal("could not load kernel symbols\n");

            if (!kernel->loadLocalSymbols(debugSymbolTable))
                fatal("could not load kernel local symbols\n");

            DPRINTF(Loader, "Kernel start = %#x\n", kernelStart);
            DPRINTF(Loader, "Kernel end   = %#x\n", kernelEnd);
            DPRINTF(Loader, "Kernel entry = %#x\n", kernelEntry);
            DPRINTF(Loader, "Kernel loaded...\n");
        }
    }

    // increment the number of running systms
    numSystemsRunning++;

    activeCpus.clear();

    if (!FullSystem)
        return;

    for (i = 0; i < threadContexts.size(); i++)
        TheISA::startupCPU(threadContexts[i], i);
}

void
System::replaceThreadContext(ThreadContext *tc, int context_id)
{
    if (context_id >= threadContexts.size()) {
        panic("replaceThreadContext: bad id, %d >= %d\n",
              context_id, threadContexts.size());
    }

    threadContexts[context_id] = tc;
    if (context_id < remoteGDB.size())
        remoteGDB[context_id]->replaceThreadContext(tc);
}

Addr
System::allocPhysPages(int npages)
{
    Addr return_addr = pagePtr << LogVMPageSize;
    pagePtr += npages;
    if (return_addr >= physmem->size())
        fatal("Out of memory, please increase size of physical memory.");
    return return_addr;
}

Addr
System::memSize()
{
    return physmem->size();
}

Addr
System::freeMemSize()
{
   return physmem->size() - (pagePtr << LogVMPageSize);
}

bool
System::isMemory(const Addr addr) const
{
    std::list<Range<Addr> >::const_iterator i;
    for (i = memRanges.begin(); i != memRanges.end(); i++) {
        if (*i == addr)
            return true;
    }
    return false;
}

void
System::resume()
{
    SimObject::resume();
    totalNumInsts = 0;
}

void
System::serialize(ostream &os)
{
    if (FullSystem)
        kernelSymtab->serialize("kernel_symtab", os);
    SERIALIZE_SCALAR(pagePtr);
    SERIALIZE_SCALAR(nextPID);
}

void
System::unserialize(Checkpoint *cp, const string &section)
{
    if (FullSystem)
        kernelSymtab->unserialize("kernel_symtab", cp, section);
    UNSERIALIZE_SCALAR(pagePtr);
    UNSERIALIZE_SCALAR(nextPID);
}

void
System::regStats()
{
    for (uint32_t j = 0; j < numWorkIds ; j++) {
        workItemStats[j] = new Stats::Histogram();
        stringstream namestr;
        ccprintf(namestr, "work_item_type%d", j);
        workItemStats[j]->init(20)
                         .name(name() + "." + namestr.str())
                         .desc("Run time stat for" + namestr.str())
                         .prereq(*workItemStats[j]);
    }
}

void
System::workItemEnd(uint32_t tid, uint32_t workid)
{
    std::pair<uint32_t,uint32_t> p(tid, workid);
    if (!lastWorkItemStarted.count(p))
        return;

    Tick samp = curTick() - lastWorkItemStarted[p];
    DPRINTF(WorkItems, "Work item end: %d\t%d\t%lld\n", tid, workid, samp);

    if (workid >= numWorkIds)
        fatal("Got workid greater than specified in system configuration\n");

    workItemStats[workid]->sample(samp);
    lastWorkItemStarted.erase(p);
}

void
System::printSystems()
{
    vector<System *>::iterator i = systemList.begin();
    vector<System *>::iterator end = systemList.end();
    for (; i != end; ++i) {
        System *sys = *i;
        cerr << "System " << sys->name() << ": " << hex << sys << endl;
    }
}

void
printSystems()
{
    System::printSystems();
}

const char *System::MemoryModeStrings[3] = {"invalid", "atomic",
    "timing"};

System *
SystemParams::create()
{
    return new System(this);
}
<commit_msg>System: Fix the check which detects running out of physical memory.<commit_after>/*
 * Copyright (c) 2011-2012 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2003-2006 The Regents of The University of Michigan
 * Copyright (c) 2011 Regents of the University of California
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Lisa Hsu
 *          Nathan Binkert
 *          Ali Saidi
 *          Rick Strong
 */

#include "arch/isa_traits.hh"
#include "arch/remote_gdb.hh"
#include "arch/utility.hh"
#include "arch/vtophys.hh"
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"
#include "config/the_isa.hh"
#include "cpu/thread_context.hh"
#include "debug/Loader.hh"
#include "debug/WorkItems.hh"
#include "kern/kernel_stats.hh"
#include "mem/fs_translating_port_proxy.hh"
#include "mem/mem_object.hh"
#include "mem/physical.hh"
#include "params/System.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/full_system.hh"
#include "sim/system.hh"

using namespace std;
using namespace TheISA;

vector<System *> System::systemList;

int System::numSystemsRunning = 0;

System::System(Params *p)
    : MemObject(p), _systemPort("system_port", this),
      physmem(p->physmem),
      _numContexts(0),
      init_param(p->init_param),
      loadAddrMask(p->load_addr_mask),
      nextPID(0),
      memoryMode(p->mem_mode),
      workItemsBegin(0),
      workItemsEnd(0),
      numWorkIds(p->num_work_ids),
      _params(p),
      totalNumInsts(0),
      instEventQueue("system instruction-based event queue")
{
    // add self to global system list
    systemList.push_back(this);

    /** Keep track of all memories we can execute code out of
     * in our system
     */
    for (int x = 0; x < p->memories.size(); x++) {
        if (!p->memories[x])
            continue;
        memRanges.push_back(RangeSize(p->memories[x]->start(),
                                      p->memories[x]->size()));
    }

    if (FullSystem) {
        kernelSymtab = new SymbolTable;
        if (!debugSymbolTable)
            debugSymbolTable = new SymbolTable;

        /**
         * Get a port proxy to memory
         */
        physProxy = new PortProxy(*getSystemPort());
        virtProxy = new FSTranslatingPortProxy(*getSystemPort());
    }
}

System::~System()
{
    delete kernelSymtab;
    delete kernel;

    for (uint32_t j = 0; j < numWorkIds; j++)
        delete workItemStats[j];
}

void
System::init()
{
    // check that the system port is connected
    if (!_systemPort.isConnected())
        panic("System port on %s is not connected.\n", name());
}

Port*
System::getPort(const std::string &if_name, int idx)
{
    // no need to distinguish at the moment (besides checking)
    return &_systemPort;
}

void
System::setMemoryMode(Enums::MemoryMode mode)
{
    assert(getState() == Drained);
    memoryMode = mode;
}

bool System::breakpoint()
{
    if (remoteGDB.size())
        return remoteGDB[0]->breakpoint();
    return false;
}

/**
 * Setting rgdb_wait to a positive integer waits for a remote debugger to
 * connect to that context ID before continuing.  This should really
   be a parameter on the CPU object or something...
 */
int rgdb_wait = -1;

int
System::registerThreadContext(ThreadContext *tc, int assigned)
{
    int id;
    if (assigned == -1) {
        for (id = 0; id < threadContexts.size(); id++) {
            if (!threadContexts[id])
                break;
        }

        if (threadContexts.size() <= id)
            threadContexts.resize(id + 1);
    } else {
        if (threadContexts.size() <= assigned)
            threadContexts.resize(assigned + 1);
        id = assigned;
    }

    if (threadContexts[id])
        fatal("Cannot have two CPUs with the same id (%d)\n", id);

    threadContexts[id] = tc;
    _numContexts++;

    int port = getRemoteGDBPort();
    if (port) {
        RemoteGDB *rgdb = new RemoteGDB(this, tc);
        GDBListener *gdbl = new GDBListener(rgdb, port + id);
        gdbl->listen();

        if (rgdb_wait != -1 && rgdb_wait == id)
            gdbl->accept();

        if (remoteGDB.size() <= id) {
            remoteGDB.resize(id + 1);
        }

        remoteGDB[id] = rgdb;
    }

    activeCpus.push_back(false);

    return id;
}

int
System::numRunningContexts()
{
    int running = 0;
    for (int i = 0; i < _numContexts; ++i) {
        if (threadContexts[i]->status() != ThreadContext::Halted)
            ++running;
    }
    return running;
}

void
System::initState()
{
    int i;
    if (FullSystem) {
        for (i = 0; i < threadContexts.size(); i++)
            TheISA::startupCPU(threadContexts[i], i);
        // Moved from the constructor to here since it relies on the
        // address map being resolved in the interconnect
        /**
         * Load the kernel code into memory
         */
        if (params()->kernel == "") {
            inform("No kernel set for full system simulation. "
                    "Assuming you know what you're doing...\n");
        } else {
            // Load kernel code
            kernel = createObjectFile(params()->kernel);
            inform("kernel located at: %s", params()->kernel);

            if (kernel == NULL)
                fatal("Could not load kernel file %s", params()->kernel);

            // Load program sections into memory
            kernel->loadSections(physProxy, loadAddrMask);

            // setup entry points
            kernelStart = kernel->textBase();
            kernelEnd = kernel->bssBase() + kernel->bssSize();
            kernelEntry = kernel->entryPoint();

            // load symbols
            if (!kernel->loadGlobalSymbols(kernelSymtab))
                fatal("could not load kernel symbols\n");

            if (!kernel->loadLocalSymbols(kernelSymtab))
                fatal("could not load kernel local symbols\n");

            if (!kernel->loadGlobalSymbols(debugSymbolTable))
                fatal("could not load kernel symbols\n");

            if (!kernel->loadLocalSymbols(debugSymbolTable))
                fatal("could not load kernel local symbols\n");

            DPRINTF(Loader, "Kernel start = %#x\n", kernelStart);
            DPRINTF(Loader, "Kernel end   = %#x\n", kernelEnd);
            DPRINTF(Loader, "Kernel entry = %#x\n", kernelEntry);
            DPRINTF(Loader, "Kernel loaded...\n");
        }
    }

    // increment the number of running systms
    numSystemsRunning++;

    activeCpus.clear();

    if (!FullSystem)
        return;

    for (i = 0; i < threadContexts.size(); i++)
        TheISA::startupCPU(threadContexts[i], i);
}

void
System::replaceThreadContext(ThreadContext *tc, int context_id)
{
    if (context_id >= threadContexts.size()) {
        panic("replaceThreadContext: bad id, %d >= %d\n",
              context_id, threadContexts.size());
    }

    threadContexts[context_id] = tc;
    if (context_id < remoteGDB.size())
        remoteGDB[context_id]->replaceThreadContext(tc);
}

Addr
System::allocPhysPages(int npages)
{
    Addr return_addr = pagePtr << LogVMPageSize;
    pagePtr += npages;
    if (return_addr + npages - 1 >= physmem->size())
        fatal("Out of memory, please increase size of physical memory.");
    return return_addr;
}

Addr
System::memSize()
{
    return physmem->size();
}

Addr
System::freeMemSize()
{
   return physmem->size() - (pagePtr << LogVMPageSize);
}

bool
System::isMemory(const Addr addr) const
{
    std::list<Range<Addr> >::const_iterator i;
    for (i = memRanges.begin(); i != memRanges.end(); i++) {
        if (*i == addr)
            return true;
    }
    return false;
}

void
System::resume()
{
    SimObject::resume();
    totalNumInsts = 0;
}

void
System::serialize(ostream &os)
{
    if (FullSystem)
        kernelSymtab->serialize("kernel_symtab", os);
    SERIALIZE_SCALAR(pagePtr);
    SERIALIZE_SCALAR(nextPID);
}

void
System::unserialize(Checkpoint *cp, const string &section)
{
    if (FullSystem)
        kernelSymtab->unserialize("kernel_symtab", cp, section);
    UNSERIALIZE_SCALAR(pagePtr);
    UNSERIALIZE_SCALAR(nextPID);
}

void
System::regStats()
{
    for (uint32_t j = 0; j < numWorkIds ; j++) {
        workItemStats[j] = new Stats::Histogram();
        stringstream namestr;
        ccprintf(namestr, "work_item_type%d", j);
        workItemStats[j]->init(20)
                         .name(name() + "." + namestr.str())
                         .desc("Run time stat for" + namestr.str())
                         .prereq(*workItemStats[j]);
    }
}

void
System::workItemEnd(uint32_t tid, uint32_t workid)
{
    std::pair<uint32_t,uint32_t> p(tid, workid);
    if (!lastWorkItemStarted.count(p))
        return;

    Tick samp = curTick() - lastWorkItemStarted[p];
    DPRINTF(WorkItems, "Work item end: %d\t%d\t%lld\n", tid, workid, samp);

    if (workid >= numWorkIds)
        fatal("Got workid greater than specified in system configuration\n");

    workItemStats[workid]->sample(samp);
    lastWorkItemStarted.erase(p);
}

void
System::printSystems()
{
    vector<System *>::iterator i = systemList.begin();
    vector<System *>::iterator end = systemList.end();
    for (; i != end; ++i) {
        System *sys = *i;
        cerr << "System " << sys->name() << ": " << hex << sys << endl;
    }
}

void
printSystems()
{
    System::printSystems();
}

const char *System::MemoryModeStrings[3] = {"invalid", "atomic",
    "timing"};

System *
SystemParams::create()
{
    return new System(this);
}
<|endoftext|>