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;
}