Rearange menu of mpegplayer. Add new menu with "settings" and "quit", and remove...
[kugel-rb.git] / rbutil / rbutilqt / base / autodetection.cpp
blobb038aa683d55dfb9e1cb834ef52cf724bd0ee1f9
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"
24 #include "../ipodpatcher/ipodpatcher.h"
25 #include "../sansapatcher/sansapatcher.h"
27 #if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
28 #include <stdio.h>
29 #include <usb.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();
101 m_mountpoint = mounts.at(i);
102 qDebug() << "[Autodetect] rockbox-info.txt detected:"
103 << m_device << m_mountpoint;
104 return true;
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
113 m_device = "player";
114 m_mountpoint = mounts.at(i);
115 return true;
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);
122 return true;
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);
129 return true;
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;
137 return true;
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);
148 return true;
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);
158 #endif
163 int n;
164 // try ipodpatcher
165 // initialize sector buffer. Needed.
166 ipod_sectorbuf = NULL;
167 ipod_alloc_buffer(&ipod_sectorbuf, BUFFER_SIZE);
168 struct ipod_t ipod;
169 n = ipod_scan(&ipod);
170 if(n == 1) {
171 qDebug() << "[Autodetect] Ipod found:" << ipod.modelstr << "at" << ipod.diskname;
172 m_device = ipod.targetname;
173 m_mountpoint = resolveMountPoint(ipod.diskname);
174 return true;
176 else {
177 qDebug() << "[Autodetect] ipodpatcher: no Ipod found." << n;
179 free(ipod_sectorbuf);
180 ipod_sectorbuf = NULL;
182 // try sansapatcher
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);
188 if(n == 1) {
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);
192 return true;
194 else {
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())
202 return false;
203 return true;
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)
218 int num;
219 struct statfs *mntinf;
221 num = getmntinfo(&mntinf, MNT_WAIT);
222 while(num--) {
223 tempList << QString(mntinf->f_mntonname);
224 mntinf++;
226 #elif defined(Q_OS_LINUX)
228 FILE *mn = setmntent("/etc/mtab", "r");
229 if(!mn)
230 return QStringList("");
232 struct mntent *ent;
233 while((ent = getmntent(mn)))
234 tempList << QString(ent->mnt_dir);
235 endmntent(mn);
237 #else
238 #error Unknown Plattform
239 #endif
240 return tempList;
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");
254 if(!mn)
255 return QString("");
257 struct mntent *ent;
258 while((ent = getmntent(mn))) {
259 if(QString(ent->mnt_fsname).startsWith(device)
260 && QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)) {
261 endmntent(mn);
262 return QString(ent->mnt_dir);
265 endmntent(mn);
267 #endif
269 #if defined(Q_OS_MACX)
270 int num;
271 struct statfs *mntinf;
273 num = getmntinfo(&mntinf, MNT_WAIT);
274 while(num--) {
275 if(QString(mntinf->f_mntfromname).startsWith(device)
276 && QString(mntinf->f_fstypename).contains("vfat", Qt::CaseInsensitive))
277 return QString(mntinf->f_mntonname);
278 mntinf++;
280 #endif
282 #if defined(Q_OS_OPENBSD)
283 int num;
284 struct statfs *mntinf;
286 num = getmntinfo(&mntinf, MNT_WAIT);
287 while(num--) {
288 if(QString(mntinf->f_mntfromname).startsWith(device)
289 && QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive))
290 return QString(mntinf->f_mntonname);
291 mntinf++;
293 #endif
295 #if defined(Q_OS_WIN32)
296 QString result;
297 unsigned int driveno = device.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt();
299 int letter;
300 for(letter = 'A'; letter <= 'Z'; letter++) {
301 if(resolveDevicename(QString(letter)).toUInt() == driveno) {
302 result = letter;
303 break;
306 qDebug() << "[Autodetect] resolved mountpoint is:" << result;
307 if(!result.isEmpty())
308 return result + ":/";
309 #endif
310 return QString("");
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");
323 if(!mn)
324 return QString("");
326 struct mntent *ent;
327 while((ent = getmntent(mn))) {
328 if(QString(ent->mnt_dir).startsWith(path)
329 && QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)) {
330 endmntent(mn);
331 return QString(ent->mnt_fsname);
334 endmntent(mn);
336 #endif
338 #if defined(Q_OS_MACX)
339 int num;
340 struct statfs *mntinf;
342 num = getmntinfo(&mntinf, MNT_WAIT);
343 while(num--) {
344 if(QString(mntinf->f_mntonname).startsWith(path)
345 && QString(mntinf->f_fstypename).contains("vfat", Qt::CaseInsensitive))
346 return QString(mntinf->f_mntfromname);
347 mntinf++;
349 #endif
351 #if defined(Q_OS_OPENBSD)
352 int num;
353 struct statfs *mntinf;
355 num = getmntinfo(&mntinf, MNT_WAIT);
356 while(num--) {
357 if(QString(mntinf->f_mntonname).startsWith(device)
358 && QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive))
359 return QString(mntinf->f_mntfromname);
360 mntinf++;
362 #endif
363 #if defined(Q_OS_WIN32)
364 DWORD written;
365 HANDLE h;
366 TCHAR uncpath[MAX_PATH];
367 UCHAR buffer[0x400];
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;
375 return "";
377 // get the extents
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!";
382 return "";
384 //qDebug() << "Disk:" << extents->Extents[0].DiskNumber;
385 return QString("%1").arg(extents->Extents[0].DiskNumber);
387 #endif
388 return QString("");
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);
405 // usb pid detection
406 QList<uint32_t> attached;
407 attached = System::listUsbIds();
409 int i = attached.size();
410 while(i--) {
411 if(usbids.contains(attached.at(i))) {
412 m_device = usbids.value(attached.at(i));
413 qDebug() << "[USB] detected supported player" << m_device;
414 return true;
416 if(usberror.contains(attached.at(i))) {
417 m_errdev = usberror.value(attached.at(i));
418 qDebug() << "[USB] detected problem with player" << m_errdev;
419 return true;
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;
425 return true;
428 return false;
432 bool Autodetection::detectAjbrec(QString root)
434 QFile f(root + "/ajbrec.ajz");
435 char header[24];
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
444 // for them first.
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.
452 switch(header[11]) {
453 case 2:
454 m_device = "recorderv2";
455 break;
457 case 4:
458 m_device = "fmrecorder";
459 break;
461 case 8:
462 m_device = "ondiofm";
463 break;
465 case 16:
466 m_device = "ondiosp";
467 break;
469 default:
470 break;
472 f.close();
474 if(m_device.isEmpty()) return false;
475 return true;