1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
9 * Copyright (C) 2007 by Dominik Wenger
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 #include "autodetection.h"
22 #include "rbsettings.h"
24 #include "../ipodpatcher/ipodpatcher.h"
25 #include "../sansapatcher/sansapatcher.h"
27 #if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
31 #if defined(Q_OS_LINUX)
34 #if defined(Q_OS_MACX)
35 #include <sys/param.h>
36 #include <sys/ucred.h>
37 #include <sys/mount.h>
39 #if defined(Q_OS_WIN32)
50 #if defined(Q_OS_OPENBSD)
51 #include <sys/param.h>
52 #include <sys/mount.h>
58 Autodetection::Autodetection(QObject
* parent
): QObject(parent
)
62 bool Autodetection::detect()
70 // Try detection via rockbox.info / rbutil.log
71 QStringList mounts
= mountpoints();
73 for(int i
=0; i
< mounts
.size();i
++)
75 // do the file checking
76 QDir
dir(mounts
.at(i
));
77 qDebug() << "[Autodetect] paths to check:" << mounts
;
80 // check logfile first.
81 if(QFile(mounts
.at(i
) + "/.rockbox/rbutil.log").exists()) {
82 QSettings
log(mounts
.at(i
) + "/.rockbox/rbutil.log",
83 QSettings::IniFormat
, this);
84 if(!log
.value("platform").toString().isEmpty()) {
85 if(m_device
.isEmpty())
86 m_device
= log
.value("platform").toString();
87 m_mountpoint
= mounts
.at(i
);
88 qDebug() << "[Autodetect] rbutil.log detected:" << m_device
<< m_mountpoint
;
93 // check rockbox-info.txt afterwards.
94 RockboxInfo
info(mounts
.at(i
));
97 if(m_device
.isEmpty())
99 m_device
= info
.target();
101 m_mountpoint
= mounts
.at(i
);
102 qDebug() << "[Autodetect] rockbox-info.txt detected:"
103 << m_device
<< m_mountpoint
;
107 // check for some specific files in root folder
108 QDir
root(mounts
.at(i
));
109 QStringList rootentries
= root
.entryList(QDir::Files
);
110 if(rootentries
.contains("archos.mod", Qt::CaseInsensitive
))
112 // archos.mod in root folder -> Archos Player
114 m_mountpoint
= mounts
.at(i
);
117 if(rootentries
.contains("ONDIOST.BIN", Qt::CaseInsensitive
))
119 // ONDIOST.BIN in root -> Ondio FM
120 m_device
= "ondiofm";
121 m_mountpoint
= mounts
.at(i
);
124 if(rootentries
.contains("ONDIOSP.BIN", Qt::CaseInsensitive
))
126 // ONDIOSP.BIN in root -> Ondio SP
127 m_device
= "ondiosp";
128 m_mountpoint
= mounts
.at(i
);
131 if(rootentries
.contains("ajbrec.ajz", Qt::CaseInsensitive
))
133 qDebug() << "[Autodetect] ajbrec.ajz found. Trying detectAjbrec()";
134 if(detectAjbrec(mounts
.at(i
))) {
135 m_mountpoint
= mounts
.at(i
);
136 qDebug() << "[Autodetect]" << m_device
;
140 // detection based on player specific folders
141 QStringList rootfolders
= root
.entryList(QDir::Dirs
142 | QDir::NoDotAndDotDot
| QDir::Hidden
| QDir::System
);
143 if(rootfolders
.contains("GBSYSTEM", Qt::CaseInsensitive
))
145 // GBSYSTEM folder -> Gigabeat
146 m_device
= "gigabeatf";
147 m_mountpoint
= mounts
.at(i
);
150 #if defined(Q_OS_WIN32)
151 // on windows, try to detect the drive letter of an Ipod
152 if(rootfolders
.contains("iPod_Control", Qt::CaseInsensitive
))
154 // iPod_Control folder -> Ipod found
155 // detecting of the Ipod type is done below using ipodpatcher
156 m_mountpoint
= mounts
.at(i
);
165 // initialize sector buffer. Needed.
166 ipod_sectorbuf
= NULL
;
167 ipod_alloc_buffer(&ipod_sectorbuf
, BUFFER_SIZE
);
169 n
= ipod_scan(&ipod
);
171 qDebug() << "[Autodetect] Ipod found:" << ipod
.modelstr
<< "at" << ipod
.diskname
;
172 m_device
= ipod
.targetname
;
173 m_mountpoint
= resolveMountPoint(ipod
.diskname
);
177 qDebug() << "[Autodetect] ipodpatcher: no Ipod found." << n
;
179 free(ipod_sectorbuf
);
180 ipod_sectorbuf
= NULL
;
183 // initialize sector buffer. Needed.
184 sansa_sectorbuf
= NULL
;
185 sansa_alloc_buffer(&sansa_sectorbuf
, BUFFER_SIZE
);
186 struct sansa_t sansa
;
187 n
= sansa_scan(&sansa
);
189 qDebug() << "[Autodetect] Sansa found:" << sansa
.targetname
<< "at" << sansa
.diskname
;
190 m_device
= QString("sansa%1").arg(sansa
.targetname
);
191 m_mountpoint
= resolveMountPoint(sansa
.diskname
);
195 qDebug() << "[Autodetect] sansapatcher: no Sansa found." << n
;
197 free(sansa_sectorbuf
);
198 sansa_sectorbuf
= NULL
;
200 if(m_mountpoint
.isEmpty() && m_device
.isEmpty()
201 && m_errdev
.isEmpty() && m_incompat
.isEmpty())
207 QStringList
Autodetection::mountpoints()
209 QStringList tempList
;
210 #if defined(Q_OS_WIN32)
211 QFileInfoList list
= QDir::drives();
212 for(int i
=0; i
<list
.size();i
++)
214 tempList
<< list
.at(i
).absolutePath();
217 #elif defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
219 struct statfs
*mntinf
;
221 num
= getmntinfo(&mntinf
, MNT_WAIT
);
223 tempList
<< QString(mntinf
->f_mntonname
);
226 #elif defined(Q_OS_LINUX)
228 FILE *mn
= setmntent("/etc/mtab", "r");
230 return QStringList("");
233 while((ent
= getmntent(mn
)))
234 tempList
<< QString(ent
->mnt_dir
);
238 #error Unknown Plattform
244 /** resolve device name to mount point / drive letter
245 * @param device device name / disk number
246 * @return mount point / drive letter
248 QString
Autodetection::resolveMountPoint(QString device
)
250 qDebug() << "[Autodetect] resolving mountpoint:" << device
;
252 #if defined(Q_OS_LINUX)
253 FILE *mn
= setmntent("/etc/mtab", "r");
258 while((ent
= getmntent(mn
))) {
259 if(QString(ent
->mnt_fsname
).startsWith(device
)
260 && QString(ent
->mnt_type
).contains("vfat", Qt::CaseInsensitive
)) {
262 return QString(ent
->mnt_dir
);
269 #if defined(Q_OS_MACX)
271 struct statfs
*mntinf
;
273 num
= getmntinfo(&mntinf
, MNT_WAIT
);
275 if(QString(mntinf
->f_mntfromname
).startsWith(device
)
276 && QString(mntinf
->f_fstypename
).contains("vfat", Qt::CaseInsensitive
))
277 return QString(mntinf
->f_mntonname
);
282 #if defined(Q_OS_OPENBSD)
284 struct statfs
*mntinf
;
286 num
= getmntinfo(&mntinf
, MNT_WAIT
);
288 if(QString(mntinf
->f_mntfromname
).startsWith(device
)
289 && QString(mntinf
->f_fstypename
).contains("msdos", Qt::CaseInsensitive
))
290 return QString(mntinf
->f_mntonname
);
295 #if defined(Q_OS_WIN32)
297 unsigned int driveno
= device
.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt();
300 for(letter
= 'A'; letter
<= 'Z'; letter
++) {
301 if(resolveDevicename(QString(letter
)).toUInt() == driveno
) {
306 qDebug() << "[Autodetect] resolved mountpoint is:" << result
;
307 if(!result
.isEmpty())
308 return result
+ ":/";
314 /** Resolve mountpoint to devicename / disk number
315 * @param path mountpoint path / drive letter
316 * @return devicename / disk number
318 QString
Autodetection::resolveDevicename(QString path
)
320 qDebug() << "[Autodetect] resolving device name" << path
;
321 #if defined(Q_OS_LINUX)
322 FILE *mn
= setmntent("/etc/mtab", "r");
327 while((ent
= getmntent(mn
))) {
328 if(QString(ent
->mnt_dir
).startsWith(path
)
329 && QString(ent
->mnt_type
).contains("vfat", Qt::CaseInsensitive
)) {
331 return QString(ent
->mnt_fsname
);
338 #if defined(Q_OS_MACX)
340 struct statfs
*mntinf
;
342 num
= getmntinfo(&mntinf
, MNT_WAIT
);
344 if(QString(mntinf
->f_mntonname
).startsWith(path
)
345 && QString(mntinf
->f_fstypename
).contains("vfat", Qt::CaseInsensitive
))
346 return QString(mntinf
->f_mntfromname
);
351 #if defined(Q_OS_OPENBSD)
353 struct statfs
*mntinf
;
355 num
= getmntinfo(&mntinf
, MNT_WAIT
);
357 if(QString(mntinf
->f_mntonname
).startsWith(device
)
358 && QString(mntinf
->f_fstypename
).contains("msdos", Qt::CaseInsensitive
))
359 return QString(mntinf
->f_mntfromname
);
363 #if defined(Q_OS_WIN32)
366 TCHAR uncpath
[MAX_PATH
];
368 PVOLUME_DISK_EXTENTS extents
= (PVOLUME_DISK_EXTENTS
)buffer
;
370 _stprintf(uncpath
, _TEXT("\\\\.\\%c:"), path
.toAscii().at(0));
371 h
= CreateFile(uncpath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
372 NULL
, OPEN_EXISTING
, 0, NULL
);
373 if(h
== INVALID_HANDLE_VALUE
) {
374 //qDebug() << "error getting extents for" << uncpath;
378 if(DeviceIoControl(h
, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
,
379 NULL
, 0, extents
, sizeof(buffer
), &written
, NULL
)) {
380 if(extents
->NumberOfDiskExtents
> 1) {
381 qDebug() << "[Autodetect] resolving device name: volume spans multiple disks!";
384 //qDebug() << "Disk:" << extents->Extents[0].DiskNumber;
385 return QString("%1").arg(extents
->Extents
[0].DiskNumber
);
393 /** @brief detect devices based on usb pid / vid.
394 * @return true upon success, false otherwise.
396 bool Autodetection::detectUsb()
398 // usbids holds the mapping in the form
399 // ((VID<<16)|(PID)), targetname
400 // the ini file needs to hold the IDs as hex values.
401 QMap
<int, QString
> usbids
= RbSettings::usbIdMap(RbSettings::MapDevice
);
402 QMap
<int, QString
> usberror
= RbSettings::usbIdMap(RbSettings::MapError
);
403 QMap
<int, QString
> usbincompat
= RbSettings::usbIdMap(RbSettings::MapIncompatible
);
406 QList
<uint32_t> attached
;
407 attached
= System::listUsbIds();
409 int i
= attached
.size();
411 if(usbids
.contains(attached
.at(i
))) {
412 m_device
= usbids
.value(attached
.at(i
));
413 qDebug() << "[USB] detected supported player" << m_device
;
416 if(usberror
.contains(attached
.at(i
))) {
417 m_errdev
= usberror
.value(attached
.at(i
));
418 qDebug() << "[USB] detected problem with player" << m_errdev
;
421 QString idstring
= QString("%1").arg(attached
.at(i
), 8, 16, QChar('0'));
422 if(!RbSettings::platformValue(idstring
, RbSettings::CurName
).toString().isEmpty()) {
423 m_incompat
= idstring
;
424 qDebug() << "[USB] detected incompatible player" << m_incompat
;
432 bool Autodetection::detectAjbrec(QString root
)
434 QFile
f(root
+ "/ajbrec.ajz");
436 f
.open(QIODevice::ReadOnly
);
437 if(!f
.read(header
, 24)) return false;
439 // check the header of the file.
440 // recorder v1 had a 6 bytes sized header
441 // recorder v2, FM, Ondio SP and FM have a 24 bytes header.
443 // recorder v1 has the binary length in the first 4 bytes, so check
445 int len
= (header
[0]<<24) | (header
[1]<<16) | (header
[2]<<8) | header
[3];
446 qDebug() << "[Autodetect] ABJREC possible bin length:" << len
447 << "file len:" << f
.size();
448 if((f
.size() - 6) == len
)
449 m_device
= "recorder";
451 // size didn't match, now we need to assume we have a headerlength of 24.
454 m_device
= "recorderv2";
458 m_device
= "fmrecorder";
462 m_device
= "ondiofm";
466 m_device
= "ondiosp";
474 if(m_device
.isEmpty()) return false;