Fix wpsbuild to properly generate "-" for theme related settings (to get the default...
[kugel-rb.git] / rbutil / rbutilqt / base / autodetection.cpp
blobbafa88d8bf6a77eb7d9c00f0da69988d72969de1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
9 * Copyright (C) 2007 by Dominik Wenger
10 * $Id$
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 ****************************************************************************/
20 #include <QtCore>
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)
29 #include <stdio.h>
30 #endif
31 #if defined(Q_OS_LINUX)
32 #include <mntent.h>
33 #endif
34 #if defined(Q_OS_MACX)
35 #include <sys/param.h>
36 #include <sys/ucred.h>
37 #include <sys/mount.h>
38 #endif
39 #if defined(Q_OS_WIN32)
40 #if defined(UNICODE)
41 #define _UNICODE
42 #endif
43 #include <stdio.h>
44 #include <tchar.h>
45 #include <windows.h>
46 #include <setupapi.h>
47 #include <winioctl.h>
48 #endif
50 #if defined(Q_OS_OPENBSD)
51 #include <sys/param.h>
52 #include <sys/mount.h>
53 #endif
55 #include "system.h"
56 #include "utils.h"
58 Autodetection::Autodetection(QObject* parent): QObject(parent)
62 bool Autodetection::detect()
64 m_device = "";
65 m_mountpoint = "";
66 m_errdev = "";
68 detectUsb();
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;
78 if(dir.exists())
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;
89 return true;
93 // check rockbox-info.txt afterwards.
94 RockboxInfo info(mounts.at(i));
95 if(info.success())
97 if(m_device.isEmpty())
99 m_device = info.target();
100 // special case for video64mb. This is a workaround, and
101 // should get replaced when autodetection is reworked.
102 if(m_device == "ipodvideo" && info.ram() == 64)
104 m_device = "ipodvideo64mb";
107 m_mountpoint = mounts.at(i);
108 qDebug() << "[Autodetect] rockbox-info.txt detected:"
109 << m_device << m_mountpoint;
110 return true;
113 // check for some specific files in root folder
114 QDir root(mounts.at(i));
115 QStringList rootentries = root.entryList(QDir::Files);
116 if(rootentries.contains("archos.mod", Qt::CaseInsensitive))
118 // archos.mod in root folder -> Archos Player
119 m_device = "player";
120 m_mountpoint = mounts.at(i);
121 return true;
123 if(rootentries.contains("ONDIOST.BIN", Qt::CaseInsensitive))
125 // ONDIOST.BIN in root -> Ondio FM
126 m_device = "ondiofm";
127 m_mountpoint = mounts.at(i);
128 return true;
130 if(rootentries.contains("ONDIOSP.BIN", Qt::CaseInsensitive))
132 // ONDIOSP.BIN in root -> Ondio SP
133 m_device = "ondiosp";
134 m_mountpoint = mounts.at(i);
135 return true;
137 if(rootentries.contains("ajbrec.ajz", Qt::CaseInsensitive))
139 qDebug() << "[Autodetect] ajbrec.ajz found. Trying detectAjbrec()";
140 if(detectAjbrec(mounts.at(i))) {
141 m_mountpoint = mounts.at(i);
142 qDebug() << "[Autodetect]" << m_device;
143 return true;
146 // detection based on player specific folders
147 QStringList rootfolders = root.entryList(QDir::Dirs
148 | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
149 if(rootfolders.contains("GBSYSTEM", Qt::CaseInsensitive))
151 // GBSYSTEM folder -> Gigabeat
152 m_device = "gigabeatf";
153 m_mountpoint = mounts.at(i);
154 return true;
156 #if defined(Q_OS_WIN32)
157 // on windows, try to detect the drive letter of an Ipod
158 if(rootfolders.contains("iPod_Control", Qt::CaseInsensitive))
160 // iPod_Control folder -> Ipod found
161 // detecting of the Ipod type is done below using ipodpatcher
162 m_mountpoint = mounts.at(i);
164 #endif
169 int n;
170 // try ipodpatcher
171 // initialize sector buffer. Needed.
172 ipod_sectorbuf = NULL;
173 ipod_alloc_buffer(&ipod_sectorbuf, BUFFER_SIZE);
174 struct ipod_t ipod;
175 n = ipod_scan(&ipod);
176 if(n == 1) {
177 qDebug() << "[Autodetect] Ipod found:" << ipod.modelstr << "at" << ipod.diskname;
178 // if the found ipod is a macpod also notice it as device with problem.
179 if(ipod.macpod)
180 m_errdev = ipod.targetname;
181 m_device = ipod.targetname;
182 m_mountpoint = resolveMountPoint(ipod.diskname);
183 return true;
185 else {
186 qDebug() << "[Autodetect] ipodpatcher: no Ipod found." << n;
188 free(ipod_sectorbuf);
189 ipod_sectorbuf = NULL;
191 // try sansapatcher
192 // initialize sector buffer. Needed.
193 sansa_sectorbuf = NULL;
194 sansa_alloc_buffer(&sansa_sectorbuf, BUFFER_SIZE);
195 struct sansa_t sansa;
196 n = sansa_scan(&sansa);
197 if(n == 1) {
198 qDebug() << "[Autodetect] Sansa found:" << sansa.targetname << "at" << sansa.diskname;
199 m_device = QString("sansa%1").arg(sansa.targetname);
200 m_mountpoint = resolveMountPoint(sansa.diskname);
201 return true;
203 else {
204 qDebug() << "[Autodetect] sansapatcher: no Sansa found." << n;
206 free(sansa_sectorbuf);
207 sansa_sectorbuf = NULL;
209 if(m_mountpoint.isEmpty() && m_device.isEmpty()
210 && m_errdev.isEmpty() && m_incompat.isEmpty())
211 return false;
212 return true;
216 QStringList Autodetection::mountpoints()
218 QStringList tempList;
219 #if defined(Q_OS_WIN32)
220 QFileInfoList list = QDir::drives();
221 for(int i=0; i<list.size();i++)
223 tempList << list.at(i).absolutePath();
226 #elif defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
227 int num;
228 struct statfs *mntinf;
230 num = getmntinfo(&mntinf, MNT_WAIT);
231 while(num--) {
232 tempList << QString(mntinf->f_mntonname);
233 mntinf++;
235 #elif defined(Q_OS_LINUX)
237 FILE *mn = setmntent("/etc/mtab", "r");
238 if(!mn)
239 return QStringList("");
241 struct mntent *ent;
242 while((ent = getmntent(mn)))
243 tempList << QString(ent->mnt_dir);
244 endmntent(mn);
246 #else
247 #error Unknown Plattform
248 #endif
249 return tempList;
253 /** resolve device name to mount point / drive letter
254 * @param device device name / disk number
255 * @return mount point / drive letter
257 QString Autodetection::resolveMountPoint(QString device)
259 qDebug() << "[Autodetect] resolving mountpoint:" << device;
261 #if defined(Q_OS_LINUX)
262 FILE *mn = setmntent("/etc/mtab", "r");
263 if(!mn)
264 return QString("");
266 struct mntent *ent;
267 while((ent = getmntent(mn))) {
268 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
269 // MacPod.
270 if(QString(ent->mnt_fsname) == device
271 && (QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)
272 || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive))) {
273 endmntent(mn);
274 qDebug() << "[Autodetect] resolved mountpoint is:" << ent->mnt_dir;
275 return QString(ent->mnt_dir);
278 endmntent(mn);
280 #endif
282 #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
283 int num;
284 struct statfs *mntinf;
286 num = getmntinfo(&mntinf, MNT_WAIT);
287 while(num--) {
288 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
289 // MacPod.
290 if(QString(mntinf->f_mntfromname) == device
291 && (QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive)
292 || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive))) {
293 qDebug() << "[Autodetect] resolved mountpoint is:" << mntinf->f_mntonname;
294 return QString(mntinf->f_mntonname);
296 mntinf++;
298 #endif
300 #if defined(Q_OS_WIN32)
301 QString result;
302 unsigned int driveno = device.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt();
304 int letter;
305 for(letter = 'A'; letter <= 'Z'; letter++) {
306 if(resolveDevicename(QString(letter)).toUInt() == driveno) {
307 result = letter;
308 break;
311 qDebug() << "[Autodetect] resolved mountpoint is:" << result;
312 if(!result.isEmpty())
313 return result + ":/";
314 #endif
315 return QString("");
319 /** Resolve mountpoint to devicename / disk number
320 * @param path mountpoint path / drive letter
321 * @return devicename / disk number
323 QString Autodetection::resolveDevicename(QString path)
325 qDebug() << "[Autodetect] resolving device name" << path;
326 #if defined(Q_OS_LINUX)
327 FILE *mn = setmntent("/etc/mtab", "r");
328 if(!mn)
329 return QString("");
331 struct mntent *ent;
332 while((ent = getmntent(mn))) {
333 // check for valid filesystem type.
334 // Linux can handle hfs (and hfsplus), so consider it a valid file
335 // system. Otherwise resolving the device name would fail, which in
336 // turn would make it impossible to warn about a MacPod.
337 if(QString(ent->mnt_dir) == path
338 && (QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)
339 || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive))) {
340 endmntent(mn);
341 qDebug() << "[Autodetect] device name is" << ent->mnt_fsname;
342 return QString(ent->mnt_fsname);
345 endmntent(mn);
347 #endif
349 #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
350 int num;
351 struct statfs *mntinf;
353 num = getmntinfo(&mntinf, MNT_WAIT);
354 while(num--) {
355 // check for valid filesystem type. OS X can handle hfs (hfs+ is
356 // treated as hfs), BSD should be the same.
357 if(QString(mntinf->f_mntonname) == path
358 && (QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive)
359 || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive))) {
360 qDebug() << "[Autodetect] device name is" << mntinf->f_mntfromname;
361 return QString(mntinf->f_mntfromname);
363 mntinf++;
365 #endif
367 #if defined(Q_OS_WIN32)
368 DWORD written;
369 HANDLE h;
370 TCHAR uncpath[MAX_PATH];
371 UCHAR buffer[0x400];
372 PVOLUME_DISK_EXTENTS extents = (PVOLUME_DISK_EXTENTS)buffer;
374 _stprintf(uncpath, _TEXT("\\\\.\\%c:"), path.toAscii().at(0));
375 h = CreateFile(uncpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
376 NULL, OPEN_EXISTING, 0, NULL);
377 if(h == INVALID_HANDLE_VALUE) {
378 //qDebug() << "error getting extents for" << uncpath;
379 return "";
381 // get the extents
382 if(DeviceIoControl(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
383 NULL, 0, extents, sizeof(buffer), &written, NULL)) {
384 if(extents->NumberOfDiskExtents > 1) {
385 qDebug() << "[Autodetect] resolving device name: volume spans multiple disks!";
386 return "";
388 qDebug() << "[Autodetect] device name is" << extents->Extents[0].DiskNumber;
389 return QString("%1").arg(extents->Extents[0].DiskNumber);
391 #endif
392 return QString("");
397 /** @brief detect devices based on usb pid / vid.
398 * @return true upon success, false otherwise.
400 bool Autodetection::detectUsb()
402 // usbids holds the mapping in the form
403 // ((VID<<16)|(PID)), targetname
404 // the ini file needs to hold the IDs as hex values.
405 QMap<int, QString> usbids = SystemInfo::usbIdMap(SystemInfo::MapDevice);
406 QMap<int, QString> usberror = SystemInfo::usbIdMap(SystemInfo::MapError);
407 QMap<int, QString> usbincompat = SystemInfo::usbIdMap(SystemInfo::MapIncompatible);
409 // usb pid detection
410 QList<uint32_t> attached;
411 attached = System::listUsbIds();
413 int i = attached.size();
414 while(i--) {
415 if(usbids.contains(attached.at(i))) {
416 m_device = usbids.value(attached.at(i));
417 qDebug() << "[USB] detected supported player" << m_device;
418 return true;
420 if(usberror.contains(attached.at(i))) {
421 m_errdev = usberror.value(attached.at(i));
422 qDebug() << "[USB] detected problem with player" << m_errdev;
423 return true;
425 QString idstring = QString("%1").arg(attached.at(i), 8, 16, QChar('0'));
426 if(!SystemInfo::platformValue(idstring, SystemInfo::CurName).toString().isEmpty()) {
427 m_incompat = idstring;
428 qDebug() << "[USB] detected incompatible player" << m_incompat;
429 return true;
432 return false;
436 bool Autodetection::detectAjbrec(QString root)
438 QFile f(root + "/ajbrec.ajz");
439 char header[24];
440 f.open(QIODevice::ReadOnly);
441 if(!f.read(header, 24)) return false;
443 // check the header of the file.
444 // recorder v1 had a 6 bytes sized header
445 // recorder v2, FM, Ondio SP and FM have a 24 bytes header.
447 // recorder v1 has the binary length in the first 4 bytes, so check
448 // for them first.
449 int len = (header[0]<<24) | (header[1]<<16) | (header[2]<<8) | header[3];
450 qDebug() << "[Autodetect] ABJREC possible bin length:" << len
451 << "file len:" << f.size();
452 if((f.size() - 6) == len)
453 m_device = "recorder";
455 // size didn't match, now we need to assume we have a headerlength of 24.
456 switch(header[11]) {
457 case 2:
458 m_device = "recorderv2";
459 break;
461 case 4:
462 m_device = "fmrecorder";
463 break;
465 case 8:
466 m_device = "ondiofm";
467 break;
469 case 16:
470 m_device = "ondiosp";
471 break;
473 default:
474 break;
476 f.close();
478 if(m_device.isEmpty()) return false;
479 return true;