/* unieject - Universal eject command
   Copyright (C) 2005-2006, Diego Pettenò

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with unieject; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <unieject_internal.h>
#include <unieject.h>

#include <cdio/mmc.h>

#include <stdio.h>

#if defined(__linux__) && defined(USE_LOCK_WORKAROUND)
# include <fcntl.h>
# include <errno.h>
# include <sys/ioctl.h>
# include <linux/cdrom.h>

# ifndef CDROM_LOCKDOOR
#  error "Unable to find CDROM_LOCKDOOR definition, and lock workaround requested"
# endif
#endif

/* Set how long to wait for MMC commands to complete */
#define DEFAULT_TIMEOUT_MS 100000

int libunieject_togglelock(struct unieject_opts *opts, int lock)
{
	if ( ! (unieject_get_misccaps(*opts) & CDIO_DRIVE_CAP_MISC_LOCK) )
	{
		unieject_error(*opts, _("the selected device doesn't have locking capabilities.\n"));
		return -2;
	}
	
	if ( opts->fake )
		return 0;

/*
 * On Linux the allow medium removal command is a priviledged command that
 * cannot be instanced by userspace.
 * Until this changes, use a workaround with syscalls instead.
 */
#if defined(__linux__) && defined(USE_LOCK_WORKAROUND)
	cdio_destroy(opts->cdio);
	opts->cdio = NULL;
	
	int devfd = open(opts->device, O_RDONLY|O_NONBLOCK);
	if ( UNLIKELY(devfd == -1) )
	{
		unieject_error(*opts, _("unable to open device descriptor [%s].\n"), strerror(errno));
		return -4;
	}
	
	if ( UNLIKELY(ioctl(devfd, CDROM_LOCKDOOR, lock ? 1 : 0) == -1) )
	{
		unieject_error(*opts, _("error in ioctl [%s].\n"), strerror(errno));
		return -5;
	}
	
	close(devfd);
	
	return 0;
#else
	mmc_cdb_t lockcmd = { {0, } }; /* Command description buffer */
	uint8_t buf[1]; /* Fake buffer */
	
	CDIO_MMC_SET_COMMAND(lockcmd.field, CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL);
	
	lockcmd.field[4] = lock ? 1 : 0;
	
	driver_return_code_t sts = mmc_run_cmd(
		(CdIo_t*)opts->cdio,
		DEFAULT_TIMEOUT_MS, /* Should this be configurable? */
		&lockcmd,
		SCSI_MMC_DATA_READ,
		0,
		buf
		);
	
	return unieject_status(*opts, sts);
#endif
}


syntax highlighted by Code2HTML, v. 0.9.1