/*
 * cdstatus.c
 * watch cd/dvd drive status, especially the eject button of a locked drive
 * parts of the code were taken from auto-eject-cdrom.c:
 * Copyright (C) 2001 Jens Axboe <axboe@suse.de>
 * Modified and subsequently
 *   Copyright (C) 2005 Peter Willis <psyphreak@phreaker.net>
 *   (still no license on the thing as the original author included no license;
 *   maybe i should email him for permission?)
 */
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mount.h>

#include <vdr/thread.h>

#include <linux/cdrom.h>

#include "cdstatus.h"

int debug = 0;

cCDStatus::cCDStatus(char *device)
:cThread("cd drive watch thread")
{
	running = false;
	cd_device = device;
	mediaStatus = mediaNoChange;
	fd = open(device, O_RDONLY | O_NONBLOCK);
}

cCDStatus::~cCDStatus()
{
	running = false;
	newSet.Signal();
	Cancel(3);
}

bool cCDStatus::Start(void)
{											
	if (fd > 0)
	{
		running = true;
		return cThread::Start();
	}
	return false;
}

int cCDStatus::WaitCmd(int fd, struct cdrom_generic_command *cgc, unsigned char *buffer,
	     int len, int ddir, int timeout)
{
	struct request_sense sense;
	int ret;

	memset(&sense, 0, sizeof(sense));

	cgc->timeout = timeout;
	cgc->buffer = buffer;
	cgc->buflen = len;
	cgc->data_direction = ddir;
	cgc->sense = &sense;
	cgc->quiet = 1;

	ret = ioctl(fd, CDROM_SEND_PACKET, cgc);
	if (ret == -1) {
//		perror("ioctl");
		esyslog("Error: could not get data from CDROM.");
	}

	return ret;
}

int cCDStatus::GetMediaEvent(int fd)
{
	struct cdrom_generic_command cgc;
	unsigned char buffer[8];
	int ret;

	memset((&cgc), 0, sizeof(struct cdrom_generic_command));
	memset(buffer, 0, sizeof(buffer));

	cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
	cgc.cmd[1] = 1;
	cgc.cmd[4] = 16;
	cgc.cmd[8] = sizeof(buffer);

	ret = WaitCmd(fd, &cgc, buffer, sizeof(buffer), CGC_DATA_READ, 600);
	if (ret < 0) {
//		perror("GET_EVENT");
		return ret;
	}

	return buffer[4] & 0xf;
}


int cCDStatus::EjectRequest(void)
{
	char mountline[1032];

	snprintf(mountline, sizeof(mountline)-1, "umount %s", cd_device);
	if ( system(mountline) == -1 ) {
		if ( debug ) {
//			perror("umount");
			dsyslog("Error: could not umount CDROM \"%s\".", cd_device);
		}
	}
	if ( (fd < 0) || ( ioctl(fd, CDROMEJECT, NULL) != 0 ) ) {
//		perror("ioctl");
		dsyslog("Error: could not eject CDROM \"%s\".", cd_device);
		return(1);
	}
	return(0);
}

/*
int cCDStatus::Mount(const char *device)
{
	char mountline[1031];

	snprintf(mountline, sizeof(mountline)-1, "mount %s", device);
	if ( system(mountline) == -1 ) {
		perror("mount");
		fprintf(stderr, "Error: could not mount CDROM.\n");
		return(1);
	}
	return(0);
}
*/

void cCDStatus::Action()
{
	int event, first;

	running=true;
	first = 1;

	dsyslog("CDROM watch thread started with pid %d", getpid());


	while (running) {
		event = GetMediaEvent(fd);
		if (event < 0)
			 running = false;

		switch (event) {
			case 0:
				if (first)
					if ( debug ) dsyslog("no media change");
				break;
			case 1:
				if ( debug ) dsyslog("eject request");
				mediaStatus = mediaReqEject;
				EjectRequest();
				break;
			case 2:
				if ( debug ) dsyslog("new media");
				mediaStatus = mediaNew;
//				Mount(fd);
				break;
			case 3:
				if ( debug ) dsyslog("media removal");
				mediaStatus = mediaRemoval;
				break;
			case 4:
				if ( debug ) dsyslog("media change");
				mediaStatus = mediaChange;
//				Mount(fd);
				break;
			default:
				if ( debug ) dsyslog("unknown media event (%d)", event);
				break;
		}

		first = 0;

        newSet.Wait(500);
	}

	dsyslog("CDROM watch thread ended");

}

eMediaStatus cCDStatus::GetMediaStatus(void)
{
	return mediaStatus;
}
