static long cmm_ioctl()

in pcmcia/cm4000_cs.c [1403:1620]


static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct cm4000_dev *dev = filp->private_data;
	unsigned int iobase = dev->p_dev->resource[0]->start;
	struct inode *inode = file_inode(filp);
	struct pcmcia_device *link;
	int rc;
	void __user *argp = (void __user *)arg;
#ifdef CM4000_DEBUG
	char *ioctl_names[CM_IOC_MAXNR + 1] = {
		[_IOC_NR(CM_IOCGSTATUS)] "CM_IOCGSTATUS",
		[_IOC_NR(CM_IOCGATR)] "CM_IOCGATR",
		[_IOC_NR(CM_IOCARDOFF)] "CM_IOCARDOFF",
		[_IOC_NR(CM_IOCSPTS)] "CM_IOCSPTS",
		[_IOC_NR(CM_IOSDBGLVL)] "CM4000_DBGLVL",
	};
	DEBUGP(3, dev, "cmm_ioctl(device=%d.%d) %s\n", imajor(inode),
	       iminor(inode), ioctl_names[_IOC_NR(cmd)]);
#endif

	mutex_lock(&cmm_mutex);
	rc = -ENODEV;
	link = dev_table[iminor(inode)];
	if (!pcmcia_dev_present(link)) {
		DEBUGP(4, dev, "DEV_OK false\n");
		goto out;
	}

	if (test_bit(IS_CMM_ABSENT, &dev->flags)) {
		DEBUGP(4, dev, "CMM_ABSENT flag set\n");
		goto out;
	}
	rc = -EINVAL;

	if (_IOC_TYPE(cmd) != CM_IOC_MAGIC) {
		DEBUGP(4, dev, "ioctype mismatch\n");
		goto out;
	}
	if (_IOC_NR(cmd) > CM_IOC_MAXNR) {
		DEBUGP(4, dev, "iocnr mismatch\n");
		goto out;
	}
	rc = 0;

	switch (cmd) {
	case CM_IOCGSTATUS:
		DEBUGP(4, dev, " ... in CM_IOCGSTATUS\n");
		{
			int status;

			/* clear other bits, but leave inserted & powered as
			 * they are */
			status = dev->flags0 & 3;
			if (test_bit(IS_ATR_PRESENT, &dev->flags))
				status |= CM_ATR_PRESENT;
			if (test_bit(IS_ATR_VALID, &dev->flags))
				status |= CM_ATR_VALID;
			if (test_bit(IS_CMM_ABSENT, &dev->flags))
				status |= CM_NO_READER;
			if (test_bit(IS_BAD_CARD, &dev->flags))
				status |= CM_BAD_CARD;
			if (copy_to_user(argp, &status, sizeof(int)))
				rc = -EFAULT;
		}
		break;
	case CM_IOCGATR:
		DEBUGP(4, dev, "... in CM_IOCGATR\n");
		{
			struct atreq __user *atreq = argp;
			int tmp;
			/* allow nonblocking io and being interrupted */
			if (wait_event_interruptible
			    (dev->atrq,
			     ((filp->f_flags & O_NONBLOCK)
			      || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
				  != 0)))) {
				if (filp->f_flags & O_NONBLOCK)
					rc = -EAGAIN;
				else
					rc = -ERESTARTSYS;
				break;
			}

			rc = -EFAULT;
			if (test_bit(IS_ATR_VALID, &dev->flags) == 0) {
				tmp = -1;
				if (copy_to_user(&(atreq->atr_len), &tmp,
						 sizeof(int)))
					break;
			} else {
				if (copy_to_user(atreq->atr, dev->atr,
						 dev->atr_len))
					break;

				tmp = dev->atr_len;
				if (copy_to_user(&(atreq->atr_len), &tmp, sizeof(int)))
					break;
			}
			rc = 0;
			break;
		}
	case CM_IOCARDOFF:

#ifdef CM4000_DEBUG
		DEBUGP(4, dev, "... in CM_IOCARDOFF\n");
		if (dev->flags0 & 0x01) {
			DEBUGP(4, dev, "    Card inserted\n");
		} else {
			DEBUGP(2, dev, "    No card inserted\n");
		}
		if (dev->flags0 & 0x02) {
			DEBUGP(4, dev, "    Card powered\n");
		} else {
			DEBUGP(2, dev, "    Card not powered\n");
		}
#endif

		/* is a card inserted and powered? */
		if ((dev->flags0 & 0x01) && (dev->flags0 & 0x02)) {

			/* get IO lock */
			if (wait_event_interruptible
			    (dev->ioq,
			     ((filp->f_flags & O_NONBLOCK)
			      || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
				  == 0)))) {
				if (filp->f_flags & O_NONBLOCK)
					rc = -EAGAIN;
				else
					rc = -ERESTARTSYS;
				break;
			}
			/* Set Flags0 = 0x42 */
			DEBUGP(4, dev, "Set Flags0=0x42 \n");
			xoutb(0x42, REG_FLAGS0(iobase));
			clear_bit(IS_ATR_PRESENT, &dev->flags);
			clear_bit(IS_ATR_VALID, &dev->flags);
			dev->mstate = M_CARDOFF;
			clear_bit(LOCK_IO, &dev->flags);
			if (wait_event_interruptible
			    (dev->atrq,
			     ((filp->f_flags & O_NONBLOCK)
			      || (test_bit(IS_ATR_VALID, (void *)&dev->flags) !=
				  0)))) {
				if (filp->f_flags & O_NONBLOCK)
					rc = -EAGAIN;
				else
					rc = -ERESTARTSYS;
				break;
			}
		}
		/* release lock */
		clear_bit(LOCK_IO, &dev->flags);
		wake_up_interruptible(&dev->ioq);

		rc = 0;
		break;
	case CM_IOCSPTS:
		{
			struct ptsreq krnptsreq;

			if (copy_from_user(&krnptsreq, argp,
					   sizeof(struct ptsreq))) {
				rc = -EFAULT;
				break;
			}

			rc = 0;
			DEBUGP(4, dev, "... in CM_IOCSPTS\n");
			/* wait for ATR to get valid */
			if (wait_event_interruptible
			    (dev->atrq,
			     ((filp->f_flags & O_NONBLOCK)
			      || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
				  != 0)))) {
				if (filp->f_flags & O_NONBLOCK)
					rc = -EAGAIN;
				else
					rc = -ERESTARTSYS;
				break;
			}
			/* get IO lock */
			if (wait_event_interruptible
			    (dev->ioq,
			     ((filp->f_flags & O_NONBLOCK)
			      || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
				  == 0)))) {
				if (filp->f_flags & O_NONBLOCK)
					rc = -EAGAIN;
				else
					rc = -ERESTARTSYS;
				break;
			}

			if ((rc = set_protocol(dev, &krnptsreq)) != 0) {
				/* auto power_on again */
				dev->mstate = M_FETCH_ATR;
				clear_bit(IS_ATR_VALID, &dev->flags);
			}
			/* release lock */
			clear_bit(LOCK_IO, &dev->flags);
			wake_up_interruptible(&dev->ioq);

		}
		break;
#ifdef CM4000_DEBUG
	case CM_IOSDBGLVL:
		rc = -ENOTTY;
		break;
#endif
	default:
		DEBUGP(4, dev, "... in default (unknown IOCTL code)\n");
		rc = -ENOTTY;
	}
out:
	mutex_unlock(&cmm_mutex);
	return rc;
}