in xillybus/xillybus_core.c [112:283]
irqreturn_t xillybus_isr(int irq, void *data)
{
struct xilly_endpoint *ep = data;
u32 *buf;
unsigned int buf_size;
int i;
int opcode;
unsigned int msg_channel, msg_bufno, msg_data, msg_dir;
struct xilly_channel *channel;
buf = ep->msgbuf_addr;
buf_size = ep->msg_buf_size/sizeof(u32);
dma_sync_single_for_cpu(ep->dev, ep->msgbuf_dma_addr,
ep->msg_buf_size, DMA_FROM_DEVICE);
for (i = 0; i < buf_size; i += 2) {
if (((buf[i+1] >> 28) & 0xf) != ep->msg_counter) {
malformed_message(ep, &buf[i]);
dev_warn(ep->dev,
"Sending a NACK on counter %x (instead of %x) on entry %d\n",
((buf[i+1] >> 28) & 0xf),
ep->msg_counter,
i/2);
if (++ep->failed_messages > 10) {
dev_err(ep->dev,
"Lost sync with interrupt messages. Stopping.\n");
} else {
dma_sync_single_for_device(ep->dev,
ep->msgbuf_dma_addr,
ep->msg_buf_size,
DMA_FROM_DEVICE);
iowrite32(0x01, /* Message NACK */
ep->registers + fpga_msg_ctrl_reg);
}
return IRQ_HANDLED;
} else if (buf[i] & (1 << 22)) /* Last message */
break;
}
if (i >= buf_size) {
dev_err(ep->dev, "Bad interrupt message. Stopping.\n");
return IRQ_HANDLED;
}
buf_size = i + 2;
for (i = 0; i < buf_size; i += 2) { /* Scan through messages */
opcode = (buf[i] >> 24) & 0xff;
msg_dir = buf[i] & 1;
msg_channel = (buf[i] >> 1) & 0x7ff;
msg_bufno = (buf[i] >> 12) & 0x3ff;
msg_data = buf[i+1] & 0xfffffff;
switch (opcode) {
case XILLYMSG_OPCODE_RELEASEBUF:
if ((msg_channel > ep->num_channels) ||
(msg_channel == 0)) {
malformed_message(ep, &buf[i]);
break;
}
channel = ep->channels[msg_channel];
if (msg_dir) { /* Write channel */
if (msg_bufno >= channel->num_wr_buffers) {
malformed_message(ep, &buf[i]);
break;
}
spin_lock(&channel->wr_spinlock);
channel->wr_buffers[msg_bufno]->end_offset =
msg_data;
channel->wr_fpga_buf_idx = msg_bufno;
channel->wr_empty = 0;
channel->wr_sleepy = 0;
spin_unlock(&channel->wr_spinlock);
wake_up_interruptible(&channel->wr_wait);
} else {
/* Read channel */
if (msg_bufno >= channel->num_rd_buffers) {
malformed_message(ep, &buf[i]);
break;
}
spin_lock(&channel->rd_spinlock);
channel->rd_fpga_buf_idx = msg_bufno;
channel->rd_full = 0;
spin_unlock(&channel->rd_spinlock);
wake_up_interruptible(&channel->rd_wait);
if (!channel->rd_synchronous)
queue_delayed_work(
xillybus_wq,
&channel->rd_workitem,
XILLY_RX_TIMEOUT);
}
break;
case XILLYMSG_OPCODE_NONEMPTY:
if ((msg_channel > ep->num_channels) ||
(msg_channel == 0) || (!msg_dir) ||
!ep->channels[msg_channel]->wr_supports_nonempty) {
malformed_message(ep, &buf[i]);
break;
}
channel = ep->channels[msg_channel];
if (msg_bufno >= channel->num_wr_buffers) {
malformed_message(ep, &buf[i]);
break;
}
spin_lock(&channel->wr_spinlock);
if (msg_bufno == channel->wr_host_buf_idx)
channel->wr_ready = 1;
spin_unlock(&channel->wr_spinlock);
wake_up_interruptible(&channel->wr_ready_wait);
break;
case XILLYMSG_OPCODE_QUIESCEACK:
ep->idtlen = msg_data;
wake_up_interruptible(&ep->ep_wait);
break;
case XILLYMSG_OPCODE_FIFOEOF:
if ((msg_channel > ep->num_channels) ||
(msg_channel == 0) || (!msg_dir) ||
!ep->channels[msg_channel]->num_wr_buffers) {
malformed_message(ep, &buf[i]);
break;
}
channel = ep->channels[msg_channel];
spin_lock(&channel->wr_spinlock);
channel->wr_eof = msg_bufno;
channel->wr_sleepy = 0;
channel->wr_hangup = channel->wr_empty &&
(channel->wr_host_buf_idx == msg_bufno);
spin_unlock(&channel->wr_spinlock);
wake_up_interruptible(&channel->wr_wait);
break;
case XILLYMSG_OPCODE_FATAL_ERROR:
ep->fatal_error = 1;
wake_up_interruptible(&ep->ep_wait); /* For select() */
dev_err(ep->dev,
"FPGA reported a fatal error. This means that the low-level communication with the device has failed. This hardware problem is most likely unrelated to Xillybus (neither kernel module nor FPGA core), but reports are still welcome. All I/O is aborted.\n");
break;
default:
malformed_message(ep, &buf[i]);
break;
}
}
dma_sync_single_for_device(ep->dev, ep->msgbuf_dma_addr,
ep->msg_buf_size, DMA_FROM_DEVICE);
ep->msg_counter = (ep->msg_counter + 1) & 0xf;
ep->failed_messages = 0;
iowrite32(0x03, ep->registers + fpga_msg_ctrl_reg); /* Message ACK */
return IRQ_HANDLED;
}