#include "nge.h"
#undef NGE_DBG
#define NGE_DBG NGE_DBG_RECV
#define RXD_END 0x20000000
#define RXD_ERR 0x40000000
#define RXD_OWN 0x80000000
#define RXD_CSUM_MSK 0x1C000000
#define RXD_BCNT_MSK 0x00003FFF
#define RXD_CK8G_NO_HSUM 0x0
#define RXD_CK8G_TCP_SUM_ERR 0x04000000
#define RXD_CK8G_UDP_SUM_ERR 0x08000000
#define RXD_CK8G_IP_HSUM_ERR 0x0C000000
#define RXD_CK8G_IP_HSUM 0x10000000
#define RXD_CK8G_TCP_SUM 0x14000000
#define RXD_CK8G_UDP_SUM 0x18000000
#define RXD_CK8G_RESV 0x1C000000
extern ddi_device_acc_attr_t nge_data_accattr;
void
nge_recv_recycle(caddr_t arg)
{
boolean_t val;
boolean_t valid;
nge_t *ngep;
dma_area_t *bufp;
buff_ring_t *brp;
nge_sw_statistics_t *sw_stp;
bufp = (dma_area_t *)arg;
ngep = (nge_t *)bufp->private;
brp = ngep->buff;
sw_stp = &ngep->statistics.sw_statistics;
if (bufp->signature != brp->buf_sign) {
if (bufp->rx_delivered == B_TRUE) {
nge_free_dma_mem(bufp);
kmem_free(bufp, sizeof (dma_area_t));
val = nge_atomic_decrease(&brp->rx_hold, 1);
ASSERT(val == B_TRUE);
}
return;
}
bufp->rx_recycle.free_func = nge_recv_recycle;
bufp->rx_recycle.free_arg = (caddr_t)bufp;
bufp->mp = desballoc(DMA_VPTR(*bufp),
ngep->buf_size + NGE_HEADROOM, 0, &bufp->rx_recycle);
if (bufp->mp == NULL) {
sw_stp->mp_alloc_err++;
sw_stp->recy_free++;
nge_free_dma_mem(bufp);
kmem_free(bufp, sizeof (dma_area_t));
val = nge_atomic_decrease(&brp->rx_hold, 1);
ASSERT(val == B_TRUE);
} else {
mutex_enter(brp->recycle_lock);
if (bufp->signature != brp->buf_sign)
valid = B_TRUE;
else
valid = B_FALSE;
bufp->rx_delivered = valid;
if (bufp->rx_delivered == B_FALSE) {
bufp->next = brp->recycle_list;
brp->recycle_list = bufp;
}
mutex_exit(brp->recycle_lock);
if (valid == B_TRUE)
freemsg(bufp->mp);
else {
val = nge_atomic_decrease(&brp->rx_hold, 1);
ASSERT(val == B_TRUE);
}
}
}
static mblk_t *nge_recv_packet(nge_t *ngep, uint32_t start_index, size_t len);
static mblk_t *
nge_recv_packet(nge_t *ngep, uint32_t start_index, size_t len)
{
uint8_t *rptr;
uint32_t minsize;
uint32_t maxsize;
mblk_t *mp;
buff_ring_t *brp;
sw_rx_sbd_t *srbdp;
dma_area_t *bufp;
nge_sw_statistics_t *sw_stp;
void *hw_bd_p;
brp = ngep->buff;
minsize = ETHERMIN;
maxsize = ngep->max_sdu;
sw_stp = &ngep->statistics.sw_statistics;
mp = NULL;
srbdp = &brp->sw_rbds[start_index];
DMA_SYNC(*srbdp->bufp, DDI_DMA_SYNC_FORKERNEL);
hw_bd_p = DMA_VPTR(srbdp->desc);
if (brp->free_list == NULL) {
mutex_enter(brp->recycle_lock);
brp->free_list = brp->recycle_list;
brp->recycle_list = NULL;
mutex_exit(brp->recycle_lock);
}
bufp = brp->free_list;
if (len > maxsize || len < minsize) {
ngep->desc_attr.rxd_fill(hw_bd_p, &srbdp->bufp->cookie,
srbdp->bufp->alength);
srbdp->flags = CONTROLER_OWN;
return (NULL);
}
if (len <= ngep->param_rxbcopy_threshold || bufp == NULL)
brp->rx_bcopy = B_TRUE;
else
brp->rx_bcopy = B_FALSE;
if (brp->rx_bcopy) {
mp = allocb(len + NGE_HEADROOM, 0);
if (mp == NULL) {
sw_stp->mp_alloc_err++;
ngep->desc_attr.rxd_fill(hw_bd_p, &srbdp->bufp->cookie,
srbdp->bufp->alength);
srbdp->flags = CONTROLER_OWN;
return (NULL);
}
rptr = DMA_VPTR(*srbdp->bufp);
mp->b_rptr = mp->b_rptr + NGE_HEADROOM;
bcopy(rptr + NGE_HEADROOM, mp->b_rptr, len);
mp->b_wptr = mp->b_rptr + len;
} else {
mp = srbdp->bufp->mp;
mp->b_rptr += NGE_HEADROOM;
mp->b_wptr = mp->b_rptr + len;
mp->b_next = mp->b_cont = NULL;
srbdp->bufp->rx_delivered = B_TRUE;
srbdp->bufp = NULL;
nge_atomic_increase(&brp->rx_hold, 1);
srbdp->bufp = bufp;
brp->free_list = bufp->next;
bufp->next = NULL;
}
ngep->desc_attr.rxd_fill(hw_bd_p, &srbdp->bufp->cookie,
srbdp->bufp->alength);
srbdp->flags = CONTROLER_OWN;
sw_stp->rbytes += len;
sw_stp->recv_count++;
return (mp);
}
#define RX_HW_ERR 0x01
#define RX_SUM_NO 0x02
#define RX_SUM_ERR 0x04
static uint32_t
nge_rxsta_handle(nge_t *ngep, uint32_t stflag, uint32_t *pflags);
static uint32_t
nge_rxsta_handle(nge_t *ngep, uint32_t stflag, uint32_t *pflags)
{
uint32_t errors;
uint32_t err_flag;
nge_sw_statistics_t *sw_stp;
err_flag = 0;
sw_stp = &ngep->statistics.sw_statistics;
if ((RXD_END & stflag) == 0)
return (RX_HW_ERR);
errors = stflag & RXD_CSUM_MSK;
switch (errors) {
default:
break;
case RXD_CK8G_TCP_SUM:
case RXD_CK8G_UDP_SUM:
*pflags |= HCK_IPV4_HDRCKSUM_OK;
*pflags |= HCK_FULLCKSUM_OK;
break;
case RXD_CK8G_TCP_SUM_ERR:
case RXD_CK8G_UDP_SUM_ERR:
sw_stp->tcp_hwsum_err++;
*pflags |= HCK_IPV4_HDRCKSUM_OK;
break;
case RXD_CK8G_IP_HSUM:
*pflags |= HCK_IPV4_HDRCKSUM_OK;
break;
case RXD_CK8G_NO_HSUM:
err_flag |= RX_SUM_NO;
break;
case RXD_CK8G_IP_HSUM_ERR:
sw_stp->ip_hwsum_err++;
err_flag |= RX_SUM_ERR;
break;
}
if ((stflag & RXD_ERR) != 0) {
err_flag |= RX_HW_ERR;
NGE_DEBUG(("Receive desc error, status: 0x%x", stflag));
}
return (err_flag);
}
static mblk_t *
nge_recv_ring(nge_t *ngep)
{
uint32_t stflag;
uint32_t flag_err;
uint32_t sum_flags;
size_t len;
uint64_t end_index;
uint64_t sync_start;
mblk_t *mp;
mblk_t **tail;
mblk_t *head;
recv_ring_t *rrp;
buff_ring_t *brp;
sw_rx_sbd_t *srbdp;
void * hw_bd_p;
nge_mode_cntl mode_cntl;
mp = NULL;
head = NULL;
tail = &head;
rrp = ngep->recv;
brp = ngep->buff;
end_index = sync_start = rrp->prod_index;
if (sync_start + ngep->param_recv_max_packet <= ngep->rx_desc) {
(void) ddi_dma_sync(rrp->desc.dma_hdl,
sync_start * ngep->desc_attr.rxd_size,
ngep->param_recv_max_packet * ngep->desc_attr.rxd_size,
DDI_DMA_SYNC_FORKERNEL);
} else {
(void) ddi_dma_sync(rrp->desc.dma_hdl,
sync_start * ngep->desc_attr.rxd_size,
0,
DDI_DMA_SYNC_FORKERNEL);
(void) ddi_dma_sync(rrp->desc.dma_hdl,
0,
(ngep->param_recv_max_packet + sync_start - ngep->rx_desc) *
ngep->desc_attr.rxd_size,
DDI_DMA_SYNC_FORKERNEL);
}
for (;;) {
sum_flags = 0;
flag_err = 0;
end_index = rrp->prod_index;
srbdp = &brp->sw_rbds[end_index];
hw_bd_p = DMA_VPTR(srbdp->desc);
stflag = ngep->desc_attr.rxd_check(hw_bd_p, &len);
if ((stflag & RXD_OWN) != 0 || HOST_OWN == srbdp->flags)
break;
ngep->recv_count++;
flag_err = nge_rxsta_handle(ngep, stflag, &sum_flags);
if ((flag_err & RX_HW_ERR) == 0) {
srbdp->flags = NGE_END_PACKET;
mp = nge_recv_packet(ngep, end_index, len);
} else {
ngep->desc_attr.rxd_fill(hw_bd_p, &srbdp->bufp->cookie,
srbdp->bufp->alength);
srbdp->flags = CONTROLER_OWN;
}
if (mp != NULL) {
if (!(flag_err & (RX_SUM_NO | RX_SUM_ERR))) {
mac_hcksum_set(mp, 0, 0, 0, 0, sum_flags);
}
*tail = mp;
tail = &mp->b_next;
mp = NULL;
}
rrp->prod_index = NEXT(end_index, rrp->desc.nslots);
if (ngep->recv_count >= ngep->param_recv_max_packet)
break;
}
if (sync_start + ngep->recv_count <= ngep->rx_desc) {
(void) ddi_dma_sync(rrp->desc.dma_hdl,
sync_start * ngep->desc_attr.rxd_size,
ngep->recv_count * ngep->desc_attr.rxd_size,
DDI_DMA_SYNC_FORDEV);
} else {
(void) ddi_dma_sync(rrp->desc.dma_hdl,
sync_start * ngep->desc_attr.rxd_size,
0,
DDI_DMA_SYNC_FORDEV);
(void) ddi_dma_sync(rrp->desc.dma_hdl,
0,
(ngep->recv_count + sync_start - ngep->rx_desc) *
ngep->desc_attr.rxd_size,
DDI_DMA_SYNC_FORDEV);
}
mode_cntl.mode_val = nge_reg_get32(ngep, NGE_MODE_CNTL);
mode_cntl.mode_bits.rxdm = NGE_SET;
mode_cntl.mode_bits.tx_rcom_en = NGE_SET;
nge_reg_put32(ngep, NGE_MODE_CNTL, mode_cntl.mode_val);
return (head);
}
void
nge_receive(nge_t *ngep)
{
mblk_t *mp;
recv_ring_t *rrp;
rrp = ngep->recv;
mp = nge_recv_ring(ngep);
mutex_exit(ngep->genlock);
if (mp != NULL)
mac_rx(ngep->mh, rrp->handle, mp);
mutex_enter(ngep->genlock);
}
void
nge_hot_rxd_fill(void *hwd, const ddi_dma_cookie_t *cookie, size_t len)
{
uint64_t dmac_addr;
hot_rx_bd * hw_bd_p;
hw_bd_p = (hot_rx_bd *)hwd;
dmac_addr = cookie->dmac_laddress + NGE_HEADROOM;
hw_bd_p->cntl_status.cntl_val = 0;
hw_bd_p->host_buf_addr_hi = dmac_addr >> 32;
hw_bd_p->host_buf_addr_lo = (uint32_t)dmac_addr;
hw_bd_p->cntl_status.control_bits.bcnt = len - 1;
membar_producer();
hw_bd_p->cntl_status.control_bits.own = NGE_SET;
}
void
nge_sum_rxd_fill(void *hwd, const ddi_dma_cookie_t *cookie, size_t len)
{
sum_rx_bd * hw_bd_p;
hw_bd_p = hwd;
hw_bd_p->cntl_status.cntl_val = 0;
hw_bd_p->host_buf_addr =
(uint32_t)(cookie->dmac_address + NGE_HEADROOM);
hw_bd_p->cntl_status.control_bits.bcnt = len - 1;
membar_producer();
hw_bd_p->cntl_status.control_bits.own = NGE_SET;
}
uint32_t
nge_hot_rxd_check(const void *hwd, size_t *len)
{
uint32_t err_flag;
const hot_rx_bd * hrbdp;
hrbdp = hwd;
err_flag = hrbdp->cntl_status.cntl_val;
*len = err_flag & RXD_BCNT_MSK;
return (err_flag);
}
uint32_t
nge_sum_rxd_check(const void *hwd, size_t *len)
{
uint32_t err_flag;
const sum_rx_bd * hrbdp;
hrbdp = hwd;
err_flag = hrbdp->cntl_status.cntl_val;
*len = err_flag & RXD_BCNT_MSK;
return (err_flag);
}