in iphase.c [2886:3064]
static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb) {
IADEV *iadev;
struct dle *wr_ptr;
struct tx_buf_desc __iomem *buf_desc_ptr;
int desc;
int comp_code;
int total_len;
struct cpcs_trailer *trailer;
struct ia_vcc *iavcc;
iadev = INPH_IA_DEV(vcc->dev);
iavcc = INPH_IA_VCC(vcc);
if (!iavcc->txing) {
printk("discard packet on closed VC\n");
if (vcc->pop)
vcc->pop(vcc, skb);
else
dev_kfree_skb_any(skb);
return 0;
}
if (skb->len > iadev->tx_buf_sz - 8) {
printk("Transmit size over tx buffer size\n");
if (vcc->pop)
vcc->pop(vcc, skb);
else
dev_kfree_skb_any(skb);
return 0;
}
if ((unsigned long)skb->data & 3) {
printk("Misaligned SKB\n");
if (vcc->pop)
vcc->pop(vcc, skb);
else
dev_kfree_skb_any(skb);
return 0;
}
/* Get a descriptor number from our free descriptor queue
We get the descr number from the TCQ now, since I am using
the TCQ as a free buffer queue. Initially TCQ will be
initialized with all the descriptors and is hence, full.
*/
desc = get_desc (iadev, iavcc);
if (desc == 0xffff)
return 1;
comp_code = desc >> 13;
desc &= 0x1fff;
if ((desc == 0) || (desc > iadev->num_tx_desc))
{
IF_ERR(printk(DEV_LABEL "invalid desc for send: %d\n", desc);)
atomic_inc(&vcc->stats->tx);
if (vcc->pop)
vcc->pop(vcc, skb);
else
dev_kfree_skb_any(skb);
return 0; /* return SUCCESS */
}
if (comp_code)
{
IF_ERR(printk(DEV_LABEL "send desc:%d completion code %d error\n",
desc, comp_code);)
}
/* remember the desc and vcc mapping */
iavcc->vc_desc_cnt++;
iadev->desc_tbl[desc-1].iavcc = iavcc;
iadev->desc_tbl[desc-1].txskb = skb;
IA_SKB_STATE(skb) = 0;
iadev->ffL.tcq_rd += 2;
if (iadev->ffL.tcq_rd > iadev->ffL.tcq_ed)
iadev->ffL.tcq_rd = iadev->ffL.tcq_st;
writew(iadev->ffL.tcq_rd, iadev->seg_reg+TCQ_RD_PTR);
/* Put the descriptor number in the packet ready queue
and put the updated write pointer in the DLE field
*/
*(u16*)(iadev->seg_ram+iadev->ffL.prq_wr) = desc;
iadev->ffL.prq_wr += 2;
if (iadev->ffL.prq_wr > iadev->ffL.prq_ed)
iadev->ffL.prq_wr = iadev->ffL.prq_st;
/* Figure out the exact length of the packet and padding required to
make it aligned on a 48 byte boundary. */
total_len = skb->len + sizeof(struct cpcs_trailer);
total_len = ((total_len + 47) / 48) * 48;
IF_TX(printk("ia packet len:%d padding:%d\n", total_len, total_len - skb->len);)
/* Put the packet in a tx buffer */
trailer = iadev->tx_buf[desc-1].cpcs;
IF_TX(printk("Sent: skb = 0x%p skb->data: 0x%p len: %d, desc: %d\n",
skb, skb->data, skb->len, desc);)
trailer->control = 0;
/*big endian*/
trailer->length = ((skb->len & 0xff) << 8) | ((skb->len & 0xff00) >> 8);
trailer->crc32 = 0; /* not needed - dummy bytes */
/* Display the packet */
IF_TXPKT(printk("Sent data: len = %d MsgNum = %d\n",
skb->len, tcnter++);
xdump(skb->data, skb->len, "TX: ");
printk("\n");)
/* Build the buffer descriptor */
buf_desc_ptr = iadev->seg_ram+TX_DESC_BASE;
buf_desc_ptr += desc; /* points to the corresponding entry */
buf_desc_ptr->desc_mode = AAL5 | EOM_EN | APP_CRC32 | CMPL_INT;
/* Huh ? p.115 of users guide describes this as a read-only register */
writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG);
buf_desc_ptr->vc_index = vcc->vci;
buf_desc_ptr->bytes = total_len;
if (vcc->qos.txtp.traffic_class == ATM_ABR)
clear_lockup (vcc, iadev);
/* Build the DLE structure */
wr_ptr = iadev->tx_dle_q.write;
memset((caddr_t)wr_ptr, 0, sizeof(*wr_ptr));
wr_ptr->sys_pkt_addr = dma_map_single(&iadev->pci->dev, skb->data,
skb->len, DMA_TO_DEVICE);
wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) |
buf_desc_ptr->buf_start_lo;
/* wr_ptr->bytes = swap_byte_order(total_len); didn't seem to affect?? */
wr_ptr->bytes = skb->len;
/* hw bug - DLEs of 0x2d, 0x2e, 0x2f cause DMA lockup */
if ((wr_ptr->bytes >> 2) == 0xb)
wr_ptr->bytes = 0x30;
wr_ptr->mode = TX_DLE_PSI;
wr_ptr->prq_wr_ptr_data = 0;
/* end is not to be used for the DLE q */
if (++wr_ptr == iadev->tx_dle_q.end)
wr_ptr = iadev->tx_dle_q.start;
/* Build trailer dle */
wr_ptr->sys_pkt_addr = iadev->tx_buf[desc-1].dma_addr;
wr_ptr->local_pkt_addr = ((buf_desc_ptr->buf_start_hi << 16) |
buf_desc_ptr->buf_start_lo) + total_len - sizeof(struct cpcs_trailer);
wr_ptr->bytes = sizeof(struct cpcs_trailer);
wr_ptr->mode = DMA_INT_ENABLE;
wr_ptr->prq_wr_ptr_data = iadev->ffL.prq_wr;
/* end is not to be used for the DLE q */
if (++wr_ptr == iadev->tx_dle_q.end)
wr_ptr = iadev->tx_dle_q.start;
iadev->tx_dle_q.write = wr_ptr;
ATM_DESC(skb) = vcc->vci;
skb_queue_tail(&iadev->tx_dma_q, skb);
atomic_inc(&vcc->stats->tx);
iadev->tx_pkt_cnt++;
/* Increment transaction counter */
writel(2, iadev->dma+IPHASE5575_TX_COUNTER);
#if 0
/* add flow control logic */
if (atomic_read(&vcc->stats->tx) % 20 == 0) {
if (iavcc->vc_desc_cnt > 10) {
vcc->tx_quota = vcc->tx_quota * 3 / 4;
printk("Tx1: vcc->tx_quota = %d \n", (u32)vcc->tx_quota );
iavcc->flow_inc = -1;
iavcc->saved_tx_quota = vcc->tx_quota;
} else if ((iavcc->flow_inc < 0) && (iavcc->vc_desc_cnt < 3)) {
// vcc->tx_quota = 3 * iavcc->saved_tx_quota / 4;
printk("Tx2: vcc->tx_quota = %d \n", (u32)vcc->tx_quota );
iavcc->flow_inc = 0;
}
}
#endif
IF_TX(printk("ia send done\n");)
return 0;
}