/*
* upf_usbgem.c : ADMtek an986/adm8511/adm8513/adm8515 USB to
* Fast Ethernet Driver for Solaris
*/
/*
* Copyright (c) 2004-2011 Masayuki Murayama. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
*
* 3. Neither the name of the author nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/*
* Changelog:
*/
/*
* TODO
*/
/* ======================================================= */
/*
* Solaris system header files and macros
*/
#include <sys/byteorder.h>
/* ethernet stuff */
#include <sys/ethernet.h>
/* interface card depend stuff */
#include "usbgem.h"
/* hardware stuff */
#include "usbgem_mii.h"
#include "adm8511reg.h"
char ident[] = "pegasus usbnic driver v" VERSION;
/*
* Useful macros
*/
/*
* Debugging
*/
#ifdef DEBUG_LEVEL
#else
#endif
/*
*/
/* timeouts */
/*
* Local device definitions
*/
struct upf_dev {
/*
* Misc HW information
*/
int chip_type;
};
/*
* private functions
*/
/* mii operations */
/* nic operations */
static int upf_attach_chip(struct usbgem_dev *);
static int upf_reset_chip(struct usbgem_dev *);
static int upf_init_chip(struct usbgem_dev *);
static int upf_start_chip(struct usbgem_dev *);
static int upf_stop_chip(struct usbgem_dev *);
static int upf_set_media(struct usbgem_dev *);
static int upf_set_rx_filter(struct usbgem_dev *);
static int upf_get_stats(struct usbgem_dev *);
/* packet operations */
/* interrupt handler */
/* =============================================================== */
/*
* I/O functions
*/
/* =============================================================== */
/* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
/* bRequest */ UPF_REQ_SET_REGISTER, \
/* wValue */ (v), \
/* wIndex */ (p), \
/* wLength */ 1, \
/* buf */ NULL, \
/* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
/* bRequest */ UPF_REQ_SET_REGISTER, \
/* wValue */ 0, \
/* wIndex */ (p), \
/* wLength */ 2, \
/* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \
/* bRequest */ UPF_REQ_SET_REGISTER, \
/* wValue */ 0, \
/* wIndex */ (p), \
/* wLength */ (len), \
/* buf */ (buf), \
/* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
/* bRequest */ UPF_REQ_GET_REGISTER, \
/* wValue */ 0, \
/* wIndex */ (p), \
/* wLength */ 1, \
/* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
/* bRequest */ UPF_REQ_GET_REGISTER, \
/* wValue */ 0, \
/* wIndex */ (p), \
/* wLength */ 2, \
/* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \
/* bRequest */ UPF_REQ_GET_REGISTER, \
/* wValue */ 0, \
/* wIndex */ (p), \
/* wLength */ (len), \
/* buf */ (buf), \
/* =============================================================== */
/*
* Hardware manupilation
*/
/* =============================================================== */
static int
{
int i;
int err;
for (i = 0; i < 1000; i++) {
return (USB_SUCCESS);
}
drv_usecwait(10);
}
/* time out */
return (USB_FAILURE);
return (USB_FAILURE);
}
/*
*/
static int
{
/* ethernet control register 0 */
/* ethernet control reg1: will be set later in set_rx_filter() */
/* ethernet control register 2: will be set later in set_rx_filter() */
#ifdef CONFIG_VLAN
}
#endif
/* Multicast address hash: clear */
/* Ethernet ID : will be set later in upf_set_rx_filter() */
/* PAUSE timer */
/* receive packet number based pause control:set in upf_set_media() */
/* occupied receive FIFO based pause control:set in upf_set_media() */
/* EP1 control: default */
/* Rx FIFO control */
/* use 24K internal sram, 16pkts in fifo */
}
/* BIST contror: do nothing */
return (USB_SUCCESS);
return (err);
}
static int
{
/* enable RX and TX */
return (USB_SUCCESS);
return (err);
}
static int
{
int err;
/* disable RX and TX */
return (USB_SUCCESS);
return (err);
}
static int
{
/* do nothing */
return (USB_SUCCESS);
}
static uint_t
{
/* hash table is 64 = 2^6 bit width */
}
static int
{
int i;
int err;
#ifdef DEBUG_LEVEL
#endif
/* reset rx mode */
/* promiscious mode implies all multicast and all physical */
/* XXX - multicast hash table didin't work */
/* accept all multicast packets */
}
/* need to update mac address */
}
/* update rx mode */
#if DEBUG_LEVEL > 0
#endif
return (USB_SUCCESS);
return (err);
}
static int
{
int err;
/* select duplex */
if (dp->full_duplex) {
}
/* select speed */
}
/* rx flow control */
switch (dp->flow_control) {
case FLOW_CONTROL_SYMMETRIC:
case FLOW_CONTROL_RX_PAUSE:
break;
default:
break;
}
/* tx flow control */
switch (dp->flow_control) {
case FLOW_CONTROL_SYMMETRIC:
case FLOW_CONTROL_TX_PAUSE:
/* pegasus II has internal 24k fifo */
/* 16 packts can be stored in rx fifo */
} else {
/* an986 has external 32k fifo */
/* AN986 fails to link up when RPNBFC is enabled */
}
break;
default:
break;
}
/* update ether control registers */
return (USB_SUCCESS);
return (err);
}
/*
*/
static mblk_t *
{
int msglen;
}
/* allocate msg block */
/* avoid usb controller bug */
if ((msglen & 0x3f) == 0) {
/* add a header for additional 0-length usb message */
}
return (NULL);
}
/* copy contents of the buffer */
/* the nic requires a two byte header of the packet size */
/* copy the payload */
if (len > 0) {
}
}
/* clear ethernet pads and additional usb header if we have */
*bp++ = 0;
}
return (new);
}
static void
{
int i;
}
}
static mblk_t *
{
uint8_t *p;
#ifdef DEBUG_LEVEL
if (upf_debug > 3) {
}
#endif
/* get the length of Rx packet */
rsr = p[3];
/* As Rx packets from ADM8513 have two byte header, remove it */
} else {
}
/* check if error happen */
if (rsr & RSR_ERRORS) {
}
}
}
return (NULL);
}
#ifndef CONFIG_VLAN
/* check packet size */
/* too long */
return (NULL);
return (NULL);
}
#endif
/* remove tailing crc and rx status fields */
return (mp);
}
/*
* Device depend interrupt handler
*/
static void
{
"!%s: %s: size:%d, %02x %02x %02x %02x %02x %02x %02x %02x",
}
}
/*
* MII Interfaces
*/
static uint16_t
{
int i;
*errp = USB_SUCCESS;
/* set PHYADDR */
/* Initiate MII read transaction */
for (i = 0; i < 100; i++) {
/* done */
return (val);
}
drv_usecwait(10);
}
/* timeout */
*errp = USB_FAILURE;
return (0);
return (0);
}
static void
{
int i;
*errp = USB_SUCCESS;
for (i = 0; i < 100; i++) {
/* done */
return;
}
drv_usecwait(10);
}
/* time out */
*errp = USB_FAILURE;
return;
}
static int
{
int err;
/*
* first, try to enable internal phy
*/
if (val) {
/* reset internal phy */
/* identify the chip generation */
if (val == 0xa5) {
} else {
/* adm8511 or adm8515 */
}
} else {
/*
* It should be AN986 which doesn't have an internal PHY.
* We need to setup gpio ports in AN986, which are
* connected to external PHY control pins.
*/
/* reset external phy */
/* output port#0 L, port#1 L */
/* output port#0 H, port#1 L */
/* hw link detection doesn't work correctly */
}
return (USB_SUCCESS);
return (USB_FAILURE);
}
static int
{
int err;
if (!lp->phy_init_done) {
}
return (usbgem_mii_probe_default(dp));
}
static int
{
if (!lp->phy_init_done) {
}
/* special treatment for Linksys products */
}
return (err);
}
/* ======================================================== */
/*
* OS depend (device driver DKI) routine
*/
/* ======================================================== */
static uint16_t
{
int i;
*errp = USB_SUCCESS;
for (i = 0; i < 100; i++) {
if (eectrl & EECTRL_DONE) {
return (data);
}
drv_usecwait(10);
}
/* time out */
*errp = USB_FAILURE;
return (0);
return (0);
}
static void
{
int i;
int err;
for (i = 0; i < size; i += 4) {
i*2,
}
}
static int
{
int i;
int err;
/*
* Read mac address from EEPROM
*/
for (i = 0; i < 3; i++) {
if (err != USB_SUCCESS) {
goto usberr;
}
}
"%s: %s: mac: %02x:%02x:%02x:%02x:%02x:%02x",
#ifdef CONFIG_VLAN
#endif
#if DEBUG_LEVEL > 3
#endif
return (USB_SUCCESS);
return (USB_FAILURE);
}
static int
{
int i;
int ret;
int unit;
int len;
const char *drv_name;
void *base;
if (cmd == DDI_ATTACH) {
/*
* construct usbgem configration
*/
/* name */
ugcp->usbgc_ifnum = 0;
/* time out parameters */
/* flow control */
/* MII timeout parameters */
/* I/O methods */
/* mac operation */
/* packet operation */
/* mii operations */
/* mtu */
return (DDI_SUCCESS);
}
err:
return (DDI_FAILURE);
}
if (cmd == DDI_RESUME) {
return (usbgem_resume(dip));
}
return (DDI_FAILURE);
}
static int
{
int ret;
if (cmd == DDI_DETACH) {
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
if (cmd == DDI_SUSPEND) {
return (usbgem_suspend(dip));
}
return (DDI_FAILURE);
}
/* ======================================================== */
/*
* OS depend (loadable streams driver) routine
*/
/* ======================================================== */
#ifdef USBGEM_CONFIG_GLDv3
#else
0, /* mi_idnum */
"upf", /* mi_idname */
0, /* mi_minpsz */
ETHERMTU, /* mi_maxpsz */
32*1024, /* mi_hiwat */
1, /* mi_lowat */
};
(int (*)()) NULL, /* qi_putp */
usbgem_rsrv, /* qi_srvp */
usbgem_open, /* qi_qopen */
usbgem_close, /* qi_qclose */
(int (*)()) NULL, /* qi_qadmin */
&upfminfo, /* qi_minfo */
NULL /* qi_mstat */
};
usbgem_wput, /* qi_putp */
usbgem_wsrv, /* qi_srvp */
(int (*)()) NULL, /* qi_qopen */
(int (*)()) NULL, /* qi_qclose */
(int (*)()) NULL, /* qi_qadmin */
&upfminfo, /* qi_minfo */
NULL /* qi_mstat */
};
&upfrinit, /* st_rdinit */
&upfwinit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL /* st_muxwrinit */
};
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 */
&upf_info, /* cb_stream */
D_MP /* cb_flag */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
usbgem_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
upfattach, /* devo_attach */
upfdetach, /* devo_detach */
nodev, /* devo_reset */
&cb_upf_ops, /* devo_cb_ops */
NULL, /* devo_bus_ops */
usbgem_power, /* devo_power */
#if DEVO_REV >= 4
usbgem_quiesce, /* devo_quiesce */
#endif
};
#endif
&mod_driverops, /* Type of module. This one is a driver */
ident,
&upf_ops, /* driver ops */
};
};
/* ======================================================== */
/*
* _init : done
*/
/* ======================================================== */
int
_init(void)
{
int status;
if (status != DDI_SUCCESS) {
return (status);
}
if (status != DDI_SUCCESS) {
}
return (status);
}
/*
* _fini : done
*/
int
_fini(void)
{
int status;
if (status == DDI_SUCCESS) {
}
return (status);
}
int
{
}