c7c9a0f069
Change-Id: Ia1b05747e64b2122cfe17ee0c2f7bf5477f3ba09
378 lines
12 KiB
C++
378 lines
12 KiB
C++
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
*
|
|
* Copyright (C) 2007 by Dominik Wenger
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <QtCore>
|
|
#include "autodetection.h"
|
|
#include "rbsettings.h"
|
|
#include "playerbuildinfo.h"
|
|
|
|
#include "../ipodpatcher/ipodpatcher.h"
|
|
#include "../sansapatcher/sansapatcher.h"
|
|
|
|
|
|
#include "system.h"
|
|
#include "utils.h"
|
|
#include "rockboxinfo.h"
|
|
#include "Logger.h"
|
|
|
|
Autodetection::Autodetection(QObject* parent): QObject(parent)
|
|
{
|
|
}
|
|
|
|
|
|
bool Autodetection::detect(void)
|
|
{
|
|
QMap<PlayerStatus, QString> states;
|
|
states[PlayerOk] = "Ok";
|
|
states[PlayerAmbiguous] = "Ambiguous";
|
|
states[PlayerError] = "Error";
|
|
states[PlayerIncompatible] = "Incompatible";
|
|
states[PlayerMtpMode] = "MtpMode";
|
|
|
|
// clear detection state
|
|
m_detected.clear();
|
|
|
|
detectUsb();
|
|
mergeMounted();
|
|
mergePatcher();
|
|
// if any entry with usbdevices containing a value is left that entry
|
|
// hasn't been merged later. This indicates a problem during detection
|
|
// (ambiguous player but refining it failed). In this case create an entry
|
|
// for eacho of those so the user can select.
|
|
QList<struct Detected> detected;
|
|
for(int i = 0; i < m_detected.size(); ++i) {
|
|
int j = m_detected.at(i).usbdevices.size();
|
|
if(j > 0) {
|
|
struct Detected entry = m_detected.at(i);
|
|
while(j--) {
|
|
struct Detected d;
|
|
d.device = entry.usbdevices.at(j);
|
|
d.mountpoint = entry.mountpoint;
|
|
d.status = PlayerAmbiguous;
|
|
detected.append(d);
|
|
}
|
|
}
|
|
else {
|
|
detected.append(m_detected.at(i));
|
|
}
|
|
}
|
|
m_detected = detected;
|
|
for(int i = 0; i < m_detected.size(); ++i) {
|
|
LOG_INFO() << "Detected player:" << m_detected.at(i).device
|
|
<< "at" << m_detected.at(i).mountpoint
|
|
<< states[m_detected.at(i).status];
|
|
}
|
|
|
|
return m_detected.size() > 0;
|
|
}
|
|
|
|
|
|
/** @brief detect devices based on usb pid / vid.
|
|
*/
|
|
void Autodetection::detectUsb()
|
|
{
|
|
// usb pid detection
|
|
QList<uint32_t> attached;
|
|
attached = System::listUsbIds();
|
|
|
|
int i = attached.size();
|
|
while(i--) {
|
|
QStringList a = PlayerBuildInfo::instance()->value(
|
|
PlayerBuildInfo::UsbIdTargetList, attached.at(i)).toStringList();
|
|
if(a.size() > 0) {
|
|
struct Detected d;
|
|
d.status = PlayerOk;
|
|
d.usbdevices = a;
|
|
m_detected.append(d);
|
|
LOG_INFO() << "[USB] detected supported player" << d.usbdevices;
|
|
}
|
|
QStringList b = PlayerBuildInfo::instance()->value(
|
|
PlayerBuildInfo::UsbIdErrorList, attached.at(i)).toStringList();
|
|
if(b.size() > 0) {
|
|
struct Detected d;
|
|
d.status = PlayerMtpMode;
|
|
d.usbdevices = b;
|
|
m_detected.append(d);
|
|
LOG_WARNING() << "[USB] detected problem with player" << d.device;
|
|
}
|
|
QString idstring = QString("%1").arg(attached.at(i), 8, 16, QChar('0'));
|
|
if(!PlayerBuildInfo::instance()->value(
|
|
PlayerBuildInfo::DisplayName, idstring).toString().isEmpty()) {
|
|
struct Detected d;
|
|
d.status = PlayerIncompatible;
|
|
d.device = idstring;
|
|
m_detected.append(d);
|
|
LOG_WARNING() << "[USB] detected incompatible player" << d.device;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Merge players detected by checking mounted filesystems for known files:
|
|
// - rockbox-info.txt / rbutil.log
|
|
// - player specific files
|
|
void Autodetection::mergeMounted(void)
|
|
{
|
|
QStringList mounts = Utils::mountpoints(Utils::MountpointsSupported);
|
|
LOG_INFO() << "paths to check:" << mounts;
|
|
|
|
for(int i = 0; i < mounts.size(); i++)
|
|
{
|
|
// do the file checking
|
|
QDir dir(mounts.at(i));
|
|
if(dir.exists())
|
|
{
|
|
// check logfile first.
|
|
if(QFile(mounts.at(i) + "/.rockbox/rbutil.log").exists()) {
|
|
QSettings log(mounts.at(i) + "/.rockbox/rbutil.log",
|
|
QSettings::IniFormat, this);
|
|
if(!log.value("platform").toString().isEmpty()) {
|
|
struct Detected d;
|
|
d.device = log.value("platform").toString();
|
|
d.mountpoint = mounts.at(i);
|
|
d.status = PlayerOk;
|
|
updateDetectedDevice(d);
|
|
LOG_INFO() << "rbutil.log detected:"
|
|
<< log.value("platform").toString() << mounts.at(i);
|
|
}
|
|
}
|
|
|
|
// check rockbox-info.txt afterwards.
|
|
RockboxInfo info(mounts.at(i));
|
|
if(info.success())
|
|
{
|
|
struct Detected d;
|
|
d.device = info.target();
|
|
d.mountpoint = mounts.at(i);
|
|
d.status = PlayerOk;
|
|
updateDetectedDevice(d);
|
|
LOG_INFO() << "rockbox-info.txt detected:"
|
|
<< info.target() << mounts.at(i);
|
|
}
|
|
|
|
// check for some specific files in root folder
|
|
QDir root(mounts.at(i));
|
|
QStringList rootentries = root.entryList(QDir::Files);
|
|
if(rootentries.contains("archos.mod", Qt::CaseInsensitive))
|
|
{
|
|
// archos.mod in root folder -> Archos Player
|
|
struct Detected d;
|
|
d.device = "player";
|
|
d.mountpoint = mounts.at(i);
|
|
d.status = PlayerOk;
|
|
updateDetectedDevice(d);
|
|
}
|
|
if(rootentries.contains("ONDIOST.BIN", Qt::CaseInsensitive))
|
|
{
|
|
// ONDIOST.BIN in root -> Ondio FM
|
|
struct Detected d;
|
|
d.device = "ondiofm";
|
|
d.mountpoint = mounts.at(i);
|
|
d.status = PlayerOk;
|
|
updateDetectedDevice(d);
|
|
}
|
|
if(rootentries.contains("ONDIOSP.BIN", Qt::CaseInsensitive))
|
|
{
|
|
// ONDIOSP.BIN in root -> Ondio SP
|
|
struct Detected d;
|
|
d.device = "ondiosp";
|
|
d.mountpoint = mounts.at(i);
|
|
d.status = PlayerOk;
|
|
updateDetectedDevice(d);
|
|
}
|
|
if(rootentries.contains("ajbrec.ajz", Qt::CaseInsensitive))
|
|
{
|
|
LOG_INFO() << "ajbrec.ajz found. Trying detectAjbrec()";
|
|
struct Detected d;
|
|
d.device = detectAjbrec(mounts.at(i));
|
|
d.mountpoint = mounts.at(i);
|
|
d.status = PlayerOk;
|
|
if(!d.device.isEmpty()) {
|
|
LOG_INFO() << d.device;
|
|
updateDetectedDevice(d);
|
|
}
|
|
}
|
|
// detection based on player specific folders
|
|
QStringList rootfolders = root.entryList(QDir::Dirs
|
|
| QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
|
|
if(rootfolders.contains("GBSYSTEM", Qt::CaseInsensitive))
|
|
{
|
|
// GBSYSTEM folder -> Gigabeat
|
|
struct Detected d;
|
|
d.device = "gigabeatf";
|
|
d.mountpoint = mounts.at(i);
|
|
updateDetectedDevice(d);
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
// Ipods have a folder "iPod_Control" in the root.
|
|
for(int i = 0; i < m_detected.size(); ++i) {
|
|
struct Detected entry = m_detected.at(i);
|
|
for(int j = 0; j < entry.usbdevices.size(); ++j) {
|
|
// limit this to Ipods only.
|
|
if(!entry.usbdevices.at(j).startsWith("ipod")
|
|
&& !entry.device.startsWith("ipod")) {
|
|
continue;
|
|
}
|
|
// look for iPod_Control on all supported volumes.
|
|
for(int k = 0; k < mounts.size(); k++) {
|
|
QDir root(mounts.at(k));
|
|
QStringList rootfolders = root.entryList(QDir::Dirs
|
|
| QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
|
|
if(rootfolders.contains("iPod_Control", Qt::CaseInsensitive)) {
|
|
entry.mountpoint = mounts.at(k);
|
|
m_detected.takeAt(i);
|
|
m_detected.append(entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
void Autodetection::mergePatcher(void)
|
|
{
|
|
int n;
|
|
// try ipodpatcher
|
|
// initialize sector buffer. Needed.
|
|
struct ipod_t ipod;
|
|
ipod.sectorbuf = nullptr;
|
|
ipod_alloc_buffer(&ipod, BUFFER_SIZE);
|
|
n = ipod_scan(&ipod);
|
|
// FIXME: handle more than one Ipod connected in ipodpatcher.
|
|
if(n == 1) {
|
|
LOG_INFO() << "Ipod found:" << ipod.modelstr << "at" << ipod.diskname;
|
|
// since resolveMountPoint is doing exact matches we need to select
|
|
// the correct partition.
|
|
QString mp(ipod.diskname);
|
|
#ifdef Q_OS_LINUX
|
|
mp.append("2");
|
|
#endif
|
|
#ifdef Q_OS_MACX
|
|
mp.append("s2");
|
|
#endif
|
|
struct Detected d;
|
|
d.device = ipod.targetname;
|
|
d.mountpoint = Utils::resolveMountPoint(mp);
|
|
// if the found ipod is a macpod also notice it as device with problem.
|
|
if(ipod.macpod)
|
|
d.status = PlayerWrongFilesystem;
|
|
else
|
|
d.status = PlayerOk;
|
|
updateDetectedDevice(d);
|
|
}
|
|
else {
|
|
LOG_INFO() << "ipodpatcher: no Ipod found." << n;
|
|
}
|
|
ipod_dealloc_buffer(&ipod);
|
|
|
|
// try sansapatcher
|
|
// initialize sector buffer. Needed.
|
|
struct sansa_t sansa;
|
|
sansa_alloc_buffer(&sansa, BUFFER_SIZE);
|
|
n = sansa_scan(&sansa);
|
|
if(n == 1) {
|
|
LOG_INFO() << "Sansa found:"
|
|
<< sansa.targetname << "at" << sansa.diskname;
|
|
QString mp(sansa.diskname);
|
|
#ifdef Q_OS_LINUX
|
|
mp.append("1");
|
|
#endif
|
|
#ifdef Q_OS_MACX
|
|
mp.append("s1");
|
|
#endif
|
|
struct Detected d;
|
|
d.device = QString("sansa%1").arg(sansa.targetname);
|
|
d.mountpoint = Utils::resolveMountPoint(mp);
|
|
d.status = PlayerOk;
|
|
updateDetectedDevice(d);
|
|
}
|
|
else {
|
|
LOG_INFO() << "sansapatcher: no Sansa found." << n;
|
|
}
|
|
sansa_dealloc_buffer(&sansa);
|
|
}
|
|
|
|
|
|
QString Autodetection::detectAjbrec(const QString& root)
|
|
{
|
|
QFile f(root + "/ajbrec.ajz");
|
|
char header[24];
|
|
f.open(QIODevice::ReadOnly);
|
|
if(!f.read(header, 24)) return QString();
|
|
f.close();
|
|
|
|
// check the header of the file.
|
|
// recorder v1 had a 6 bytes sized header
|
|
// recorder v2, FM, Ondio SP and FM have a 24 bytes header.
|
|
|
|
// recorder v1 has the binary length in the first 4 bytes, so check
|
|
// for them first.
|
|
int len = (header[0]<<24) | (header[1]<<16) | (header[2]<<8) | header[3];
|
|
LOG_INFO() << "abjrec.ajz possible bin length:" << len
|
|
<< "file len:" << f.size();
|
|
if((f.size() - 6) == len)
|
|
return "recorder";
|
|
|
|
// size didn't match, now we need to assume we have a headerlength of 24.
|
|
switch(header[11]) {
|
|
case 2:
|
|
return "recorderv2";
|
|
case 4:
|
|
return "fmrecorder";
|
|
case 8:
|
|
return "ondiofm";
|
|
case 16:
|
|
return "ondiosp";
|
|
default:
|
|
break;
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
|
|
int Autodetection::findDetectedDevice(const QString& device)
|
|
{
|
|
int i = m_detected.size();
|
|
while(i--) {
|
|
if(m_detected.at(i).usbdevices.contains(device))
|
|
return i;
|
|
}
|
|
i = m_detected.size();
|
|
while(i--) {
|
|
if(m_detected.at(i).device == device)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
void Autodetection::updateDetectedDevice(Detected& entry)
|
|
{
|
|
int index = findDetectedDevice(entry.device);
|
|
if(index < 0) {
|
|
m_detected.append(entry);
|
|
}
|
|
else {
|
|
m_detected.takeAt(index);
|
|
m_detected.append(entry);
|
|
}
|
|
}
|