/*
 *  linux/fs/supermount/mediactl.c
 *
 *  Original version:
 *      Copyright (C) 1995, 1997
 *      Stephen Tweedie (sct@dcs.ed.ac.uk)
 *
 *  Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov
 *                                    (alexis@abc.cap.ru)
 *  Rewritten for kernel 2.4.21 (C) 2003 Andrey Borzenkov
 *                                       (arvidjaar@mail.ru)
 *
 *  $Id: mediactl.c,v 1.7 2003/11/23 11:27:32 bor Exp $
 */

#define S_DBG_TRACE_CURRENT S_DBG_TRACE_MEDIACTL
#include "supermount.h"

/* 
 * Try to lock the drive door.  This is not guaranteed to work on all
 * hardware, but we try any tricks we know of anyway.  
 */
void
supermount_mediactl(struct super_block *sb, int operation, int opt)
{
	struct block_device *bdev;
	struct super_block *subsb;

	if (!subfs_is_mounted(sb))
		return;

	subsb = subfs_sb(sb);
	bdev = subsb->s_bdev;

       /* FIXME is locking needed here? */
	switch (operation) {
	case SUPERMOUNT_INC_COUNT:
		SUPERMOUNT_BUG_ON(atomic_read(&bdev->bd_supermount) < 0);
		atomic_inc(&bdev->bd_supermount);
		return;
	case SUPERMOUNT_DEC_COUNT:
		SUPERMOUNT_BUG_ON(atomic_read(&bdev->bd_supermount) <= 0);
		atomic_dec(&bdev->bd_supermount);
		return;
	}

	if (!bdev->bd_op->mediactl)
		return;
	/*
	 * tray is (un-)locked in open (BKL for bdev), release (BKL for bdev)
	 * and ioctl (BKL). We are in good company.
	 * This must be changed if block devices ever stop using BKL
	 * for open/release. Unfortunately, using just bdev->bd_sem is not
	 * enough due to ioctl.
	 */
	lock_kernel();
	bdev->bd_op->mediactl(subsb->s_dev, operation, opt);
	unlock_kernel();
}
