You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
586 lines
14 KiB
586 lines
14 KiB
/* This file is part of the KDE Project
|
|
Copyright (c) 2003 Gav Wood <gav kde org>
|
|
Copyright (c) 2004 Kévin Ottens <ervin ipsquad net>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/* Some code of this file comes from kdeautorun */
|
|
|
|
#include "linuxcdpolling.h"
|
|
|
|
#include <tqthread.h>
|
|
#include <tqmutex.h>
|
|
#include <tqfile.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include "fstabbackend.h"
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
// Never ever include directly a kernel header!
|
|
// #include <linux/cdrom.h>
|
|
// Instead we redefine the necessary (copied from the header)
|
|
|
|
/* This struct is used by the CDROMREADTOCHDR ioctl */
|
|
struct cdrom_tochdr
|
|
{
|
|
unsigned char cdth_trk0; /* start track */
|
|
unsigned char cdth_trk1; /* end track */
|
|
};
|
|
|
|
#define CDROMREADTOCHDR 0x5305 /* Read TOC header
|
|
(struct cdrom_tochdr) */
|
|
#define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
|
|
#define CDROM_DISC_STATUS 0x5327 /* Get disc type, etc. */
|
|
|
|
/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
|
|
#define CDS_NO_INFO 0 /* if not implemented */
|
|
#define CDS_NO_DISC 1
|
|
#define CDS_TRAY_OPEN 2
|
|
#define CDS_DRIVE_NOT_READY 3
|
|
#define CDS_DISC_OK 4
|
|
|
|
/* return values for the CDROM_DISC_STATUS ioctl */
|
|
/* can also return CDS_NO_[INFO|DISC], from above */
|
|
#define CDS_AUDIO 100
|
|
#define CDS_DATA_1 101
|
|
#define CDS_DATA_2 102
|
|
#define CDS_XA_2_1 103
|
|
#define CDS_XA_2_2 104
|
|
#define CDS_MIXED 105
|
|
|
|
#define CDSL_CURRENT ((int) (~0U>>1))
|
|
|
|
// -------
|
|
|
|
|
|
|
|
DiscType::DiscType(Type type)
|
|
: m_type(type)
|
|
{
|
|
}
|
|
|
|
bool DiscType::isKnownDisc() const
|
|
{
|
|
return m_type != None
|
|
&& m_type != Unknown
|
|
&& m_type != UnknownType
|
|
&& m_type != Broken;
|
|
}
|
|
|
|
bool DiscType::isDisc() const
|
|
{
|
|
return m_type != None
|
|
&& m_type != Unknown
|
|
&& m_type != Broken;
|
|
}
|
|
|
|
bool DiscType::isNotDisc() const
|
|
{
|
|
return m_type == None;
|
|
}
|
|
|
|
bool DiscType::isData() const
|
|
{
|
|
return m_type == Data;
|
|
}
|
|
|
|
DiscType::operator int() const
|
|
{
|
|
return (int)m_type;
|
|
}
|
|
|
|
|
|
class PollingThread : public TQThread
|
|
{
|
|
public:
|
|
PollingThread(const TQCString &devNode) : m_dev(devNode)
|
|
{
|
|
kdDebug(1219) << "PollingThread::PollingThread("
|
|
<< devNode << ")" << endl;
|
|
m_stop = false;
|
|
m_currentType = DiscType::None;
|
|
m_lastPollType = DiscType::None;
|
|
}
|
|
|
|
|
|
void stop()
|
|
{
|
|
TQMutexLocker locker(&m_mutex);
|
|
m_stop = true;
|
|
}
|
|
|
|
bool hasChanged()
|
|
{
|
|
TQMutexLocker locker(&m_mutex);
|
|
|
|
return m_currentType!=m_lastPollType;
|
|
}
|
|
|
|
DiscType type()
|
|
{
|
|
TQMutexLocker locker(&m_mutex);
|
|
m_currentType = m_lastPollType;
|
|
return m_currentType;
|
|
}
|
|
|
|
protected:
|
|
virtual void run()
|
|
{
|
|
kdDebug(1219) << "PollingThread(" << m_dev << ") start" << endl;
|
|
while (!m_stop && m_lastPollType!=DiscType::Broken)
|
|
{
|
|
m_mutex.lock();
|
|
DiscType type = m_lastPollType;
|
|
m_mutex.unlock();
|
|
|
|
type = LinuxCDPolling::identifyDiscType(m_dev, type);
|
|
|
|
m_mutex.lock();
|
|
m_lastPollType = type;
|
|
m_mutex.unlock();
|
|
|
|
msleep(500);
|
|
}
|
|
kdDebug(1219) << "PollingThread(" << m_dev << ") stop" << endl;
|
|
}
|
|
|
|
private:
|
|
TQMutex m_mutex;
|
|
bool m_stop;
|
|
const TQCString m_dev;
|
|
DiscType m_currentType;
|
|
DiscType m_lastPollType;
|
|
};
|
|
|
|
|
|
LinuxCDPolling::LinuxCDPolling(MediaList &list)
|
|
: TQObject(), BackendBase(list)
|
|
{
|
|
connect(&m_mediaList, TQT_SIGNAL(mediumAdded(const TQString &,
|
|
const TQString &, bool)),
|
|
this, TQT_SLOT(slotMediumAdded(const TQString &)) );
|
|
|
|
connect(&m_mediaList, TQT_SIGNAL(mediumRemoved(const TQString &,
|
|
const TQString &, bool)),
|
|
this, TQT_SLOT(slotMediumRemoved(const TQString &)) );
|
|
|
|
connect(&m_mediaList, TQT_SIGNAL(mediumStateChanged(const TQString &,
|
|
const TQString &, bool, bool)),
|
|
this, TQT_SLOT(slotMediumStateChanged(const TQString &)) );
|
|
|
|
connect(&m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotTimeout()));
|
|
}
|
|
|
|
LinuxCDPolling::~LinuxCDPolling()
|
|
{
|
|
TQMap<TQString, PollingThread*>::iterator it = m_threads.begin();
|
|
TQMap<TQString, PollingThread*>::iterator end = m_threads.end();
|
|
|
|
for(; it!=end; ++it)
|
|
{
|
|
PollingThread *thread = it.data();
|
|
thread->stop();
|
|
thread->wait();
|
|
delete thread;
|
|
}
|
|
}
|
|
|
|
void LinuxCDPolling::slotMediumAdded(const TQString &id)
|
|
{
|
|
kdDebug(1219) << "LinuxCDPolling::slotMediumAdded(" << id << ")" << endl;
|
|
|
|
if (m_threads.tqcontains(id)) return;
|
|
|
|
const Medium *medium = m_mediaList.findById(id);
|
|
|
|
TQString mime = medium->mimeType();
|
|
kdDebug(1219) << "mime == " << mime << endl;
|
|
|
|
if (mime.tqfind("dvd")==-1 && mime.tqfind("cd")==-1) return;
|
|
|
|
if (!medium->isMounted())
|
|
{
|
|
m_excludeNotification.append( id );
|
|
|
|
TQCString dev = TQFile::encodeName( medium->deviceNode() ).data();
|
|
PollingThread *thread = new PollingThread(dev);
|
|
m_threads[id] = thread;
|
|
thread->start();
|
|
m_timer.start(500);
|
|
}
|
|
}
|
|
|
|
void LinuxCDPolling::slotMediumRemoved(const TQString &id)
|
|
{
|
|
kdDebug(1219) << "LinuxCDPolling::slotMediumRemoved(" << id << ")" << endl;
|
|
|
|
if (!m_threads.tqcontains(id)) return;
|
|
|
|
PollingThread *thread = m_threads[id];
|
|
m_threads.remove(id);
|
|
thread->stop();
|
|
thread->wait();
|
|
delete thread;
|
|
|
|
m_excludeNotification.remove(id);
|
|
}
|
|
|
|
void LinuxCDPolling::slotMediumStateChanged(const TQString &id)
|
|
{
|
|
kdDebug(1219) << "LinuxCDPolling::slotMediumStateChanged("
|
|
<< id << ")" << endl;
|
|
|
|
const Medium *medium = m_mediaList.findById(id);
|
|
|
|
TQString mime = medium->mimeType();
|
|
kdDebug(1219) << "mime == " << mime << endl;
|
|
|
|
if (mime.tqfind("dvd")==-1 && mime.tqfind("cd")==-1) return;
|
|
|
|
if (!m_threads.tqcontains(id) && !medium->isMounted())
|
|
{
|
|
// It is just a mount state change, no need to notify
|
|
m_excludeNotification.append( id );
|
|
|
|
TQCString dev = TQFile::encodeName( medium->deviceNode() ).data();
|
|
PollingThread *thread = new PollingThread(dev);
|
|
m_threads[id] = thread;
|
|
thread->start();
|
|
m_timer.start(500);
|
|
}
|
|
else if (m_threads.tqcontains(id) && medium->isMounted())
|
|
{
|
|
PollingThread *thread = m_threads[id];
|
|
m_threads.remove(id);
|
|
thread->stop();
|
|
thread->wait();
|
|
delete thread;
|
|
}
|
|
}
|
|
|
|
void LinuxCDPolling::slotTimeout()
|
|
{
|
|
//kdDebug(1219) << "LinuxCDPolling::slotTimeout()" << endl;
|
|
|
|
if (m_threads.isEmpty())
|
|
{
|
|
m_timer.stop();
|
|
return;
|
|
}
|
|
|
|
TQMap<TQString, PollingThread*>::iterator it = m_threads.begin();
|
|
TQMap<TQString, PollingThread*>::iterator end = m_threads.end();
|
|
|
|
for(; it!=end; ++it)
|
|
{
|
|
TQString id = it.key();
|
|
PollingThread *thread = it.data();
|
|
|
|
if (thread->hasChanged())
|
|
{
|
|
DiscType type = thread->type();
|
|
const Medium *medium = m_mediaList.findById(id);
|
|
applyType(type, medium);
|
|
}
|
|
}
|
|
}
|
|
|
|
static TQString baseType(const Medium *medium)
|
|
{
|
|
kdDebug(1219) << "baseType(" << medium->id() << ")" << endl;
|
|
|
|
TQString devNode = medium->deviceNode();
|
|
TQString mountPoint = medium->mountPoint();
|
|
TQString fsType = medium->fsType();
|
|
bool mounted = medium->isMounted();
|
|
|
|
TQString mimeType, iconName, label;
|
|
|
|
FstabBackend::guess(devNode, mountPoint, fsType, mounted,
|
|
mimeType, iconName, label);
|
|
|
|
if (devNode.tqfind("dvd")!=-1)
|
|
{
|
|
kdDebug(1219) << "=> dvd" << endl;
|
|
return "dvd";
|
|
}
|
|
else
|
|
{
|
|
kdDebug(1219) << "=> cd" << endl;
|
|
return "cd";
|
|
}
|
|
}
|
|
|
|
static void restoreEmptyState(MediaList &list, const Medium *medium,
|
|
bool allowNotification)
|
|
{
|
|
kdDebug(1219) << "restoreEmptyState(" << medium->id() << ")" << endl;
|
|
|
|
TQString id = medium->id();
|
|
TQString devNode = medium->deviceNode();
|
|
TQString mountPoint = medium->mountPoint();
|
|
TQString fsType = medium->fsType();
|
|
bool mounted = medium->isMounted();
|
|
|
|
TQString mimeType, iconName, label;
|
|
|
|
FstabBackend::guess(devNode, mountPoint, fsType, mounted,
|
|
mimeType, iconName, label);
|
|
|
|
list.changeMediumState(id, devNode, mountPoint, fsType, mounted,
|
|
allowNotification, mimeType, iconName, label);
|
|
}
|
|
|
|
|
|
void LinuxCDPolling::applyType(DiscType type, const Medium *medium)
|
|
{
|
|
kdDebug(1219) << "LinuxCDPolling::applyType(" << type << ", "
|
|
<< medium->id() << ")" << endl;
|
|
|
|
TQString id = medium->id();
|
|
TQString dev = medium->deviceNode();
|
|
|
|
bool notify = !m_excludeNotification.tqcontains(id);
|
|
m_excludeNotification.remove(id);
|
|
|
|
switch (type)
|
|
{
|
|
case DiscType::Data:
|
|
restoreEmptyState(m_mediaList, medium, notify);
|
|
break;
|
|
case DiscType::Audio:
|
|
case DiscType::Mixed:
|
|
m_mediaList.changeMediumState(id, "audiocd:/?device="+dev,
|
|
notify, "media/audiocd");
|
|
break;
|
|
case DiscType::VCD:
|
|
m_mediaList.changeMediumState(id, false, notify, "media/vcd");
|
|
break;
|
|
case DiscType::SVCD:
|
|
m_mediaList.changeMediumState(id, false, notify, "media/svcd");
|
|
break;
|
|
case DiscType::DVD:
|
|
m_mediaList.changeMediumState(id, false, notify, "media/dvdvideo");
|
|
break;
|
|
case DiscType::Blank:
|
|
if (baseType(medium)=="dvd")
|
|
{
|
|
m_mediaList.changeMediumState(id, false,
|
|
notify, "media/blankdvd");
|
|
}
|
|
else
|
|
{
|
|
m_mediaList.changeMediumState(id, false,
|
|
notify, "media/blankcd");
|
|
}
|
|
break;
|
|
case DiscType::None:
|
|
case DiscType::Unknown:
|
|
case DiscType::UnknownType:
|
|
restoreEmptyState(m_mediaList, medium, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
DiscType LinuxCDPolling::identifyDiscType(const TQCString &devNode,
|
|
const DiscType ¤t)
|
|
{
|
|
//kdDebug(1219) << "LinuxCDPolling::identifyDiscType("
|
|
// << devNode << ")" << endl;
|
|
|
|
int fd;
|
|
struct cdrom_tochdr th;
|
|
|
|
// open the device
|
|
fd = open(devNode, O_RDONLY | O_NONBLOCK);
|
|
if (fd < 0) return DiscType::Broken;
|
|
|
|
switch (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT))
|
|
{
|
|
case CDS_DISC_OK:
|
|
{
|
|
if (current.isDisc())
|
|
{
|
|
close(fd);
|
|
return current;
|
|
}
|
|
|
|
// see if we can read the disc's table of contents (TOC).
|
|
if (ioctl(fd, CDROMREADTOCHDR, &th))
|
|
{
|
|
close(fd);
|
|
return DiscType::Blank;
|
|
}
|
|
|
|
// read disc status info
|
|
int status = ioctl(fd, CDROM_DISC_STATUS, CDSL_CURRENT);
|
|
|
|
// release the device
|
|
close(fd);
|
|
|
|
switch (status)
|
|
{
|
|
case CDS_AUDIO:
|
|
return DiscType::Audio;
|
|
case CDS_DATA_1:
|
|
case CDS_DATA_2:
|
|
if (hasDirectory(devNode, "video_ts"))
|
|
{
|
|
return DiscType::DVD;
|
|
}
|
|
else if (hasDirectory(devNode, "vcd"))
|
|
{
|
|
return DiscType::VCD;
|
|
}
|
|
else if (hasDirectory(devNode, "svcd"))
|
|
{
|
|
return DiscType::SVCD;
|
|
}
|
|
else
|
|
{
|
|
return DiscType::Data;
|
|
}
|
|
case CDS_MIXED:
|
|
return DiscType::Mixed;
|
|
default:
|
|
return DiscType::UnknownType;
|
|
}
|
|
}
|
|
case CDS_NO_INFO:
|
|
close(fd);
|
|
return DiscType::Unknown;
|
|
default:
|
|
close(fd);
|
|
return DiscType::None;
|
|
}
|
|
}
|
|
|
|
bool LinuxCDPolling::hasDirectory(const TQCString &devNode, const TQCString &dir)
|
|
{
|
|
bool ret = false; // return value
|
|
int fd = 0; // file descriptor for drive
|
|
unsigned short bs; // the discs block size
|
|
unsigned short ts; // the path table size
|
|
unsigned int tl; // the path table location (in blocks)
|
|
unsigned char len_di = 0; // length of the directory name in current path table entry
|
|
unsigned int parent = 0; // the number of the parent directory's path table entry
|
|
char dirname[256]; // filename for the current path table entry
|
|
int pos = 0; // our position into the path table
|
|
int curr_record = 1; // the path table record we're on
|
|
TQCString fixed_directory = dir.upper(); // the uppercase version of the "directory" parameter
|
|
|
|
// open the drive
|
|
fd = open(devNode, O_RDONLY | O_NONBLOCK);
|
|
if (fd == -1) return false;
|
|
|
|
// read the block size
|
|
lseek(fd, 0x8080, SEEK_CUR);
|
|
if (read(fd, &bs, 2) != 2)
|
|
{
|
|
close(fd);
|
|
return false;
|
|
}
|
|
if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN)
|
|
bs = ((bs << 8) & 0xFF00) | ((bs >> 8) & 0xFF);
|
|
|
|
// read in size of path table
|
|
lseek(fd, 2, SEEK_CUR);
|
|
if (read(fd, &ts, 2) != 2)
|
|
{
|
|
close(fd);
|
|
return false;
|
|
}
|
|
if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN)
|
|
ts = ((ts << 8) & 0xFF00) | ((ts >> 8) & 0xFF);
|
|
|
|
// read in which block path table is in
|
|
lseek(fd, 6, SEEK_CUR);
|
|
if (read(fd, &tl, 4) != 4)
|
|
{
|
|
close(fd);
|
|
return false;
|
|
}
|
|
if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN)
|
|
tl = ((tl << 24) & 0xFF000000) | ((tl << 8) & 0xFF0000) |
|
|
((tl >> 8) & 0xFF00) | ((tl >> 24) & 0xFF);
|
|
|
|
// seek to the path table
|
|
lseek(fd, bs * tl, SEEK_SET);
|
|
|
|
// loop through the path table entries
|
|
while (pos < ts)
|
|
{
|
|
// get the length of the filename of the current entry
|
|
if (read(fd, &len_di, 1) != 1)
|
|
{
|
|
ret = false;
|
|
break;
|
|
}
|
|
|
|
// get the record number of this entry's parent
|
|
// i'm pretty sure that the 1st entry is always the top directory
|
|
lseek(fd, 5, SEEK_CUR);
|
|
if (read(fd, &parent, 2) != 2)
|
|
{
|
|
ret = false;
|
|
break;
|
|
}
|
|
if (Q_BYTE_ORDER != Q_LITTLE_ENDIAN)
|
|
parent = ((parent << 8) & 0xFF00) | ((parent >> 8) & 0xFF);
|
|
|
|
// read the name
|
|
if (read(fd, dirname, len_di) != len_di)
|
|
{
|
|
ret = false;
|
|
break;
|
|
}
|
|
dirname[len_di] = 0;
|
|
qstrcpy(dirname, TQCString(dirname).upper());
|
|
|
|
// if we found a folder that has the root as a parent, and the directory name matches
|
|
// then return success
|
|
if ((parent == 1) && (dirname == fixed_directory))
|
|
{
|
|
ret = true;
|
|
break;
|
|
}
|
|
|
|
// all path table entries are padded to be even, so if this is an odd-length table, seek a byte to fix it
|
|
if (len_di%2 == 1)
|
|
{
|
|
lseek(fd, 1, SEEK_CUR);
|
|
pos++;
|
|
}
|
|
|
|
// update our position
|
|
pos += 8 + len_di;
|
|
curr_record++;
|
|
}
|
|
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#include "linuxcdpolling.moc"
|