Add w32 mountpoint resolving based on disc number correctly this time.
[Rockbox.git] / rbutil / rbutilqt / autodetection.cpp
blob7399907f1570683929cd7bf9928de197c1f07420
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 "autodetection.h"
22 #if defined(Q_OS_LINUX) || defined(Q_OS_MACX)
23 #include <stdio.h>
24 #include <usb.h>
25 #endif
26 #if defined(Q_OS_LINUX)
27 #include <mntent.h>
28 #endif
29 #if defined(Q_OS_MACX)
30 #include <sys/param.h>
31 #include <sys/ucred.h>
32 #include <sys/mount.h>
33 #endif
34 #if defined(Q_OS_WIN32)
35 #if defined(UNICODE)
36 #define _UNICODE
37 #endif
38 #include <stdio.h>
39 #include <tchar.h>
40 #include <windows.h>
41 #include <setupapi.h>
42 #include <winioctl.h>
43 #endif
44 #include "detect.h"
45 #include "utils.h"
47 Autodetection::Autodetection(QObject* parent): QObject(parent)
52 bool Autodetection::detect()
54 m_device = "";
55 m_mountpoint = "";
56 m_errdev = "";
58 detectUsb();
60 // Try detection via rockbox.info / rbutil.log
61 QStringList mountpoints = getMountpoints();
63 for(int i=0; i< mountpoints.size();i++)
65 // do the file checking
66 QDir dir(mountpoints.at(i));
67 if(dir.exists())
69 qDebug() << "file checking:" << mountpoints.at(i);
70 // check logfile first.
71 if(QFile(mountpoints.at(i) + "/.rockbox/rbutil.log").exists()) {
72 QSettings log(mountpoints.at(i) + "/.rockbox/rbutil.log",
73 QSettings::IniFormat, this);
74 if(!log.value("platform").toString().isEmpty()) {
75 if(m_device.isEmpty())
76 m_device = log.value("platform").toString();
77 m_mountpoint = mountpoints.at(i);
78 qDebug() << "rbutil.log detected:" << m_device << m_mountpoint;
79 return true;
83 // check rockbox-info.txt afterwards.
84 QFile file(mountpoints.at(i) + "/.rockbox/rockbox-info.txt");
85 if(file.exists())
87 file.open(QIODevice::ReadOnly | QIODevice::Text);
88 QString line = file.readLine();
89 if(line.startsWith("Target: "))
91 line.remove("Target: ");
92 if(m_device.isEmpty())
93 m_device = line.trimmed(); // trim whitespaces
94 m_mountpoint = mountpoints.at(i);
95 qDebug() << "rockbox-info.txt detected:" << m_device << m_mountpoint;
96 return true;
99 // check for some specific files in root folder
100 QDir root(mountpoints.at(i));
101 QStringList rootentries = root.entryList(QDir::Files);
102 if(rootentries.contains("archos.mod", Qt::CaseInsensitive))
104 // archos.mod in root folder -> Archos Player
105 m_device = "player";
106 m_mountpoint = mountpoints.at(i);
107 return true;
109 if(rootentries.contains("ONDIOST.BIN", Qt::CaseInsensitive))
111 // ONDIOST.BIN in root -> Ondio FM
112 m_device = "ondiofm";
113 m_mountpoint = mountpoints.at(i);
114 return true;
116 if(rootentries.contains("ONDIOSP.BIN", Qt::CaseInsensitive))
118 // ONDIOSP.BIN in root -> Ondio SP
119 m_device = "ondiosp";
120 m_mountpoint = mountpoints.at(i);
121 return true;
123 if(rootentries.contains("ajbrec.ajz", Qt::CaseInsensitive))
125 qDebug() << "ajbrec.ajz found. Trying detectAjbrec()";
126 if(detectAjbrec(mountpoints.at(i))) {
127 m_mountpoint = mountpoints.at(i);
128 qDebug() << m_device;
129 return true;
132 // detection based on player specific folders
133 QStringList rootfolders = root.entryList(QDir::Dirs
134 | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
135 if(rootfolders.contains("GBSYSTEM", Qt::CaseInsensitive))
137 // GBSYSTEM folder -> Gigabeat
138 m_device = "gigabeatf";
139 m_mountpoint = mountpoints.at(i);
140 return true;
142 #if defined(Q_OS_WIN32)
143 // on windows, try to detect the drive letter of an Ipod
144 if(rootfolders.contains("iPod_Control", Qt::CaseInsensitive))
146 // iPod_Control folder -> Ipod found
147 // detecting of the Ipod type is done below using ipodpatcher
148 m_mountpoint = mountpoints.at(i);
150 #endif
155 int n;
156 //try ipodpatcher
157 struct ipod_t ipod;
158 n = ipod_scan(&ipod);
159 if(n == 1) {
160 qDebug() << "Ipod found:" << ipod.modelstr << "at" << ipod.diskname;
161 m_device = ipod.targetname;
162 m_mountpoint = resolveMountPoint(ipod.diskname);
163 return true;
165 else {
166 qDebug() << "ipodpatcher: no Ipod found." << n;
169 //try sansapatcher
170 struct sansa_t sansa;
171 n = sansa_scan(&sansa);
172 if(n == 1) {
173 qDebug() << "Sansa found:" << sansa.targetname << "at" << sansa.diskname;
174 m_device = QString("sansa%1").arg(sansa.targetname);
175 m_mountpoint = resolveMountPoint(sansa.diskname);
176 return true;
178 else {
179 qDebug() << "sansapatcher: no Sansa found." << n;
182 if(m_mountpoint.isEmpty() && m_device.isEmpty() && m_errdev.isEmpty() && m_incompat.isEmpty())
183 return false;
184 return true;
188 QStringList Autodetection::getMountpoints()
190 QStringList tempList;
191 #if defined(Q_OS_WIN32)
192 QFileInfoList list = QDir::drives();
193 for(int i=0; i<list.size();i++)
195 tempList << list.at(i).absolutePath();
198 #elif defined(Q_OS_MACX)
199 int num;
200 struct statfs *mntinf;
202 num = getmntinfo(&mntinf, MNT_WAIT);
203 while(num--) {
204 tempList << QString(mntinf->f_mntonname);
205 mntinf++;
207 #elif defined(Q_OS_LINUX)
209 FILE *mn = setmntent("/etc/mtab", "r");
210 if(!mn)
211 return QStringList("");
213 struct mntent *ent;
214 while((ent = getmntent(mn)))
215 tempList << QString(ent->mnt_dir);
216 endmntent(mn);
218 #else
219 #error Unknown Plattform
220 #endif
221 return tempList;
224 QString Autodetection::resolveMountPoint(QString device)
226 qDebug() << "Autodetection::resolveMountPoint(QString)" << device;
228 #if defined(Q_OS_LINUX)
229 FILE *mn = setmntent("/etc/mtab", "r");
230 if(!mn)
231 return QString("");
233 struct mntent *ent;
234 while((ent = getmntent(mn))) {
235 if(QString(ent->mnt_fsname).startsWith(device)
236 && QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)) {
237 endmntent(mn);
238 return QString(ent->mnt_dir);
241 endmntent(mn);
243 #endif
245 #if defined(Q_OS_MACX)
246 int num;
247 struct statfs *mntinf;
249 num = getmntinfo(&mntinf, MNT_WAIT);
250 while(num--) {
251 if(QString(mntinf->f_mntfromname).startsWith(device)
252 && QString(mntinf->f_fstypename).contains("vfat", Qt::CaseInsensitive))
253 return QString(mntinf->f_mntonname);
254 mntinf++;
256 #endif
258 #if defined(Q_OS_WIN32)
259 QString result;
260 unsigned int driveno = device.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt();
262 for(int letter = 'A'; letter <= 'Z'; letter++) {
263 DWORD written;
264 HANDLE h;
265 TCHAR uncpath[MAX_PATH];
266 UCHAR buffer[0x400];
267 PVOLUME_DISK_EXTENTS extents = (PVOLUME_DISK_EXTENTS)buffer;
269 _stprintf(uncpath, _TEXT("\\\\.\\%c:"), letter);
270 h = CreateFile(uncpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
271 NULL, OPEN_EXISTING, 0, NULL);
272 if(h == INVALID_HANDLE_VALUE) {
273 //qDebug() << "error getting extents for" << uncpath;
274 continue;
276 // get the extents
277 if(DeviceIoControl(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
278 NULL, 0, extents, sizeof(buffer), &written, NULL)) {
279 for(unsigned int a = 0; a < extents->NumberOfDiskExtents; a++) {
280 qDebug() << "Disk:" << extents->Extents[a].DiskNumber;
281 if(extents->Extents[a].DiskNumber == driveno) {
282 result = letter;
283 qDebug("drive found for volume %i: %c", driveno, letter);
284 break;
291 return result + ":/";
292 #endif
293 return QString("");
297 /** @brief detect devices based on usb pid / vid.
298 * @return true upon success, false otherwise.
300 bool Autodetection::detectUsb()
302 // usbids holds the mapping in the form
303 // ((VID<<16)|(PID)), targetname
304 // the ini file needs to hold the IDs as hex values.
305 QMap<int, QString> usbids = settings->usbIdMap();
306 QMap<int, QString> usberror = settings->usbIdErrorMap();
307 QMap<int, QString> usbincompat = settings->usbIdIncompatMap();
309 // usb pid detection
310 QList<uint32_t> attached;
311 attached = Detect::listUsbIds();
313 int i = attached.size();
314 while(i--) {
315 if(usbids.contains(attached.at(i))) {
316 m_device = usbids.value(attached.at(i));
317 qDebug() << "[USB] detected supported player" << m_device;
318 return true;
320 if(usberror.contains(attached.at(i))) {
321 m_errdev = usberror.value(attached.at(i));
322 qDebug() << "[USB] detected problem with player" << m_errdev;
323 return true;
325 if(usbincompat.contains(attached.at(i))) {
326 m_incompat = usbincompat.value(attached.at(i));
327 qDebug() << "[USB] detected incompatible player" << m_incompat;
328 return true;
331 return false;
335 bool Autodetection::detectAjbrec(QString root)
337 QFile f(root + "/ajbrec.ajz");
338 char header[24];
339 f.open(QIODevice::ReadOnly);
340 if(!f.read(header, 24)) return false;
342 // check the header of the file.
343 // recorder v1 had a 6 bytes sized header
344 // recorder v2, FM, Ondio SP and FM have a 24 bytes header.
346 // recorder v1 has the binary length in the first 4 bytes, so check
347 // for them first.
348 int len = (header[0]<<24) | (header[1]<<16) | (header[2]<<8) | header[3];
349 qDebug() << "possible bin length:" << len;
350 qDebug() << "file len:" << f.size();
351 if((f.size() - 6) == len)
352 m_device = "recorder";
354 // size didn't match, now we need to assume we have a headerlength of 24.
355 switch(header[11]) {
356 case 2:
357 m_device = "recorderv2";
358 break;
360 case 4:
361 m_device = "fmrecorder";
362 break;
364 case 8:
365 m_device = "ondiofm";
366 break;
368 case 16:
369 m_device = "ondiosp";
370 break;
372 default:
373 break;
375 f.close();
377 if(m_device.isEmpty()) return false;
378 return true;