in xillybus/xillyusb.c [1971:2112]
static int xillyusb_discovery(struct usb_interface *interface)
{
int rc;
struct xillyusb_dev *xdev = usb_get_intfdata(interface);
__le16 bogus_chandesc[2];
struct xillyfifo idt_fifo;
struct xillyusb_channel *chan;
unsigned int idt_len, names_offset;
unsigned char *idt;
int num_channels;
rc = xillyusb_send_opcode(xdev, ~0, OPCODE_QUIESCE, 0);
if (rc) {
dev_err(&interface->dev, "Failed to send quiesce request. Aborting.\n");
return rc;
}
/* Phase I: Set up one fake upstream channel and obtain IDT */
/* Set up a fake IDT with one async IN stream */
bogus_chandesc[0] = cpu_to_le16(0x80);
bogus_chandesc[1] = cpu_to_le16(0);
rc = setup_channels(xdev, bogus_chandesc, 1);
if (rc)
return rc;
rc = fifo_init(&idt_fifo, LOG2_IDT_FIFO_SIZE);
if (rc)
return rc;
chan = xdev->channels;
chan->in_fifo = &idt_fifo;
chan->read_data_ok = 1;
xdev->num_channels = 1;
rc = xillyusb_send_opcode(xdev, ~0, OPCODE_REQ_IDT, 0);
if (rc) {
dev_err(&interface->dev, "Failed to send IDT request. Aborting.\n");
goto unfifo;
}
rc = wait_event_interruptible_timeout(idt_fifo.waitq,
!chan->read_data_ok,
XILLY_RESPONSE_TIMEOUT);
if (xdev->error) {
rc = xdev->error;
goto unfifo;
}
if (rc < 0) {
rc = -EINTR; /* Interrupt on probe method? Interesting. */
goto unfifo;
}
if (chan->read_data_ok) {
rc = -ETIMEDOUT;
dev_err(&interface->dev, "No response from FPGA. Aborting.\n");
goto unfifo;
}
idt_len = READ_ONCE(idt_fifo.fill);
idt = kmalloc(idt_len, GFP_KERNEL);
if (!idt) {
rc = -ENOMEM;
goto unfifo;
}
fifo_read(&idt_fifo, idt, idt_len, xilly_memcpy);
if (crc32_le(~0, idt, idt_len) != 0) {
dev_err(&interface->dev, "IDT failed CRC check. Aborting.\n");
rc = -ENODEV;
goto unidt;
}
if (*idt > 0x90) {
dev_err(&interface->dev, "No support for IDT version 0x%02x. Maybe the xillyusb driver needs an upgrade. Aborting.\n",
(int)*idt);
rc = -ENODEV;
goto unidt;
}
/* Phase II: Set up the streams as defined in IDT */
num_channels = le16_to_cpu(*((__le16 *)(idt + 1)));
names_offset = 3 + num_channels * 4;
idt_len -= 4; /* Exclude CRC */
if (idt_len < names_offset) {
dev_err(&interface->dev, "IDT too short. This is exceptionally weird, because its CRC is OK\n");
rc = -ENODEV;
goto unidt;
}
rc = setup_channels(xdev, (void *)idt + 3, num_channels);
if (rc)
goto unidt;
/*
* Except for wildly misbehaving hardware, or if it was disconnected
* just after responding with the IDT, there is no reason for any
* work item to be running now. To be sure that xdev->channels
* is updated on anything that might run in parallel, flush the
* workqueue, which rarely does anything.
*/
flush_workqueue(xdev->workq);
xdev->num_channels = num_channels;
fifo_mem_release(&idt_fifo);
kfree(chan);
rc = xillybus_init_chrdev(&interface->dev, &xillyusb_fops,
THIS_MODULE, xdev,
idt + names_offset,
idt_len - names_offset,
num_channels,
xillyname, true);
kfree(idt);
return rc;
unidt:
kfree(idt);
unfifo:
safely_assign_in_fifo(chan, NULL);
fifo_mem_release(&idt_fifo);
return rc;
}