in solos-pci.c [757:895]
static void solos_bh(unsigned long card_arg)
{
struct solos_card *card = (void *)card_arg;
uint32_t card_flags;
uint32_t rx_done = 0;
int port;
/*
* Since fpga_tx() is going to need to read the flags under its lock,
* it can return them to us so that we don't have to hit PCI MMIO
* again for the same information
*/
card_flags = fpga_tx(card);
for (port = 0; port < card->nr_ports; port++) {
if (card_flags & (0x10 << port)) {
struct pkt_hdr _hdr, *header;
struct sk_buff *skb;
struct atm_vcc *vcc;
int size;
if (card->using_dma) {
skb = card->rx_skb[port];
card->rx_skb[port] = NULL;
dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr,
RX_DMA_SIZE, DMA_FROM_DEVICE);
header = (void *)skb->data;
size = le16_to_cpu(header->size);
skb_put(skb, size + sizeof(*header));
skb_pull(skb, sizeof(*header));
} else {
header = &_hdr;
rx_done |= 0x10 << port;
memcpy_fromio(header, RX_BUF(card, port), sizeof(*header));
size = le16_to_cpu(header->size);
if (size > (card->buffer_size - sizeof(*header))){
dev_warn(&card->dev->dev, "Invalid buffer size\n");
continue;
}
/* Use netdev_alloc_skb() because it adds NET_SKB_PAD of
* headroom, and ensures we can route packets back out an
* Ethernet interface (for example) without having to
* reallocate. Adding NET_IP_ALIGN also ensures that both
* PPPoATM and PPPoEoBR2684 packets end up aligned. */
skb = netdev_alloc_skb_ip_align(NULL, size + 1);
if (!skb) {
if (net_ratelimit())
dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n");
continue;
}
memcpy_fromio(skb_put(skb, size),
RX_BUF(card, port) + sizeof(*header),
size);
}
if (atmdebug) {
dev_info(&card->dev->dev, "Received: port %d\n", port);
dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n",
size, le16_to_cpu(header->vpi),
le16_to_cpu(header->vci));
print_buffer(skb);
}
switch (le16_to_cpu(header->type)) {
case PKT_DATA:
vcc = find_vcc(card->atmdev[port], le16_to_cpu(header->vpi),
le16_to_cpu(header->vci));
if (!vcc) {
if (net_ratelimit())
dev_warn(&card->dev->dev, "Received packet for unknown VPI.VCI %d.%d on port %d\n",
le16_to_cpu(header->vpi), le16_to_cpu(header->vci),
port);
dev_kfree_skb_any(skb);
break;
}
atm_charge(vcc, skb->truesize);
vcc->push(vcc, skb);
atomic_inc(&vcc->stats->rx);
break;
case PKT_STATUS:
if (process_status(card, port, skb) &&
net_ratelimit()) {
dev_warn(&card->dev->dev, "Bad status packet of %d bytes on port %d:\n", skb->len, port);
print_buffer(skb);
}
dev_kfree_skb_any(skb);
break;
case PKT_COMMAND:
default: /* FIXME: Not really, surely? */
if (process_command(card, port, skb))
break;
spin_lock(&card->cli_queue_lock);
if (skb_queue_len(&card->cli_queue[port]) > 10) {
if (net_ratelimit())
dev_warn(&card->dev->dev, "Dropping console response on port %d\n",
port);
dev_kfree_skb_any(skb);
} else
skb_queue_tail(&card->cli_queue[port], skb);
spin_unlock(&card->cli_queue_lock);
break;
}
}
/* Allocate RX skbs for any ports which need them */
if (card->using_dma && card->atmdev[port] &&
!card->rx_skb[port]) {
/* Unlike the MMIO case (qv) we can't add NET_IP_ALIGN
* here; the FPGA can only DMA to addresses which are
* aligned to 4 bytes. */
struct sk_buff *skb = dev_alloc_skb(RX_DMA_SIZE);
if (skb) {
SKB_CB(skb)->dma_addr =
dma_map_single(&card->dev->dev, skb->data,
RX_DMA_SIZE, DMA_FROM_DEVICE);
iowrite32(SKB_CB(skb)->dma_addr,
card->config_regs + RX_DMA_ADDR(port));
card->rx_skb[port] = skb;
} else {
if (net_ratelimit())
dev_warn(&card->dev->dev, "Failed to allocate RX skb");
/* We'll have to try again later */
tasklet_schedule(&card->tlet);
}
}
}
if (rx_done)
iowrite32(rx_done, card->config_regs + FLAGS_ADDR);
return;
}