static ssize_t cmm_write()

in pcmcia/cm4000_cs.c [1050:1374]


static ssize_t cmm_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	struct cm4000_dev *dev = filp->private_data;
	unsigned int iobase = dev->p_dev->resource[0]->start;
	unsigned short s;
	unsigned char infolen;
	unsigned char sendT0;
	unsigned short nsend;
	unsigned short nr;
	ssize_t rc;
	int i;

	DEBUGP(2, dev, "-> cmm_write(%s,%d)\n", current->comm, current->pid);

	if (count == 0)		/* according to manpage */
		return 0;

	if (dev->proto == 0 && count < 4) {
		/* T0 must have at least 4 bytes */
		DEBUGP(4, dev, "T0 short write\n");
		return -EIO;
	}

	nr = count & 0x1ff;	/* max bytes to write */

	sendT0 = dev->proto ? 0 : nr > 5 ? 0x08 : 0;

	if (!pcmcia_dev_present(dev->p_dev) || /* device removed */
	    test_bit(IS_CMM_ABSENT, &dev->flags))
		return -ENODEV;

	if (test_bit(IS_BAD_CSUM, &dev->flags)) {
		DEBUGP(4, dev, "bad csum\n");
		return -EIO;
	}

	/*
	 * wait for atr to become valid.
	 * note: it is important to lock this code. if we dont, the monitor
	 * could be run between test_bit and the call to sleep on the
	 * atr-queue.  if *then* the monitor detects atr valid, it will wake up
	 * any process on the atr-queue, *but* since we have been interrupted,
	 * we do not yet sleep on this queue. this would result in a missed
	 * wake_up and the calling process would sleep forever (until
	 * interrupted).  also, do *not* restore_flags before sleep_on, because
	 * this could result in the same situation!
	 */
	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)
			return -EAGAIN;
		return -ERESTARTSYS;
	}

	if (test_bit(IS_ATR_VALID, &dev->flags) == 0) {	/* invalid atr */
		DEBUGP(4, dev, "invalid ATR\n");
		return -EIO;
	}

	/* lock io */
	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)
			return -EAGAIN;
		return -ERESTARTSYS;
	}

	if (copy_from_user(dev->sbuf, buf, ((count > 512) ? 512 : count)))
		return -EFAULT;

	rc = 0;
	dev->flags0 = inb(REG_FLAGS0(iobase));
	if ((dev->flags0 & 1) == 0	/* no smartcard inserted */
	    || dev->flags0 == 0xff) {	/* no cardman inserted */
		clear_bit(IS_ATR_VALID, &dev->flags);
		if (dev->flags0 & 1) {
			set_bit(IS_CMM_ABSENT, &dev->flags);
			rc = -ENODEV;
		} else {
			DEBUGP(4, dev, "IO error\n");
			rc = -EIO;
		}
		goto release_io;
	}

	xoutb(0x80, REG_FLAGS0(iobase));	/* reset SM  */

	if (!io_detect_cm4000(iobase, dev)) {
		rc = -ENODEV;
		goto release_io;
	}

	/* reflect T=0 send/read mode in flags1 */
	dev->flags1 |= (sendT0);

	set_cardparameter(dev);

	/* dummy read, reset flag procedure received */
	inb(REG_FLAGS1(iobase));

	dev->flags1 = 0x20	/* T_Active */
	    | (sendT0)
	    | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0)/* inverse parity  */
	    | (((dev->baudv - 1) & 0x0100) >> 8);	/* MSB-Baud */
	DEBUGP(1, dev, "set dev->flags1 = 0x%.2x\n", dev->flags1);
	xoutb(dev->flags1, REG_FLAGS1(iobase));

	/* xmit data */
	DEBUGP(4, dev, "Xmit data\n");
	for (i = 0; i < nr; i++) {
		if (i >= 256) {
			dev->flags1 = 0x20	/* T_Active */
			    | (sendT0)	/* SendT0 */
				/* inverse parity: */
			    | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0)
			    | (((dev->baudv - 1) & 0x0100) >> 8) /* MSB-Baud */
			    | 0x10;	/* set address high */
			DEBUGP(4, dev, "dev->flags = 0x%.2x - set address "
			       "high\n", dev->flags1);
			xoutb(dev->flags1, REG_FLAGS1(iobase));
		}
		if (test_bit(IS_INVREV, &dev->flags)) {
			DEBUGP(4, dev, "Apply inverse convention for 0x%.2x "
				"-> 0x%.2x\n", (unsigned char)dev->sbuf[i],
			      invert_revert(dev->sbuf[i]));
			xoutb(i, REG_BUF_ADDR(iobase));
			xoutb(invert_revert(dev->sbuf[i]),
			      REG_BUF_DATA(iobase));
		} else {
			xoutb(i, REG_BUF_ADDR(iobase));
			xoutb(dev->sbuf[i], REG_BUF_DATA(iobase));
		}
	}
	DEBUGP(4, dev, "Xmit done\n");

	if (dev->proto == 0) {
		/* T=0 proto: 0 byte reply  */
		if (nr == 4) {
			DEBUGP(4, dev, "T=0 assumes 0 byte reply\n");
			xoutb(i, REG_BUF_ADDR(iobase));
			if (test_bit(IS_INVREV, &dev->flags))
				xoutb(0xff, REG_BUF_DATA(iobase));
			else
				xoutb(0x00, REG_BUF_DATA(iobase));
		}

		/* numSendBytes */
		if (sendT0)
			nsend = nr;
		else {
			if (nr == 4)
				nsend = 5;
			else {
				nsend = 5 + (unsigned char)dev->sbuf[4];
				if (dev->sbuf[4] == 0)
					nsend += 0x100;
			}
		}
	} else
		nsend = nr;

	/* T0: output procedure byte */
	if (test_bit(IS_INVREV, &dev->flags)) {
		DEBUGP(4, dev, "T=0 set Procedure byte (inverse-reverse) "
		       "0x%.2x\n", invert_revert(dev->sbuf[1]));
		xoutb(invert_revert(dev->sbuf[1]), REG_NUM_BYTES(iobase));
	} else {
		DEBUGP(4, dev, "T=0 set Procedure byte 0x%.2x\n", dev->sbuf[1]);
		xoutb(dev->sbuf[1], REG_NUM_BYTES(iobase));
	}

	DEBUGP(1, dev, "set NumSendBytes = 0x%.2x\n",
	       (unsigned char)(nsend & 0xff));
	xoutb((unsigned char)(nsend & 0xff), REG_NUM_SEND(iobase));

	DEBUGP(1, dev, "Trigger CARDMAN CONTROLLER (0x%.2x)\n",
	       0x40	/* SM_Active */
	      | (dev->flags0 & 2 ? 0 : 4)	/* power on if needed */
	      |(dev->proto ? 0x10 : 0x08)	/* T=1/T=0 */
	      |(nsend & 0x100) >> 8 /* MSB numSendBytes */ );
	xoutb(0x40		/* SM_Active */
	      | (dev->flags0 & 2 ? 0 : 4)	/* power on if needed */
	      |(dev->proto ? 0x10 : 0x08)	/* T=1/T=0 */
	      |(nsend & 0x100) >> 8,	/* MSB numSendBytes */
	      REG_FLAGS0(iobase));

	/* wait for xmit done */
	if (dev->proto == 1) {
		DEBUGP(4, dev, "Wait for xmit done\n");
		for (i = 0; i < 1000; i++) {
			if (inb(REG_FLAGS0(iobase)) & 0x08)
				break;
			msleep_interruptible(10);
		}
		if (i == 1000) {
			DEBUGP(4, dev, "timeout waiting for xmit done\n");
			rc = -EIO;
			goto release_io;
		}
	}

	/* T=1: wait for infoLen */

	infolen = 0;
	if (dev->proto) {
		/* wait until infoLen is valid */
		for (i = 0; i < 6000; i++) {	/* max waiting time of 1 min */
			io_read_num_rec_bytes(iobase, &s);
			if (s >= 3) {
				infolen = inb(REG_FLAGS1(iobase));
				DEBUGP(4, dev, "infolen=%d\n", infolen);
				break;
			}
			msleep_interruptible(10);
		}
		if (i == 6000) {
			DEBUGP(4, dev, "timeout waiting for infoLen\n");
			rc = -EIO;
			goto release_io;
		}
	} else
		clear_bit(IS_PROCBYTE_PRESENT, &dev->flags);

	/* numRecBytes | bit9 of numRecytes */
	io_read_num_rec_bytes(iobase, &dev->rlen);
	for (i = 0; i < 600; i++) {	/* max waiting time of 2 sec */
		if (dev->proto) {
			if (dev->rlen >= infolen + 4)
				break;
		}
		msleep_interruptible(10);
		/* numRecBytes | bit9 of numRecytes */
		io_read_num_rec_bytes(iobase, &s);
		if (s > dev->rlen) {
			DEBUGP(1, dev, "NumRecBytes inc (reset timeout)\n");
			i = 0;	/* reset timeout */
			dev->rlen = s;
		}
		/* T=0: we are done when numRecBytes doesn't
		 *      increment any more and NoProcedureByte
		 *      is set and numRecBytes == bytes sent + 6
		 *      (header bytes + data + 1 for sw2)
		 *      except when the card replies an error
		 *      which means, no data will be sent back.
		 */
		else if (dev->proto == 0) {
			if ((inb(REG_BUF_ADDR(iobase)) & 0x80)) {
				/* no procedure byte received since last read */
				DEBUGP(1, dev, "NoProcedure byte set\n");
				/* i=0; */
			} else {
				/* procedure byte received since last read */
				DEBUGP(1, dev, "NoProcedure byte unset "
					"(reset timeout)\n");
				dev->procbyte = inb(REG_FLAGS1(iobase));
				DEBUGP(1, dev, "Read procedure byte 0x%.2x\n",
				      dev->procbyte);
				i = 0;	/* resettimeout */
			}
			if (inb(REG_FLAGS0(iobase)) & 0x08) {
				DEBUGP(1, dev, "T0Done flag (read reply)\n");
				break;
			}
		}
		if (dev->proto)
			infolen = inb(REG_FLAGS1(iobase));
	}
	if (i == 600) {
		DEBUGP(1, dev, "timeout waiting for numRecBytes\n");
		rc = -EIO;
		goto release_io;
	} else {
		if (dev->proto == 0) {
			DEBUGP(1, dev, "Wait for T0Done bit to be  set\n");
			for (i = 0; i < 1000; i++) {
				if (inb(REG_FLAGS0(iobase)) & 0x08)
					break;
				msleep_interruptible(10);
			}
			if (i == 1000) {
				DEBUGP(1, dev, "timeout waiting for T0Done\n");
				rc = -EIO;
				goto release_io;
			}

			dev->procbyte = inb(REG_FLAGS1(iobase));
			DEBUGP(4, dev, "Read procedure byte 0x%.2x\n",
			      dev->procbyte);

			io_read_num_rec_bytes(iobase, &dev->rlen);
			DEBUGP(4, dev, "Read NumRecBytes = %i\n", dev->rlen);

		}
	}
	/* T=1: read offset=zero, T=0: read offset=after challenge */
	dev->rpos = dev->proto ? 0 : nr == 4 ? 5 : nr > dev->rlen ? 5 : nr;
	DEBUGP(4, dev, "dev->rlen = %i,  dev->rpos = %i, nr = %i\n",
	      dev->rlen, dev->rpos, nr);

release_io:
	DEBUGP(4, dev, "Reset SM\n");
	xoutb(0x80, REG_FLAGS0(iobase));	/* reset SM */

	if (rc < 0) {
		DEBUGP(4, dev, "Write failed but clear T_Active\n");
		dev->flags1 &= 0xdf;
		xoutb(dev->flags1, REG_FLAGS1(iobase));
	}

	clear_bit(LOCK_IO, &dev->flags);
	wake_up_interruptible(&dev->ioq);
	wake_up_interruptible(&dev->readq);	/* tell read we have data */

	/* ITSEC E2: clear write buffer */
	memset((char *)dev->sbuf, 0, 512);

	/* return error or actually written bytes */
	DEBUGP(2, dev, "<- cmm_write\n");
	return rc < 0 ? rc : nr;
}