Packard Bell Vibe 500: correct the path to a proper one in rbutil (proper directory...
[kugel-rb.git] / rbutil / rbutilqt / base / autodetection.cpp
blob98d24c3369c77a16f911a59f9fce14fdccd8f94c
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 // since resolveMountPoint is doing exact matches we need to select
183 // the correct partition.
184 QString mp(ipod.diskname);
185 #ifdef Q_OS_LINUX
186 mp.append("2");
187 #endif
188 #ifdef Q_OS_MACX
189 mp.append("s2");
190 #endif
191 m_mountpoint = resolveMountPoint(mp);
192 return true;
194 else {
195 qDebug() << "[Autodetect] ipodpatcher: no Ipod found." << n;
197 free(ipod_sectorbuf);
198 ipod_sectorbuf = NULL;
200 // try sansapatcher
201 // initialize sector buffer. Needed.
202 sansa_sectorbuf = NULL;
203 sansa_alloc_buffer(&sansa_sectorbuf, BUFFER_SIZE);
204 struct sansa_t sansa;
205 n = sansa_scan(&sansa);
206 if(n == 1) {
207 qDebug() << "[Autodetect] Sansa found:" << sansa.targetname << "at" << sansa.diskname;
208 m_device = QString("sansa%1").arg(sansa.targetname);
209 QString mp(sansa.diskname);
210 #ifdef Q_OS_LINUX
211 mp.append("1");
212 #endif
213 #ifdef Q_OS_MACX
214 mp.append("s1");
215 #endif
216 m_mountpoint = resolveMountPoint(mp);
217 return true;
219 else {
220 qDebug() << "[Autodetect] sansapatcher: no Sansa found." << n;
222 free(sansa_sectorbuf);
223 sansa_sectorbuf = NULL;
225 if(m_mountpoint.isEmpty() && m_device.isEmpty()
226 && m_errdev.isEmpty() && m_incompat.isEmpty())
227 return false;
228 return true;
232 QStringList Autodetection::mountpoints()
234 QStringList tempList;
235 #if defined(Q_OS_WIN32)
236 QFileInfoList list = QDir::drives();
237 for(int i=0; i<list.size();i++)
239 tempList << list.at(i).absolutePath();
242 #elif defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
243 int num;
244 struct statfs *mntinf;
246 num = getmntinfo(&mntinf, MNT_WAIT);
247 while(num--) {
248 tempList << QString(mntinf->f_mntonname);
249 mntinf++;
251 #elif defined(Q_OS_LINUX)
253 FILE *mn = setmntent("/etc/mtab", "r");
254 if(!mn)
255 return QStringList("");
257 struct mntent *ent;
258 while((ent = getmntent(mn)))
259 tempList << QString(ent->mnt_dir);
260 endmntent(mn);
262 #else
263 #error Unknown Plattform
264 #endif
265 return tempList;
269 /** resolve device name to mount point / drive letter
270 * @param device device name / disk number
271 * @return mount point / drive letter
273 QString Autodetection::resolveMountPoint(QString device)
275 qDebug() << "[Autodetect] resolving mountpoint:" << device;
277 #if defined(Q_OS_LINUX)
278 FILE *mn = setmntent("/etc/mtab", "r");
279 if(!mn)
280 return QString("");
282 struct mntent *ent;
283 while((ent = getmntent(mn))) {
284 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
285 // MacPod.
286 if(QString(ent->mnt_fsname) == device
287 && (QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)
288 || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive))) {
289 endmntent(mn);
290 qDebug() << "[Autodetect] resolved mountpoint is:" << ent->mnt_dir;
291 return QString(ent->mnt_dir);
294 endmntent(mn);
296 #endif
298 #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
299 int num;
300 struct statfs *mntinf;
302 num = getmntinfo(&mntinf, MNT_WAIT);
303 while(num--) {
304 // Check for valid filesystem. Allow hfs too, as an Ipod might be a
305 // MacPod.
306 if(QString(mntinf->f_mntfromname) == device
307 && (QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive)
308 || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive))) {
309 qDebug() << "[Autodetect] resolved mountpoint is:" << mntinf->f_mntonname;
310 return QString(mntinf->f_mntonname);
312 mntinf++;
314 #endif
316 #if defined(Q_OS_WIN32)
317 QString result;
318 unsigned int driveno = device.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt();
320 int letter;
321 for(letter = 'A'; letter <= 'Z'; letter++) {
322 if(resolveDevicename(QString(letter)).toUInt() == driveno) {
323 result = letter;
324 break;
327 qDebug() << "[Autodetect] resolved mountpoint is:" << result;
328 if(!result.isEmpty())
329 return result + ":/";
330 #endif
331 return QString("");
335 /** Resolve mountpoint to devicename / disk number
336 * @param path mountpoint path / drive letter
337 * @return devicename / disk number
339 QString Autodetection::resolveDevicename(QString path)
341 qDebug() << "[Autodetect] resolving device name" << path;
342 #if defined(Q_OS_LINUX)
343 FILE *mn = setmntent("/etc/mtab", "r");
344 if(!mn)
345 return QString("");
347 struct mntent *ent;
348 while((ent = getmntent(mn))) {
349 // check for valid filesystem type.
350 // Linux can handle hfs (and hfsplus), so consider it a valid file
351 // system. Otherwise resolving the device name would fail, which in
352 // turn would make it impossible to warn about a MacPod.
353 if(QString(ent->mnt_dir) == path
354 && (QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)
355 || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive))) {
356 endmntent(mn);
357 qDebug() << "[Autodetect] device name is" << ent->mnt_fsname;
358 return QString(ent->mnt_fsname);
361 endmntent(mn);
363 #endif
365 #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
366 int num;
367 struct statfs *mntinf;
369 num = getmntinfo(&mntinf, MNT_WAIT);
370 while(num--) {
371 // check for valid filesystem type. OS X can handle hfs (hfs+ is
372 // treated as hfs), BSD should be the same.
373 if(QString(mntinf->f_mntonname) == path
374 && (QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive)
375 || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive))) {
376 qDebug() << "[Autodetect] device name is" << mntinf->f_mntfromname;
377 return QString(mntinf->f_mntfromname);
379 mntinf++;
381 #endif
383 #if defined(Q_OS_WIN32)
384 DWORD written;
385 HANDLE h;
386 TCHAR uncpath[MAX_PATH];
387 UCHAR buffer[0x400];
388 PVOLUME_DISK_EXTENTS extents = (PVOLUME_DISK_EXTENTS)buffer;
390 _stprintf(uncpath, _TEXT("\\\\.\\%c:"), path.toAscii().at(0));
391 h = CreateFile(uncpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
392 NULL, OPEN_EXISTING, 0, NULL);
393 if(h == INVALID_HANDLE_VALUE) {
394 //qDebug() << "error getting extents for" << uncpath;
395 return "";
397 // get the extents
398 if(DeviceIoControl(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
399 NULL, 0, extents, sizeof(buffer), &written, NULL)) {
400 if(extents->NumberOfDiskExtents > 1) {
401 qDebug() << "[Autodetect] resolving device name: volume spans multiple disks!";
402 return "";
404 qDebug() << "[Autodetect] device name is" << extents->Extents[0].DiskNumber;
405 return QString("%1").arg(extents->Extents[0].DiskNumber);
407 #endif
408 return QString("");
413 /** @brief detect devices based on usb pid / vid.
414 * @return true upon success, false otherwise.
416 bool Autodetection::detectUsb()
418 // usbids holds the mapping in the form
419 // ((VID<<16)|(PID)), targetname
420 // the ini file needs to hold the IDs as hex values.
421 QMap<int, QString> usbids = SystemInfo::usbIdMap(SystemInfo::MapDevice);
422 QMap<int, QString> usberror = SystemInfo::usbIdMap(SystemInfo::MapError);
423 QMap<int, QString> usbincompat = SystemInfo::usbIdMap(SystemInfo::MapIncompatible);
425 // usb pid detection
426 QList<uint32_t> attached;
427 attached = System::listUsbIds();
429 int i = attached.size();
430 while(i--) {
431 if(usbids.contains(attached.at(i))) {
432 m_device = usbids.value(attached.at(i));
433 qDebug() << "[USB] detected supported player" << m_device;
434 return true;
436 if(usberror.contains(attached.at(i))) {
437 m_errdev = usberror.value(attached.at(i));
438 qDebug() << "[USB] detected problem with player" << m_errdev;
439 return true;
441 QString idstring = QString("%1").arg(attached.at(i), 8, 16, QChar('0'));
442 if(!SystemInfo::platformValue(idstring, SystemInfo::CurName).toString().isEmpty()) {
443 m_incompat = idstring;
444 qDebug() << "[USB] detected incompatible player" << m_incompat;
445 return true;
448 return false;
452 bool Autodetection::detectAjbrec(QString root)
454 QFile f(root + "/ajbrec.ajz");
455 char header[24];
456 f.open(QIODevice::ReadOnly);
457 if(!f.read(header, 24)) return false;
459 // check the header of the file.
460 // recorder v1 had a 6 bytes sized header
461 // recorder v2, FM, Ondio SP and FM have a 24 bytes header.
463 // recorder v1 has the binary length in the first 4 bytes, so check
464 // for them first.
465 int len = (header[0]<<24) | (header[1]<<16) | (header[2]<<8) | header[3];
466 qDebug() << "[Autodetect] ABJREC possible bin length:" << len
467 << "file len:" << f.size();
468 if((f.size() - 6) == len)
469 m_device = "recorder";
471 // size didn't match, now we need to assume we have a headerlength of 24.
472 switch(header[11]) {
473 case 2:
474 m_device = "recorderv2";
475 break;
477 case 4:
478 m_device = "fmrecorder";
479 break;
481 case 8:
482 m_device = "ondiofm";
483 break;
485 case 16:
486 m_device = "ondiosp";
487 break;
489 default:
490 break;
492 f.close();
494 if(m_device.isEmpty()) return false;
495 return true;