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"
23 #include "systeminfo.h"
25 #include "../ipodpatcher/ipodpatcher.h"
26 #include "../sansapatcher/sansapatcher.h"
28 #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>
57 #include "rockboxinfo.h"
59 Autodetection::Autodetection(QObject
* parent
): QObject(parent
)
63 bool Autodetection::detect()
71 // Try detection via rockbox.info / rbutil.log
72 QStringList mounts
= mountpoints();
74 for(int i
=0; i
< mounts
.size();i
++)
76 // do the file checking
77 QDir
dir(mounts
.at(i
));
78 qDebug() << "[Autodetect] paths to check:" << mounts
;
81 // check logfile first.
82 if(QFile(mounts
.at(i
) + "/.rockbox/rbutil.log").exists()) {
83 QSettings
log(mounts
.at(i
) + "/.rockbox/rbutil.log",
84 QSettings::IniFormat
, this);
85 if(!log
.value("platform").toString().isEmpty()) {
86 if(m_device
.isEmpty())
87 m_device
= log
.value("platform").toString();
88 m_mountpoint
= mounts
.at(i
);
89 qDebug() << "[Autodetect] rbutil.log detected:" << m_device
<< m_mountpoint
;
94 // check rockbox-info.txt afterwards.
95 RockboxInfo
info(mounts
.at(i
));
98 if(m_device
.isEmpty())
100 m_device
= info
.target();
101 // special case for video64mb. This is a workaround, and
102 // should get replaced when autodetection is reworked.
103 if(m_device
== "ipodvideo" && info
.ram() == 64)
105 m_device
= "ipodvideo64mb";
108 m_mountpoint
= mounts
.at(i
);
109 qDebug() << "[Autodetect] rockbox-info.txt detected:"
110 << m_device
<< m_mountpoint
;
114 // check for some specific files in root folder
115 QDir
root(mounts
.at(i
));
116 QStringList rootentries
= root
.entryList(QDir::Files
);
117 if(rootentries
.contains("archos.mod", Qt::CaseInsensitive
))
119 // archos.mod in root folder -> Archos Player
121 m_mountpoint
= mounts
.at(i
);
124 if(rootentries
.contains("ONDIOST.BIN", Qt::CaseInsensitive
))
126 // ONDIOST.BIN in root -> Ondio FM
127 m_device
= "ondiofm";
128 m_mountpoint
= mounts
.at(i
);
131 if(rootentries
.contains("ONDIOSP.BIN", Qt::CaseInsensitive
))
133 // ONDIOSP.BIN in root -> Ondio SP
134 m_device
= "ondiosp";
135 m_mountpoint
= mounts
.at(i
);
138 if(rootentries
.contains("ajbrec.ajz", Qt::CaseInsensitive
))
140 qDebug() << "[Autodetect] ajbrec.ajz found. Trying detectAjbrec()";
141 if(detectAjbrec(mounts
.at(i
))) {
142 m_mountpoint
= mounts
.at(i
);
143 qDebug() << "[Autodetect]" << m_device
;
147 // detection based on player specific folders
148 QStringList rootfolders
= root
.entryList(QDir::Dirs
149 | QDir::NoDotAndDotDot
| QDir::Hidden
| QDir::System
);
150 if(rootfolders
.contains("GBSYSTEM", Qt::CaseInsensitive
))
152 // GBSYSTEM folder -> Gigabeat
153 m_device
= "gigabeatf";
154 m_mountpoint
= mounts
.at(i
);
157 #if defined(Q_OS_WIN32)
158 // on windows, try to detect the drive letter of an Ipod
159 if(rootfolders
.contains("iPod_Control", Qt::CaseInsensitive
))
161 // iPod_Control folder -> Ipod found
162 // detecting of the Ipod type is done below using ipodpatcher
163 m_mountpoint
= mounts
.at(i
);
172 // initialize sector buffer. Needed.
173 ipod_sectorbuf
= NULL
;
174 ipod_alloc_buffer(&ipod_sectorbuf
, BUFFER_SIZE
);
176 n
= ipod_scan(&ipod
);
178 qDebug() << "[Autodetect] Ipod found:" << ipod
.modelstr
<< "at" << ipod
.diskname
;
179 // if the found ipod is a macpod also notice it as device with problem.
181 m_errdev
= ipod
.targetname
;
182 m_device
= ipod
.targetname
;
183 // since resolveMountPoint is doing exact matches we need to select
184 // the correct partition.
185 QString
mp(ipod
.diskname
);
192 m_mountpoint
= resolveMountPoint(mp
);
196 qDebug() << "[Autodetect] ipodpatcher: no Ipod found." << n
;
198 free(ipod_sectorbuf
);
199 ipod_sectorbuf
= NULL
;
202 // initialize sector buffer. Needed.
203 sansa_sectorbuf
= NULL
;
204 sansa_alloc_buffer(&sansa_sectorbuf
, BUFFER_SIZE
);
205 struct sansa_t sansa
;
206 n
= sansa_scan(&sansa
);
208 qDebug() << "[Autodetect] Sansa found:" << sansa
.targetname
<< "at" << sansa
.diskname
;
209 m_device
= QString("sansa%1").arg(sansa
.targetname
);
210 QString
mp(sansa
.diskname
);
217 m_mountpoint
= resolveMountPoint(mp
);
221 qDebug() << "[Autodetect] sansapatcher: no Sansa found." << n
;
223 free(sansa_sectorbuf
);
224 sansa_sectorbuf
= NULL
;
226 if(m_mountpoint
.isEmpty() && m_device
.isEmpty()
227 && m_errdev
.isEmpty() && m_incompat
.isEmpty())
233 QStringList
Autodetection::mountpoints()
235 QStringList tempList
;
236 #if defined(Q_OS_WIN32)
237 QFileInfoList list
= QDir::drives();
238 for(int i
=0; i
<list
.size();i
++)
240 tempList
<< list
.at(i
).absolutePath();
241 qDebug() << "[Autodetection] Mounted on" << list
.at(i
).absolutePath();
244 #elif defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
246 struct statfs
*mntinf
;
248 num
= getmntinfo(&mntinf
, MNT_WAIT
);
250 tempList
<< QString(mntinf
->f_mntonname
);
251 qDebug() << "[Autodetection] Mounted on" << mntinf
->f_mntonname
252 << "is" << mntinf
->f_mntfromname
<< "type" << mntinf
->f_fstypename
;
255 #elif defined(Q_OS_LINUX)
257 FILE *mn
= setmntent("/etc/mtab", "r");
259 return QStringList("");
262 while((ent
= getmntent(mn
))) {
263 tempList
<< QString(ent
->mnt_dir
);
264 qDebug() << "[Autodetection] Mounted on" << ent
->mnt_dir
265 << "is" << ent
->mnt_fsname
<< "type" << ent
->mnt_type
;
270 #error Unknown Plattform
276 /** resolve device name to mount point / drive letter
277 * @param device device name / disk number
278 * @return mount point / drive letter
280 QString
Autodetection::resolveMountPoint(QString device
)
282 qDebug() << "[Autodetect] resolving mountpoint:" << device
;
284 #if defined(Q_OS_LINUX)
285 FILE *mn
= setmntent("/etc/mtab", "r");
290 while((ent
= getmntent(mn
))) {
291 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
293 if(QString(ent
->mnt_fsname
) == device
) {
295 if(QString(ent
->mnt_type
).contains("vfat", Qt::CaseInsensitive
)
296 || QString(ent
->mnt_type
).contains("hfs", Qt::CaseInsensitive
)) {
297 qDebug() << "[Autodetect] resolved mountpoint is:" << ent
->mnt_dir
;
298 result
= QString(ent
->mnt_dir
);
301 qDebug() << "[Autodetect] mountpoint is wrong filesystem!";
311 #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
313 struct statfs
*mntinf
;
315 num
= getmntinfo(&mntinf
, MNT_WAIT
);
317 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
319 if(QString(mntinf
->f_mntfromname
) == device
) {
320 if(QString(mntinf
->f_fstypename
).contains("msdos", Qt::CaseInsensitive
)
321 || QString(mntinf
->f_fstypename
).contains("hfs", Qt::CaseInsensitive
)) {
322 qDebug() << "[Autodetect] resolved mountpoint is:" << mntinf
->f_mntonname
;
323 return QString(mntinf
->f_mntonname
);
326 qDebug() << "[Autodetect] mountpoint is wrong filesystem!";
334 #if defined(Q_OS_WIN32)
336 unsigned int driveno
= device
.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt();
339 for(letter
= 'A'; letter
<= 'Z'; letter
++) {
340 if(resolveDevicename(QString(letter
)).toUInt() == driveno
) {
342 qDebug() << "[Autodetect] resolved mountpoint is:" << result
;
346 if(!result
.isEmpty())
347 return result
+ ":/";
349 qDebug() << "[Autodetect] resolving mountpoint failed!";
354 /** Resolve mountpoint to devicename / disk number
355 * @param path mountpoint path / drive letter
356 * @return devicename / disk number
358 QString
Autodetection::resolveDevicename(QString path
)
360 qDebug() << "[Autodetect] resolving device name" << path
;
361 #if defined(Q_OS_LINUX)
362 FILE *mn
= setmntent("/etc/mtab", "r");
367 while((ent
= getmntent(mn
))) {
368 // check for valid filesystem type.
369 // Linux can handle hfs (and hfsplus), so consider it a valid file
370 // system. Otherwise resolving the device name would fail, which in
371 // turn would make it impossible to warn about a MacPod.
372 if(QString(ent
->mnt_dir
) == path
373 && (QString(ent
->mnt_type
).contains("vfat", Qt::CaseInsensitive
)
374 || QString(ent
->mnt_type
).contains("hfs", Qt::CaseInsensitive
))) {
376 qDebug() << "[Autodetect] device name is" << ent
->mnt_fsname
;
377 return QString(ent
->mnt_fsname
);
384 #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
386 struct statfs
*mntinf
;
388 num
= getmntinfo(&mntinf
, MNT_WAIT
);
390 // check for valid filesystem type. OS X can handle hfs (hfs+ is
391 // treated as hfs), BSD should be the same.
392 if(QString(mntinf
->f_mntonname
) == path
393 && (QString(mntinf
->f_fstypename
).contains("msdos", Qt::CaseInsensitive
)
394 || QString(mntinf
->f_fstypename
).contains("hfs", Qt::CaseInsensitive
))) {
395 qDebug() << "[Autodetect] device name is" << mntinf
->f_mntfromname
;
396 return QString(mntinf
->f_mntfromname
);
402 #if defined(Q_OS_WIN32)
405 TCHAR uncpath
[MAX_PATH
];
407 PVOLUME_DISK_EXTENTS extents
= (PVOLUME_DISK_EXTENTS
)buffer
;
409 _stprintf(uncpath
, _TEXT("\\\\.\\%c:"), path
.toAscii().at(0));
410 h
= CreateFile(uncpath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
411 NULL
, OPEN_EXISTING
, 0, NULL
);
412 if(h
== INVALID_HANDLE_VALUE
) {
413 //qDebug() << "error getting extents for" << uncpath;
417 if(DeviceIoControl(h
, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
,
418 NULL
, 0, extents
, sizeof(buffer
), &written
, NULL
)) {
419 if(extents
->NumberOfDiskExtents
> 1) {
420 qDebug() << "[Autodetect] resolving device name: volume spans multiple disks!";
423 qDebug() << "[Autodetect] device name is" << extents
->Extents
[0].DiskNumber
;
424 return QString("%1").arg(extents
->Extents
[0].DiskNumber
);
432 /** @brief detect devices based on usb pid / vid.
433 * @return true upon success, false otherwise.
435 bool Autodetection::detectUsb()
437 // usbids holds the mapping in the form
438 // ((VID<<16)|(PID)), targetname
439 // the ini file needs to hold the IDs as hex values.
440 QMap
<int, QString
> usbids
= SystemInfo::usbIdMap(SystemInfo::MapDevice
);
441 QMap
<int, QString
> usberror
= SystemInfo::usbIdMap(SystemInfo::MapError
);
442 QMap
<int, QString
> usbincompat
= SystemInfo::usbIdMap(SystemInfo::MapIncompatible
);
445 QList
<uint32_t> attached
;
446 attached
= System::listUsbIds();
448 int i
= attached
.size();
450 if(usbids
.contains(attached
.at(i
))) {
451 m_device
= usbids
.value(attached
.at(i
));
452 qDebug() << "[USB] detected supported player" << m_device
;
455 if(usberror
.contains(attached
.at(i
))) {
456 m_errdev
= usberror
.value(attached
.at(i
));
457 qDebug() << "[USB] detected problem with player" << m_errdev
;
460 QString idstring
= QString("%1").arg(attached
.at(i
), 8, 16, QChar('0'));
461 if(!SystemInfo::platformValue(idstring
, SystemInfo::CurName
).toString().isEmpty()) {
462 m_incompat
= idstring
;
463 qDebug() << "[USB] detected incompatible player" << m_incompat
;
471 bool Autodetection::detectAjbrec(QString root
)
473 QFile
f(root
+ "/ajbrec.ajz");
475 f
.open(QIODevice::ReadOnly
);
476 if(!f
.read(header
, 24)) return false;
478 // check the header of the file.
479 // recorder v1 had a 6 bytes sized header
480 // recorder v2, FM, Ondio SP and FM have a 24 bytes header.
482 // recorder v1 has the binary length in the first 4 bytes, so check
484 int len
= (header
[0]<<24) | (header
[1]<<16) | (header
[2]<<8) | header
[3];
485 qDebug() << "[Autodetect] ABJREC possible bin length:" << len
486 << "file len:" << f
.size();
487 if((f
.size() - 6) == len
)
488 m_device
= "recorder";
490 // size didn't match, now we need to assume we have a headerlength of 24.
493 m_device
= "recorderv2";
497 m_device
= "fmrecorder";
501 m_device
= "ondiofm";
505 m_device
= "ondiosp";
513 if(m_device
.isEmpty()) return false;