On windows check for iPod_Control folder to allow resolving the drive letter for...
[Rockbox.git] / rbutil / rbutilqt / autodetection.cpp
blobd9b77514a8473397559d8fa084aef8a226ecbbc2
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 #endif
44 Autodetection::Autodetection(QObject* parent): QObject(parent)
49 bool Autodetection::detect()
51 m_device = "";
52 m_mountpoint = "";
53 m_errdev = "";
55 detectUsb();
57 // Try detection via rockbox.info / rbutil.log
58 QStringList mountpoints = getMountpoints();
60 for(int i=0; i< mountpoints.size();i++)
62 // do the file checking
63 QDir dir(mountpoints.at(i));
64 if(dir.exists())
66 qDebug() << "file checking:" << mountpoints.at(i);
67 // check logfile first.
68 if(QFile(mountpoints.at(i) + "/.rockbox/rbutil.log").exists()) {
69 QSettings log(mountpoints.at(i) + "/.rockbox/rbutil.log",
70 QSettings::IniFormat, this);
71 if(!log.value("platform").toString().isEmpty()) {
72 if(m_device.isEmpty())
73 m_device = log.value("platform").toString();
74 m_mountpoint = mountpoints.at(i);
75 qDebug() << "rbutil.log detected:" << m_device << m_mountpoint;
76 return true;
80 // check rockbox-info.txt afterwards.
81 QFile file(mountpoints.at(i) + "/.rockbox/rockbox-info.txt");
82 if(file.exists())
84 file.open(QIODevice::ReadOnly | QIODevice::Text);
85 QString line = file.readLine();
86 if(line.startsWith("Target: "))
88 line.remove("Target: ");
89 if(m_device.isEmpty())
90 m_device = line.trimmed(); // trim whitespaces
91 m_mountpoint = mountpoints.at(i);
92 qDebug() << "rockbox-info.txt detected:" << m_device << m_mountpoint;
93 return true;
96 // check for some specific files in root folder
97 QDir root(mountpoints.at(i));
98 QStringList rootentries = root.entryList(QDir::Files);
99 if(rootentries.contains("archos.mod", Qt::CaseInsensitive))
101 // archos.mod in root folder -> Archos Player
102 m_device = "player";
103 m_mountpoint = mountpoints.at(i);
104 return true;
106 if(rootentries.contains("ONDIOST.BIN", Qt::CaseInsensitive))
108 // ONDIOST.BIN in root -> Ondio FM
109 m_device = "ondiofm";
110 m_mountpoint = mountpoints.at(i);
111 return true;
113 if(rootentries.contains("ONDIOSP.BIN", Qt::CaseInsensitive))
115 // ONDIOSP.BIN in root -> Ondio SP
116 m_device = "ondiosp";
117 m_mountpoint = mountpoints.at(i);
118 return true;
120 if(rootentries.contains("ajbrec.ajz", Qt::CaseInsensitive))
122 qDebug() << "ajbrec.ajz found. Trying detectAjbrec()";
123 if(detectAjbrec(mountpoints.at(i))) {
124 m_mountpoint = mountpoints.at(i);
125 qDebug() << m_device;
126 return true;
129 // detection based on player specific folders
130 QStringList rootfolders = root.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
131 if(rootfolders.contains("GBSYSTEM"), Qt::CaseInsensitive)
133 // GBSYSTEM folder -> Gigabeat
134 m_device = "gigabeatf";
135 m_mountpoint = mountpoints.at(i);
136 return true;
138 #if defined(Q_OS_WIN32)
139 // on windows, try to detect the drive letter of an Ipod
140 if(rootfolders.contains("iPod_Control"), Qt::CaseInsensitive)
142 // iPod_Control folder -> Ipod found
143 // detecting of the Ipod type is done below using ipodpatcher
144 m_mountpoint = mountpoints.at(i);
146 #endif
151 int n;
152 //try ipodpatcher
153 struct ipod_t ipod;
154 n = ipod_scan(&ipod);
155 if(n == 1) {
156 qDebug() << "Ipod found:" << ipod.modelstr << "at" << ipod.diskname;
157 m_device = ipod.targetname;
158 #if !defined(Q_OS_WIN32)
159 m_mountpoint = resolveMountPoint(ipod.diskname);
160 #endif
161 return true;
164 //try sansapatcher
165 struct sansa_t sansa;
166 n = sansa_scan(&sansa);
167 if(n == 1) {
168 qDebug() << "Sansa found:" << sansa.targetname << "at" << sansa.diskname;
169 m_device = QString("sansa%1").arg(sansa.targetname);
170 m_mountpoint = resolveMountPoint(sansa.diskname);
171 return true;
174 if(m_mountpoint.isEmpty() && m_device.isEmpty() && m_errdev.isEmpty())
175 return false;
176 return true;
180 QStringList Autodetection::getMountpoints()
182 QStringList tempList;
183 #if defined(Q_OS_WIN32)
184 QFileInfoList list = QDir::drives();
185 for(int i=0; i<list.size();i++)
187 tempList << list.at(i).absolutePath();
190 #elif defined(Q_OS_MACX)
191 int num;
192 struct statfs *mntinf;
194 num = getmntinfo(&mntinf, MNT_WAIT);
195 while(num--) {
196 tempList << QString(mntinf->f_mntonname);
197 mntinf++;
199 #elif defined(Q_OS_LINUX)
201 FILE *mn = setmntent("/etc/mtab", "r");
202 if(!mn)
203 return QStringList("");
205 struct mntent *ent;
206 while((ent = getmntent(mn)))
207 tempList << QString(ent->mnt_dir);
208 endmntent(mn);
210 #else
211 #error Unknown Plattform
212 #endif
213 return tempList;
216 QString Autodetection::resolveMountPoint(QString device)
218 qDebug() << "Autodetection::resolveMountPoint(QString)" << device;
220 #if defined(Q_OS_LINUX)
221 FILE *mn = setmntent("/etc/mtab", "r");
222 if(!mn)
223 return QString("");
225 struct mntent *ent;
226 while((ent = getmntent(mn))) {
227 if(QString(ent->mnt_fsname).startsWith(device)
228 && QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive)) {
229 endmntent(mn);
230 return QString(ent->mnt_dir);
233 endmntent(mn);
235 #endif
237 #if defined(Q_OS_MACX)
238 int num;
239 struct statfs *mntinf;
241 num = getmntinfo(&mntinf, MNT_WAIT);
242 while(num--) {
243 if(QString(mntinf->f_mntfromname).startsWith(device)
244 && QString(mntinf->f_fstypename).contains("vfat", Qt::CaseInsensitive))
245 return QString(mntinf->f_mntonname);
246 mntinf++;
248 #endif
249 return QString("");
254 /** @brief detect devices based on usb pid / vid.
255 * @return true upon success, false otherwise.
257 bool Autodetection::detectUsb()
259 // autodetection uses the buildin device settings only
260 QSettings dev(":/ini/rbutil.ini", QSettings::IniFormat, this);
262 // get a list of ID -> target name
263 QStringList platforms;
264 dev.beginGroup("platforms");
265 platforms = dev.childKeys();
266 dev.endGroup();
268 // usbids holds the mapping in the form
269 // ((VID<<16)|(PID)), targetname
270 // the ini file needs to hold the IDs as hex values.
271 QMap<int, QString> usbids;
272 QMap<int, QString> usberror;
274 for(int i = 0; i < platforms.size(); i++) {
275 dev.beginGroup("platforms");
276 QString target = dev.value(platforms.at(i)).toString();
277 dev.endGroup();
278 dev.beginGroup(target);
279 if(!dev.value("usbid").toString().isEmpty())
280 usbids.insert(dev.value("usbid").toString().toInt(0, 16), target);
281 if(!dev.value("usberror").toString().isEmpty())
282 usberror.insert(dev.value("usberror").toString().toInt(0, 16), target);
283 dev.endGroup();
286 // usb pid detection
287 #if defined(Q_OS_LINUX) | defined(Q_OS_MACX)
288 usb_init();
289 usb_find_busses();
290 usb_find_devices();
291 struct usb_bus *b;
292 b = usb_get_busses();
294 while(b) {
295 qDebug() << "bus:" << b->dirname << b->devices;
296 if(b->devices) {
297 qDebug() << "devices present.";
298 struct usb_device *u;
299 u = b->devices;
300 while(u) {
301 uint32_t id;
302 id = u->descriptor.idVendor << 16 | u->descriptor.idProduct;
303 m_usbconid.append(id);
304 qDebug("%x", id);
306 if(usbids.contains(id)) {
307 m_device = usbids.value(id);
308 return true;
310 if(usberror.contains(id)) {
311 m_errdev = usberror.value(id);
312 // we detected something, so return true
313 qDebug() << "detected device with problems via usb!";
314 return true;
316 u = u->next;
319 b = b->next;
321 #endif
323 #if defined(Q_OS_WIN32)
324 HDEVINFO deviceInfo;
325 SP_DEVINFO_DATA infoData;
326 DWORD i;
328 // Iterate over all devices
329 // by doing it this way it's unneccessary to use GUIDs which might be not
330 // present in current MinGW. It also seemed to be more reliably than using
331 // a GUID.
332 // See KB259695 for an example.
333 deviceInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
335 infoData.cbSize = sizeof(SP_DEVINFO_DATA);
337 for(i = 0; SetupDiEnumDeviceInfo(deviceInfo, i, &infoData); i++) {
338 DWORD data;
339 LPTSTR buffer = NULL;
340 DWORD buffersize = 0;
342 // get device desriptor first
343 // for some reason not doing so results in bad things (tm)
344 while(!SetupDiGetDeviceRegistryProperty(deviceInfo, &infoData,
345 SPDRP_DEVICEDESC,&data, (PBYTE)buffer, buffersize, &buffersize)) {
346 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
347 if(buffer) free(buffer);
348 // double buffer size to avoid problems as per KB888609
349 buffer = (LPTSTR)malloc(buffersize * 2);
351 else {
352 break;
356 // now get the hardware id, which contains PID and VID.
357 while(!SetupDiGetDeviceRegistryProperty(deviceInfo, &infoData,
358 SPDRP_HARDWAREID,&data, (PBYTE)buffer, buffersize, &buffersize)) {
359 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
360 if(buffer) free(buffer);
361 // double buffer size to avoid problems as per KB888609
362 buffer = (LPTSTR)malloc(buffersize * 2);
364 else {
365 break;
369 unsigned int vid, pid, rev;
370 if(_stscanf(buffer, _TEXT("USB\\Vid_%x&Pid_%x&Rev_%x"), &vid, &pid, &rev) != 3) {
371 qDebug() << "Error getting USB ID -- possibly no USB device";
373 else {
374 uint32_t id;
375 id = vid << 16 | pid;
376 m_usbconid.append(id);
377 qDebug("VID: %04x PID: %04x", vid, pid);
378 if(usbids.contains(id)) {
379 m_device = usbids.value(id);
380 if(buffer) free(buffer);
381 SetupDiDestroyDeviceInfoList(deviceInfo);
382 qDebug() << "detectUsb: Got" << m_device;
383 return true;
385 if(usberror.contains(id)) {
386 m_errdev = usberror.value(id);
387 // we detected something, so return true
388 if(buffer) free(buffer);
389 SetupDiDestroyDeviceInfoList(deviceInfo);
390 qDebug() << "detectUsb: Got" << m_device;
391 qDebug() << "detected device with problems via usb!";
392 return true;
395 if(buffer) free(buffer);
397 SetupDiDestroyDeviceInfoList(deviceInfo);
399 #endif
400 return false;
404 bool Autodetection::detectAjbrec(QString root)
406 QFile f(root + "/ajbrec.ajz");
407 char header[24];
408 f.open(QIODevice::ReadOnly);
409 if(!f.read(header, 24)) return false;
411 // check the header of the file.
412 // recorder v1 had a 6 bytes sized header
413 // recorder v2, FM, Ondio SP and FM have a 24 bytes header.
415 // recorder v1 has the binary length in the first 4 bytes, so check
416 // for them first.
417 int len = (header[0]<<24) | (header[1]<<16) | (header[2]<<8) | header[3];
418 qDebug() << "possible bin length:" << len;
419 qDebug() << "file len:" << f.size();
420 if((f.size() - 6) == len)
421 m_device = "recorder";
423 // size didn't match, now we need to assume we have a headerlength of 24.
424 switch(header[11]) {
425 case 2:
426 m_device = "recorderv2";
427 break;
429 case 4:
430 m_device = "fmrecorder";
431 break;
433 case 8:
434 m_device = "ondiofm";
435 break;
437 case 16:
438 m_device = "ondiosp";
439 break;
441 default:
442 break;
444 f.close();
446 if(m_device.isEmpty()) return false;
447 return true;