1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
9 * Copyright (C) 2007 by Dominik Wenger
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)
26 #if defined(Q_OS_LINUX)
29 #if defined(Q_OS_MACX)
30 #include <sys/param.h>
31 #include <sys/ucred.h>
32 #include <sys/mount.h>
34 #if defined(Q_OS_WIN32)
44 Autodetection::Autodetection(QObject
* parent
): QObject(parent
)
49 bool Autodetection::detect()
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
));
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
;
80 // check rockbox-info.txt afterwards.
81 QFile
file(mountpoints
.at(i
) + "/.rockbox/rockbox-info.txt");
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
;
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
103 m_mountpoint
= mountpoints
.at(i
);
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
);
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
);
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
;
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
);
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
);
154 n
= ipod_scan(&ipod
);
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
);
165 struct sansa_t sansa
;
166 n
= sansa_scan(&sansa
);
168 qDebug() << "Sansa found:" << sansa
.targetname
<< "at" << sansa
.diskname
;
169 m_device
= QString("sansa%1").arg(sansa
.targetname
);
170 m_mountpoint
= resolveMountPoint(sansa
.diskname
);
174 if(m_mountpoint
.isEmpty() && m_device
.isEmpty() && m_errdev
.isEmpty())
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)
192 struct statfs
*mntinf
;
194 num
= getmntinfo(&mntinf
, MNT_WAIT
);
196 tempList
<< QString(mntinf
->f_mntonname
);
199 #elif defined(Q_OS_LINUX)
201 FILE *mn
= setmntent("/etc/mtab", "r");
203 return QStringList("");
206 while((ent
= getmntent(mn
)))
207 tempList
<< QString(ent
->mnt_dir
);
211 #error Unknown Plattform
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");
226 while((ent
= getmntent(mn
))) {
227 if(QString(ent
->mnt_fsname
).startsWith(device
)
228 && QString(ent
->mnt_type
).contains("vfat", Qt::CaseInsensitive
)) {
230 return QString(ent
->mnt_dir
);
237 #if defined(Q_OS_MACX)
239 struct statfs
*mntinf
;
241 num
= getmntinfo(&mntinf
, MNT_WAIT
);
243 if(QString(mntinf
->f_mntfromname
).startsWith(device
)
244 && QString(mntinf
->f_fstypename
).contains("vfat", Qt::CaseInsensitive
))
245 return QString(mntinf
->f_mntonname
);
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();
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();
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
);
287 #if defined(Q_OS_LINUX) | defined(Q_OS_MACX)
292 b
= usb_get_busses();
295 qDebug() << "bus:" << b
->dirname
<< b
->devices
;
297 qDebug() << "devices present.";
298 struct usb_device
*u
;
302 id
= u
->descriptor
.idVendor
<< 16 | u
->descriptor
.idProduct
;
303 m_usbconid
.append(id
);
306 if(usbids
.contains(id
)) {
307 m_device
= usbids
.value(id
);
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!";
323 #if defined(Q_OS_WIN32)
325 SP_DEVINFO_DATA infoData
;
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
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
++) {
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);
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);
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";
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
;
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!";
395 if(buffer
) free(buffer
);
397 SetupDiDestroyDeviceInfoList(deviceInfo
);
404 bool Autodetection::detectAjbrec(QString root
)
406 QFile
f(root
+ "/ajbrec.ajz");
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
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.
426 m_device
= "recorderv2";
430 m_device
= "fmrecorder";
434 m_device
= "ondiofm";
438 m_device
= "ondiosp";
446 if(m_device
.isEmpty()) return false;