diff options
Diffstat (limited to 'usr/src')
34 files changed, 13723 insertions, 0 deletions
diff --git a/usr/src/pkg/manifests/driver-network-axf.mf b/usr/src/pkg/manifests/driver-network-axf.mf new file mode 100644 index 0000000000..038ed8aed0 --- /dev/null +++ b/usr/src/pkg/manifests/driver-network-axf.mf @@ -0,0 +1,50 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent Inc. +# + +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/network/axf@$(PKGVERS) +set name=pkg.description value="ASIX AX88172/772 USB to Fast Ethernet Driver" +set name=pkg.summary value="ASIX AX88172/772 USB to Fast Ethernet Driver" +set name=info.classification \ + value=org.opensolaris.category.2008:Drivers/Networking +set name=variant.arch value=i386 +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +driver name=axf clone_perms="axf 0666 root sys" perms="* 0666 root sys" \ + alias=usb1189,893 \ + alias=usb13b1,18 \ + alias=usb1557,7720 \ + alias=usb1631,6200 \ + alias=usb2001,1a00 \ + alias=usb2001,3c05 \ + alias=usb411,3d \ + alias=usb557,2009 \ + alias=usb5ac,1402 \ + alias=usb6189,182d \ + alias=usb77b,2226 \ + alias=usb7aa,17 \ + alias=usb7b8,420a \ + alias=usb7d1,3c05 \ + alias=usb846,1040 \ + alias=usb8dd,90ff \ + alias=usbb95,1720 \ + alias=usbb95,7720 \ + alias=usbb95,772a +file path=kernel/drv/$(ARCH64)/axf group=sys +file path=kernel/drv/axf group=sys +license usr/src/uts/common/io/axf/THIRDPARTYLICENSE \ + license=usr/src/uts/common/io/axf/THIRDPARTYLICENSE +depend fmri=driver/usb/usbgem type=require diff --git a/usr/src/pkg/manifests/driver-network-udmf.mf b/usr/src/pkg/manifests/driver-network-udmf.mf new file mode 100644 index 0000000000..124852293b --- /dev/null +++ b/usr/src/pkg/manifests/driver-network-udmf.mf @@ -0,0 +1,36 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent Inc. +# + +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/network/udmf@$(PKGVERS) +set name=pkg.description value="Davicom DM9601E USB to Fast Ethernet Driver" +set name=pkg.summary value="Davicom DM9601E USB to Fast Ethernet Driver" +set name=info.classification \ + value=org.opensolaris.category.2008:Drivers/Networking +set name=variant.arch value=i386 +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +driver name=udmf clone_perms="udmf 0666 root sys" perms="* 0666 root sys" \ + alias=usb7aa,9601 \ + alias=usba46,268 \ + alias=usba46,6688 \ + alias=usba46,9601 \ + alias=usbfe6,8101 +file path=kernel/drv/$(ARCH64)/udmf group=sys +file path=kernel/drv/udmf group=sys +license usr/src/uts/common/io/udmf/THIRDPARTYLICENSE \ + license=usr/src/uts/common/io/udmf/THIRDPARTYLICENSE +depend fmri=driver/usb/usbgem type=require diff --git a/usr/src/pkg/manifests/driver-network-upf.mf b/usr/src/pkg/manifests/driver-network-upf.mf new file mode 100644 index 0000000000..e12a8a41d7 --- /dev/null +++ b/usr/src/pkg/manifests/driver-network-upf.mf @@ -0,0 +1,104 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent Inc. +# + +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/network/upf@$(PKGVERS) +set name=pkg.description value="ADMtek USB to Fast Ethernet Driver" +set name=pkg.summary value="ADMtek USB to Fast Ethernet Driver" +set name=info.classification \ + value=org.opensolaris.category.2008:Drivers/Networking +set name=variant.arch value=i386 +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +driver name=upf clone_perms="upf 0666 root sys" perms="* 0666 root sys" \ + alias=usb1044,8002 \ + alias=usb1342,304 \ + alias=usb15e8,9100 \ + alias=usb15e8,9110 \ + alias=usb2001,200c \ + alias=usb2001,4001 \ + alias=usb2001,4002 \ + alias=usb2001,4003 \ + alias=usb2001,400b \ + alias=usb2001,4102 \ + alias=usb2001,abc1 \ + alias=usb334,1701 \ + alias=usb3f0,811c \ + alias=usb411,1 \ + alias=usb411,5 \ + alias=usb411,9 \ + alias=usb45e,7a \ + alias=usb49f,8511 \ + alias=usb4bb,904 \ + alias=usb4bb,913 \ + alias=usb4bb,93a \ + alias=usb506,4601 \ + alias=usb50d,121 \ + alias=usb557,2007 \ + alias=usb56e,200c \ + alias=usb56e,4002 \ + alias=usb56e,4005 \ + alias=usb56e,400b \ + alias=usb56e,4010 \ + alias=usb56e,abc1 \ + alias=usb5cc,3000 \ + alias=usb66b,200c \ + alias=usb66b,2202 \ + alias=usb66b,2203 \ + alias=usb66b,2204 \ + alias=usb66b,2206 \ + alias=usb66b,400b \ + alias=usb66b,8b4 \ + alias=usb67c,1001 \ + alias=usb707,200 \ + alias=usb707,201 \ + alias=usb7a6,1986 \ + alias=usb7a6,8511 \ + alias=usb7a6,8513 \ + alias=usb7a6,8515 \ + alias=usb7a6,986 \ + alias=usb7aa,4 \ + alias=usb7aa,d \ + alias=usb7b8,110c \ + alias=usb7b8,200c \ + alias=usb7b8,4002 \ + alias=usb7b8,4004 \ + alias=usb7b8,4007 \ + alias=usb7b8,400b \ + alias=usb7b8,400c \ + alias=usb7b8,4102 \ + alias=usb7b8,4104 \ + alias=usb7b8,abc1 \ + alias=usb7c9,b100 \ + alias=usb83a,1046 \ + alias=usb83a,5046 \ + alias=usb83a,b004 \ + alias=usb846,1020 \ + alias=usb8d1,3 \ + alias=usb8dd,8511 \ + alias=usb8dd,986 \ + alias=usb8dd,987 \ + alias=usb8dd,988 \ + alias=usb951,a \ + alias=usbb3p,109 \ + alias=usbb3p,901 \ + alias=usbdb7,2 \ + alias=usbe66,400c +file path=kernel/drv/$(ARCH64)/upf group=sys +file path=kernel/drv/upf group=sys +license usr/src/uts/common/io/upf/THIRDPARTYLICENSE \ + license=usr/src/uts/common/io/upf/THIRDPARTYLICENSE +depend fmri=driver/usb/usbgem type=require diff --git a/usr/src/pkg/manifests/driver-network-urf.mf b/usr/src/pkg/manifests/driver-network-urf.mf new file mode 100644 index 0000000000..0cc0145d3f --- /dev/null +++ b/usr/src/pkg/manifests/driver-network-urf.mf @@ -0,0 +1,37 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent Inc. +# + +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/network/urf@$(PKGVERS) +set name=pkg.description value="Realtek RTL8150 USB to Fast Ethernet Driver" +set name=pkg.summary value="Realtek RTL8150 USB to Fast Ethernet Driver" +set name=info.classification \ + value=org.opensolaris.category.2008:Drivers/Networking +set name=variant.arch value=i386 +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +driver name=urf clone_perms="urf 0666 root sys" perms="* 0666 root sys" \ + alias=usb1557,8150 \ + alias=usb3980,3 \ + alias=usb411,12 \ + alias=usb586,401a \ + alias=usb7b8,401a \ + alias=usbbda,8150 +file path=kernel/drv/$(ARCH64)/urf group=sys +file path=kernel/drv/urf group=sys +license usr/src/uts/common/io/urf/THIRDPARTYLICENSE \ + license=usr/src/uts/common/io/urf/THIRDPARTYLICENSE +depend fmri=driver/usb/usbgem type=require diff --git a/usr/src/pkg/manifests/driver-usb-usbgem.mf b/usr/src/pkg/manifests/driver-usb-usbgem.mf new file mode 100644 index 0000000000..6e378e5dc8 --- /dev/null +++ b/usr/src/pkg/manifests/driver-usb-usbgem.mf @@ -0,0 +1,29 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent Inc. +# + +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/usb/usbgem@$(PKGVERS) +set name=pkg.description value="USB General Ethernet Module" +set name=pkg.summary value="USB General Ethernet Module" +set name=info.classification value=org.opensolaris.category.2008:Drivers/Ports +set name=variant.arch value=i386 +dir path=kernel group=sys +dir path=kernel/misc group=sys +dir path=kernel/misc/$(ARCH64) group=sys +file path=kernel/misc/$(ARCH64)/usbgem group=sys mode=0755 +$(i386_ONLY)file path=kernel/misc/usbgem group=sys mode=0755 +license usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE \ + license=usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE +depend fmri=driver/usb type=require diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index fc68987bbe..d7f7aa28e7 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -2157,6 +2157,15 @@ NULLDRIVER_OBJS = nulldriver.o TPM_OBJS = tpm.o tpm_hcall.o # +# USB Fast ethernet drivers +# +USBGEM_OBJS = usbgem.o +AXF_OBJS = axf_usbgem.o +UDMF_OBJS = udmf_usbgem.o +URF_OBJS = urf_usbgem.o +UPF_OBJS = upf_usbgem.o + +# # BNXE objects # BNXE_OBJS += bnxe_cfg.o \ diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index d4fbb8c276..ecf3e27e0c 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -1279,6 +1279,30 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/usba10/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usbgem/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/axf/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/udf/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/udmf/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/upf/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/urf/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vuidmice/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -2541,6 +2565,21 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/usba/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/usba10/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usbgem/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/axf/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/udmf/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/upf/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/urf/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/vuidmice/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/io/axf/THIRDPARTYLICENSE b/usr/src/uts/common/io/axf/THIRDPARTYLICENSE new file mode 100644 index 0000000000..dccd9148cf --- /dev/null +++ b/usr/src/uts/common/io/axf/THIRDPARTYLICENSE @@ -0,0 +1,32 @@ +/* + * axf_usbgem.c : ASIX AX88172/772 USB to Fast Ethernet Driver for Solaris + * + * Copyright (c) 2004-2012 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 + * and/or other materials provided with the distribution. + * + * 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. + */ diff --git a/usr/src/uts/common/io/axf/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/axf/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..0107a73b3c --- /dev/null +++ b/usr/src/uts/common/io/axf/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +AXF DRIVER diff --git a/usr/src/uts/common/io/axf/ax88172reg.h b/usr/src/uts/common/io/axf/ax88172reg.h new file mode 100644 index 0000000000..ae297df7fb --- /dev/null +++ b/usr/src/uts/common/io/axf/ax88172reg.h @@ -0,0 +1,163 @@ +/* + * @(#)ax88172reg.h 1.1 09/06/15 + * Macro definitions for ASIX AX88172 USB to fast ethernet controler + * based on ASIX AX88172/88772 data sheet + * This file is public domain. Coded by M.Murayama (KHF04453@nifty.com) + */ + +#ifndef __AX88172_H__ +#define __AX88172_H__ + +/* + * Vendor command definitions + */ +#define VCMD_READ_SRAM 0x02 +#define VCMD_WRITE_RXSRAM 0x03 +#define VCMD_WRITE_TXSRAM 0x04 +#define VCMD_SOFTWARE_MII_OP 0x06 +#define VCMD_READ_MII_REG 0x07 +#define VCMD_WRITE_MII_REG 0x08 +#define VCMD_READ_MII_OPMODE 0x09 +#define VCMD_HARDWARE_MII_OP 0x0a +#define VCMD_READ_SROM 0x0b +#define VCMD_WRITE_SROM 0x0c +#define VCMD_WRITE_SROM_ENABLE 0x0d +#define VCMD_WRITE_SROM_DISABLE 0x0e +#define VCMD_READ_RXCTRL 0x0f +#define VCMD_WRITE_RXCTRL 0x10 +#define VCMD_READ_IPGS 0x11 +#define VCMD_WRITE_IPG 0x12 +#define VCMD_WRITE_IPG1 0x13 +#define VCMD_WRITE_IPG2 0x14 +#define VCMD_READ_MCAST_FILTER 0x15 +#define VCMD_WRITE_MCAST_FILTER 0x16 +#define VCMD_READ_NODE_ID 0x17 +#define VCMD_READ_PHY_IDS 0x19 +#define VCMD_READ_MEDIUM_STATUS 0x1a +#define VCMD_WRITE_MEDIUM_STATUS 0x1b +#define VCMD_SET_MONITOR_MODE 0x1c +#define VCMD_GET_MONITOR_MODE 0x1d +#define VCMD_READ_GPIO 0x1e +#define VCMD_WRITE_GPIO 0x1f + +/* ax88772 only, currently not supported */ +#define VCMD_WRITE_IPGS_88772 0x12 +#define VCMD_READ_NODE_ID_88772 0x13 +#define VCMD_WRITE_NODE_ID_88772 0x14 +#define VCMD_WRITE_TEST_REG_88772 0x17 +#define VCMD_SOFTWARE_RESET_88772 0x20 +#define VCMD_READ_PHY_SELECT_88772 0x21 +#define VCMD_WRITE_PHY_SELECT_88772 0x22 + + +/* + * Register definitions + */ + +/* Rx control register */ +#define RCR_SO 0x80 /* Start Operation */ +#define RCR_AP_88772 0x20 /* accept physical address from mcast filter */ +#define RCR_AM 0x10 /* accept multicast address */ +#define RCR_AB 0x08 /* accept broadcast address */ +#define RCR_SEP 0x04 /* save error packet */ +#define RCR_AMALL 0x02 /* accept all multicast address */ +#define RCR_PRO 0x01 /* promiscious, all frames received */ + +#define RCR_MFB 0x0300 +#define RCR_MFB_SHIFT 8 +#define RCR_MFB_2K (0U << RCR_MFB_SHIFT) +#define RCR_MFB_4K (1U << RCR_MFB_SHIFT) +#define RCR_MFB_8K (2U << RCR_MFB_SHIFT) +#define RCR_MFB_16K (3U << RCR_MFB_SHIFT) + +#define RCR_BITS \ + "\020" \ + "\010SO" \ + "\006AP" \ + "\005AM" \ + "\004AB" \ + "\003SEP" \ + "\002AMALL" \ + "\001PRO" + +/* Medium status register */ +#define MSR_SM 0x1000 /* super mac support */ +#define MSR_SBP 0x0800 /* stop backpressure */ +#define MSR_PS 0x0200 /* port speed in mii mode */ +#define MSR_RE 0x0100 /* rx enable */ +#define MSR_PF 0x0080 /* check only length/type for pause frame */ +#define MSR_JFE 0x0040 /* jumbo frame enable */ +#define MSR_TFC 0x0020 /* tx flow control enable */ +#define MSR_RFC 0x0010 /* rx flow control enable (178) */ +#define MSR_FCEN 0x0010 /* flow control enable (172/772) */ +#define MSR_ENCK 0x0008 /* Enable GTX_CLK and TXC clock output (178) */ +#define MSR_TXABT 0x0004 /* Tx abort allow, always set */ +#define MSR_FDPX 0x0002 /* full duplex */ +#define MSR_GM 0x0001 /* Gigabit mode (178) */ + +#define MSR_BITS \ + "\020" \ + "\015SM" \ + "\014SBP" \ + "\012PS" \ + "\011RE" \ + "\005FCEN" \ + "\004ENCK" \ + "\003TXABT" \ + "\002FDPX" \ + "\001GM" + +/* monitor mode register */ +#define MMR_RWMP 0x04 /* remote wakeup by magic pkt */ +#define MMR_RWLU 0x02 /* remote wakeup by linkup */ +#define MMR_MOM 0x01 /* monitor mode 1:en, 0:dis */ + +#define MMR_BITS \ + "\020" \ + "\003RWMP" \ + "\002RWLU" \ + "\001MOM" + +/* GPIO register */ +#define GPIO_RSE 0x80 /* reload serial eeprom (88772) */ +#define GPIO_DATA2 0x20 +#define GPIO_EN2 0x10 +#define GPIO_DATA1 0x08 +#define GPIO_EN1 0x04 +#define GPIO_DATA0 0x02 +#define GPIO_EN0 0x01 + +#define GPIO_BITS \ + "\020" \ + "\010RSE" \ + "\006DATA2" \ + "\005EN2" \ + "\004DATA1" \ + "\003EN1" \ + "\002DATA0" \ + "\001EN0" + +/* Software reset register */ +#define SWRST_IPPD 0x40 /* internal phy power down control */ +#define SWRST_IPRL 0x20 /* internal phy reset control */ +#define SWRST_BZ 0x10 /* force Bulk In to return zero-length pkt */ +#define SWRST_PRL 0x08 /* external phy reset pin level */ +#define SWRST_PRTE 0x04 /* external phy tri-state enable */ +#define SWRST_RT 0x02 /* clear frame length error for Bulk-Out */ +#define SWRST_RR 0x01 /* clear frame length error for Bulk-In */ + +#define SWRST_BITS \ + "\020" \ + "\007IPPD" \ + "\006IPRL" \ + "\005BZ" \ + "\004PRL" \ + "\003PRTE" \ + "\002RT" \ + "\001RR" + +/* Software PHY Select Status register */ +#define SPSS_ASEL 0x02 /* 1:auto select 0:manual select */ +#define SPSS_PSEL 0x01 /* 1:intenal phy, 0:external (when ASEL=0) */ + +#endif /* __AX88172_H__ */ diff --git a/usr/src/uts/common/io/axf/axf_usbgem.c b/usr/src/uts/common/io/axf/axf_usbgem.c new file mode 100644 index 0000000000..28b07fa4d5 --- /dev/null +++ b/usr/src/uts/common/io/axf/axf_usbgem.c @@ -0,0 +1,1541 @@ +/* + * axf_usbgem.c : ASIX AX88172/772 USB to Fast Ethernet Driver for Solaris + * + * Copyright (c) 2004-2012 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 + * and/or other materials provided with the distribution. + * + * 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. + */ + +#pragma ident "@(#)axf_usbgem.c 1.3 12/02/09" + +/* + * Changelog: + */ + +/* + * TODO + * handle RXMODE_ENABLE in set_rx_filter() + */ +/* ======================================================= */ + +/* + * Solaris system header files and macros + */ + +/* minimum kernel headers for drivers */ +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/debug.h> +#include <sys/kmem.h> +#include <sys/modctl.h> +#include <sys/errno.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/byteorder.h> + +/* ethernet stuff */ +#include <sys/ethernet.h> + +/* interface card depend stuff */ +#include <sys/stropts.h> +#include <sys/stream.h> +#include <sys/strlog.h> +#include <sys/usb/usba.h> +#include "usbgem.h" + +/* hardware stuff */ +#include "usbgem_mii.h" +#include "ax88172reg.h" + +char ident[] = "ax88x72 usbnic driver v" VERSION; + +/* + * Useful macros + */ +#define CHECK_AND_JUMP(err, label) if (err != USB_SUCCESS) goto label +#define LE16P(p) ((((uint8_t *)(p))[1] << 8) | ((uint8_t *)(p))[0]) + +#define AX88172(dp) \ + (((struct axf_dev *)(dp)->private)->chip->type == CHIP_TYPE_AX88172) + +#define AX88772(dp) \ + (((struct axf_dev *)(dp)->private)->chip->type == CHIP_TYPE_AX88772) + +/* + * Debugging + */ +#ifdef DEBUG_LEVEL +static int axf_debug = DEBUG_LEVEL; +#define DPRINTF(n, args) if (axf_debug > (n)) cmn_err args +#else +#define DPRINTF(n, args) +#endif + +/* + * Our configration for ax88172 + */ +/* timeouts */ +#define ONESEC (drv_usectohz(1*1000000)) + +/* + * RX/TX buffer size + */ + +/* + * Local device definitions + */ +struct chip_info { + uint16_t vid; /* usb vendor id */ + uint16_t pid; /* usb product id */ + int type; + uint8_t gpio_reset[2]; + uint8_t gpio_speed[2]; + uint8_t gpio_duplex[2]; + char *name; +#define CHIP_TYPE_AX88172 0 +#define CHIP_TYPE_AX88772 1 +#define CHIP_TYPE_AX88178 2 +}; + +#define GPIO_DEFAULT {0x00, 0x15}, {0, 0}, {0, 0} +struct chip_info chiptbl_88x7x[] = { +/* AX88172 */ +{ + /* Planex UE2-100TX, Hawking UF200, TrendNet TU2-ET100 */ + 0x07b8, 0x420a, CHIP_TYPE_AX88172, + + /* + * the default setting covers below: + * gpio bit2 has to be 0 and gpio bit0 has to be 1 + */ + {0, 0}, + {GPIO_EN1, GPIO_DATA1 | GPIO_EN1}, + {0, 0}, + "Planex UE2-100TX", /* tested */ +}, +{ + 0x2001, 0x1a00, CHIP_TYPE_AX88172, + {0x9f, 0x9e}, {0, 0}, {0, 0}, + "D-Link dube100", /* XXX */ +}, +{ + 0x077b, 0x2226, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "Linksys USB200M", +}, +{ + 0x0846, 0x1040, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "Netgear FA120", +}, +{ + 0x0b95, 0x1720, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "Intellinet, ST Lab USB Ethernet", +}, +{ + 0x08dd, 0x90ff, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "Billionton Systems, USB2AR", +}, +{ + 0x0557, 0x2009, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "ATEN UC210T", +}, +{ + 0x0411, 0x003d, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "Buffalo LUA-U2-KTX", +}, +{ + 0x6189, 0x182d, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "Sitecom LN-029 USB 2.0 10/100 Ethernet adapter", +}, +{ + 0x07aa, 0x0017, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "corega FEther USB2-TX", +}, +{ + 0x1189, 0x0893, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "Surecom EP-1427X-2", +}, +{ + 0x1631, 0x6200, CHIP_TYPE_AX88172, + GPIO_DEFAULT, + "goodway corp usb gwusb2e", +}, +/* AX88772 and AX88178 */ +{ + 0x13b1, 0x0018, CHIP_TYPE_AX88772, + {0, 0}, {0, 0}, {0, 0}, + "Linksys USB200M rev.2", +}, +{ + 0x1557, 0x7720, CHIP_TYPE_AX88772, + {0, 0}, {0, 0}, {0, 0}, + "0Q0 cable ethernet", +}, +{ + 0x07d1, 0x3c05, CHIP_TYPE_AX88772, + {0, 0}, {0, 0}, {0, 0}, + "DLink DUB E100 ver B1", +}, +{ + 0x2001, 0x3c05, CHIP_TYPE_AX88772, + {0, 0}, {0, 0}, {0, 0}, + "DLink DUB E100 ver B1(2)", +}, +{ + 0x05ac, 0x1402, CHIP_TYPE_AX88772, + {0, 0}, {0, 0}, {0, 0}, + "Apple Ethernet USB Adapter", +}, +{ + 0x1737, 0x0039, CHIP_TYPE_AX88178, + {0, 0}, {0, 0}, {0, 0}, + "Linksys USB1000", +}, +{ + 0x0411, 0x006e, CHIP_TYPE_AX88178, + {0, 0}, {0, 0}, {0, 0}, + "Buffalo LUA-U2-KGT/LUA-U2-GT", +}, +{ + 0x04bb, 0x0930, CHIP_TYPE_AX88178, + {0, 0}, {0, 0}, {0, 0}, + "I/O DATA ETG-US2", +}, +{ + 0x050d, 0x5055, CHIP_TYPE_AX88178, + {0, 0}, {0, 0}, {0, 0}, + "Belkin F5D5055", +}, +{ + /* generic ax88772 must be the last entry */ + /* planex UE-200TX-G */ + 0x0b95, 0x7720, CHIP_TYPE_AX88772, + {0, 0}, {0, 0}, {0, 0}, + "ASIX AX88772/AX88178", /* tested */ +}, +}; + +#define CHIPTABLESIZE (sizeof (chiptbl_88x7x) / sizeof (struct chip_info)) + +struct axf_dev { + /* + * Misc HW information + */ + struct chip_info *chip; + uint8_t ipg[3]; + uint8_t gpio; + uint16_t rcr; + uint16_t msr; + uint8_t last_link_state; + boolean_t phy_has_reset; +}; + +/* + * private functions + */ + +/* mii operations */ +static uint16_t axf_mii_read(struct usbgem_dev *, uint_t, int *errp); +static void axf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp); + +/* nic operations */ +static int axf_reset_chip(struct usbgem_dev *); +static int axf_init_chip(struct usbgem_dev *); +static int axf_start_chip(struct usbgem_dev *); +static int axf_stop_chip(struct usbgem_dev *); +static int axf_set_media(struct usbgem_dev *); +static int axf_set_rx_filter(struct usbgem_dev *); +static int axf_get_stats(struct usbgem_dev *); +static void axf_interrupt(struct usbgem_dev *, mblk_t *); + +/* packet operations */ +static mblk_t *axf_tx_make_packet(struct usbgem_dev *, mblk_t *); +static mblk_t *axf_rx_make_packet(struct usbgem_dev *, mblk_t *); + +/* =============================================================== */ +/* + * I/O functions + */ +/* =============================================================== */ +/* BEGIN CSTYLED */ +#define OUT(dp, req, val, ix, len, buf, errp, label) \ + if ((*(errp) = usbgem_ctrl_out((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ (req), \ + /* wValue */ (val), \ + /* wIndex */ (ix), \ + /* wLength */ (len), \ + /* value */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label + +#define IN(dp, req, val, ix, len, buf, errp, label) \ + if ((*(errp) = usbgem_ctrl_in((dp), \ + /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ (req), \ + /* wValue */ (val), \ + /* wIndex */ (ix), \ + /* wLength */ (len), \ + /* valuep */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label +/* END CSTYLED */ + +/* =============================================================== */ +/* + * Hardware manupilation + */ +/* =============================================================== */ +static int +axf_reset_phy(struct usbgem_dev *dp) +{ + uint8_t phys[2]; + uint8_t val8; + int err; + struct axf_dev *lp = dp->private; + + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + if (AX88172(dp)) { + delay(drv_usectohz(5000)); + IN(dp, VCMD_READ_GPIO, 0, 0, 1, &val8, &err, usberr); + + DPRINTF(0, (CE_CONT, "!%s: %s: gpio 0x%b", + dp->name, __func__, val8, GPIO_BITS)); + + /* reset MII PHY */ + val8 = lp->chip->gpio_reset[1] + | lp->chip->gpio_speed[dp->speed] + | lp->chip->gpio_duplex[dp->full_duplex]; + + OUT(dp, VCMD_WRITE_GPIO, + val8, 0, 0, NULL, &err, usberr); + delay(drv_usectohz(5000)); + + val8 = lp->chip->gpio_reset[0] + | lp->chip->gpio_speed[dp->speed] + | lp->chip->gpio_duplex[dp->full_duplex]; + + OUT(dp, VCMD_WRITE_GPIO, + val8, 0, 0, NULL, &err, usberr); + delay(drv_usectohz(5000)); + } else { + lp->gpio = GPIO_RSE | GPIO_DATA2 | GPIO_EN2; + OUT(dp, VCMD_WRITE_GPIO, lp->gpio, 0, + 0, NULL, &err, usberr); + drv_usecwait(1000); + + OUT(dp, VCMD_WRITE_PHY_SELECT_88772, + dp->mii_phy_addr == 16 ? 1 : 0, 0, 0, NULL, &err, usberr); + + OUT(dp, VCMD_SOFTWARE_RESET_88772, + SWRST_IPPD | SWRST_PRL, 0, 0, NULL, &err, usberr); + delay(drv_usectohz(150*1000)); + OUT(dp, VCMD_SOFTWARE_RESET_88772, + 0, 0, 0, NULL, &err, usberr); + + OUT(dp, VCMD_SOFTWARE_RESET_88772, + dp->mii_phy_addr == 16 ? SWRST_IPRL : SWRST_PRTE, + 0, 0, NULL, &err, usberr); + delay(drv_usectohz(150*1000)); + } + + + return (USB_SUCCESS); + +usberr: + return (USB_FAILURE); +} + +static int +axf_reset_chip(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + + if (AX88172(dp)) { + /* there are no ways to reset nic */ + return (USB_SUCCESS); + } +#ifdef NEVER + OUT(dp, VCMD_SOFTWARE_RESET_88772, + SWRST_RR | SWRST_RT, 0, 0, NULL, &err, usberr); + OUT(dp, VCMD_SOFTWARE_RESET_88772, + 0, 0, 0, NULL, &err, usberr); +usberr: +#endif + return (err); +} + +/* + * Setup ax88172 + */ +static int +axf_init_chip(struct usbgem_dev *dp) +{ + int i; + uint32_t val; + int err = USB_SUCCESS; + uint16_t reg; + uint8_t buf[2]; + uint16_t tmp16; + struct axf_dev *lp = dp->private; + + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* rx conrol register: read default value */ + if (!AX88172(dp)) { + /* clear rx control */ + OUT(dp, VCMD_WRITE_RXCTRL, 0, 0, 0, NULL, &err, usberr); + } + + IN(dp, VCMD_READ_RXCTRL, 0, 0, 2, buf, &err, usberr); + lp->rcr = LE16P(buf); + DPRINTF(0, (CE_CONT, "!%s: %s: rcr(default):%b", + dp->name, __func__, lp->rcr, RCR_BITS)); + + lp->rcr &= ~RCR_SO; + + /* Media status register */ + if (AX88172(dp)) { +#ifdef notdef + lp->msr = MSR_TXABT; +#else + lp->msr = 0; +#endif + } else { + lp->msr = MSR_RE | MSR_TXABT; + } + DPRINTF(0, (CE_CONT, "!%s: %s: msr:%b", + dp->name, __func__, lp->msr, MSR_BITS)); + err = axf_set_media(dp); + CHECK_AND_JUMP(err, usberr); + + /* write IPG0-2 registers */ + if (AX88172(dp)) { + OUT(dp, VCMD_WRITE_IPG, lp->ipg[0], 0, 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_IPG1, lp->ipg[1], 0, 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_IPG2, lp->ipg[2], 0, 0, NULL, &err, usberr); + } else { + /* EMPTY */ + } +#ifdef ENABLE_RX_IN_INIT_CHIP + /* enable Rx */ + lp->rcr |= RCR_SO; + OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0, 0, NULL, &err, usberr); +#endif +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +static int +axf_start_chip(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + struct axf_dev *lp = dp->private; +#ifndef ENABLE_RX_IN_INIT_CHIP + /* enable Rx */ + lp->rcr |= RCR_SO; + OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0, 0, NULL, &err, usberr); + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); +#endif + return (err); +} + +static int +axf_stop_chip(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + struct axf_dev *lp = dp->private; + + /* Disable Rx */ + lp->rcr &= ~RCR_SO; + OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0, 0, NULL, &err, usberr); + + /* + * Restore factory mac address + * if we have changed current mac address + */ + if (!AX88172(dp) && + bcmp(dp->dev_addr.ether_addr_octet, + dp->cur_addr.ether_addr_octet, + ETHERADDRL) != 0) { + OUT(dp, VCMD_WRITE_NODE_ID_88772, 0, 0, + ETHERADDRL, dp->cur_addr.ether_addr_octet, &err, usberr); + } +usberr: + return (axf_reset_chip(dp)); +} + +static int +axf_get_stats(struct usbgem_dev *dp) +{ + /* empty */ + return (USB_SUCCESS); +} + +static uint_t +axf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr) +{ + return (usbgem_ether_crc_be(addr) >> (32 - 6)); +} + +static int +axf_set_rx_filter(struct usbgem_dev *dp) +{ + int i; + uint8_t mode; + uint8_t mhash[8]; + uint8_t buf[2]; + uint_t h; + int err = USB_SUCCESS; + struct axf_dev *lp = dp->private; + + DPRINTF(2, (CE_CONT, "!%s: %s: called, rxmode:%x", + dp->name, __func__, dp->rxmode)); + + if (lp->rcr & RCR_SO) { + /* set promiscuous mode before changing it. */ + OUT(dp, VCMD_WRITE_RXCTRL, + lp->rcr | RCR_PRO, 0, 0, NULL, &err, usberr); + } + + lp->rcr &= ~(RCR_AP_88772 | RCR_AM | RCR_SEP | RCR_AMALL | RCR_PRO); + mode = RCR_AB; /* accept broadcast packets */ + + bzero(mhash, sizeof (mhash)); + + if (dp->rxmode & RXMODE_PROMISC) { + /* promiscious mode implies all multicast and all physical */ + mode |= RCR_PRO; + } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 32) { + /* accept all multicast packets */ + mode |= RCR_AMALL; + } else if (dp->mc_count > 0) { + /* + * make hash table to select interresting + * multicast address only. + */ + mode |= RCR_AM; + for (i = 0; i < dp->mc_count; i++) { + h = dp->mc_list[i].hash; + mhash[h / 8] |= 1 << (h % 8); + } + } + if (AX88172(dp)) { + if (bcmp(dp->dev_addr.ether_addr_octet, + dp->cur_addr.ether_addr_octet, ETHERADDRL) != 0) { + /* + * we use promiscious mode instead of changing the + * mac address in ax88172 + */ + mode |= RCR_PRO; + } + } else { + OUT(dp, VCMD_WRITE_NODE_ID_88772, 0, 0, + ETHERADDRL, dp->cur_addr.ether_addr_octet, &err, usberr); + } + lp->rcr |= mode; + + /* set multicast hash table */ + if (mode & RCR_AM) { + /* need to set up multicast hash table */ + OUT(dp, VCMD_WRITE_MCAST_FILTER, 0, 0, + sizeof (mhash), mhash, &err, usberr); + } + + /* update rcr */ + OUT(dp, VCMD_WRITE_RXCTRL, lp->rcr, 0, + 0, NULL, &err, usberr); + +#if DEBUG_LEVEL > 1 + /* verify rxctrl reg */ + IN(dp, VCMD_READ_RXCTRL, 0, 0, 2, buf, &err, usberr); + cmn_err(CE_CONT, "!%s: %s: rcr:%b returned", + dp->name, __func__, LE16P(buf), RCR_BITS); +#endif +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +static int +axf_set_media(struct usbgem_dev *dp) +{ + uint8_t val8; + uint8_t gpio; + uint8_t gpio_old; + int err = USB_SUCCESS; + uint16_t msr; + struct axf_dev *lp = dp->private; + + IN(dp, VCMD_READ_GPIO, 0, 0, 1, &gpio, &err, usberr); + + DPRINTF(0, (CE_CONT, "!%s: %s: called, gpio:%b", + dp->name, __func__, gpio, GPIO_BITS)); + + msr = lp->msr; + gpio_old = gpio; + gpio = lp->chip->gpio_reset[0]; + + /* setup speed */ + if (AX88172(dp)) { + /* EMPTY */ + } else { + msr &= ~(MSR_PS | MSR_GM | MSR_ENCK); + + switch (dp->speed) { + case USBGEM_SPD_1000: + msr |= MSR_GM | MSR_ENCK; + break; + + case USBGEM_SPD_100: + msr |= MSR_PS; + break; + + case USBGEM_SPD_10: + break; + } + } + gpio |= lp->chip->gpio_speed[dp->speed == USBGEM_SPD_100 ? 1 : 0]; + + /* select duplex */ + msr &= ~MSR_FDPX; + if (dp->full_duplex) { + msr |= MSR_FDPX; + + /* select flow control */ + if (AX88172(dp)) { + msr &= ~MSR_FCEN; + switch (dp->flow_control) { + case FLOW_CONTROL_TX_PAUSE: + case FLOW_CONTROL_SYMMETRIC: + case FLOW_CONTROL_RX_PAUSE: + msr |= MSR_FCEN; + break; + } + } else { + msr &= ~(MSR_RFC | MSR_TFC); + switch (dp->flow_control) { + case FLOW_CONTROL_TX_PAUSE: + msr |= MSR_TFC; + break; + + case FLOW_CONTROL_SYMMETRIC: + msr |= MSR_TFC | MSR_RFC; + break; + + case FLOW_CONTROL_RX_PAUSE: + msr |= MSR_RFC; + break; + } + } + } + gpio |= lp->chip->gpio_duplex[dp->full_duplex ? 1 : 0]; + + /* update medium status register */ + lp->msr = msr; + OUT(dp, VCMD_WRITE_MEDIUM_STATUS, lp->msr, 0, + 0, NULL, &err, usberr); + + if (gpio != gpio_old) { + /* LED control required for some products */ + OUT(dp, VCMD_WRITE_GPIO, + gpio, 0, 0, NULL, &err, usberr); + } + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end (%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +#define FILL_PKT_HEADER(bp, len) { \ + (bp)[0] = (uint8_t)(len); \ + (bp)[1] = (uint8_t)((len) >> 8); \ + (bp)[2] = (uint8_t)(~(len)); \ + (bp)[3] = (uint8_t)((~(len)) >> 8); \ +} + +#define PKT_HEADER_SIZE 4 + +/* + * send/receive packet check + */ +static mblk_t * +axf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + int n; + size_t len; + size_t pkt_size; + mblk_t *new; + mblk_t *tp; + uint8_t *bp; + uint8_t *last_pos; + uint_t align_mask; + size_t header_size; + int pad_size; + + len = msgdsize(mp); + + if (AX88172(dp)) { +#ifdef notdef + align_mask = 63; +#else + align_mask = 511; +#endif + header_size = 0; + + if (len >= ETHERMIN && mp->b_cont == NULL && + (len & align_mask) != 0) { + /* use the mp "as is" */ + return (mp); + } + } else { + align_mask = 511; + header_size = PKT_HEADER_SIZE; + } + + /* + * re-allocate the mp + */ + /* minimum ethernet packet size of ETHERMIN */ + pkt_size = max(len, ETHERMIN); + + if (((pkt_size + header_size) & align_mask) == 0) { + /* padding is required in usb communication */ + pad_size = PKT_HEADER_SIZE; + } else { + pad_size = 0; + } + + if ((new = allocb(header_size + pkt_size + pad_size, 0)) == NULL) { + return (NULL); + } + + bp = new->b_rptr; + if (header_size) { + uint16_t tmp; + + /* add a header */ + tmp = (uint16_t)pkt_size; + FILL_PKT_HEADER(bp, tmp); + bp += header_size; + } + + /* copy contents of the buffer */ + for (tp = mp; tp; tp = tp->b_cont) { + n = (uintptr_t)tp->b_wptr - (uintptr_t)tp->b_rptr; + bcopy(tp->b_rptr, bp, n); + bp += n; + } + + /* add pads for ethernet packets */ + last_pos = new->b_rptr + header_size + pkt_size; + while (bp < last_pos) { + *bp++ = 0; + } + + /* add a zero-length pad segment for usb communications */ + if (pad_size) { + /* add a dummy header for zero-length packet */ + FILL_PKT_HEADER(bp, 0); + bp += pad_size; + } + + /* close the payload of the packet */ + new->b_wptr = bp; + + return (new); +} + +static void +axf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n) +{ + int i; + + for (i = 0; i < n; i += 8, bp += 8) { + cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x", + bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]); + } +} + +static mblk_t * +axf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + mblk_t *tp; + uintptr_t rest; + + if (AX88172(dp)) { + return (mp); + } + + tp = mp; + rest = (uintptr_t)tp->b_wptr - (uintptr_t)tp->b_rptr; + + if (rest <= PKT_HEADER_SIZE) { + /* + * the usb bulk-in frame doesn't include any valid + * ethernet packets. + */ + return (NULL); + } + + for (; ; ) { + uint16_t len; + uint16_t cksum; + + /* analyse the header of the received usb frame */ + len = LE16P(tp->b_rptr + 0); + cksum = LE16P(tp->b_rptr + 2); + + /* test if the header is valid */ + if (len + cksum != 0xffff) { + /* discard whole the packet */ + cmn_err(CE_WARN, + "!%s: %s: corrupted header:%04x %04x", + dp->name, __func__, len, cksum); + return (NULL); + } +#if DEBUG_LEVEL > 0 + if (len < ETHERMIN || len > ETHERMAX) { + cmn_err(CE_NOTE, + "!%s: %s: incorrect pktsize:%d", + dp->name, __func__, len); + } +#endif + /* extract a ethernet packet from the bulk-in frame */ + tp->b_rptr += PKT_HEADER_SIZE; + tp->b_wptr = tp->b_rptr + len; + + if (len & 1) { + /* + * skip a tailing pad byte if the packet + * length is odd + */ + len++; + } + rest -= len + PKT_HEADER_SIZE; + + if (rest <= PKT_HEADER_SIZE) { + /* no more vaild ethernet packets */ + break; + } + +#if DEBUG_LEVEL > 10 + axf_dump_packet(dp, tp->b_wptr, 18); +#endif + /* allocate a mblk_t header for the next ethernet packet */ + tp->b_next = dupb(mp); + tp->b_next->b_rptr = tp->b_rptr + len; + tp = tp->b_next; + } + + return (mp); +} + +/* + * MII Interfaces + */ +static uint16_t +axf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp) +{ + uint8_t buf[2]; + uint16_t val; + + DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d", + dp->name, __func__, index)); + + /* switch to software MII operation mode */ + OUT(dp, VCMD_SOFTWARE_MII_OP, 0, 0, 0, NULL, errp, usberr); + + /* Read MII register */ + IN(dp, VCMD_READ_MII_REG, dp->mii_phy_addr, index, + 2, buf, errp, usberr); + + /* switch to hardware MII operation mode */ + OUT(dp, VCMD_HARDWARE_MII_OP, 0, 0, 0, NULL, errp, usberr); + + return (LE16P(buf)); + +usberr: + cmn_err(CE_CONT, + "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp); + return (0); +} + +static void +axf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp) +{ + uint8_t buf[2]; + + DPRINTF(4, (CE_CONT, "!%s: %s called, reg:%x val:%x", + dp->name, __func__, index, val)); + + /* switch software MII operation mode */ + OUT(dp, VCMD_SOFTWARE_MII_OP, 0, 0, 0, NULL, errp, usberr); + + /* Write to the specified MII register */ + buf[0] = (uint8_t)val; + buf[1] = (uint8_t)(val >> 8); + OUT(dp, VCMD_WRITE_MII_REG, dp->mii_phy_addr, index, + 2, buf, errp, usberr); + + /* switch to hardware MII operation mode */ + OUT(dp, VCMD_HARDWARE_MII_OP, 0, 0, 0, NULL, errp, usberr); + +usberr: + ; +} + +static void +axf_interrupt(struct usbgem_dev *dp, mblk_t *mp) +{ + uint8_t *bp; + struct axf_dev *lp = dp->private; + + bp = mp->b_rptr; + + DPRINTF(2, (CE_CONT, + "!%s: %s: size:%d, %02x %02x %02x %02x %02x %02x %02x %02x", + dp->name, __func__, mp->b_wptr - mp->b_rptr, + bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7])); + + if (lp->last_link_state ^ bp[2]) { + usbgem_mii_update_link(dp); + } + + lp->last_link_state = bp[2]; +} + +/* ======================================================== */ +/* + * OS depend (device driver DKI) routine + */ +/* ======================================================== */ +#ifdef DEBUG_LEVEL +static void +axf_eeprom_dump(struct usbgem_dev *dp, int size) +{ + int i; + int err; + uint8_t w0[2], w1[2], w2[2], w3[2]; + + cmn_err(CE_CONT, "!%s: eeprom dump:", dp->name); + + err = USB_SUCCESS; + + for (i = 0; i < size; i += 4) { + IN(dp, VCMD_READ_SROM, i + 0, 0, 2, w0, &err, usberr); + IN(dp, VCMD_READ_SROM, i + 1, 0, 2, w1, &err, usberr); + IN(dp, VCMD_READ_SROM, i + 2, 0, 2, w2, &err, usberr); + IN(dp, VCMD_READ_SROM, i + 3, 0, 2, w3, &err, usberr); + cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x", + i, + (w0[1] << 8) | w0[0], + (w1[1] << 8) | w1[0], + (w2[1] << 8) | w2[0], + (w3[1] << 8) | w3[0]); + } +usberr: + ; +} +#endif + +static int +axf_attach_chip(struct usbgem_dev *dp) +{ + uint8_t phys[2]; + int err; + uint_t vcmd; + int ret; +#ifdef CONFIG_FULLSIZE_VLAN + uint8_t maxpktsize[2]; + uint16_t vlan_pktsize; +#endif +#ifdef DEBUG_LEVEL + uint8_t val8; +#endif + struct axf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s enter", dp->name, __func__)); + + ret = USB_SUCCESS; + /* + * mac address in EEPROM has loaded to ID registers. + */ + vcmd = AX88172(dp) ? VCMD_READ_NODE_ID : VCMD_READ_NODE_ID_88772; + IN(dp, vcmd, 0, 0, + ETHERADDRL, dp->dev_addr.ether_addr_octet, &err, usberr); + + /* + * setup IPG values + */ + lp->ipg[0] = 0x15; + lp->ipg[1] = 0x0c; + lp->ipg[2] = 0x12; + + /* + * We cannot scan phy because the nic returns undefined + * value, i.e. remained garbage, when MII phy is not at the + * specified index. + */ +#ifdef DEBUG_LEVELx + if (lp->chip->vid == 0x07b8 && lp->chip->pid == 0x420a) { + /* + * restore the original phy address of brain + * damaged Planex UE2-100TX + */ + OUT(dp, VCMD_WRITE_SROM_ENABLE, 0, 0, 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_SROM, 0x11, 0xe004, 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_SROM_DISABLE, 0, 0, 0, NULL, &err, usberr); + } +#endif + if (AX88172(dp)) { + IN(dp, VCMD_READ_PHY_IDS, 0, 0, 2, &phys, &err, usberr); + dp->mii_phy_addr = phys[1]; + DPRINTF(0, (CE_CONT, "!%s: %s: phys_addr:%d %d", + dp->name, __func__, phys[0], phys[1])); + } else { + /* use built-in phy */ + dp->mii_phy_addr = 0x10; + } + + dp->misc_flag |= USBGEM_VLAN; +#ifdef CONFIG_FULLSIZE_VLAN + if (AX88172(dp) || AX88772(dp)) { + /* check max packet size in srom */ + IN(dp, VCMD_READ_SROM, 0x10, 0, 2, maxpktsize, &err, usberr); + vlan_pktsize = ETHERMAX + ETHERFCSL + 4 /* VTAG_SIZE */; + + if (LE16P(maxpktsize) < vlan_pktsize) { + cmn_err(CE_CONT, + "!%s: %s: max packet size in srom is too small, " + "changing %d -> %d, do power cycle for the device", + dp->name, __func__, + LE16P(maxpktsize), vlan_pktsize); + OUT(dp, VCMD_WRITE_SROM_ENABLE, + 0, 0, 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_SROM, 0x10, + vlan_pktsize, 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_SROM_DISABLE, + 0, 0, 0, NULL, &err, usberr); + + /* need to power off the device */ + ret = USB_FAILURE; + } + } +#endif +#ifdef DEBUG_LEVEL + IN(dp, VCMD_READ_GPIO, 0, 0, 1, &val8, &err, usberr); + cmn_err(CE_CONT, + "!%s: %s: ipg 0x%02x 0x%02x 0x%02x, gpio 0x%b", + dp->name, __func__, lp->ipg[0], lp->ipg[1], lp->ipg[2], + val8, GPIO_BITS); +#endif + /* fix rx buffer size */ + if (!AX88172(dp)) { + dp->rx_buf_len = 2048; + } + +#if DEBUG_LEVEL > 0 + axf_eeprom_dump(dp, 0x20); +#endif + return (ret); + +usberr: + cmn_err(CE_WARN, "%s: %s: usb error detected (%d)", + dp->name, __func__, err); + return (USB_FAILURE); +} + +static boolean_t +axf_scan_phy(struct usbgem_dev *dp) +{ + int i; + int err; + uint16_t val; + int phy_addr_saved; + struct axf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + phy_addr_saved = dp->mii_phy_addr; + + /* special probe routine for unreliable MII addr */ +#define PROBE_PAT \ + (MII_ABILITY_100BASE_TX_FD | \ + MII_ABILITY_100BASE_TX | \ + MII_ABILITY_10BASE_T_FD | \ + MII_ABILITY_10BASE_T) + + for (i = 0; i < 32; i++) { + dp->mii_phy_addr = i; + axf_mii_write(dp, MII_AN_ADVERT, 0, &err); + if (err != USBGEM_SUCCESS) { + break; + } + val = axf_mii_read(dp, MII_AN_ADVERT, &err); + if (err != USBGEM_SUCCESS) { + break; + } + if (val != 0) { + DPRINTF(0, (CE_CONT, "!%s: %s: index:%d, val %b != 0", + dp->name, __func__, i, val, MII_ABILITY_BITS)); + continue; + } + + axf_mii_write(dp, MII_AN_ADVERT, PROBE_PAT, &err); + if (err != USBGEM_SUCCESS) { + break; + } + val = axf_mii_read(dp, MII_AN_ADVERT, &err); + if (err != USBGEM_SUCCESS) { + break; + } + if ((val & MII_ABILITY_TECH) != PROBE_PAT) { + DPRINTF(0, (CE_CONT, "!%s: %s: " + "index:%d, pat:%x != val:%b", + dp->name, __func__, i, + PROBE_PAT, val, MII_ABILITY_BITS)); + continue; + } + + /* found */ + dp->mii_phy_addr = phy_addr_saved; + return (i); + } +#undef PROBE_PAT + if (i == 32) { + cmn_err(CE_CONT, "!%s: %s: no mii phy found", + dp->name, __func__); + } else { + cmn_err(CE_CONT, "!%s: %s: i/o error while scanning phy", + dp->name, __func__); + } + dp->mii_phy_addr = phy_addr_saved; + return (-1); +} + +static int +axf_mii_probe(struct usbgem_dev *dp) +{ + int my_guess; + int err; + uint8_t old_11th[2]; + uint8_t new_11th[2]; + struct axf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + (void) axf_reset_phy(dp); + lp->phy_has_reset = B_TRUE; + + if (AX88172(dp)) { + my_guess = axf_scan_phy(dp); + if (my_guess >= 0 && my_guess < 32 && + my_guess != dp->mii_phy_addr) { + /* + * phy addr in srom is wrong, need to fix it + */ + IN(dp, VCMD_READ_SROM, + 0x11, 0, 2, old_11th, &err, usberr); + + new_11th[0] = my_guess; + new_11th[1] = old_11th[1]; + + OUT(dp, VCMD_WRITE_SROM_ENABLE, + 0, 0, 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_SROM, + 0x11, LE16P(new_11th), 0, NULL, &err, usberr); + OUT(dp, VCMD_WRITE_SROM_DISABLE, + 0, 0, 0, NULL, &err, usberr); +#if 1 + /* XXX - read back, but it doesn't work, why? */ + delay(drv_usectohz(1000*1000)); + IN(dp, VCMD_READ_SROM, + 0x11, 0, 2, new_11th, &err, usberr); +#endif + cmn_err(CE_NOTE, "!%s: %s: phy addr in srom fixed: " + "%04x -> %04x", + dp->name, __func__, + LE16P(old_11th), LE16P(new_11th)); + return (USBGEM_FAILURE); +usberr: + cmn_err(CE_NOTE, + "!%s: %s: failed to patch phy addr, " + "current: %04x", + dp->name, __func__, LE16P(old_11th)); + return (USBGEM_FAILURE); + } + } + return (usbgem_mii_probe_default(dp)); +} + +static int +axf_mii_init(struct usbgem_dev *dp) +{ + struct axf_dev *lp = dp->private; + + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + if (!lp->phy_has_reset) { + (void) axf_reset_phy(dp); + } + + /* prepare to reset phy on the next reconnect or resume */ + lp->phy_has_reset = B_FALSE; + + return (USB_SUCCESS); +} + +static int +axfattach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int i; + ddi_iblock_cookie_t c; + int ret; + int revid; + int unit; + int vid; + int pid; + struct chip_info *p; + int len; + const char *drv_name; + struct usbgem_dev *dp; + void *base; + struct usbgem_conf *ugcp; + struct axf_dev *lp; + + unit = ddi_get_instance(dip); + drv_name = ddi_driver_name(dip); + + DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d", + drv_name, unit, __func__, cmd)); + + if (cmd == DDI_ATTACH) { + /* + * Check if the chip is supported. + */ + vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "usb-vendor-id", -1); + pid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "usb-product-id", -1); + revid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "usb-revision-id", -1); + + for (i = 0, p = chiptbl_88x7x; i < CHIPTABLESIZE; i++, p++) { + if (p->vid == vid && p->pid == pid) { + /* found */ + cmn_err(CE_CONT, "!%s%d: %s " + "(vid: 0x%04x, did: 0x%04x, revid: 0x%02x)", + drv_name, unit, p->name, vid, pid, revid); + goto chip_found; + } + } + + /* Not found */ + cmn_err(CE_WARN, "!%s: %s: wrong usb venid/prodid (0x%x, 0x%x)", + drv_name, __func__, vid, pid); + + /* assume 88772 */ + p = &chiptbl_88x7x[CHIPTABLESIZE - 1]; +chip_found: + /* + * construct usbgem configration + */ + ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP); + + /* name */ + /* + * softmac requires that ppa is the instance number + * of the device, otherwise it hangs in seaching the device. + */ + (void) sprintf(ugcp->usbgc_name, "%s%d", drv_name, unit); + ugcp->usbgc_ppa = unit; + + ugcp->usbgc_ifnum = 0; + ugcp->usbgc_alt = 0; + + ugcp->usbgc_tx_list_max = 64; + + ugcp->usbgc_rx_header_len = 0; + ugcp->usbgc_rx_list_max = 64; + + /* time out parameters */ + ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT; + ugcp->usbgc_tx_timeout_interval = USBGEM_TX_TIMEOUT_INTERVAL; + + /* flow control */ + /* + * XXX - flow control caused link down frequently under + * heavy traffic + */ + ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE; + + /* MII timeout parameters */ + ugcp->usbgc_mii_link_watch_interval = ONESEC; + ugcp->usbgc_mii_an_watch_interval = ONESEC/5; + ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */ + ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */ + ugcp->usbgc_mii_an_wait = 0; + ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT; + + ugcp->usbgc_mii_an_delay = ONESEC/10; + ugcp->usbgc_mii_linkdown_action = MII_ACTION_RSA; + ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET; + ugcp->usbgc_mii_dont_reset = B_FALSE; + ugcp->usbgc_mii_hw_link_detection = B_TRUE; + ugcp->usbgc_mii_stop_mac_on_linkdown = B_FALSE; + + /* I/O methods */ + + /* mac operation */ + ugcp->usbgc_attach_chip = &axf_attach_chip; + ugcp->usbgc_reset_chip = &axf_reset_chip; + ugcp->usbgc_init_chip = &axf_init_chip; + ugcp->usbgc_start_chip = &axf_start_chip; + ugcp->usbgc_stop_chip = &axf_stop_chip; + ugcp->usbgc_multicast_hash = &axf_mcast_hash; + + ugcp->usbgc_set_rx_filter = &axf_set_rx_filter; + ugcp->usbgc_set_media = &axf_set_media; + ugcp->usbgc_get_stats = &axf_get_stats; + ugcp->usbgc_interrupt = &axf_interrupt; + + /* packet operation */ + ugcp->usbgc_tx_make_packet = &axf_tx_make_packet; + ugcp->usbgc_rx_make_packet = &axf_rx_make_packet; + + /* mii operations */ + ugcp->usbgc_mii_probe = &axf_mii_probe; + ugcp->usbgc_mii_init = &axf_mii_init; + ugcp->usbgc_mii_config = &usbgem_mii_config_default; + ugcp->usbgc_mii_read = &axf_mii_read; + ugcp->usbgc_mii_write = &axf_mii_write; + + /* mtu */ + ugcp->usbgc_min_mtu = ETHERMTU; + ugcp->usbgc_max_mtu = ETHERMTU; + ugcp->usbgc_default_mtu = ETHERMTU; + + lp = kmem_zalloc(sizeof (struct axf_dev), KM_SLEEP); + lp->chip = p; + lp->last_link_state = 0; + lp->phy_has_reset = B_FALSE; + + dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct axf_dev)); + + kmem_free(ugcp, sizeof (*ugcp)); + + if (dp != NULL) { + return (DDI_SUCCESS); + } + +err_free_mem: + kmem_free(lp, sizeof (struct axf_dev)); +err_close_pipe: +err: + return (DDI_FAILURE); + } + + if (cmd == DDI_RESUME) { + return (usbgem_resume(dip)); + } + + return (DDI_FAILURE); +} + +static int +axfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int ret; + + if (cmd == DDI_DETACH) { + ret = usbgem_do_detach(dip); + 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 +USBGEM_STREAM_OPS(axf_ops, axfattach, axfdetach); +#else +static struct module_info axfminfo = { + 0, /* mi_idnum */ + "axf", /* mi_idname */ + 0, /* mi_minpsz */ + ETHERMTU, /* mi_maxpsz */ + ETHERMTU*128, /* mi_hiwat */ + 1, /* mi_lowat */ +}; + +static struct qinit axfrinit = { + (int (*)()) NULL, /* qi_putp */ + usbgem_rsrv, /* qi_srvp */ + usbgem_open, /* qi_qopen */ + usbgem_close, /* qi_qclose */ + (int (*)()) NULL, /* qi_qadmin */ + &axfminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct qinit axfwinit = { + usbgem_wput, /* qi_putp */ + usbgem_wsrv, /* qi_srvp */ + (int (*)()) NULL, /* qi_qopen */ + (int (*)()) NULL, /* qi_qclose */ + (int (*)()) NULL, /* qi_qadmin */ + &axfminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct streamtab axf_info = { + &axfrinit, /* st_rdinit */ + &axfwinit, /* st_wrinit */ + NULL, /* st_muxrinit */ + NULL /* st_muxwrinit */ +}; + +static struct cb_ops cb_axf_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 */ + &axf_info, /* cb_stream */ + D_NEW|D_MP /* cb_flag */ +}; + +static struct dev_ops axf_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + usbgem_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + axfattach, /* devo_attach */ + axfdetach, /* devo_detach */ + nodev, /* devo_reset */ + &cb_axf_ops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + usbgem_power, /* devo_power */ +#if DEVO_REV >= 4 + usbgem_quiesce, /* devo_quiesce */ +#endif +}; +#endif + +static struct modldrv modldrv = { + &mod_driverops, /* Type of module. This one is a driver */ + ident, + &axf_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, &modldrv, NULL +}; + +/* ======================================================== */ +/* + * _init : done + */ +/* ======================================================== */ +int +_init(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!axf: _init: called")); + + status = usbgem_mod_init(&axf_ops, "axf"); + if (status != DDI_SUCCESS) { + return (status); + } + status = mod_install(&modlinkage); + if (status != DDI_SUCCESS) { + usbgem_mod_fini(&axf_ops); + } + return (status); +} + +/* + * _fini : done + */ +int +_fini(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!axf: _fini: called")); + status = mod_remove(&modlinkage); + if (status == DDI_SUCCESS) { + usbgem_mod_fini(&axf_ops); + } + return (status); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} diff --git a/usr/src/uts/common/io/udmf/THIRDPARTYLICENSE b/usr/src/uts/common/io/udmf/THIRDPARTYLICENSE new file mode 100644 index 0000000000..90268961ad --- /dev/null +++ b/usr/src/uts/common/io/udmf/THIRDPARTYLICENSE @@ -0,0 +1,33 @@ +/* + * udmfE_usbgem.c : Davicom DM9601E USB to Fast Ethernet Driver for Solaris + * + * Copyright (c) 2009-2012 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 + * and/or other materials provided with the distribution. + * + * 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. + */ + diff --git a/usr/src/uts/common/io/udmf/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/udmf/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..f645cc4b47 --- /dev/null +++ b/usr/src/uts/common/io/udmf/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +UDMF LICENSE diff --git a/usr/src/uts/common/io/udmf/dm9601reg.h b/usr/src/uts/common/io/udmf/dm9601reg.h new file mode 100644 index 0000000000..8203b109cf --- /dev/null +++ b/usr/src/uts/common/io/udmf/dm9601reg.h @@ -0,0 +1,347 @@ +/* + * Macro definitions for Davicom DM9601 USB to fast ethernet controler + * based on Davicom DM9601E data sheet + * This file is public domain. Coded by M.Murayama (KHF04453@nifty.com) + */ + +#ifndef __DM9601_H__ +#define __DM9601_H__ + +/* + * offset of registers + */ +#define NCR 0x00U /* network control register */ +#define NSR 0x01U /* network status register */ +#define TCR 0x02U /* tx control register */ +#define TSR1 0x03U /* tx status register 1 */ +#define TSR2 0x04U /* tx status register 2 */ +#define RCR 0x05U /* rx control register */ +#define RSR 0x06U /* rx status register */ +#define ROCR 0x07U /* rx overflow counter register */ +#define BPTR 0x08U /* back pressure threshold regster */ +#define FCTR 0x09U /* flow control threshold regster */ +#define FCR 0x0aU /* flow control threshold regster */ +#define EPCR 0x0bU /* eeprom & phy control register */ +#define EPAR 0x0cU /* eeprom & phy address register */ +#define EPDR 0x0dU /* eeprom & phy data register (2byte) */ +#define WCR 0x0fU /* wake up control register */ +#define PAR 0x10U /* physical address register (6byte) */ +#define MAR 0x16U /* multicast address register (8byte) */ +#define GPCR 0x1eU /* general purpose control register */ +#define GPR 0x1fU /* general purpose register */ +#define VID 0x28U /* vendor ID (2byte) */ +#define PID 0x2aU /* product ID (2byte) */ +#define CHIPR 0x2cU /* chip revision */ +#define USBDA 0xf0U /* usb device address register */ +#define RXC 0xf1U /* received packet counter register */ +#define TUSC 0xf2U /* tx packet counter/usb status register */ +#define USBC 0xf4U /* usb control register */ + +/* + * register definitions + */ +/* network control register */ +#define NCR_EXT_PHY 0x80U /* 1: select external phy */ +#define NCR_WAKEEN 0x40U /* 1: wake up event enable */ +#define NCR_FCOL 0x10U /* force collision mode for test */ +#define NCR_FDX 0x08U /* 1: full duplex mode (for external phy) */ +#define NCR_LBK 0x06U +#define NCR_LBK_SHIFT 1 +#define NCR_LBK_NORMAL (0U << NCR_LBK_SHIFT) +#define NCR_LBK_MAC (1U << NCR_LBK_SHIFT) +#define NCR_LBK_PHY_D (2U << NCR_LBK_SHIFT) +#define NCR_LBK_PHY_A (3U << NCR_LBK_SHIFT) +#define NCR_RST 0x01U /* 1: reset, auto clear */ + +#define NCR_BITS \ + "\020" \ + "\010EXT_PHY" \ + "\007WAKEEN" \ + "\005FCOL" \ + "\004FDX" \ + "\001RST" + +/* network status register */ +#define NSR_SPEED 0x80U /* 1:10M 0:100M */ +#define NSR_LINKST 0x40U /* 1:ok 0:fail */ +#define NSR_WAKEST 0x20U /* 1:enabled */ +#define NSR_TXFULL 0x10U /* 1:tx fifo full */ +#define NSR_TX2END 0x08U /* tx packet2 complete status */ +#define NSR_TX1END 0x04U /* tx packet1 complete status */ +#define NSR_RXOV 0x02U /* rx fifo overflow */ +#define NSR_RXRDY 0x01U /* rx packet ready */ + +#define NSR_BITS \ + "\020" \ + "\010SPEED_10" \ + "\007LINKST_UP" \ + "\006WAKEST" \ + "\005TXFULL" \ + "\004TX2END" \ + "\003TX1END" \ + "\002RXOV" \ + "\001RXRDY" + +/* tx control register */ +#define TCR_TJDIS 0x40U /* tx jitter control */ +#define TCR_EXCEDM 0x20U /* excessive collision mode */ +#define TCR_PAD_DIS2 0x10U /* PAD appends disable for pkt2 */ +#define TCR_CRC_DIS2 0x08U /* CRC appends disable for pkt2 */ +#define TCR_PAD_DIS1 0x04U /* PAD appends disable for pkt1 */ +#define TCR_CRC_DIS1 0x02U /* CRC appends disable for pkt1 */ + +#define TCR_BITS \ + "\020" \ + "\007TJDIS" \ + "\006EXCEDM" \ + "\005PAD_DIS2" \ + "\004CRC_DIS2" \ + "\003PAD_DIS1" \ + "\002CRC_DIS1" + +/* tx status register (ro) */ +#define TSR_TJTO 0x80U /* tx jabber time out */ +#define TSR_LC 0x40U /* loss of carrier */ +#define TSR_NC 0x20U /* no carrier */ +#define TSR_LATEC 0x10U /* late collision */ +#define TSR_COL 0x08U /* late collision */ +#define TSR_EL 0x04U /* excessive collision */ + +#define TSR_BITS \ + "\020" \ + "\010TJTO" \ + "\007LC" \ + "\006NC" \ + "\005LATEC" \ + "\004COL" \ + "\003EL" + +/* rx control register */ +#define RCR_WTDIS 0x40U /* watch dog timer disable */ +#define RCR_DIS_LONG 0x20U /* discard longer packets than 1522 */ +#define RCR_DIS_CRC 0x10U /* discard crc error packets */ +#define RCR_ALL 0x08U /* pass all multicast */ +#define RCR_RUNT 0x04U /* pass runt packets */ +#define RCR_PRMSC 0x02U /* promiscuous mode */ +#define RCR_RXEN 0x01U /* rx enable */ + +#define RCR_BITS \ + "\020" \ + "\007WTDIS" \ + "\006DIS_LONG" \ + "\005DIS_CRC" \ + "\004ALL" \ + "\003RUNT" \ + "\002PRMSC" \ + "\001RXEN" + +/* rx status register */ +#define RSR_RF 0x80U /* runt frame */ +#define RSR_MF 0x40U /* multicast frame */ +#define RSR_LCS 0x20U /* late collision seen */ +#define RSR_RWTO 0x10U /* receive watchdog timeout */ +#define RSR_PLE 0x08U /* physical layer error */ +#define RSR_AE 0x04U /* alignment error */ +#define RSR_CE 0x02U /* crc error */ +#define RSR_FOE 0x01U /* fifo overflow error */ + +#define RSR_BITS \ + "\020" \ + "\010RF" \ + "\007MF" \ + "\006LCS" \ + "\005RWTO" \ + "\004PLE" \ + "\003AE" \ + "\002CE" \ + "\001FOE" + +/* receive overflow counter register */ +#define ROCR_RXFU 0x80U /* receive overflow counter overflow */ +#define ROCR_ROC 0x7fU /* receive overflow counter */ + +#define ROCR_BITS \ + "\020" \ + "\010RXFU" + +/* back pressure threshold register */ +#define BPTR_BPHW 0xf0U /* high water overflow threshold */ +#define BPTR_BPHW_SHIFT 4 +#define BPTR_BPHW_UNIT 1024U +#define BPTR_BPHW_DEFAULT (3 << BPTR_BPHW_SHIFT) /* 3k */ +#define BPTR_JPT 0x0fU /* jam pattern time */ +#define BPTR_JPT_SHIFT 0 +#define BPTR_JPT_5us (0U << BPTR_JPT_SHIFT) +#define BPTR_JPT_10us (1U << BPTR_JPT_SHIFT) +#define BPTR_JPT_15us (2U << BPTR_JPT_SHIFT) +#define BPTR_JPT_25us (3U << BPTR_JPT_SHIFT) +#define BPTR_JPT_50us (4U << BPTR_JPT_SHIFT) +#define BPTR_JPT_100us (5U << BPTR_JPT_SHIFT) +#define BPTR_JPT_150us (6U << BPTR_JPT_SHIFT) +#define BPTR_JPT_200us (7U << BPTR_JPT_SHIFT) +#define BPTR_JPT_250us (8U << BPTR_JPT_SHIFT) +#define BPTR_JPT_300us (9U << BPTR_JPT_SHIFT) +#define BPTR_JPT_350us (10U << BPTR_JPT_SHIFT) +#define BPTR_JPT_400us (11U << BPTR_JPT_SHIFT) +#define BPTR_JPT_450us (12U << BPTR_JPT_SHIFT) +#define BPTR_JPT_500us (13U << BPTR_JPT_SHIFT) +#define BPTR_JPT_550us (14U << BPTR_JPT_SHIFT) +#define BPTR_JPT_600us (15U << BPTR_JPT_SHIFT) + +/* flow control threshold register */ +#define FCTR_HWOT 0xf0U /* rx fifo high water overflow threshold */ +#define FCTR_HWOT_SHIFT 4 +#define FCTR_HWOT_UNIT 1024U +#define FCTR_LWOT 0x0fU /* rx fifo low water overflow threshold */ +#define FCTR_LWOT_SHIFT 0 +#define FCTR_LWOT_UNIT 1024U + +/* rx/tx flow control register */ +#define FCR_TXPO 0x80U /* tx pause packet */ +#define FCR_TXPF 0x40U /* tx pause packet */ +#define FCR_TXPEN 0x20U /* tx pause packet */ +#define FCR_BKPA 0x10U /* back pressure mode */ +#define FCR_BKPM 0x08U /* back pressure mode */ +#define FCR_BKPS 0x04U /* rx pause packet current status (r/c) */ +#define FCR_RXPCS 0x02U /* rx pause packet current status (ro) */ +#define FCR_FLCE 0x01U /* flow control enbale */ + +#define FCR_BITS \ + "\020" \ + "\000TXPO" \ + "\000TXPF" \ + "\000TXPEN" \ + "\000BKPA" \ + "\000BKPM" \ + "\000BKPS" \ + "\000RXPCS" \ + "\000FLCE" + +/* EEPROM & PHY control register (0x0b) */ +#define EPCR_REEP 0x20U /* reload eeprom */ +#define EPCR_WEP 0x10U /* write eeprom enable */ +#define EPCR_EPOS 0x08U /* select device, 0:eeprom, 1:phy */ +#define EPCR_ERPRR 0x04U /* read command */ +#define EPCR_ERPRW 0x02U /* write command */ +#define EPCR_ERRE 0x01U /* eeprom/phy access in progress (ro) */ + +#define EPCR_BITS \ + "\020" \ + "\005REEP" \ + "\004WEP" \ + "\003EPOS" \ + "\002ERPRR" \ + "\001ERPRW" \ + "\000ERRE" + +/* EEPROM & PHY access register (0x0c) */ +#define EPAR_PHYADR 0xc0U /* phy address, internal phy(1) or external */ +#define EPAR_PHYADR_SHIFT 6 +#define EPAR_EROA 0x3fU /* eeprom word addr or phy register addr */ +#define EPAR_EROA_SHIFT 0 + +/* EEPROM & PHY data register (0x0d(low)-0x0e(hi)) */ + +/* wake up control register (0x0f) */ +#define WCR_LINKEN 0x20U /* enable link status event */ +#define WCR_SAMPLEEN 0x10U /* enable sample frame event */ +#define WCR_MAGICEN 0x08U /* enable magic pkt event */ +#define WCR_LINKST 0x04U /* link status change occur ro */ +#define WCR_SAMPLEST 0x02U /* sample frame rx occur ro */ +#define WCR_MAGICST 0x01U /* magic pkt rx occur ro */ + +#define WCR_BITS \ + "\020" \ + "\000LINKEN" \ + "\000SAMPLEEN" \ + "\000MAGICEN" \ + "\000LINKST" \ + "\000SAMPLEST" \ + "\000MAGICST" + +/* physical address register (0x10-0x15) */ +/* multicast address register (0x16-0x1c) */ +/* general purpose control register (0x1e) */ +#define GPCR_GEPCTRL 0x7f +#define GPCR_OUT(n) (1U << (n)) + +#define GPCR_BITS \ + "\020" \ + "\006OUT5" \ + "\005OUT4" \ + "\004OUT3" \ + "\003OUT2" \ + "\002OUT1" \ + "\001OUT0" + +/* general purpose register (0x1f) */ +#define GPR_GEPIO5 0x20U +#define GPR_GEPIO4 0x10U +#define GPR_GEPIO3 0x08U +#define GPR_GEPIO2 0x04U +#define GPR_GEPIO1 0x02U +#define GPR_GEPIO0 0x01U + +#define GPR_BITS \ + "\020" \ + "\006GEPIO5" \ + "\005GEPIO4" \ + "\004GEPIO3" \ + "\003GEPIO2" \ + "\002GEPIO1" \ + "\001GEPIO0" + +/* vendor id register (0x28-0x29) */ +/* product id register (0x2a-0x2b) */ +/* chip revision register (0x2c) */ + +/* usb device address register (0xf0) */ +#define USBDA_USBFA 0x3fU /* usb device address */ +#define USBDA_USBFA_SHIFT 0 + +/* receive packet counter register (0xf1) */ + +/* transmitpacket counter/usb status register (0xf2) */ +#define TUSR_RXFAULT 0x80U /* indicate rx has unexpected condition */ +#define TUSR_SUSFLAG 0x40U /* indicate device has suspended condition */ +#define TUSR_EP1RDY 0x20U /* ready for read from ep1 pipe */ +#define TUSR_SRAM 0x18U /* sram size 0:32K, 1:48K, 2:16K, 3:64K */ +#define TUSR_SRAM_SHIFT 3 +#define TUSR_SRAM_32K (0U << TUSR_SRAM_SHIFT) +#define TUSR_SRAM_48K (1U << TUSR_SRAM_SHIFT) +#define TUSR_SRAM_16K (2U << TUSR_SRAM_SHIFT) +#define TUSR_SRAM_64K (3U << TUSR_SRAM_SHIFT) +#define TUSR_TXC2 0x04U /* two or more packets in tx buffer */ +#define TUSR_TXC1 0x02U /* one packet in tx buffer */ +#define TUSR_TXC0 0x01U /* no packet in tx buffer */ + +#define TUSR_BITS \ + "\020" \ + "\010RXFAULT" \ + "\007SUSFLAG" \ + "\006EP1RDY" \ + "\003TXC2" \ + "\002TXC1" \ + "\001TXC0" + +/* usb control register (0xf4) */ +#define USBC_EP3ACK 0x20U /* ep3 will alway return 8byte data if NAK=0 */ +#define USBC_EP3NACK 0x10U /* ep3 will alway return NAK */ +#define USBC_MEMTST 0x01U + +/* bulk message format */ +#define TX_HEADER_SIZE 2 +#define RX_HEADER_SIZE 3 + +/* interrupt msg format */ +struct intr_msg { + uint8_t im_nsr; + uint8_t im_tsr1; + uint8_t im_tsr2; + uint8_t im_rsr; + uint8_t im_rocr; + uint8_t im_rxc; + uint8_t im_txc; + uint8_t im_gpr; +}; +#endif /* __DM9601_H__ */ diff --git a/usr/src/uts/common/io/udmf/udmf_usbgem.c b/usr/src/uts/common/io/udmf/udmf_usbgem.c new file mode 100644 index 0000000000..d5093ed3a7 --- /dev/null +++ b/usr/src/uts/common/io/udmf/udmf_usbgem.c @@ -0,0 +1,1034 @@ +/* + * udmfE_usbgem.c : Davicom DM9601E USB to Fast Ethernet Driver for Solaris + * + * Copyright (c) 2009-2012 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 + * and/or other materials provided with the distribution. + * + * 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 + */ + +/* minimum kernel headers for drivers */ +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/debug.h> +#include <sys/kmem.h> +#include <sys/modctl.h> +#include <sys/errno.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/byteorder.h> + +/* ethernet stuff */ +#include <sys/ethernet.h> + +/* interface card depend stuff */ +#include <sys/stropts.h> +#include <sys/stream.h> +#include <sys/strlog.h> +#include <sys/strsun.h> +#include <sys/usb/usba.h> +#include "usbgem.h" + +/* hardware stuff */ +#include "usbgem_mii.h" +#include "dm9601reg.h" + +char ident[] = "dm9601 usbnic driver v" VERSION; + +/* + * Useful macros + */ +#define CHECK_AND_JUMP(err, label) if (err != USB_SUCCESS) goto label +#define LE16P(p) ((((uint8_t *)(p))[1] << 8) | ((uint8_t *)(p))[0]) + +/* + * Debugging + */ +#ifdef DEBUG_LEVEL +static int udmf_debug = DEBUG_LEVEL; +#define DPRINTF(n, args) if (udmf_debug > (n)) cmn_err args +#else +#define DPRINTF(n, args) +#endif + +/* + * Our configration for dm9601 + */ +/* timeouts */ +#define ONESEC (drv_usectohz(1*1000000)) + +/* + * Local device definitions + */ +struct udmf_dev { + /* + * Misc HW information + */ + uint8_t rcr; + uint8_t last_nsr; + uint8_t mac_addr[ETHERADDRL]; +}; + +/* + * private functions + */ + +/* mii operations */ +static uint16_t udmf_mii_read(struct usbgem_dev *, uint_t, int *errp); +static void udmf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp); + +/* nic operations */ +static int udmf_reset_chip(struct usbgem_dev *); +static int udmf_init_chip(struct usbgem_dev *); +static int udmf_start_chip(struct usbgem_dev *); +static int udmf_stop_chip(struct usbgem_dev *); +static int udmf_set_media(struct usbgem_dev *); +static int udmf_set_rx_filter(struct usbgem_dev *); +static int udmf_get_stats(struct usbgem_dev *); +static void udmf_interrupt(struct usbgem_dev *, mblk_t *); + +/* packet operations */ +static mblk_t *udmf_tx_make_packet(struct usbgem_dev *, mblk_t *); +static mblk_t *udmf_rx_make_packet(struct usbgem_dev *, mblk_t *); + +/* =============================================================== */ +/* + * I/O functions + */ +/* =============================================================== */ +#define OUT(dp, ix, len, buf, errp, label) \ + if ((*(errp) = usbgem_ctrl_out((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ 1, \ + /* wValue */ 0, \ + /* wIndex */ (ix), \ + /* wLength */ (len), \ + /* value */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label + +#define OUTB(dp, ix, val, errp, label) \ + if ((*(errp) = usbgem_ctrl_out((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ 3, \ + /* wValue */ (val), \ + /* wIndex */ (ix), \ + /* wLength */ 0, \ + /* value */ NULL, \ + /* size */ 0)) != USB_SUCCESS) goto label + +#define IN(dp, ix, len, buf, errp, label) \ + if ((*(errp) = usbgem_ctrl_in((dp), \ + /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ 0, \ + /* wValue */ 0, \ + /* wIndex */ (ix), \ + /* wLength */ (len), \ + /* valuep */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label + +/* =============================================================== */ +/* + * Hardware manupilation + */ +/* =============================================================== */ +static void +udmf_enable_phy(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + + /* de-assert reset signal to phy */ + OUTB(dp, GPCR, GPCR_OUT(0), &err, usberr); + OUTB(dp, GPR, 0, &err, usberr); +usberr: + ; +} + +static int +udmf_reset_chip(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + OUTB(dp, NCR, NCR_LBK_NORMAL | NCR_RST, &err, usberr); + drv_usecwait(100); +usberr: + return (err); +} + +/* + * Setup dm9601 + */ +static int +udmf_init_chip(struct usbgem_dev *dp) +{ + int i; + uint32_t val; + int err = USB_SUCCESS; + uint16_t reg; + uint8_t buf[2]; + struct udmf_dev *lp = dp->private; + + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + OUTB(dp, NCR, NCR_LBK_NORMAL, &err, usberr); + + /* tx control regiser: enable padding and crc generation */ + OUTB(dp, TCR, 0, &err, usberr); + + /* rx control register: will be set later by udmf_set_rx_filer() */ + lp->rcr = RCR_RUNT; + + /* back pressure threshold: */ + OUTB(dp, BPTR, (2 << BPTR_BPHW_SHIFT) | BPTR_JPT_200us, + &err, usberr); + + /* flow control threshold: same as default */ + OUTB(dp, FCTR, (3 << FCTR_HWOT_SHIFT) | (8 << FCTR_LWOT_SHIFT), + &err, usberr); + + /* usb control register */ + OUTB(dp, USBC, USBC_EP3ACK | 0x06, &err, usberr); + + /* flow control: will be set later by udmf_set_media() */ + + /* wake up control register: */ + OUTB(dp, WCR, 0, &err, usberr); + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +static int +udmf_start_chip(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + struct udmf_dev *lp = dp->private; + + /* enable Rx */ + lp->rcr |= RCR_RXEN; + OUTB(dp, RCR, lp->rcr, &err, usberr); + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +static int +udmf_stop_chip(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + struct udmf_dev *lp = dp->private; + + /* disable rx */ + lp->rcr &= ~RCR_RXEN; + OUTB(dp, RCR, lp->rcr, &err, usberr); + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +static int +udmf_get_stats(struct usbgem_dev *dp) +{ + /* empty */ + return (USB_SUCCESS); +} + +static uint_t +udmf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr) +{ + return (usbgem_ether_crc_le(addr) & 0x3f); +} + +static int +udmf_set_rx_filter(struct usbgem_dev *dp) +{ + int i; + uint8_t rcr; + uint8_t mode; + uint8_t mhash[8]; + uint8_t *mac; + uint_t h; + int err = USB_SUCCESS; + struct udmf_dev *lp = dp->private; + static uint8_t invalid_mac[ETHERADDRL] = {0, 0, 0, 0, 0, 0}; + + DPRINTF(2, (CE_CONT, "!%s: %s: called, rxmode:%x", + dp->name, __func__, dp->rxmode)); + + if (lp->rcr & RCR_RXEN) { + /* set promiscuous mode before changing rx filter mode */ + OUTB(dp, RCR, lp->rcr | RCR_PRMSC, &err, usberr); + } + + lp->rcr &= ~(RCR_ALL | RCR_PRMSC); + mode = 0; + bzero(mhash, sizeof (mhash)); + mac = dp->cur_addr.ether_addr_octet; + + if ((dp->rxmode & RXMODE_ENABLE) == 0) { + mac = invalid_mac; + } else if (dp->rxmode & RXMODE_PROMISC) { + /* promiscious mode implies all multicast and all physical */ + mode |= RCR_PRMSC; + } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 32) { + /* accept all multicast packets */ + mode |= RCR_ALL; + } else if (dp->mc_count > 0) { + /* + * make hash table to select interresting + * multicast address only. + */ + for (i = 0; i < dp->mc_count; i++) { + /* hash table is 64 = 2^6 bit width */ + h = dp->mc_list[i].hash; + mhash[h / 8] |= 1 << (h % 8); + } + } + + /* set node address */ + if (bcmp(mac, lp->mac_addr, ETHERADDRL) != 0) { + OUT(dp, PAR, ETHERADDRL, dp->cur_addr.ether_addr_octet, + &err, usberr); + bcopy(mac, lp->mac_addr, ETHERADDRL); + } + + /* set multicast hash table */ + OUT(dp, MAR, sizeof (mhash), &mhash[0], &err, usberr); + + /* update rcr */ + lp->rcr |= mode; + OUTB(dp, RCR, lp->rcr, &err, usberr); + +#if DEBUG_LEVEL > 1 + /* verify rcr */ + IN(dp, RCR, 1, &rcr, &err, usberr); + cmn_err(CE_CONT, "!%s: %s: rcr:%b returned", + dp->name, __func__, rcr, RCR_BITS); +#endif +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +static int +udmf_set_media(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + uint8_t fcr; + struct udmf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* setup flow control */ + fcr = 0; + if (dp->full_duplex) { + /* select flow control */ + switch (dp->flow_control) { + case FLOW_CONTROL_RX_PAUSE: + fcr |= FCR_FLCE; + break; + + case FLOW_CONTROL_TX_PAUSE: + fcr |= FCR_TXPEN; + break; + + case FLOW_CONTROL_SYMMETRIC: + fcr |= FCR_FLCE | FCR_TXPEN; + break; + } + } + + /* update flow control register */ + OUTB(dp, FCR, fcr, &err, usberr); + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)", + dp->name, __func__, + err, err == USB_SUCCESS ? "success" : "error")); + return (err); +} + +/* + * send/receive packet check + */ +static mblk_t * +udmf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + int n; + size_t pkt_size; + mblk_t *new; + mblk_t *tp; + uint8_t *bp; + uint8_t *last_pos; + uint_t align_mask; + + pkt_size = msgdsize(mp); + align_mask = 63; + + /* + * re-allocate the mp + */ + + /* minimum ethernet packet size of ETHERMIN */ + pkt_size = max(pkt_size, ETHERMIN); + +#if 0 /* CONFIG_ADD_TX_DELIMITOR_ALWAYS */ + pkt_size += TX_HEADER_SIZE; +#endif + if (((pkt_size + TX_HEADER_SIZE) & align_mask) == 0) { + /* padding is required in usb communication */ + pkt_size += TX_HEADER_SIZE; + } + + if ((new = allocb(TX_HEADER_SIZE + pkt_size, 0)) == NULL) { + return (NULL); + } + new->b_wptr = new->b_rptr + TX_HEADER_SIZE + pkt_size; + + /* add a header */ + bp = new->b_rptr; + bp[0] = (uint8_t)pkt_size; + bp[1] = (uint8_t)(pkt_size >> 8); + bp += TX_HEADER_SIZE; + + /* copy contents of the buffer */ + for (tp = mp; tp; tp = tp->b_cont) { + n = MBLKL(tp); + bcopy(tp->b_rptr, bp, n); + bp += n; + } + + /* clear the rest including the next zero length header */ + last_pos = new->b_wptr; + while (bp < last_pos) { + *bp++ = 0; + } + + return (new); +} + +static void +udmf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n) +{ + int i; + + for (i = 0; i < n; i += 8, bp += 8) { + cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x", + bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]); + } +} + +static mblk_t * +udmf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + size_t len; + uint8_t rx_stat; + + len = MBLKL(mp); + + if (len <= RX_HEADER_SIZE) { + /* + * the usb bulk-in frame doesn't include a valid + * ethernet packet. + */ + return (NULL); + } + + /* remove rx header */ + rx_stat = mp->b_rptr[0]; + if (rx_stat & (RSR_RF | RSR_LCS | RSR_RWTO | + RSR_PLE | RSR_AE | RSR_CE | RSR_FOE)) { + if (rx_stat & RSR_RF) { + dp->stats.runt++; + } + if (rx_stat & RSR_LCS) { + /* late collision */ + dp->stats.rcv_internal_err++; + } + if (rx_stat & RSR_RWTO) { + /* rx timeout */ + dp->stats.rcv_internal_err++; + } + if (rx_stat & RSR_PLE) { + /* physical layer error */ + dp->stats.rcv_internal_err++; + } + if (rx_stat & RSR_AE) { + /* alignment error */ + dp->stats.frame++; + } + if (rx_stat & RSR_CE) { + /* crc error */ + dp->stats.crc++; + } + if (rx_stat & RSR_FOE) { + /* fifo overflow error */ + dp->stats.overflow++; + } + dp->stats.errrcv++; + } + len = LE16P(&mp->b_rptr[1]); + if (len >= ETHERFCSL) { + len -= ETHERFCSL; + } + mp->b_rptr += RX_HEADER_SIZE; + mp->b_wptr = mp->b_rptr + len; + + return (mp); +} + +/* + * MII Interfaces + */ +static uint16_t +udmf_ep_read(struct usbgem_dev *dp, uint_t which, uint_t addr, int *errp) +{ + int i; + uint8_t epcr; + uint16_t val; + + DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d", + dp->name, __func__, addr)); + + OUTB(dp, EPAR, addr, errp, usberr); + OUTB(dp, EPCR, which | EPCR_ERPRR, errp, usberr); + + for (i = 0; i < 100; i++) { + IN(dp, EPCR, sizeof (epcr), &epcr, errp, usberr); + if ((epcr & EPCR_ERRE) == 0) { + /* done */ + IN(dp, EPDR, sizeof (val), &val, errp, usberr); + val = LE_16(val); + goto done; + } + drv_usecwait(10); + } + /* timeout */ + cmn_err(CE_WARN, "!%s: %s: timeout", dp->name, __func__); + val = 0; +done: + OUTB(dp, EPCR, 0, errp, usberr); + return (val); + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)", + dp->name, __func__, + *errp, *errp == USB_SUCCESS ? "success" : "error")); + return (0); +} + +static void +udmf_ep_write(struct usbgem_dev *dp, uint_t which, uint_t addr, + uint16_t val, int *errp) +{ + int i; + uint8_t epcr; + + DPRINTF(5, (CE_CONT, "!%s: %s called", dp->name, __func__)); + + val = LE_16(val); + OUT(dp, EPDR, sizeof (val), &val, errp, usberr); + + OUTB(dp, EPAR, addr, errp, usberr); + + OUTB(dp, EPCR, which | EPCR_WEP | EPCR_ERPRW, errp, usberr); + + for (i = 0; i < 100; i++) { + IN(dp, EPCR, 1, &epcr, errp, usberr); + if ((epcr & EPCR_ERRE) == 0) { + /* done */ + goto done; + } + drv_usecwait(10); + } + /* timeout */ + cmn_err(CE_WARN, "!%s: %s: timeout", dp->name, __func__); +done: + OUTB(dp, EPCR, 0, errp, usberr); + return; + +usberr: + DPRINTF(2, (CE_CONT, "!%s: %s: end err:%d(%s)", + dp->name, __func__, + *errp, *errp == USB_SUCCESS ? "success" : "error")); +} + +static uint16_t +udmf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp) +{ + uint16_t val; + + val = udmf_ep_read(dp, EPCR_EPOS, + (dp->mii_phy_addr << EPAR_PHYADR_SHIFT) | index, errp); + + return (val); +} + +static void +udmf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp) +{ + udmf_ep_write(dp, EPCR_EPOS, + (dp->mii_phy_addr << EPAR_PHYADR_SHIFT) | index, val, errp); +} + +static void +udmf_interrupt(struct usbgem_dev *dp, mblk_t *mp) +{ + struct intr_msg *imp; + struct udmf_dev *lp = dp->private; + + imp = (struct intr_msg *)&mp->b_rptr[0]; + + DPRINTF(4, (CE_CONT, + "!%s: %s: size:%d, nsr:%b tsr1:%b tsr2:%b" + " rsr:%b rocr:%b rxc:%02x txc:%b gpr:%b", + dp->name, __func__, mp->b_wptr - mp->b_rptr, + imp->im_nsr, NSR_BITS, + imp->im_tsr1, TSR_BITS, + imp->im_tsr2, TSR_BITS, + imp->im_rsr, RSR_BITS, + imp->im_rocr, ROCR_BITS, + imp->im_rxc, + imp->im_txc, TUSR_BITS, + imp->im_gpr, GPR_BITS)); + + if ((lp->last_nsr ^ imp->im_nsr) & NSR_LINKST) { + usbgem_mii_update_link(dp); + } + + lp->last_nsr = imp->im_nsr; +} + +/* ======================================================== */ +/* + * OS depend (device driver DKI) routine + */ +/* ======================================================== */ +static uint16_t +udmf_eeprom_read(struct usbgem_dev *dp, uint_t index, int *errp) +{ + uint16_t val; + + val = udmf_ep_read(dp, 0, index, errp); + + return (val); +} + +#ifdef DEBUG_LEVEL +static void +udmf_eeprom_dump(struct usbgem_dev *dp, int size) +{ + int i; + int err; + uint16_t w0, w1, w2, w3; + + cmn_err(CE_CONT, "!%s: eeprom dump:", dp->name); + + err = USB_SUCCESS; + + for (i = 0; i < size; i += 4) { + w0 = udmf_eeprom_read(dp, i + 0, &err); + w1 = udmf_eeprom_read(dp, i + 1, &err); + w2 = udmf_eeprom_read(dp, i + 2, &err); + w3 = udmf_eeprom_read(dp, i + 3, &err); + cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x", + i, w0, w1, w2, w3); + } +usberr: + ; +} +#endif + +static int +udmf_attach_chip(struct usbgem_dev *dp) +{ + int i; + uint_t val; + uint8_t *m; + int err; + struct udmf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s enter", dp->name, __func__)); + + /* + * get mac address from EEPROM + */ + m = dp->dev_addr.ether_addr_octet; + for (i = 0; i < ETHERADDRL; i += 2) { + val = udmf_eeprom_read(dp, i/2, &err); + m[i + 0] = (uint8_t)val; + m[i + 1] = (uint8_t)(val >> 8); + } + + /* invalidate a private cache for mac addr */ + bzero(lp->mac_addr, sizeof (lp->mac_addr)); +#ifdef CONFIG_VLAN + dp->misc_flag = USBGEM_VLAN; +#endif +#if DEBUG_LEVEL > 0 + udmf_eeprom_dump(dp, /* 0x3f + 1 */ 128); +#endif +{ + static uint8_t bcst[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + DPRINTF(0, (CE_CONT, "!%s: %s: hash of bcast:%x", + dp->name, __func__, usbgem_ether_crc_be(bcst))); +} + return (USB_SUCCESS); + +usberr: + cmn_err(CE_WARN, "%s: %s: usb error detected (%d)", + dp->name, __func__, err); + return (USB_FAILURE); +} + +static int +udmf_mii_probe(struct usbgem_dev *dp) +{ + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + udmf_enable_phy(dp); + return (usbgem_mii_probe_default(dp)); +} + +static int +udmf_mii_init(struct usbgem_dev *dp) +{ + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + udmf_enable_phy(dp); + return (USB_SUCCESS); +} + +static int +udmfattach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int i; + ddi_iblock_cookie_t c; + int ret; + int revid; + int unit; + int len; + const char *drv_name; + struct usbgem_dev *dp; + void *base; + struct usbgem_conf *ugcp; + struct udmf_dev *lp; + + unit = ddi_get_instance(dip); + drv_name = ddi_driver_name(dip); + + DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d", + drv_name, unit, __func__, cmd)); + + if (cmd == DDI_ATTACH) { + /* + * construct usbgem configration + */ + ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP); + + /* name */ + /* + * softmac requires that ppa is the instance number + * of the device, otherwise it hangs in seaching the device. + */ + (void) sprintf(ugcp->usbgc_name, "%s%d", drv_name, unit); + ugcp->usbgc_ppa = unit; + + ugcp->usbgc_ifnum = 0; + ugcp->usbgc_alt = 0; + + ugcp->usbgc_tx_list_max = 64; + + ugcp->usbgc_rx_header_len = RX_HEADER_SIZE; + ugcp->usbgc_rx_list_max = 64; + + /* time out parameters */ + ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT; + ugcp->usbgc_tx_timeout_interval = USBGEM_TX_TIMEOUT_INTERVAL; +#if 1 + /* flow control */ + ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE; +#else + /* + * XXX - flow control caused link down frequently under + * heavy traffic + */ + ugcp->usbgc_flow_control = FLOW_CONTROL_NONE; +#endif + /* MII timeout parameters */ + ugcp->usbgc_mii_link_watch_interval = + USBGEM_LINK_WATCH_INTERVAL; + ugcp->usbgc_mii_an_watch_interval = + USBGEM_LINK_WATCH_INTERVAL/5; + ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */ + ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */ + ugcp->usbgc_mii_an_wait = (25*ONESEC)/10; + ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT; + + ugcp->usbgc_mii_an_delay = ONESEC/10; + ugcp->usbgc_mii_linkdown_action = MII_ACTION_RSA; + ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET; + ugcp->usbgc_mii_dont_reset = B_FALSE; + ugcp->usbgc_mii_hw_link_detection = B_TRUE; + + /* I/O methods */ + + /* mac operation */ + ugcp->usbgc_attach_chip = &udmf_attach_chip; + ugcp->usbgc_reset_chip = &udmf_reset_chip; + ugcp->usbgc_init_chip = &udmf_init_chip; + ugcp->usbgc_start_chip = &udmf_start_chip; + ugcp->usbgc_stop_chip = &udmf_stop_chip; + ugcp->usbgc_multicast_hash = &udmf_mcast_hash; + + ugcp->usbgc_set_rx_filter = &udmf_set_rx_filter; + ugcp->usbgc_set_media = &udmf_set_media; + ugcp->usbgc_get_stats = &udmf_get_stats; + ugcp->usbgc_interrupt = &udmf_interrupt; + + /* packet operation */ + ugcp->usbgc_tx_make_packet = &udmf_tx_make_packet; + ugcp->usbgc_rx_make_packet = &udmf_rx_make_packet; + + /* mii operations */ + ugcp->usbgc_mii_probe = &udmf_mii_probe; + ugcp->usbgc_mii_init = &udmf_mii_init; + ugcp->usbgc_mii_config = &usbgem_mii_config_default; + ugcp->usbgc_mii_read = &udmf_mii_read; + ugcp->usbgc_mii_write = &udmf_mii_write; + ugcp->usbgc_mii_addr_min = 1; + + /* mtu */ + ugcp->usbgc_min_mtu = ETHERMTU; + ugcp->usbgc_max_mtu = ETHERMTU; + ugcp->usbgc_default_mtu = ETHERMTU; + + lp = kmem_zalloc(sizeof (struct udmf_dev), KM_SLEEP); + + ddi_set_driver_private(dip, NULL); + + dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct udmf_dev)); + + kmem_free(ugcp, sizeof (*ugcp)); + + if (dp != NULL) { + return (DDI_SUCCESS); + } + +err_free_mem: + kmem_free(lp, sizeof (struct udmf_dev)); +err_close_pipe: +err: + return (DDI_FAILURE); + } + + if (cmd == DDI_RESUME) { + return (usbgem_resume(dip)); + } + + return (DDI_FAILURE); +} + +static int +udmfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int ret; + + if (cmd == DDI_DETACH) { + ret = usbgem_do_detach(dip); + 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 +USBGEM_STREAM_OPS(udmf_ops, udmfattach, udmfdetach); +#else +static struct module_info udmfminfo = { + 0, /* mi_idnum */ + "udmf", /* mi_idname */ + 0, /* mi_minpsz */ + ETHERMTU, /* mi_maxpsz */ + ETHERMTU*128, /* mi_hiwat */ + 1, /* mi_lowat */ +}; + +static struct qinit udmfrinit = { + (int (*)()) NULL, /* qi_putp */ + usbgem_rsrv, /* qi_srvp */ + usbgem_open, /* qi_qopen */ + usbgem_close, /* qi_qclose */ + (int (*)()) NULL, /* qi_qadmin */ + &udmfminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct qinit udmfwinit = { + usbgem_wput, /* qi_putp */ + usbgem_wsrv, /* qi_srvp */ + (int (*)()) NULL, /* qi_qopen */ + (int (*)()) NULL, /* qi_qclose */ + (int (*)()) NULL, /* qi_qadmin */ + &udmfminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct streamtab udmf_info = { + &udmfrinit, /* st_rdinit */ + &udmfwinit, /* st_wrinit */ + NULL, /* st_muxrinit */ + NULL /* st_muxwrinit */ +}; + +static struct cb_ops cb_udmf_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 */ + &udmf_info, /* cb_stream */ + D_NEW|D_MP /* cb_flag */ +}; + +static struct dev_ops udmf_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + usbgem_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + udmfattach, /* devo_attach */ + udmfdetach, /* devo_detach */ + nodev, /* devo_reset */ + &cb_udmf_ops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + usbgem_power, /* devo_power */ +#if DEVO_REV >= 4 + usbgem_quiesce, /* devo_quiesce */ +#endif +}; +#endif + +static struct modldrv modldrv = { + &mod_driverops, /* Type of module. This one is a driver */ + ident, + &udmf_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, &modldrv, NULL +}; + +/* ======================================================== */ +/* + * _init : done + */ +/* ======================================================== */ +int +_init(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!udmf: _init: called")); + + status = usbgem_mod_init(&udmf_ops, "udmf"); + if (status != DDI_SUCCESS) { + return (status); + } + status = mod_install(&modlinkage); + if (status != DDI_SUCCESS) { + usbgem_mod_fini(&udmf_ops); + } + return (status); +} + +/* + * _fini : done + */ +int +_fini(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!udmf: _fini: called")); + status = mod_remove(&modlinkage); + if (status == DDI_SUCCESS) { + usbgem_mod_fini(&udmf_ops); + } + return (status); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} diff --git a/usr/src/uts/common/io/upf/THIRDPARTYLICENSE b/usr/src/uts/common/io/upf/THIRDPARTYLICENSE new file mode 100644 index 0000000000..8a64e73616 --- /dev/null +++ b/usr/src/uts/common/io/upf/THIRDPARTYLICENSE @@ -0,0 +1,31 @@ +/* + * 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 + * and/or other materials provided with the distribution. + * + * 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. + */ + diff --git a/usr/src/uts/common/io/upf/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/upf/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..6704149796 --- /dev/null +++ b/usr/src/uts/common/io/upf/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +UPF DRIVER diff --git a/usr/src/uts/common/io/upf/adm8511reg.h b/usr/src/uts/common/io/upf/adm8511reg.h new file mode 100644 index 0000000000..71fd41eeb0 --- /dev/null +++ b/usr/src/uts/common/io/upf/adm8511reg.h @@ -0,0 +1,210 @@ +/* + * @(#)adm8511reg.h 1.1 09/06/20 + * Register dehinitsions of ADMtek ADM8511 Fast Ethernet to USB controller. + * Codeded by Masayuki Murayama(KHF04453@nifty.ne.jp) + * This file is public domain. + */ + +#ifndef __ADM8511_H__ +#define __ADM8511_H__ + +#define EC0 0x00 /* B */ +#define EC1 0x01 /* B */ +#define EC2 0x02 /* B */ +#define MA 0x08 /* 8byte array */ +#define EID 0x10 /* B */ +#define PAUSETIMER 0x18 /* B pause timer */ +#define RPNBFC 0x1a /* B */ +#define ORFBFC 0x1b /* B */ +#define EP1C 0x1c /* B */ +#define RXFC 0x1d /* B */ +#define BIST 0x1e /* B */ +#define EEOFFSET 0x20 /* B */ +#define EEDATA 0x21 /* W */ +#define EECTRL 0x23 /* B */ +#define PHYA 0x25 /* B */ +#define PHYD 0x26 /* W */ +#define PHYAC 0x28 /* B */ +#define USBSTAT 0x2a /* B */ +#define ETHTXSTAT 0x2b /* W */ +#define ETHRXSTAT 0x2d /* B */ +#define LOSTCNT 0x2e /* W */ +#define WF0MASK 0x30 /* 16byte array */ +#define WF0OFFSET 0x40 /* W */ +#define WF0CRC 0x41 /* W */ +#define WF1MASK 0x48 /* 16byte array */ +#define WF1OFFSET 0x58 /* W */ +#define WF1CRC 0x59 /* W */ +#define WF2MASK 0x60 /* 16byte array */ +#define WF2OFFSET 0x70 /* W */ +#define WF2CRC 0x71 /* W */ +#define WCTRL 0x78 /* B */ +#define WSTAT 0x7a /* B */ +#define IPHYC 0x7b /* B */ +#define GPIO54 0x7c /* B */ +#define GPIO10 0x7e /* B */ +#define GPIO32 0x7f /* B */ +#define TEST 0x80 /* B */ +#define TM 0x81 /* B */ +#define RPN 0x82 /* B */ + +/* Ethernet control register 0: offset 0 */ +#define EC0_TXE 0x80U +#define EC0_RXE 0x40U +#define EC0_RXFCE 0x20U +#define EC0_WOE 0x10U +#define EC0_RXSA 0x08U +#define EC0_SBO 0x04U +#define EC0_RXMA 0x02U +#define EC0_RXCS 0x01U + +#define EC0_BITS \ + "\020" \ + "\010TXE" \ + "\007RXE" \ + "\006RXFCE" \ + "\005WOE" \ + "\004RXSA" \ + "\003SBO" \ + "\002RXMA" \ + "\001RXCS" + +/* Ethernet control register 1: offset 1 */ +#define EC1_FD 0x20U +#define EC1_100M 0x10U /* 0:10Mbps 1:100Mbps */ +#define EC1_RM 0x08U /* reset mac */ + +#define EC1_BITS \ + "\020" \ + "\006FD" \ + "\005100M" \ + "\004RM" + +/* Ethernet control register 2: offset 2 */ +#define EC2_MEPL 0x80U /* 8515: MTU 0:1528, 1:1638 */ +#define EC2_RPNC 0x40U +#define EC2_LEEPRS 0x20U +#define EC2_EEPRW 0x10U +#define EC2_LB 0x08U +#define EC2_PROM 0x04U +#define EC2_RXBP 0x02U +#define EC2_EP3RC 0x01U + +#define EC2_BITS \ + "\020" \ + "\010MEPS" \ + "\007RPNC" \ + "\006LEEPRS" \ + "\005EEPRW" \ + "\004LB" \ + "\003PROM" \ + "\002RXBP" \ + "\001EP3RC" + +/* Recieve Packet number based Flow Control register: offset 0x1a */ +#define RPNBFC_PN 0x7eU /* */ +#define RPNBFC_PN_SHIFT 1 +#define RPNBFC_FCP 0x01U /* enable rx flow control */ + +/* Occupied Recieve FIFO based Flow Control register: offset 0x1b */ +#define ORFBFC_RXS 0x7eU /* */ +#define ORFBFC_RXS_SHIFT 1 +#define ORFBFC_RXS_UNIT 1024U +#define ORFBFC_FCRXS 0x01U /* enable rx flow control */ + +/* EP1 control register: offset 0x1c */ +#define EP1C_EP1S0E 0x80U /* send 0 enable */ +#define EP1C_ITMA 0x60U /* internal test mode A */ +#define EP1C_ITMB 0x1fU /* internal test mode B */ + +#define EP1C_BITS \ + "\020" \ + "\010EP1S0E" + +/* Rx FIFO Control register: offset 0x1d */ +#define RXFC_EXT_SRAM 0x02 /* enable external 32k sram */ +#define RXFC_RX32PKT 0x01 /* max 32 packet */ + +/* EEPROM offset register: offset 0x20 */ +#define EEOFFSET_MASK 0x3f /* eeprom offset address in word */ + +/* EEPROM access control register: offset 0x23 */ +#define EECTRL_DONE 0x04 +#define EECTRL_RD 0x02 +#define EECTRL_WR 0x01 + +#define EECTRL_BITS \ + "\020" \ + "\003DONE" \ + "\002RD" \ + "\001WR" + +/* PHY control register: offset 28 */ +#define PHYAC_DO 0x80U /* Done */ +#define PHYAC_RDPHY 0x40U /* read phy */ +#define PHYAC_WRPHY 0x20U /* write phy */ +#define PHYAC_PHYRA 0x1fU /* PHY register address */ + +#define PHYCTRL_BITS \ + "\020" \ + "\010DO" \ + "\007RDPHY" \ + "\006WRPHY" + +/* Internal PHY control register: offset 7b */ +#define IPHYC_EPHY 0x02 +#define IPHYC_PHYR 0x01 + +#define IPHYC_BITS \ + "\020" \ + "\002EPHY" \ + "\001PHYR" + +/* GPIO45 register: offset 7c */ +#define GPIO54_5OE 0x20 +#define GPIO54_5O 0x10 +#define GPIO54_5I 0x08 +#define GPIO54_4OE 0x04 +#define GPIO54_4O 0x02 +#define GPIO54_4I 0x01 + +/* GPIO01 register: offset 7e */ +#define GPIO10_1OE 0x20 +#define GPIO10_1O 0x10 +#define GPIO10_1I 0x08 +#define GPIO10_0OE 0x04 +#define GPIO10_0O 0x02 +#define GPIO10_0I 0x01 + +/* GPIO23 register: offset 7f */ +#define GPIO32_3OE 0x20 +#define GPIO32_3O 0x10 +#define GPIO32_3I 0x08 +#define GPIO32_2OE 0x04 +#define GPIO32_2O 0x02 +#define GPIO32_2I 0x01 + +/* rx status at the end of received packets */ +/* byte 0 and 1 is packet length in little endian */ +/* byte 2 is receive status */ +#define RSR_DRIBBLE 0x10 +#define RSR_CRC 0x08 +#define RSR_RUNT 0x04 +#define RSR_LONG 0x02 +#define RSR_MULTI 0x01 + +#define RSR_ERRORS \ + (RSR_DRIBBLE | RSR_CRC | RSR_RUNT | RSR_LONG | RSR_MULTI) + +#define RSR_BITS \ + "\020" \ + "\005DRIBBLE" \ + "\004CRC" \ + "\003RUNT" \ + "\002LONG" \ + "\001MULTI" +/* byte 3 is reserved */ + +/* TEST register: offset 80 */ + +#endif /* __ADM8511_H__ */ diff --git a/usr/src/uts/common/io/upf/upf_usbgem.c b/usr/src/uts/common/io/upf/upf_usbgem.c new file mode 100644 index 0000000000..fe7266a1a8 --- /dev/null +++ b/usr/src/uts/common/io/upf/upf_usbgem.c @@ -0,0 +1,1211 @@ +/* + * 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 + * and/or other materials provided with the distribution. + * + * 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/types.h> +#include <sys/conf.h> +#include <sys/debug.h> +#include <sys/kmem.h> +#include <sys/modctl.h> +#include <sys/errno.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/byteorder.h> + +/* ethernet stuff */ +#include <sys/ethernet.h> + +/* interface card depend stuff */ +#include <sys/stropts.h> +#include <sys/stream.h> +#include <sys/strlog.h> +#include <sys/usb/usba.h> +#include "usbgem.h" + +/* hardware stuff */ +#include "usbgem_mii.h" +#include "adm8511reg.h" + +char ident[] = "pegasus usbnic driver v" VERSION; + +/* + * Useful macros + */ +#define CHECK_AND_JUMP(val, label) \ + if ((val) != USB_SUCCESS) { goto label; } + +/* + * Debugging + */ +#ifdef DEBUG_LEVEL +static int upf_debug = DEBUG_LEVEL; +#define DPRINTF(n, args) if (upf_debug > (n)) cmn_err args +#else +#define DPRINTF(n, args) +#endif + +/* + * Our configration for ADMtek Pegasus/PegasusII + */ +/* timeouts */ +#define ONESEC (drv_usectohz(1*1000000)) + +/* + * Local device definitions + */ +struct upf_dev { + /* + * Misc HW information + */ + uint8_t ec[3]; + uint8_t mac_addr[ETHERADDRL]; + int chip_type; +#define CHIP_AN986 1 /* avoid 0 */ +#define CHIP_ADM8511 2 /* including adm8515 */ +#define CHIP_ADM8513 3 + boolean_t phy_init_done; + uint8_t last_link_state; + + uint16_t vid; /* vendor id */ + uint16_t pid; /* product id */ +}; + +/* + * private functions + */ + +/* mii operations */ +static uint16_t upf_mii_read(struct usbgem_dev *, uint_t, int *errp); +static void upf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp); + +/* 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 */ +static mblk_t *upf_tx_make_packet(struct usbgem_dev *, mblk_t *); +static mblk_t *upf_rx_make_packet(struct usbgem_dev *, mblk_t *); + +/* interrupt handler */ +static void upf_interrupt(struct usbgem_dev *, mblk_t *); + +/* =============================================================== */ +/* + * I/O functions + */ +/* =============================================================== */ +#define UPF_REQ_GET_REGISTER 0xf0 +#define UPF_REQ_SET_REGISTER 0xf1 +#define OUTB(dp, p, v, errp, label) \ + if ((*(errp) = usbgem_ctrl_out((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ UPF_REQ_SET_REGISTER, \ + /* wValue */ (v), \ + /* wIndex */ (p), \ + /* wLength */ 1, \ + /* buf */ NULL, \ + /* size */ 0)) != USB_SUCCESS) goto label; + +#define OUTW(dp, p, v, errp, label) \ + if ((*(errp) = usbgem_ctrl_out_val((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ UPF_REQ_SET_REGISTER, \ + /* wValue */ 0, \ + /* wIndex */ (p), \ + /* wLength */ 2, \ + /* value */ (v))) != USB_SUCCESS) goto label + +#define OUTS(dp, p, buf, len, errp, label) \ + if ((*(errp) = usbgem_ctrl_out((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ UPF_REQ_SET_REGISTER, \ + /* wValue */ 0, \ + /* wIndex */ (p), \ + /* wLength */ (len), \ + /* buf */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label + +#define INB(dp, p, vp, errp, label) \ + if ((*(errp) = usbgem_ctrl_in_val((dp), \ + /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ UPF_REQ_GET_REGISTER, \ + /* wValue */ 0, \ + /* wIndex */ (p), \ + /* wLength */ 1, \ + /* valuep */ (vp))) != USB_SUCCESS) goto label + +#define INW(dp, p, vp, errp, label) \ + if ((*(errp) = usbgem_ctrl_in_val((dp), \ + /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ UPF_REQ_GET_REGISTER, \ + /* wValue */ 0, \ + /* wIndex */ (p), \ + /* wLength */ 2, \ + /* valuep */ (vp))) != USB_SUCCESS) goto label + +#define INS(dp, p, buf, len, errp, label) \ + if ((*(errp) = usbgem_ctrl_in((dp), \ + /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ UPF_REQ_GET_REGISTER, \ + /* wValue */ 0, \ + /* wIndex */ (p), \ + /* wLength */ (len), \ + /* buf */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label + +/* =============================================================== */ +/* + * Hardware manupilation + */ +/* =============================================================== */ +static int +upf_reset_chip(struct usbgem_dev *dp) +{ + int i; + uint8_t val; + int err; + struct upf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + bzero(lp->mac_addr, sizeof (lp->mac_addr)); + + lp->ec[1] = 0; + OUTB(dp, EC1, EC1_RM, &err, usberr); + + for (i = 0; i < 1000; i++) { + INB(dp, EC1, &val, &err, usberr); + if ((val & EC1_RM) == 0) { + lp->ec[1] = val; + return (USB_SUCCESS); + } + drv_usecwait(10); + } + + /* time out */ + cmn_err(CE_WARN, "!%s: failed to reset: timeout", dp->name); + return (USB_FAILURE); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__); + return (USB_FAILURE); +} + +/* + * Setup an986/adm8511/adm8513/adm8515 + */ +static int +upf_init_chip(struct usbgem_dev *dp) +{ + uint64_t zero64 = 0; + int err = USB_SUCCESS; + struct upf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* ethernet control register 0 */ + lp->ec[0] |= EC0_RXSA | EC0_RXCS; + OUTB(dp, EC0, lp->ec[0], &err, usberr); + + /* ethernet control reg1: will be set later in set_rx_filter() */ + + /* ethernet control register 2: will be set later in set_rx_filter() */ + INB(dp, EC2, &lp->ec[2], &err, usberr); + lp->ec[2] |= EC2_RXBP | EC2_EP3RC; +#ifdef CONFIG_VLAN + if (dp->misc_flag & USBGEM_VLAN) { + lp->ec[2] |= EC2_MEPL; + } +#endif + OUTB(dp, EC2, lp->ec[2], &err, usberr); + + /* Multicast address hash: clear */ + OUTS(dp, MA, &zero64, 8, &err, usberr); + + /* Ethernet ID : will be set later in upf_set_rx_filter() */ + + /* PAUSE timer */ + OUTB(dp, PAUSETIMER, 0x1f, &err, usberr); + + /* 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 */ + if (lp->chip_type != CHIP_AN986) { + /* use 24K internal sram, 16pkts in fifo */ + OUTB(dp, RXFC, 0, &err, usberr); + } + + /* BIST contror: do nothing */ + err = upf_set_media(dp); + CHECK_AND_JUMP(err, usberr); + + DPRINTF(2, (CE_CONT, "!%s: %s: end (success)", dp->name, __func__)); + return (USB_SUCCESS); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr(%d) detected", + dp->name, __func__, err); + return (err); +} + +static int +upf_start_chip(struct usbgem_dev *dp) +{ + int err = USB_SUCCESS; + struct upf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* enable RX and TX */ + lp->ec[0] |= EC0_TXE | EC0_RXE; + OUTB(dp, EC0, lp->ec[0], &err, usberr); + return (USB_SUCCESS); + +usberr: + cmn_err(CE_WARN, "!%s: %s: usberr(%d) detected", + dp->name, __func__, err); + return (err); +} + +static int +upf_stop_chip(struct usbgem_dev *dp) +{ + int err; + struct upf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* disable RX and TX */ + lp->ec[0] &= ~(EC0_TXE | EC0_RXE); + OUTB(dp, EC0, lp->ec[0], &err, usberr); + + return (USB_SUCCESS); + +usberr: + cmn_err(CE_WARN, "!%s: %s: usberr(%d) detected", + dp->name, __func__, err); + return (err); +} + +static int +upf_get_stats(struct usbgem_dev *dp) +{ + /* do nothing */ + return (USB_SUCCESS); +} + +static uint_t +upf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr) +{ + /* hash table is 64 = 2^6 bit width */ + return (usbgem_ether_crc_le(addr) & 0x3f); +} + +static int +upf_set_rx_filter(struct usbgem_dev *dp) +{ + int i; + int err; +#ifdef DEBUG_LEVEL + uint8_t reg0; + uint8_t reg1; + uint8_t reg2; +#endif + struct upf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called, rxmode:%b", + dp->name, __func__, dp->rxmode, RXMODE_BITS)); + + /* reset rx mode */ + lp->ec[0] &= ~EC0_RXMA; + lp->ec[2] &= ~EC2_PROM; + + if (dp->rxmode & RXMODE_PROMISC) { + /* promiscious mode implies all multicast and all physical */ + lp->ec[0] |= EC0_RXMA; + lp->ec[2] |= EC2_PROM; + } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 0) { + /* XXX - multicast hash table didin't work */ + /* accept all multicast packets */ + lp->ec[0] |= EC0_RXMA; + } + + if (bcmp(dp->cur_addr.ether_addr_octet, + lp->mac_addr, ETHERADDRL) != 0) { + + /* need to update mac address */ + bcopy(dp->cur_addr.ether_addr_octet, + lp->mac_addr, ETHERADDRL); + OUTS(dp, EID, + lp->mac_addr, ETHERADDRL, &err, usberr); + } + + /* update rx mode */ + OUTS(dp, EC0, lp->ec, 3, &err, usberr); + +#if DEBUG_LEVEL > 0 + INB(dp, EC0, ®0, &err, usberr); + INB(dp, EC1, ®1, &err, usberr); + INB(dp, EC2, ®2, &err, usberr); + + cmn_err(CE_CONT, "!%s: %s: returned, ec:%b %b %b", + dp->name, __func__, + reg0, EC0_BITS, reg1, EC1_BITS, reg2, EC2_BITS); +#endif + return (USB_SUCCESS); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__); + return (err); +} + +static int +upf_set_media(struct usbgem_dev *dp) +{ + int err; + struct upf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + lp->ec[1] &= ~(EC1_FD | EC1_100M); + + /* select duplex */ + if (dp->full_duplex) { + lp->ec[1] |= EC1_FD; + } + + /* select speed */ + if (dp->speed == USBGEM_SPD_100) { + lp->ec[1] |= EC1_100M; + } + + /* rx flow control */ + switch (dp->flow_control) { + case FLOW_CONTROL_SYMMETRIC: + case FLOW_CONTROL_RX_PAUSE: + lp->ec[0] |= EC0_RXFCE; + break; + + default: + lp->ec[0] &= ~EC0_RXFCE; + break; + } + + /* tx flow control */ + switch (dp->flow_control) { + case FLOW_CONTROL_SYMMETRIC: + case FLOW_CONTROL_TX_PAUSE: + if (lp->chip_type != CHIP_AN986) { + /* pegasus II has internal 24k fifo */ + OUTB(dp, ORFBFC, + (12 << ORFBFC_RXS_SHIFT) | ORFBFC_FCRXS, + &err, usberr); + + /* 16 packts can be stored in rx fifo */ + OUTB(dp, RPNBFC_PN, + (8 << RPNBFC_PN_SHIFT) | RPNBFC_FCP, + &err, usberr); + } else { + /* an986 has external 32k fifo */ + OUTB(dp, ORFBFC, + (16 << ORFBFC_RXS_SHIFT) | ORFBFC_FCRXS, + &err, usberr); + + /* AN986 fails to link up when RPNBFC is enabled */ + OUTB(dp, RPNBFC, 0, &err, usberr); + } + break; + + default: + OUTB(dp, ORFBFC, 0, &err, usberr); + OUTB(dp, RPNBFC, 0, &err, usberr); + break; + } + + /* update ether control registers */ + OUTS(dp, EC0, lp->ec, 2, &err, usberr); + DPRINTF(0, (CE_CONT, "!%s: %s: returned, ec0:%b, ec1:%b", + dp->name, __func__, lp->ec[0], EC0_BITS, lp->ec[1], EC1_BITS)); + + return (USB_SUCCESS); + +usberr: + cmn_err(CE_WARN, "%s: %s: failed to write ec1", dp->name, __func__); + return (err); +} + +/* + * send/receive packet check + */ +static mblk_t * +upf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + size_t len; + mblk_t *new; + mblk_t *tp; + uint8_t *bp; + uint8_t *last_pos; + int msglen; + + DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + len = msgdsize(mp); + if (len < ETHERMIN) { + len = ETHERMIN; + } + + /* allocate msg block */ + msglen = len + sizeof (uint16_t); + + /* avoid usb controller bug */ + if ((msglen & 0x3f) == 0) { + /* add a header for additional 0-length usb message */ + msglen += sizeof (uint16_t); + } + + if ((new = allocb(msglen, 0)) == NULL) { + return (NULL); + } + + /* copy contents of the buffer */ + new->b_wptr = new->b_rptr + msglen; + bp = new->b_rptr; + + /* the nic requires a two byte header of the packet size */ + bp[0] = (uint8_t)len; + bp[1] = (uint8_t)(len >> 8); + bp += sizeof (uint16_t); + + /* copy the payload */ + for (tp = mp; tp; tp = tp->b_cont) { + len = (uintptr_t)tp->b_wptr - (uintptr_t)tp->b_rptr; + if (len > 0) { + bcopy(tp->b_rptr, bp, len); + bp += len; + } + } + + /* clear ethernet pads and additional usb header if we have */ + last_pos = new->b_wptr; + while (bp < last_pos) { + *bp++ = 0; + } + + return (new); +} + +static void +upf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n) +{ + int i; + + for (i = 0; i < n; i += 8, bp += 8) { + cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x", + bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]); + } +} + +static mblk_t * +upf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + uint8_t *p; + uint16_t rxhd; + uint_t len; + uint8_t rsr; + struct upf_dev *lp = dp->private; + + ASSERT(mp != NULL); + +#ifdef DEBUG_LEVEL + len = msgdsize(mp); + DPRINTF(2, (CE_CONT, "!%s: time:%d %s: cont:%p", + dp->name, ddi_get_lbolt(), __func__, len, mp->b_cont)); + + if (upf_debug > 3) { + upf_dump_packet(dp, mp->b_rptr, max(6, len)); + } +#endif + /* get the length of Rx packet */ + p = mp->b_wptr - 4; + rsr = p[3]; + if (lp->chip_type == CHIP_ADM8513) { + /* As Rx packets from ADM8513 have two byte header, remove it */ + p = mp->b_rptr; + len = ((p[1] << 8) | p[0]) & 0x0fff; + mp->b_rptr += 2; + } else { + len = (((p[1] << 8) | p[0]) & 0x0fff) - ETHERFCSL - 4; + } + + DPRINTF(2, (CE_CONT, "!%s: %s: rsr:%b len:%d", + dp->name, __func__, rsr, RSR_BITS, len)); + + /* check if error happen */ + if (rsr & RSR_ERRORS) { + DPRINTF(0, (CE_CONT, "!%s: rsr:%b", dp->name, rsr, RSR_BITS)); + if (rsr & (RSR_CRC | RSR_DRIBBLE)) { + dp->stats.frame++; + } + if (rsr & RSR_LONG) { + dp->stats.frame_too_long++; + } + if (rsr & RSR_RUNT) { + dp->stats.runt++; + } + + dp->stats.errrcv++; + return (NULL); + } +#ifndef CONFIG_VLAN + /* check packet size */ + if (len > ETHERMAX) { + /* too long */ + dp->stats.frame_too_long++; + dp->stats.errrcv++; + return (NULL); + } else if (len < ETHERMIN) { + dp->stats.runt++; + dp->stats.errrcv++; + return (NULL); + } +#endif + /* remove tailing crc and rx status fields */ + mp->b_wptr = mp->b_rptr + len; + ASSERT(mp->b_next == NULL); + return (mp); +} + +/* + * Device depend interrupt handler + */ +static void +upf_interrupt(struct usbgem_dev *dp, mblk_t *mp) +{ + uint8_t *bp; + struct upf_dev *lp = dp->private; + + bp = mp->b_rptr; + + DPRINTF(2, (CE_CONT, + "!%s: %s: size:%d, %02x %02x %02x %02x %02x %02x %02x %02x", + dp->name, __func__, mp->b_wptr - mp->b_rptr, + bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7])); + + if ((lp->last_link_state ^ bp[5]) & 1) { + DPRINTF(1, (CE_CONT, "!%s:%s link status changed:", + dp->name, __func__)); + usbgem_mii_update_link(dp); + } + + lp->last_link_state = bp[5] & 1; +} + +/* + * MII Interfaces + */ +static uint16_t +upf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp) +{ + uint8_t phyctrl; + uint16_t val; + int i; + + DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d", + dp->name, __func__, index)); + ASSERT(index >= 0 && index < 32); + + *errp = USB_SUCCESS; + + /* set PHYADDR */ + OUTB(dp, PHYA, dp->mii_phy_addr, errp, usberr); + + /* Initiate MII read transaction */ + OUTB(dp, PHYAC, index | PHYAC_RDPHY, errp, usberr); + + for (i = 0; i < 100; i++) { + INB(dp, PHYAC, &phyctrl, errp, usberr); + if (phyctrl & PHYAC_DO) { + /* done */ + INW(dp, PHYD, &val, errp, usberr); + DPRINTF(4, (CE_CONT, "!%s: %s: return %04x", + dp->name, __func__, val)); + return (val); + } + drv_usecwait(10); + } + /* timeout */ + cmn_err(CE_WARN, "!%s: %s: timeout detected", dp->name, __func__); + *errp = USB_FAILURE; + return (0); + +usberr: + cmn_err(CE_CONT, + "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp); + return (0); +} + +static void +upf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp) +{ + int i; + uint8_t phyctrl; + + DPRINTF(4, (CE_CONT, "!%s: %s called index:%d val:0x%04x", + dp->name, __func__, index, val)); + ASSERT(index >= 0 && index < 32); + + *errp = USB_SUCCESS; + + OUTW(dp, PHYD, val, errp, usberr); + OUTB(dp, PHYA, dp->mii_phy_addr, errp, usberr); + OUTB(dp, PHYAC, index | PHYAC_WRPHY, errp, usberr); + + for (i = 0; i < 100; i++) { + INB(dp, PHYAC, &phyctrl, errp, usberr); + if (phyctrl & PHYAC_DO) { + /* done */ + return; + } + drv_usecwait(10); + } + + /* time out */ + cmn_err(CE_WARN, "!%s: %s: timeout detected", dp->name, __func__); + *errp = USB_FAILURE; + return; + +usberr: + cmn_err(CE_CONT, + "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp); +} + + +static int +upf_enable_phy(struct usbgem_dev *dp) +{ + uint8_t val; + int err; + struct upf_dev *lp = dp->private; + + /* + * first, try to enable internal phy + */ + INB(dp, IPHYC, &val, &err, usberr); + val = (val | IPHYC_EPHY) & ~IPHYC_PHYR; + OUTB(dp, IPHYC, val, &err, usberr); + + INB(dp, IPHYC, &val, &err, usberr); + DPRINTF(0, (CE_CONT, "!%s: %s: IPHYC: %b", + dp->name, __func__, val, IPHYC_BITS)); + if (val) { + /* reset internal phy */ + OUTB(dp, IPHYC, val | IPHYC_PHYR, &err, usberr); + OUTB(dp, IPHYC, val, &err, usberr); + delay(drv_usectohz(10000)); + + /* identify the chip generation */ + OUTB(dp, 0x83, 0xa5, &err, usberr); + INB(dp, 0x83, &val, &err, usberr); + if (val == 0xa5) { + lp->chip_type = CHIP_ADM8513; + } else { + /* adm8511 or adm8515 */ + lp->chip_type = CHIP_ADM8511; + } + dp->ugc.usbgc_mii_hw_link_detection = B_TRUE; + } 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. + */ + lp->chip_type = CHIP_AN986; + + /* reset external phy */ + /* output port#0 L, port#1 L */ + OUTB(dp, GPIO10, GPIO10_0O | GPIO10_0OE, &err, usberr); + + /* output port#0 H, port#1 L */ + OUTB(dp, GPIO10, + GPIO10_0O | GPIO10_0OE | GPIO10_1OE, &err, usberr); + + /* hw link detection doesn't work correctly */ + dp->ugc.usbgc_mii_hw_link_detection = B_FALSE; + } + + return (USB_SUCCESS); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__); + return (USB_FAILURE); +} + +static int +upf_mii_probe(struct usbgem_dev *dp) +{ + int err; + uint16_t val; + struct upf_dev *lp = dp->private; + + if (!lp->phy_init_done) { + upf_enable_phy(dp); + lp->phy_init_done = B_TRUE; + } + + return (usbgem_mii_probe_default(dp)); +} + +static int +upf_mii_init(struct usbgem_dev *dp) +{ + uint16_t val; + int err = USB_SUCCESS; + struct upf_dev *lp = dp->private; + + if (!lp->phy_init_done) { + upf_enable_phy(dp); + } + lp->phy_init_done = B_FALSE; + + if (lp->chip_type == CHIP_AN986 && + (lp->vid == 0x0db7 /* elecom */ || + lp->vid == 0x066b /* linksys */ || + lp->vid == 0x077b /* linksys */ || + lp->vid == 0x2001 /* dlink */)) { + /* special treatment for Linksys products */ + val = upf_mii_read(dp, 0x1b, &err) | 0x4; + upf_mii_write(dp, 0x1b, val, &err); + } + return (err); +} + +/* ======================================================== */ +/* + * OS depend (device driver DKI) routine + */ +/* ======================================================== */ +static uint16_t +upf_read_eeprom(struct usbgem_dev *dp, int index, int *errp) +{ + int i; + uint8_t eectrl; + uint16_t data; + + *errp = USB_SUCCESS; + + OUTB(dp, EECTRL, 0, errp, usberr); + + OUTB(dp, EEOFFSET, index, errp, usberr); + OUTB(dp, EECTRL, EECTRL_RD, errp, usberr); + + for (i = 0; i < 100; i++) { + INB(dp, EECTRL, &eectrl, errp, usberr); + if (eectrl & EECTRL_DONE) { + INW(dp, EEDATA, &data, errp, usberr); + return (data); + } + drv_usecwait(10); + } + + /* time out */ + *errp = USB_FAILURE; + return (0); + +usberr: + cmn_err(CE_CONT, + "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp); + return (0); +} + +static void +upf_eeprom_dump(struct usbgem_dev *dp, int size) +{ + int i; + int err; + + cmn_err(CE_CONT, "!%s: %s dump:", dp->name, __func__); + + for (i = 0; i < size; i += 4) { + cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x", + i*2, + upf_read_eeprom(dp, i + 0, &err), + upf_read_eeprom(dp, i + 1, &err), + upf_read_eeprom(dp, i + 2, &err), + upf_read_eeprom(dp, i + 3, &err)); + } +} + +static int +upf_attach_chip(struct usbgem_dev *dp) +{ + int i; + int err; + uint16_t val; + uint8_t *mac; + struct upf_dev *lp = dp->private; + + /* + * Read mac address from EEPROM + */ + mac = dp->dev_addr.ether_addr_octet; + for (i = 0; i < 3; i++) { + val = upf_read_eeprom(dp, i, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + mac[i*2+0] = (uint8_t)val; + mac[i*2+1] = (uint8_t)(val >> 8); + } + + DPRINTF(0, (CE_CONT, + "%s: %s: mac: %02x:%02x:%02x:%02x:%02x:%02x", + dp->name, __func__, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); + + dp->misc_flag = 0; +#ifdef CONFIG_VLAN + dp->misc_flag |= USBGEM_VLAN; +#endif +#if DEBUG_LEVEL > 3 + upf_eeprom_dump(dp, 0x80); +#endif + return (USB_SUCCESS); + +usberr: + cmn_err(CE_WARN, "!%s: %s: usb error detected", dp->name, __func__); + return (USB_FAILURE); +} + +static int +upfattach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int i; + ddi_iblock_cookie_t c; + int ret; + int unit; + uint32_t tcr; + int len; + const char *drv_name; + struct usbgem_dev *dp; + void *base; + struct usbgem_conf *ugcp; + struct upf_dev *lp; + + unit = ddi_get_instance(dip); + drv_name = ddi_driver_name(dip); + + DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d", + drv_name, unit, __func__, cmd)); + + if (cmd == DDI_ATTACH) { + /* + * construct usbgem configration + */ + ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP); + + /* name */ + (void) sprintf(ugcp->usbgc_name, "%s%d", drv_name, unit); + ugcp->usbgc_ppa = unit; + + ugcp->usbgc_ifnum = 0; + ugcp->usbgc_alt = 0; + + ugcp->usbgc_tx_list_max = 16; + + ugcp->usbgc_rx_header_len = 4; + ugcp->usbgc_rx_list_max = 64; + + /* time out parameters */ + ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT; + ugcp->usbgc_tx_timeout_interval = USBGEM_TX_TIMEOUT_INTERVAL; + + /* flow control */ + ugcp->usbgc_flow_control = FLOW_CONTROL_NONE; + ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE; + + /* MII timeout parameters */ + ugcp->usbgc_mii_link_watch_interval = ONESEC; + ugcp->usbgc_mii_an_watch_interval = ONESEC/5; + ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */ + ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */ + ugcp->usbgc_mii_an_wait = MII_AN_TIMEOUT/2; + ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT; + ugcp->usbgc_mii_an_delay = ONESEC/10; + + ugcp->usbgc_mii_linkdown_action = MII_ACTION_RESET; + ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET; + ugcp->usbgc_mii_dont_reset = B_FALSE; + + /* I/O methods */ + + /* mac operation */ + ugcp->usbgc_attach_chip = &upf_attach_chip; + ugcp->usbgc_reset_chip = &upf_reset_chip; + ugcp->usbgc_init_chip = &upf_init_chip; + ugcp->usbgc_start_chip = &upf_start_chip; + ugcp->usbgc_stop_chip = &upf_stop_chip; + ugcp->usbgc_multicast_hash = &upf_mcast_hash; + + ugcp->usbgc_set_rx_filter = &upf_set_rx_filter; + ugcp->usbgc_set_media = &upf_set_media; + ugcp->usbgc_get_stats = &upf_get_stats; + ugcp->usbgc_interrupt = &upf_interrupt; + + /* packet operation */ + ugcp->usbgc_tx_make_packet = &upf_tx_make_packet; + ugcp->usbgc_rx_make_packet = &upf_rx_make_packet; + + /* mii operations */ + ugcp->usbgc_mii_probe = &upf_mii_probe; + ugcp->usbgc_mii_init = &upf_mii_init; + ugcp->usbgc_mii_config = &usbgem_mii_config_default; + ugcp->usbgc_mii_read = &upf_mii_read; + ugcp->usbgc_mii_write = &upf_mii_write; + + /* mtu */ + ugcp->usbgc_min_mtu = ETHERMTU; + ugcp->usbgc_max_mtu = ETHERMTU; + ugcp->usbgc_default_mtu = ETHERMTU; + + lp = kmem_zalloc(sizeof (struct upf_dev), KM_SLEEP); + + lp->vid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "usb-vendor-id", -1); + lp->pid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "usb-product-id", -1); + + dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct upf_dev)); + + kmem_free(ugcp, sizeof (*ugcp)); + + if (dp != NULL) { + return (DDI_SUCCESS); + } + +err_free_mem: + kmem_free(lp, sizeof (struct upf_dev)); +err_close_pipe: +err: + return (DDI_FAILURE); + } + if (cmd == DDI_RESUME) { + dp = USBGEM_GET_DEV(dip); + lp = dp->private; + lp->phy_init_done = B_FALSE; + + return (usbgem_resume(dip)); + } + return (DDI_FAILURE); +} + +static int +upfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int ret; + + if (cmd == DDI_DETACH) { + ret = usbgem_do_detach(dip); + 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 +USBGEM_STREAM_OPS(upf_ops, upfattach, upfdetach); +#else +static struct module_info upfminfo = { + 0, /* mi_idnum */ + "upf", /* mi_idname */ + 0, /* mi_minpsz */ + ETHERMTU, /* mi_maxpsz */ + 32*1024, /* mi_hiwat */ + 1, /* mi_lowat */ +}; + +static struct qinit upfrinit = { + (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 */ +}; + +static struct qinit upfwinit = { + 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 */ +}; + +static struct streamtab upf_info = { + &upfrinit, /* st_rdinit */ + &upfwinit, /* st_wrinit */ + NULL, /* st_muxrinit */ + NULL /* st_muxwrinit */ +}; + +static struct cb_ops cb_upf_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 */ + &upf_info, /* cb_stream */ + D_MP /* cb_flag */ +}; + +static struct dev_ops upf_ops = { + 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 +static struct modldrv modldrv = { + &mod_driverops, /* Type of module. This one is a driver */ + ident, + &upf_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, &modldrv, NULL +}; + +/* ======================================================== */ +/* + * _init : done + */ +/* ======================================================== */ +int +_init(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!upf: _init: called")); + + status = usbgem_mod_init(&upf_ops, "upf"); + if (status != DDI_SUCCESS) { + return (status); + } + status = mod_install(&modlinkage); + if (status != DDI_SUCCESS) { + usbgem_mod_fini(&upf_ops); + } + return (status); +} + +/* + * _fini : done + */ +int +_fini(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!upf: _fini: called")); + status = mod_remove(&modlinkage); + if (status == DDI_SUCCESS) { + usbgem_mod_fini(&upf_ops); + } + return (status); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} diff --git a/usr/src/uts/common/io/urf/THIRDPARTYLICENSE b/usr/src/uts/common/io/urf/THIRDPARTYLICENSE new file mode 100644 index 0000000000..32c62e38f9 --- /dev/null +++ b/usr/src/uts/common/io/urf/THIRDPARTYLICENSE @@ -0,0 +1,33 @@ +/* + * urf_usbgem.c : Realtek RTL8150 USB to Fast Ethernet Driver for Solaris + * + * Copyright (c) 2003-2012 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 + * and/or other materials provided with the distribution. + * + * 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. + */ + diff --git a/usr/src/uts/common/io/urf/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/urf/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..3d08b20683 --- /dev/null +++ b/usr/src/uts/common/io/urf/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +URF DRIVER diff --git a/usr/src/uts/common/io/urf/rtl8150reg.h b/usr/src/uts/common/io/urf/rtl8150reg.h new file mode 100644 index 0000000000..3f0e124ec7 --- /dev/null +++ b/usr/src/uts/common/io/urf/rtl8150reg.h @@ -0,0 +1,223 @@ +/* + * @(#)rtl8150reg.h 1.1 04/09/16 + * Macro definitions for Realtek 8150 USB to fast ethernet controller + * based on Realtek RTL8150 data sheet + * This file is public domain. Coded by M.Murayama (KHF04453@nifty.com) + */ + +#ifndef __RTL8150REG_H__ +#define __RTL8150REG_H__ + +/* + * Register offset + */ +#define IDR 0x0120 /* Base of ID registers */ +#define MAR 0x0126 /* Base of multicast registers */ +#define CR 0x012e /* Command register */ +#define TCR 0x012f /* Transmit Configuration register */ +#define RCR 0x0130 /* Receive Configuration register */ +#define TSR 0x0132 /* Transmit Status register */ +#define RSR 0x0133 /* Receive Status register */ +#define CON0 0x0135 /* Configuration register 0 */ +#define CON1 0x0136 /* Configuration register 1 */ +#define MSR 0x0137 /* Media Status register */ +#define PHYADD 0x0138 /* PHY address register */ +#define PHYDAT 0x0139 /* PHY data register */ +#define PHYCNT 0x013b /* PHY control register */ +#define GPPC 0x013d /* General purpose pin control */ +#define WAKECNT 0x013e /* Wake up event control */ +#define BMCR 0x0140 /* Basic Mode Control register */ +#define BMSR 0x0142 /* Basic Mode Status register */ +#define ANAR 0x0144 /* Auto Negotiation Advertisement register */ +#define ANLP 0x0146 /* Auto Negotiation Link Partner register */ +#define ANER 0x0148 /* Auto Negotiation Expansion register */ +#define NWAYT 0x014a /* Nway test register */ +#define CSCR 0x014c /* CS configuration register */ +#define CRC0 0x014e /* Power management register for wakeup frame0 */ +#define CRC1 0x0150 /* Power management register for wakeup frame1 */ +#define CRC2 0x0152 /* Power management register for wakeup frame2 */ +#define CRC3 0x0154 /* Power management register for wakeup frame3 */ +#define CRC4 0x0156 /* Power management register for wakeup frame4 */ +#define BYTEMASK0 0x0158 /* Power management wakeup frame0 bytemask */ +#define BYTEMASK1 0x0160 /* Power management wakeup frame1 bytemask */ +#define BYTEMASK2 0x0168 /* Power management wakeup frame2 bytemask */ +#define BYTEMASK3 0x0170 /* Power management wakeup frame3 bytemask */ +#define BYTEMASK4 0x0178 /* Power management wakeup frame4 bytemask */ +#define PHY1 0x0180 /* PHY parameter 1 */ +#define PHY2 0x0184 /* PHY parameter 2 */ +#define TW1 0x0186 /* Twister parameter 1 */ + +/* + * Bit field definitions + */ +/* CR : Command register (uint8_t) */ +#define CR_WEPROM 0x20 /* EEPROM write enable */ +#define CR_SOFT_RST 0x10 /* Reset */ +#define CR_RE 0x08 /* Ethernet receive enable */ +#define CR_TE 0x04 /* Ethernet transmit enable */ +#define CR_EP3CLREN 0x02 /* clear performance counter after EP3 */ +#define CR_AUTOLOAD 0x01 /* autoload contents of 93c46 */ + +#define CR_BITS "\020\006WEPROM\005SOFT_RST\004RE\003TE\002EP3CLREN\001AUTOLOAD" + +/* TCR: Transmit Configuration register */ +#define TCR_TXRR 0xc0 /* Tx retry count */ +#define TCR_TXRR_SHIFT 6 +#define TCR_IFG 0x18 /* Interframe Gap */ +#define TCR_IFG_SHIFT 3 +#define TCR_IFG_802_3 (3 << TCR_IFG_SHIFT) /* 802.3 standard */ +#define TCR_NOCRC 0x01 /* Inhibit Appending CRC */ + +#define TCR_BITS "\020\001NOCRC" + +/* Receive Configuration register */ +#define RCR_TAIL 0x0080 /* Rx header forward to host in CRC field */ +#define RCR_AER 0x0040 /* Accept Error packet */ +#define RCR_AR 0x0020 /* Accept runt */ +#define RCR_AM 0x0010 /* Accept multicast */ +#define RCR_AB 0x0008 /* Accept broadcast */ +#define RCR_AD 0x0004 /* Accept physical match */ +#define RCR_AAM 0x0002 /* Accept all Multicast */ +#define RCR_AAP 0x0001 /* Accept all physical */ + +#define RCR_ACCEPT_MODE \ + (RCR_AER | RCR_AR | RCR_AM | RCR_AB | RCR_AD | RCR_AAM | RCR_AAP) + +#define RCR_BITS \ + "\020\010TAIL\007AER\006AR\005AM\004AB\003AD\002AAM\001AAP" + +/* Transmit Status register */ + +#define TSR_ECOL 0x20 /* excessive collision indication */ +#define TSR_LCOL 0x10 /* late collision indication */ +#define TSR_LOSS_CRS 0x08 /* lost of carrier indication */ +#define TSR_JBR 0x04 /* jabber time out indication */ +#define TSR_BUF_EMPTY 0x02 /* Tx buffer is empty */ +#define TSR_BUF_FULL 0x01 /* Tx buffer is full */ + +#define TSR_BITS \ + "\020" \ + "\006ECOL" \ + "\005LCOL" \ + "\004LOSS_CRS" \ + "\003JBR" \ + "\002BUF_EMPTY" \ + "\001BUF_FULL" + +/* Receive status register in Rx packet field */ +#define RSR_WEVENT 0x80 /* Wakeup event indication */ +#define RSR_RX_BUF_FULL 0x40 /* Receive buffer full indication */ +#define RSR_LKCHG 0x20 /* Link change indication */ +#define RSR_RUNT 0x10 /* short packet indication */ +#define RSR_LONG 0x08 /* Long packet indication */ +#define RSR_CRC 0x04 /* CRC error indication */ +#define RSR_FAE 0x02 /* Frame alignment error */ +#define RSR_ROK 0x01 /* Receive OK indication */ + +#define RSR_ERRS (RSR_RUNT | RSR_LONG | RSR_CRC | RSR_FAE) +#define RSR_BITS \ + "\020" \ + "\010WEVENT" \ + "\007RX_BUF_FULL" \ + "\006LKCHG" \ + "\005RUNT" \ + "\004LONG" \ + "\003CRC" \ + "\002FAE" \ + "\001ROK" + +/* Config 0 */ + +#define CON0_SUSLED 0x80 +#define CON0_PARM_EN 0x40 /* parameter enable */ +#define CON0_LDPS 0x08 +#define CON0_MSEL 0x04 /* media select 1:MII, 0:auto */ +#define CON0_LEDS 0x03 /* LED pattern */ + +/* Config 1 */ +#define CON0_BWF 0x40 /* Broadcast wakeup function 1:on 0:off */ +#define CON0_MWF 0x20 /* Multicast wakeup function 1:on 0:off */ +#define CON0_UWF 0x10 /* Unicast wakeup function 1:on 0:off */ +#define CON0_LONGWF1 0x02 /* */ +#define CON0_LONGWF0 0x01 /* */ + + +/* MSR : Media Status register */ +#define MSR_TXFCE 0x80 /* Tx Flow control enable */ +#define MSR_RXFCE 0x40 /* Rx Flow control enable */ +#define MSR_DUPLEX 0x10 /* full duplex */ +#define MSR_SPEED_100 0x08 /* 100Mbps mode */ +#define MSR_LINK 0x04 /* link status */ +#define MSR_TXPF 0x02 /* 8150 sends pause packet */ +#define MSR_RXPF 0x01 /* 8150 is in backoff state */ + +#define MSR_BITS \ + "\020" \ + "\010TXFCE" \ + "\007RXFCE" \ + "\005DUPLEX" \ + "\004SPEED_100" \ + "\003LINK" \ + "\002TXPF" \ + "\001RXPF" + +/* MII PHY Address */ +#define PHYADD_MASK 0x1f + +/* MII PHY Data */ +#define PHYCNT_OWN 0x40 /* 8150 owns:1 not owns:0 */ +#define PHYCNT_RWCR 0x20 /* write:1 read:0 */ +#define PHYCNT_PHYOFF 0x1f + +/* BMCR (almost same with MII_CONTROL register) */ +#define BMCR_RESET 0x8000 /* PHY reset */ +#define BMCR_Spd_Set 0x2000 /* 100Mbps */ +#define BMCR_ANE 0x1000 /* auto negotiation enable */ +#define BMCR_RSA 0x0200 /* restart auto negotiation */ +#define BMCR_duplex 0x0100 /* 100Mbps */ + +/* Basic mode status register */ +/* Auto-negotiation Advertisement register */ +/* Auto-negotiation Link Partner Ability register */ +/* Auto-negotiation Expansion register */ + +/* Nway test register */ +#define NWAYT_NWLPBK 0x0080 +#define NWAYT_ENNWLE 0x0008 +#define NWAYT_FLAGABD 0x0004 +#define NWAYT_FLAGPDF 0x0002 +#define NWAYT_FLAGLSC 0x0001 + +/* CS configuration register */ +#define CS_TESTFUN 0x8000 /* */ +#define CS_LD 0x0200 /* */ +#define CS_HEARTBEAT 0x0100 /* */ +#define CS_JBEN 0x0080 /* */ +#define CS_F_LINK100 0x0040 /* */ +#define CS_F_CONNECT 0x0020 /* */ +#define CS_CON_STATUS 0x0008 /* */ +#define CS_CON_STATUS_EN 0x0004 /* */ +#define CS_PASS_SCR 0x0001 /* bypass scramble function */ + +/* + * header format of rx packet + */ +#define RXHD_MULT 0x8000 /* multicast packet */ +#define RXHD_PHYS 0x4000 /* physical match packet */ +#define RXHD_RUNT 0x2000 /* too short */ +#define RXHD_VALID 0x1000 /* packet is ok */ +#define RXHD_BYTECNT 0x0fff /* rx byte count */ + +#define RXHD_BITS \ + "\020" \ + "\020MULT" \ + "\017PHYS" \ + "\016RUNT" \ + "\015VALID" +/* + * Offset to EPROM contents + */ +#define URF_EEPROM_BASE 0x1200 +#define EPROM_EthernetID 0x0002 + +#endif /* __RTL8150REG_H__ */ diff --git a/usr/src/uts/common/io/urf/urf_usbgem.c b/usr/src/uts/common/io/urf/urf_usbgem.c new file mode 100644 index 0000000000..1098a8854d --- /dev/null +++ b/usr/src/uts/common/io/urf/urf_usbgem.c @@ -0,0 +1,1039 @@ +/* + * urf_usbgem.c : Realtek RTL8150 USB to Fast Ethernet Driver for Solaris + * + * Copyright (c) 2003-2012 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 + * and/or other materials provided with the distribution. + * + * 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 + */ + +/* minimum kernel headers for drivers */ +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/debug.h> +#include <sys/kmem.h> +#include <sys/modctl.h> +#include <sys/errno.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/byteorder.h> + +/* ethernet stuff */ +#include <sys/ethernet.h> + +/* interface card depend stuff */ +#include <sys/stropts.h> +#include <sys/stream.h> +#include <sys/strlog.h> +#include <sys/usb/usba.h> +#include "usbgem.h" +#include "usbgem_mii.h" +#include "rtl8150reg.h" + +char ident[] = "rtl8150 usbnic driver v" VERSION; + +/* + * Useful macros + */ +#define ROUNDUP2(x, y) (((x)+(y)-1) & ~((y)-1)) +#define CHECK_AND_JUMP(err, label) if (err != USB_SUCCESS) goto label + +/* + * Debugging + */ +#ifdef DEBUG_LEVEL +static int urf_debug = DEBUG_LEVEL; +#define DPRINTF(n, args) if (urf_debug > (n)) cmn_err args +#else +#define DPRINTF(n, args) +#endif + +/* + * Our configration for rtl8150 + */ +/* timeouts */ +#define ONESEC (drv_usectohz(1*1000000)) + +/* + * Local device definitions + */ +struct chip_info { + int flags; + char *name; + int type; +}; + +#define CHIPTABLESIZE (sizeof (chiptbl_8150) / sizeof (struct chip_info)) + +struct urf_dev { + /* + * Misc HW information + */ + struct chip_info *chip; + uint8_t cr; + uint8_t tsr; + uint16_t rcr; + uint8_t txok_cnt; +}; + +/* + * private functions + */ + +/* mii operations */ +static uint16_t urf_mii_read(struct usbgem_dev *, uint_t, int *errp); +static void urf_mii_write(struct usbgem_dev *, uint_t, uint16_t, int *errp); + +/* nic operations */ +static int urf_attach_chip(struct usbgem_dev *); +static int urf_reset_chip(struct usbgem_dev *); +static int urf_init_chip(struct usbgem_dev *); +static int urf_start_chip(struct usbgem_dev *); +static int urf_stop_chip(struct usbgem_dev *); +static int urf_set_media(struct usbgem_dev *); +static int urf_set_rx_filter(struct usbgem_dev *); +static int urf_get_stats(struct usbgem_dev *); + +/* packet operations */ +static mblk_t *urf_tx_make_packet(struct usbgem_dev *, mblk_t *); +static mblk_t *urf_rx_make_packet(struct usbgem_dev *, mblk_t *); + +/* =============================================================== */ +/* + * I/O functions + */ +/* =============================================================== */ +#define OUTB(dp, p, v, errp, label) \ + if ((*(errp) = usbgem_ctrl_out_val((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ USB_REQ_SET_ADDRESS, \ + /* wValue */ (p), \ + /* wIndex */ 0, \ + /* wLength */ 1, \ + /* value */ (v))) != USB_SUCCESS) goto label + +#define OUTW(dp, p, v, errp, label) \ + if ((*(errp) = usbgem_ctrl_out_val((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ USB_REQ_SET_ADDRESS, \ + /* wValue */ (p), \ + /* wIndex */ 0, \ + /* wLength */ 2, \ + /* value */ (v))) != USB_SUCCESS) goto label + +/* BEGIN CSTYLED */ +#define OUTS(dp, p, buf, len, errp, label) \ + if ((*(errp) = usbgem_ctrl_out((dp), \ + /* bmRequestType */ USB_DEV_REQ_HOST_TO_DEV \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ USB_REQ_SET_ADDRESS, \ + /* wValue */ (p), \ + /* wIndex */ 0, \ + /* wLength */ (len), \ + /* value */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label +/* END CSTYLED */ + +#define IN(dp, p, vp, errp, label) \ + if ((*(errp) = usbgem_ctrl_in_val((dp), \ + /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ USB_REQ_SET_ADDRESS, \ + /* wValue */ (p), \ + /* wIndex */ 0, \ + /* wLength */ sizeof ((*vp)), \ + /* valuep */ (vp))) != USB_SUCCESS) goto label + +#define INS(dp, p, buf, len, errp, label) \ + if ((*(errp) = usbgem_ctrl_in((dp), \ + /* bmRequestType */ USB_DEV_REQ_DEV_TO_HOST \ + | USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_RCPT_DEV, \ + /* bRequest */ USB_REQ_SET_ADDRESS, \ + /* wValue */ (p), \ + /* wIndex */ 0, \ + /* wLength */ (len), \ + /* valuep */ (buf), \ + /* size */ (len))) != USB_SUCCESS) goto label + +/* =============================================================== */ +/* + * variables + */ +/* =============================================================== */ +static int urf_ppa = 0; + +/* =============================================================== */ +/* + * Hardware manupilation + */ +/* =============================================================== */ +static int +urf_reset_chip(struct usbgem_dev *dp) +{ + int i; + int err; + uint8_t reg; + struct urf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + lp->cr = 0; + OUTB(dp, CR, lp->cr | CR_SOFT_RST, &err, usberr); + + for (i = 0; i < 100; i++) { + IN(dp, CR, ®, &err, usberr); + if ((reg & CR_SOFT_RST) == 0) { + return (USB_SUCCESS); + } + } + /* time out */ + cmn_err(CE_WARN, "%s: failed to reset: timeout", dp->name); + return (USB_FAILURE); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__); + return (USB_FAILURE); +} + +/* + * Setup rtl8150 + */ +static int +urf_init_chip(struct usbgem_dev *dp) +{ + int i; + uint32_t val; + int err; + struct urf_dev *lp = dp->private; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* ID registers: set later by urf_set_rx_filter */ + + /* Multicast registers: set later by urf_set_rx_filter */ + + /* Command register : Enable Tx and Rx before writing TCR and RCR */ + lp->cr |= CR_RE | CR_TE; + OUTB(dp, CR, lp->cr, &err, usberr); + + /* Transmit configration register : */ + OUTB(dp, TCR, TCR_IFG_802_3, &err, usberr); + + /* Receive configuration register : disable rx filter */ + lp->rcr = RCR_TAIL | RCR_AER | RCR_AR; + OUTW(dp, RCR, lp->rcr, &err, usberr); +#ifdef notdef + /* Media status register */ + err = urf_set_media(dp); + CHECK_AND_JUMP(err, usberr); +#endif + /* Configuration register 0: no need to change */ + + DPRINTF(2, (CE_CONT, "!%s: %s: end (success)", dp->name, __func__)); + return (USB_SUCCESS); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__); + return (USB_FAILURE); +} + +static int +urf_start_chip(struct usbgem_dev *dp) +{ + struct urf_dev *lp = dp->private; + + /* do nothing */ + return (USB_SUCCESS); +} + +static int +urf_stop_chip(struct usbgem_dev *dp) +{ + return (urf_reset_chip(dp)); +} + +static int +urf_get_stats(struct usbgem_dev *dp) +{ + /* do nothing */ + return (USB_SUCCESS); +} + +static uint_t +urf_mcast_hash(struct usbgem_dev *dp, const uint8_t *addr) +{ + return (usbgem_ether_crc_be(addr)); +} + +static int +urf_set_rx_filter(struct usbgem_dev *dp) +{ + int i; + uint16_t mode; + uint8_t mhash[8]; + int err; + int16_t rcr; + struct urf_dev *lp = dp->private; + + DPRINTF(2, (CE_CONT, "!%s: %s: called, rxmode:%x", + dp->name, __func__, dp->rxmode)); + + if (lp->rcr & (RCR_AB | RCR_AD | RCR_AAM | RCR_AAP | RCR_AM)) { +#ifdef notdef + /* disable rx filter before changing it. */ + lp->rcr &= ~(RCR_AB | RCR_AD | RCR_AAM | RCR_AAP | RCR_AM); + OUTW(dp, RCR, lp->rcr, &err, usberr); +#else + /* receive all packets while we change rx filter */ + OUTW(dp, RCR, lp->rcr | RCR_AAM | RCR_AAP, &err, usberr); +#endif + } + + mode = RCR_AB /* accept broadcast */ + | RCR_AD; /* accept physical match */ + bzero(mhash, sizeof (mhash)); + + if (dp->rxmode & RXMODE_PROMISC) { + /* promiscious mode implies all multicast and all physical */ + mode |= RCR_AAM | RCR_AAP; + } else if ((dp->rxmode & RXMODE_ALLMULTI) || dp->mc_count > 64/2) { + /* accept all multicast packets */ + mode |= RCR_AAM; + } else if (dp->mc_count > 0) { + /* + * make hash table to select interresting + * multicast address only. + */ + mode |= RCR_AM; + for (i = 0; i < dp->mc_count; i++) { + uint_t h; + /* hash table is 64 = 2^6 bit width */ + h = dp->mc_list[i].hash >> (32 - 6); + mhash[h / 8] |= 1 << (h % 8); + } + } + lp->rcr |= mode; + + /* set mac address */ + OUTS(dp, IDR, dp->cur_addr.ether_addr_octet, ETHERADDRL, &err, usberr); + + /* set multicast hash table */ + if (mode & RCR_AM) { + /* need to set up multicast hash table */ + OUTS(dp, MAR, mhash, sizeof (mhash), &err, usberr); + } + + OUTW(dp, RCR, lp->rcr, &err, usberr); + +#if DEBUG_LEVEL > 2 + IN(dp, RCR, &rcr, &err, usberr); + cmn_err(CE_CONT, "!%s: %s: rcr:%b returned", + dp->name, __func__, rcr, RCR_BITS); +#endif + return (USB_SUCCESS); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__); + return (USB_FAILURE); +} + +static int +urf_set_media(struct usbgem_dev *dp) +{ + uint8_t new; + uint8_t old; + int err; + struct urf_dev *lp = dp->private; + + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* select duplex: do nothing */ + + /* select speed: do nothing */ + + /* flow control */ + IN(dp, MSR, &old, &err, usberr); + + + /* setup flow control */ + new = old & ~(MSR_TXFCE | MSR_RXFCE); + switch (dp->flow_control) { + case FLOW_CONTROL_SYMMETRIC: + new |= MSR_TXFCE | MSR_RXFCE; + break; + + case FLOW_CONTROL_TX_PAUSE: + new |= MSR_TXFCE; + break; + + case FLOW_CONTROL_RX_PAUSE: + new |= MSR_RXFCE; + break; + + case FLOW_CONTROL_NONE: + default: + break; + } + + if (new != old) { + OUTB(dp, MSR, new, &err, usberr); + } + DPRINTF(2, (CE_CONT, "!%s: %s: returned", dp->name, __func__)); + return (USB_SUCCESS); + +usberr: + cmn_err(CE_NOTE, "!%s: %s: usberr detected", dp->name, __func__); + return (USB_FAILURE); +} + +/* + * send/receive packet check + */ +static mblk_t * +urf_tx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + size_t len; + mblk_t *new; + mblk_t *tp; + uint8_t *bp; + uint8_t *last_pos; + + len = msgdsize(mp); + + if (len < ETHERMIN || mp->b_cont != NULL || (len & 0x3f) == 0) { + /* + * re-allocate mp + */ + len = max(len, ETHERMIN); + + if ((len & 0x3f) == 0) { + /* workaround for buggy USB hba */ + len++; + } + + if ((new = allocb(len, 0)) == NULL) { + return (NULL); + } + + /* copy contents of the buffer */ + new->b_wptr = new->b_rptr + len; + bp = new->b_rptr; + for (tp = mp; tp; tp = tp->b_cont) { + len = (uintptr_t)tp->b_wptr - (uintptr_t)tp->b_rptr; + bcopy(tp->b_rptr, bp, len); + bp += len; + } + + last_pos = new->b_wptr; + while (bp < last_pos) { + *bp++ = 0; + } + + mp = new; + } + + return (mp); +} + +static void +urf_dump_packet(struct usbgem_dev *dp, uint8_t *bp, int n) +{ + int i; + + for (i = 0; i < n; i += 8, bp += 8) { + cmn_err(CE_CONT, "%02x %02x %02x %02x %02x %02x %02x %02x", + bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7]); + } +} + +static mblk_t * +urf_rx_make_packet(struct usbgem_dev *dp, mblk_t *mp) +{ + uint8_t *p; + uint16_t rxhd; + uint_t len; + + ASSERT(mp != NULL); + len = msgdsize(mp); +#ifdef DEBUG_LEVEL + DPRINTF(2, (CE_CONT, "!%s: time:%d %s: len:%d cont:%p", + dp->name, ddi_get_lbolt(), __func__, len, mp->b_cont)); + + if (urf_debug > 2) { + urf_dump_packet(dp, mp->b_rptr, max(6, len)); + } +#endif + if (len < ETHERMIN + ETHERFCSL) { + /* Too short */ + dp->stats.runt++; + dp->stats.errrcv++; + return (NULL); + } + + /* get Rx header which is placed at tail of the packet. */ + p = mp->b_wptr - 4; + rxhd = (p[1] << 8) | p[0]; + len = rxhd & RXHD_BYTECNT; + + DPRINTF(2, (CE_CONT, "!%s: %s: rsr:%b len:%d", + dp->name, __func__, rxhd, RXHD_BITS, len)); + + /* check if error happen */ + if ((rxhd & (RXHD_VALID)) == 0) { + DPRINTF(-1, (CE_CONT, "!%s: %s: rxhd:%b", + dp->name, __func__, rxhd, RXHD_BITS)); + if (rxhd & RXHD_RUNT) { + dp->stats.runt++; + } + + dp->stats.errrcv++; + return (NULL); + } +#ifdef notdef + /* check packet size */ + if (len > ETHERMAX + ETHERFCSL) { + /* too long */ + dp->stats.frame_too_long++; + dp->stats.errrcv++; + return (NULL); + } else if (len < ETHERMIN + ETHERFCSL) { + dp->stats.runt++; + dp->stats.errrcv++; + return (NULL); + } +#endif + /* remove tailing crc field */ + mp->b_wptr -= ETHERFCSL; + return (mp); +} + +/* + * MII Interfaces + */ +static uint16_t +urf_mii_read(struct usbgem_dev *dp, uint_t index, int *errp) +{ + int reg; + uint16_t val; + + DPRINTF(4, (CE_CONT, "!%s: %s: called, ix:%d", + dp->name, __func__, index)); + + *errp = USB_SUCCESS; + + switch (index) { + case MII_CONTROL: + reg = BMCR; + break; + + case MII_STATUS: + reg = BMSR; + break; + + case MII_AN_ADVERT: + reg = ANAR; + break; + + case MII_AN_LPABLE: + reg = ANLP; + break; + + case MII_AN_EXPANSION: + reg = ANER; + break; + + default: + return (0); + } + + IN(dp, reg, &val, errp, usberr); + + if (index == MII_STATUS) { + uint8_t msr; + /* + * Fix MII status register as it does't have LINKUP and + * MFPRMBLSUPR bits. + */ + IN(dp, MSR, &msr, errp, usberr); + + val |= (MII_STATUS_MFPRMBLSUPR | MII_STATUS_LINKUP); + if ((msr & MSR_LINK) == 0) { + val &= ~MII_STATUS_LINKUP; + } + } + + return (val); + +usberr: + cmn_err(CE_CONT, + "!%s: %s: usberr(%d) detected", dp->name, __func__, *errp); + + return (0); +} + +static void +urf_mii_write(struct usbgem_dev *dp, uint_t index, uint16_t val, int *errp) +{ + int reg; + + DPRINTF(5, (CE_CONT, "!%s: %s called", dp->name, __func__)); + + *errp = USB_SUCCESS; + + switch (index) { + case MII_CONTROL: + reg = BMCR; + break; + + case MII_STATUS: + reg = BMSR; + break; + + case MII_AN_ADVERT: + reg = ANAR; + break; + + case MII_AN_LPABLE: + reg = ANLP; + break; + + case MII_AN_EXPANSION: + reg = ANER; + break; + + default: + return; + } + + OUTW(dp, reg, val, errp, usberr); +usberr: + ; +} + +/* ======================================================== */ +/* + * OS depend (device driver DKI) routine + */ +/* ======================================================== */ +static void +urf_eeprom_dump(struct usbgem_dev *dp, int size) +{ + int i; + int err; + uint16_t w0, w1, w2, w3; + + cmn_err(CE_CONT, "!%s: eeprom dump:", dp->name); + for (i = URF_EEPROM_BASE; i < size + URF_EEPROM_BASE; i += 8) { + IN(dp, i + 0, &w0, &err, usberr); + IN(dp, i + 2, &w1, &err, usberr); + IN(dp, i + 4, &w2, &err, usberr); + IN(dp, i + 6, &w3, &err, usberr); + cmn_err(CE_CONT, "!0x%02x: 0x%04x 0x%04x 0x%04x 0x%04x", + i - URF_EEPROM_BASE, w0, w1, w2, w3); + } +usberr: + ; +} + +static int +urf_attach_chip(struct usbgem_dev *dp) +{ + int i; + uint8_t old; + uint_t new; + uint8_t reg; + int err; + struct urf_dev *lp = dp->private; + + /* + * setup flow control bit in eeprom + */ + IN(dp, URF_EEPROM_BASE + 9, &old, &err, usberr); + + DPRINTF(0, (CE_CONT, "!%s: eeprom offset 9: %02x", dp->name, old)); + + if (dp->ugc.usbgc_flow_control != FLOW_CONTROL_NONE) { + /* enable PAUSE bit */ + new = old | 0x04; + } else { + /* clear PAUSE bit */ + new = old & ~0x04; + } + if (new != old) { + /* make eeprom writable */ + OUTB(dp, CR, lp->cr | CR_WEPROM, &err, usberr); + + /* eerom allows only word access for writing */ + IN(dp, URF_EEPROM_BASE + 8, ®, &err, usberr); + new = (new << 8) | reg; + + OUTW(dp, URF_EEPROM_BASE + 8, new, &err, usberr); + + /* make eeprom non-writable */ + OUTB(dp, CR, lp->cr, &err, usberr); + } + + /* + * load EEPROM contents into nic + */ + OUTB(dp, CR, lp->cr | CR_AUTOLOAD, &err, usberr); + CHECK_AND_JUMP(err, usberr); + + for (i = 0; i < 100; i++) { + IN(dp, CR, ®, &err, usberr); + if ((reg & CR_AUTOLOAD) == 0) { + goto autoload_done; + } + } + /* timeout */ + cmn_err(CE_WARN, "%s: %s: failed to autoload: timeout", + dp->name, __func__); + goto usberr; + +autoload_done: + /* + * mac address in EEPROM has loaded to ID registers. + */ + INS(dp, IDR, dp->dev_addr.ether_addr_octet, ETHERADDRL, &err, usberr); + + /* no need to scan phy */ + dp->mii_phy_addr = -1; + +#if DEBUG_LEVEL > 2 + urf_eeprom_dump(dp, 0x80); +#endif + +#ifdef CONFIG_VLAN + dp->misc_flag = USBGEM_VLAN; +#endif + return (USB_SUCCESS); + +usberr: + cmn_err(CE_WARN, "%s: urf_attach_chip: usb error detected", dp->name); + return (USB_FAILURE); +} + +static int +urfattach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int i; + ddi_iblock_cookie_t c; + int ret; + int unit; + struct chip_info *p; + const char *drv_name; + struct usbgem_dev *dp; + void *base; + struct usbgem_conf *ugcp; + struct urf_dev *lp; + + unit = ddi_get_instance(dip); + drv_name = ddi_driver_name(dip); + + DPRINTF(3, (CE_CONT, "!%s%d: %s: called, cmd:%d", + drv_name, __func__, unit, cmd)); + + if (cmd == DDI_ATTACH) { + /* + * Check if the chip is supported. + */ + + /* + * Check the chip if it is really realtek rtl8150 + */ + + /* + * construct usbgem configration + */ + ugcp = kmem_zalloc(sizeof (*ugcp), KM_SLEEP); + + /* name */ + (void) sprintf(ugcp->usbgc_name, + "%s%d(ppa=%d)", drv_name, unit, urf_ppa); +#ifdef USBGEM_CONFIG_GLDv3 + ugcp->usbgc_ppa = urf_ppa; +#else + ugcp->usbgc_ppa = unit; +#endif + ugcp->usbgc_ifnum = 0; + ugcp->usbgc_alt = 0; + + ugcp->usbgc_tx_list_max = 16; + + /* the rx status partially replaces FCS */ + ugcp->usbgc_rx_header_len = 0; + ugcp->usbgc_rx_list_max = 64; + + /* time out parameters */ + ugcp->usbgc_tx_timeout = USBGEM_TX_TIMEOUT; + ugcp->usbgc_tx_timeout_interval = ONESEC; + + /* flow control */ + ugcp->usbgc_flow_control = FLOW_CONTROL_RX_PAUSE; + + /* MII timeout parameters */ + ugcp->usbgc_mii_link_watch_interval = ONESEC; + ugcp->usbgc_mii_an_watch_interval = ONESEC/5; + ugcp->usbgc_mii_reset_timeout = MII_RESET_TIMEOUT; /* 1 sec */ + ugcp->usbgc_mii_an_timeout = MII_AN_TIMEOUT; /* 5 sec */ + ugcp->usbgc_mii_an_wait = (25*ONESEC)/10; + ugcp->usbgc_mii_linkdown_timeout = MII_LINKDOWN_TIMEOUT; + + ugcp->usbgc_mii_an_delay = ONESEC/10; + ugcp->usbgc_mii_linkdown_action = MII_ACTION_RSA; + ugcp->usbgc_mii_linkdown_timeout_action = MII_ACTION_RESET; + ugcp->usbgc_mii_dont_reset = B_FALSE; + + /* I/O methods */ + + /* mac operation */ + ugcp->usbgc_attach_chip = &urf_attach_chip; + ugcp->usbgc_reset_chip = &urf_reset_chip; + ugcp->usbgc_init_chip = &urf_init_chip; + ugcp->usbgc_start_chip = &urf_start_chip; + ugcp->usbgc_stop_chip = &urf_stop_chip; + ugcp->usbgc_multicast_hash = &urf_mcast_hash; + + ugcp->usbgc_set_rx_filter = &urf_set_rx_filter; + ugcp->usbgc_set_media = &urf_set_media; + ugcp->usbgc_get_stats = &urf_get_stats; +#ifdef notdef + ugcp->usbgc_interrupt = &urf_interrupt; +#else + ugcp->usbgc_interrupt = NULL; +#endif + /* packet operation */ + ugcp->usbgc_tx_make_packet = &urf_tx_make_packet; + ugcp->usbgc_rx_make_packet = &urf_rx_make_packet; + + /* mii operations */ + ugcp->usbgc_mii_probe = &usbgem_mii_probe_default; + ugcp->usbgc_mii_init = &usbgem_mii_init_default; + ugcp->usbgc_mii_config = &usbgem_mii_config_default; + ugcp->usbgc_mii_read = &urf_mii_read; + ugcp->usbgc_mii_write = &urf_mii_write; + + /* mtu */ + ugcp->usbgc_min_mtu = ETHERMTU; + ugcp->usbgc_max_mtu = ETHERMTU; + ugcp->usbgc_default_mtu = ETHERMTU; + + lp = kmem_zalloc(sizeof (struct urf_dev), KM_SLEEP); + lp->chip = NULL; + + ddi_set_driver_private(dip, NULL); + + dp = usbgem_do_attach(dip, ugcp, lp, sizeof (struct urf_dev)); + + kmem_free(ugcp, sizeof (*ugcp)); + + if (dp != NULL) { + urf_ppa++; + return (DDI_SUCCESS); + } + +err_free_mem: + kmem_free(lp, sizeof (struct urf_dev)); +err_close_pipe: +err: + return (DDI_FAILURE); + } + if (cmd == DDI_RESUME) { + return (usbgem_resume(dip)); + } + return (DDI_FAILURE); +} + +static int +urfdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int ret; + + if (cmd == DDI_DETACH) { + ret = usbgem_do_detach(dip); + if (ret != DDI_SUCCESS) { + return (DDI_FAILURE); + } + urf_ppa--; + return (DDI_SUCCESS); + } + if (cmd == DDI_SUSPEND) { + return (usbgem_suspend(dip)); + } + return (DDI_FAILURE); +} + +/* ======================================================== */ +/* + * OS depend (loadable streams driver) routine + */ +/* ======================================================== */ +#ifdef USBGEM_CONFIG_GLDv3 +USBGEM_STREAM_OPS(urf_ops, urfattach, urfdetach); +#else +static struct module_info urfminfo = { + 0, /* mi_idnum */ + "urf", /* mi_idname */ + 0, /* mi_minpsz */ + ETHERMTU, /* mi_maxpsz */ + ETHERMTU*128, /* mi_hiwat */ + 1, /* mi_lowat */ +}; + +static struct qinit urfrinit = { + (int (*)()) NULL, /* qi_putp */ + usbgem_rsrv, /* qi_srvp */ + usbgem_open, /* qi_qopen */ + usbgem_close, /* qi_qclose */ + (int (*)()) NULL, /* qi_qadmin */ + &urfminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct qinit urfwinit = { + usbgem_wput, /* qi_putp */ + usbgem_wsrv, /* qi_srvp */ + (int (*)()) NULL, /* qi_qopen */ + (int (*)()) NULL, /* qi_qclose */ + (int (*)()) NULL, /* qi_qadmin */ + &urfminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct streamtab urf_info = { + &urfrinit, /* st_rdinit */ + &urfwinit, /* st_wrinit */ + NULL, /* st_muxrinit */ + NULL /* st_muxwrinit */ +}; + +static struct cb_ops cb_urf_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 */ + &urf_info, /* cb_stream */ + D_NEW|D_MP /* cb_flag */ +}; + +static struct dev_ops urf_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + usbgem_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + urfattach, /* devo_attach */ + urfdetach, /* devo_detach */ + nodev, /* devo_reset */ + &cb_urf_ops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + usbgem_power, /* devo_power */ +#if DEVO_REV >= 4 + usbgem_quiesce, /* devo_quiesce */ +#endif + +}; +#endif + +static struct modldrv modldrv = { + &mod_driverops, /* Type of module. This one is a driver */ + ident, + &urf_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, &modldrv, NULL +}; + +/* ======================================================== */ +/* + * _init : done + */ +/* ======================================================== */ +int +_init(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!urf: _init: called")); + + status = usbgem_mod_init(&urf_ops, "urf"); + if (status != DDI_SUCCESS) { + return (status); + } + status = mod_install(&modlinkage); + if (status != DDI_SUCCESS) { + usbgem_mod_fini(&urf_ops); + } + return (status); +} + +/* + * _fini : done + */ +int +_fini(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!urf: _fini: called")); + status = mod_remove(&modlinkage); + if (status == DDI_SUCCESS) { + usbgem_mod_fini(&urf_ops); + } + return (status); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} diff --git a/usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE b/usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE new file mode 100644 index 0000000000..53b5c92a32 --- /dev/null +++ b/usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE @@ -0,0 +1,32 @@ +/* + * usbgem.c: General USB to Fast Ethernet mac driver framework + * + * Copyright (c) 2002-2012 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 + * and/or other materials provided with the distribution. + * + * 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. + */ diff --git a/usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..ceb793bd37 --- /dev/null +++ b/usr/src/uts/common/io/usbgem/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +USBGEM DRIVER diff --git a/usr/src/uts/common/io/usbgem/usbgem.c b/usr/src/uts/common/io/usbgem/usbgem.c new file mode 100644 index 0000000000..1e8488a081 --- /dev/null +++ b/usr/src/uts/common/io/usbgem/usbgem.c @@ -0,0 +1,6389 @@ +/* + * usbgem.c: General USB to Fast Ethernet mac driver framework + * + * Copyright (c) 2002-2012 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 + * and/or other materials provided with the distribution. + * + * 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. + */ + +#pragma ident "@(#)usbgem.c 1.6 12/02/09" + +/* + * Change log + */ + +/* + * TODO: + * implement DELAYED_START + */ + +/* + * System Header files. + */ +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/debug.h> +#include <sys/kmem.h> +#include <sys/vtrace.h> +#include <sys/ethernet.h> +#include <sys/modctl.h> +#include <sys/errno.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#ifndef USBGEM_CONFIG_GLDv3 +#include <sys/dlpi.h> +#include <sys/strsubr.h> +#endif +#include <sys/stream.h> /* required for MBLK* */ +#include <sys/strsun.h> /* required for mionack() */ +#include <sys/byteorder.h> + +#include <sys/usb/usba.h> +#ifdef USBGEM_CONFIG_GLDv3 +#include <inet/common.h> +#include <inet/led.h> +#include <inet/mi.h> +#include <inet/nd.h> +#endif + +/* supplement definitions */ +extern const char *usb_str_cr(usb_cr_t); + +#ifndef USBGEM_CONFIG_GLDv3 +#pragma weak gld_linkstate +#endif +#include <sys/note.h> + +#include "usbgem_mii.h" +#include "usbgem.h" + +#ifdef MODULE +char ident[] = "usb general ethernet mac driver v" VERSION; +#else +extern char ident[]; +#endif + +/* Debugging support */ +#ifdef USBGEM_DEBUG_LEVEL +static int usbgem_debug = USBGEM_DEBUG_LEVEL; +#define DPRINTF(n, args) if (usbgem_debug > (n)) cmn_err args +#else +#define DPRINTF(n, args) +#endif + +/* + * Useful macros and typedefs + */ +#define ROUNDUP(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define DEFAULT_PIPE(dp) ((dp)->reg_data->dev_default_ph) +#define VTAG_SIZE 4 +#define BOOLEAN(x) ((x) != 0) +/* + * configuration parameters + */ +#define USBDRV_MAJOR_VER 2 +#define USBDRV_MINOR_VER 0 + +#define ETHERHEADERL (sizeof (struct ether_header)) +#define MAXPKTLEN(dp) ((dp)->mtu + ETHERHEADERL) +#define MAXPKTBUF(dp) ((dp)->mtu + ETHERHEADERL + ETHERFCSL) + +#define WATCH_INTERVAL_FAST drv_usectohz(100*1000) + +#define STOP_GRACEFUL B_TRUE + +/* + * Private functions + */ +static int usbgem_open_pipes(struct usbgem_dev *dp); +static int usbgem_close_pipes(struct usbgem_dev *dp); +static void usbgem_intr_cb(usb_pipe_handle_t, usb_intr_req_t *); +static void usbgem_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *); +static void usbgem_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *); + +static int usbgem_mii_start(struct usbgem_dev *); +static void usbgem_mii_stop(struct usbgem_dev *); + +/* local buffer management */ +static int usbgem_init_rx_buf(struct usbgem_dev *); + +/* internal mac interfaces */ +static void usbgem_tx_timeout(struct usbgem_dev *); +static void usbgem_mii_link_watcher(struct usbgem_dev *); +static int usbgem_mac_init(struct usbgem_dev *); +static int usbgem_mac_start(struct usbgem_dev *); +static int usbgem_mac_stop(struct usbgem_dev *, int, boolean_t); +static void usbgem_mac_ioctl(struct usbgem_dev *, queue_t *, mblk_t *); + +int usbgem_speed_value[] = {10, 100, 1000}; + +static int usbgem_ctrl_retry = 5; + +/* usb event support */ +static int usbgem_disconnect_cb(dev_info_t *dip); +static int usbgem_reconnect_cb(dev_info_t *dip); +int usbgem_suspend(dev_info_t *dip); +int usbgem_resume(dev_info_t *dip); + +static uint8_t usbgem_bcastaddr[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +#ifdef MODULE +extern struct mod_ops mod_miscops; + +static struct modlmisc modlmisc = { + &mod_miscops, + "usbgem v" VERSION, +}; + +static struct modlinkage modlinkage = { + MODREV_1, &modlmisc, NULL +}; + +/* + * _init : done + */ +int +_init(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!usbgem: _init: called")); + status = mod_install(&modlinkage); + + return (status); +} + +/* + * _fini : done + */ +int +_fini(void) +{ + int status; + + DPRINTF(2, (CE_CONT, "!usbgem: _fini: called")); + status = mod_remove(&modlinkage); + return (status); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} +#endif /* MODULE */ + +/* ============================================================== */ +/* + * Ether CRC calculation utilities + */ +/* ============================================================== */ +/* + * Ether CRC calculation according to 21143 data sheet + */ +#define CRC32_POLY_LE 0xedb88320 +uint32_t +usbgem_ether_crc_le(const uint8_t *addr) +{ + int idx; + int bit; + uint_t data; + uint32_t crc = 0xffffffff; + + crc = 0xffffffff; + for (idx = 0; idx < ETHERADDRL; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) { + crc = (crc >> 1) ^ + (((crc ^ data) & 1) ? CRC32_POLY_LE : 0); + } + } + return (crc); +} + +#define CRC32_POLY_BE 0x04c11db7 +uint32_t +usbgem_ether_crc_be(const uint8_t *addr) +{ + int idx; + int bit; + uint_t data; + uint32_t crc; + + crc = 0xffffffff; + for (idx = 0; idx < ETHERADDRL; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) { + crc = (crc << 1) ^ + ((((crc >> 31) ^ data) & 1) ? CRC32_POLY_BE : 0); + } + } + return (crc); +} + +int +usbgem_prop_get_int(struct usbgem_dev *dp, char *prop_template, int def_val) +{ + char propname[32]; + + (void) sprintf(propname, prop_template, dp->name); + + return (ddi_prop_get_int(DDI_DEV_T_ANY, dp->dip, + DDI_PROP_DONTPASS, propname, def_val)); +} + +static int +usbgem_population(uint32_t x) +{ + int i; + int cnt; + + cnt = 0; + for (i = 0; i < 32; i++) { + if (x & (1 << i)) { + cnt++; + } + } + return (cnt); +} + +static clock_t +usbgem_timestamp_nz() +{ + clock_t now; + now = ddi_get_lbolt(); + return (now ? now : (clock_t)1); +} + +#ifdef USBGEM_DEBUG_LEVEL +#ifdef USBGEM_DEBUG_VLAN +#ifdef notdef +#include <netinet/in.h> +#endif +static void +usbgem_dump_packet(struct usbgem_dev *dp, char *title, mblk_t *mp, + boolean_t check_cksum) +{ + char msg[180]; + uint8_t buf[18+20+20]; + uint8_t *p; + size_t offset; + uint_t ethertype; + uint_t proto; + uint_t ipproto = 0; + uint_t iplen; + uint_t iphlen; + uint_t tcplen; + uint_t udplen; + uint_t cksum; + int rest; + int len; + char *bp; + mblk_t *tp; + extern uint_t ip_cksum(mblk_t *, int, uint32_t); + + msg[0] = 0; + bp = msg; + + rest = sizeof (buf); + offset = 0; + for (tp = mp; tp; tp = tp->b_cont) { + len = tp->b_wptr - tp->b_rptr; + len = min(rest, len); + bcopy(tp->b_rptr, &buf[offset], len); + rest -= len; + offset += len; + if (rest == 0) { + break; + } + } + + offset = 0; + p = &buf[offset]; + + /* ethernet address */ + sprintf(bp, + "ether: %02x:%02x:%02x:%02x:%02x:%02x" + " -> %02x:%02x:%02x:%02x:%02x:%02x", + p[6], p[7], p[8], p[9], p[10], p[11], + p[0], p[1], p[2], p[3], p[4], p[5]); + bp = &msg[strlen(msg)]; + + /* vlag tag and etherrtype */ + ethertype = GET_ETHERTYPE(p); + if (ethertype == VTAG_TPID) { + sprintf(bp, " vtag:0x%04x", GET_NET16(&p[14])); + bp = &msg[strlen(msg)]; + + offset += VTAG_SIZE; + p = &buf[offset]; + ethertype = GET_ETHERTYPE(p); + } + sprintf(bp, " type:%04x", ethertype); + bp = &msg[strlen(msg)]; + + /* ethernet packet length */ + sprintf(bp, " mblklen:%d", msgdsize(mp)); + bp = &msg[strlen(msg)]; + if (mp->b_cont) { + sprintf(bp, "("); + bp = &msg[strlen(msg)]; + for (tp = mp; tp; tp = tp->b_cont) { + if (tp == mp) { + sprintf(bp, "%d", tp->b_wptr - tp->b_rptr); + } else { + sprintf(bp, "+%d", tp->b_wptr - tp->b_rptr); + } + bp = &msg[strlen(msg)]; + } + sprintf(bp, ")"); + bp = &msg[strlen(msg)]; + } + + if (ethertype != ETHERTYPE_IP) { + goto x; + } + + /* ip address */ + offset += sizeof (struct ether_header); + p = &buf[offset]; + ipproto = p[9]; + iplen = GET_NET16(&p[2]); + sprintf(bp, ", ip: %d.%d.%d.%d -> %d.%d.%d.%d proto:%d iplen:%d", + p[12], p[13], p[14], p[15], + p[16], p[17], p[18], p[19], + ipproto, iplen); + bp = (void *)&msg[strlen(msg)]; + + iphlen = (p[0] & 0xf) * 4; + + /* cksum for psuedo header */ + cksum = *(uint16_t *)&p[12]; + cksum += *(uint16_t *)&p[14]; + cksum += *(uint16_t *)&p[16]; + cksum += *(uint16_t *)&p[18]; + cksum += BE_16(ipproto); + + /* tcp or udp protocol header */ + offset += iphlen; + p = &buf[offset]; + if (ipproto == IPPROTO_TCP) { + tcplen = iplen - iphlen; + sprintf(bp, ", tcp: len:%d cksum:%x", + tcplen, GET_NET16(&p[16])); + bp = (void *)&msg[strlen(msg)]; + + if (check_cksum) { + cksum += BE_16(tcplen); + cksum = (uint16_t)ip_cksum(mp, offset, cksum); + sprintf(bp, " (%s)", + (cksum == 0 || cksum == 0xffff) ? "ok" : "ng"); + bp = (void *)&msg[strlen(msg)]; + } + } else if (ipproto == IPPROTO_UDP) { + udplen = GET_NET16(&p[4]); + sprintf(bp, ", udp: len:%d cksum:%x", + udplen, GET_NET16(&p[6])); + bp = (void *)&msg[strlen(msg)]; + + if (GET_NET16(&p[6]) && check_cksum) { + cksum += *(uint16_t *)&p[4]; + cksum = (uint16_t)ip_cksum(mp, offset, cksum); + sprintf(bp, " (%s)", + (cksum == 0 || cksum == 0xffff) ? "ok" : "ng"); + bp = (void *)&msg[strlen(msg)]; + } + } +x: + cmn_err(CE_CONT, "!%s: %s: %s", dp->name, title, msg); +} +#endif /* USBGEM_DEBUG_VLAN */ +#endif /* USBGEM_DEBUG_LEVEL */ + +#ifdef GEM_GCC_RUNTIME +/* + * gcc3 runtime routines + */ +#pragma weak memcmp +int +memcmp(const void *s1, const void *s2, size_t n) +{ + int i; + int ret; + + ret = 0; + for (i = 0; i < n; i++) { + ret = (int)((uint8_t *)s1)[i] - (int)((uint8_t *)s2)[i]; + if (ret) { + return (ret); + } + } + return (0); +} + +#pragma weak memset +void * +memset(void *s, int c, size_t n) +{ + if ((c & 0xff) == 0) { + bzero(s, n); + } else { + while (n--) { + ((uint8_t *)s)[n] = c; + } + } + return (s); +} + +#pragma weak _memcpy = memcpy +#pragma weak memcpy +void * +memcpy(void *s1, const void *s2, size_t n) +{ + bcopy(s2, s1, n); + return (s1); +} +#endif /* GEM_GCC_RUNTIME */ +/* ============================================================== */ +/* + * hardware operations + */ +/* ============================================================== */ +static int +usbgem_hal_reset_chip(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_reset_chip)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + +static int +usbgem_hal_init_chip(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_init_chip)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + +static int +usbgem_hal_attach_chip(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_attach_chip)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + +static int +usbgem_hal_set_rx_filter(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_set_rx_filter)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + +static int +usbgem_hal_set_media(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_set_media)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + +static int +usbgem_hal_start_chip(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_start_chip)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + +static int +usbgem_hal_stop_chip(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_stop_chip)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + +static int +usbgem_hal_get_stats(struct usbgem_dev *dp) +{ + int err; + + sema_p(&dp->hal_op_lock); + err = (*dp->ugc.usbgc_get_stats)(dp); + sema_v(&dp->hal_op_lock); + return (err); +} + + +/* ============================================================== */ +/* + * USB pipe management + */ +/* ============================================================== */ +static boolean_t +usbgem_rx_start_unit(struct usbgem_dev *dp, usb_bulk_req_t *req) +{ + mblk_t *mp; + int err; + usb_flags_t flags; + + ASSERT(req); + + mp = allocb(dp->rx_buf_len, BPRI_MED); + if (mp == NULL) { + cmn_err(CE_WARN, "!%s: %s: failed to allocate mblk", + dp->name, __func__); + goto err; + } + + req->bulk_len = dp->rx_buf_len; + req->bulk_data = mp; + req->bulk_client_private = (usb_opaque_t)dp; + req->bulk_timeout = 0; + req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK; + req->bulk_cb = usbgem_bulkin_cb; + req->bulk_exc_cb = usbgem_bulkin_cb; + req->bulk_completion_reason = 0; + req->bulk_cb_flags = 0; + + flags = 0; + err = usb_pipe_bulk_xfer(dp->bulkin_pipe, req, flags); + + if (err != USB_SUCCESS) { + cmn_err(CE_WARN, "%s: failed to bulk_xfer for rx, err:%d", + dp->name, err); + + /* free req and mp */ + usb_free_bulk_req(req); + goto err; + } + return (B_TRUE); +err: + return (B_FALSE); +} + +/* ============================================================== */ +/* + * Rx/Tx buffer management + */ +/* ============================================================== */ +static int +usbgem_init_rx_buf(struct usbgem_dev *dp) +{ + int i; + usb_bulk_req_t *req; + + ASSERT(dp->mac_state == MAC_STATE_ONLINE); + + for (i = 0; i < dp->ugc.usbgc_rx_list_max; i++) { + req = usb_alloc_bulk_req(dp->dip, 0, USB_FLAGS_SLEEP); + if (req == NULL) { + cmn_err(CE_WARN, + "!%s: %s: failed to allocate bulkreq for rx", + dp->name, __func__); + return (USB_FAILURE); + } + if (!usbgem_rx_start_unit(dp, req)) { + return (USB_FAILURE); + } + mutex_enter(&dp->rxlock); + dp->rx_busy_cnt++; + mutex_exit(&dp->rxlock); + } + return (USB_SUCCESS); +} + +/* ============================================================== */ +/* + * memory resource management + */ +/* ============================================================== */ +static int +usbgem_free_memory(struct usbgem_dev *dp) +{ + usb_bulk_req_t *req; + + /* free all tx requst structure */ + while ((req = dp->tx_free_list) != NULL) { + dp->tx_free_list = + (usb_bulk_req_t *)req->bulk_client_private; + req->bulk_data = NULL; + usb_free_bulk_req(req); + } + return (USB_SUCCESS); +} + +static int +usbgem_alloc_memory(struct usbgem_dev *dp) +{ + int i; + usb_bulk_req_t *req; + + /* allocate tx requests */ + dp->tx_free_list = NULL; + for (i = 0; i < dp->ugc.usbgc_tx_list_max; i++) { + req = usb_alloc_bulk_req(dp->dip, 0, USB_FLAGS_SLEEP); + if (req == NULL) { + cmn_err(CE_WARN, + "%s:%s failed to allocate tx requests", + dp->name, __func__); + + /* free partially allocated tx requests */ + (void) usbgem_free_memory(dp); + return (USB_FAILURE); + } + + /* add the new one allocated into tx free list */ + req->bulk_client_private = (usb_opaque_t)dp->tx_free_list; + dp->tx_free_list = req; + } + + return (USB_SUCCESS); +} + +/* ========================================================== */ +/* + * Start transmission. + * Return zero on success, + */ +/* ========================================================== */ + +#ifdef TXTIMEOUT_TEST +static int usbgem_send_cnt = 0; +#endif + +/* + * usbgem_send is used only to send data packet into ethernet line. + */ +static mblk_t * +usbgem_send_common(struct usbgem_dev *dp, mblk_t *mp, uint32_t flags) +{ + int err; + mblk_t *new; + usb_bulk_req_t *req; + int mcast; + int bcast; + int len; + boolean_t intr; + usb_flags_t usb_flags = 0; +#ifdef USBGEM_DEBUG_LEVEL + usb_pipe_state_t p_state; +#endif + DPRINTF(2, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + intr = (flags & 1) != 0; + len = msgdsize(mp); + bcast = 0; + mcast = 0; + if (mp->b_rptr[0] & 1) { + if (bcmp(mp->b_rptr, &usbgem_bcastaddr, ETHERADDRL) == 0) { + bcast = 1; + } else { + mcast = 1; + } + } + new = (*dp->ugc.usbgc_tx_make_packet)(dp, mp); + if (new == NULL) { + /* + * no memory resource. we don't stop downstream, + * we just discard the packet. + */ + DPRINTF(0, (CE_CONT, "!%s: %s: no memory", + dp->name, __func__)); + freemsg(mp); + + mutex_enter(&dp->txlock); + dp->stats.noxmtbuf++; + dp->stats.errxmt++; + mutex_exit(&dp->txlock); + + return (NULL); + } + + ASSERT(new->b_cont == NULL); + + mutex_enter(&dp->txlock); + if (dp->tx_free_list == NULL) { + /* + * no tx free slot + */ + ASSERT(dp->tx_busy_cnt == dp->ugc.usbgc_tx_list_max); + mutex_exit(&dp->txlock); + + DPRINTF(4, (CE_CONT, "!%s: %s: no free slot", + dp->name, __func__)); + if (new && new != mp) { + /* free reallocated message */ + freemsg(new); + } + return (mp); + } + req = dp->tx_free_list; + dp->tx_free_list = (usb_bulk_req_t *)req->bulk_client_private; + dp->tx_busy_cnt++; + + if (dp->tx_free_list == NULL) { + intr = B_TRUE; + } + if (intr) { + dp->tx_intr_pended++; + } + DB_TCI(new) = intr; +#ifdef USBGEM_DEBUG_LEVEL + new->b_datap->db_cksum32 = dp->tx_seq_num; + dp->tx_seq_num++; +#endif + dp->stats.obytes += len; + dp->stats.opackets++; + if (bcast | mcast) { + dp->stats.obcast += bcast; + dp->stats.omcast += mcast; + } + mutex_exit(&dp->txlock); + + DPRINTF(2, (CE_CONT, "!%s: %s: sending", dp->name, __func__)); + + req->bulk_len = (long)new->b_wptr - (long)new->b_rptr; + req->bulk_data = new; + req->bulk_client_private = (usb_opaque_t)dp; + req->bulk_timeout = dp->bulkout_timeout; /* in second */ + req->bulk_attributes = 0; + req->bulk_cb = usbgem_bulkout_cb; + req->bulk_exc_cb = usbgem_bulkout_cb; + req->bulk_completion_reason = 0; + req->bulk_cb_flags = 0; + + if (intr) { + usb_flags = USB_FLAGS_SLEEP; + } + if ((err = usb_pipe_bulk_xfer(dp->bulkout_pipe, req, usb_flags)) + != USB_SUCCESS) { + + /* failed to transfer the packet, discard it. */ + freemsg(new); + req->bulk_data = NULL; + + /* recycle the request block */ + mutex_enter(&dp->txlock); + dp->tx_busy_cnt--; + req->bulk_client_private = (usb_opaque_t)dp->tx_free_list; + dp->tx_free_list = req; + mutex_exit(&dp->txlock); + + cmn_err(CE_NOTE, + "%s: %s: usb_pipe_bulk_xfer: failed: err:%d", + dp->name, __func__, err); + + /* we use another flag to indicate error state. */ + if (dp->fatal_error == (clock_t)0) { + dp->fatal_error = usbgem_timestamp_nz(); + } + } else { + /* record the start time */ + dp->tx_start_time = ddi_get_lbolt(); + } + + if (err == USB_SUCCESS && (usb_flags & USB_FLAGS_SLEEP)) { + usbgem_bulkout_cb(dp->bulkout_pipe, req); + } + + if (new != mp) { + freemsg(mp); + } + return (NULL); +} + +int +usbgem_restart_nic(struct usbgem_dev *dp) +{ + int ret; + int flags = 0; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + ASSERT(dp->mac_state != MAC_STATE_DISCONNECTED); + + /* + * ensure to stop the nic + */ + if (dp->mac_state == MAC_STATE_ONLINE) { + (void) usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL); + } + + /* now the nic become quiescent, reset the chip */ + if (usbgem_hal_reset_chip(dp) != USB_SUCCESS) { + cmn_err(CE_WARN, "%s: %s: failed to reset chip", + dp->name, __func__); + goto err; + } + + /* + * restore the nic state step by step + */ + if (dp->nic_state < NIC_STATE_INITIALIZED) { + goto done; + } + + if (usbgem_mac_init(dp) != USB_SUCCESS) { + cmn_err(CE_WARN, "%s: %s: failed to initialize chip", + dp->name, __func__); + goto err; + } + + /* setup mac address and enable rx filter */ + sema_p(&dp->rxfilter_lock); + dp->rxmode |= RXMODE_ENABLE; + ret = usbgem_hal_set_rx_filter(dp); + sema_v(&dp->rxfilter_lock); + if (ret != USB_SUCCESS) { + goto err; + } + + /* + * update the link state asynchronously + */ + cv_signal(&dp->link_watcher_wait_cv); + + /* + * XXX - a panic happened because of linkdown. + * We must check mii_state here, because the link can be down just + * before the restart event happen. If the link is down now, + * gem_mac_start() will be called from gem_mii_link_check() when + * the link become up later. + */ + if (dp->mii_state == MII_STATE_LINKUP) { + if (usbgem_hal_set_media(dp) != USB_SUCCESS) { + goto err; + } + if (dp->nic_state < NIC_STATE_ONLINE) { + goto done; + } + + (void) usbgem_mac_start(dp); + + } +done: + return (USB_SUCCESS); +err: +#ifdef GEM_CONFIG_FMA + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); +#endif + return (USB_FAILURE); +} + +static void +usbgem_tx_timeout(struct usbgem_dev *dp) +{ + int ret; + uint_t rwlock; + clock_t now; + + for (; ; ) { + mutex_enter(&dp->tx_watcher_lock); + ret = cv_timedwait(&dp->tx_watcher_cv, &dp->tx_watcher_lock, + dp->tx_watcher_interval + ddi_get_lbolt()); + mutex_exit(&dp->tx_watcher_lock); + + if (dp->tx_watcher_stop) { + break; + } + + now = ddi_get_lbolt(); + + rwlock = RW_READER; +again: + rw_enter(&dp->dev_state_lock, rwlock); + + if ((dp->mac_state != MAC_STATE_DISCONNECTED && + dp->fatal_error && + now - dp->fatal_error >= dp->ugc.usbgc_tx_timeout) || + (dp->mac_state == MAC_STATE_ONLINE && + dp->mii_state == MII_STATE_LINKUP && + dp->tx_busy_cnt != 0 && + now - dp->tx_start_time >= dp->ugc.usbgc_tx_timeout)) { + if (rwlock == RW_READER) { + /* + * Upgrade dev_state_lock from shared mode + * to exclusive mode to restart nic + */ + rwlock = RW_WRITER; + rw_exit(&dp->dev_state_lock); + goto again; + } + cmn_err(CE_WARN, "%s: %s: restarting the nic:" + " fatal_error:%ld nic_state:%d" + " mac_state:%d starttime:%ld", + dp->name, __func__, + dp->fatal_error ? now - dp->fatal_error: 0, + dp->nic_state, dp->mac_state, + dp->tx_busy_cnt ? now - dp->tx_start_time : 0); + + (void) usbgem_restart_nic(dp); + } + + rw_exit(&dp->dev_state_lock); + } +} + +static int +usbgem_tx_watcher_start(struct usbgem_dev *dp) +{ + int err; + kthread_t *wdth; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* make a first call of uwgem_lw_link_check() */ + dp->tx_watcher_stop = 0; + dp->tx_watcher_interval = drv_usectohz(1000*1000); + + wdth = thread_create(NULL, 0, usbgem_tx_timeout, dp, 0, &p0, + TS_RUN, minclsyspri); + if (wdth == NULL) { + cmn_err(CE_WARN, + "!%s: %s: failed to create a tx_watcher thread", + dp->name, __func__); + return (USB_FAILURE); + } + dp->tx_watcher_did = wdth->t_did; + + return (USB_SUCCESS); +} + +static void +usbgem_tx_watcher_stop(struct usbgem_dev *dp) +{ + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + if (dp->tx_watcher_did) { + /* Ensure timer routine stopped */ + dp->tx_watcher_stop = 1; + cv_signal(&dp->tx_watcher_cv); + thread_join(dp->tx_watcher_did); + dp->tx_watcher_did = NULL; + } +} + +/* ================================================================== */ +/* + * Callback handlers + */ +/* ================================================================== */ +static void +usbgem_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) +{ + mblk_t *newmp; + mblk_t *mp; + mblk_t *tp; + uint64_t len = 0; + int pkts = 0; + int bcast = 0; + int mcast = 0; + boolean_t busy; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)req->bulk_client_private; + mp = req->bulk_data; + req->bulk_data = NULL; + + DPRINTF(2, (CE_CONT, "!%s: %s: mp:%p, cr:%s(%d)", + dp->name, __func__, mp, + usb_str_cr(req->bulk_completion_reason), + req->bulk_completion_reason)); + + /* + * we cannot acquire dev_state_lock because the routine + * must be executed during usbgem_mac_stop() to avoid + * dead lock. + * we use a simle membar operation to get the state correctly. + */ + membar_consumer(); + + if (req->bulk_completion_reason == USB_CR_OK && + dp->nic_state == NIC_STATE_ONLINE) { + newmp = (*dp->ugc.usbgc_rx_make_packet)(dp, mp); + + if (newmp != mp) { + /* the message has been reallocated, free old one */ + freemsg(mp); + } + + /* the message may includes one or more ethernet packets */ + for (tp = newmp; tp; tp = tp->b_next) { + len += (uintptr_t)tp->b_wptr - (uintptr_t)tp->b_rptr; + pkts++; + if (tp->b_rptr[0] & 1) { + if (bcmp(tp->b_rptr, &usbgem_bcastaddr, + ETHERADDRL) == 0) { + bcast++; + } else { + mcast++; + } + } + } + + /* send up if it is a valid packet */ +#ifdef USBGEM_CONFIG_GLDv3 + mac_rx(dp->mh, NULL, newmp); +#else + while (newmp) { + tp = newmp; + newmp = newmp->b_next; + tp->b_next = NULL; + gld_recv(dp->macinfo, tp); + } +#endif + } else { + freemsg(mp); + len = 0; + } + + mutex_enter(&dp->rxlock); + /* update rx_active */ + if (dp->rx_active) { + dp->rx_active = dp->mac_state == MAC_STATE_ONLINE; + } + + dp->stats.rbytes += len; + dp->stats.rpackets += pkts; + if (bcast | mcast) { + dp->stats.rbcast += bcast; + dp->stats.rmcast += mcast; + } + mutex_exit(&dp->rxlock); + + if (dp->rx_active) { + /* prepare to receive the next packets */ + if (usbgem_rx_start_unit(dp, req)) { + /* we successed */ + goto done; + } + cmn_err(CE_WARN, + "!%s: %s: failed to fill next rx packet", + dp->name, __func__); + /* + * we use another flag to indicate error state. + * if we acquire dev_state_lock for RW_WRITER here, + * usbgem_mac_stop() may hang. + */ + if (dp->fatal_error == (clock_t)0) { + dp->fatal_error = usbgem_timestamp_nz(); + } + } else { + /* no need to prepare the next packets */ + usb_free_bulk_req(req); + } + + mutex_enter(&dp->rxlock); + dp->rx_active = B_FALSE; + dp->rx_busy_cnt--; + if (dp->rx_busy_cnt == 0) { + /* wake up someone waits for me */ + cv_broadcast(&dp->rx_drain_cv); + } + mutex_exit(&dp->rxlock); +done: + ; +} + +static void +usbgem_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) +{ + boolean_t intr; + boolean_t tx_sched; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)req->bulk_client_private; + tx_sched = B_FALSE; + + DPRINTF(2, (CE_CONT, + "!%s: %s: cr:%s(%d) cb_flags:0x%x head:%d tail:%d", + dp->name, __func__, + usb_str_cr(req->bulk_completion_reason), + req->bulk_completion_reason, + req->bulk_cb_flags, + dp->tx_busy_cnt)); + + /* we have finished to transfer the packet into tx fifo */ + intr = DB_TCI(req->bulk_data); + freemsg(req->bulk_data); + + if (req->bulk_completion_reason != USB_CR_OK && + dp->fatal_error == (clock_t)0) { + dp->fatal_error = usbgem_timestamp_nz(); + } + + mutex_enter(&dp->txlock); + + if (intr) { + ASSERT(dp->tx_intr_pended > 0); + /* find the last interrupt we have scheduled */ + if (--(dp->tx_intr_pended) == 0) { + tx_sched = B_TRUE; + } + } + + ASSERT(dp->tx_busy_cnt > 0); + req->bulk_client_private = (usb_opaque_t)dp->tx_free_list; + dp->tx_free_list = req; + dp->tx_busy_cnt--; + +#ifdef CONFIG_TX_LIMITER + if (tx_sched) { + dp->tx_max_packets = + min(dp->tx_max_packets + 1, dp->ugc.usbgc_tx_list_max); + } +#endif + if (dp->mac_state != MAC_STATE_ONLINE && dp->tx_busy_cnt == 0) { + cv_broadcast(&dp->tx_drain_cv); + } + + mutex_exit(&dp->txlock); + + if (tx_sched) { +#ifdef USBGEM_CONFIG_GLDv3 + mac_tx_update(dp->mh); +#else + gld_sched(dp->macinfo); +#endif + } +} + +static void +usbgem_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req) +{ + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)req->intr_client_private; + dp->stats.intr++; + + if (req->intr_completion_reason == USB_CR_OK) { + (*dp->ugc.usbgc_interrupt)(dp, req->intr_data); + } + + /* free the request and data */ + usb_free_intr_req(req); +} + +/* ======================================================================== */ +/* + * MII support routines + */ +/* ======================================================================== */ +static void +usbgem_choose_forcedmode(struct usbgem_dev *dp) +{ + /* choose media mode */ + if (dp->anadv_1000fdx || dp->anadv_1000hdx) { + dp->speed = USBGEM_SPD_1000; + dp->full_duplex = dp->anadv_1000fdx; + } else if (dp->anadv_100fdx || dp->anadv_100t4) { + dp->speed = USBGEM_SPD_100; + dp->full_duplex = B_TRUE; + } else if (dp->anadv_100hdx) { + dp->speed = USBGEM_SPD_100; + dp->full_duplex = B_FALSE; + } else { + dp->speed = USBGEM_SPD_10; + dp->full_duplex = dp->anadv_10fdx; + } +} + +static uint16_t +usbgem_mii_read(struct usbgem_dev *dp, uint_t reg, int *errp) +{ + uint16_t val; + + sema_p(&dp->hal_op_lock); + val = (*dp->ugc.usbgc_mii_read)(dp, reg, errp); + sema_v(&dp->hal_op_lock); + + return (val); +} + +static void +usbgem_mii_write(struct usbgem_dev *dp, uint_t reg, uint16_t val, int *errp) +{ + sema_p(&dp->hal_op_lock); + (*dp->ugc.usbgc_mii_write)(dp, reg, val, errp); + sema_v(&dp->hal_op_lock); +} + +static int +usbgem_mii_probe(struct usbgem_dev *dp) +{ + int err; + + err = (*dp->ugc.usbgc_mii_probe)(dp); + return (err); +} + +static int +usbgem_mii_init(struct usbgem_dev *dp) +{ + int err; + + err = (*dp->ugc.usbgc_mii_init)(dp); + return (err); +} + +#define fc_cap_decode(x) \ + ((((x) & MII_ABILITY_PAUSE) != 0 ? 1 : 0) | \ + (((x) & MII_ABILITY_ASM_DIR) != 0 ? 2 : 0)) + +int +usbgem_mii_config_default(struct usbgem_dev *dp, int *errp) +{ + uint16_t mii_stat; + uint16_t val; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* + * Configure bits in advertisement register + */ + mii_stat = dp->mii_status; + + DPRINTF(1, (CE_CONT, "!%s: %s: MII_STATUS reg:%b", + dp->name, __func__, mii_stat, MII_STATUS_BITS)); + + if ((mii_stat & MII_STATUS_ABILITY_TECH) == 0) { + /* it's funny */ + cmn_err(CE_WARN, "!%s: wrong ability bits: mii_status:%b", + dp->name, mii_stat, MII_STATUS_BITS); + return (USB_FAILURE); + } + + /* Do not change the rest of ability bits in advert reg */ + val = usbgem_mii_read(dp, MII_AN_ADVERT, errp) & ~MII_ABILITY_ALL; + if (*errp != USB_SUCCESS) { + goto usberr; + } + + DPRINTF(0, (CE_CONT, + "!%s: %s: 100T4:%d 100F:%d 100H:%d 10F:%d 10H:%d", + dp->name, __func__, + dp->anadv_100t4, dp->anadv_100fdx, dp->anadv_100hdx, + dp->anadv_10fdx, dp->anadv_10hdx)); + + /* set technology bits */ + if (dp->anadv_100t4) { + val |= MII_ABILITY_100BASE_T4; + } + if (dp->anadv_100fdx) { + val |= MII_ABILITY_100BASE_TX_FD; + } + if (dp->anadv_100hdx) { + val |= MII_ABILITY_100BASE_TX; + } + if (dp->anadv_10fdx) { + val |= MII_ABILITY_10BASE_T_FD; + } + if (dp->anadv_10hdx) { + val |= MII_ABILITY_10BASE_T; + } + + /* set flow control capabilities */ + if (dp->anadv_pause) { + val |= MII_ABILITY_PAUSE; + } + if (dp->anadv_asmpause) { + val |= MII_ABILITY_ASM_DIR; + } + + DPRINTF(0, (CE_CONT, + "!%s: %s: setting MII_AN_ADVERT reg:%b, pause:%d, asmpause:%d", + dp->name, __func__, val, MII_ABILITY_BITS, + dp->anadv_pause, dp->anadv_asmpause)); + + usbgem_mii_write(dp, MII_AN_ADVERT, val, errp); + if (*errp != USB_SUCCESS) { + goto usberr; + } + + if (dp->mii_status & MII_STATUS_XSTATUS) { + /* + * 1000Base-T GMII support + */ + if (!dp->anadv_autoneg) { + /* enable manual configuration */ + val = MII_1000TC_CFG_EN; + if (dp->anadv_1000t_ms == 2) { + val |= MII_1000TC_CFG_VAL; + } + } else { + val = 0; + if (dp->anadv_1000fdx) { + val |= MII_1000TC_ADV_FULL; + } + if (dp->anadv_1000hdx) { + val |= MII_1000TC_ADV_HALF; + } + switch (dp->anadv_1000t_ms) { + case 1: + /* slave */ + val |= MII_1000TC_CFG_EN; + break; + + case 2: + /* master */ + val |= MII_1000TC_CFG_EN | MII_1000TC_CFG_VAL; + break; + + default: + /* auto: do nothing */ + break; + } + } + DPRINTF(0, (CE_CONT, + "!%s: %s: setting MII_1000TC reg:%b", + dp->name, __func__, val, MII_1000TC_BITS)); + + usbgem_mii_write(dp, MII_1000TC, val, errp); + if (*errp != USB_SUCCESS) { + goto usberr; + } + } + return (USB_SUCCESS); + +usberr: + return (*errp); +} + +static char *usbgem_fc_type[] = { + "without", + "with symmetric", + "with tx", + "with rx", +}; + +#ifdef USBGEM_CONFIG_GLDv3 +#define USBGEM_LINKUP(dp) mac_link_update((dp)->mh, LINK_STATE_UP) +#define USBGEM_LINKDOWN(dp) mac_link_update((dp)->mh, LINK_STATE_DOWN) +#else +#define USBGEM_LINKUP(dp) \ + if (gld_linkstate) { \ + gld_linkstate((dp)->macinfo, GLD_LINKSTATE_UP); \ + } +#define USBGEM_LINKDOWN(dp) \ + if (gld_linkstate) { \ + gld_linkstate((dp)->macinfo, GLD_LINKSTATE_DOWN); \ + } +#endif + +static uint8_t usbgem_fc_result[4 /* my cap */][4 /* lp cap */] = { +/* none symm tx rx/symm */ +/* none */ + {FLOW_CONTROL_NONE, + FLOW_CONTROL_NONE, + FLOW_CONTROL_NONE, + FLOW_CONTROL_NONE}, +/* sym */ + {FLOW_CONTROL_NONE, + FLOW_CONTROL_SYMMETRIC, + FLOW_CONTROL_NONE, + FLOW_CONTROL_SYMMETRIC}, +/* tx */ + {FLOW_CONTROL_NONE, + FLOW_CONTROL_NONE, + FLOW_CONTROL_NONE, + FLOW_CONTROL_TX_PAUSE}, +/* rx/symm */ + {FLOW_CONTROL_NONE, + FLOW_CONTROL_SYMMETRIC, + FLOW_CONTROL_RX_PAUSE, + FLOW_CONTROL_SYMMETRIC}, +}; + +static boolean_t +usbgem_mii_link_check(struct usbgem_dev *dp, int *oldstatep, int *newstatep) +{ + boolean_t tx_sched = B_FALSE; + uint16_t status; + uint16_t advert; + uint16_t lpable; + uint16_t exp; + uint16_t ctl1000; + uint16_t stat1000; + uint16_t val; + clock_t now; + clock_t diff; + int linkdown_action; + boolean_t fix_phy = B_FALSE; + int err; + uint_t rwlock; + + DPRINTF(4, (CE_CONT, "!%s: %s: time:%d state:%d", + dp->name, __func__, ddi_get_lbolt(), dp->mii_state)); + + if (dp->mii_state != MII_STATE_LINKUP) { + rwlock = RW_WRITER; + } else { + rwlock = RW_READER; + } +again: + rw_enter(&dp->dev_state_lock, rwlock); + + /* save old mii state */ + *oldstatep = dp->mii_state; + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + /* stop periodic execution of the link watcher */ + dp->mii_interval = 0; + tx_sched = B_FALSE; + goto next; + } + + now = ddi_get_lbolt(); + diff = now - dp->mii_last_check; + dp->mii_last_check = now; + + /* + * For NWAM, don't show linkdown state right + * when the device is attached. + */ + if (dp->linkup_delay > 0) { + if (dp->linkup_delay > diff) { + dp->linkup_delay -= diff; + } else { + /* link up timeout */ + dp->linkup_delay = -1; + } + } + +next_nowait: + switch (dp->mii_state) { + case MII_STATE_UNKNOWN: + goto reset_phy; + + case MII_STATE_RESETTING: + dp->mii_timer -= diff; + if (dp->mii_timer > 0) { + /* don't read phy registers in resetting */ + dp->mii_interval = WATCH_INTERVAL_FAST; + goto next; + } + + val = usbgem_mii_read(dp, MII_CONTROL, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + if (val & MII_CONTROL_RESET) { + cmn_err(CE_NOTE, + "!%s: time:%ld resetting phy not complete." + " mii_control:0x%b", + dp->name, ddi_get_lbolt(), + val, MII_CONTROL_BITS); + } + + /* ensure neither isolated nor pwrdown nor auto-nego mode */ + usbgem_mii_write(dp, MII_CONTROL, 0, &err); + if (err != USB_SUCCESS) { + goto usberr; + } +#if USBGEM_DEBUG_LEVEL > 10 + val = usbgem_mii_read(dp, MII_CONTROL, &err); + cmn_err(CE_CONT, "!%s: readback control %b", + dp->name, val, MII_CONTROL_BITS); +#endif + /* As resetting PHY has completed, configure PHY registers */ + if ((*dp->ugc.usbgc_mii_config)(dp, &err) != USB_SUCCESS) { + /* we failed to configure PHY */ + goto usberr; + } + + /* prepare for forced mode */ + usbgem_choose_forcedmode(dp); + + dp->mii_lpable = 0; + dp->mii_advert = 0; + dp->mii_exp = 0; + dp->mii_ctl1000 = 0; + dp->mii_stat1000 = 0; + + dp->flow_control = FLOW_CONTROL_NONE; + + if (!dp->anadv_autoneg) { + /* skip auto-negotiation phase */ + dp->mii_state = MII_STATE_MEDIA_SETUP; + dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout; + goto next_nowait; + } + + /* issue an auto-negotiation command */ + goto autonego; + + case MII_STATE_AUTONEGOTIATING: + /* + * Autonegotiation in progress + */ + dp->mii_timer -= diff; + if (dp->mii_timer - + (dp->ugc.usbgc_mii_an_timeout - dp->ugc.usbgc_mii_an_wait) + > 0) { + /* wait for minimum time (2.3 - 2.5 sec) */ + dp->mii_interval = WATCH_INTERVAL_FAST; + goto next; + } + + /* read PHY status */ + status = usbgem_mii_read(dp, MII_STATUS, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + DPRINTF(4, (CE_CONT, + "!%s: %s: called: mii_state:%d MII_STATUS reg:%b", + dp->name, __func__, dp->mii_state, + status, MII_STATUS_BITS)); + + if (status & MII_STATUS_REMFAULT) { + /* + * The link parnert told me something wrong happend. + * What do we do ? + */ + cmn_err(CE_CONT, + "!%s: auto-negotiation failed: remote fault", + dp->name); + goto autonego; + } + + if ((status & MII_STATUS_ANDONE) == 0) { + if (dp->mii_timer <= 0) { + /* + * Auto-negotiation has been timed out, + * Reset PHY and try again. + */ + if (!dp->mii_supress_msg) { + cmn_err(CE_WARN, + "!%s: auto-negotiation failed:" + " timeout", + dp->name); + dp->mii_supress_msg = B_TRUE; + } + goto autonego; + } + /* + * Auto-negotiation is in progress. Wait for a while. + */ + dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval; + goto next; + } + + /* + * Auto-negotiation has been completed. Let's go to AN_DONE. + */ + dp->mii_state = MII_STATE_AN_DONE; + dp->mii_supress_msg = B_FALSE; + DPRINTF(0, (CE_CONT, + "!%s: auto-negotiation completed, MII_STATUS:%b", + dp->name, status, MII_STATUS_BITS)); + + if (dp->ugc.usbgc_mii_an_delay > 0) { + dp->mii_timer = dp->ugc.usbgc_mii_an_delay; + dp->mii_interval = drv_usectohz(20*1000); + goto next; + } + + dp->mii_timer = 0; + diff = 0; + goto next_nowait; + + case MII_STATE_AN_DONE: + /* + * Auto-negotiation has done. Now we can set up media. + */ + dp->mii_timer -= diff; + if (dp->mii_timer > 0) { + /* wait for a while */ + dp->mii_interval = WATCH_INTERVAL_FAST; + goto next; + } + + /* + * Setup speed and duplex mode according with + * the result of auto negotiation. + */ + + /* + * Read registers required to determin current + * duplex mode and media speed. + */ + if (dp->ugc.usbgc_mii_an_delay > 0) { + /* the 'status' variable is not initialized yet */ + status = usbgem_mii_read(dp, MII_STATUS, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + } + advert = usbgem_mii_read(dp, MII_AN_ADVERT, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + lpable = usbgem_mii_read(dp, MII_AN_LPABLE, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + exp = usbgem_mii_read(dp, MII_AN_EXPANSION, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + if (exp == 0xffff) { + /* some phys don't have exp register */ + exp = 0; + } + + ctl1000 = 0; + stat1000 = 0; + if (dp->mii_status & MII_STATUS_XSTATUS) { + ctl1000 = usbgem_mii_read(dp, MII_1000TC, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + stat1000 = usbgem_mii_read(dp, MII_1000TS, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + } + dp->mii_lpable = lpable; + dp->mii_advert = advert; + dp->mii_exp = exp; + dp->mii_ctl1000 = ctl1000; + dp->mii_stat1000 = stat1000; + + cmn_err(CE_CONT, + "!%s: auto-negotiation done: " + "status:%b, advert:%b, lpable:%b, exp:%b", + dp->name, + status, MII_STATUS_BITS, + advert, MII_ABILITY_BITS, + lpable, MII_ABILITY_BITS, + exp, MII_AN_EXP_BITS); + + DPRINTF(0, (CE_CONT, "!%s: MII_STATUS:%b", + dp->name, status, MII_STATUS_BITS)); + + if (dp->mii_status & MII_STATUS_XSTATUS) { + cmn_err(CE_CONT, + "! MII_1000TC reg:%b, MII_1000TS reg:%b", + ctl1000, MII_1000TC_BITS, + stat1000, MII_1000TS_BITS); + } + + if (usbgem_population(lpable) <= 1 && + (exp & MII_AN_EXP_LPCANAN) == 0) { + if ((advert & MII_ABILITY_TECH) != lpable) { + cmn_err(CE_WARN, + "!%s: but the link partner doesn't seem" + " to have auto-negotiation capability." + " please check the link configuration.", + dp->name); + } + /* + * it should be a result of pararell detection, + * which cannot detect duplex mode. + */ + if ((advert & lpable) == 0 && + lpable & MII_ABILITY_10BASE_T) { + /* no common technology, try 10M half mode */ + lpable |= advert & MII_ABILITY_10BASE_T; + fix_phy = B_TRUE; + } + } else if (lpable == 0) { + cmn_err(CE_WARN, "!%s: wrong lpable.", dp->name); + goto reset_phy; + } + /* + * configure current link mode according to AN priority. + */ + val = advert & lpable; + if ((ctl1000 & MII_1000TC_ADV_FULL) && + (stat1000 & MII_1000TS_LP_FULL)) { + /* 1000BaseT & full duplex */ + dp->speed = USBGEM_SPD_1000; + dp->full_duplex = B_TRUE; + } else if ((ctl1000 & MII_1000TC_ADV_HALF) && + (stat1000 & MII_1000TS_LP_HALF)) { + /* 1000BaseT & half duplex */ + dp->speed = USBGEM_SPD_1000; + dp->full_duplex = B_FALSE; + } else if ((val & MII_ABILITY_100BASE_TX_FD)) { + /* 100BaseTx & fullduplex */ + dp->speed = USBGEM_SPD_100; + dp->full_duplex = B_TRUE; + } else if ((val & MII_ABILITY_100BASE_T4)) { + /* 100BaseTx & fullduplex */ + dp->speed = USBGEM_SPD_100; + dp->full_duplex = B_TRUE; + } else if ((val & MII_ABILITY_100BASE_TX)) { + /* 100BaseTx & half duplex */ + dp->speed = USBGEM_SPD_100; + dp->full_duplex = B_FALSE; + } else if ((val & MII_ABILITY_10BASE_T_FD)) { + /* 10BaseT & full duplex */ + dp->speed = USBGEM_SPD_10; + dp->full_duplex = B_TRUE; + } else if ((val & MII_ABILITY_10BASE_T)) { + /* 10BaseT & half duplex */ + dp->speed = USBGEM_SPD_10; + dp->full_duplex = B_FALSE; + } else { + /* + * the link partner doesn't seem to have + * auto-negotiation capability and our PHY + * could not report current mode correctly. + * We guess current mode by mii_control register. + */ + val = usbgem_mii_read(dp, MII_CONTROL, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + + /* select 100m half or 10m half */ + dp->speed = (val & MII_CONTROL_100MB) ? + USBGEM_SPD_100 : USBGEM_SPD_10; + dp->full_duplex = B_FALSE; + fix_phy = B_TRUE; + + cmn_err(CE_NOTE, + "!%s: auto-negotiation done but " + "common ability not found.\n" + "PHY state: control:%b advert:%b lpable:%b\n" + "guessing %d Mbps %s duplex mode", + dp->name, + val, MII_CONTROL_BITS, + advert, MII_ABILITY_BITS, + lpable, MII_ABILITY_BITS, + usbgem_speed_value[dp->speed], + dp->full_duplex ? "full" : "half"); + } + + if (dp->full_duplex) { + dp->flow_control = + usbgem_fc_result[fc_cap_decode(advert)] + [fc_cap_decode(lpable)]; + } else { + dp->flow_control = FLOW_CONTROL_NONE; + } + dp->mii_state = MII_STATE_MEDIA_SETUP; + dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout; + goto next_nowait; + + case MII_STATE_MEDIA_SETUP: + DPRINTF(2, (CE_CONT, "!%s: setup midia mode", dp->name)); + + /* assume the link state is down */ + dp->mii_state = MII_STATE_LINKDOWN; + dp->mii_supress_msg = B_FALSE; + + /* use short interval */ + dp->mii_interval = WATCH_INTERVAL_FAST; + + if ((!dp->anadv_autoneg) || + dp->ugc.usbgc_mii_an_oneshot || fix_phy) { + + /* + * write the result of auto negotiation back. + */ + val = usbgem_mii_read(dp, MII_CONTROL, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + val &= ~(MII_CONTROL_SPEED | MII_CONTROL_FDUPLEX | + MII_CONTROL_ANE | MII_CONTROL_RSAN); + + if (dp->full_duplex) { + val |= MII_CONTROL_FDUPLEX; + } + + switch (dp->speed) { + case USBGEM_SPD_1000: + val |= MII_CONTROL_1000MB; + break; + + case USBGEM_SPD_100: + val |= MII_CONTROL_100MB; + break; + + default: + cmn_err(CE_WARN, "%s: unknown speed:%d", + dp->name, dp->speed); + /* FALLTHROUGH */ + + case USBGEM_SPD_10: + /* for USBGEM_SPD_10, do nothing */ + break; + } + + if (dp->mii_status & MII_STATUS_XSTATUS) { + usbgem_mii_write(dp, + MII_1000TC, MII_1000TC_CFG_EN, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + } + usbgem_mii_write(dp, MII_CONTROL, val, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + } + /* + * XXX -- nic state should be one of + * NIC_STATE_DISCONNECTED + * NIC_STATE_STOPPED + * NIC_STATE_INITIALIZED + * NIC_STATE_ONLINE + */ + if (dp->nic_state >= NIC_STATE_INITIALIZED) { + /* notify the result of autonegotiation to mac */ + if (usbgem_hal_set_media(dp) != USB_SUCCESS) { + goto usberr; + } + } + goto next_nowait; + + case MII_STATE_LINKDOWN: + status = usbgem_mii_read(dp, MII_STATUS, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + if (status & MII_STATUS_LINKUP) { + /* + * Link is going up + */ + dp->mii_state = MII_STATE_LINKUP; + dp->mii_supress_msg = B_FALSE; + + DPRINTF(0, (CE_CONT, + "!%s: link up detected: status:%b", + dp->name, status, MII_STATUS_BITS)); + + /* + * MII_CONTROL_100MB and MII_CONTROL_FDUPLEX are + * ignored when MII_CONTROL_ANE is set. + */ + cmn_err(CE_CONT, + "!%s: Link up: %d Mbps %s duplex %s flow control", + dp->name, + usbgem_speed_value[dp->speed], + dp->full_duplex ? "full" : "half", + usbgem_fc_type[dp->flow_control]); + + dp->mii_interval = + dp->ugc.usbgc_mii_link_watch_interval; + + if (dp->ugc.usbgc_mii_hw_link_detection && + dp->nic_state == NIC_STATE_ONLINE) { + dp->mii_interval = 0; + } + + if (dp->nic_state == NIC_STATE_ONLINE) { + if (dp->mac_state == MAC_STATE_INITIALIZED) { + (void) usbgem_mac_start(dp); + } + tx_sched = B_TRUE; + } + + goto next; + } + + dp->mii_supress_msg = B_TRUE; + if (dp->anadv_autoneg) { + dp->mii_timer -= diff; + if (dp->mii_timer <= 0) { + /* + * the link down timer expired. + * need to restart auto-negotiation. + */ + linkdown_action = + dp->ugc.usbgc_mii_linkdown_timeout_action; + goto restart_autonego; + } + } + /* don't change mii_state */ + goto next; + + case MII_STATE_LINKUP: + if (rwlock == RW_READER) { + /* first pass, read mii status */ + status = usbgem_mii_read(dp, MII_STATUS, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + } + if ((status & MII_STATUS_LINKUP) == 0) { + /* + * Link is going down + */ + cmn_err(CE_NOTE, + "!%s: link down detected: status:%b", + dp->name, status, MII_STATUS_BITS); + /* + * Acquire exclusive lock to change mii_state + */ + if (rwlock == RW_READER) { + rwlock = RW_WRITER; + rw_exit(&dp->dev_state_lock); + goto again; + } + + dp->mii_state = MII_STATE_LINKDOWN; + dp->mii_timer = dp->ugc.usbgc_mii_linkdown_timeout; + + /* + * As we may change the state of the device, + * let us acquire exclusive lock for the state. + */ + if (dp->nic_state == NIC_STATE_ONLINE && + dp->mac_state == MAC_STATE_ONLINE && + dp->ugc.usbgc_mii_stop_mac_on_linkdown) { + (void) usbgem_restart_nic(dp); + /* drain tx */ + tx_sched = B_TRUE; + } + + if (dp->anadv_autoneg) { + /* need to restart auto-negotiation */ + linkdown_action = + dp->ugc.usbgc_mii_linkdown_action; + goto restart_autonego; + } + /* + * don't use hw link down detection until the link + * status become stable for a while. + */ + dp->mii_interval = + dp->ugc.usbgc_mii_link_watch_interval; + + goto next; + } + + /* + * still link up, no need to change mii_state + */ + if (dp->ugc.usbgc_mii_hw_link_detection && + dp->nic_state == NIC_STATE_ONLINE) { + /* + * no need to check link status periodicly + * if nic can generate interrupts when link go down. + */ + dp->mii_interval = 0; + } + goto next; + } + /* NOTREACHED */ + cmn_err(CE_PANIC, "!%s: %s: not reached", dp->name, __func__); + + /* + * Actions for new state. + */ +restart_autonego: + switch (linkdown_action) { + case MII_ACTION_RESET: + if (!dp->mii_supress_msg) { + cmn_err(CE_CONT, "!%s: resetting PHY", dp->name); + } + dp->mii_supress_msg = B_TRUE; + goto reset_phy; + + case MII_ACTION_NONE: + dp->mii_supress_msg = B_TRUE; + if (dp->ugc.usbgc_mii_an_oneshot) { + goto autonego; + } + /* PHY will restart autonego automatically */ + dp->mii_state = MII_STATE_AUTONEGOTIATING; + dp->mii_timer = dp->ugc.usbgc_mii_an_timeout; + dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval; + goto next; + + case MII_ACTION_RSA: + if (!dp->mii_supress_msg) { + cmn_err(CE_CONT, "!%s: restarting auto-negotiation", + dp->name); + } + dp->mii_supress_msg = B_TRUE; + goto autonego; + + default: + cmn_err(CE_PANIC, "!%s: unknowm linkdown action: %d", + dp->name, dp->ugc.usbgc_mii_linkdown_action); + dp->mii_supress_msg = B_TRUE; + } + /* NOTREACHED */ + +reset_phy: + if (!dp->mii_supress_msg) { + cmn_err(CE_CONT, "!%s: resetting PHY", dp->name); + } + dp->mii_state = MII_STATE_RESETTING; + dp->mii_timer = dp->ugc.usbgc_mii_reset_timeout; + if (!dp->ugc.usbgc_mii_dont_reset) { + usbgem_mii_write(dp, MII_CONTROL, MII_CONTROL_RESET, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + } + dp->mii_interval = WATCH_INTERVAL_FAST; + goto next; + +autonego: + if (!dp->mii_supress_msg) { + cmn_err(CE_CONT, "!%s: auto-negotiation started", dp->name); + } + dp->mii_state = MII_STATE_AUTONEGOTIATING; + dp->mii_timer = dp->ugc.usbgc_mii_an_timeout; + + /* start/restart autoneg */ + val = usbgem_mii_read(dp, MII_CONTROL, &err) & + ~(MII_CONTROL_ISOLATE | MII_CONTROL_PWRDN | MII_CONTROL_RESET); + if (err != USB_SUCCESS) { + goto usberr; + } + if (val & MII_CONTROL_ANE) { + val |= MII_CONTROL_RSAN; + } + usbgem_mii_write(dp, MII_CONTROL, + val | dp->ugc.usbgc_mii_an_cmd | MII_CONTROL_ANE, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + + dp->mii_interval = dp->ugc.usbgc_mii_an_watch_interval; + goto next; + +usberr: + dp->mii_state = MII_STATE_UNKNOWN; + dp->mii_interval = dp->ugc.usbgc_mii_link_watch_interval; + tx_sched = B_TRUE; + +next: + *newstatep = dp->mii_state; + rw_exit(&dp->dev_state_lock); + return (tx_sched); +} + +static void +usbgem_mii_link_watcher(struct usbgem_dev *dp) +{ + int old_mii_state; + int new_mii_state; + boolean_t tx_sched; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + for (; ; ) { + + mutex_enter(&dp->link_watcher_lock); + if (dp->mii_interval) { + (void) cv_timedwait(&dp->link_watcher_wait_cv, + &dp->link_watcher_lock, + dp->mii_interval + ddi_get_lbolt()); + } else { + cv_wait(&dp->link_watcher_wait_cv, + &dp->link_watcher_lock); + } + mutex_exit(&dp->link_watcher_lock); + + if (dp->link_watcher_stop) { + break; + } + + /* we block callbacks from disconnect/suspend and restart */ + tx_sched = usbgem_mii_link_check(dp, + &old_mii_state, &new_mii_state); + + /* + * gld v2 notifier functions are not able to + * be called with any locks in this layer. + */ + if (tx_sched) { + /* kick potentially stopped downstream */ +#ifdef USBGEM_CONFIG_GLDv3 + mac_tx_update(dp->mh); +#else + gld_sched(dp->macinfo); +#endif + } + + if (old_mii_state != new_mii_state) { + /* notify new mii link state */ + if (new_mii_state == MII_STATE_LINKUP) { + dp->linkup_delay = 0; + USBGEM_LINKUP(dp); + } else if (dp->linkup_delay <= 0) { + USBGEM_LINKDOWN(dp); + } + } else if (dp->linkup_delay < 0) { + /* first linkup timeout */ + dp->linkup_delay = 0; + USBGEM_LINKDOWN(dp); + } + } + + thread_exit(); +} + +void +usbgem_mii_update_link(struct usbgem_dev *dp) +{ + cv_signal(&dp->link_watcher_wait_cv); +} + +int +usbgem_mii_probe_default(struct usbgem_dev *dp) +{ + int phy; + uint16_t status; + uint16_t xstatus; + int err; + uint16_t adv; + uint16_t adv_org; + + DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* + * Scan PHY + */ + dp->mii_status = 0; + + /* Try default phy first */ + if (dp->mii_phy_addr) { + status = usbgem_mii_read(dp, MII_STATUS, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + if (status != 0xffff && status != 0x0000) { + goto PHY_found; + } + + if (dp->mii_phy_addr < 0) { + cmn_err(CE_NOTE, + "!%s: failed to probe default internal and/or non-MII PHY", + dp->name); + return (USB_FAILURE); + } + + cmn_err(CE_NOTE, + "!%s: failed to probe default MII PHY at %d", + dp->name, dp->mii_phy_addr); + } + + /* Try all possible address */ + for (phy = dp->ugc.usbgc_mii_addr_min; phy < 32; phy++) { + dp->mii_phy_addr = phy; + status = usbgem_mii_read(dp, MII_STATUS, &err); + if (err != USB_SUCCESS) { + DPRINTF(0, (CE_CONT, + "!%s: %s: mii_read(status) failed", + dp->name, __func__)); + goto usberr; + } + + if (status != 0xffff && status != 0x0000) { + usbgem_mii_write(dp, MII_CONTROL, 0, &err); + if (err != USB_SUCCESS) { + DPRINTF(0, (CE_CONT, + "!%s: %s: mii_write(control) failed", + dp->name, __func__)); + goto usberr; + } + goto PHY_found; + } + } + for (phy = dp->ugc.usbgc_mii_addr_min; phy < 32; phy++) { + dp->mii_phy_addr = phy; + usbgem_mii_write(dp, MII_CONTROL, 0, &err); + if (err != USB_SUCCESS) { + DPRINTF(0, (CE_CONT, + "!%s: %s: mii_write(control) failed", + dp->name, __func__)); + goto usberr; + } + status = usbgem_mii_read(dp, MII_STATUS, &err); + if (err != USB_SUCCESS) { + DPRINTF(0, (CE_CONT, + "!%s: %s: mii_read(status) failed", + dp->name, __func__)); + goto usberr; + } + + if (status != 0xffff && status != 0) { + goto PHY_found; + } + } + + cmn_err(CE_NOTE, "!%s: no MII PHY found", dp->name); + return (USB_FAILURE); + +PHY_found: + dp->mii_status = status; + dp->mii_status_ro = ~status; + dp->mii_phy_id = usbgem_mii_read(dp, MII_PHYIDH, &err) << 16; + if (err != USB_SUCCESS) { + DPRINTF(0, (CE_CONT, + "!%s: %s: mii_read(PHYIDH) failed", + dp->name, __func__)); + goto usberr; + } + dp->mii_phy_id |= usbgem_mii_read(dp, MII_PHYIDL, &err); + if (err != USB_SUCCESS) { + DPRINTF(0, (CE_CONT, + "!%s: %s: mii_read(PHYIDL) failed", + dp->name, __func__)); + goto usberr; + } + + if (dp->mii_phy_addr < 0) { + cmn_err(CE_CONT, "!%s: using internal/non-MII PHY(0x%08x)", + dp->name, dp->mii_phy_id); + } else { + cmn_err(CE_CONT, "!%s: MII PHY (0x%08x) found at %d", + dp->name, dp->mii_phy_id, dp->mii_phy_addr); + } + + cmn_err(CE_CONT, + "!%s: PHY control:%b, status:%b, advert:%b, lpar:%b, exp:%b", + dp->name, + usbgem_mii_read(dp, MII_CONTROL, &err), MII_CONTROL_BITS, + status, MII_STATUS_BITS, + usbgem_mii_read(dp, MII_AN_ADVERT, &err), MII_ABILITY_BITS, + usbgem_mii_read(dp, MII_AN_LPABLE, &err), MII_ABILITY_BITS, + usbgem_mii_read(dp, MII_AN_EXPANSION, &err), MII_AN_EXP_BITS); + + dp->mii_xstatus = 0; + if (status & MII_STATUS_XSTATUS) { + dp->mii_xstatus = usbgem_mii_read(dp, MII_XSTATUS, &err); + + cmn_err(CE_CONT, "!%s: xstatus:%b", + dp->name, dp->mii_xstatus, MII_XSTATUS_BITS); + } + dp->mii_xstatus_ro = ~dp->mii_xstatus; + + /* check if the phy can advertize pause abilities */ + adv_org = usbgem_mii_read(dp, MII_AN_ADVERT, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + + usbgem_mii_write(dp, MII_AN_ADVERT, + MII_ABILITY_PAUSE | MII_ABILITY_ASM_DIR, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + + adv = usbgem_mii_read(dp, MII_AN_ADVERT, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + + if ((adv & MII_ABILITY_PAUSE) == 0) { + dp->ugc.usbgc_flow_control &= ~1; + } + + if ((adv & MII_ABILITY_ASM_DIR) == 0) { + dp->ugc.usbgc_flow_control &= ~2; + } + + usbgem_mii_write(dp, MII_AN_ADVERT, adv_org, &err); + if (err != USB_SUCCESS) { + goto usberr; + } + return (USB_SUCCESS); + +usberr: + return (USB_FAILURE); +} + +int +usbgem_mii_init_default(struct usbgem_dev *dp) +{ + /* ENPTY */ + return (USB_SUCCESS); +} + +static int +usbgem_mii_start(struct usbgem_dev *dp) +{ + int err; + kthread_t *lwth; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* make a first call of usbgem_mii_link_check() */ + dp->link_watcher_stop = 0; + dp->mii_state = MII_STATE_UNKNOWN; + dp->mii_interval = drv_usectohz(1000*1000); /* 1sec */ + dp->mii_last_check = ddi_get_lbolt(); + dp->linkup_delay = 600 * drv_usectohz(1000*1000); /* 10 minutes */ + + lwth = thread_create(NULL, 0, usbgem_mii_link_watcher, dp, 0, &p0, + TS_RUN, minclsyspri); + if (lwth == NULL) { + cmn_err(CE_WARN, + "!%s: %s: failed to create a link watcher thread", + dp->name, __func__); + return (USB_FAILURE); + } + dp->link_watcher_did = lwth->t_did; + + return (USB_SUCCESS); +} + +static void +usbgem_mii_stop(struct usbgem_dev *dp) +{ + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* Ensure timer routine stopped */ + dp->link_watcher_stop = 1; + cv_signal(&dp->link_watcher_wait_cv); + thread_join(dp->link_watcher_did); +} + +/* ============================================================== */ +/* + * internal mac register operation interface + */ +/* ============================================================== */ +/* + * usbgem_mac_init: cold start + */ +static int +usbgem_mac_init(struct usbgem_dev *dp) +{ + int err; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + /* pretend we succeeded */ + return (USB_SUCCESS); + } + + ASSERT(dp->mac_state == MAC_STATE_STOPPED); + + /* reset fatal error timestamp */ + dp->fatal_error = (clock_t)0; + + /* reset tx side state */ + mutex_enter(&dp->txlock); + dp->tx_busy_cnt = 0; + dp->tx_max_packets = dp->ugc.usbgc_tx_list_max; + mutex_exit(&dp->txlock); + + /* reset rx side state */ + mutex_enter(&dp->rxlock); + dp->rx_busy_cnt = 0; + mutex_exit(&dp->rxlock); + + err = usbgem_hal_init_chip(dp); + if (err == USB_SUCCESS) { + dp->mac_state = MAC_STATE_INITIALIZED; + } + + return (err); +} + +/* + * usbgem_mac_start: warm start + */ +static int +usbgem_mac_start(struct usbgem_dev *dp) +{ + int err; + int i; + usb_flags_t flags = 0; + usb_intr_req_t *req; +#ifdef USBGEM_DEBUG_LEVEL + usb_pipe_state_t p_state; +#endif + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + /* do nothing but don't return failure */ + return (USB_SUCCESS); + } + + if (dp->mac_state != MAC_STATE_INITIALIZED) { + /* don't return failer */ + DPRINTF(0, (CE_CONT, + "!%s: %s: mac_state(%d) is not MAC_STATE_INITIALIZED", + dp->name, __func__, dp->mac_state)); + goto x; + } + + dp->mac_state = MAC_STATE_ONLINE; + + if (usbgem_hal_start_chip(dp) != USB_SUCCESS) { + cmn_err(CE_NOTE, + "!%s: %s: usb error was detected during start_chip", + dp->name, __func__); + goto x; + } + +#ifdef USBGEM_DEBUG_LEVEL + usb_pipe_get_state(dp->intr_pipe, &p_state, 0); + ASSERT(p_state == USB_PIPE_STATE_IDLE); +#endif /* USBGEM_DEBUG_LEVEL */ + + if (dp->ugc.usbgc_interrupt && dp->intr_pipe) { + + /* make a request for interrupt */ + + req = usb_alloc_intr_req(dp->dip, 0, USB_FLAGS_SLEEP); + if (req == NULL) { + cmn_err(CE_WARN, "!%s: %s: failed to allocate intreq", + dp->name, __func__); + goto x; + } + req->intr_data = NULL; + req->intr_client_private = (usb_opaque_t)dp; + req->intr_timeout = 0; + req->intr_attributes = + USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING; + req->intr_len = dp->ep_intr->wMaxPacketSize; + req->intr_cb = usbgem_intr_cb; + req->intr_exc_cb = usbgem_intr_cb; + req->intr_completion_reason = 0; + req->intr_cb_flags = 0; + + err = usb_pipe_intr_xfer(dp->intr_pipe, req, flags); + if (err != USB_SUCCESS) { + cmn_err(CE_WARN, + "%s: err:%d failed to start polling of intr pipe", + dp->name, err); + goto x; + } + } + + /* kick to receive the first packet */ + if (usbgem_init_rx_buf(dp) != USB_SUCCESS) { + goto err_stop_intr; + } + dp->rx_active = B_TRUE; + + return (USB_SUCCESS); + +err_stop_intr: + /* stop the interrupt pipe */ + DPRINTF(0, (CE_CONT, "!%s: %s: FAULURE", dp->name, __func__)); + if (dp->ugc.usbgc_interrupt && dp->intr_pipe) { + usb_pipe_stop_intr_polling(dp->intr_pipe, USB_FLAGS_SLEEP); + } +x: + ASSERT(dp->mac_state == MAC_STATE_ONLINE); + /* we use another flag to indicate error state. */ + if (dp->fatal_error == (clock_t)0) { + dp->fatal_error = usbgem_timestamp_nz(); + } + return (USB_FAILURE); +} + +static int +usbgem_mac_stop(struct usbgem_dev *dp, int new_state, boolean_t graceful) +{ + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* + * we must have writer lock for dev_state_lock + */ + ASSERT(new_state == MAC_STATE_STOPPED || + new_state == MAC_STATE_DISCONNECTED); + + /* stop polling interrupt pipe */ + if (dp->ugc.usbgc_interrupt && dp->intr_pipe) { + usb_pipe_stop_intr_polling(dp->intr_pipe, USB_FLAGS_SLEEP); + } + + if (new_state == MAC_STATE_STOPPED || graceful) { + /* stop the nic hardware completely */ + if (usbgem_hal_stop_chip(dp) != USB_SUCCESS) { + (void) usbgem_hal_reset_chip(dp); + } + } + + /* stop preparing new rx packets and sending new packets */ + dp->mac_state = new_state; + + /* other processors must get mac_state correctly after here */ + membar_producer(); + + /* cancel all requests we have sent */ + usb_pipe_reset(dp->dip, dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0); + usb_pipe_reset(dp->dip, dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0); + + DPRINTF(0, (CE_CONT, + "!%s: %s: rx_busy_cnt:%d tx_busy_cnt:%d", + dp->name, __func__, dp->rx_busy_cnt, dp->tx_busy_cnt)); + + /* + * Here all rx packets has been cancelled and their call back + * function has been exeuted, because we called usb_pipe_reset + * synchronously. + * So actually we just ensure rx_busy_cnt == 0. + */ + mutex_enter(&dp->rxlock); + while (dp->rx_busy_cnt > 0) { + cv_wait(&dp->rx_drain_cv, &dp->rxlock); + } + mutex_exit(&dp->rxlock); + + DPRINTF(0, (CE_CONT, "!%s: %s: rx_busy_cnt is %d now", + dp->name, __func__, dp->rx_busy_cnt)); + + mutex_enter(&dp->txlock); + while (dp->tx_busy_cnt > 0) { + cv_wait(&dp->tx_drain_cv, &dp->txlock); + } + mutex_exit(&dp->txlock); + + DPRINTF(0, (CE_CONT, "!%s: %s: tx_busy_cnt is %d now", + dp->name, __func__, dp->tx_busy_cnt)); + + return (USB_SUCCESS); +} + +static int +usbgem_add_multicast(struct usbgem_dev *dp, const uint8_t *ep) +{ + int cnt; + int err; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + sema_p(&dp->rxfilter_lock); + if (dp->mc_count_req++ < USBGEM_MAXMC) { + /* append the new address at the end of the mclist */ + cnt = dp->mc_count; + bcopy(ep, dp->mc_list[cnt].addr.ether_addr_octet, + ETHERADDRL); + if (dp->ugc.usbgc_multicast_hash) { + dp->mc_list[cnt].hash = + (*dp->ugc.usbgc_multicast_hash)(dp, ep); + } + dp->mc_count = cnt + 1; + } + + if (dp->mc_count_req != dp->mc_count) { + /* multicast address list overflow */ + dp->rxmode |= RXMODE_MULTI_OVF; + } else { + dp->rxmode &= ~RXMODE_MULTI_OVF; + } + + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + /* tell new multicast list to the hardware */ + err = usbgem_hal_set_rx_filter(dp); + } + sema_v(&dp->rxfilter_lock); + + return (err); +} + +static int +usbgem_remove_multicast(struct usbgem_dev *dp, const uint8_t *ep) +{ + size_t len; + int i; + int cnt; + int err; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + sema_p(&dp->rxfilter_lock); + dp->mc_count_req--; + cnt = dp->mc_count; + for (i = 0; i < cnt; i++) { + if (bcmp(ep, &dp->mc_list[i].addr, ETHERADDRL)) { + continue; + } + /* shrink the mclist by copying forward */ + len = (cnt - (i + 1)) * sizeof (*dp->mc_list); + if (len > 0) { + bcopy(&dp->mc_list[i+1], &dp->mc_list[i], len); + } + dp->mc_count--; + break; + } + + if (dp->mc_count_req != dp->mc_count) { + /* multicast address list overflow */ + dp->rxmode |= RXMODE_MULTI_OVF; + } else { + dp->rxmode &= ~RXMODE_MULTI_OVF; + } + + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + err = usbgem_hal_set_rx_filter(dp); + } + sema_v(&dp->rxfilter_lock); + + return (err); +} + + +/* ============================================================== */ +/* + * ioctl + */ +/* ============================================================== */ +enum ioc_reply { + IOC_INVAL = -1, /* bad, NAK with EINVAL */ + IOC_DONE, /* OK, reply sent */ + IOC_ACK, /* OK, just send ACK */ + IOC_REPLY, /* OK, just send reply */ + IOC_RESTART_ACK, /* OK, restart & ACK */ + IOC_RESTART_REPLY /* OK, restart & reply */ +}; + + +#ifdef USBGEM_CONFIG_MAC_PROP +static int +usbgem_get_def_val(struct usbgem_dev *dp, mac_prop_id_t pr_num, + uint_t pr_valsize, void *pr_val) +{ + link_flowctrl_t fl; + int err = 0; + + ASSERT(pr_valsize > 0); + switch (pr_num) { + case MAC_PROP_AUTONEG: + *(uint8_t *)pr_val = + BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG); + break; + + case MAC_PROP_FLOWCTRL: + if (pr_valsize < sizeof (link_flowctrl_t)) { + return (EINVAL); + } + switch (dp->ugc.usbgc_flow_control) { + case FLOW_CONTROL_NONE: + fl = LINK_FLOWCTRL_NONE; + break; + case FLOW_CONTROL_SYMMETRIC: + fl = LINK_FLOWCTRL_BI; + break; + case FLOW_CONTROL_TX_PAUSE: + fl = LINK_FLOWCTRL_TX; + break; + case FLOW_CONTROL_RX_PAUSE: + fl = LINK_FLOWCTRL_RX; + break; + } + bcopy(&fl, pr_val, sizeof (fl)); + break; + + case MAC_PROP_ADV_1000FDX_CAP: + case MAC_PROP_EN_1000FDX_CAP: + *(uint8_t *)pr_val = + (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD); + break; + + case MAC_PROP_ADV_1000HDX_CAP: + case MAC_PROP_EN_1000HDX_CAP: + *(uint8_t *)pr_val = + (dp->mii_xstatus & MII_XSTATUS_1000BASET) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX); + break; + + case MAC_PROP_ADV_100T4_CAP: + case MAC_PROP_EN_100T4_CAP: + *(uint8_t *)pr_val = + BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4); + break; + + case MAC_PROP_ADV_100FDX_CAP: + case MAC_PROP_EN_100FDX_CAP: + *(uint8_t *)pr_val = + BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD); + break; + + case MAC_PROP_ADV_100HDX_CAP: + case MAC_PROP_EN_100HDX_CAP: + *(uint8_t *)pr_val = + BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX); + break; + + case MAC_PROP_ADV_10FDX_CAP: + case MAC_PROP_EN_10FDX_CAP: + *(uint8_t *)pr_val = + BOOLEAN(dp->mii_status & MII_STATUS_10_FD); + break; + + case MAC_PROP_ADV_10HDX_CAP: + case MAC_PROP_EN_10HDX_CAP: + *(uint8_t *)pr_val = + BOOLEAN(dp->mii_status & MII_STATUS_10); + break; + + default: + err = ENOTSUP; + break; + } + return (err); +} + +#ifdef MAC_VERSION_V1 +static void +usbgem_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num, + mac_prop_info_handle_t prh) +{ + struct usbgem_dev *dp = arg; + link_flowctrl_t fl; + + /* + * By default permissions are read/write unless specified + * otherwise by the driver. + */ + + switch (pr_num) { + case MAC_PROP_DUPLEX: + case MAC_PROP_SPEED: + case MAC_PROP_STATUS: + case MAC_PROP_ADV_1000FDX_CAP: + case MAC_PROP_ADV_1000HDX_CAP: + case MAC_PROP_ADV_100FDX_CAP: + case MAC_PROP_ADV_100HDX_CAP: + case MAC_PROP_ADV_10FDX_CAP: + case MAC_PROP_ADV_10HDX_CAP: + case MAC_PROP_ADV_100T4_CAP: + case MAC_PROP_EN_100T4_CAP: + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + break; + + case MAC_PROP_EN_1000FDX_CAP: + if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN( + dp->mii_xstatus & MII_XSTATUS_1000BASET_FD)); + } else if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD) + == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN( + dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD)); + } else { + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + } + break; + + case MAC_PROP_EN_1000HDX_CAP: + if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN( + dp->mii_xstatus & MII_XSTATUS_1000BASET)); + } else if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN( + dp->mii_xstatus & MII_XSTATUS_1000BASEX)); + } else { + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + } + break; + + case MAC_PROP_EN_100FDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_100_BASEX_FD) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD)); + } else { + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + } + break; + + case MAC_PROP_EN_100HDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_100_BASEX) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX)); + } else { + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + } + break; + + case MAC_PROP_EN_10FDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN(dp->mii_status & MII_STATUS_10_FD)); + } else { + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + } + break; + + case MAC_PROP_EN_10HDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_10) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN(dp->mii_status & MII_STATUS_10)); + } else { + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + } + break; + + case MAC_PROP_AUTONEG: + if ((dp->mii_status_ro & MII_STATUS_CANAUTONEG) == 0) { + mac_prop_info_set_default_uint8(prh, + BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG)); + } else { + mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); + } + break; + + case MAC_PROP_FLOWCTRL: + switch (dp->ugc.usbgc_flow_control) { + case FLOW_CONTROL_NONE: + fl = LINK_FLOWCTRL_NONE; + break; + case FLOW_CONTROL_SYMMETRIC: + fl = LINK_FLOWCTRL_BI; + break; + case FLOW_CONTROL_TX_PAUSE: + fl = LINK_FLOWCTRL_TX; + break; + case FLOW_CONTROL_RX_PAUSE: + fl = LINK_FLOWCTRL_RX; + break; + } + mac_prop_info_set_default_link_flowctrl(prh, fl); + break; + + case MAC_PROP_MTU: + mac_prop_info_set_range_uint32(prh, + dp->ugc.usbgc_min_mtu, dp->ugc.usbgc_max_mtu); + break; + + case MAC_PROP_PRIVATE: + break; + } +} +#endif + +static int +usbgem_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num, + uint_t pr_valsize, const void *pr_val) +{ + struct usbgem_dev *dp = arg; + int err = 0; + boolean_t update = B_FALSE; + link_flowctrl_t flowctrl; + uint32_t cur_mtu, new_mtu; + + rw_enter(&dp->dev_state_lock, RW_WRITER); + switch (pr_num) { + case MAC_PROP_EN_1000FDX_CAP: + if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) == 0 || + (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD) == 0) { + if (dp->anadv_1000fdx != *(uint8_t *)pr_val) { + dp->anadv_1000fdx = *(uint8_t *)pr_val; + update = B_TRUE; + } + } else { + err = ENOTSUP; + } + break; + + case MAC_PROP_EN_1000HDX_CAP: + if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) == 0 || + (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX) == 0) { + if (dp->anadv_1000hdx != *(uint8_t *)pr_val) { + dp->anadv_1000hdx = *(uint8_t *)pr_val; + update = B_TRUE; + } + } else { + err = ENOTSUP; + } + break; + + case MAC_PROP_EN_100FDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_100_BASEX_FD) == 0) { + if (dp->anadv_100fdx != *(uint8_t *)pr_val) { + dp->anadv_100fdx = *(uint8_t *)pr_val; + update = B_TRUE; + } + } else { + err = ENOTSUP; + } + break; + + case MAC_PROP_EN_100HDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_100_BASEX) == 0) { + if (dp->anadv_100hdx != *(uint8_t *)pr_val) { + dp->anadv_100hdx = *(uint8_t *)pr_val; + update = B_TRUE; + } + } else { + err = ENOTSUP; + } + break; + + case MAC_PROP_EN_10FDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) { + if (dp->anadv_10fdx != *(uint8_t *)pr_val) { + dp->anadv_10fdx = *(uint8_t *)pr_val; + update = B_TRUE; + } + } else { + err = ENOTSUP; + } + break; + + case MAC_PROP_EN_10HDX_CAP: + if ((dp->mii_status_ro & MII_STATUS_10_FD) == 0) { + if (dp->anadv_10hdx != *(uint8_t *)pr_val) { + dp->anadv_10hdx = *(uint8_t *)pr_val; + update = B_TRUE; + } + } else { + err = ENOTSUP; + } + break; + + case MAC_PROP_AUTONEG: + if ((dp->mii_status_ro & MII_STATUS_CANAUTONEG) == 0) { + if (dp->anadv_autoneg != *(uint8_t *)pr_val) { + dp->anadv_autoneg = *(uint8_t *)pr_val; + update = B_TRUE; + } + } else { + err = ENOTSUP; + } + break; + + case MAC_PROP_FLOWCTRL: + bcopy(pr_val, &flowctrl, sizeof (flowctrl)); + + switch (flowctrl) { + default: + err = EINVAL; + break; + + case LINK_FLOWCTRL_NONE: + if (dp->flow_control != FLOW_CONTROL_NONE) { + dp->flow_control = FLOW_CONTROL_NONE; + update = B_TRUE; + } + break; + + case LINK_FLOWCTRL_RX: + if (dp->flow_control != FLOW_CONTROL_RX_PAUSE) { + dp->flow_control = FLOW_CONTROL_RX_PAUSE; + update = B_TRUE; + } + break; + + case LINK_FLOWCTRL_TX: + if (dp->flow_control != FLOW_CONTROL_TX_PAUSE) { + dp->flow_control = FLOW_CONTROL_TX_PAUSE; + update = B_TRUE; + } + break; + + case LINK_FLOWCTRL_BI: + if (dp->flow_control != FLOW_CONTROL_SYMMETRIC) { + dp->flow_control = FLOW_CONTROL_SYMMETRIC; + update = B_TRUE; + } + break; + } + break; + + case MAC_PROP_ADV_1000FDX_CAP: + case MAC_PROP_ADV_1000HDX_CAP: + case MAC_PROP_ADV_100FDX_CAP: + case MAC_PROP_ADV_100HDX_CAP: + case MAC_PROP_ADV_10FDX_CAP: + case MAC_PROP_ADV_10HDX_CAP: + case MAC_PROP_STATUS: + case MAC_PROP_SPEED: + case MAC_PROP_DUPLEX: + err = ENOTSUP; /* read-only prop. Can't set this. */ + break; + + case MAC_PROP_MTU: + bcopy(pr_val, &new_mtu, sizeof (new_mtu)); + if (new_mtu != dp->mtu) { + err = EINVAL; + } + break; + + case MAC_PROP_PRIVATE: + err = ENOTSUP; + break; + + default: + err = ENOTSUP; + break; + } + + if (update) { + /* sync with PHY */ + usbgem_choose_forcedmode(dp); + dp->mii_state = MII_STATE_UNKNOWN; + cv_signal(&dp->link_watcher_wait_cv); + } + rw_exit(&dp->dev_state_lock); + return (err); +} + +static int +#ifdef MAC_VERSION_V1 +usbgem_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num, + uint_t pr_valsize, void *pr_val) +#else +usbgem_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num, + uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm) +#endif +{ + struct usbgem_dev *dp = arg; + int err = 0; + link_flowctrl_t flowctrl; + uint64_t tmp = 0; + + if (pr_valsize == 0) { + return (EINVAL); + } +#ifndef MAC_VERSION_V1 + *perm = MAC_PROP_PERM_RW; +#endif + bzero(pr_val, pr_valsize); +#ifndef MAC_VERSION_V1 + if ((pr_flags & MAC_PROP_DEFAULT) && (pr_num != MAC_PROP_PRIVATE)) { + return (usbgem_get_def_val(dp, pr_num, pr_valsize, pr_val)); + } +#endif + rw_enter(&dp->dev_state_lock, RW_READER); + switch (pr_num) { + case MAC_PROP_DUPLEX: +#ifndef MAC_VERSION_V1 + *perm = MAC_PROP_PERM_READ; +#endif + if (pr_valsize >= sizeof (link_duplex_t)) { + if (dp->mii_state != MII_STATE_LINKUP) { + *(link_duplex_t *)pr_val = LINK_DUPLEX_UNKNOWN; + } else if (dp->full_duplex) { + *(link_duplex_t *)pr_val = LINK_DUPLEX_FULL; + } else { + *(link_duplex_t *)pr_val = LINK_DUPLEX_HALF; + } + } else { + err = EINVAL; + } + break; + case MAC_PROP_SPEED: +#ifndef MAC_VERSION_V1 + *perm = MAC_PROP_PERM_READ; +#endif + if (pr_valsize >= sizeof (uint64_t)) { + switch (dp->speed) { + case USBGEM_SPD_1000: + tmp = 1000000000; + break; + case USBGEM_SPD_100: + tmp = 100000000; + break; + case USBGEM_SPD_10: + tmp = 10000000; + break; + default: + tmp = 0; + } + bcopy(&tmp, pr_val, sizeof (tmp)); + } else { + err = EINVAL; + } + break; + + case MAC_PROP_AUTONEG: +#ifndef MAC_VERSION_V1 + if (dp->mii_status_ro & MII_STATUS_CANAUTONEG) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_autoneg; + break; + + case MAC_PROP_FLOWCTRL: + if (pr_valsize >= sizeof (link_flowctrl_t)) { + switch (dp->flow_control) { + case FLOW_CONTROL_NONE: + flowctrl = LINK_FLOWCTRL_NONE; + break; + case FLOW_CONTROL_RX_PAUSE: + flowctrl = LINK_FLOWCTRL_RX; + break; + case FLOW_CONTROL_TX_PAUSE: + flowctrl = LINK_FLOWCTRL_TX; + break; + case FLOW_CONTROL_SYMMETRIC: + flowctrl = LINK_FLOWCTRL_BI; + break; + } + bcopy(&flowctrl, pr_val, sizeof (flowctrl)); + } else { + err = EINVAL; + } + break; + + case MAC_PROP_ADV_1000FDX_CAP: + case MAC_PROP_ADV_1000HDX_CAP: + case MAC_PROP_ADV_100FDX_CAP: + case MAC_PROP_ADV_100HDX_CAP: + case MAC_PROP_ADV_10FDX_CAP: + case MAC_PROP_ADV_10HDX_CAP: + case MAC_PROP_ADV_100T4_CAP: + usbgem_get_def_val(dp, pr_num, pr_valsize, pr_val); + break; + + case MAC_PROP_EN_1000FDX_CAP: +#ifndef MAC_VERSION_V1 + if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET_FD) && + (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX_FD)) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_1000fdx; + break; + + case MAC_PROP_EN_1000HDX_CAP: +#ifndef MAC_VERSION_V1 + if ((dp->mii_xstatus_ro & MII_XSTATUS_1000BASET) && + (dp->mii_xstatus_ro & MII_XSTATUS_1000BASEX)) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_1000hdx; + break; + + case MAC_PROP_EN_100FDX_CAP: +#ifndef MAC_VERSION_V1 + if (dp->mii_status_ro & MII_STATUS_100_BASEX_FD) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_100fdx; + break; + + case MAC_PROP_EN_100HDX_CAP: +#ifndef MAC_VERSION_V1 + if (dp->mii_status_ro & MII_STATUS_100_BASEX) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_100hdx; + break; + + case MAC_PROP_EN_10FDX_CAP: +#ifndef MAC_VERSION_V1 + if (dp->mii_status_ro & MII_STATUS_10_FD) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_10fdx; + break; + + case MAC_PROP_EN_10HDX_CAP: +#ifndef MAC_VERSION_V1 + if (dp->mii_status_ro & MII_STATUS_10) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_10hdx; + break; + + case MAC_PROP_EN_100T4_CAP: +#ifndef MAC_VERSION_V1 + if (dp->mii_status_ro & MII_STATUS_100_BASE_T4) { + *perm = MAC_PROP_PERM_READ; + } +#endif + *(uint8_t *)pr_val = dp->anadv_100t4; + break; + + case MAC_PROP_PRIVATE: + err = ENOTSUP; + break; + +#ifndef MAC_VERSION_V1 + case MAC_PROP_MTU: { + mac_propval_range_t range; + if (!(pr_flags & MAC_PROP_POSSIBLE)) { + err = ENOTSUP; + break; + } + if (pr_valsize < sizeof (mac_propval_range_t)) { + err = EINVAL; + break; + } + range.mpr_count = 1; + range.mpr_type = MAC_PROPVAL_UINT32; + range.range_uint32[0].mpur_min = ETHERMTU; + range.range_uint32[0].mpur_max = dp->mtu; + bcopy(&range, pr_val, sizeof (range)); + break; + } +#endif + default: + err = ENOTSUP; + break; + } + + rw_exit(&dp->dev_state_lock); + return (err); +} +#endif /* USBGEM_CONFIG_MAC_PROP */ + +#ifdef USBGEM_CONFIG_ND +/* ============================================================== */ +/* + * ND interface + */ +/* ============================================================== */ +enum { + PARAM_AUTONEG_CAP, + PARAM_PAUSE_CAP, + PARAM_ASYM_PAUSE_CAP, + PARAM_1000FDX_CAP, + PARAM_1000HDX_CAP, + PARAM_100T4_CAP, + PARAM_100FDX_CAP, + PARAM_100HDX_CAP, + PARAM_10FDX_CAP, + PARAM_10HDX_CAP, + + PARAM_ADV_AUTONEG_CAP, + PARAM_ADV_PAUSE_CAP, + PARAM_ADV_ASYM_PAUSE_CAP, + PARAM_ADV_1000FDX_CAP, + PARAM_ADV_1000HDX_CAP, + PARAM_ADV_100T4_CAP, + PARAM_ADV_100FDX_CAP, + PARAM_ADV_100HDX_CAP, + PARAM_ADV_10FDX_CAP, + PARAM_ADV_10HDX_CAP, + PARAM_ADV_1000T_MS, + + PARAM_LP_AUTONEG_CAP, + PARAM_LP_PAUSE_CAP, + PARAM_LP_ASYM_PAUSE_CAP, + PARAM_LP_1000FDX_CAP, + PARAM_LP_1000HDX_CAP, + PARAM_LP_100T4_CAP, + PARAM_LP_100FDX_CAP, + PARAM_LP_100HDX_CAP, + PARAM_LP_10FDX_CAP, + PARAM_LP_10HDX_CAP, + + PARAM_LINK_STATUS, + PARAM_LINK_SPEED, + PARAM_LINK_DUPLEX, + + PARAM_LINK_AUTONEG, + PARAM_LINK_RX_PAUSE, + PARAM_LINK_TX_PAUSE, + + PARAM_LOOP_MODE, + PARAM_MSI_CNT, +#ifdef DEBUG_RESUME + PARAM_RESUME_TEST, +#endif + + PARAM_COUNT +}; + +struct usbgem_nd_arg { + struct usbgem_dev *dp; + int item; +}; + +static int +usbgem_param_get(queue_t *q, mblk_t *mp, caddr_t arg, cred_t *credp) +{ + struct usbgem_dev *dp = ((struct usbgem_nd_arg *)(void *)arg)->dp; + int item = ((struct usbgem_nd_arg *)(void *)arg)->item; + long val; + + DPRINTF(1, (CE_CONT, "!%s: %s: called, item:%d", + dp->name, __func__, item)); + + switch (item) { + case PARAM_AUTONEG_CAP: + val = BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG); + DPRINTF(1, (CE_CONT, "autoneg_cap:%d", val)); + break; + + case PARAM_PAUSE_CAP: + val = dp->ugc.usbgc_flow_control != FLOW_CONTROL_NONE; + break; + + case PARAM_ASYM_PAUSE_CAP: + val = dp->ugc.usbgc_flow_control > FLOW_CONTROL_SYMMETRIC; + break; + + case PARAM_1000FDX_CAP: + val = (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD); + break; + + case PARAM_1000HDX_CAP: + val = (dp->mii_xstatus & MII_XSTATUS_1000BASET) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX); + break; + + case PARAM_100T4_CAP: + val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4); + break; + + case PARAM_100FDX_CAP: + val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD); + break; + + case PARAM_100HDX_CAP: + val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX); + break; + + case PARAM_10FDX_CAP: + val = BOOLEAN(dp->mii_status & MII_STATUS_10_FD); + break; + + case PARAM_10HDX_CAP: + val = BOOLEAN(dp->mii_status & MII_STATUS_10); + break; + + case PARAM_ADV_AUTONEG_CAP: + val = dp->anadv_autoneg; + break; + + case PARAM_ADV_PAUSE_CAP: + val = dp->anadv_pause; + break; + + case PARAM_ADV_ASYM_PAUSE_CAP: + val = dp->anadv_asmpause; + break; + + case PARAM_ADV_1000FDX_CAP: + val = dp->anadv_1000fdx; + break; + + case PARAM_ADV_1000HDX_CAP: + val = dp->anadv_1000hdx; + break; + + case PARAM_ADV_100T4_CAP: + val = dp->anadv_100t4; + break; + + case PARAM_ADV_100FDX_CAP: + val = dp->anadv_100fdx; + break; + + case PARAM_ADV_100HDX_CAP: + val = dp->anadv_100hdx; + break; + + case PARAM_ADV_10FDX_CAP: + val = dp->anadv_10fdx; + break; + + case PARAM_ADV_10HDX_CAP: + val = dp->anadv_10hdx; + break; + + case PARAM_ADV_1000T_MS: + val = dp->anadv_1000t_ms; + break; + + case PARAM_LP_AUTONEG_CAP: + val = BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN); + break; + + case PARAM_LP_PAUSE_CAP: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_PAUSE); + break; + + case PARAM_LP_ASYM_PAUSE_CAP: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_ASM_DIR); + break; + + case PARAM_LP_1000FDX_CAP: + val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_FULL); + break; + + case PARAM_LP_1000HDX_CAP: + val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_HALF); + break; + + case PARAM_LP_100T4_CAP: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_T4); + break; + + case PARAM_LP_100FDX_CAP: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX_FD); + break; + + case PARAM_LP_100HDX_CAP: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX); + break; + + case PARAM_LP_10FDX_CAP: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T_FD); + break; + + case PARAM_LP_10HDX_CAP: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T); + break; + + case PARAM_LINK_STATUS: + val = (dp->mii_state == MII_STATE_LINKUP); + break; + + case PARAM_LINK_SPEED: + val = usbgem_speed_value[dp->speed]; + break; + + case PARAM_LINK_DUPLEX: + val = 0; + if (dp->mii_state == MII_STATE_LINKUP) { + val = dp->full_duplex ? 2 : 1; + } + break; + + case PARAM_LINK_AUTONEG: + val = BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN); + break; + + case PARAM_LINK_RX_PAUSE: + val = (dp->flow_control == FLOW_CONTROL_SYMMETRIC) || + (dp->flow_control == FLOW_CONTROL_RX_PAUSE); + break; + + case PARAM_LINK_TX_PAUSE: + val = (dp->flow_control == FLOW_CONTROL_SYMMETRIC) || + (dp->flow_control == FLOW_CONTROL_TX_PAUSE); + break; + +#ifdef DEBUG_RESUME + case PARAM_RESUME_TEST: + val = 0; + break; +#endif + default: + cmn_err(CE_WARN, "%s: unimplemented ndd control (%d)", + dp->name, item); + break; + } + + (void) mi_mpprintf(mp, "%ld", val); + + return (0); +} + +static int +usbgem_param_set(queue_t *q, + mblk_t *mp, char *value, caddr_t arg, cred_t *credp) +{ + struct usbgem_dev *dp = ((struct usbgem_nd_arg *)(void *)arg)->dp; + int item = ((struct usbgem_nd_arg *)(void *)arg)->item; + long val; + char *end; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + if (ddi_strtol(value, &end, 10, &val)) { + return (EINVAL); + } + if (end == value) { + return (EINVAL); + } + + switch (item) { + case PARAM_ADV_AUTONEG_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_status & MII_STATUS_CANAUTONEG) == 0) { + goto err; + } + dp->anadv_autoneg = (int)val; + break; + + case PARAM_ADV_PAUSE_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && dp->ugc.usbgc_flow_control == FLOW_CONTROL_NONE) { + goto err; + } + dp->anadv_pause = (int)val; + break; + + case PARAM_ADV_ASYM_PAUSE_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && + dp->ugc.usbgc_flow_control <= FLOW_CONTROL_SYMMETRIC) { + goto err; + } + dp->anadv_asmpause = (int)val; + break; + + case PARAM_ADV_1000FDX_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_xstatus & + (MII_XSTATUS_1000BASET_FD | + MII_XSTATUS_1000BASEX_FD)) == 0) { + goto err; + } + dp->anadv_1000fdx = (int)val; + break; + + case PARAM_ADV_1000HDX_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_xstatus & + (MII_XSTATUS_1000BASET | MII_XSTATUS_1000BASEX)) == 0) { + goto err; + } + dp->anadv_1000hdx = (int)val; + break; + + case PARAM_ADV_100T4_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_status & MII_STATUS_100_BASE_T4) == 0) { + goto err; + } + dp->anadv_100t4 = (int)val; + break; + + case PARAM_ADV_100FDX_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_status & MII_STATUS_100_BASEX_FD) == 0) { + goto err; + } + dp->anadv_100fdx = (int)val; + break; + + case PARAM_ADV_100HDX_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_status & MII_STATUS_100_BASEX) == 0) { + goto err; + } + dp->anadv_100hdx = (int)val; + break; + + case PARAM_ADV_10FDX_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_status & MII_STATUS_10_FD) == 0) { + goto err; + } + dp->anadv_10fdx = (int)val; + break; + + case PARAM_ADV_10HDX_CAP: + if (val != 0 && val != 1) { + goto err; + } + if (val && (dp->mii_status & MII_STATUS_10) == 0) { + goto err; + } + dp->anadv_10hdx = (int)val; + break; + + case PARAM_ADV_1000T_MS: + if (val != 0 && val != 1 && val != 2) { + goto err; + } + if (val && (dp->mii_xstatus & + (MII_XSTATUS_1000BASET | MII_XSTATUS_1000BASET_FD)) == 0) { + goto err; + } + dp->anadv_1000t_ms = (int)val; + break; + +#ifdef DEBUG_RESUME + case PARAM_RESUME_TEST: + mutex_exit(&dp->xmitlock); + mutex_exit(&dp->intrlock); + gem_suspend(dp->dip); + gem_resume(dp->dip); + mutex_enter(&dp->intrlock); + mutex_enter(&dp->xmitlock); + break; +#endif + } + + /* sync with PHY */ + usbgem_choose_forcedmode(dp); + + dp->mii_state = MII_STATE_UNKNOWN; + if (dp->ugc.usbgc_mii_hw_link_detection) { + /* wake up link watcher possiblely sleeps */ + cv_signal(&dp->link_watcher_wait_cv); + } + + return (0); +err: + return (EINVAL); +} + +static void +usbgem_nd_load(struct usbgem_dev *dp, + char *name, ndgetf_t gf, ndsetf_t sf, int item) +{ + struct usbgem_nd_arg *arg; + + ASSERT(item >= 0); + ASSERT(item < PARAM_COUNT); + + arg = &((struct usbgem_nd_arg *)(void *)dp->nd_arg_p)[item]; + arg->dp = dp; + arg->item = item; + + DPRINTF(2, (CE_CONT, "!%s: %s: name:%s, item:%d", + dp->name, __func__, name, item)); + (void) nd_load(&dp->nd_data_p, name, gf, sf, (caddr_t)arg); +} + +static void +usbgem_nd_setup(struct usbgem_dev *dp) +{ + DPRINTF(1, (CE_CONT, "!%s: %s: called, mii_status:0x%b", + dp->name, __func__, dp->mii_status, MII_STATUS_BITS)); + + ASSERT(dp->nd_arg_p == NULL); + + dp->nd_arg_p = + kmem_zalloc(sizeof (struct usbgem_nd_arg) * PARAM_COUNT, KM_SLEEP); + +#define SETFUNC(x) ((x) ? usbgem_param_set : NULL) + + usbgem_nd_load(dp, "autoneg_cap", + usbgem_param_get, NULL, PARAM_AUTONEG_CAP); + usbgem_nd_load(dp, "pause_cap", + usbgem_param_get, NULL, PARAM_PAUSE_CAP); + usbgem_nd_load(dp, "asym_pause_cap", + usbgem_param_get, NULL, PARAM_ASYM_PAUSE_CAP); + usbgem_nd_load(dp, "1000fdx_cap", + usbgem_param_get, NULL, PARAM_1000FDX_CAP); + usbgem_nd_load(dp, "1000hdx_cap", + usbgem_param_get, NULL, PARAM_1000HDX_CAP); + usbgem_nd_load(dp, "100T4_cap", + usbgem_param_get, NULL, PARAM_100T4_CAP); + usbgem_nd_load(dp, "100fdx_cap", + usbgem_param_get, NULL, PARAM_100FDX_CAP); + usbgem_nd_load(dp, "100hdx_cap", + usbgem_param_get, NULL, PARAM_100HDX_CAP); + usbgem_nd_load(dp, "10fdx_cap", + usbgem_param_get, NULL, PARAM_10FDX_CAP); + usbgem_nd_load(dp, "10hdx_cap", + usbgem_param_get, NULL, PARAM_10HDX_CAP); + + /* Our advertised capabilities */ + usbgem_nd_load(dp, "adv_autoneg_cap", usbgem_param_get, + SETFUNC(dp->mii_status & MII_STATUS_CANAUTONEG), + PARAM_ADV_AUTONEG_CAP); + usbgem_nd_load(dp, "adv_pause_cap", usbgem_param_get, + SETFUNC(dp->ugc.usbgc_flow_control & 1), + PARAM_ADV_PAUSE_CAP); + usbgem_nd_load(dp, "adv_asym_pause_cap", usbgem_param_get, + SETFUNC(dp->ugc.usbgc_flow_control & 2), + PARAM_ADV_ASYM_PAUSE_CAP); + usbgem_nd_load(dp, "adv_1000fdx_cap", usbgem_param_get, + SETFUNC(dp->mii_xstatus & + (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASET_FD)), + PARAM_ADV_1000FDX_CAP); + usbgem_nd_load(dp, "adv_1000hdx_cap", usbgem_param_get, + SETFUNC(dp->mii_xstatus & + (MII_XSTATUS_1000BASEX | MII_XSTATUS_1000BASET)), + PARAM_ADV_1000HDX_CAP); + usbgem_nd_load(dp, "adv_100T4_cap", usbgem_param_get, + SETFUNC((dp->mii_status & MII_STATUS_100_BASE_T4) && + !dp->mii_advert_ro), + PARAM_ADV_100T4_CAP); + usbgem_nd_load(dp, "adv_100fdx_cap", usbgem_param_get, + SETFUNC((dp->mii_status & MII_STATUS_100_BASEX_FD) && + !dp->mii_advert_ro), + PARAM_ADV_100FDX_CAP); + usbgem_nd_load(dp, "adv_100hdx_cap", usbgem_param_get, + SETFUNC((dp->mii_status & MII_STATUS_100_BASEX) && + !dp->mii_advert_ro), + PARAM_ADV_100HDX_CAP); + usbgem_nd_load(dp, "adv_10fdx_cap", usbgem_param_get, + SETFUNC((dp->mii_status & MII_STATUS_10_FD) && + !dp->mii_advert_ro), + PARAM_ADV_10FDX_CAP); + usbgem_nd_load(dp, "adv_10hdx_cap", usbgem_param_get, + SETFUNC((dp->mii_status & MII_STATUS_10) && + !dp->mii_advert_ro), + PARAM_ADV_10HDX_CAP); + usbgem_nd_load(dp, "adv_1000t_ms", usbgem_param_get, + SETFUNC(dp->mii_xstatus & + (MII_XSTATUS_1000BASET_FD | MII_XSTATUS_1000BASET)), + PARAM_ADV_1000T_MS); + + + /* Partner's advertised capabilities */ + usbgem_nd_load(dp, "lp_autoneg_cap", + usbgem_param_get, NULL, PARAM_LP_AUTONEG_CAP); + usbgem_nd_load(dp, "lp_pause_cap", + usbgem_param_get, NULL, PARAM_LP_PAUSE_CAP); + usbgem_nd_load(dp, "lp_asym_pause_cap", + usbgem_param_get, NULL, PARAM_LP_ASYM_PAUSE_CAP); + usbgem_nd_load(dp, "lp_1000fdx_cap", + usbgem_param_get, NULL, PARAM_LP_1000FDX_CAP); + usbgem_nd_load(dp, "lp_1000hdx_cap", + usbgem_param_get, NULL, PARAM_LP_1000HDX_CAP); + usbgem_nd_load(dp, "lp_100T4_cap", + usbgem_param_get, NULL, PARAM_LP_100T4_CAP); + usbgem_nd_load(dp, "lp_100fdx_cap", + usbgem_param_get, NULL, PARAM_LP_100FDX_CAP); + usbgem_nd_load(dp, "lp_100hdx_cap", + usbgem_param_get, NULL, PARAM_LP_100HDX_CAP); + usbgem_nd_load(dp, "lp_10fdx_cap", + usbgem_param_get, NULL, PARAM_LP_10FDX_CAP); + usbgem_nd_load(dp, "lp_10hdx_cap", + usbgem_param_get, NULL, PARAM_LP_10HDX_CAP); + + /* Current operating modes */ + usbgem_nd_load(dp, "link_status", + usbgem_param_get, NULL, PARAM_LINK_STATUS); + usbgem_nd_load(dp, "link_speed", + usbgem_param_get, NULL, PARAM_LINK_SPEED); + usbgem_nd_load(dp, "link_duplex", + usbgem_param_get, NULL, PARAM_LINK_DUPLEX); + usbgem_nd_load(dp, "link_autoneg", + usbgem_param_get, NULL, PARAM_LINK_AUTONEG); + usbgem_nd_load(dp, "link_rx_pause", + usbgem_param_get, NULL, PARAM_LINK_RX_PAUSE); + usbgem_nd_load(dp, "link_tx_pause", + usbgem_param_get, NULL, PARAM_LINK_TX_PAUSE); +#ifdef DEBUG_RESUME + usbgem_nd_load(dp, "resume_test", + usbgem_param_get, usbgem_param_set, PARAM_RESUME_TEST); +#endif +#undef SETFUNC +} + +static +enum ioc_reply +usbgem_nd_ioctl(struct usbgem_dev *dp, + queue_t *wq, mblk_t *mp, struct iocblk *iocp) +{ + boolean_t ok; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + switch (iocp->ioc_cmd) { + case ND_GET: + ok = nd_getset(wq, dp->nd_data_p, mp); + DPRINTF(1, (CE_CONT, + "%s: get %s", dp->name, ok ? "OK" : "FAIL")); + return (ok ? IOC_REPLY : IOC_INVAL); + + case ND_SET: + ok = nd_getset(wq, dp->nd_data_p, mp); + + DPRINTF(1, (CE_CONT, "%s: set %s err %d", + dp->name, ok ? "OK" : "FAIL", iocp->ioc_error)); + + if (!ok) { + return (IOC_INVAL); + } + + if (iocp->ioc_error) { + return (IOC_REPLY); + } + + return (IOC_RESTART_REPLY); + } + + cmn_err(CE_WARN, "%s: invalid cmd 0x%x", dp->name, iocp->ioc_cmd); + + return (IOC_INVAL); +} + +static void +usbgem_nd_cleanup(struct usbgem_dev *dp) +{ + ASSERT(dp->nd_data_p != NULL); + ASSERT(dp->nd_arg_p != NULL); + + nd_free(&dp->nd_data_p); + + kmem_free(dp->nd_arg_p, sizeof (struct usbgem_nd_arg) * PARAM_COUNT); + dp->nd_arg_p = NULL; +} +#endif /* USBGEM_CONFIG_ND */ + +static void +usbgem_mac_ioctl(struct usbgem_dev *dp, queue_t *wq, mblk_t *mp) +{ + struct iocblk *iocp; + enum ioc_reply status; + int cmd; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* + * Validate the command before bothering with the mutex ... + */ + iocp = (void *)mp->b_rptr; + iocp->ioc_error = 0; + cmd = iocp->ioc_cmd; + + DPRINTF(1, (CE_CONT, "%s: %s cmd:0x%x", dp->name, __func__, cmd)); + +#ifdef USBGEM_CONFIG_ND + switch (cmd) { + default: + _NOTE(NOTREACHED) + status = IOC_INVAL; + break; + + case ND_GET: + case ND_SET: + status = usbgem_nd_ioctl(dp, wq, mp, iocp); + break; + } + + /* + * Finally, decide how to reply + */ + switch (status) { + default: + case IOC_INVAL: + /* + * Error, reply with a NAK and EINVAL or the specified error + */ + miocnak(wq, mp, 0, iocp->ioc_error == 0 ? + EINVAL : iocp->ioc_error); + break; + + case IOC_DONE: + /* + * OK, reply already sent + */ + break; + + case IOC_RESTART_ACK: + case IOC_ACK: + /* + * OK, reply with an ACK + */ + miocack(wq, mp, 0, 0); + break; + + case IOC_RESTART_REPLY: + case IOC_REPLY: + /* + * OK, send prepared reply as ACK or NAK + */ + mp->b_datap->db_type = + iocp->ioc_error == 0 ? M_IOCACK : M_IOCNAK; + qreply(wq, mp); + break; + } +#else + miocnak(wq, mp, 0, EINVAL); + return; +#endif /* USBGEM_CONFIG_GLDv3 */ +} + +#ifndef SYS_MAC_H +#define XCVR_UNDEFINED 0 +#define XCVR_NONE 1 +#define XCVR_10 2 +#define XCVR_100T4 3 +#define XCVR_100X 4 +#define XCVR_100T2 5 +#define XCVR_1000X 6 +#define XCVR_1000T 7 +#endif +static int +usbgem_mac_xcvr_inuse(struct usbgem_dev *dp) +{ + int val = XCVR_UNDEFINED; + + if ((dp->mii_status & MII_STATUS_XSTATUS) == 0) { + if (dp->mii_status & MII_STATUS_100_BASE_T4) { + val = XCVR_100T4; + } else if (dp->mii_status & + (MII_STATUS_100_BASEX_FD | + MII_STATUS_100_BASEX)) { + val = XCVR_100X; + } else if (dp->mii_status & + (MII_STATUS_100_BASE_T2_FD | + MII_STATUS_100_BASE_T2)) { + val = XCVR_100T2; + } else if (dp->mii_status & + (MII_STATUS_10_FD | MII_STATUS_10)) { + val = XCVR_10; + } + } else if (dp->mii_xstatus & + (MII_XSTATUS_1000BASET_FD | MII_XSTATUS_1000BASET)) { + val = XCVR_1000T; + } else if (dp->mii_xstatus & + (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASEX)) { + val = XCVR_1000X; + } + + return (val); +} + +#ifdef USBGEM_CONFIG_GLDv3 +/* ============================================================== */ +/* + * GLDv3 interface + */ +/* ============================================================== */ +static int usbgem_m_getstat(void *, uint_t, uint64_t *); +static int usbgem_m_start(void *); +static void usbgem_m_stop(void *); +static int usbgem_m_setpromisc(void *, boolean_t); +static int usbgem_m_multicst(void *, boolean_t, const uint8_t *); +static int usbgem_m_unicst(void *, const uint8_t *); +static mblk_t *usbgem_m_tx(void *, mblk_t *); +static void usbgem_m_ioctl(void *, queue_t *, mblk_t *); +#ifdef GEM_CONFIG_MAC_PROP +static int usbgem_m_setprop(void *, const char *, mac_prop_id_t, + uint_t, const void *); +#ifdef MAC_VERSION_V1 +static int usbgem_m_getprop(void *, const char *, mac_prop_id_t, + uint_t, void *); +#else +static int usbgem_m_getprop(void *, const char *, mac_prop_id_t, + uint_t, uint_t, void *, uint_t *); +#endif +#endif + +#ifdef _SYS_MAC_PROVIDER_H +#define GEM_M_CALLBACK_FLAGS (MC_IOCTL) +#else +#define GEM_M_CALLBACK_FLAGS (MC_IOCTL) +#endif + +static mac_callbacks_t gem_m_callbacks = { +#ifdef USBGEM_CONFIG_MAC_PROP +#ifdef MAC_VERSION_V1 + GEM_M_CALLBACK_FLAGS | MC_SETPROP | MC_GETPROP | MC_PROPINFO, +#else + GEM_M_CALLBACK_FLAGS | MC_SETPROP | MC_GETPROP, +#endif +#else + GEM_M_CALLBACK_FLAGS, +#endif + usbgem_m_getstat, + usbgem_m_start, + usbgem_m_stop, + usbgem_m_setpromisc, + usbgem_m_multicst, + usbgem_m_unicst, + usbgem_m_tx, +#ifdef _SYS_MAC_PROVIDER_H +#ifdef MAC_VERSION_V1 + NULL, +#endif +#else + NULL, /* m_resources */ +#endif + usbgem_m_ioctl, + NULL, /* m_getcapab */ +#ifdef USBGEM_CONFIG_MAC_PROP + NULL, + NULL, + usbgem_m_setprop, + usbgem_m_getprop, +#endif +#ifdef MAC_VERSION_V1 + usbgem_m_propinfo, +#endif +}; + +static int +usbgem_m_start(void *arg) +{ + int ret; + int err; + struct usbgem_dev *dp = arg; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + err = EIO; + + rw_enter(&dp->dev_state_lock, RW_WRITER); + dp->nic_state = NIC_STATE_ONLINE; + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + err = 0; + goto x; + } + if (usbgem_mac_init(dp) != USB_SUCCESS) { + goto x; + } + + /* initialize rx filter state */ + sema_p(&dp->rxfilter_lock); + dp->mc_count = 0; + dp->mc_count_req = 0; + + bcopy(dp->dev_addr.ether_addr_octet, + dp->cur_addr.ether_addr_octet, ETHERADDRL); + dp->rxmode |= RXMODE_ENABLE; + + ret = usbgem_hal_set_rx_filter(dp); + sema_v(&dp->rxfilter_lock); + + if (ret != USB_SUCCESS) { + goto x; + } + + if (dp->mii_state == MII_STATE_LINKUP) { + /* setup media mode if the link have been up */ + if (usbgem_hal_set_media(dp) != USB_SUCCESS) { + goto x; + } + if (usbgem_mac_start(dp) != USB_SUCCESS) { + goto x; + } + } + + err = 0; +x: + rw_exit(&dp->dev_state_lock); + return (err); +} + +static void +usbgem_m_stop(void *arg) +{ + struct usbgem_dev *dp = arg; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* stop rx gracefully */ + rw_enter(&dp->dev_state_lock, RW_READER); + sema_p(&dp->rxfilter_lock); + dp->rxmode &= ~RXMODE_ENABLE; + + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + (void) usbgem_hal_set_rx_filter(dp); + } + sema_v(&dp->rxfilter_lock); + rw_exit(&dp->dev_state_lock); + + /* make the nic state inactive */ + rw_enter(&dp->dev_state_lock, RW_WRITER); + dp->nic_state = NIC_STATE_STOPPED; + + /* stop mac completely */ + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + (void) usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL); + } + rw_exit(&dp->dev_state_lock); +} + +static int +usbgem_m_multicst(void *arg, boolean_t add, const uint8_t *ep) +{ + int err; + int ret; + struct usbgem_dev *dp = arg; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_READER); + if (add) { + ret = usbgem_add_multicast(dp, ep); + } else { + ret = usbgem_remove_multicast(dp, ep); + } + rw_exit(&dp->dev_state_lock); + + err = 0; + if (ret != USB_SUCCESS) { +#ifdef GEM_CONFIG_FMA + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); +#endif + err = EIO; + } + + return (err); +} + +static int +usbgem_m_setpromisc(void *arg, boolean_t on) +{ + int err; + struct usbgem_dev *dp = arg; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_READER); + + sema_p(&dp->rxfilter_lock); + if (on) { + dp->rxmode |= RXMODE_PROMISC; + } else { + dp->rxmode &= ~RXMODE_PROMISC; + } + + err = 0; + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + if (usbgem_hal_set_rx_filter(dp) != USB_SUCCESS) { + err = EIO; + } + } + sema_v(&dp->rxfilter_lock); + + rw_exit(&dp->dev_state_lock); + +#ifdef GEM_CONFIG_FMA + if (err != 0) { + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); + } +#endif + return (err); +} + +int +usbgem_m_getstat(void *arg, uint_t stat, uint64_t *valp) +{ + int ret; + uint64_t val; + struct usbgem_dev *dp = arg; + struct usbgem_stats *gstp = &dp->stats; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_READER); + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + rw_exit(&dp->dev_state_lock); + return (0); + } + ret = usbgem_hal_get_stats(dp); + rw_exit(&dp->dev_state_lock); + +#ifdef GEM_CONFIG_FMA + if (ret != USB_SUCCESS) { + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); + return (EIO); + } +#endif + + switch (stat) { + case MAC_STAT_IFSPEED: + val = usbgem_speed_value[dp->speed] *1000000ull; + break; + + case MAC_STAT_MULTIRCV: + val = gstp->rmcast; + break; + + case MAC_STAT_BRDCSTRCV: + val = gstp->rbcast; + break; + + case MAC_STAT_MULTIXMT: + val = gstp->omcast; + break; + + case MAC_STAT_BRDCSTXMT: + val = gstp->obcast; + break; + + case MAC_STAT_NORCVBUF: + val = gstp->norcvbuf + gstp->missed; + break; + + case MAC_STAT_IERRORS: + val = gstp->errrcv; + break; + + case MAC_STAT_NOXMTBUF: + val = gstp->noxmtbuf; + break; + + case MAC_STAT_OERRORS: + val = gstp->errxmt; + break; + + case MAC_STAT_COLLISIONS: + val = gstp->collisions; + break; + + case MAC_STAT_RBYTES: + val = gstp->rbytes; + break; + + case MAC_STAT_IPACKETS: + val = gstp->rpackets; + break; + + case MAC_STAT_OBYTES: + val = gstp->obytes; + break; + + case MAC_STAT_OPACKETS: + val = gstp->opackets; + break; + + case MAC_STAT_UNDERFLOWS: + val = gstp->underflow; + break; + + case MAC_STAT_OVERFLOWS: + val = gstp->overflow; + break; + + case ETHER_STAT_ALIGN_ERRORS: + val = gstp->frame; + break; + + case ETHER_STAT_FCS_ERRORS: + val = gstp->crc; + break; + + case ETHER_STAT_FIRST_COLLISIONS: + val = gstp->first_coll; + break; + + case ETHER_STAT_MULTI_COLLISIONS: + val = gstp->multi_coll; + break; + + case ETHER_STAT_SQE_ERRORS: + val = gstp->sqe; + break; + + case ETHER_STAT_DEFER_XMTS: + val = gstp->defer; + break; + + case ETHER_STAT_TX_LATE_COLLISIONS: + val = gstp->xmtlatecoll; + break; + + case ETHER_STAT_EX_COLLISIONS: + val = gstp->excoll; + break; + + case ETHER_STAT_MACXMT_ERRORS: + val = gstp->xmit_internal_err; + break; + + case ETHER_STAT_CARRIER_ERRORS: + val = gstp->nocarrier; + break; + + case ETHER_STAT_TOOLONG_ERRORS: + val = gstp->frame_too_long; + break; + + case ETHER_STAT_MACRCV_ERRORS: + val = gstp->rcv_internal_err; + break; + + case ETHER_STAT_XCVR_ADDR: + val = dp->mii_phy_addr; + break; + + case ETHER_STAT_XCVR_ID: + val = dp->mii_phy_id; + break; + + case ETHER_STAT_XCVR_INUSE: + val = usbgem_mac_xcvr_inuse(dp); + break; + + case ETHER_STAT_CAP_1000FDX: + val = (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD); + break; + + case ETHER_STAT_CAP_1000HDX: + val = (dp->mii_xstatus & MII_XSTATUS_1000BASET) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX); + break; + + case ETHER_STAT_CAP_100FDX: + val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD); + break; + + case ETHER_STAT_CAP_100HDX: + val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX); + break; + + case ETHER_STAT_CAP_10FDX: + val = BOOLEAN(dp->mii_status & MII_STATUS_10_FD); + break; + + case ETHER_STAT_CAP_10HDX: + val = BOOLEAN(dp->mii_status & MII_STATUS_10); + break; + + case ETHER_STAT_CAP_ASMPAUSE: + val = dp->ugc.usbgc_flow_control > FLOW_CONTROL_SYMMETRIC; + break; + + case ETHER_STAT_CAP_PAUSE: + val = dp->ugc.usbgc_flow_control != FLOW_CONTROL_NONE; + break; + + case ETHER_STAT_CAP_AUTONEG: + val = BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG); + break; + + case ETHER_STAT_ADV_CAP_1000FDX: + val = dp->anadv_1000fdx; + break; + + case ETHER_STAT_ADV_CAP_1000HDX: + val = dp->anadv_1000hdx; + break; + + case ETHER_STAT_ADV_CAP_100FDX: + val = dp->anadv_100fdx; + break; + + case ETHER_STAT_ADV_CAP_100HDX: + val = dp->anadv_100hdx; + break; + + case ETHER_STAT_ADV_CAP_10FDX: + val = dp->anadv_10fdx; + break; + + case ETHER_STAT_ADV_CAP_10HDX: + val = dp->anadv_10hdx; + break; + + case ETHER_STAT_ADV_CAP_ASMPAUSE: + val = dp->anadv_asmpause; + break; + + case ETHER_STAT_ADV_CAP_PAUSE: + val = dp->anadv_pause; + break; + + case ETHER_STAT_ADV_CAP_AUTONEG: + val = dp->anadv_autoneg; + break; + + case ETHER_STAT_LP_CAP_1000FDX: + val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_FULL); + break; + + case ETHER_STAT_LP_CAP_1000HDX: + val = BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_HALF); + break; + + case ETHER_STAT_LP_CAP_100FDX: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX_FD); + break; + + case ETHER_STAT_LP_CAP_100HDX: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX); + break; + + case ETHER_STAT_LP_CAP_10FDX: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T_FD); + break; + + case ETHER_STAT_LP_CAP_10HDX: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T); + break; + + case ETHER_STAT_LP_CAP_ASMPAUSE: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_ASM_DIR); + break; + + case ETHER_STAT_LP_CAP_PAUSE: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_PAUSE); + break; + + case ETHER_STAT_LP_CAP_AUTONEG: + val = BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN); + break; + + case ETHER_STAT_LINK_ASMPAUSE: + val = BOOLEAN(dp->flow_control & 2); + break; + + case ETHER_STAT_LINK_PAUSE: + val = BOOLEAN(dp->flow_control & 1); + break; + + case ETHER_STAT_LINK_AUTONEG: + val = dp->anadv_autoneg && + BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN); + break; + + case ETHER_STAT_LINK_DUPLEX: + val = (dp->mii_state == MII_STATE_LINKUP) ? + (dp->full_duplex ? 2 : 1) : 0; + break; + + case ETHER_STAT_TOOSHORT_ERRORS: + val = gstp->runt; + break; +#ifdef NEVER /* it doesn't make sense */ + case ETHER_STAT_CAP_REMFAULT: + val = B_TRUE; + break; + + case ETHER_STAT_ADV_REMFAULT: + val = dp->anadv_remfault; + break; +#endif + case ETHER_STAT_LP_REMFAULT: + val = BOOLEAN(dp->mii_lpable & MII_AN_ADVERT_REMFAULT); + break; + + case ETHER_STAT_JABBER_ERRORS: + val = gstp->jabber; + break; + + case ETHER_STAT_CAP_100T4: + val = BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4); + break; + + case ETHER_STAT_ADV_CAP_100T4: + val = dp->anadv_100t4; + break; + + case ETHER_STAT_LP_CAP_100T4: + val = BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_T4); + break; + + default: +#if GEM_DEBUG_LEVEL > 2 + cmn_err(CE_WARN, + "%s: unrecognized parameter value = %d", + __func__, stat); +#endif + *valp = 0; + return (ENOTSUP); + } + + *valp = val; + + return (0); +} + +static int +usbgem_m_unicst(void *arg, const uint8_t *mac) +{ + int err; + struct usbgem_dev *dp = arg; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_READER); + + sema_p(&dp->rxfilter_lock); + bcopy(mac, dp->cur_addr.ether_addr_octet, ETHERADDRL); + dp->rxmode |= RXMODE_ENABLE; + + err = 0; + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + if (usbgem_hal_set_rx_filter(dp) != USB_SUCCESS) { + err = EIO; + } + } + sema_v(&dp->rxfilter_lock); + rw_exit(&dp->dev_state_lock); + +#ifdef GEM_CONFIG_FMA + if (err != 0) { + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); + } +#endif + return (err); +} + +/* + * usbgem_m_tx is used only for sending data packets into ethernet wire. + */ +static mblk_t * +usbgem_m_tx(void *arg, mblk_t *mp_head) +{ + int limit; + mblk_t *mp; + mblk_t *nmp; + uint32_t flags; + struct usbgem_dev *dp = arg; + + DPRINTF(4, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + mp = mp_head; + flags = 0; + + rw_enter(&dp->dev_state_lock, RW_READER); + + if (dp->mii_state != MII_STATE_LINKUP || + dp->mac_state != MAC_STATE_ONLINE) { + /* some nics hate to send packets during the link is down */ + for (; mp; mp = nmp) { + nmp = mp->b_next; + mp->b_next = NULL; + freemsg(mp); + } + goto x; + } + + ASSERT(dp->nic_state == NIC_STATE_ONLINE); + + limit = dp->tx_max_packets; + for (; limit-- && mp; mp = nmp) { + nmp = mp->b_next; + mp->b_next = NULL; + if (usbgem_send_common(dp, mp, + (limit == 0 && nmp) ? 1 : 0)) { + mp->b_next = nmp; + break; + } + } +#ifdef CONFIG_TX_LIMITER + if (mp == mp_head) { + /* no packets were sent, descrease allocation limit */ + mutex_enter(&dp->txlock); + dp->tx_max_packets = max(dp->tx_max_packets - 1, 1); + mutex_exit(&dp->txlock); + } +#endif +x: + rw_exit(&dp->dev_state_lock); + + return (mp); +} + +static void +usbgem_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) +{ + struct usbgem_dev *dp = arg; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", + ((struct usbgem_dev *)arg)->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_READER); + usbgem_mac_ioctl((struct usbgem_dev *)arg, wq, mp); + rw_exit(&dp->dev_state_lock); +} + +static void +usbgem_gld3_init(struct usbgem_dev *dp, mac_register_t *macp) +{ + macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; + macp->m_driver = dp; + macp->m_dip = dp->dip; + macp->m_src_addr = dp->dev_addr.ether_addr_octet; + macp->m_callbacks = &gem_m_callbacks; + macp->m_min_sdu = 0; + macp->m_max_sdu = dp->mtu; + + if (dp->misc_flag & USBGEM_VLAN) { + macp->m_margin = VTAG_SIZE; + } +} +#else +/* ============================================================== */ +/* + * GLDv2 interface + */ +/* ============================================================== */ +static int usbgem_gld_reset(gld_mac_info_t *); +static int usbgem_gld_start(gld_mac_info_t *); +static int usbgem_gld_stop(gld_mac_info_t *); +static int usbgem_gld_set_mac_address(gld_mac_info_t *, uint8_t *); +static int usbgem_gld_set_multicast(gld_mac_info_t *, uint8_t *, int); +static int usbgem_gld_set_promiscuous(gld_mac_info_t *, int); +static int usbgem_gld_get_stats(gld_mac_info_t *, struct gld_stats *); +static int usbgem_gld_send(gld_mac_info_t *, mblk_t *); +static int usbgem_gld_send_tagged(gld_mac_info_t *, mblk_t *, uint32_t); + +static int +usbgem_gld_reset(gld_mac_info_t *macinfo) +{ + int err; + struct usbgem_dev *dp; + + err = GLD_SUCCESS; + dp = (struct usbgem_dev *)macinfo->gldm_private; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_WRITER); + if (usbgem_mac_init(dp) != USB_SUCCESS) { + err = GLD_FAILURE; + goto x; + } + + dp->nic_state = NIC_STATE_INITIALIZED; + + /* setup media mode if the link have been up */ + if (dp->mii_state == MII_STATE_LINKUP) { + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + (void) usbgem_hal_set_media(dp); + } + } +x: + rw_exit(&dp->dev_state_lock); + return (err); +} + +static int +usbgem_gld_start(gld_mac_info_t *macinfo) +{ + int err; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_WRITER); + + dp->nic_state = NIC_STATE_ONLINE; + + if (dp->mii_state == MII_STATE_LINKUP) { + if (usbgem_mac_start(dp) != USB_SUCCESS) { + /* sema_v(&dp->mii_lock); */ + err = GLD_FAILURE; + goto x; + } + } + + /* + * XXX - don't call gld_linkstate() here, + * otherwise it cause recursive mutex call. + */ + err = GLD_SUCCESS; +x: + rw_exit(&dp->dev_state_lock); + + return (err); +} + +static int +usbgem_gld_stop(gld_mac_info_t *macinfo) +{ + int err = GLD_SUCCESS; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* try to stop rx gracefully */ + rw_enter(&dp->dev_state_lock, RW_READER); + sema_p(&dp->rxfilter_lock); + dp->rxmode &= ~RXMODE_ENABLE; + + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + (void) usbgem_hal_set_rx_filter(dp); + } + sema_v(&dp->rxfilter_lock); + rw_exit(&dp->dev_state_lock); + + /* make the nic state inactive */ + rw_enter(&dp->dev_state_lock, RW_WRITER); + dp->nic_state = NIC_STATE_STOPPED; + + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + if (usbgem_mac_stop(dp, MAC_STATE_STOPPED, STOP_GRACEFUL) + != USB_SUCCESS) { + err = GLD_FAILURE; + } + } + rw_exit(&dp->dev_state_lock); + + return (err); +} + +static int +usbgem_gld_set_multicast(gld_mac_info_t *macinfo, uint8_t *ep, int flag) +{ + int err; + int ret; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + rw_enter(&dp->dev_state_lock, RW_READER); + if (flag == GLD_MULTI_ENABLE) { + ret = usbgem_add_multicast(dp, ep); + } else { + ret = usbgem_remove_multicast(dp, ep); + } + rw_exit(&dp->dev_state_lock); + + err = GLD_SUCCESS; + if (ret != USB_SUCCESS) { +#ifdef GEM_CONFIG_FMA + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); +#endif + err = GLD_FAILURE; + } + return (err); +} + +static int +usbgem_gld_set_promiscuous(gld_mac_info_t *macinfo, int flag) +{ + boolean_t need_to_change = B_TRUE; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + sema_p(&dp->rxfilter_lock); + if (flag == GLD_MAC_PROMISC_NONE) { + dp->rxmode &= ~(RXMODE_PROMISC | RXMODE_ALLMULTI_REQ); + } else if (flag == GLD_MAC_PROMISC_MULTI) { + dp->rxmode |= RXMODE_ALLMULTI_REQ; + } else if (flag == GLD_MAC_PROMISC_PHYS) { + dp->rxmode |= RXMODE_PROMISC; + } else { + /* mode unchanged */ + need_to_change = B_FALSE; + } + + if (need_to_change) { + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + (void) usbgem_hal_set_rx_filter(dp); + } + } + sema_v(&dp->rxfilter_lock); + + return (GLD_SUCCESS); +} + +static int +usbgem_gld_set_mac_address(gld_mac_info_t *macinfo, uint8_t *mac) +{ + struct usbgem_dev *dp; + dp = (struct usbgem_dev *)macinfo->gldm_private; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + sema_p(&dp->rxfilter_lock); + bcopy(mac, dp->cur_addr.ether_addr_octet, ETHERADDRL); + dp->rxmode |= RXMODE_ENABLE; + + if (dp->mac_state != MAC_STATE_DISCONNECTED) { + (void) usbgem_hal_set_rx_filter(dp); + } + sema_v(&dp->rxfilter_lock); + + return (GLD_SUCCESS); +} + +static int +usbgem_gld_get_stats(gld_mac_info_t *macinfo, struct gld_stats *gs) +{ + struct usbgem_dev *dp; + struct usbgem_stats *vs; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + + if ((*dp->ugc.usbgc_get_stats)(dp) != USB_SUCCESS) { +#ifdef GEM_CONFIG_FMA + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); +#endif + return (USB_FAILURE); + } + + vs = &dp->stats; + + gs->glds_errxmt = vs->errxmt; + gs->glds_errrcv = vs->errrcv; + gs->glds_collisions = vs->collisions; + + gs->glds_excoll = vs->excoll; + gs->glds_defer = vs->defer; + gs->glds_frame = vs->frame; + gs->glds_crc = vs->crc; + + gs->glds_overflow = vs->overflow; /* fifo err,underrun,rbufovf */ + gs->glds_underflow = vs->underflow; + gs->glds_short = vs->runt; + gs->glds_missed = vs->missed; /* missed pkts while rbuf ovf */ + gs->glds_xmtlatecoll = vs->xmtlatecoll; + gs->glds_nocarrier = vs->nocarrier; + gs->glds_norcvbuf = vs->norcvbuf; /* OS resource exaust */ + gs->glds_intr = vs->intr; + + /* all before here must be kept in place for v0 compatibility */ + gs->glds_speed = usbgem_speed_value[dp->speed] * 1000000; + gs->glds_media = GLDM_PHYMII; + gs->glds_duplex = dp->full_duplex ? GLD_DUPLEX_FULL : GLD_DUPLEX_HALF; + + /* gs->glds_media_specific */ + gs->glds_dot3_first_coll = vs->first_coll; + gs->glds_dot3_multi_coll = vs->multi_coll; + gs->glds_dot3_sqe_error = 0; + gs->glds_dot3_mac_xmt_error = 0; + gs->glds_dot3_mac_rcv_error = 0; + gs->glds_dot3_frame_too_long = vs->frame_too_long; + + return (GLD_SUCCESS); +} + +static int +usbgem_gld_ioctl(gld_mac_info_t *macinfo, queue_t *wq, mblk_t *mp) +{ + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + usbgem_mac_ioctl(dp, wq, mp); + + return (GLD_SUCCESS); +} + +/* + * gem_gld_send is used only for sending data packets into ethernet wire. + */ +static int +usbgem_gld_send(gld_mac_info_t *macinfo, mblk_t *mp) +{ + int ret; + uint32_t flags = 0; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + + /* nic state must be online of suspended */ + rw_enter(&dp->dev_state_lock, RW_READER); + + ASSERT(dp->nic_state == NIC_STATE_ONLINE); + ASSERT(mp->b_next == NULL); + + if (dp->mii_state != MII_STATE_LINKUP) { + /* Some nics hate to send packets while the link is down. */ + /* we discard the untransmitted packets silently */ + rw_exit(&dp->dev_state_lock); + + freemsg(mp); +#ifdef GEM_CONFIG_FMA + /* FIXME - should we ignore the error? */ + ddi_fm_service_impact(dp->dip, DDI_SERVICE_DEGRADED); +#endif + return (GLD_SUCCESS); + } + + ret = (usbgem_send_common(dp, mp, flags) == NULL) + ? GLD_SUCCESS : GLD_NORESOURCES; + rw_exit(&dp->dev_state_lock); + + return (ret); +} + +/* + * usbgem_gld_send is used only for sending data packets into ethernet wire. + */ +static int +usbgem_gld_send_tagged(gld_mac_info_t *macinfo, mblk_t *mp, uint32_t vtag) +{ + uint32_t flags; + struct usbgem_dev *dp; + + dp = (struct usbgem_dev *)macinfo->gldm_private; + + /* + * Some nics hate to send packets while the link is down. + */ + if (dp->mii_state != MII_STATE_LINKUP) { + /* we dicard the untransmitted packets silently */ + freemsg(mp); +#ifdef GEM_CONFIG_FMA + /* FIXME - should we ignore the error? */ + ddi_fm_service_impact(dp->dip, DDI_SERVICE_UNAFFECTED); +#endif + return (GLD_SUCCESS); + } +#ifdef notyet + flags = GLD_VTAG_TCI(vtag) << GEM_SEND_VTAG_SHIFT; +#endif + return ((usbgem_send_common(dp, mp, 0) == NULL) ? + GLD_SUCCESS : GLD_NORESOURCES); +} + +static void +usbgem_gld_init(struct usbgem_dev *dp, gld_mac_info_t *macinfo, char *ident) +{ + /* + * configure GLD + */ + macinfo->gldm_devinfo = dp->dip; + macinfo->gldm_private = (caddr_t)dp; + + macinfo->gldm_reset = usbgem_gld_reset; + macinfo->gldm_start = usbgem_gld_start; + macinfo->gldm_stop = usbgem_gld_stop; + macinfo->gldm_set_mac_addr = usbgem_gld_set_mac_address; + macinfo->gldm_send = usbgem_gld_send; + macinfo->gldm_set_promiscuous = usbgem_gld_set_promiscuous; + macinfo->gldm_get_stats = usbgem_gld_get_stats; + macinfo->gldm_ioctl = usbgem_gld_ioctl; + macinfo->gldm_set_multicast = usbgem_gld_set_multicast; + macinfo->gldm_intr = NULL; + macinfo->gldm_mctl = NULL; + + macinfo->gldm_ident = ident; + macinfo->gldm_type = DL_ETHER; + macinfo->gldm_minpkt = 0; + macinfo->gldm_maxpkt = dp->mtu; + macinfo->gldm_addrlen = ETHERADDRL; + macinfo->gldm_saplen = -2; + macinfo->gldm_ppa = ddi_get_instance(dp->dip); +#ifdef GLD_CAP_LINKSTATE + macinfo->gldm_capabilities = GLD_CAP_LINKSTATE; +#endif + macinfo->gldm_vendor_addr = dp->dev_addr.ether_addr_octet; + macinfo->gldm_broadcast_addr = usbgem_bcastaddr; +} +#endif /* USBGEM_CONFIG_GLDv3 */ + + +/* ======================================================================== */ +/* + * .conf interface + */ +/* ======================================================================== */ +void +usbgem_generate_macaddr(struct usbgem_dev *dp, uint8_t *mac) +{ + extern char hw_serial[]; + char *hw_serial_p; + int i; + uint64_t val; + uint64_t key; + + cmn_err(CE_NOTE, + "!%s: using temp ether address," + " do not use this for long time", + dp->name); + + /* prefer a fixed address for DHCP */ + hw_serial_p = &hw_serial[0]; + val = stoi(&hw_serial_p); + + key = 0; + for (i = 0; i < USBGEM_NAME_LEN; i++) { + if (dp->name[i] == 0) { + break; + } + key ^= dp->name[i]; + } + key ^= ddi_get_instance(dp->dip); + val ^= key << 32; + + /* generate a local address */ + mac[0] = 0x02; + mac[1] = (uint8_t)(val >> 32); + mac[2] = (uint8_t)(val >> 24); + mac[3] = (uint8_t)(val >> 16); + mac[4] = (uint8_t)(val >> 8); + mac[5] = (uint8_t)val; +} + +boolean_t +usbgem_get_mac_addr_conf(struct usbgem_dev *dp) +{ + char propname[32]; + char *valstr; + uint8_t mac[ETHERADDRL]; + char *cp; + int c; + int i; + int j; + uint8_t v; + uint8_t d; + uint8_t ored; + + DPRINTF(3, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + /* + * Get ethernet address from .conf file + */ + (void) sprintf(propname, "mac-addr"); + if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dp->dip, + DDI_PROP_DONTPASS, propname, &valstr)) != DDI_PROP_SUCCESS) { + return (B_FALSE); + } + + if (strlen(valstr) != ETHERADDRL*3-1) { + goto syntax_err; + } + + cp = valstr; + j = 0; + ored = 0; + for (;;) { + v = 0; + for (i = 0; i < 2; i++) { + c = *cp++; + + if (c >= 'a' && c <= 'f') { + d = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + d = c - 'A' + 10; + } else if (c >= '0' && c <= '9') { + d = c - '0'; + } else { + goto syntax_err; + } + v = (v << 4) | d; + } + + mac[j++] = v; + ored |= v; + if (j == ETHERADDRL) { + /* done */ + break; + } + + c = *cp++; + if (c != ':') { + goto syntax_err; + } + } + + if (ored == 0) { + usbgem_generate_macaddr(dp, mac); + } + for (i = 0; i < ETHERADDRL; i++) { + dp->dev_addr.ether_addr_octet[i] = mac[i]; + } + ddi_prop_free(valstr); + return (B_TRUE); + +syntax_err: + cmn_err(CE_CONT, + "!%s: read mac addr: trying .conf: syntax err %s", + dp->name, valstr); + ddi_prop_free(valstr); + + return (B_FALSE); +} + +static void +usbgem_read_conf(struct usbgem_dev *dp) +{ + int val; + + DPRINTF(1, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + /* + * Get media mode infomation from .conf file + */ + dp->anadv_autoneg = usbgem_prop_get_int(dp, "adv_autoneg_cap", 1) != 0; + dp->anadv_1000fdx = usbgem_prop_get_int(dp, "adv_1000fdx_cap", 1) != 0; + dp->anadv_1000hdx = usbgem_prop_get_int(dp, "adv_1000hdx_cap", 1) != 0; + dp->anadv_100t4 = usbgem_prop_get_int(dp, "adv_100T4_cap", 1) != 0; + dp->anadv_100fdx = usbgem_prop_get_int(dp, "adv_100fdx_cap", 1) != 0; + dp->anadv_100hdx = usbgem_prop_get_int(dp, "adv_100hdx_cap", 1) != 0; + dp->anadv_10fdx = usbgem_prop_get_int(dp, "adv_10fdx_cap", 1) != 0; + dp->anadv_10hdx = usbgem_prop_get_int(dp, "adv_10hdx_cap", 1) != 0; + dp->anadv_1000t_ms = usbgem_prop_get_int(dp, "adv_1000t_ms", 0); + + if ((ddi_prop_exists(DDI_DEV_T_ANY, dp->dip, + DDI_PROP_DONTPASS, "full-duplex"))) { + dp->full_duplex = + usbgem_prop_get_int(dp, "full-duplex", 1) != 0; + dp->anadv_autoneg = B_FALSE; + if (dp->full_duplex) { + dp->anadv_1000hdx = B_FALSE; + dp->anadv_100hdx = B_FALSE; + dp->anadv_10hdx = B_FALSE; + } else { + dp->anadv_1000fdx = B_FALSE; + dp->anadv_100fdx = B_FALSE; + dp->anadv_10fdx = B_FALSE; + } + } + + if ((val = usbgem_prop_get_int(dp, "speed", 0)) > 0) { + dp->anadv_autoneg = B_FALSE; + switch (val) { + case 1000: + dp->speed = USBGEM_SPD_1000; + dp->anadv_100t4 = B_FALSE; + dp->anadv_100fdx = B_FALSE; + dp->anadv_100hdx = B_FALSE; + dp->anadv_10fdx = B_FALSE; + dp->anadv_10hdx = B_FALSE; + break; + case 100: + dp->speed = USBGEM_SPD_100; + dp->anadv_1000fdx = B_FALSE; + dp->anadv_1000hdx = B_FALSE; + dp->anadv_10fdx = B_FALSE; + dp->anadv_10hdx = B_FALSE; + break; + case 10: + dp->speed = USBGEM_SPD_10; + dp->anadv_1000fdx = B_FALSE; + dp->anadv_1000hdx = B_FALSE; + dp->anadv_100t4 = B_FALSE; + dp->anadv_100fdx = B_FALSE; + dp->anadv_100hdx = B_FALSE; + break; + default: + cmn_err(CE_WARN, + "!%s: property %s: illegal value:%d", + dp->name, "speed", val); + dp->anadv_autoneg = B_TRUE; + break; + } + } + val = usbgem_prop_get_int(dp, + "adv_pause", dp->ugc.usbgc_flow_control & 1); + val |= usbgem_prop_get_int(dp, + "adv_asmpause", BOOLEAN(dp->ugc.usbgc_flow_control & 2)) << 1; + if (val > FLOW_CONTROL_RX_PAUSE || val < FLOW_CONTROL_NONE) { + cmn_err(CE_WARN, + "!%s: property %s: illegal value:%d", + dp->name, "flow-control", val); + } else { + val = min(val, dp->ugc.usbgc_flow_control); + } + dp->anadv_pause = BOOLEAN(val & 1); + dp->anadv_asmpause = BOOLEAN(val & 2); + + dp->mtu = usbgem_prop_get_int(dp, "mtu", dp->mtu); + dp->txthr = usbgem_prop_get_int(dp, "txthr", dp->txthr); + dp->rxthr = usbgem_prop_get_int(dp, "rxthr", dp->rxthr); + dp->txmaxdma = usbgem_prop_get_int(dp, "txmaxdma", dp->txmaxdma); + dp->rxmaxdma = usbgem_prop_get_int(dp, "rxmaxdma", dp->rxmaxdma); +#ifdef GEM_CONFIG_POLLING + dp->poll_pkt_delay = + usbgem_prop_get_int(dp, "pkt_delay", dp->poll_pkt_delay); + + dp->max_poll_interval[GEM_SPD_10] = + usbgem_prop_get_int(dp, "max_poll_interval_10", + dp->max_poll_interval[GEM_SPD_10]); + dp->max_poll_interval[GEM_SPD_100] = + usbgem_prop_get_int(dp, "max_poll_interval_100", + dp->max_poll_interval[GEM_SPD_100]); + dp->max_poll_interval[GEM_SPD_1000] = + usbgem_prop_get_int(dp, "max_poll_interval_1000", + dp->max_poll_interval[GEM_SPD_1000]); + + dp->min_poll_interval[GEM_SPD_10] = + usbgem_prop_get_int(dp, "min_poll_interval_10", + dp->min_poll_interval[GEM_SPD_10]); + dp->min_poll_interval[GEM_SPD_100] = + usbgem_prop_get_int(dp, "min_poll_interval_100", + dp->min_poll_interval[GEM_SPD_100]); + dp->min_poll_interval[GEM_SPD_1000] = + usbgem_prop_get_int(dp, "min_poll_interval_1000", + dp->min_poll_interval[GEM_SPD_1000]); +#endif +} + +/* + * usbem kstat support + */ +#ifndef GEM_CONFIG_GLDv3 +/* kstat items based from dmfe driver */ + +struct usbgem_kstat_named { + struct kstat_named ks_xcvr_addr; + struct kstat_named ks_xcvr_id; + struct kstat_named ks_xcvr_inuse; + struct kstat_named ks_link_up; + struct kstat_named ks_link_duplex; /* 0:unknwon, 1:half, 2:full */ + struct kstat_named ks_cap_1000fdx; + struct kstat_named ks_cap_1000hdx; + struct kstat_named ks_cap_100fdx; + struct kstat_named ks_cap_100hdx; + struct kstat_named ks_cap_10fdx; + struct kstat_named ks_cap_10hdx; +#ifdef NEVER + struct kstat_named ks_cap_remfault; +#endif + struct kstat_named ks_cap_autoneg; + + struct kstat_named ks_adv_cap_1000fdx; + struct kstat_named ks_adv_cap_1000hdx; + struct kstat_named ks_adv_cap_100fdx; + struct kstat_named ks_adv_cap_100hdx; + struct kstat_named ks_adv_cap_10fdx; + struct kstat_named ks_adv_cap_10hdx; +#ifdef NEVER + struct kstat_named ks_adv_cap_remfault; +#endif + struct kstat_named ks_adv_cap_autoneg; + struct kstat_named ks_lp_cap_1000fdx; + struct kstat_named ks_lp_cap_1000hdx; + struct kstat_named ks_lp_cap_100fdx; + struct kstat_named ks_lp_cap_100hdx; + struct kstat_named ks_lp_cap_10fdx; + struct kstat_named ks_lp_cap_10hdx; + struct kstat_named ks_lp_cap_remfault; + struct kstat_named ks_lp_cap_autoneg; +}; + +static int +usbgem_kstat_update(kstat_t *ksp, int rw) +{ + struct usbgem_kstat_named *knp; + struct usbgem_dev *dp = (struct usbgem_dev *)ksp->ks_private; + + if (rw != KSTAT_READ) { + return (0); + } + + knp = (struct usbgem_kstat_named *)ksp->ks_data; + + knp->ks_xcvr_addr.value.ul = dp->mii_phy_addr; + knp->ks_xcvr_id.value.ul = dp->mii_phy_id; + knp->ks_xcvr_inuse.value.ul = usbgem_mac_xcvr_inuse(dp); + knp->ks_link_up.value.ul = dp->mii_state == MII_STATE_LINKUP; + knp->ks_link_duplex.value.ul = + (dp->mii_state == MII_STATE_LINKUP) ? + (dp->full_duplex ? 2 : 1) : 0; + + knp->ks_cap_1000fdx.value.ul = + (dp->mii_xstatus & MII_XSTATUS_1000BASET_FD) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX_FD); + knp->ks_cap_1000hdx.value.ul = + (dp->mii_xstatus & MII_XSTATUS_1000BASET) || + (dp->mii_xstatus & MII_XSTATUS_1000BASEX); + knp->ks_cap_100fdx.value.ul = + BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD); + knp->ks_cap_100hdx.value.ul = + BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX); + knp->ks_cap_10fdx.value.ul = + BOOLEAN(dp->mii_status & MII_STATUS_10_FD); + knp->ks_cap_10hdx.value.ul = + BOOLEAN(dp->mii_status & MII_STATUS_10); +#ifdef NEVER + knp->ks_cap_remfault.value.ul = B_TRUE; +#endif + knp->ks_cap_autoneg.value.ul = + BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG); + + knp->ks_adv_cap_1000fdx.value.ul = dp->anadv_1000fdx; + knp->ks_adv_cap_1000hdx.value.ul = dp->anadv_1000hdx; + knp->ks_adv_cap_100fdx.value.ul = dp->anadv_100fdx; + knp->ks_adv_cap_100hdx.value.ul = dp->anadv_100hdx; + knp->ks_adv_cap_10fdx.value.ul = dp->anadv_10fdx; + knp->ks_adv_cap_10hdx.value.ul = dp->anadv_10hdx; +#ifdef NEVER + knp->ks_adv_cap_remfault.value.ul = 0; +#endif + knp->ks_adv_cap_autoneg.value.ul = dp->anadv_autoneg; + + knp->ks_lp_cap_1000fdx.value.ul = + BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_FULL); + knp->ks_lp_cap_1000hdx.value.ul = + BOOLEAN(dp->mii_stat1000 & MII_1000TS_LP_HALF); + knp->ks_lp_cap_100fdx.value.ul = + BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX_FD); + knp->ks_lp_cap_100hdx.value.ul = + BOOLEAN(dp->mii_lpable & MII_ABILITY_100BASE_TX); + knp->ks_lp_cap_10fdx.value.ul = + BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T_FD); + knp->ks_lp_cap_10hdx.value.ul = + BOOLEAN(dp->mii_lpable & MII_ABILITY_10BASE_T); + knp->ks_lp_cap_remfault.value.ul = + BOOLEAN(dp->mii_exp & MII_AN_EXP_PARFAULT); + knp->ks_lp_cap_autoneg.value.ul = + BOOLEAN(dp->mii_exp & MII_AN_EXP_LPCANAN); + + return (0); +} + + +static int +usbgem_kstat_init(struct usbgem_dev *dp) +{ + int i; + kstat_t *ksp; + struct usbgem_kstat_named *knp; + + ksp = kstat_create( + (char *)ddi_driver_name(dp->dip), ddi_get_instance(dp->dip), + "mii", "net", KSTAT_TYPE_NAMED, + sizeof (*knp) / sizeof (knp->ks_xcvr_addr), 0); + + if (ksp == NULL) { + cmn_err(CE_WARN, "%s: %s() for mii failed", + dp->name, __func__); + return (USB_FAILURE); + } + + knp = (struct usbgem_kstat_named *)ksp->ks_data; + + kstat_named_init(&knp->ks_xcvr_addr, "xcvr_addr", + KSTAT_DATA_INT32); + kstat_named_init(&knp->ks_xcvr_id, "xcvr_id", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_xcvr_inuse, "xcvr_inuse", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_link_up, "link_up", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_link_duplex, "link_duplex", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_cap_1000fdx, "cap_1000fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_cap_1000hdx, "cap_1000hdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_cap_100fdx, "cap_100fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_cap_100hdx, "cap_100hdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_cap_10fdx, "cap_10fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_cap_10hdx, "cap_10hdx", + KSTAT_DATA_UINT32); +#ifdef NEVER + kstat_named_init(&knp->ks_cap_remfault, "cap_rem_fault", + KSTAT_DATA_UINT32); +#endif + kstat_named_init(&knp->ks_cap_autoneg, "cap_autoneg", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_adv_cap_1000fdx, "adv_cap_1000fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_adv_cap_1000hdx, "adv_cap_1000hdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_adv_cap_100fdx, "adv_cap_100fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_adv_cap_100hdx, "adv_cap_100hdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_adv_cap_10fdx, "adv_cap_10fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_adv_cap_10hdx, "adv_cap_10hdx", + KSTAT_DATA_UINT32); +#ifdef NEVER + kstat_named_init(&knp->ks_adv_cap_remfault, "adv_rem_fault", + KSTAT_DATA_UINT32); +#endif + kstat_named_init(&knp->ks_adv_cap_autoneg, "adv_cap_autoneg", + KSTAT_DATA_UINT32); + + kstat_named_init(&knp->ks_lp_cap_1000fdx, "lp_cap_1000fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_lp_cap_1000hdx, "lp_cap_1000hdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_lp_cap_100fdx, "lp_cap_100fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_lp_cap_100hdx, "lp_cap_100hdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_lp_cap_10fdx, "lp_cap_10fdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_lp_cap_10hdx, "lp_cap_10hdx", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_lp_cap_remfault, "lp_cap_rem_fault", + KSTAT_DATA_UINT32); + kstat_named_init(&knp->ks_lp_cap_autoneg, "lp_cap_autoneg", + KSTAT_DATA_UINT32); + + ksp->ks_private = (void *) dp; + ksp->ks_update = usbgem_kstat_update; + dp->ksp = ksp; + + kstat_install(ksp); + + return (USB_SUCCESS); +} +#endif /* GEM_CONFIG_GLDv3 */ +/* ======================================================================== */ +/* + * attach/detatch/usb support + */ +/* ======================================================================== */ +int +usbgem_ctrl_out(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + void *bp, int size) +{ + mblk_t *data; + usb_ctrl_setup_t setup; + usb_cr_t completion_reason; + usb_cb_flags_t cb_flags; + usb_flags_t flags; + int i; + int ret; + + DPRINTF(4, (CE_CONT, "!%s: %s " + "reqt:0x%02x req:0x%02x val:0x%04x ix:0x%04x len:0x%02x " + "bp:0x%p nic_state:%d", + dp->name, __func__, reqt, req, val, ix, len, bp, dp->nic_state)); + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + return (USB_PIPE_ERROR); + } + + data = NULL; + if (size > 0) { + if ((data = allocb(size, 0)) == NULL) { + return (USB_FAILURE); + } + + bcopy(bp, data->b_rptr, size); + data->b_wptr = data->b_rptr + size; + } + + setup.bmRequestType = reqt; + setup.bRequest = req; + setup.wValue = val; + setup.wIndex = ix; + setup.wLength = len; + setup.attrs = 0; /* attributes */ + + for (i = usbgem_ctrl_retry; i > 0; i--) { + completion_reason = 0; + cb_flags = 0; + + ret = usb_pipe_ctrl_xfer_wait(DEFAULT_PIPE(dp), + &setup, &data, &completion_reason, &cb_flags, 0); + + if (ret == USB_SUCCESS) { + break; + } + if (i == 1) { + cmn_err(CE_WARN, + "!%s: %s failed: " + "reqt:0x%x req:0x%x val:0x%x ix:0x%x len:0x%x " + "ret:%d cr:%s(%d), cb_flags:0x%x %s", + dp->name, __func__, reqt, req, val, ix, len, + ret, usb_str_cr(completion_reason), + completion_reason, + cb_flags, + (i > 1) ? "retrying..." : "fatal"); + } + } + + if (data != NULL) { + freemsg(data); + } + + return (ret); +} + +int +usbgem_ctrl_in(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + void *bp, int size) +{ + mblk_t *data; + usb_ctrl_setup_t setup; + usb_cr_t completion_reason; + usb_cb_flags_t cb_flags; + int i; + int ret; + int reclen; + + DPRINTF(4, (CE_CONT, + "!%s: %s:" + " reqt:0x%02x req:0x%02x val:0x%04x ix:0x%04x len:0x%02x" + " bp:x%p mac_state:%d", + dp->name, __func__, reqt, req, val, ix, len, bp, dp->mac_state)); + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + return (USB_PIPE_ERROR); + } + + data = NULL; + + setup.bmRequestType = reqt; + setup.bRequest = req; + setup.wValue = val; + setup.wIndex = ix; + setup.wLength = len; + setup.attrs = USB_ATTRS_AUTOCLEARING; /* XXX */ + + for (i = usbgem_ctrl_retry; i > 0; i--) { + completion_reason = 0; + cb_flags = 0; + ret = usb_pipe_ctrl_xfer_wait(DEFAULT_PIPE(dp), &setup, &data, + &completion_reason, &cb_flags, 0); + + if (ret == USB_SUCCESS) { + reclen = msgdsize(data); + bcopy(data->b_rptr, bp, min(reclen, size)); + break; + } + if (i == 1) { + cmn_err(CE_WARN, + "!%s: %s failed: " + "reqt:0x%x req:0x%x val:0x%x ix:0x%x len:0x%x " + "ret:%d cr:%s(%d) cb_flags:0x%x %s", + dp->name, __func__, + reqt, req, val, ix, len, + ret, usb_str_cr(completion_reason), + completion_reason, + cb_flags, + (i > 1) ? "retrying..." : "fatal"); + } + } + + if (data) { + freemsg(data); + } + + return (ret); +} + +int +usbgem_ctrl_out_val(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + uint32_t v) +{ + uint8_t buf[4]; + + /* convert to little endian from native byte order */ + switch (len) { + case 4: + buf[3] = v >> 24; + buf[2] = v >> 16; + /* FALLTHROUGH */ + case 2: + buf[1] = v >> 8; + /* FALLTHROUGH */ + case 1: + buf[0] = v; + } + + return (usbgem_ctrl_out(dp, reqt, req, val, ix, len, buf, len)); +} + +int +usbgem_ctrl_in_val(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + void *valp) +{ + uint8_t buf[4]; + uint_t v; + int err; + +#ifdef SANITY + bzero(buf, sizeof (buf)); +#endif + err = usbgem_ctrl_in(dp, reqt, req, val, ix, len, buf, len); + if (err == USB_SUCCESS) { + v = 0; + switch (len) { + case 4: + v |= buf[3] << 24; + v |= buf[2] << 16; + /* FALLTHROUGH */ + case 2: + v |= buf[1] << 8; + /* FALLTHROUGH */ + case 1: + v |= buf[0]; + } + + switch (len) { + case 4: + *(uint32_t *)valp = v; + break; + case 2: + *(uint16_t *)valp = v; + break; + case 1: + *(uint8_t *)valp = v; + break; + } + } + return (err); +} + +/* + * Attach / detach / disconnect / reconnect management + */ +static int +usbgem_open_pipes(struct usbgem_dev *dp) +{ + int i; + int ret; + int ifnum; + int alt; + usb_client_dev_data_t *reg_data; + usb_ep_data_t *ep_tree_node; + + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + ifnum = dp->ugc.usbgc_ifnum; + alt = dp->ugc.usbgc_alt; + + ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt, + 0, USB_EP_ATTR_BULK, USB_EP_DIR_IN); + if (ep_tree_node == NULL) { + cmn_err(CE_WARN, "!%s: %s: ep_bulkin is NULL", + dp->name, __func__); + goto err; + } + dp->ep_bulkin = &ep_tree_node->ep_descr; + + ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt, + 0, USB_EP_ATTR_BULK, USB_EP_DIR_OUT); + if (ep_tree_node == NULL) { + cmn_err(CE_WARN, "!%s: %s: ep_bulkout is NULL", + dp->name, __func__); + goto err; + } + dp->ep_bulkout = &ep_tree_node->ep_descr; + + ep_tree_node = usb_lookup_ep_data(dp->dip, dp->reg_data, ifnum, alt, + 0, USB_EP_ATTR_INTR, USB_EP_DIR_IN); + if (ep_tree_node) { + dp->ep_intr = &ep_tree_node->ep_descr; + } else { + /* don't care */ + DPRINTF(1, (CE_CONT, "!%s: %s: ep_intr is NULL", + dp->name, __func__)); + dp->ep_intr = NULL; + } + + /* XXX -- no need to open default pipe */ + + /* open bulk out pipe */ + bzero(&dp->policy_bulkout, sizeof (usb_pipe_policy_t)); + dp->policy_bulkout.pp_max_async_reqs = 1; + + if ((ret = usb_pipe_open(dp->dip, + dp->ep_bulkout, &dp->policy_bulkout, USB_FLAGS_SLEEP, + &dp->bulkout_pipe)) != USB_SUCCESS) { + cmn_err(CE_WARN, + "!%s: %s: err:%x: failed to open bulk-out pipe", + dp->name, __func__, ret); + dp->bulkout_pipe = NULL; + goto err; + } + DPRINTF(1, (CE_CONT, "!%s: %s: bulkout_pipe opened successfully", + dp->name, __func__)); + + /* open bulk in pipe */ + bzero(&dp->policy_bulkin, sizeof (usb_pipe_policy_t)); + dp->policy_bulkin.pp_max_async_reqs = 1; + if ((ret = usb_pipe_open(dp->dip, + dp->ep_bulkin, &dp->policy_bulkin, USB_FLAGS_SLEEP, + &dp->bulkin_pipe)) != USB_SUCCESS) { + cmn_err(CE_WARN, + "!%s: %s: ret:%x failed to open bulk-in pipe", + dp->name, __func__, ret); + dp->bulkin_pipe = NULL; + goto err; + } + DPRINTF(1, (CE_CONT, "!%s: %s: bulkin_pipe opened successfully", + dp->name, __func__)); + + if (dp->ep_intr) { + /* open interrupt pipe */ + bzero(&dp->policy_interrupt, sizeof (usb_pipe_policy_t)); + dp->policy_interrupt.pp_max_async_reqs = 1; + if ((ret = usb_pipe_open(dp->dip, dp->ep_intr, + &dp->policy_interrupt, USB_FLAGS_SLEEP, + &dp->intr_pipe)) != USB_SUCCESS) { + cmn_err(CE_WARN, + "!%s: %s: ret:%x failed to open interrupt pipe", + dp->name, __func__, ret); + dp->intr_pipe = NULL; + goto err; + } + } + DPRINTF(1, (CE_CONT, "!%s: %s: intr_pipe opened successfully", + dp->name, __func__)); + + return (USB_SUCCESS); + +err: + if (dp->bulkin_pipe) { + usb_pipe_close(dp->dip, + dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0); + dp->bulkin_pipe = NULL; + } + if (dp->bulkout_pipe) { + usb_pipe_close(dp->dip, + dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0); + dp->bulkout_pipe = NULL; + } + if (dp->intr_pipe) { + usb_pipe_close(dp->dip, + dp->intr_pipe, USB_FLAGS_SLEEP, NULL, 0); + dp->intr_pipe = NULL; + } + + return (USB_FAILURE); +} + +static int +usbgem_close_pipes(struct usbgem_dev *dp) +{ + DPRINTF(0, (CE_CONT, "!%s: %s: called", dp->name, __func__)); + + if (dp->intr_pipe) { + usb_pipe_close(dp->dip, + dp->intr_pipe, USB_FLAGS_SLEEP, NULL, 0); + dp->intr_pipe = NULL; + } + DPRINTF(1, (CE_CONT, "!%s: %s: 1", dp->name, __func__)); + + ASSERT(dp->bulkin_pipe); + usb_pipe_close(dp->dip, dp->bulkin_pipe, USB_FLAGS_SLEEP, NULL, 0); + dp->bulkin_pipe = NULL; + DPRINTF(1, (CE_CONT, "!%s: %s: 2", dp->name, __func__)); + + ASSERT(dp->bulkout_pipe); + usb_pipe_close(dp->dip, dp->bulkout_pipe, USB_FLAGS_SLEEP, NULL, 0); + dp->bulkout_pipe = NULL; + DPRINTF(1, (CE_CONT, "!%s: %s: 3", dp->name, __func__)); + + return (USB_SUCCESS); +} + +#define FREEZE_GRACEFUL (B_TRUE) +#define FREEZE_NO_GRACEFUL (B_FALSE) +static int +usbgem_freeze_device(struct usbgem_dev *dp, boolean_t graceful) +{ + DPRINTF(0, (CE_NOTE, "!%s: %s: called", dp->name, __func__)); + + /* stop nic activity */ + (void) usbgem_mac_stop(dp, MAC_STATE_DISCONNECTED, graceful); + + /* + * Here we free all memory resource allocated, because it will + * cause to panic the system that we free usb_bulk_req objects + * during the usb device is disconnected. + */ + (void) usbgem_free_memory(dp); + + return (USB_SUCCESS); +} + +static int +usbgem_disconnect_cb(dev_info_t *dip) +{ + int ret; + struct usbgem_dev *dp; + + dp = USBGEM_GET_DEV(dip); + + cmn_err(CE_NOTE, "!%s: the usb device was disconnected (dp=%p)", + dp->name, (void *)dp); + + /* start serialize */ + rw_enter(&dp->dev_state_lock, RW_WRITER); + + ret = usbgem_freeze_device(dp, 0); + + /* end of serialize */ + rw_exit(&dp->dev_state_lock); + + return (ret); +} + +static int +usbgem_recover_device(struct usbgem_dev *dp) +{ + int err; + + DPRINTF(0, (CE_NOTE, "!%s: %s: called", dp->name, __func__)); + + err = USB_SUCCESS; + + /* reinitialize the usb connection */ + usbgem_close_pipes(dp); + if ((err = usbgem_open_pipes(dp)) != USB_SUCCESS) { + goto x; + } + + /* initialize nic state */ + dp->mac_state = MAC_STATE_STOPPED; + dp->mii_state = MII_STATE_UNKNOWN; + + /* allocate memory resources again */ + if ((err = usbgem_alloc_memory(dp)) != USB_SUCCESS) { + goto x; + } + + /* restart nic and recover state */ + (void) usbgem_restart_nic(dp); + + usbgem_mii_init(dp); + + /* kick potentially stopped house keeping thread */ + cv_signal(&dp->link_watcher_wait_cv); +x: + return (err); +} + +static int +usbgem_reconnect_cb(dev_info_t *dip) +{ + int err = USB_SUCCESS; + struct usbgem_dev *dp; + + dp = USBGEM_GET_DEV(dip); + DPRINTF(0, (CE_CONT, "!%s: dp=%p", ddi_get_name(dip), dp)); +#ifdef notdef + /* check device changes after disconnect */ + if (usb_check_same_device(dp->dip, NULL, USB_LOG_L2, -1, + USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) { + cmn_err(CE_CONT, + "!%s: no or different device installed", dp->name); + return (DDI_SUCCESS); + } +#endif + cmn_err(CE_NOTE, "%s: the usb device was reconnected", dp->name); + + /* start serialize */ + rw_enter(&dp->dev_state_lock, RW_WRITER); + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + err = usbgem_recover_device(dp); + } + + /* end of serialize */ + rw_exit(&dp->dev_state_lock); + + return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); +} + +int +usbgem_suspend(dev_info_t *dip) +{ + int err = USB_SUCCESS; + struct usbgem_dev *dp; + + dp = USBGEM_GET_DEV(dip); + + DPRINTF(0, (CE_CONT, "!%s: %s: callded", dp->name, __func__)); + + /* start serialize */ + rw_enter(&dp->dev_state_lock, RW_WRITER); + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + err = usbgem_freeze_device(dp, STOP_GRACEFUL); + } + + /* end of serialize */ + rw_exit(&dp->dev_state_lock); + + return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); +} + +int +usbgem_resume(dev_info_t *dip) +{ + int err = USB_SUCCESS; + struct usbgem_dev *dp; + + dp = USBGEM_GET_DEV(dip); + + DPRINTF(0, (CE_CONT, "!%s: %s: callded", dp->name, __func__)); +#ifdef notdef + /* check device changes after disconnect */ + if (usb_check_same_device(dp->dip, NULL, USB_LOG_L2, -1, + USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) { + cmn_err(CE_CONT, + "!%s: no or different device installed", dp->name); + return (DDI_SUCCESS); + } +#endif + /* start serialize */ + rw_enter(&dp->dev_state_lock, RW_WRITER); + + if (dp->mac_state == MAC_STATE_DISCONNECTED) { + err = usbgem_recover_device(dp); + } + + /* end of serialize */ + rw_exit(&dp->dev_state_lock); + + return (err == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); +} + +#define USBGEM_LOCAL_DATA_SIZE(gc) \ + (sizeof (struct usbgem_dev) + USBGEM_MCALLOC) + +struct usbgem_dev * +usbgem_do_attach(dev_info_t *dip, + struct usbgem_conf *gc, void *lp, int lmsize) +{ + struct usbgem_dev *dp; + int i; +#ifdef USBGEM_CONFIG_GLDv3 + mac_register_t *macp = NULL; +#else + gld_mac_info_t *macinfo; + void *tmp; +#endif + int ret; + int unit; + int err; + + unit = ddi_get_instance(dip); + + DPRINTF(2, (CE_CONT, "!usbgem%d: %s: called", unit, __func__)); + + /* + * Allocate soft data structure + */ + dp = kmem_zalloc(USBGEM_LOCAL_DATA_SIZE(gc), KM_SLEEP); + if (dp == NULL) { +#ifndef USBGEM_CONFIG_GLDv3 + gld_mac_free(macinfo); +#endif + return (NULL); + } +#ifdef USBGEM_CONFIG_GLDv3 + if ((macp = mac_alloc(MAC_VERSION)) == NULL) { + cmn_err(CE_WARN, "!gem%d: %s: mac_alloc failed", + unit, __func__); + return (NULL); + } +#else + macinfo = gld_mac_alloc(dip); + dp->macinfo = macinfo; +#endif + + /* link to private area */ + dp->private = lp; + dp->priv_size = lmsize; + dp->mc_list = (struct mcast_addr *)&dp[1]; + + dp->dip = dip; + bcopy(gc->usbgc_name, dp->name, USBGEM_NAME_LEN); + + /* + * register with usb service + */ + if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) { + cmn_err(CE_WARN, + "%s: %s: usb_client_attach failed", + dp->name, __func__); + goto err_free_private; + } + + if (usb_get_dev_data(dip, &dp->reg_data, + USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) { + dp->reg_data = NULL; + goto err_unregister_client; + } +#ifdef USBGEM_DEBUG_LEVEL + usb_print_descr_tree(dp->dip, dp->reg_data); +#endif + + if (usbgem_open_pipes(dp) != USB_SUCCESS) { + /* failed to open pipes */ + cmn_err(CE_WARN, "!%s: %s: failed to open pipes", + dp->name, __func__); + goto err_unregister_client; + } + + /* + * Initialize mutexs and condition variables + */ + mutex_init(&dp->rxlock, NULL, MUTEX_DRIVER, NULL); + mutex_init(&dp->txlock, NULL, MUTEX_DRIVER, NULL); + cv_init(&dp->rx_drain_cv, NULL, CV_DRIVER, NULL); + cv_init(&dp->tx_drain_cv, NULL, CV_DRIVER, NULL); + rw_init(&dp->dev_state_lock, NULL, RW_DRIVER, NULL); + mutex_init(&dp->link_watcher_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&dp->link_watcher_wait_cv, NULL, CV_DRIVER, NULL); + sema_init(&dp->hal_op_lock, 1, NULL, SEMA_DRIVER, NULL); + sema_init(&dp->rxfilter_lock, 1, NULL, SEMA_DRIVER, NULL); + + /* + * Initialize configuration + */ + dp->ugc = *gc; + + dp->mtu = ETHERMTU; + dp->rxmode = 0; + dp->speed = USBGEM_SPD_10; /* default is 10Mbps */ + dp->full_duplex = B_FALSE; /* default is half */ + dp->flow_control = FLOW_CONTROL_NONE; + + dp->nic_state = NIC_STATE_STOPPED; + dp->mac_state = MAC_STATE_STOPPED; + dp->mii_state = MII_STATE_UNKNOWN; + + /* performance tuning parameters */ + dp->txthr = ETHERMAX; /* tx fifo threshoold */ + dp->txmaxdma = 16*4; /* tx max dma burst size */ + dp->rxthr = 128; /* rx fifo threshoold */ + dp->rxmaxdma = 16*4; /* rx max dma burst size */ + + /* + * Get media mode infomation from .conf file + */ + usbgem_read_conf(dp); + + /* rx_buf_len depend on MTU */ + dp->rx_buf_len = MAXPKTBUF(dp) + dp->ugc.usbgc_rx_header_len; + + /* + * Reset the chip + */ + if (usbgem_hal_reset_chip(dp) != USB_SUCCESS) { + cmn_err(CE_WARN, + "!%s: %s: failed to reset the usb device", + dp->name, __func__); + goto err_destroy_locks; + } + + /* + * HW dependant paremeter initialization + */ + if (usbgem_hal_attach_chip(dp) != USB_SUCCESS) { + cmn_err(CE_WARN, + "!%s: %s: failed to attach the usb device", + dp->name, __func__); + goto err_destroy_locks; + } + + /* allocate resources */ + if (usbgem_alloc_memory(dp) != USB_SUCCESS) { + goto err_destroy_locks; + } + + DPRINTF(0, (CE_CONT, + "!%s: %02x:%02x:%02x:%02x:%02x:%02x", + dp->name, + dp->dev_addr.ether_addr_octet[0], + dp->dev_addr.ether_addr_octet[1], + dp->dev_addr.ether_addr_octet[2], + dp->dev_addr.ether_addr_octet[3], + dp->dev_addr.ether_addr_octet[4], + dp->dev_addr.ether_addr_octet[5])); + + /* copy mac address */ + dp->cur_addr = dp->dev_addr; + + /* pre-calculated tx timeout in second for performance */ + dp->bulkout_timeout = + dp->ugc.usbgc_tx_timeout / drv_usectohz(1000*1000); + +#ifdef USBGEM_CONFIG_GLDv3 + usbgem_gld3_init(dp, macp); +#else + usbgem_gld_init(dp, macinfo, ident); +#endif + + /* Probe MII phy (scan phy) */ + dp->mii_lpable = 0; + dp->mii_advert = 0; + dp->mii_exp = 0; + dp->mii_ctl1000 = 0; + dp->mii_stat1000 = 0; + + dp->mii_status_ro = 0; + dp->mii_xstatus_ro = 0; + + if (usbgem_mii_probe(dp) != USB_SUCCESS) { + cmn_err(CE_WARN, "!%s: %s: mii_probe failed", + dp->name, __func__); + goto err_free_memory; + } + + /* mask unsupported abilities */ + dp->anadv_autoneg &= BOOLEAN(dp->mii_status & MII_STATUS_CANAUTONEG); + dp->anadv_1000fdx &= + BOOLEAN(dp->mii_xstatus & + (MII_XSTATUS_1000BASEX_FD | MII_XSTATUS_1000BASET_FD)); + dp->anadv_1000hdx &= + BOOLEAN(dp->mii_xstatus & + (MII_XSTATUS_1000BASEX | MII_XSTATUS_1000BASET)); + dp->anadv_100t4 &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASE_T4); + dp->anadv_100fdx &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX_FD); + dp->anadv_100hdx &= BOOLEAN(dp->mii_status & MII_STATUS_100_BASEX); + dp->anadv_10fdx &= BOOLEAN(dp->mii_status & MII_STATUS_10_FD); + dp->anadv_10hdx &= BOOLEAN(dp->mii_status & MII_STATUS_10); + + if (usbgem_mii_init(dp) != USB_SUCCESS) { + cmn_err(CE_WARN, "!%s: %s: mii_init failed", + dp->name, __func__); + goto err_free_memory; + } + + /* + * initialize kstats including mii statistics + */ +#ifdef USBGEM_CONFIG_GLDv3 +#ifdef USBGEM_CONFIG_ND + usbgem_nd_setup(dp); +#endif +#else + if (usbgem_kstat_init(dp) != USB_SUCCESS) { + goto err_free_memory; + } +#endif + + /* + * Add interrupt to system. + */ +#ifdef USBGEM_CONFIG_GLDv3 + if (ret = mac_register(macp, &dp->mh)) { + cmn_err(CE_WARN, "!%s: mac_register failed, error:%d", + dp->name, ret); + goto err_release_stats; + } + mac_free(macp); + macp = NULL; +#else + /* gld_register will corrupts driver_private */ + tmp = ddi_get_driver_private(dip); + if (gld_register(dip, + (char *)ddi_driver_name(dip), macinfo) != DDI_SUCCESS) { + cmn_err(CE_WARN, "!%s: %s: gld_register failed", + dp->name, __func__); + ddi_set_driver_private(dip, tmp); + goto err_release_stats; + } + /* restore driver private */ + ddi_set_driver_private(dip, tmp); +#endif /* USBGEM_CONFIG_GLDv3 */ + if (usb_register_hotplug_cbs(dip, + usbgem_suspend, usbgem_resume) != USB_SUCCESS) { + cmn_err(CE_WARN, + "!%s: %s: failed to register hotplug cbs", + dp->name, __func__); + goto err_unregister_gld; + } + + /* reset mii and start mii link watcher */ + if (usbgem_mii_start(dp) != USB_SUCCESS) { + goto err_unregister_hotplug; + } + + /* start tx watchdow watcher */ + if (usbgem_tx_watcher_start(dp)) { + goto err_usbgem_mii_stop; + } + + ddi_set_driver_private(dip, (caddr_t)dp); + + DPRINTF(2, (CE_CONT, "!%s: %s: return: success", dp->name, __func__)); + + return (dp); + +err_usbgem_mii_stop: + usbgem_mii_stop(dp); + +err_unregister_hotplug: + usb_unregister_hotplug_cbs(dip); + +err_unregister_gld: +#ifdef USBGEM_CONFIG_GLDv3 + mac_unregister(dp->mh); +#else + gld_unregister(macinfo); +#endif + +err_release_stats: +#ifdef USBGEM_CONFIG_GLDv3 +#ifdef USBGEM_CONFIG_ND + /* release NDD resources */ + usbgem_nd_cleanup(dp); +#endif +#else + kstat_delete(dp->ksp); +#endif + +err_free_memory: + usbgem_free_memory(dp); + +err_destroy_locks: + cv_destroy(&dp->tx_drain_cv); + cv_destroy(&dp->rx_drain_cv); + mutex_destroy(&dp->txlock); + mutex_destroy(&dp->rxlock); + rw_destroy(&dp->dev_state_lock); + mutex_destroy(&dp->link_watcher_lock); + cv_destroy(&dp->link_watcher_wait_cv); + sema_destroy(&dp->hal_op_lock); + sema_destroy(&dp->rxfilter_lock); + +err_close_pipes: + (void) usbgem_close_pipes(dp); + +err_unregister_client: + usb_client_detach(dp->dip, dp->reg_data); + +err_free_private: +#ifdef USBGEM_CONFIG_GLDv3 + if (macp) { + mac_free(macp); + } +#else + gld_mac_free(macinfo); +#endif + kmem_free((caddr_t)dp, USBGEM_LOCAL_DATA_SIZE(gc)); + + return (NULL); +} + +int +usbgem_do_detach(dev_info_t *dip) +{ + struct usbgem_dev *dp; + + dp = USBGEM_GET_DEV(dip); + +#ifdef USBGEM_CONFIG_GLDv3 + /* unregister with gld v3 */ + if (mac_unregister(dp->mh) != DDI_SUCCESS) { + return (DDI_FAILURE); + } +#else + /* unregister with gld v2 */ + if (gld_unregister(dp->macinfo) != DDI_SUCCESS) { + return (DDI_FAILURE); + } +#endif + /* unregister with hotplug service */ + usb_unregister_hotplug_cbs(dip); + + /* stop tx watchdog watcher */ + usbgem_tx_watcher_stop(dp); + + /* stop the link manager */ + usbgem_mii_stop(dp); + + /* unregister with usb service */ + (void) usbgem_free_memory(dp); + (void) usbgem_close_pipes(dp); + usb_client_detach(dp->dip, dp->reg_data); + dp->reg_data = NULL; + + /* unregister with kernel statistics */ +#ifdef USBGEM_CONFIG_GLDv3 +#ifdef USBGEM_CONFIG_ND + /* release ndd resources */ + usbgem_nd_cleanup(dp); +#endif +#else + /* destroy kstat objects */ + kstat_delete(dp->ksp); +#endif + + /* release locks and condition variables */ + mutex_destroy(&dp->txlock); + mutex_destroy(&dp->rxlock); + cv_destroy(&dp->tx_drain_cv); + cv_destroy(&dp->rx_drain_cv); + rw_destroy(&dp->dev_state_lock); + mutex_destroy(&dp->link_watcher_lock); + cv_destroy(&dp->link_watcher_wait_cv); + sema_destroy(&dp->hal_op_lock); + sema_destroy(&dp->rxfilter_lock); + + /* release basic memory resources */ +#ifndef USBGEM_CONFIG_GLDv3 + gld_mac_free(dp->macinfo); +#endif + kmem_free((caddr_t)(dp->private), dp->priv_size); + kmem_free((caddr_t)dp, USBGEM_LOCAL_DATA_SIZE(&dp->ugc)); + + DPRINTF(2, (CE_CONT, "!%s: %s: return: success", + ddi_driver_name(dip), __func__)); + + return (DDI_SUCCESS); +} + +int +usbgem_mod_init(struct dev_ops *dop, char *name) +{ +#ifdef USBGEM_CONFIG_GLDv3 + major_t major; + major = ddi_name_to_major(name); + if (major == DDI_MAJOR_T_NONE) { + return (DDI_FAILURE); + } + mac_init_ops(dop, name); +#endif + return (DDI_SUCCESS); +} + +void +usbgem_mod_fini(struct dev_ops *dop) +{ +#ifdef USBGEM_CONFIG_GLDv3 + mac_fini_ops(dop); +#endif +} + +int +usbgem_quiesce(dev_info_t *dip) +{ + struct usbgem_dev *dp; + + dp = USBGEM_GET_DEV(dip); + + ASSERT(dp != NULL); + + if (dp->mac_state != MAC_STATE_DISCONNECTED && + dp->mac_state != MAC_STATE_STOPPED) { + if (usbgem_hal_stop_chip(dp) != USB_SUCCESS) { + (void) usbgem_hal_reset_chip(dp); + } + } + + /* devo_quiesce() must return DDI_SUCCESS always */ + return (DDI_SUCCESS); +} diff --git a/usr/src/uts/common/io/usbgem/usbgem.h b/usr/src/uts/common/io/usbgem/usbgem.h new file mode 100644 index 0000000000..62a88921ca --- /dev/null +++ b/usr/src/uts/common/io/usbgem/usbgem.h @@ -0,0 +1,432 @@ +/* + * usbgem.h: General USB to Ethernet MAC driver framework + * @(#)usbgem.h 1.4 12/02/09 + * (C) Copyright 2003-2009 Masayuki Murayama KHF04453@nifty.ne.jp + */ + +#ifndef __USBGEM_H__ +#define __USBGEM_H__ + +#pragma ident "@(#)usbgem.h 1.4 12/02/09" + +#ifdef USBGEM_CONFIG_GLDv3 +#include <sys/mac.h> +#ifndef MAC_VERSION +#include <sys/mac_provider.h> +#endif +#include <sys/mac_ether.h> +#else +#include <sys/gld.h> +#endif /* GLDv3 */ + +/* + * Useful macros and typedefs + */ +#define USBGEM_NAME_LEN 32 + +#define USBGEM_TX_TIMEOUT (drv_usectohz(3*1000000)) +#define USBGEM_TX_TIMEOUT_INTERVAL (drv_usectohz(1*1000000)) +#define USBGEM_LINK_WATCH_INTERVAL (drv_usectohz(1*1000000)) + +/* general return code */ +#define USBGEM_SUCCESS 0 +#define USBGEM_FAILURE 1 + +/* return code of usbgem_tx_done */ +#define INTR_RESTART_TX 0x80000000U + +struct usbgem_stats { + uint32_t intr; + + uint32_t crc; + uint32_t errrcv; + uint32_t overflow; + uint32_t frame; + uint32_t missed; + uint32_t runt; + uint32_t frame_too_long; + uint32_t norcvbuf; + uint32_t sqe; + + uint32_t collisions; + uint32_t first_coll; + uint32_t multi_coll; + uint32_t excoll; + uint32_t xmit_internal_err; + uint32_t nocarrier; + uint32_t defer; + uint32_t errxmt; + uint32_t underflow; + uint32_t xmtlatecoll; + uint32_t noxmtbuf; + uint32_t jabber; + + + uint64_t rbytes; + uint64_t obytes; + uint64_t rpackets; + uint64_t opackets; + uint32_t rbcast; + uint32_t obcast; + uint32_t rmcast; + uint32_t omcast; + uint32_t rcv_internal_err; +}; + +struct mcast_addr { + struct ether_addr addr; + uint32_t hash; +}; + +#define USBGEM_MAXMC 64 +#define USBGEM_MCALLOC (sizeof (struct mcast_addr) * USBGEM_MAXMC) + +#define SLOT(dp, n) ((n) % (dp)->ugc.usbgc_tx_list_max) + +/* + * mac soft state + */ +struct usbgem_dev { + dev_info_t *dip; +#ifdef USBGEM_CONFIG_GLDv3 + mac_handle_t mh; +#else + void *macinfo; /* opaque handle for upper layer */ +#endif + char name[USBGEM_NAME_LEN]; + + /* pointer to usb private data */ + usb_client_dev_data_t *reg_data; + + /* usb handles */ + usb_pipe_handle_t default_pipe; + usb_pipe_handle_t bulkin_pipe; + usb_pipe_handle_t bulkout_pipe; + usb_pipe_handle_t intr_pipe; + + /* usb endpoints */ + usb_ep_descr_t *ep_default; + usb_ep_descr_t *ep_bulkin; + usb_ep_descr_t *ep_bulkout; + usb_ep_descr_t *ep_intr; + + /* usb policies */ + usb_pipe_policy_t policy_default; + usb_pipe_policy_t policy_bulkin; + usb_pipe_policy_t policy_bulkout; + usb_pipe_policy_t policy_interrupt; + + /* MAC address information */ + struct ether_addr cur_addr; + struct ether_addr dev_addr; + + /* RX state and resource management */ + kmutex_t rxlock; + int rx_busy_cnt; + boolean_t rx_active; + kcondvar_t rx_drain_cv; + + /* RX buffer management */ + int rx_buf_len; + + /* TX state and resource management */ + kmutex_t txlock; + int tx_busy_cnt; + usb_bulk_req_t *tx_free_list; + kcondvar_t tx_drain_cv; + clock_t tx_start_time; + int bulkout_timeout; /* in second */ + int tx_max_packets; + int tx_seq_num; + int tx_intr_pended; + + /* NIC state from OS view */ + int nic_state; +#define NIC_STATE_UNKNOWN 0 +#define NIC_STATE_STOPPED 1 +#define NIC_STATE_INITIALIZED 2 +#define NIC_STATE_ONLINE 3 + + /* MAC state from hardware view */ + int mac_state; +#define MAC_STATE_DISCONNECTED 0 /* it includes suspended state too */ +#define MAC_STATE_STOPPED 1 /* powered up / buf not initialized */ +#define MAC_STATE_INITIALIZED 2 /* initialized */ +#define MAC_STATE_ONLINE 3 /* working correctly */ +#define MAC_STATE_ERROR 4 /* need to restart nic */ + + clock_t fatal_error; + + /* robustness: timer and watchdog */ + uint_t tx_watcher_stop; + kt_did_t tx_watcher_did; + kcondvar_t tx_watcher_cv; + kmutex_t tx_watcher_lock; + clock_t tx_watcher_timeout; + clock_t tx_watcher_interval; + + /* MII mamagement */ + boolean_t anadv_autoneg:1; + boolean_t anadv_1000fdx:1; + boolean_t anadv_1000hdx:1; + boolean_t anadv_100t4:1; + boolean_t anadv_100fdx:1; + boolean_t anadv_100hdx:1; + boolean_t anadv_10fdx:1; + boolean_t anadv_10hdx:1; + boolean_t anadv_1000t_ms:2; + boolean_t anadv_pause:1; + boolean_t anadv_asmpause:1; + boolean_t mii_advert_ro:1; + + boolean_t full_duplex:1; + int speed:3; +#define USBGEM_SPD_10 0 +#define USBGEM_SPD_100 1 +#define USBGEM_SPD_1000 2 +#define USBGEM_SPD_NUM 3 + unsigned int flow_control:2; +#define FLOW_CONTROL_NONE 0 +#define FLOW_CONTROL_SYMMETRIC 1 +#define FLOW_CONTROL_TX_PAUSE 2 +#define FLOW_CONTROL_RX_PAUSE 3 + + boolean_t mii_supress_msg:1; + + uint32_t mii_phy_id; + uint16_t mii_status; + uint16_t mii_advert; + uint16_t mii_lpable; + uint16_t mii_exp; + uint16_t mii_ctl1000; + uint16_t mii_stat1000; + uint16_t mii_xstatus; + int8_t mii_phy_addr; /* must be signed */ + + uint16_t mii_status_ro; + uint16_t mii_xstatus_ro; + + int mii_state; +#define MII_STATE_UNKNOWN 0 +#define MII_STATE_RESETTING 1 +#define MII_STATE_AUTONEGOTIATING 2 +#define MII_STATE_AN_DONE 3 +#define MII_STATE_MEDIA_SETUP 4 +#define MII_STATE_LINKUP 5 +#define MII_STATE_LINKDOWN 6 + + clock_t mii_last_check; /* in tick */ + clock_t mii_timer; /* in tick */ +#define MII_RESET_TIMEOUT drv_usectohz(1000*1000) +#define MII_AN_TIMEOUT drv_usectohz(5000*1000) +#define MII_LINKDOWN_TIMEOUT drv_usectohz(10000*1000) + + clock_t mii_interval; /* in tick */ + clock_t linkup_delay; /* in tick */ + + uint_t link_watcher_stop; + kt_did_t link_watcher_did; + kcondvar_t link_watcher_wait_cv; + kmutex_t link_watcher_lock; + + krwlock_t dev_state_lock; /* mac_state and nic_state */ + ksema_t hal_op_lock; /* serialize hw operations */ + ksema_t drv_op_lock; /* hotplug op lock */ + + /* multcast list */ + ksema_t rxfilter_lock; + int mc_count; + int mc_count_req; + struct mcast_addr *mc_list; + int rxmode; +#define RXMODE_PROMISC 0x01 +#define RXMODE_ALLMULTI_REQ 0x02 +#define RXMODE_MULTI_OVF 0x04 +#define RXMODE_ENABLE 0x08 +#define RXMODE_ALLMULTI (RXMODE_ALLMULTI_REQ | RXMODE_MULTI_OVF) +#define RXMODE_BITS \ + "\020" \ + "\004ENABLE" \ + "\003MULTI_OVF" \ + "\002ALLMULTI_REQ" \ + "\001PROMISC" + + /* statistcs */ + struct usbgem_stats stats; + + /* pointer to local structure */ + void *private; + int priv_size; + + /* configuration */ + struct usbgem_conf { + /* name */ + char usbgc_name[USBGEM_NAME_LEN]; + int usbgc_ppa; + + /* specification on usb */ + int usbgc_ifnum; /* interface number */ + int usbgc_alt; /* alternate */ + + /* specification on tx engine */ + int usbgc_tx_list_max; + + /* specification on rx engine */ + int usbgc_rx_header_len; + int usbgc_rx_list_max; + + /* time out parameters */ + clock_t usbgc_tx_timeout; + clock_t usbgc_tx_timeout_interval; + + /* flow control */ + int usbgc_flow_control; + + /* MII timeout parameters */ + clock_t usbgc_mii_linkdown_timeout; + clock_t usbgc_mii_link_watch_interval; + clock_t usbgc_mii_reset_timeout; + + clock_t usbgc_mii_an_watch_interval; + clock_t usbgc_mii_an_timeout; + clock_t usbgc_mii_an_wait; + clock_t usbgc_mii_an_delay; + + /* MII configuration */ + int usbgc_mii_addr_min; + int usbgc_mii_linkdown_action; + int usbgc_mii_linkdown_timeout_action; +#define MII_ACTION_NONE 0 +#define MII_ACTION_RESET 1 +#define MII_ACTION_RSA 2 + boolean_t usbgc_mii_dont_reset:1; + boolean_t usbgc_mii_an_oneshot:1; + boolean_t usbgc_mii_hw_link_detection:1; + boolean_t usbgc_mii_stop_mac_on_linkdown:1; + uint16_t usbgc_mii_an_cmd; + + /* I/O methods */ + + /* mac operation */ + int (*usbgc_attach_chip)(struct usbgem_dev *dp); + int (*usbgc_reset_chip)(struct usbgem_dev *dp); + int (*usbgc_init_chip)(struct usbgem_dev *dp); + int (*usbgc_start_chip)(struct usbgem_dev *dp); + int (*usbgc_stop_chip)(struct usbgem_dev *dp); + uint32_t (*usbgc_multicast_hash)(struct usbgem_dev *dp, + const uint8_t *); + int (*usbgc_set_rx_filter)(struct usbgem_dev *dp); + int (*usbgc_set_media)(struct usbgem_dev *dp); + int (*usbgc_get_stats)(struct usbgem_dev *dp); + void (*usbgc_interrupt)(struct usbgem_dev *dp, mblk_t *mp); + + /* packet manipulation */ + mblk_t *(*usbgc_tx_make_packet)(struct usbgem_dev *dp, + mblk_t *mp); + mblk_t *(*usbgc_rx_make_packet)(struct usbgem_dev *dp, + mblk_t *mp); + /* mii operations */ + int (*usbgc_mii_probe)(struct usbgem_dev *dp); + int (*usbgc_mii_init)(struct usbgem_dev *dp); + int (*usbgc_mii_config)(struct usbgem_dev *dp, int *errp); + uint16_t (*usbgc_mii_read)(struct usbgem_dev *dp, uint_t reg, + int *errp); + void (*usbgc_mii_write)(struct usbgem_dev *dp, uint_t reg, + uint16_t val, int *errp); + + /* jumbo frame */ + int usbgc_max_mtu; + int usbgc_default_mtu; + int usbgc_min_mtu; + } ugc; + + int misc_flag; +#define USBGEM_VLAN 0x0001 + timeout_id_t intr_watcher_id; + + /* buffer size */ + uint_t mtu; + + /* performance tuning parameters */ + uint_t txthr; /* tx fifo threshoold */ + uint_t txmaxdma; /* tx max dma burst size */ + uint_t rxthr; /* rx fifo threshoold */ + uint_t rxmaxdma; /* tx max dma burst size */ + + /* kstat stuff */ + kstat_t *ksp; + + /* ndd stuff */ + caddr_t nd_data_p; + caddr_t nd_arg_p; + +#ifdef USBGEM_DEBUG_LEVEL + int tx_cnt; +#endif +}; + +/* + * Exported functions + */ +int usbgem_ctrl_out(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + void *bp, int size); + +int usbgem_ctrl_in(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + void *bp, int size); + +int usbgem_ctrl_out_val(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + uint32_t v); + +int usbgem_ctrl_in_val(struct usbgem_dev *dp, + uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len, + void *valp); + +void usbgem_generate_macaddr(struct usbgem_dev *, uint8_t *); +boolean_t usbgem_get_mac_addr_conf(struct usbgem_dev *); +int usbgem_mii_probe_default(struct usbgem_dev *); +int usbgem_mii_init_default(struct usbgem_dev *); +int usbgem_mii_config_default(struct usbgem_dev *, int *errp); +void usbgem_mii_update_link(struct usbgem_dev *); +void usbgem_restart_tx(struct usbgem_dev *); +boolean_t usbgem_tx_done(struct usbgem_dev *, int); +void usbgem_receive(struct usbgem_dev *); +struct usbgem_dev *usbgem_do_attach(dev_info_t *, + struct usbgem_conf *, void *, int); +int usbgem_do_detach(dev_info_t *); + +uint32_t usbgem_ether_crc_le(const uint8_t *addr); +uint32_t usbgem_ether_crc_be(const uint8_t *addr); + +int usbgem_resume(dev_info_t *); +int usbgem_suspend(dev_info_t *); +int usbgem_quiesce(dev_info_t *); + +#ifdef USBGEM_CONFIG_GLDv3 +#if DEVO_REV < 4 +#define USBGEM_STREAM_OPS(dev_ops, attach, detach) \ + DDI_DEFINE_STREAM_OPS(dev_ops, nulldev, nulldev, attach, detach, \ + nodev, NULL, D_MP, NULL) +#else +#define USBGEM_STREAM_OPS(dev_ops, attach, detach) \ + DDI_DEFINE_STREAM_OPS(dev_ops, nulldev, nulldev, attach, detach, \ + nodev, NULL, D_MP, NULL, usbgem_quiesce) +#endif +#else +#define usbgem_getinfo gld_getinfo +#define usbgem_open gld_open +#define usbgem_close gld_close +#define usbgem_wput gld_wput +#define usbgem_wsrv gld_wsrv +#define usbgem_rsrv gld_rsrv +#define usbgem_power NULL +#endif +int usbgem_mod_init(struct dev_ops *, char *); +void usbgem_mod_fini(struct dev_ops *); + +#define USBGEM_GET_DEV(dip) \ + ((struct usbgem_dev *)(ddi_get_driver_private(dip))) + +#endif /* __USBGEM_H__ */ diff --git a/usr/src/uts/common/io/usbgem/usbgem_mii.h b/usr/src/uts/common/io/usbgem/usbgem_mii.h new file mode 100644 index 0000000000..1d52706d3d --- /dev/null +++ b/usr/src/uts/common/io/usbgem/usbgem_mii.h @@ -0,0 +1,241 @@ +/* + * gem_mii.h: mii header for gem + * + * Copyright (c) 2002-2007 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 + * and/or other materials provided with the distribution. + * + * 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. + */ + +/* + * gem_mii.h : MII registers + */ +#ifndef _GEM_MII_H_ +#define _GEM_MII_H_ + +#ifdef GEM_CONFIG_GLDv3 +#include <sys/miiregs.h> +#else +#define MII_CONTROL 0 +#define MII_STATUS 1 +#define MII_PHYIDH 2 +#define MII_PHYIDL 3 +#define MII_AN_ADVERT 4 +#define MII_AN_LPABLE 5 +#define MII_AN_EXPANSION 6 +#define MII_AN_NXTPGXMIT 7 +#endif /* GEM_CONFIG_GLDv3 */ + +#define MII_AN_LPANXT 8 +#define MII_MS_CONTROL 9 +#define MII_MS_STATUS 10 +#define MII_XSTATUS 15 + +/* for 1000BaseT support */ +#define MII_1000TC MII_MS_CONTROL +#define MII_1000TS MII_MS_STATUS +#ifndef GEM_CONFIG_GLDv3 +#define MII_CONTROL_RESET 0x8000 +#define MII_CONTROL_LOOPBACK 0x4000 +#define MII_CONTROL_100MB 0x2000 +#define MII_CONTROL_ANE 0x1000 +#define MII_CONTROL_PWRDN 0x0800 +#define MII_CONTROL_ISOLATE 0x0400 +#define MII_CONTROL_RSAN 0x0200 +#define MII_CONTROL_FDUPLEX 0x0100 +#define MII_CONTROL_COLTST 0x0080 +#endif /* !GEM_CONFIG_GLDv3 */ +#define MII_CONTROL_SPEED 0x2040 + +#define MII_CONTROL_10MB 0x0000 +#define MII_CONTROL_1000MB 0x0040 + +#define MII_CONTROL_BITS \ + "\020" \ + "\020RESET" \ + "\017LOOPBACK" \ + "\016100MB" \ + "\015ANE" \ + "\014PWRDN" \ + "\013ISOLATE" \ + "\012RSAN" \ + "\011FDUPLEX" \ + "\010COLTST" \ + "\0071000M" +#ifndef GEM_CONFIG_GLDv3 +#define MII_STATUS_100_BASE_T4 0x8000 +#define MII_STATUS_100_BASEX_FD 0x4000 +#define MII_STATUS_100_BASEX 0x2000 +#define MII_STATUS_10_FD 0x1000 +#define MII_STATUS_10 0x0800 +#define MII_STATUS_MFPRMBLSUPR 0x0040 +#define MII_STATUS_ANDONE 0x0020 +#define MII_STATUS_REMFAULT 0x0010 +#define MII_STATUS_CANAUTONEG 0x0008 +#define MII_STATUS_LINKUP 0x0004 +#define MII_STATUS_JABBERING 0x0002 +#define MII_STATUS_EXTENDED 0x0001 +#endif /* !GEM_CONFIG_GLDv3 */ +#define MII_STATUS_XSTATUS 0x0100 +#define MII_STATUS_100_BASE_T2_FD 0x0400 +#define MII_STATUS_100_BASE_T2 0x0200 + +#define MII_STATUS_ABILITY_TECH \ + (MII_STATUS_100_BASE_T4 | \ + MII_STATUS_100_BASEX_FD | \ + MII_STATUS_100_BASEX | \ + MII_STATUS_10 | \ + MII_STATUS_10_FD) + + +#define MII_STATUS_BITS \ + "\020" \ + "\020100_BASE_T4" \ + "\017100_BASEX_FD" \ + "\016100_BASEX" \ + "\01510_BASE_FD" \ + "\01410_BASE" \ + "\013100_BASE_T2_FD" \ + "\012100_BASE_T2" \ + "\011XSTATUS" \ + "\007MFPRMBLSUPR" \ + "\006ANDONE" \ + "\005REMFAULT" \ + "\004CANAUTONEG" \ + "\003LINKUP" \ + "\002JABBERING" \ + "\001EXTENDED" +#ifndef GEM_CONFIG_GLDv3 +#define MII_AN_ADVERT_NP 0x8000 +#define MII_AN_ADVERT_REMFAULT 0x2000 +#define MII_AN_ADVERT_SELECTOR 0x001f +#endif /* !GEM_CONFIG_GLDv3 */ + +#define MII_ABILITY_ASM_DIR 0x0800 /* for annex 28B */ +#ifndef MII_ABILITY_PAUSE +#define MII_ABILITY_PAUSE 0x0400 /* for IEEE 802.3x */ +#endif +#ifndef GEM_CONFIG_GLDv3 +#define MII_ABILITY_100BASE_T4 0x0200 +#define MII_ABILITY_100BASE_TX_FD 0x0100 +#define MII_ABILITY_100BASE_TX 0x0080 +#define MII_ABILITY_10BASE_T_FD 0x0040 +#define MII_ABILITY_10BASE_T 0x0020 +#endif /* !GEM_CONFIG_GLDv3 */ + +#define MII_AN_LPABLE_NP 0x8000 + +#define MII_ABILITY_TECH \ + (MII_ABILITY_100BASE_T4 | \ + MII_ABILITY_100BASE_TX_FD | \ + MII_ABILITY_100BASE_TX | \ + MII_ABILITY_10BASE_T | \ + MII_ABILITY_10BASE_T_FD) + +#define MII_ABILITY_ALL \ + (MII_AN_ADVERT_REMFAULT | \ + MII_ABILITY_ASM_DIR | \ + MII_ABILITY_PAUSE | \ + MII_ABILITY_TECH) + + +#define MII_ABILITY_BITS \ + "\020" \ + "\016REMFAULT" \ + "\014ASM_DIR" \ + "\013PAUSE" \ + "\012100BASE_T4" \ + "\011100BASE_TX_FD" \ + "\010100BASE_TX" \ + "\00710BASE_T_FD" \ + "\00610BASE_T" +#ifndef GEM_CONFIG_GLDv3 +#define MII_AN_EXP_PARFAULT 0x0010 +#define MII_AN_EXP_LPCANNXTP 0x0008 +#define MII_AN_EXP_CANNXTPP 0x0004 +#define MII_AN_EXP_PAGERCVD 0x0002 +#define MII_AN_EXP_LPCANAN 0x0001 +#endif /* !GEM_CONFIG_GLDv3 */ + +#define MII_AN_EXP_BITS \ + "\020" \ + "\005PARFAULT" \ + "\004LPCANNXTP" \ + "\003CANNXTPP" \ + "\002PAGERCVD" \ + "\001LPCANAN" + +#define MII_1000TC_TESTMODE 0xe000 +#define MII_1000TC_CFG_EN 0x1000 +#define MII_1000TC_CFG_VAL 0x0800 +#define MII_1000TC_PORTTYPE 0x0400 +#define MII_1000TC_ADV_FULL 0x0200 +#define MII_1000TC_ADV_HALF 0x0100 + +#define MII_1000TC_BITS \ + "\020" \ + "\015CFG_EN" \ + "\014CFG_VAL" \ + "\013PORTTYPE" \ + "\012FULL" \ + "\011HALF" + +#define MII_1000TS_CFG_FAULT 0x8000 +#define MII_1000TS_CFG_MASTER 0x4000 +#define MII_1000TS_LOCALRXOK 0x2000 +#define MII_1000TS_REMOTERXOK 0x1000 +#define MII_1000TS_LP_FULL 0x0800 +#define MII_1000TS_LP_HALF 0x0400 + +#define MII_1000TS_BITS \ + "\020" \ + "\020CFG_FAULT" \ + "\017CFG_MASTER" \ + "\014CFG_LOCALRXOK" \ + "\013CFG_REMOTERXOK" \ + "\012LP_FULL" \ + "\011LP_HALF" + +#define MII_XSTATUS_1000BASEX_FD 0x8000 +#define MII_XSTATUS_1000BASEX 0x4000 +#define MII_XSTATUS_1000BASET_FD 0x2000 +#define MII_XSTATUS_1000BASET 0x1000 + +#define MII_XSTATUS_BITS \ + "\020" \ + "\0201000BASEX_FD" \ + "\0171000BASEX" \ + "\0161000BASET_FD" \ + "\0151000BASET" + +#define MII_READ_CMD(p, r) \ + ((6<<(18+5+5)) | ((p)<<(18+5)) | ((r)<<18)) + +#define MII_WRITE_CMD(p, r, v) \ + ((5<<(18+5+5)) | ((p)<<(18+5)) | ((r)<<18) | (2 << 16) | (v)) + +#endif /* _GEM_MII_H_ */ diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel index a1cabf9407..1892ee104b 100644 --- a/usr/src/uts/intel/Makefile.intel +++ b/usr/src/uts/intel/Makefile.intel @@ -493,6 +493,15 @@ DRV_KMODS += usbftdi DRV_KMODS += usbecm # +# USBGEM modules +# +DRV_KMODS += usbgem +DRV_KMODS += axf +DRV_KMODS += udmf +DRV_KMODS += upf +DRV_KMODS += urf + +# # 1394 modules # MISC_KMODS += s1394 sbp2 diff --git a/usr/src/uts/intel/axf/Makefile b/usr/src/uts/intel/axf/Makefile new file mode 100644 index 0000000000..3a2e591c55 --- /dev/null +++ b/usr/src/uts/intel/axf/Makefile @@ -0,0 +1,80 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = axf +OBJECTS = $(AXF_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(AXF_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +CPPFLAGS += -I$(UTSBASE)/common/io/usbgem +CPPFLAGS += -DVERSION=\"2.0.2\" +CPPFLAGS += -DUSBGEM_CONFIG_GLDv3 +LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem + +CERRWARN += -_gcc=-Wno-unused-function +CERRWARN += -_gcc=-Wno-unused-variable +CERRWARN += -_gcc=-Wno-unused-label + +LINTTAGS += -erroff=E_FUNC_VAR_UNUSED +LINTTAGS += -erroff=E_FUNC_ARG_UNUSED +LINTTAGS += -erroff=E_STATIC_UNUSED +LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTTAGS += -erroff=E_FUNC_SET_NOT_USED +LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/udmf/Makefile b/usr/src/uts/intel/udmf/Makefile new file mode 100644 index 0000000000..f43cb3268e --- /dev/null +++ b/usr/src/uts/intel/udmf/Makefile @@ -0,0 +1,80 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = udmf +OBJECTS = $(UDMF_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(UDMF_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +CPPFLAGS += -I$(UTSBASE)/common/io/usbgem +CPPFLAGS += -DVERSION=\"2.0.0\" +CPPFLAGS += -DUSBGEM_CONFIG_GLDv3 +LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem + +CERRWARN += -_gcc=-Wno-unused-value +CERRWARN += -_gcc=-Wno-unused-function +CERRWARN += -_gcc=-Wno-unused-variable +CERRWARN += -_gcc=-Wno-unused-label + +LINTTAGS += -erroff=E_FUNC_VAR_UNUSED +LINTTAGS += -erroff=E_FUNC_ARG_UNUSED +LINTTAGS += -erroff=E_STATIC_UNUSED +LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTTAGS += -erroff=E_FUNC_SET_NOT_USED + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/upf/Makefile b/usr/src/uts/intel/upf/Makefile new file mode 100644 index 0000000000..25f7e577db --- /dev/null +++ b/usr/src/uts/intel/upf/Makefile @@ -0,0 +1,81 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = upf +OBJECTS = $(UPF_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(UPF_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +CPPFLAGS += -I$(UTSBASE)/common/io/usbgem +CPPFLAGS += -DVERSION=\"2.0.1\" +CPPFLAGS += -DUSBGEM_CONFIG_GLDv3 +LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem + +CERRWARN += -_gcc=-Wno-type-limits +CERRWARN += -_gcc=-Wno-unused-function +CERRWARN += -_gcc=-Wno-unused-variable +CERRWARN += -_gcc=-Wno-unused-label + +LINTTAGS += -erroff=E_FUNC_VAR_UNUSED +LINTTAGS += -erroff=E_FUNC_ARG_UNUSED +LINTTAGS += -erroff=E_STATIC_UNUSED +LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTTAGS += -erroff=E_FUNC_SET_NOT_USED +LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/urf/Makefile b/usr/src/uts/intel/urf/Makefile new file mode 100644 index 0000000000..93b6b1aafb --- /dev/null +++ b/usr/src/uts/intel/urf/Makefile @@ -0,0 +1,80 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = urf +OBJECTS = $(URF_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(URF_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +CPPFLAGS += -I$(UTSBASE)/common/io/usbgem +CPPFLAGS += -DVERSION=\"2.0.0\" +CPPFLAGS += -DUSBGEM_CONFIG_GLDv3 +CERRWARN += -_gcc=-Wno-uninitialized +LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba -N misc/usbgem + +CERRWARN += -_gcc=-Wno-unused-function +CERRWARN += -_gcc=-Wno-unused-variable +CERRWARN += -_gcc=-Wno-unused-label + +LINTTAGS += -erroff=E_FUNC_VAR_UNUSED +LINTTAGS += -erroff=E_FUNC_ARG_UNUSED +LINTTAGS += -erroff=E_STATIC_UNUSED +LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTTAGS += -erroff=E_FUNC_SET_NOT_USED + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/usbgem/Makefile b/usr/src/uts/intel/usbgem/Makefile new file mode 100644 index 0000000000..6ec0666264 --- /dev/null +++ b/usr/src/uts/intel/usbgem/Makefile @@ -0,0 +1,93 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = usbgem +OBJECTS = $(USBGEM_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(USBGEM_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# The USBGEM has support for various different features. We use +# these pre-processor macros to define the set we care about. +# +CPPFLAGS += \ + -DUSBGEM_CONFIG_GLDv3 \ + -DMODULE \ + -DGEN_CONFIG_FMA \ + -DUSBGEM_CONFIG_MAC_PROP \ + -DVERSION=\"1.6\" + +CERRWARN += -_gcc=-Wno-uninitialized +CERRWARN += -_gcc=-Wno-parentheses +CERRWARN += -_gcc=-Wno-switch +CERRWARN += -_gcc=-Wno-unused-variable +CERRWARN += -_gcc=-Wno-unused-label +CERRWARN += -_gcc=-Wno-unused-function + +LINTTAGS += -erroff=E_FUNC_VAR_UNUSED +LINTTAGS += -erroff=E_FUNC_ARG_UNUSED +LINTTAGS += -erroff=E_STATIC_UNUSED +LINTTAGS += -erroff=E_FUNC_SET_NOT_USED +LINTTAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 +LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTTAGS += -erroff=E_BAD_FORMAT_ARG_TYPE2 +LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV + +LDFLAGS += -dy -N misc/mac -N drv/ip -N misc/usba + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ |