smcp.c revision bd118333506194b55077122465f5051a4e3ac349
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* smcp -- Upper MAC driver for SMC PCI adapters
*
*/
#ifdef _DDICT
#include "sys/ethernet.h"
#else
#include <sys/ethernet.h>
#endif
#include SMC_INCLUDE
#include "smcp.h"
char _depends_on[] = "misc/gld";
static ddi_dma_attr_t buf_dma_attr = {
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0x0ffffff, /* maximum DMAable byte count */
1, /* alignment in bytes */
0x7f, /* bitmap of burst sizes */
1, /* minimum transfer */
0x0ffffffU, /* maximum transfer */
0x0ffffffU, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
static ddi_dma_attr_t txdata_attr = {
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0x0ffffff, /* maximum DMAable byte count */
1, /* alignment in bytes */
0x7f, /* bitmap of burst sizes */
1, /* minimum transfer */
0x0ffffffU, /* maximum transfer */
0x0ffffffU, /* maximum segment length */
2, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
static ddi_dma_attr_t host_ram_dma_attr = {
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffU, /* highest usable address */
0x0ffffff, /* maximum DMAable byte count */
HOST_RAM_ALIGNMENT, /* alignment in bytes */
0x7f, /* bitmap of burst sizes */
1, /* minimum transfer */
0x0ffffffU, /* maximum transfer */
0x0ffffffU, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
static ddi_device_acc_attr_t accattr = {
};
/*
* Declarations and Module Linkage
*/
#ifdef DEBUG
static int SMCG_debug = 0;
#endif
/* Required system entry points */
static int SMCG_probe(dev_info_t *);
/* Required driver entry points for GLD */
static int SMCG_set_mac_addr(gld_mac_info_t *, unsigned char *);
static int SMCG_reset(gld_mac_info_t *);
static int SMCG_start_board(gld_mac_info_t *);
static int SMCG_stop_board(gld_mac_info_t *);
static int SMCG_set_multicast(gld_mac_info_t *, unsigned char *, int);
static int SMCG_set_promiscuous(gld_mac_info_t *, int);
/* Internal functions */
static int SMCG_init_board(gld_mac_info_t *);
static int SMCG_dma_alloc(smcg_t *);
static void SMCG_dma_unalloc(smcg_t *);
static void SMCG_freertn(struct smcg_rx_buffer_desc *);
static unsigned char
/* Standard Streams initialization */
static struct module_info minfo = {
};
};
};
/* Standard Module linkage initialization for a Streams driver */
static struct cb_ops cb_smcg_ops = {
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&smcg_info, /* cb_stream */
(int)(D_MP) /* cb_flag */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
gld_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
SMCG_probe, /* devo_probe */
SMCG_attach, /* devo_attach */
SMCG_detach, /* devo_detach */
nodev, /* devo_reset */
&cb_smcg_ops, /* devo_cb_ops */
};
&mod_driverops, /* Type of module. This one is a driver */
&smcg_ops /* driver specific ops */
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
/*
* DDI Entry Points
*/
/* probe(9E) -- Determine if a device is present */
/* ARGSUSED */
static int
{
return (DDI_PROBE_SUCCESS);
}
/*
* attach(9E) -- Attach a device to the system
*
* Called once for each board successfully probed.
*/
static int
{
int rc;
#ifdef DEBUG
if (SMCG_debug & SMCGDDI)
#endif
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
/*
* Allocate gld_mac_info_t and Lower MAC Adapter_Struc structures
*/
return (DDI_FAILURE);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* create pci handle for UM_PCI_Service */
!= DDI_SUCCESS) {
goto attach_fail_cleanup;
}
/*
* Query the LMAC for the device information
*/
if (rc != ADAPTER_AND_CONFIG) {
goto attach_fail_cleanup;
}
/*
* Initialize pointers to device specific functions which will be
* used by the generic layer.
*/
/*
* Initialize board characteristics needed by the generic layer.
*/
/* Get the board's vendor-assigned hardware network address. */
/* Link macinfo, smcg, and LMAC Adapter Structs */
"Max number_of_tx_buffs is %d", SMCG_MAX_TXDESCS);
}
}
"Max number_of_rx_buffs is %d", SMCG_MAX_RXDESCS);
}
}
!= DDI_SUCCESS)
goto attach_fail_cleanup;
/*
* rbuf_lock Protects receive data structures
* txbuf_lock Protects transmit data structures
* lm_lock Protects all calls to LMAC layer
* rlist_lock Protects receive buffer list
* Note: Locks should be acquired in the above order.
*/
/*
* SMCG_dma_alloc is called before it is possible to get
* any interrupts, send or receive packets... Therefore I'm
* not going to take rlist_lock for it.
*/
goto attach_fail_cleanup1;
#ifdef SAFE
#endif
/* Add the interrupt handler */
!= DDI_SUCCESS) {
goto attach_fail_cleanup1;
}
/*
* Register ourselves with the GLD interface
*
* gld_register will:
* link us with the GLD system;
* create the minor node.
*/
goto attach_fail_cleanup1;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/* detach(9E) -- Detach a device from the system */
static int
{
int i;
#ifdef DEBUG
if (SMCG_debug & SMCGDDI)
#endif
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
i = 50;
while (smcg->rx_bufs_outstanding > 0) {
if (--i == 0) {
SMCG_NAME "%d: %d buffers not reclaimed",
return (DDI_FAILURE);
}
}
/*
* Unregister ourselves from the GLD interface
*
* gld_unregister will:
* remove the minor node;
* unlink us from the GLD system.
*/
smcg->detaching_flag = 0;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* GLD Entry Points
*/
/*
* SMCG_reset() -- reset the board to initial state.
*/
static int
{
int rc;
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
#endif
}
/*
* SMCG_init_board() -- initialize the specified network board.
*/
static int
{
int rc;
int i;
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
(void *)macinfo);
#endif
smcg->rx_ring_index = 0;
/* Give the buffers on the receive ring to the LMAC */
for (i = 0; i < pAd->num_of_rx_buffs; i++) {
}
/*
* The spec says we should wait for UM_Status_Change, but all LMs
* we currently support change the status prior to returning from
* LM_Initialize_Adapter().
*/
return (rc);
}
/*
* SMCG_start_board() -- start the board receiving and allow transmits.
*/
static int
{
int rc;
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
(void *)macinfo);
#endif
/*
* The spec says we should wait for UM_Status_Change, but all LMs
* we currently support change the status prior to returning from
* LM_Open_Adapter().
*/
}
/*
* SMCG_stop_board() -- stop board receiving
*/
static int
{
int rc, i;
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
(void *)macinfo);
#endif
i = 20;
}
i++)
(void) ddi_dma_unbind_handle(
}
/*
* The spec says we should wait for UM_Status_Change, but all LMs
* we currently support change the status prior to returning from
* LM_Close_Adapter().
*/
#ifdef DEBUG
#endif
}
/*
* SMCG_set_mac_addr() -- set node MAC address
*/
static int
{
return (GLD_SUCCESS);
}
/*
* SMCG_set_multicast() -- set (enable) or disable a multicast address
*
* in "mcast".
*/
static int
{
int rc;
int i;
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
#endif
for (i = 0; i < ETHERADDRL; i++)
if (op == GLD_MULTI_ENABLE) {
}
} else {
if (--smcg->smcg_multicount == 0) {
}
}
#ifdef DEBUG
#endif
}
/*
* SMCG_set_promiscuous() -- set or reset promiscuous mode on the board
*
*/
static int
{
int rc;
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
(void *)macinfo,
#endif
if (on != GLD_MAC_PROMISC_NONE) {
} else {
}
#ifdef DEBUG
#endif
}
/*
* SMCG_get_stats() -- update statistics
*/
static int
{
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
#endif
/*
* I am not taking mutexes around the statistics assigments as they could
* have changed by the time the user application gets them.
*
* However not taking the mutex means that statistics may not be self
* consistant.
*
* But if this is changed in the future then take mutexes in the following
* order rbuf_lock, txbuf_lock, lm_lock.
*/
/* stats added in conversion to v2 */
else {
}
/* Stats which are calculated from other stats */
return (GLD_SUCCESS);
}
/*
* SMCG_send() -- send a packet
*/
static int
{
unsigned int ncookies;
if (i >= SMCG_MAX_TX_MBLKS) {
return (GLD_NORESOURCES); /* retry send */
}
break;
}
}
return (GLD_BADARG);
}
== smcg->tx_ring_tail) {
return (GLD_NORESOURCES); /* retry send */
}
if (blocklen == 0)
continue;
ASSERT(i < SMCG_MAX_TX_MBLKS);
if (rc != DDI_DMA_MAPPED) {
while (--i >= 0)
(void) ddi_dma_unbind_handle(
dmahandle[i]);
if (rc == DDI_DMA_NORESOURCES) {
return (GLD_NORESOURCES);
}
#ifdef DEBUG
if (SMCG_debug & SMCGTRACE)
"Send bind handle failure = 0x%x", rc);
#endif
return (GLD_FAILURE);
}
/* CONSTANTCONDITION */
while (1) {
j++;
if (--ncookies == 0)
break;
&cookie);
}
i++;
}
dbuf.fragment_count = j;
for (i = 0;
(void) ddi_dma_unbind_handle(
} else
smcg->tx_ring_head =
#ifdef DEBUG
#endif
return (GLD_SUCCESS);
} else if (rc == OUT_OF_RESOURCES) {
return (GLD_NORESOURCES);
} else {
return (GLD_FAILURE);
}
}
/*
* SMCG_intr() -- interrupt from board to inform us that a receive or
* transmit has completed.
*/
static uint_t
{
int rc;
#ifdef DEBUG
if (SMCG_debug & SMCGINT)
#endif
}
#ifdef DEBUG
#endif
if (rc == NOT_MY_INTERRUPT)
return (DDI_INTR_UNCLAIMED);
if (smcg->smcg_need_gld_sched) {
smcg->smcg_need_gld_sched = 0;
}
return (DDI_INTR_CLAIMED);
}
/* ARGSUSED */
int
{
struct smcg_rx_buffer_desc *bdesc;
/*
* Look for a free data buffer to replace the one we are about
* to pass upstream
*/
goto rcv_done; /* No resources */
}
} else {
/* freelist empty, leave buffer intact, and copy out data */
goto rcv_done; /* No resources, drop the packet */
}
length);
}
/*
* Queue received msgblks to be sent up to GLD with out holding
* any mutexes
*/
/* Make sure that the last one points to NULL */
} else {
}
return (SUCCESS);
}
/*
* UM_Status_Change -- LM has completed a driver state change
*/
/* ARGSUSED */
int
{
/*
* This function is called by several LMACs but the completion
* mechanism is not used by the UMAC to determine if the event
* has completed, because all applicable functions complete
* prior to returning.
*/
return (SUCCESS);
}
/*
* UM_Receive_Copy_Complete() -- LM has completed a receive copy
*/
/* ARGSUSED */
int
{
/*
* This completion mechanism is not used by the UMAC to
* determine if the copy has completed, because all LMACs
* complete the copy prior to returning.
*/
return (SUCCESS);
}
/*
* UM_Send_Complete() -- LM has completed sending a packet
*/
/* ARGSUSED */
int
{
int i;
(void) ddi_dma_unbind_handle(
}
return (SUCCESS);
}
/*
* UM_Interrupt() -- LM has generated an interrupt at our request
*/
/* ARGSUSED */
int
{
#ifdef DEBUG
#endif
return (SUCCESS);
}
int
lm_stub()
{
return (SUCCESS);
}
int
{
unsigned long regnum; /* register number */
unsigned short vendid;
unsigned short devid;
unsigned long compval;
switch (func) {
case PCI_BIOS_PRESENT:
/* return PCI present with rev 2.1 */
break;
case FIND_PCI_DEVICE:
} else {
if (pci_config_get32(
compval) {
} else {
}
}
break;
case PCI_READ_CONFIG_BYTE:
break;
case PCI_READ_CONFIG_WORD:
if (regnum & 0x1) {
} else {
}
break;
case PCI_READ_CONFIG_DWORD:
if (regnum & 0x3) {
} else {
}
break;
case PCI_WRITE_CONFIG_BYTE:
break;
case PCI_WRITE_CONFIG_WORD:
if (regnum & 0x1) {
} else {
}
break;
case PCI_WRITE_CONFIG_DWORD:
if (regnum & 0x1) {
} else {
}
break;
default:
break;
}
return (0);
}
/* Functions that the LMAC doesn't know about */
/*
* SMCG_dma_alloc assumes that either rlist_lock mutex is held or
* that it is called from a point where no interrupts, send or receives
* happen.
*/
static int
{
/* Allocate resources for shared memory block */
return (DDI_FAILURE);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* Allocate a list of receive buffers */
!= DDI_SUCCESS)
goto failure;
goto failure;
}
goto failure;
}
(char *)&smcg->rx_freelist[i];
}
/*
* Remove one buffer from free list for each receive descriptor,
* and associate with an element in the receive ring
*/
for (i = 0; i < pAd->num_of_rx_buffs; i++) {
/* Unlink from free list */
}
/* Allocate the handles to which we bind outgoing data */
for (i = 0; i < pAd->num_of_tx_buffs; i++)
for (j = 0; j < SMCG_MAX_TX_MBLKS; j++)
&txdata_attr, DDI_DMA_SLEEP, 0,
goto failure;
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* SMCG_dma_unalloc assumes that either rlist_lock mutex is held or
* that it is called from a point where no interrupts, send or receives
* happen.
*/
/* XXX Bogus to look at the supposedly opaque handles, even to look for NULL */
static void
{
struct smcg_rx_buffer_desc *bdesc;
int i, j;
for (i = 0; i < pAd->num_of_tx_buffs; i++)
for (j = 0; j < SMCG_MAX_TX_MBLKS; j++) {
}
/* Free up rx buffers currently on freelist */
}
/* Free up all rx buffers that are associated with rx descriptors */
for (i = 0; i < pAd->num_of_rx_buffs; i++) {
continue;
}
/* Free resources associated with shared ram block */
}
/* esballoc() callback - Called when the message we sent up is freed */
static void
{
/* Return the receive buffer to the freelist */
}